1
1
use core:: { iter:: FromIterator , ops:: Range } ;
2
- use std:: { borrow:: Cow , collections:: HashSet } ;
2
+ use std:: {
3
+ borrow:: Cow ,
4
+ collections:: HashSet ,
5
+ path:: { Path , PathBuf } ,
6
+ } ;
3
7
4
- use anyhow:: ensure;
8
+ use anyhow:: { bail , ensure} ;
5
9
use gimli:: { read:: Reader , DebuggingInformationEntry , Dwarf , Unit } ;
6
10
use intervaltree:: { Element , IntervalTree } ;
7
11
use object:: { Object as _, ObjectSection as _} ;
@@ -70,7 +74,15 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result<Map
70
74
let name = demangle ( & sub. name ) ;
71
75
elements. push ( Element {
72
76
range,
73
- value : Frame { name, depth } ,
77
+ value : Frame {
78
+ name,
79
+ depth,
80
+ call_loc : None ,
81
+ decl_loc : Location {
82
+ file : file_index_to_path ( sub. decl_file , & unit, & dwarf) ?,
83
+ line : sub. decl_line ,
84
+ } ,
85
+ } ,
74
86
} ) ;
75
87
} else {
76
88
// we won't walk into subprograms that are were GC-ed by the linker
@@ -91,6 +103,18 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result<Map
91
103
value : Frame {
92
104
name : demangle ( & inline_sub. origin . name ) ,
93
105
depth,
106
+ call_loc : Some ( Location {
107
+ file : file_index_to_path ( inline_sub. call_file , & unit, & dwarf) ?,
108
+ line : inline_sub. call_line ,
109
+ } ) ,
110
+ decl_loc : Location {
111
+ file : file_index_to_path (
112
+ inline_sub. origin . decl_file ,
113
+ & unit,
114
+ & dwarf,
115
+ ) ?,
116
+ line : inline_sub. origin . decl_line ,
117
+ } ,
94
118
} ,
95
119
} )
96
120
} else if entry. tag ( ) == gimli:: constants:: DW_TAG_lexical_block
@@ -111,7 +135,14 @@ pub struct Frame {
111
135
pub name : String ,
112
136
// depth in the DIE tree
113
137
pub depth : isize ,
114
- // TODO add file location
138
+ pub call_loc : Option < Location > ,
139
+ pub decl_loc : Location ,
140
+ }
141
+
142
+ #[ derive( Debug ) ]
143
+ pub struct Location {
144
+ pub file : PathBuf ,
145
+ pub line : u64 ,
115
146
}
116
147
117
148
#[ derive( Clone , Debug , PartialEq ) ]
@@ -126,9 +157,8 @@ struct Subprogram {
126
157
depth : isize ,
127
158
name : String ,
128
159
span : Span ,
129
- // NOTE the DIE contains `decl_file` and `decl_line` info but those points into
130
- // the *declaration* of the function, e.g. `fn foo() {`, which is not particularly useful.
131
- // We are more interested in the location of the statements within the function
160
+ decl_file : u64 ,
161
+ decl_line : u64 ,
132
162
}
133
163
134
164
impl Subprogram {
@@ -150,6 +180,8 @@ impl Subprogram {
150
180
let mut low_pc = None ;
151
181
let mut name = None ;
152
182
let mut pc_offset = None ;
183
+ let mut decl_file = None ;
184
+ let mut decl_line = None ;
153
185
while let Some ( attr) = attrs. next ( ) ? {
154
186
match attr. name ( ) {
155
187
gimli:: constants:: DW_AT_low_pc => {
@@ -188,12 +220,26 @@ impl Subprogram {
188
220
}
189
221
}
190
222
223
+ gimli:: constants:: DW_AT_decl_file => {
224
+ if let gimli:: AttributeValue :: FileIndex ( idx) = attr. value ( ) {
225
+ decl_file = Some ( idx) ;
226
+ }
227
+ }
228
+
229
+ gimli:: constants:: DW_AT_decl_line => {
230
+ if let gimli:: AttributeValue :: Udata ( line) = attr. value ( ) {
231
+ decl_line = Some ( line) ;
232
+ }
233
+ }
234
+
191
235
_ => { }
192
236
}
193
237
}
194
238
195
239
if let Some ( off) = linkage_name. or ( name) {
196
240
let name = dwarf. string ( off) ?. to_string ( ) ?. into_owned ( ) ;
241
+ let decl_file = decl_file. expect ( "no `decl_file`" ) ;
242
+ let decl_line = decl_line. expect ( "no `decl_line`" ) ;
197
243
198
244
Ok ( Some ( Subprogram {
199
245
depth,
@@ -205,6 +251,8 @@ impl Subprogram {
205
251
Span :: Pc ( low_pc..( low_pc + pc_off) )
206
252
} ,
207
253
name,
254
+ decl_file,
255
+ decl_line,
208
256
} ) )
209
257
} else {
210
258
// TODO what are these nameless subroutines? They seem to have "abstract origin" info
@@ -323,3 +371,49 @@ fn demangle(function: &str) -> String {
323
371
324
372
demangled
325
373
}
374
+
375
+ // XXX copy-pasted from defmt/elf2table :sadface:
376
+ fn file_index_to_path < R > (
377
+ index : u64 ,
378
+ unit : & gimli:: Unit < R > ,
379
+ dwarf : & gimli:: Dwarf < R > ,
380
+ ) -> Result < PathBuf , anyhow:: Error >
381
+ where
382
+ R : gimli:: read:: Reader ,
383
+ {
384
+ ensure ! ( index != 0 , "`FileIndex` was zero" ) ;
385
+
386
+ let header = if let Some ( program) = & unit. line_program {
387
+ program. header ( )
388
+ } else {
389
+ bail ! ( "no `LineProgram`" ) ;
390
+ } ;
391
+
392
+ let file = if let Some ( file) = header. file ( index) {
393
+ file
394
+ } else {
395
+ bail ! ( "no `FileEntry` for index {}" , index)
396
+ } ;
397
+
398
+ let mut p = PathBuf :: new ( ) ;
399
+ if let Some ( dir) = file. directory ( header) {
400
+ let dir = dwarf. attr_string ( unit, dir) ?;
401
+ let dir_s = dir. to_string_lossy ( ) ?;
402
+ let dir = Path :: new ( & dir_s[ ..] ) ;
403
+
404
+ if !dir. is_absolute ( ) {
405
+ if let Some ( ref comp_dir) = unit. comp_dir {
406
+ p. push ( & comp_dir. to_string_lossy ( ) ?[ ..] ) ;
407
+ }
408
+ }
409
+ p. push ( & dir) ;
410
+ }
411
+
412
+ p. push (
413
+ & dwarf
414
+ . attr_string ( unit, file. path_name ( ) ) ?
415
+ . to_string_lossy ( ) ?[ ..] ,
416
+ ) ;
417
+
418
+ Ok ( p)
419
+ }
0 commit comments