diff options
Diffstat (limited to 'tools/perf')
| -rw-r--r-- | tools/perf/util/probe-finder.c | 321 |
1 files changed, 167 insertions, 154 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab83b6ac5d65..508c017f566a 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -458,6 +458,124 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | |||
| 458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
| 459 | } | 459 | } |
| 460 | 460 | ||
| 461 | /* Walker on lines (Note: line number will not be sorted) */ | ||
| 462 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
| 463 | Dwarf_Addr addr, void *data); | ||
| 464 | |||
| 465 | struct __line_walk_param { | ||
| 466 | line_walk_handler_t handler; | ||
| 467 | void *data; | ||
| 468 | int retval; | ||
| 469 | }; | ||
| 470 | |||
| 471 | /* Walk on decl lines in given DIE */ | ||
| 472 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
| 473 | line_walk_handler_t handler, void *data) | ||
| 474 | { | ||
| 475 | const char *fname; | ||
| 476 | Dwarf_Addr addr; | ||
| 477 | int lineno, ret = 0; | ||
| 478 | |||
| 479 | /* Handle function declaration line */ | ||
| 480 | fname = dwarf_decl_file(sp_die); | ||
| 481 | if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
| 482 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
| 483 | ret = handler(fname, lineno, addr, data); | ||
| 484 | } | ||
| 485 | |||
| 486 | return ret; | ||
| 487 | } | ||
| 488 | |||
| 489 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
| 490 | { | ||
| 491 | struct __line_walk_param *lw = data; | ||
| 492 | |||
| 493 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
| 494 | if (lw->retval != 0) | ||
| 495 | return DWARF_CB_ABORT; | ||
| 496 | |||
| 497 | return DWARF_CB_OK; | ||
| 498 | } | ||
| 499 | |||
| 500 | /* | ||
| 501 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
| 502 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
| 503 | */ | ||
| 504 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
| 505 | void *data) | ||
| 506 | { | ||
| 507 | Dwarf_Lines *lines; | ||
| 508 | Dwarf_Line *line; | ||
| 509 | Dwarf_Addr addr; | ||
| 510 | const char *fname; | ||
| 511 | int lineno, ret = 0; | ||
| 512 | Dwarf_Die die_mem, *cu_die; | ||
| 513 | size_t nlines, i; | ||
| 514 | |||
| 515 | /* Get the CU die */ | ||
| 516 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
| 517 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
| 518 | else | ||
| 519 | cu_die = pdie; | ||
| 520 | if (!cu_die) { | ||
| 521 | pr_debug2("Failed to get CU from subprogram\n"); | ||
| 522 | return -EINVAL; | ||
| 523 | } | ||
| 524 | |||
| 525 | /* Get lines list in the CU */ | ||
| 526 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
| 527 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
| 528 | return -ENOENT; | ||
| 529 | } | ||
| 530 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
| 531 | |||
| 532 | /* Walk on the lines on lines list */ | ||
| 533 | for (i = 0; i < nlines; i++) { | ||
| 534 | line = dwarf_onesrcline(lines, i); | ||
| 535 | if (line == NULL || | ||
| 536 | dwarf_lineno(line, &lineno) != 0 || | ||
| 537 | dwarf_lineaddr(line, &addr) != 0) { | ||
| 538 | pr_debug2("Failed to get line info. " | ||
| 539 | "Possible error in debuginfo.\n"); | ||
| 540 | continue; | ||
| 541 | } | ||
| 542 | /* Filter lines based on address */ | ||
| 543 | if (pdie != cu_die) | ||
| 544 | /* | ||
| 545 | * Address filtering | ||
| 546 | * The line is included in given function, and | ||
| 547 | * no inline block includes it. | ||
| 548 | */ | ||
| 549 | if (!dwarf_haspc(pdie, addr) || | ||
| 550 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
| 551 | continue; | ||
| 552 | /* Get source line */ | ||
| 553 | fname = dwarf_linesrc(line, NULL, NULL); | ||
| 554 | |||
| 555 | ret = handler(fname, lineno, addr, data); | ||
| 556 | if (ret != 0) | ||
| 557 | return ret; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* | ||
| 561 | * Dwarf lines doesn't include function declarations and inlined | ||
| 562 | * subroutines. We have to check functions list or given function. | ||
| 563 | */ | ||
| 564 | if (pdie != cu_die) | ||
| 565 | ret = __die_walk_funclines(pdie, handler, data); | ||
| 566 | else { | ||
| 567 | struct __line_walk_param param = { | ||
| 568 | .handler = handler, | ||
| 569 | .data = data, | ||
| 570 | .retval = 0, | ||
| 571 | }; | ||
| 572 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
| 573 | ret = param.retval; | ||
| 574 | } | ||
| 575 | |||
| 576 | return ret; | ||
| 577 | } | ||
| 578 | |||
| 461 | struct __find_variable_param { | 579 | struct __find_variable_param { |
| 462 | const char *name; | 580 | const char *name; |
| 463 | Dwarf_Addr addr; | 581 | Dwarf_Addr addr; |
| @@ -1050,43 +1168,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1050 | return ret; | 1168 | return ret; |
| 1051 | } | 1169 | } |
| 1052 | 1170 | ||
| 1053 | /* Find probe point from its line number */ | 1171 | static int probe_point_line_walker(const char *fname, int lineno, |
| 1054 | static int find_probe_point_by_line(struct probe_finder *pf) | 1172 | Dwarf_Addr addr, void *data) |
| 1055 | { | 1173 | { |
| 1056 | Dwarf_Lines *lines; | 1174 | struct probe_finder *pf = data; |
| 1057 | Dwarf_Line *line; | 1175 | int ret; |
| 1058 | size_t nlines, i; | ||
| 1059 | Dwarf_Addr addr; | ||
| 1060 | int lineno; | ||
| 1061 | int ret = 0; | ||
| 1062 | 1176 | ||
| 1063 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1177 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
| 1064 | pr_warning("No source lines found.\n"); | 1178 | return 0; |
| 1065 | return -ENOENT; | ||
| 1066 | } | ||
| 1067 | 1179 | ||
| 1068 | for (i = 0; i < nlines && ret == 0; i++) { | 1180 | pf->addr = addr; |
| 1069 | line = dwarf_onesrcline(lines, i); | 1181 | ret = call_probe_finder(NULL, pf); |
| 1070 | if (dwarf_lineno(line, &lineno) != 0 || | ||
| 1071 | lineno != pf->lno) | ||
| 1072 | continue; | ||
| 1073 | 1182 | ||
| 1074 | /* TODO: Get fileno from line, but how? */ | 1183 | /* Continue if no error, because the line will be in inline function */ |
| 1075 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 1184 | return ret < 0 ?: 0; |
| 1076 | continue; | 1185 | } |
| 1077 | |||
| 1078 | if (dwarf_lineaddr(line, &addr) != 0) { | ||
| 1079 | pr_warning("Failed to get the address of the line.\n"); | ||
| 1080 | return -ENOENT; | ||
| 1081 | } | ||
| 1082 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
| 1083 | (int)i, lineno, (uintmax_t)addr); | ||
| 1084 | pf->addr = addr; | ||
| 1085 | 1186 | ||
| 1086 | ret = call_probe_finder(NULL, pf); | 1187 | /* Find probe point from its line number */ |
| 1087 | /* Continuing, because target line might be inlined. */ | 1188 | static int find_probe_point_by_line(struct probe_finder *pf) |
| 1088 | } | 1189 | { |
| 1089 | return ret; | 1190 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
| 1090 | } | 1191 | } |
| 1091 | 1192 | ||
| 1092 | /* Find lines which match lazy pattern */ | 1193 | /* Find lines which match lazy pattern */ |
| @@ -1140,15 +1241,31 @@ out_close: | |||
| 1140 | return nlines; | 1241 | return nlines; |
| 1141 | } | 1242 | } |
| 1142 | 1243 | ||
| 1244 | static int probe_point_lazy_walker(const char *fname, int lineno, | ||
| 1245 | Dwarf_Addr addr, void *data) | ||
| 1246 | { | ||
| 1247 | struct probe_finder *pf = data; | ||
| 1248 | int ret; | ||
| 1249 | |||
| 1250 | if (!line_list__has_line(&pf->lcache, lineno) || | ||
| 1251 | strtailcmp(fname, pf->fname) != 0) | ||
| 1252 | return 0; | ||
| 1253 | |||
| 1254 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | ||
| 1255 | lineno, (unsigned long long)addr); | ||
| 1256 | pf->addr = addr; | ||
| 1257 | ret = call_probe_finder(NULL, pf); | ||
| 1258 | |||
| 1259 | /* | ||
| 1260 | * Continue if no error, because the lazy pattern will match | ||
| 1261 | * to other lines | ||
| 1262 | */ | ||
| 1263 | return ret < 0 ?: 0; | ||
| 1264 | } | ||
| 1265 | |||
| 1143 | /* Find probe points from lazy pattern */ | 1266 | /* Find probe points from lazy pattern */ |
| 1144 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 1267 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
| 1145 | { | 1268 | { |
| 1146 | Dwarf_Lines *lines; | ||
| 1147 | Dwarf_Line *line; | ||
| 1148 | size_t nlines, i; | ||
| 1149 | Dwarf_Addr addr; | ||
| 1150 | Dwarf_Die die_mem; | ||
| 1151 | int lineno; | ||
| 1152 | int ret = 0; | 1269 | int ret = 0; |
| 1153 | 1270 | ||
| 1154 | if (list_empty(&pf->lcache)) { | 1271 | if (list_empty(&pf->lcache)) { |
| @@ -1162,45 +1279,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1162 | return ret; | 1279 | return ret; |
| 1163 | } | 1280 | } |
| 1164 | 1281 | ||
| 1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1282 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
| 1166 | pr_warning("No source lines found.\n"); | ||
| 1167 | return -ENOENT; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | for (i = 0; i < nlines && ret >= 0; i++) { | ||
| 1171 | line = dwarf_onesrcline(lines, i); | ||
| 1172 | |||
| 1173 | if (dwarf_lineno(line, &lineno) != 0 || | ||
| 1174 | !line_list__has_line(&pf->lcache, lineno)) | ||
| 1175 | continue; | ||
| 1176 | |||
| 1177 | /* TODO: Get fileno from line, but how? */ | ||
| 1178 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
| 1179 | continue; | ||
| 1180 | |||
| 1181 | if (dwarf_lineaddr(line, &addr) != 0) { | ||
| 1182 | pr_debug("Failed to get the address of line %d.\n", | ||
| 1183 | lineno); | ||
| 1184 | continue; | ||
| 1185 | } | ||
| 1186 | if (sp_die) { | ||
| 1187 | /* Address filtering 1: does sp_die include addr? */ | ||
| 1188 | if (!dwarf_haspc(sp_die, addr)) | ||
| 1189 | continue; | ||
| 1190 | /* Address filtering 2: No child include addr? */ | ||
| 1191 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
| 1192 | continue; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
| 1196 | (int)i, lineno, (unsigned long long)addr); | ||
| 1197 | pf->addr = addr; | ||
| 1198 | |||
| 1199 | ret = call_probe_finder(sp_die, pf); | ||
| 1200 | /* Continuing, because target line might be inlined. */ | ||
| 1201 | } | ||
| 1202 | /* TODO: deallocate lines, but how? */ | ||
| 1203 | return ret; | ||
| 1204 | } | 1283 | } |
| 1205 | 1284 | ||
| 1206 | /* Callback parameter with return value */ | 1285 | /* Callback parameter with return value */ |
| @@ -1644,91 +1723,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
| 1644 | return line_list__add_line(&lr->line_list, lineno); | 1723 | return line_list__add_line(&lr->line_list, lineno); |
| 1645 | } | 1724 | } |
| 1646 | 1725 | ||
| 1647 | /* Search function declaration lines */ | 1726 | static int line_range_walk_cb(const char *fname, int lineno, |
| 1648 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | 1727 | Dwarf_Addr addr __used, |
| 1728 | void *data) | ||
| 1649 | { | 1729 | { |
| 1650 | struct dwarf_callback_param *param = data; | 1730 | struct line_finder *lf = data; |
| 1651 | struct line_finder *lf = param->data; | ||
| 1652 | const char *src; | ||
| 1653 | int lineno; | ||
| 1654 | |||
| 1655 | src = dwarf_decl_file(sp_die); | ||
| 1656 | if (src && strtailcmp(src, lf->fname) != 0) | ||
| 1657 | return DWARF_CB_OK; | ||
| 1658 | 1731 | ||
| 1659 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | 1732 | if ((strtailcmp(fname, lf->fname) != 0) || |
| 1660 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1733 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
| 1661 | return DWARF_CB_OK; | 1734 | return 0; |
| 1662 | 1735 | ||
| 1663 | param->retval = line_range_add_line(src, lineno, lf->lr); | 1736 | if (line_range_add_line(fname, lineno, lf->lr) < 0) |
| 1664 | if (param->retval < 0) | 1737 | return -EINVAL; |
| 1665 | return DWARF_CB_ABORT; | ||
| 1666 | return DWARF_CB_OK; | ||
| 1667 | } | ||
| 1668 | 1738 | ||
| 1669 | static int find_line_range_func_decl_lines(struct line_finder *lf) | 1739 | return 0; |
| 1670 | { | ||
| 1671 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | ||
| 1672 | dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); | ||
| 1673 | return param.retval; | ||
| 1674 | } | 1740 | } |
| 1675 | 1741 | ||
| 1676 | /* Find line range from its line number */ | 1742 | /* Find line range from its line number */ |
| 1677 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1743 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
| 1678 | { | 1744 | { |
| 1679 | Dwarf_Lines *lines; | 1745 | int ret; |
| 1680 | Dwarf_Line *line; | ||
| 1681 | size_t nlines, i; | ||
| 1682 | Dwarf_Addr addr; | ||
| 1683 | int lineno, ret = 0; | ||
| 1684 | const char *src; | ||
| 1685 | Dwarf_Die die_mem; | ||
| 1686 | |||
| 1687 | line_list__init(&lf->lr->line_list); | ||
| 1688 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { | ||
| 1689 | pr_warning("No source lines found.\n"); | ||
| 1690 | return -ENOENT; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | /* Search probable lines on lines list */ | ||
| 1694 | for (i = 0; i < nlines; i++) { | ||
| 1695 | line = dwarf_onesrcline(lines, i); | ||
| 1696 | if (dwarf_lineno(line, &lineno) != 0 || | ||
| 1697 | (lf->lno_s > lineno || lf->lno_e < lineno)) | ||
| 1698 | continue; | ||
| 1699 | |||
| 1700 | if (sp_die) { | ||
| 1701 | /* Address filtering 1: does sp_die include addr? */ | ||
| 1702 | if (dwarf_lineaddr(line, &addr) != 0 || | ||
| 1703 | !dwarf_haspc(sp_die, addr)) | ||
| 1704 | continue; | ||
| 1705 | |||
| 1706 | /* Address filtering 2: No child include addr? */ | ||
| 1707 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
| 1708 | continue; | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | /* TODO: Get fileno from line, but how? */ | ||
| 1712 | src = dwarf_linesrc(line, NULL, NULL); | ||
| 1713 | if (strtailcmp(src, lf->fname) != 0) | ||
| 1714 | continue; | ||
| 1715 | |||
| 1716 | ret = line_range_add_line(src, lineno, lf->lr); | ||
| 1717 | if (ret < 0) | ||
| 1718 | return ret; | ||
| 1719 | } | ||
| 1720 | 1746 | ||
| 1721 | /* | 1747 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); |
| 1722 | * Dwarf lines doesn't include function declarations. We have to | ||
| 1723 | * check functions list or given function. | ||
| 1724 | */ | ||
| 1725 | if (sp_die) { | ||
| 1726 | src = dwarf_decl_file(sp_die); | ||
| 1727 | if (src && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
| 1728 | (lf->lno_s <= lineno && lf->lno_e >= lineno)) | ||
| 1729 | ret = line_range_add_line(src, lineno, lf->lr); | ||
| 1730 | } else | ||
| 1731 | ret = find_line_range_func_decl_lines(lf); | ||
| 1732 | 1748 | ||
| 1733 | /* Update status */ | 1749 | /* Update status */ |
| 1734 | if (ret >= 0) | 1750 | if (ret >= 0) |
| @@ -1758,9 +1774,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
| 1758 | struct line_finder *lf = param->data; | 1774 | struct line_finder *lf = param->data; |
| 1759 | struct line_range *lr = lf->lr; | 1775 | struct line_range *lr = lf->lr; |
| 1760 | 1776 | ||
| 1761 | pr_debug("find (%llx) %s\n", | ||
| 1762 | (unsigned long long)dwarf_dieoffset(sp_die), | ||
| 1763 | dwarf_diename(sp_die)); | ||
| 1764 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1777 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
| 1765 | die_compare_name(sp_die, lr->function)) { | 1778 | die_compare_name(sp_die, lr->function)) { |
| 1766 | lf->fname = dwarf_decl_file(sp_die); | 1779 | lf->fname = dwarf_decl_file(sp_die); |
