diff options
-rw-r--r-- | tools/perf/util/probe-finder.c | 262 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 1 |
2 files changed, 86 insertions, 177 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c422472fe4d1..6305f344f382 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -38,13 +38,6 @@ | |||
38 | #include "probe-finder.h" | 38 | #include "probe-finder.h" |
39 | 39 | ||
40 | 40 | ||
41 | /* Dwarf_Die Linkage to parent Die */ | ||
42 | struct die_link { | ||
43 | struct die_link *parent; /* Parent die */ | ||
44 | Dwarf_Die die; /* Current die */ | ||
45 | }; | ||
46 | |||
47 | |||
48 | /* | 41 | /* |
49 | * Generic dwarf analysis helpers | 42 | * Generic dwarf analysis helpers |
50 | */ | 43 | */ |
@@ -177,26 +170,6 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
177 | return strcmp(tname, name); | 170 | return strcmp(tname, name); |
178 | } | 171 | } |
179 | 172 | ||
180 | /* Check the address is in the subprogram(function). */ | ||
181 | static bool die_within_subprogram(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
182 | size_t *offs) | ||
183 | { | ||
184 | Dwarf_Addr epc; | ||
185 | int ret; | ||
186 | |||
187 | ret = dwarf_haspc(sp_die, addr); | ||
188 | if (ret <= 0) | ||
189 | return false; | ||
190 | |||
191 | if (offs) { | ||
192 | ret = dwarf_entrypc(sp_die, &epc); | ||
193 | DIE_IF(ret == -1); | ||
194 | *offs = addr - epc; | ||
195 | } | ||
196 | |||
197 | return true; | ||
198 | } | ||
199 | |||
200 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | 173 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ |
201 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | 174 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) |
202 | { | 175 | { |
@@ -208,70 +181,34 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | |||
208 | return epc; | 181 | return epc; |
209 | } | 182 | } |
210 | 183 | ||
211 | /* Check if the abstract origin's address or not */ | 184 | /* Get a variable die */ |
212 | static bool die_compare_abstract_origin(Dwarf_Die *in_die, void *origin_addr) | 185 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, |
213 | { | 186 | Dwarf_Die *die_mem) |
214 | Dwarf_Attribute attr; | ||
215 | Dwarf_Die origin; | ||
216 | |||
217 | if (!dwarf_attr(in_die, DW_AT_abstract_origin, &attr)) | ||
218 | return false; | ||
219 | if (!dwarf_formref_die(&attr, &origin)) | ||
220 | return false; | ||
221 | |||
222 | return origin.addr == origin_addr; | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * Search a Die from Die tree. | ||
227 | * Note: cur_link->die should be deallocated in this function. | ||
228 | */ | ||
229 | static int __search_die_tree(struct die_link *cur_link, | ||
230 | int (*die_cb)(struct die_link *, void *), | ||
231 | void *data) | ||
232 | { | 187 | { |
233 | struct die_link new_link; | 188 | Dwarf_Die child_die; |
189 | int tag; | ||
234 | int ret; | 190 | int ret; |
235 | 191 | ||
236 | if (!die_cb) | 192 | ret = dwarf_child(sp_die, die_mem); |
237 | return 0; | 193 | if (ret != 0) |
238 | 194 | return NULL; | |
239 | /* Check current die */ | ||
240 | while (!(ret = die_cb(cur_link, data))) { | ||
241 | /* Check child die */ | ||
242 | ret = dwarf_child(&cur_link->die, &new_link.die); | ||
243 | if (ret == 0) { | ||
244 | new_link.parent = cur_link; | ||
245 | ret = __search_die_tree(&new_link, die_cb, data); | ||
246 | if (ret) | ||
247 | break; | ||
248 | } | ||
249 | 195 | ||
250 | /* Move to next sibling */ | 196 | do { |
251 | ret = dwarf_siblingof(&cur_link->die, &cur_link->die); | 197 | tag = dwarf_tag(die_mem); |
252 | if (ret != 0) | 198 | if ((tag == DW_TAG_formal_parameter || |
253 | return 0; | 199 | tag == DW_TAG_variable) && |
254 | } | 200 | (die_compare_name(die_mem, name) == 0)) |
255 | return ret; | 201 | return die_mem; |
256 | } | ||
257 | 202 | ||
258 | /* Search a die in its children's die tree */ | 203 | if (die_find_variable(die_mem, name, &child_die)) { |
259 | static int search_die_from_children(Dwarf_Die *parent_die, | 204 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); |
260 | int (*die_cb)(struct die_link *, void *), | 205 | return die_mem; |
261 | void *data) | 206 | } |
262 | { | 207 | } while (dwarf_siblingof(die_mem, die_mem) == 0); |
263 | struct die_link new_link; | ||
264 | int ret; | ||
265 | 208 | ||
266 | new_link.parent = NULL; | 209 | return NULL; |
267 | ret = dwarf_child(parent_die, &new_link.die); | ||
268 | if (ret == 0) | ||
269 | return __search_die_tree(&new_link, die_cb, data); | ||
270 | else | ||
271 | return 0; | ||
272 | } | 210 | } |
273 | 211 | ||
274 | |||
275 | /* | 212 | /* |
276 | * Probe finder related functions | 213 | * Probe finder related functions |
277 | */ | 214 | */ |
@@ -347,28 +284,13 @@ error: | |||
347 | " Perhaps, it has been optimized out.", pf->var); | 284 | " Perhaps, it has been optimized out.", pf->var); |
348 | } | 285 | } |
349 | 286 | ||
350 | static int variable_search_cb(struct die_link *dlink, void *data) | ||
351 | { | ||
352 | struct probe_finder *pf = (struct probe_finder *)data; | ||
353 | int tag; | ||
354 | |||
355 | tag = dwarf_tag(&dlink->die); | ||
356 | DIE_IF(tag < 0); | ||
357 | if ((tag == DW_TAG_formal_parameter || | ||
358 | tag == DW_TAG_variable) && | ||
359 | (die_compare_name(&dlink->die, pf->var) == 0)) { | ||
360 | show_variable(&dlink->die, pf); | ||
361 | return 1; | ||
362 | } | ||
363 | /* TODO: Support struct members and arrays */ | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | /* Find a variable in a subprogram die */ | 287 | /* Find a variable in a subprogram die */ |
368 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 288 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
369 | { | 289 | { |
370 | int ret; | 290 | int ret; |
291 | Dwarf_Die vr_die; | ||
371 | 292 | ||
293 | /* TODO: Support struct members and arrays */ | ||
372 | if (!is_c_varname(pf->var)) { | 294 | if (!is_c_varname(pf->var)) { |
373 | /* Output raw parameters */ | 295 | /* Output raw parameters */ |
374 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | 296 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); |
@@ -379,31 +301,42 @@ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
379 | 301 | ||
380 | pr_debug("Searching '%s' variable in context.\n", pf->var); | 302 | pr_debug("Searching '%s' variable in context.\n", pf->var); |
381 | /* Search child die for local variables and parameters. */ | 303 | /* Search child die for local variables and parameters. */ |
382 | ret = search_die_from_children(sp_die, variable_search_cb, pf); | 304 | if (!die_find_variable(sp_die, pf->var, &vr_die)) |
383 | if (!ret) | ||
384 | die("Failed to find '%s' in this function.", pf->var); | 305 | die("Failed to find '%s' in this function.", pf->var); |
306 | |||
307 | show_variable(&vr_die, pf); | ||
385 | } | 308 | } |
386 | 309 | ||
387 | /* Show a probe point to output buffer */ | 310 | /* Show a probe point to output buffer */ |
388 | static void show_probe_point(Dwarf_Die *sp_die, size_t offs, | 311 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
389 | struct probe_finder *pf) | ||
390 | { | 312 | { |
391 | struct probe_point *pp = pf->pp; | 313 | struct probe_point *pp = pf->pp; |
314 | Dwarf_Addr eaddr; | ||
315 | Dwarf_Die die_mem; | ||
392 | const char *name; | 316 | const char *name; |
393 | char tmp[MAX_PROBE_BUFFER]; | 317 | char tmp[MAX_PROBE_BUFFER]; |
394 | int ret, i, len; | 318 | int ret, i, len; |
395 | Dwarf_Attribute fb_attr; | 319 | Dwarf_Attribute fb_attr; |
396 | size_t nops; | 320 | size_t nops; |
397 | 321 | ||
322 | /* If no real subprogram, find a real one */ | ||
323 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
324 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
325 | pf->addr, &die_mem); | ||
326 | if (!sp_die) | ||
327 | die("Probe point is not found in subprograms."); | ||
328 | } | ||
329 | |||
398 | /* Output name of probe point */ | 330 | /* Output name of probe point */ |
399 | name = dwarf_diename(sp_die); | 331 | name = dwarf_diename(sp_die); |
400 | if (name) { | 332 | if (name) { |
401 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, | 333 | dwarf_entrypc(sp_die, &eaddr); |
402 | (unsigned int)offs); | 334 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, |
335 | (unsigned long)(pf->addr - eaddr)); | ||
403 | /* Copy the function name if possible */ | 336 | /* Copy the function name if possible */ |
404 | if (!pp->function) { | 337 | if (!pp->function) { |
405 | pp->function = strdup(name); | 338 | pp->function = strdup(name); |
406 | pp->offset = offs; | 339 | pp->offset = (size_t)(pf->addr - eaddr); |
407 | } | 340 | } |
408 | } else { | 341 | } else { |
409 | /* This function has no name. */ | 342 | /* This function has no name. */ |
@@ -450,10 +383,9 @@ static void find_probe_point_by_line(struct probe_finder *pf) | |||
450 | Dwarf_Lines *lines; | 383 | Dwarf_Lines *lines; |
451 | Dwarf_Line *line; | 384 | Dwarf_Line *line; |
452 | size_t nlines, i; | 385 | size_t nlines, i; |
453 | Dwarf_Addr addr, epc; | 386 | Dwarf_Addr addr; |
454 | int lineno; | 387 | int lineno; |
455 | int ret; | 388 | int ret; |
456 | Dwarf_Die *sp_die, die_mem; | ||
457 | 389 | ||
458 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | 390 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); |
459 | DIE_IF(ret != 0); | 391 | DIE_IF(ret != 0); |
@@ -474,77 +406,57 @@ static void find_probe_point_by_line(struct probe_finder *pf) | |||
474 | (int)i, lineno, (uintmax_t)addr); | 406 | (int)i, lineno, (uintmax_t)addr); |
475 | pf->addr = addr; | 407 | pf->addr = addr; |
476 | 408 | ||
477 | sp_die = die_get_real_subprogram(&pf->cu_die, addr, &die_mem); | 409 | show_probe_point(NULL, pf); |
478 | if (!sp_die) | ||
479 | die("Probe point is not found in subprograms."); | ||
480 | dwarf_entrypc(sp_die, &epc); | ||
481 | show_probe_point(sp_die, (size_t)(addr - epc), pf); | ||
482 | /* Continuing, because target line might be inlined. */ | 410 | /* Continuing, because target line might be inlined. */ |
483 | } | 411 | } |
484 | } | 412 | } |
485 | 413 | ||
414 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
415 | { | ||
416 | struct probe_finder *pf = (struct probe_finder *)data; | ||
417 | struct probe_point *pp = pf->pp; | ||
418 | |||
419 | /* Get probe address */ | ||
420 | pf->addr = die_get_entrypc(in_die); | ||
421 | pf->addr += pp->offset; | ||
422 | pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); | ||
423 | |||
424 | show_probe_point(in_die, pf); | ||
425 | return DWARF_CB_OK; | ||
426 | } | ||
486 | 427 | ||
487 | /* Search function from function name */ | 428 | /* Search function from function name */ |
488 | static int probe_point_search_cb(struct die_link *dlink, void *data) | 429 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
489 | { | 430 | { |
490 | struct probe_finder *pf = (struct probe_finder *)data; | 431 | struct probe_finder *pf = (struct probe_finder *)data; |
491 | struct probe_point *pp = pf->pp; | 432 | struct probe_point *pp = pf->pp; |
492 | struct die_link *lk; | ||
493 | size_t offs; | ||
494 | int tag; | ||
495 | int ret; | ||
496 | 433 | ||
497 | tag = dwarf_tag(&dlink->die); | 434 | /* Check tag and diename */ |
498 | if (tag == DW_TAG_subprogram) { | 435 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
499 | if (die_compare_name(&dlink->die, pp->function) == 0) { | 436 | die_compare_name(sp_die, pp->function) != 0) |
500 | if (pp->line) { /* Function relative line */ | 437 | return 0; |
501 | pf->fname = dwarf_decl_file(&dlink->die); | 438 | |
502 | dwarf_decl_line(&dlink->die, &pf->lno); | 439 | if (pp->line) { /* Function relative line */ |
503 | pf->lno += pp->line; | 440 | pf->fname = dwarf_decl_file(sp_die); |
504 | find_probe_point_by_line(pf); | 441 | dwarf_decl_line(sp_die, &pf->lno); |
505 | return 1; | 442 | pf->lno += pp->line; |
506 | } | 443 | find_probe_point_by_line(pf); |
507 | if (dwarf_func_inline(&dlink->die)) { | 444 | } else if (!dwarf_func_inline(sp_die)) { |
508 | /* Inlined function, save it. */ | 445 | /* Real function */ |
509 | pf->origin = dlink->die.addr; | 446 | pf->addr = die_get_entrypc(sp_die); |
510 | return 0; /* Continue to search */ | 447 | pf->addr += pp->offset; |
511 | } | 448 | /* TODO: Check the address in this function */ |
512 | /* Get probe address */ | 449 | show_probe_point(sp_die, pf); |
513 | pf->addr = die_get_entrypc(&dlink->die); | 450 | } else |
514 | pf->addr += pp->offset; | 451 | /* Inlined function: search instances */ |
515 | /* TODO: Check the address in this function */ | 452 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); |
516 | show_probe_point(&dlink->die, pp->offset, pf); | 453 | |
517 | return 1; /* Exit; no same symbol in this CU. */ | 454 | return 1; /* Exit; no same symbol in this CU. */ |
518 | } | ||
519 | } else if (tag == DW_TAG_inlined_subroutine && pf->origin) { | ||
520 | if (die_compare_abstract_origin(&dlink->die, pf->origin)) { | ||
521 | /* Get probe address */ | ||
522 | pf->addr = die_get_entrypc(&dlink->die); | ||
523 | pf->addr += pp->offset; | ||
524 | pr_debug("found inline addr: 0x%jx\n", | ||
525 | (uintmax_t)pf->addr); | ||
526 | /* Inlined function. Get a real subprogram */ | ||
527 | for (lk = dlink->parent; lk != NULL; lk = lk->parent) { | ||
528 | tag = dwarf_tag(&lk->die); | ||
529 | if (tag == DW_TAG_subprogram && | ||
530 | !dwarf_func_inline(&lk->die)) | ||
531 | goto found; | ||
532 | } | ||
533 | die("Failed to find real subprogram."); | ||
534 | found: | ||
535 | /* Get offset from subprogram */ | ||
536 | ret = die_within_subprogram(&lk->die, pf->addr, &offs); | ||
537 | DIE_IF(!ret); | ||
538 | show_probe_point(&lk->die, offs, pf); | ||
539 | /* Continue to search */ | ||
540 | } | ||
541 | } | ||
542 | return 0; | ||
543 | } | 455 | } |
544 | 456 | ||
545 | static void find_probe_point_by_func(struct probe_finder *pf) | 457 | static void find_probe_point_by_func(struct probe_finder *pf) |
546 | { | 458 | { |
547 | search_die_from_children(&pf->cu_die, probe_point_search_cb, pf); | 459 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); |
548 | } | 460 | } |
549 | 461 | ||
550 | /* Find a probe point */ | 462 | /* Find a probe point */ |
@@ -669,27 +581,25 @@ static void find_line_range_by_line(struct line_finder *lf) | |||
669 | } | 581 | } |
670 | 582 | ||
671 | /* Search function from function name */ | 583 | /* Search function from function name */ |
672 | static int line_range_search_cb(struct die_link *dlink, void *data) | 584 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
673 | { | 585 | { |
674 | struct line_finder *lf = (struct line_finder *)data; | 586 | struct line_finder *lf = (struct line_finder *)data; |
675 | struct line_range *lr = lf->lr; | 587 | struct line_range *lr = lf->lr; |
676 | int tag; | ||
677 | int ret; | 588 | int ret; |
678 | 589 | ||
679 | tag = dwarf_tag(&dlink->die); | 590 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
680 | if (tag == DW_TAG_subprogram && | 591 | die_compare_name(sp_die, lr->function) == 0) { |
681 | die_compare_name(&dlink->die, lr->function) == 0) { | ||
682 | /* Get the address range of this function */ | 592 | /* Get the address range of this function */ |
683 | ret = dwarf_highpc(&dlink->die, &lf->addr_e); | 593 | ret = dwarf_highpc(sp_die, &lf->addr_e); |
684 | if (ret == 0) | 594 | if (ret == 0) |
685 | ret = dwarf_lowpc(&dlink->die, &lf->addr_s); | 595 | ret = dwarf_lowpc(sp_die, &lf->addr_s); |
686 | if (ret != 0) { | 596 | if (ret != 0) { |
687 | lf->addr_s = 0; | 597 | lf->addr_s = 0; |
688 | lf->addr_e = 0; | 598 | lf->addr_e = 0; |
689 | } | 599 | } |
690 | 600 | ||
691 | lf->fname = dwarf_decl_file(&dlink->die); | 601 | lf->fname = dwarf_decl_file(sp_die); |
692 | dwarf_decl_line(&dlink->die, &lr->offset); | 602 | dwarf_decl_line(sp_die, &lr->offset); |
693 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | 603 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
694 | lf->lno_s = lr->offset + lr->start; | 604 | lf->lno_s = lr->offset + lr->start; |
695 | if (!lr->end) | 605 | if (!lr->end) |
@@ -706,7 +616,7 @@ static int line_range_search_cb(struct die_link *dlink, void *data) | |||
706 | 616 | ||
707 | static void find_line_range_by_func(struct line_finder *lf) | 617 | static void find_line_range_by_func(struct line_finder *lf) |
708 | { | 618 | { |
709 | search_die_from_children(&lf->cu_die, line_range_search_cb, lf); | 619 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); |
710 | } | 620 | } |
711 | 621 | ||
712 | int find_line_range(int fd, struct line_range *lr) | 622 | int find_line_range(int fd, struct line_range *lr) |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 9dd4a884d0e6..74525aeb30fe 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -66,7 +66,6 @@ struct probe_finder { | |||
66 | Dwarf_Addr addr; /* Address */ | 66 | Dwarf_Addr addr; /* Address */ |
67 | const char *fname; /* File name */ | 67 | const char *fname; /* File name */ |
68 | int lno; /* Line number */ | 68 | int lno; /* Line number */ |
69 | void *origin; /* Inline origin addr */ | ||
70 | Dwarf_Die cu_die; /* Current CU */ | 69 | Dwarf_Die cu_die; /* Current CU */ |
71 | 70 | ||
72 | /* For variable searching */ | 71 | /* For variable searching */ |