diff options
author | David S. Miller <davem@davemloft.net> | 2010-05-19 02:01:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-05-19 02:01:55 -0400 |
commit | 2ec8c6bb5d8f3a62a79f463525054bae1e3d4487 (patch) | |
tree | fa7f8400ac685fb52e96f64997c7c682fc2aa021 /tools/perf/util/probe-finder.c | |
parent | 7b39f90fabcf9e2af0cd79d0a60440d821e22b56 (diff) | |
parent | 537b60d17894b7c19a6060feae40299d7109d6e7 (diff) |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Conflicts:
include/linux/mod_devicetable.h
scripts/mod/file2alias.c
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 1046 |
1 files changed, 755 insertions, 291 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c171a243d05b..562b1443e785 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <ctype.h> | 33 | #include <ctype.h> |
34 | #include <dwarf-regs.h> | ||
34 | 35 | ||
35 | #include "string.h" | 36 | #include "string.h" |
36 | #include "event.h" | 37 | #include "event.h" |
@@ -38,57 +39,8 @@ | |||
38 | #include "util.h" | 39 | #include "util.h" |
39 | #include "probe-finder.h" | 40 | #include "probe-finder.h" |
40 | 41 | ||
41 | 42 | /* Kprobe tracer basic type is up to u64 */ | |
42 | /* | 43 | #define MAX_BASIC_TYPE_BITS 64 |
43 | * Generic dwarf analysis helpers | ||
44 | */ | ||
45 | |||
46 | #define X86_32_MAX_REGS 8 | ||
47 | const char *x86_32_regs_table[X86_32_MAX_REGS] = { | ||
48 | "%ax", | ||
49 | "%cx", | ||
50 | "%dx", | ||
51 | "%bx", | ||
52 | "$stack", /* Stack address instead of %sp */ | ||
53 | "%bp", | ||
54 | "%si", | ||
55 | "%di", | ||
56 | }; | ||
57 | |||
58 | #define X86_64_MAX_REGS 16 | ||
59 | const char *x86_64_regs_table[X86_64_MAX_REGS] = { | ||
60 | "%ax", | ||
61 | "%dx", | ||
62 | "%cx", | ||
63 | "%bx", | ||
64 | "%si", | ||
65 | "%di", | ||
66 | "%bp", | ||
67 | "%sp", | ||
68 | "%r8", | ||
69 | "%r9", | ||
70 | "%r10", | ||
71 | "%r11", | ||
72 | "%r12", | ||
73 | "%r13", | ||
74 | "%r14", | ||
75 | "%r15", | ||
76 | }; | ||
77 | |||
78 | /* TODO: switching by dwarf address size */ | ||
79 | #ifdef __x86_64__ | ||
80 | #define ARCH_MAX_REGS X86_64_MAX_REGS | ||
81 | #define arch_regs_table x86_64_regs_table | ||
82 | #else | ||
83 | #define ARCH_MAX_REGS X86_32_MAX_REGS | ||
84 | #define arch_regs_table x86_32_regs_table | ||
85 | #endif | ||
86 | |||
87 | /* Return architecture dependent register string (for kprobe-tracer) */ | ||
88 | static const char *get_arch_regstr(unsigned int n) | ||
89 | { | ||
90 | return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; | ||
91 | } | ||
92 | 44 | ||
93 | /* | 45 | /* |
94 | * Compare the tail of two strings. | 46 | * Compare the tail of two strings. |
@@ -108,7 +60,7 @@ static int strtailcmp(const char *s1, const char *s2) | |||
108 | /* Line number list operations */ | 60 | /* Line number list operations */ |
109 | 61 | ||
110 | /* Add a line to line number list */ | 62 | /* Add a line to line number list */ |
111 | static void line_list__add_line(struct list_head *head, unsigned int line) | 63 | static int line_list__add_line(struct list_head *head, int line) |
112 | { | 64 | { |
113 | struct line_node *ln; | 65 | struct line_node *ln; |
114 | struct list_head *p; | 66 | struct list_head *p; |
@@ -119,21 +71,23 @@ static void line_list__add_line(struct list_head *head, unsigned int line) | |||
119 | p = &ln->list; | 71 | p = &ln->list; |
120 | goto found; | 72 | goto found; |
121 | } else if (ln->line == line) /* Already exist */ | 73 | } else if (ln->line == line) /* Already exist */ |
122 | return ; | 74 | return 1; |
123 | } | 75 | } |
124 | /* List is empty, or the smallest entry */ | 76 | /* List is empty, or the smallest entry */ |
125 | p = head; | 77 | p = head; |
126 | found: | 78 | found: |
127 | pr_debug("line list: add a line %u\n", line); | 79 | pr_debug("line list: add a line %u\n", line); |
128 | ln = zalloc(sizeof(struct line_node)); | 80 | ln = zalloc(sizeof(struct line_node)); |
129 | DIE_IF(ln == NULL); | 81 | if (ln == NULL) |
82 | return -ENOMEM; | ||
130 | ln->line = line; | 83 | ln->line = line; |
131 | INIT_LIST_HEAD(&ln->list); | 84 | INIT_LIST_HEAD(&ln->list); |
132 | list_add(&ln->list, p); | 85 | list_add(&ln->list, p); |
86 | return 0; | ||
133 | } | 87 | } |
134 | 88 | ||
135 | /* Check if the line in line number list */ | 89 | /* Check if the line in line number list */ |
136 | static int line_list__has_line(struct list_head *head, unsigned int line) | 90 | static int line_list__has_line(struct list_head *head, int line) |
137 | { | 91 | { |
138 | struct line_node *ln; | 92 | struct line_node *ln; |
139 | 93 | ||
@@ -184,9 +138,129 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | |||
184 | if (strtailcmp(src, fname) == 0) | 138 | if (strtailcmp(src, fname) == 0) |
185 | break; | 139 | break; |
186 | } | 140 | } |
141 | if (i == nfiles) | ||
142 | return NULL; | ||
187 | return src; | 143 | return src; |
188 | } | 144 | } |
189 | 145 | ||
146 | /* Compare diename and tname */ | ||
147 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
148 | { | ||
149 | const char *name; | ||
150 | name = dwarf_diename(dw_die); | ||
151 | return name ? strcmp(tname, name) : -1; | ||
152 | } | ||
153 | |||
154 | /* Get type die, but skip qualifiers and typedef */ | ||
155 | static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
156 | { | ||
157 | Dwarf_Attribute attr; | ||
158 | int tag; | ||
159 | |||
160 | do { | ||
161 | if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || | ||
162 | dwarf_formref_die(&attr, die_mem) == NULL) | ||
163 | return NULL; | ||
164 | |||
165 | tag = dwarf_tag(die_mem); | ||
166 | vr_die = die_mem; | ||
167 | } while (tag == DW_TAG_const_type || | ||
168 | tag == DW_TAG_restrict_type || | ||
169 | tag == DW_TAG_volatile_type || | ||
170 | tag == DW_TAG_shared_type || | ||
171 | tag == DW_TAG_typedef); | ||
172 | |||
173 | return die_mem; | ||
174 | } | ||
175 | |||
176 | static bool die_is_signed_type(Dwarf_Die *tp_die) | ||
177 | { | ||
178 | Dwarf_Attribute attr; | ||
179 | Dwarf_Word ret; | ||
180 | |||
181 | if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || | ||
182 | dwarf_formudata(&attr, &ret) != 0) | ||
183 | return false; | ||
184 | |||
185 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
186 | ret == DW_ATE_signed_fixed); | ||
187 | } | ||
188 | |||
189 | static int die_get_byte_size(Dwarf_Die *tp_die) | ||
190 | { | ||
191 | Dwarf_Attribute attr; | ||
192 | Dwarf_Word ret; | ||
193 | |||
194 | if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || | ||
195 | dwarf_formudata(&attr, &ret) != 0) | ||
196 | return 0; | ||
197 | |||
198 | return (int)ret; | ||
199 | } | ||
200 | |||
201 | /* Get data_member_location offset */ | ||
202 | static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
203 | { | ||
204 | Dwarf_Attribute attr; | ||
205 | Dwarf_Op *expr; | ||
206 | size_t nexpr; | ||
207 | int ret; | ||
208 | |||
209 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
210 | return -ENOENT; | ||
211 | |||
212 | if (dwarf_formudata(&attr, offs) != 0) { | ||
213 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
214 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
215 | if (ret < 0 || nexpr == 0) | ||
216 | return -ENOENT; | ||
217 | |||
218 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
219 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
220 | expr[0].atom, nexpr); | ||
221 | return -ENOTSUP; | ||
222 | } | ||
223 | *offs = (Dwarf_Word)expr[0].number; | ||
224 | } | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | /* Return values for die_find callbacks */ | ||
229 | enum { | ||
230 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | ||
231 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
232 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
233 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
234 | }; | ||
235 | |||
236 | /* Search a child die */ | ||
237 | static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
238 | int (*callback)(Dwarf_Die *, void *), | ||
239 | void *data, Dwarf_Die *die_mem) | ||
240 | { | ||
241 | Dwarf_Die child_die; | ||
242 | int ret; | ||
243 | |||
244 | ret = dwarf_child(rt_die, die_mem); | ||
245 | if (ret != 0) | ||
246 | return NULL; | ||
247 | |||
248 | do { | ||
249 | ret = callback(die_mem, data); | ||
250 | if (ret == DIE_FIND_CB_FOUND) | ||
251 | return die_mem; | ||
252 | |||
253 | if ((ret & DIE_FIND_CB_CHILD) && | ||
254 | die_find_child(die_mem, callback, data, &child_die)) { | ||
255 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
256 | return die_mem; | ||
257 | } | ||
258 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
259 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
260 | |||
261 | return NULL; | ||
262 | } | ||
263 | |||
190 | struct __addr_die_search_param { | 264 | struct __addr_die_search_param { |
191 | Dwarf_Addr addr; | 265 | Dwarf_Addr addr; |
192 | Dwarf_Die *die_mem; | 266 | Dwarf_Die *die_mem; |
@@ -205,8 +279,8 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | |||
205 | } | 279 | } |
206 | 280 | ||
207 | /* Search a real subprogram including this line, */ | 281 | /* Search a real subprogram including this line, */ |
208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | 282 | static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, |
209 | Dwarf_Die *die_mem) | 283 | Dwarf_Die *die_mem) |
210 | { | 284 | { |
211 | struct __addr_die_search_param ad; | 285 | struct __addr_die_search_param ad; |
212 | ad.addr = addr; | 286 | ad.addr = addr; |
@@ -218,77 +292,64 @@ static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | |||
218 | return die_mem; | 292 | return die_mem; |
219 | } | 293 | } |
220 | 294 | ||
221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | 295 | /* die_find callback for inline function search */ |
222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 296 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) |
223 | Dwarf_Die *die_mem) | ||
224 | { | 297 | { |
225 | Dwarf_Die child_die; | 298 | Dwarf_Addr *addr = data; |
226 | int ret; | ||
227 | 299 | ||
228 | ret = dwarf_child(sp_die, die_mem); | 300 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && |
229 | if (ret != 0) | 301 | dwarf_haspc(die_mem, *addr)) |
230 | return NULL; | 302 | return DIE_FIND_CB_FOUND; |
231 | 303 | ||
232 | do { | 304 | return DIE_FIND_CB_CONTINUE; |
233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
234 | dwarf_haspc(die_mem, addr)) | ||
235 | return die_mem; | ||
236 | |||
237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { | ||
238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
239 | return die_mem; | ||
240 | } | ||
241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
242 | |||
243 | return NULL; | ||
244 | } | 305 | } |
245 | 306 | ||
246 | /* Compare diename and tname */ | 307 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ |
247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | 308 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
309 | Dwarf_Die *die_mem) | ||
248 | { | 310 | { |
249 | const char *name; | 311 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
250 | name = dwarf_diename(dw_die); | ||
251 | DIE_IF(name == NULL); | ||
252 | return strcmp(tname, name); | ||
253 | } | 312 | } |
254 | 313 | ||
255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | 314 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) |
256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | ||
257 | { | 315 | { |
258 | Dwarf_Addr epc; | 316 | const char *name = data; |
259 | int ret; | 317 | int tag; |
260 | 318 | ||
261 | ret = dwarf_entrypc(dw_die, &epc); | 319 | tag = dwarf_tag(die_mem); |
262 | DIE_IF(ret == -1); | 320 | if ((tag == DW_TAG_formal_parameter || |
263 | return epc; | 321 | tag == DW_TAG_variable) && |
322 | (die_compare_name(die_mem, name) == 0)) | ||
323 | return DIE_FIND_CB_FOUND; | ||
324 | |||
325 | return DIE_FIND_CB_CONTINUE; | ||
264 | } | 326 | } |
265 | 327 | ||
266 | /* Get a variable die */ | 328 | /* Find a variable called 'name' */ |
267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | 329 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, |
268 | Dwarf_Die *die_mem) | 330 | Dwarf_Die *die_mem) |
269 | { | 331 | { |
270 | Dwarf_Die child_die; | 332 | return die_find_child(sp_die, __die_find_variable_cb, (void *)name, |
271 | int tag; | 333 | die_mem); |
272 | int ret; | 334 | } |
273 | 335 | ||
274 | ret = dwarf_child(sp_die, die_mem); | 336 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) |
275 | if (ret != 0) | 337 | { |
276 | return NULL; | 338 | const char *name = data; |
277 | 339 | ||
278 | do { | 340 | if ((dwarf_tag(die_mem) == DW_TAG_member) && |
279 | tag = dwarf_tag(die_mem); | 341 | (die_compare_name(die_mem, name) == 0)) |
280 | if ((tag == DW_TAG_formal_parameter || | 342 | return DIE_FIND_CB_FOUND; |
281 | tag == DW_TAG_variable) && | ||
282 | (die_compare_name(die_mem, name) == 0)) | ||
283 | return die_mem; | ||
284 | 343 | ||
285 | if (die_find_variable(die_mem, name, &child_die)) { | 344 | return DIE_FIND_CB_SIBLING; |
286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | 345 | } |
287 | return die_mem; | ||
288 | } | ||
289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
290 | 346 | ||
291 | return NULL; | 347 | /* Find a member called 'name' */ |
348 | static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
349 | Dwarf_Die *die_mem) | ||
350 | { | ||
351 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
352 | die_mem); | ||
292 | } | 353 | } |
293 | 354 | ||
294 | /* | 355 | /* |
@@ -296,19 +357,22 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | |||
296 | */ | 357 | */ |
297 | 358 | ||
298 | /* Show a location */ | 359 | /* Show a location */ |
299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) | 360 | static int convert_location(Dwarf_Op *op, struct probe_finder *pf) |
300 | { | 361 | { |
301 | unsigned int regn; | 362 | unsigned int regn; |
302 | Dwarf_Word offs = 0; | 363 | Dwarf_Word offs = 0; |
303 | int deref = 0, ret; | 364 | bool ref = false; |
304 | const char *regs; | 365 | const char *regs; |
366 | struct kprobe_trace_arg *tvar = pf->tvar; | ||
305 | 367 | ||
306 | /* TODO: support CFA */ | ||
307 | /* If this is based on frame buffer, set the offset */ | 368 | /* If this is based on frame buffer, set the offset */ |
308 | if (op->atom == DW_OP_fbreg) { | 369 | if (op->atom == DW_OP_fbreg) { |
309 | if (pf->fb_ops == NULL) | 370 | if (pf->fb_ops == NULL) { |
310 | die("The attribute of frame base is not supported.\n"); | 371 | pr_warning("The attribute of frame base is not " |
311 | deref = 1; | 372 | "supported.\n"); |
373 | return -ENOTSUP; | ||
374 | } | ||
375 | ref = true; | ||
312 | offs = op->number; | 376 | offs = op->number; |
313 | op = &pf->fb_ops[0]; | 377 | op = &pf->fb_ops[0]; |
314 | } | 378 | } |
@@ -316,35 +380,164 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) | |||
316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { | 380 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { |
317 | regn = op->atom - DW_OP_breg0; | 381 | regn = op->atom - DW_OP_breg0; |
318 | offs += op->number; | 382 | offs += op->number; |
319 | deref = 1; | 383 | ref = true; |
320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { | 384 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { |
321 | regn = op->atom - DW_OP_reg0; | 385 | regn = op->atom - DW_OP_reg0; |
322 | } else if (op->atom == DW_OP_bregx) { | 386 | } else if (op->atom == DW_OP_bregx) { |
323 | regn = op->number; | 387 | regn = op->number; |
324 | offs += op->number2; | 388 | offs += op->number2; |
325 | deref = 1; | 389 | ref = true; |
326 | } else if (op->atom == DW_OP_regx) { | 390 | } else if (op->atom == DW_OP_regx) { |
327 | regn = op->number; | 391 | regn = op->number; |
328 | } else | 392 | } else { |
329 | die("DW_OP %d is not supported.", op->atom); | 393 | pr_warning("DW_OP %x is not supported.\n", op->atom); |
394 | return -ENOTSUP; | ||
395 | } | ||
330 | 396 | ||
331 | regs = get_arch_regstr(regn); | 397 | regs = get_arch_regstr(regn); |
332 | if (!regs) | 398 | if (!regs) { |
333 | die("%u exceeds max register number.", regn); | 399 | pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); |
400 | return -ERANGE; | ||
401 | } | ||
402 | |||
403 | tvar->value = strdup(regs); | ||
404 | if (tvar->value == NULL) | ||
405 | return -ENOMEM; | ||
406 | |||
407 | if (ref) { | ||
408 | tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
409 | if (tvar->ref == NULL) | ||
410 | return -ENOMEM; | ||
411 | tvar->ref->offset = (long)offs; | ||
412 | } | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static int convert_variable_type(Dwarf_Die *vr_die, | ||
417 | struct kprobe_trace_arg *targ) | ||
418 | { | ||
419 | Dwarf_Die type; | ||
420 | char buf[16]; | ||
421 | int ret; | ||
422 | |||
423 | if (die_get_real_type(vr_die, &type) == NULL) { | ||
424 | pr_warning("Failed to get a type information of %s.\n", | ||
425 | dwarf_diename(vr_die)); | ||
426 | return -ENOENT; | ||
427 | } | ||
428 | |||
429 | ret = die_get_byte_size(&type) * 8; | ||
430 | if (ret) { | ||
431 | /* Check the bitwidth */ | ||
432 | if (ret > MAX_BASIC_TYPE_BITS) { | ||
433 | pr_info("%s exceeds max-bitwidth." | ||
434 | " Cut down to %d bits.\n", | ||
435 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); | ||
436 | ret = MAX_BASIC_TYPE_BITS; | ||
437 | } | ||
438 | |||
439 | ret = snprintf(buf, 16, "%c%d", | ||
440 | die_is_signed_type(&type) ? 's' : 'u', ret); | ||
441 | if (ret < 0 || ret >= 16) { | ||
442 | if (ret >= 16) | ||
443 | ret = -E2BIG; | ||
444 | pr_warning("Failed to convert variable type: %s\n", | ||
445 | strerror(-ret)); | ||
446 | return ret; | ||
447 | } | ||
448 | targ->type = strdup(buf); | ||
449 | if (targ->type == NULL) | ||
450 | return -ENOMEM; | ||
451 | } | ||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | ||
456 | struct perf_probe_arg_field *field, | ||
457 | struct kprobe_trace_arg_ref **ref_ptr, | ||
458 | Dwarf_Die *die_mem) | ||
459 | { | ||
460 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | ||
461 | Dwarf_Die type; | ||
462 | Dwarf_Word offs; | ||
463 | int ret; | ||
464 | |||
465 | pr_debug("converting %s in %s\n", field->name, varname); | ||
466 | if (die_get_real_type(vr_die, &type) == NULL) { | ||
467 | pr_warning("Failed to get the type of %s.\n", varname); | ||
468 | return -ENOENT; | ||
469 | } | ||
470 | |||
471 | /* Check the pointer and dereference */ | ||
472 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | ||
473 | if (!field->ref) { | ||
474 | pr_err("Semantic error: %s must be referred by '->'\n", | ||
475 | field->name); | ||
476 | return -EINVAL; | ||
477 | } | ||
478 | /* Get the type pointed by this pointer */ | ||
479 | if (die_get_real_type(&type, &type) == NULL) { | ||
480 | pr_warning("Failed to get the type of %s.\n", varname); | ||
481 | return -ENOENT; | ||
482 | } | ||
483 | /* Verify it is a data structure */ | ||
484 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | ||
485 | pr_warning("%s is not a data structure.\n", varname); | ||
486 | return -EINVAL; | ||
487 | } | ||
488 | |||
489 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
490 | if (ref == NULL) | ||
491 | return -ENOMEM; | ||
492 | if (*ref_ptr) | ||
493 | (*ref_ptr)->next = ref; | ||
494 | else | ||
495 | *ref_ptr = ref; | ||
496 | } else { | ||
497 | /* Verify it is a data structure */ | ||
498 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | ||
499 | pr_warning("%s is not a data structure.\n", varname); | ||
500 | return -EINVAL; | ||
501 | } | ||
502 | if (field->ref) { | ||
503 | pr_err("Semantic error: %s must be referred by '.'\n", | ||
504 | field->name); | ||
505 | return -EINVAL; | ||
506 | } | ||
507 | if (!ref) { | ||
508 | pr_warning("Structure on a register is not " | ||
509 | "supported yet.\n"); | ||
510 | return -ENOTSUP; | ||
511 | } | ||
512 | } | ||
513 | |||
514 | if (die_find_member(&type, field->name, die_mem) == NULL) { | ||
515 | pr_warning("%s(tyep:%s) has no member %s.\n", varname, | ||
516 | dwarf_diename(&type), field->name); | ||
517 | return -EINVAL; | ||
518 | } | ||
334 | 519 | ||
335 | if (deref) | 520 | /* Get the offset of the field */ |
336 | ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", | 521 | ret = die_get_data_member_location(die_mem, &offs); |
337 | pf->var, (intmax_t)offs, regs); | 522 | if (ret < 0) { |
523 | pr_warning("Failed to get the offset of %s.\n", field->name); | ||
524 | return ret; | ||
525 | } | ||
526 | ref->offset += (long)offs; | ||
527 | |||
528 | /* Converting next field */ | ||
529 | if (field->next) | ||
530 | return convert_variable_fields(die_mem, field->name, | ||
531 | field->next, &ref, die_mem); | ||
338 | else | 532 | else |
339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | 533 | return 0; |
340 | DIE_IF(ret < 0); | ||
341 | DIE_IF(ret >= pf->len); | ||
342 | } | 534 | } |
343 | 535 | ||
344 | /* Show a variables in kprobe event format */ | 536 | /* Show a variables in kprobe event format */ |
345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 537 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
346 | { | 538 | { |
347 | Dwarf_Attribute attr; | 539 | Dwarf_Attribute attr; |
540 | Dwarf_Die die_mem; | ||
348 | Dwarf_Op *expr; | 541 | Dwarf_Op *expr; |
349 | size_t nexpr; | 542 | size_t nexpr; |
350 | int ret; | 543 | int ret; |
@@ -356,142 +549,191 @@ static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
356 | if (ret <= 0 || nexpr == 0) | 549 | if (ret <= 0 || nexpr == 0) |
357 | goto error; | 550 | goto error; |
358 | 551 | ||
359 | show_location(expr, pf); | 552 | ret = convert_location(expr, pf); |
553 | if (ret == 0 && pf->pvar->field) { | ||
554 | ret = convert_variable_fields(vr_die, pf->pvar->var, | ||
555 | pf->pvar->field, &pf->tvar->ref, | ||
556 | &die_mem); | ||
557 | vr_die = &die_mem; | ||
558 | } | ||
559 | if (ret == 0) { | ||
560 | if (pf->pvar->type) { | ||
561 | pf->tvar->type = strdup(pf->pvar->type); | ||
562 | if (pf->tvar->type == NULL) | ||
563 | ret = -ENOMEM; | ||
564 | } else | ||
565 | ret = convert_variable_type(vr_die, pf->tvar); | ||
566 | } | ||
360 | /* *expr will be cached in libdw. Don't free it. */ | 567 | /* *expr will be cached in libdw. Don't free it. */ |
361 | return ; | 568 | return ret; |
362 | error: | 569 | error: |
363 | /* TODO: Support const_value */ | 570 | /* TODO: Support const_value */ |
364 | die("Failed to find the location of %s at this address.\n" | 571 | pr_err("Failed to find the location of %s at this address.\n" |
365 | " Perhaps, it has been optimized out.", pf->var); | 572 | " Perhaps, it has been optimized out.\n", pf->pvar->var); |
573 | return -ENOENT; | ||
366 | } | 574 | } |
367 | 575 | ||
368 | /* Find a variable in a subprogram die */ | 576 | /* Find a variable in a subprogram die */ |
369 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 577 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
370 | { | 578 | { |
371 | int ret; | ||
372 | Dwarf_Die vr_die; | 579 | Dwarf_Die vr_die; |
580 | char buf[32], *ptr; | ||
581 | int ret; | ||
373 | 582 | ||
374 | /* TODO: Support struct members and arrays */ | 583 | /* TODO: Support arrays */ |
375 | if (!is_c_varname(pf->var)) { | 584 | if (pf->pvar->name) |
376 | /* Output raw parameters */ | 585 | pf->tvar->name = strdup(pf->pvar->name); |
377 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | 586 | else { |
378 | DIE_IF(ret < 0); | 587 | ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); |
379 | DIE_IF(ret >= pf->len); | 588 | if (ret < 0) |
380 | return ; | 589 | return ret; |
590 | ptr = strchr(buf, ':'); /* Change type separator to _ */ | ||
591 | if (ptr) | ||
592 | *ptr = '_'; | ||
593 | pf->tvar->name = strdup(buf); | ||
594 | } | ||
595 | if (pf->tvar->name == NULL) | ||
596 | return -ENOMEM; | ||
597 | |||
598 | if (!is_c_varname(pf->pvar->var)) { | ||
599 | /* Copy raw parameters */ | ||
600 | pf->tvar->value = strdup(pf->pvar->var); | ||
601 | if (pf->tvar->value == NULL) | ||
602 | return -ENOMEM; | ||
603 | else | ||
604 | return 0; | ||
381 | } | 605 | } |
382 | 606 | ||
383 | pr_debug("Searching '%s' variable in context.\n", pf->var); | 607 | pr_debug("Searching '%s' variable in context.\n", |
608 | pf->pvar->var); | ||
384 | /* Search child die for local variables and parameters. */ | 609 | /* Search child die for local variables and parameters. */ |
385 | if (!die_find_variable(sp_die, pf->var, &vr_die)) | 610 | if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { |
386 | die("Failed to find '%s' in this function.", pf->var); | 611 | pr_warning("Failed to find '%s' in this function.\n", |
387 | 612 | pf->pvar->var); | |
388 | show_variable(&vr_die, pf); | 613 | return -ENOENT; |
614 | } | ||
615 | return convert_variable(&vr_die, pf); | ||
389 | } | 616 | } |
390 | 617 | ||
391 | /* Show a probe point to output buffer */ | 618 | /* Show a probe point to output buffer */ |
392 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | 619 | static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
393 | { | 620 | { |
394 | struct probe_point *pp = pf->pp; | 621 | struct kprobe_trace_event *tev; |
395 | Dwarf_Addr eaddr; | 622 | Dwarf_Addr eaddr; |
396 | Dwarf_Die die_mem; | 623 | Dwarf_Die die_mem; |
397 | const char *name; | 624 | const char *name; |
398 | char tmp[MAX_PROBE_BUFFER]; | 625 | int ret, i; |
399 | int ret, i, len; | ||
400 | Dwarf_Attribute fb_attr; | 626 | Dwarf_Attribute fb_attr; |
401 | size_t nops; | 627 | size_t nops; |
402 | 628 | ||
629 | if (pf->ntevs == pf->max_tevs) { | ||
630 | pr_warning("Too many( > %d) probe point found.\n", | ||
631 | pf->max_tevs); | ||
632 | return -ERANGE; | ||
633 | } | ||
634 | tev = &pf->tevs[pf->ntevs++]; | ||
635 | |||
403 | /* If no real subprogram, find a real one */ | 636 | /* If no real subprogram, find a real one */ |
404 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 637 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { |
405 | sp_die = die_get_real_subprogram(&pf->cu_die, | 638 | sp_die = die_find_real_subprogram(&pf->cu_die, |
406 | pf->addr, &die_mem); | 639 | pf->addr, &die_mem); |
407 | if (!sp_die) | 640 | if (!sp_die) { |
408 | die("Probe point is not found in subprograms."); | 641 | pr_warning("Failed to find probe point in any " |
642 | "functions.\n"); | ||
643 | return -ENOENT; | ||
644 | } | ||
409 | } | 645 | } |
410 | 646 | ||
411 | /* Output name of probe point */ | 647 | /* Copy the name of probe point */ |
412 | name = dwarf_diename(sp_die); | 648 | name = dwarf_diename(sp_die); |
413 | if (name) { | 649 | if (name) { |
414 | dwarf_entrypc(sp_die, &eaddr); | 650 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { |
415 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, | 651 | pr_warning("Failed to get entry pc of %s\n", |
416 | (unsigned long)(pf->addr - eaddr)); | 652 | dwarf_diename(sp_die)); |
417 | /* Copy the function name if possible */ | 653 | return -ENOENT; |
418 | if (!pp->function) { | ||
419 | pp->function = strdup(name); | ||
420 | pp->offset = (size_t)(pf->addr - eaddr); | ||
421 | } | 654 | } |
422 | } else { | 655 | tev->point.symbol = strdup(name); |
656 | if (tev->point.symbol == NULL) | ||
657 | return -ENOMEM; | ||
658 | tev->point.offset = (unsigned long)(pf->addr - eaddr); | ||
659 | } else | ||
423 | /* This function has no name. */ | 660 | /* This function has no name. */ |
424 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", | 661 | tev->point.offset = (unsigned long)pf->addr; |
425 | (uintmax_t)pf->addr); | 662 | |
426 | if (!pp->function) { | 663 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, |
427 | /* TODO: Use _stext */ | 664 | tev->point.offset); |
428 | pp->function = strdup(""); | ||
429 | pp->offset = (size_t)pf->addr; | ||
430 | } | ||
431 | } | ||
432 | DIE_IF(ret < 0); | ||
433 | DIE_IF(ret >= MAX_PROBE_BUFFER); | ||
434 | len = ret; | ||
435 | pr_debug("Probe point found: %s\n", tmp); | ||
436 | 665 | ||
437 | /* Get the frame base attribute/ops */ | 666 | /* Get the frame base attribute/ops */ |
438 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | 667 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); |
439 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | 668 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); |
440 | if (ret <= 0 || nops == 0) | 669 | if (ret <= 0 || nops == 0) { |
441 | pf->fb_ops = NULL; | 670 | pf->fb_ops = NULL; |
671 | } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && | ||
672 | pf->cfi != NULL) { | ||
673 | Dwarf_Frame *frame; | ||
674 | if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || | ||
675 | dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { | ||
676 | pr_warning("Failed to get CFA on 0x%jx\n", | ||
677 | (uintmax_t)pf->addr); | ||
678 | return -ENOENT; | ||
679 | } | ||
680 | } | ||
442 | 681 | ||
443 | /* Find each argument */ | 682 | /* Find each argument */ |
444 | /* TODO: use dwarf_cfi_addrframe */ | 683 | tev->nargs = pf->pev->nargs; |
445 | for (i = 0; i < pp->nr_args; i++) { | 684 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); |
446 | pf->var = pp->args[i]; | 685 | if (tev->args == NULL) |
447 | pf->buf = &tmp[len]; | 686 | return -ENOMEM; |
448 | pf->len = MAX_PROBE_BUFFER - len; | 687 | for (i = 0; i < pf->pev->nargs; i++) { |
449 | find_variable(sp_die, pf); | 688 | pf->pvar = &pf->pev->args[i]; |
450 | len += strlen(pf->buf); | 689 | pf->tvar = &tev->args[i]; |
690 | ret = find_variable(sp_die, pf); | ||
691 | if (ret != 0) | ||
692 | return ret; | ||
451 | } | 693 | } |
452 | 694 | ||
453 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 695 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
454 | pf->fb_ops = NULL; | 696 | pf->fb_ops = NULL; |
455 | 697 | return 0; | |
456 | if (pp->found == MAX_PROBES) | ||
457 | die("Too many( > %d) probe point found.\n", MAX_PROBES); | ||
458 | |||
459 | pp->probes[pp->found] = strdup(tmp); | ||
460 | pp->found++; | ||
461 | } | 698 | } |
462 | 699 | ||
463 | /* Find probe point from its line number */ | 700 | /* Find probe point from its line number */ |
464 | static void find_probe_point_by_line(struct probe_finder *pf) | 701 | static int find_probe_point_by_line(struct probe_finder *pf) |
465 | { | 702 | { |
466 | Dwarf_Lines *lines; | 703 | Dwarf_Lines *lines; |
467 | Dwarf_Line *line; | 704 | Dwarf_Line *line; |
468 | size_t nlines, i; | 705 | size_t nlines, i; |
469 | Dwarf_Addr addr; | 706 | Dwarf_Addr addr; |
470 | int lineno; | 707 | int lineno; |
471 | int ret; | 708 | int ret = 0; |
472 | 709 | ||
473 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | 710 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { |
474 | DIE_IF(ret != 0); | 711 | pr_warning("No source lines found in this CU.\n"); |
712 | return -ENOENT; | ||
713 | } | ||
475 | 714 | ||
476 | for (i = 0; i < nlines; i++) { | 715 | for (i = 0; i < nlines && ret == 0; i++) { |
477 | line = dwarf_onesrcline(lines, i); | 716 | line = dwarf_onesrcline(lines, i); |
478 | dwarf_lineno(line, &lineno); | 717 | if (dwarf_lineno(line, &lineno) != 0 || |
479 | if (lineno != pf->lno) | 718 | lineno != pf->lno) |
480 | continue; | 719 | continue; |
481 | 720 | ||
482 | /* TODO: Get fileno from line, but how? */ | 721 | /* TODO: Get fileno from line, but how? */ |
483 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 722 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) |
484 | continue; | 723 | continue; |
485 | 724 | ||
486 | ret = dwarf_lineaddr(line, &addr); | 725 | if (dwarf_lineaddr(line, &addr) != 0) { |
487 | DIE_IF(ret != 0); | 726 | pr_warning("Failed to get the address of the line.\n"); |
727 | return -ENOENT; | ||
728 | } | ||
488 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | 729 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", |
489 | (int)i, lineno, (uintmax_t)addr); | 730 | (int)i, lineno, (uintmax_t)addr); |
490 | pf->addr = addr; | 731 | pf->addr = addr; |
491 | 732 | ||
492 | show_probe_point(NULL, pf); | 733 | ret = convert_probe_point(NULL, pf); |
493 | /* Continuing, because target line might be inlined. */ | 734 | /* Continuing, because target line might be inlined. */ |
494 | } | 735 | } |
736 | return ret; | ||
495 | } | 737 | } |
496 | 738 | ||
497 | /* Find lines which match lazy pattern */ | 739 | /* Find lines which match lazy pattern */ |
@@ -499,16 +741,27 @@ static int find_lazy_match_lines(struct list_head *head, | |||
499 | const char *fname, const char *pat) | 741 | const char *fname, const char *pat) |
500 | { | 742 | { |
501 | char *fbuf, *p1, *p2; | 743 | char *fbuf, *p1, *p2; |
502 | int fd, line, nlines = 0; | 744 | int fd, ret, line, nlines = 0; |
503 | struct stat st; | 745 | struct stat st; |
504 | 746 | ||
505 | fd = open(fname, O_RDONLY); | 747 | fd = open(fname, O_RDONLY); |
506 | if (fd < 0) | 748 | if (fd < 0) { |
507 | die("failed to open %s", fname); | 749 | pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); |
508 | DIE_IF(fstat(fd, &st) < 0); | 750 | return fd; |
509 | fbuf = malloc(st.st_size + 2); | 751 | } |
510 | DIE_IF(fbuf == NULL); | 752 | |
511 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | 753 | ret = fstat(fd, &st); |
754 | if (ret < 0) { | ||
755 | pr_warning("Failed to get the size of %s: %s\n", | ||
756 | fname, strerror(errno)); | ||
757 | return ret; | ||
758 | } | ||
759 | fbuf = xmalloc(st.st_size + 2); | ||
760 | ret = read(fd, fbuf, st.st_size); | ||
761 | if (ret < 0) { | ||
762 | pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); | ||
763 | return ret; | ||
764 | } | ||
512 | close(fd); | 765 | close(fd); |
513 | fbuf[st.st_size] = '\n'; /* Dummy line */ | 766 | fbuf[st.st_size] = '\n'; /* Dummy line */ |
514 | fbuf[st.st_size + 1] = '\0'; | 767 | fbuf[st.st_size + 1] = '\0'; |
@@ -528,7 +781,7 @@ static int find_lazy_match_lines(struct list_head *head, | |||
528 | } | 781 | } |
529 | 782 | ||
530 | /* Find probe points from lazy pattern */ | 783 | /* Find probe points from lazy pattern */ |
531 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 784 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
532 | { | 785 | { |
533 | Dwarf_Lines *lines; | 786 | Dwarf_Lines *lines; |
534 | Dwarf_Line *line; | 787 | Dwarf_Line *line; |
@@ -536,37 +789,46 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
536 | Dwarf_Addr addr; | 789 | Dwarf_Addr addr; |
537 | Dwarf_Die die_mem; | 790 | Dwarf_Die die_mem; |
538 | int lineno; | 791 | int lineno; |
539 | int ret; | 792 | int ret = 0; |
540 | 793 | ||
541 | if (list_empty(&pf->lcache)) { | 794 | if (list_empty(&pf->lcache)) { |
542 | /* Matching lazy line pattern */ | 795 | /* Matching lazy line pattern */ |
543 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | 796 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, |
544 | pf->pp->lazy_line); | 797 | pf->pev->point.lazy_line); |
545 | if (ret <= 0) | 798 | if (ret == 0) { |
546 | die("No matched lines found in %s.", pf->fname); | 799 | pr_debug("No matched lines found in %s.\n", pf->fname); |
800 | return 0; | ||
801 | } else if (ret < 0) | ||
802 | return ret; | ||
547 | } | 803 | } |
548 | 804 | ||
549 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | 805 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { |
550 | DIE_IF(ret != 0); | 806 | pr_warning("No source lines found in this CU.\n"); |
551 | for (i = 0; i < nlines; i++) { | 807 | return -ENOENT; |
808 | } | ||
809 | |||
810 | for (i = 0; i < nlines && ret >= 0; i++) { | ||
552 | line = dwarf_onesrcline(lines, i); | 811 | line = dwarf_onesrcline(lines, i); |
553 | 812 | ||
554 | dwarf_lineno(line, &lineno); | 813 | if (dwarf_lineno(line, &lineno) != 0 || |
555 | if (!line_list__has_line(&pf->lcache, lineno)) | 814 | !line_list__has_line(&pf->lcache, lineno)) |
556 | continue; | 815 | continue; |
557 | 816 | ||
558 | /* TODO: Get fileno from line, but how? */ | 817 | /* TODO: Get fileno from line, but how? */ |
559 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 818 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) |
560 | continue; | 819 | continue; |
561 | 820 | ||
562 | ret = dwarf_lineaddr(line, &addr); | 821 | if (dwarf_lineaddr(line, &addr) != 0) { |
563 | DIE_IF(ret != 0); | 822 | pr_debug("Failed to get the address of line %d.\n", |
823 | lineno); | ||
824 | continue; | ||
825 | } | ||
564 | if (sp_die) { | 826 | if (sp_die) { |
565 | /* Address filtering 1: does sp_die include addr? */ | 827 | /* Address filtering 1: does sp_die include addr? */ |
566 | if (!dwarf_haspc(sp_die, addr)) | 828 | if (!dwarf_haspc(sp_die, addr)) |
567 | continue; | 829 | continue; |
568 | /* Address filtering 2: No child include addr? */ | 830 | /* Address filtering 2: No child include addr? */ |
569 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | 831 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) |
570 | continue; | 832 | continue; |
571 | } | 833 | } |
572 | 834 | ||
@@ -574,27 +836,44 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
574 | (int)i, lineno, (unsigned long long)addr); | 836 | (int)i, lineno, (unsigned long long)addr); |
575 | pf->addr = addr; | 837 | pf->addr = addr; |
576 | 838 | ||
577 | show_probe_point(sp_die, pf); | 839 | ret = convert_probe_point(sp_die, pf); |
578 | /* Continuing, because target line might be inlined. */ | 840 | /* Continuing, because target line might be inlined. */ |
579 | } | 841 | } |
580 | /* TODO: deallocate lines, but how? */ | 842 | /* TODO: deallocate lines, but how? */ |
843 | return ret; | ||
581 | } | 844 | } |
582 | 845 | ||
846 | /* Callback parameter with return value */ | ||
847 | struct dwarf_callback_param { | ||
848 | void *data; | ||
849 | int retval; | ||
850 | }; | ||
851 | |||
583 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | 852 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) |
584 | { | 853 | { |
585 | struct probe_finder *pf = (struct probe_finder *)data; | 854 | struct dwarf_callback_param *param = data; |
586 | struct probe_point *pp = pf->pp; | 855 | struct probe_finder *pf = param->data; |
856 | struct perf_probe_point *pp = &pf->pev->point; | ||
857 | Dwarf_Addr addr; | ||
587 | 858 | ||
588 | if (pp->lazy_line) | 859 | if (pp->lazy_line) |
589 | find_probe_point_lazy(in_die, pf); | 860 | param->retval = find_probe_point_lazy(in_die, pf); |
590 | else { | 861 | else { |
591 | /* Get probe address */ | 862 | /* Get probe address */ |
592 | pf->addr = die_get_entrypc(in_die); | 863 | if (dwarf_entrypc(in_die, &addr) != 0) { |
864 | pr_warning("Failed to get entry pc of %s.\n", | ||
865 | dwarf_diename(in_die)); | ||
866 | param->retval = -ENOENT; | ||
867 | return DWARF_CB_ABORT; | ||
868 | } | ||
869 | pf->addr = addr; | ||
593 | pf->addr += pp->offset; | 870 | pf->addr += pp->offset; |
594 | pr_debug("found inline addr: 0x%jx\n", | 871 | pr_debug("found inline addr: 0x%jx\n", |
595 | (uintmax_t)pf->addr); | 872 | (uintmax_t)pf->addr); |
596 | 873 | ||
597 | show_probe_point(in_die, pf); | 874 | param->retval = convert_probe_point(in_die, pf); |
875 | if (param->retval < 0) | ||
876 | return DWARF_CB_ABORT; | ||
598 | } | 877 | } |
599 | 878 | ||
600 | return DWARF_CB_OK; | 879 | return DWARF_CB_OK; |
@@ -603,59 +882,88 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | |||
603 | /* Search function from function name */ | 882 | /* Search function from function name */ |
604 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | 883 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
605 | { | 884 | { |
606 | struct probe_finder *pf = (struct probe_finder *)data; | 885 | struct dwarf_callback_param *param = data; |
607 | struct probe_point *pp = pf->pp; | 886 | struct probe_finder *pf = param->data; |
887 | struct perf_probe_point *pp = &pf->pev->point; | ||
608 | 888 | ||
609 | /* Check tag and diename */ | 889 | /* Check tag and diename */ |
610 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 890 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
611 | die_compare_name(sp_die, pp->function) != 0) | 891 | die_compare_name(sp_die, pp->function) != 0) |
612 | return 0; | 892 | return DWARF_CB_OK; |
613 | 893 | ||
614 | pf->fname = dwarf_decl_file(sp_die); | 894 | pf->fname = dwarf_decl_file(sp_die); |
615 | if (pp->line) { /* Function relative line */ | 895 | if (pp->line) { /* Function relative line */ |
616 | dwarf_decl_line(sp_die, &pf->lno); | 896 | dwarf_decl_line(sp_die, &pf->lno); |
617 | pf->lno += pp->line; | 897 | pf->lno += pp->line; |
618 | find_probe_point_by_line(pf); | 898 | param->retval = find_probe_point_by_line(pf); |
619 | } else if (!dwarf_func_inline(sp_die)) { | 899 | } else if (!dwarf_func_inline(sp_die)) { |
620 | /* Real function */ | 900 | /* Real function */ |
621 | if (pp->lazy_line) | 901 | if (pp->lazy_line) |
622 | find_probe_point_lazy(sp_die, pf); | 902 | param->retval = find_probe_point_lazy(sp_die, pf); |
623 | else { | 903 | else { |
624 | pf->addr = die_get_entrypc(sp_die); | 904 | if (dwarf_entrypc(sp_die, &pf->addr) != 0) { |
905 | pr_warning("Failed to get entry pc of %s.\n", | ||
906 | dwarf_diename(sp_die)); | ||
907 | param->retval = -ENOENT; | ||
908 | return DWARF_CB_ABORT; | ||
909 | } | ||
625 | pf->addr += pp->offset; | 910 | pf->addr += pp->offset; |
626 | /* TODO: Check the address in this function */ | 911 | /* TODO: Check the address in this function */ |
627 | show_probe_point(sp_die, pf); | 912 | param->retval = convert_probe_point(sp_die, pf); |
628 | } | 913 | } |
629 | } else | 914 | } else { |
915 | struct dwarf_callback_param _param = {.data = (void *)pf, | ||
916 | .retval = 0}; | ||
630 | /* Inlined function: search instances */ | 917 | /* Inlined function: search instances */ |
631 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); | 918 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, |
919 | &_param); | ||
920 | param->retval = _param.retval; | ||
921 | } | ||
632 | 922 | ||
633 | return 1; /* Exit; no same symbol in this CU. */ | 923 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ |
634 | } | 924 | } |
635 | 925 | ||
636 | static void find_probe_point_by_func(struct probe_finder *pf) | 926 | static int find_probe_point_by_func(struct probe_finder *pf) |
637 | { | 927 | { |
638 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); | 928 | struct dwarf_callback_param _param = {.data = (void *)pf, |
929 | .retval = 0}; | ||
930 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); | ||
931 | return _param.retval; | ||
639 | } | 932 | } |
640 | 933 | ||
641 | /* Find a probe point */ | 934 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ |
642 | int find_probe_point(int fd, struct probe_point *pp) | 935 | int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, |
936 | struct kprobe_trace_event **tevs, int max_tevs) | ||
643 | { | 937 | { |
644 | struct probe_finder pf = {.pp = pp}; | 938 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; |
939 | struct perf_probe_point *pp = &pev->point; | ||
645 | Dwarf_Off off, noff; | 940 | Dwarf_Off off, noff; |
646 | size_t cuhl; | 941 | size_t cuhl; |
647 | Dwarf_Die *diep; | 942 | Dwarf_Die *diep; |
648 | Dwarf *dbg; | 943 | Dwarf *dbg; |
944 | int ret = 0; | ||
945 | |||
946 | pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); | ||
947 | if (pf.tevs == NULL) | ||
948 | return -ENOMEM; | ||
949 | *tevs = pf.tevs; | ||
950 | pf.ntevs = 0; | ||
649 | 951 | ||
650 | dbg = dwarf_begin(fd, DWARF_C_READ); | 952 | dbg = dwarf_begin(fd, DWARF_C_READ); |
651 | if (!dbg) | 953 | if (!dbg) { |
652 | return -ENOENT; | 954 | pr_warning("No dwarf info found in the vmlinux - " |
955 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
956 | return -EBADF; | ||
957 | } | ||
958 | |||
959 | /* Get the call frame information from this dwarf */ | ||
960 | pf.cfi = dwarf_getcfi(dbg); | ||
653 | 961 | ||
654 | pp->found = 0; | ||
655 | off = 0; | 962 | off = 0; |
656 | line_list__init(&pf.lcache); | 963 | line_list__init(&pf.lcache); |
657 | /* Loop on CUs (Compilation Unit) */ | 964 | /* Loop on CUs (Compilation Unit) */ |
658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 965 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && |
966 | ret >= 0) { | ||
659 | /* Get the DIE(Debugging Information Entry) of this CU */ | 967 | /* Get the DIE(Debugging Information Entry) of this CU */ |
660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); | 968 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); |
661 | if (!diep) | 969 | if (!diep) |
@@ -669,12 +977,12 @@ int find_probe_point(int fd, struct probe_point *pp) | |||
669 | 977 | ||
670 | if (!pp->file || pf.fname) { | 978 | if (!pp->file || pf.fname) { |
671 | if (pp->function) | 979 | if (pp->function) |
672 | find_probe_point_by_func(&pf); | 980 | ret = find_probe_point_by_func(&pf); |
673 | else if (pp->lazy_line) | 981 | else if (pp->lazy_line) |
674 | find_probe_point_lazy(NULL, &pf); | 982 | ret = find_probe_point_lazy(NULL, &pf); |
675 | else { | 983 | else { |
676 | pf.lno = pp->line; | 984 | pf.lno = pp->line; |
677 | find_probe_point_by_line(&pf); | 985 | ret = find_probe_point_by_line(&pf); |
678 | } | 986 | } |
679 | } | 987 | } |
680 | off = noff; | 988 | off = noff; |
@@ -682,41 +990,169 @@ int find_probe_point(int fd, struct probe_point *pp) | |||
682 | line_list__free(&pf.lcache); | 990 | line_list__free(&pf.lcache); |
683 | dwarf_end(dbg); | 991 | dwarf_end(dbg); |
684 | 992 | ||
685 | return pp->found; | 993 | return (ret < 0) ? ret : pf.ntevs; |
994 | } | ||
995 | |||
996 | /* Reverse search */ | ||
997 | int find_perf_probe_point(int fd, unsigned long addr, | ||
998 | struct perf_probe_point *ppt) | ||
999 | { | ||
1000 | Dwarf_Die cudie, spdie, indie; | ||
1001 | Dwarf *dbg; | ||
1002 | Dwarf_Line *line; | ||
1003 | Dwarf_Addr laddr, eaddr; | ||
1004 | const char *tmp; | ||
1005 | int lineno, ret = 0; | ||
1006 | bool found = false; | ||
1007 | |||
1008 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
1009 | if (!dbg) | ||
1010 | return -EBADF; | ||
1011 | |||
1012 | /* Find cu die */ | ||
1013 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { | ||
1014 | ret = -EINVAL; | ||
1015 | goto end; | ||
1016 | } | ||
1017 | |||
1018 | /* Find a corresponding line */ | ||
1019 | line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); | ||
1020 | if (line) { | ||
1021 | if (dwarf_lineaddr(line, &laddr) == 0 && | ||
1022 | (Dwarf_Addr)addr == laddr && | ||
1023 | dwarf_lineno(line, &lineno) == 0) { | ||
1024 | tmp = dwarf_linesrc(line, NULL, NULL); | ||
1025 | if (tmp) { | ||
1026 | ppt->line = lineno; | ||
1027 | ppt->file = strdup(tmp); | ||
1028 | if (ppt->file == NULL) { | ||
1029 | ret = -ENOMEM; | ||
1030 | goto end; | ||
1031 | } | ||
1032 | found = true; | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | /* Find a corresponding function */ | ||
1038 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { | ||
1039 | tmp = dwarf_diename(&spdie); | ||
1040 | if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) | ||
1041 | goto end; | ||
1042 | |||
1043 | if (ppt->line) { | ||
1044 | if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, | ||
1045 | &indie)) { | ||
1046 | /* addr in an inline function */ | ||
1047 | tmp = dwarf_diename(&indie); | ||
1048 | if (!tmp) | ||
1049 | goto end; | ||
1050 | ret = dwarf_decl_line(&indie, &lineno); | ||
1051 | } else { | ||
1052 | if (eaddr == addr) { /* Function entry */ | ||
1053 | lineno = ppt->line; | ||
1054 | ret = 0; | ||
1055 | } else | ||
1056 | ret = dwarf_decl_line(&spdie, &lineno); | ||
1057 | } | ||
1058 | if (ret == 0) { | ||
1059 | /* Make a relative line number */ | ||
1060 | ppt->line -= lineno; | ||
1061 | goto found; | ||
1062 | } | ||
1063 | } | ||
1064 | /* We don't have a line number, let's use offset */ | ||
1065 | ppt->offset = addr - (unsigned long)eaddr; | ||
1066 | found: | ||
1067 | ppt->function = strdup(tmp); | ||
1068 | if (ppt->function == NULL) { | ||
1069 | ret = -ENOMEM; | ||
1070 | goto end; | ||
1071 | } | ||
1072 | found = true; | ||
1073 | } | ||
1074 | |||
1075 | end: | ||
1076 | dwarf_end(dbg); | ||
1077 | if (ret >= 0) | ||
1078 | ret = found ? 1 : 0; | ||
1079 | return ret; | ||
1080 | } | ||
1081 | |||
1082 | /* Add a line and store the src path */ | ||
1083 | static int line_range_add_line(const char *src, unsigned int lineno, | ||
1084 | struct line_range *lr) | ||
1085 | { | ||
1086 | /* Copy real path */ | ||
1087 | if (!lr->path) { | ||
1088 | lr->path = strdup(src); | ||
1089 | if (lr->path == NULL) | ||
1090 | return -ENOMEM; | ||
1091 | } | ||
1092 | return line_list__add_line(&lr->line_list, lineno); | ||
1093 | } | ||
1094 | |||
1095 | /* Search function declaration lines */ | ||
1096 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | ||
1097 | { | ||
1098 | struct dwarf_callback_param *param = data; | ||
1099 | struct line_finder *lf = param->data; | ||
1100 | const char *src; | ||
1101 | int lineno; | ||
1102 | |||
1103 | src = dwarf_decl_file(sp_die); | ||
1104 | if (src && strtailcmp(src, lf->fname) != 0) | ||
1105 | return DWARF_CB_OK; | ||
1106 | |||
1107 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | ||
1108 | (lf->lno_s > lineno || lf->lno_e < lineno)) | ||
1109 | return DWARF_CB_OK; | ||
1110 | |||
1111 | param->retval = line_range_add_line(src, lineno, lf->lr); | ||
1112 | if (param->retval < 0) | ||
1113 | return DWARF_CB_ABORT; | ||
1114 | return DWARF_CB_OK; | ||
1115 | } | ||
1116 | |||
1117 | static int find_line_range_func_decl_lines(struct line_finder *lf) | ||
1118 | { | ||
1119 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | ||
1120 | dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); | ||
1121 | return param.retval; | ||
686 | } | 1122 | } |
687 | 1123 | ||
688 | /* Find line range from its line number */ | 1124 | /* Find line range from its line number */ |
689 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1125 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
690 | { | 1126 | { |
691 | Dwarf_Lines *lines; | 1127 | Dwarf_Lines *lines; |
692 | Dwarf_Line *line; | 1128 | Dwarf_Line *line; |
693 | size_t nlines, i; | 1129 | size_t nlines, i; |
694 | Dwarf_Addr addr; | 1130 | Dwarf_Addr addr; |
695 | int lineno; | 1131 | int lineno, ret = 0; |
696 | int ret; | ||
697 | const char *src; | 1132 | const char *src; |
698 | Dwarf_Die die_mem; | 1133 | Dwarf_Die die_mem; |
699 | 1134 | ||
700 | line_list__init(&lf->lr->line_list); | 1135 | line_list__init(&lf->lr->line_list); |
701 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); | 1136 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { |
702 | DIE_IF(ret != 0); | 1137 | pr_warning("No source lines found in this CU.\n"); |
1138 | return -ENOENT; | ||
1139 | } | ||
703 | 1140 | ||
1141 | /* Search probable lines on lines list */ | ||
704 | for (i = 0; i < nlines; i++) { | 1142 | for (i = 0; i < nlines; i++) { |
705 | line = dwarf_onesrcline(lines, i); | 1143 | line = dwarf_onesrcline(lines, i); |
706 | ret = dwarf_lineno(line, &lineno); | 1144 | if (dwarf_lineno(line, &lineno) != 0 || |
707 | DIE_IF(ret != 0); | 1145 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
708 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
709 | continue; | 1146 | continue; |
710 | 1147 | ||
711 | if (sp_die) { | 1148 | if (sp_die) { |
712 | /* Address filtering 1: does sp_die include addr? */ | 1149 | /* Address filtering 1: does sp_die include addr? */ |
713 | ret = dwarf_lineaddr(line, &addr); | 1150 | if (dwarf_lineaddr(line, &addr) != 0 || |
714 | DIE_IF(ret != 0); | 1151 | !dwarf_haspc(sp_die, addr)) |
715 | if (!dwarf_haspc(sp_die, addr)) | ||
716 | continue; | 1152 | continue; |
717 | 1153 | ||
718 | /* Address filtering 2: No child include addr? */ | 1154 | /* Address filtering 2: No child include addr? */ |
719 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | 1155 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) |
720 | continue; | 1156 | continue; |
721 | } | 1157 | } |
722 | 1158 | ||
@@ -725,30 +1161,49 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | |||
725 | if (strtailcmp(src, lf->fname) != 0) | 1161 | if (strtailcmp(src, lf->fname) != 0) |
726 | continue; | 1162 | continue; |
727 | 1163 | ||
728 | /* Copy real path */ | 1164 | ret = line_range_add_line(src, lineno, lf->lr); |
729 | if (!lf->lr->path) | 1165 | if (ret < 0) |
730 | lf->lr->path = strdup(src); | 1166 | return ret; |
731 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
732 | } | 1167 | } |
1168 | |||
1169 | /* | ||
1170 | * Dwarf lines doesn't include function declarations. We have to | ||
1171 | * check functions list or given function. | ||
1172 | */ | ||
1173 | if (sp_die) { | ||
1174 | src = dwarf_decl_file(sp_die); | ||
1175 | if (src && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
1176 | (lf->lno_s <= lineno && lf->lno_e >= lineno)) | ||
1177 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1178 | } else | ||
1179 | ret = find_line_range_func_decl_lines(lf); | ||
1180 | |||
733 | /* Update status */ | 1181 | /* Update status */ |
734 | if (!list_empty(&lf->lr->line_list)) | 1182 | if (ret >= 0) |
735 | lf->found = 1; | 1183 | if (!list_empty(&lf->lr->line_list)) |
1184 | ret = lf->found = 1; | ||
1185 | else | ||
1186 | ret = 0; /* Lines are not found */ | ||
736 | else { | 1187 | else { |
737 | free(lf->lr->path); | 1188 | free(lf->lr->path); |
738 | lf->lr->path = NULL; | 1189 | lf->lr->path = NULL; |
739 | } | 1190 | } |
1191 | return ret; | ||
740 | } | 1192 | } |
741 | 1193 | ||
742 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | 1194 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) |
743 | { | 1195 | { |
744 | find_line_range_by_line(in_die, (struct line_finder *)data); | 1196 | struct dwarf_callback_param *param = data; |
1197 | |||
1198 | param->retval = find_line_range_by_line(in_die, param->data); | ||
745 | return DWARF_CB_ABORT; /* No need to find other instances */ | 1199 | return DWARF_CB_ABORT; /* No need to find other instances */ |
746 | } | 1200 | } |
747 | 1201 | ||
748 | /* Search function from function name */ | 1202 | /* Search function from function name */ |
749 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | 1203 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
750 | { | 1204 | { |
751 | struct line_finder *lf = (struct line_finder *)data; | 1205 | struct dwarf_callback_param *param = data; |
1206 | struct line_finder *lf = param->data; | ||
752 | struct line_range *lr = lf->lr; | 1207 | struct line_range *lr = lf->lr; |
753 | 1208 | ||
754 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1209 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
@@ -757,44 +1212,55 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
757 | dwarf_decl_line(sp_die, &lr->offset); | 1212 | dwarf_decl_line(sp_die, &lr->offset); |
758 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | 1213 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
759 | lf->lno_s = lr->offset + lr->start; | 1214 | lf->lno_s = lr->offset + lr->start; |
760 | if (!lr->end) | 1215 | if (lf->lno_s < 0) /* Overflow */ |
1216 | lf->lno_s = INT_MAX; | ||
1217 | lf->lno_e = lr->offset + lr->end; | ||
1218 | if (lf->lno_e < 0) /* Overflow */ | ||
761 | lf->lno_e = INT_MAX; | 1219 | lf->lno_e = INT_MAX; |
762 | else | 1220 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); |
763 | lf->lno_e = lr->offset + lr->end; | ||
764 | lr->start = lf->lno_s; | 1221 | lr->start = lf->lno_s; |
765 | lr->end = lf->lno_e; | 1222 | lr->end = lf->lno_e; |
766 | if (dwarf_func_inline(sp_die)) | 1223 | if (dwarf_func_inline(sp_die)) { |
1224 | struct dwarf_callback_param _param; | ||
1225 | _param.data = (void *)lf; | ||
1226 | _param.retval = 0; | ||
767 | dwarf_func_inline_instances(sp_die, | 1227 | dwarf_func_inline_instances(sp_die, |
768 | line_range_inline_cb, lf); | 1228 | line_range_inline_cb, |
769 | else | 1229 | &_param); |
770 | find_line_range_by_line(sp_die, lf); | 1230 | param->retval = _param.retval; |
771 | return 1; | 1231 | } else |
1232 | param->retval = find_line_range_by_line(sp_die, lf); | ||
1233 | return DWARF_CB_ABORT; | ||
772 | } | 1234 | } |
773 | return 0; | 1235 | return DWARF_CB_OK; |
774 | } | 1236 | } |
775 | 1237 | ||
776 | static void find_line_range_by_func(struct line_finder *lf) | 1238 | static int find_line_range_by_func(struct line_finder *lf) |
777 | { | 1239 | { |
778 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); | 1240 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; |
1241 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); | ||
1242 | return param.retval; | ||
779 | } | 1243 | } |
780 | 1244 | ||
781 | int find_line_range(int fd, struct line_range *lr) | 1245 | int find_line_range(int fd, struct line_range *lr) |
782 | { | 1246 | { |
783 | struct line_finder lf = {.lr = lr, .found = 0}; | 1247 | struct line_finder lf = {.lr = lr, .found = 0}; |
784 | int ret; | 1248 | int ret = 0; |
785 | Dwarf_Off off = 0, noff; | 1249 | Dwarf_Off off = 0, noff; |
786 | size_t cuhl; | 1250 | size_t cuhl; |
787 | Dwarf_Die *diep; | 1251 | Dwarf_Die *diep; |
788 | Dwarf *dbg; | 1252 | Dwarf *dbg; |
789 | 1253 | ||
790 | dbg = dwarf_begin(fd, DWARF_C_READ); | 1254 | dbg = dwarf_begin(fd, DWARF_C_READ); |
791 | if (!dbg) | 1255 | if (!dbg) { |
792 | return -ENOENT; | 1256 | pr_warning("No dwarf info found in the vmlinux - " |
1257 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1258 | return -EBADF; | ||
1259 | } | ||
793 | 1260 | ||
794 | /* Loop on CUs (Compilation Unit) */ | 1261 | /* Loop on CUs (Compilation Unit) */ |
795 | while (!lf.found) { | 1262 | while (!lf.found && ret >= 0) { |
796 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); | 1263 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) |
797 | if (ret != 0) | ||
798 | break; | 1264 | break; |
799 | 1265 | ||
800 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1266 | /* Get the DIE(Debugging Information Entry) of this CU */ |
@@ -810,20 +1276,18 @@ int find_line_range(int fd, struct line_range *lr) | |||
810 | 1276 | ||
811 | if (!lr->file || lf.fname) { | 1277 | if (!lr->file || lf.fname) { |
812 | if (lr->function) | 1278 | if (lr->function) |
813 | find_line_range_by_func(&lf); | 1279 | ret = find_line_range_by_func(&lf); |
814 | else { | 1280 | else { |
815 | lf.lno_s = lr->start; | 1281 | lf.lno_s = lr->start; |
816 | if (!lr->end) | 1282 | lf.lno_e = lr->end; |
817 | lf.lno_e = INT_MAX; | 1283 | ret = find_line_range_by_line(NULL, &lf); |
818 | else | ||
819 | lf.lno_e = lr->end; | ||
820 | find_line_range_by_line(NULL, &lf); | ||
821 | } | 1284 | } |
822 | } | 1285 | } |
823 | off = noff; | 1286 | off = noff; |
824 | } | 1287 | } |
825 | pr_debug("path: %lx\n", (unsigned long)lr->path); | 1288 | pr_debug("path: %lx\n", (unsigned long)lr->path); |
826 | dwarf_end(dbg); | 1289 | dwarf_end(dbg); |
827 | return lf.found; | 1290 | |
1291 | return (ret < 0) ? ret : lf.found; | ||
828 | } | 1292 | } |
829 | 1293 | ||