diff options
Diffstat (limited to 'tools/perf/util/probe-finder.c')
| -rw-r--r-- | tools/perf/util/probe-finder.c | 138 |
1 files changed, 79 insertions, 59 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index be0329394d56..f0692737ebf1 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -118,7 +118,6 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
| 118 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, | 118 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, |
| 119 | const char *path) | 119 | const char *path) |
| 120 | { | 120 | { |
| 121 | Dwfl_Module *mod; | ||
| 122 | int fd; | 121 | int fd; |
| 123 | 122 | ||
| 124 | fd = open(path, O_RDONLY); | 123 | fd = open(path, O_RDONLY); |
| @@ -129,11 +128,11 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self, | |||
| 129 | if (!self->dwfl) | 128 | if (!self->dwfl) |
| 130 | goto error; | 129 | goto error; |
| 131 | 130 | ||
| 132 | mod = dwfl_report_offline(self->dwfl, "", "", fd); | 131 | self->mod = dwfl_report_offline(self->dwfl, "", "", fd); |
| 133 | if (!mod) | 132 | if (!self->mod) |
| 134 | goto error; | 133 | goto error; |
| 135 | 134 | ||
| 136 | self->dbg = dwfl_module_getdwarf(mod, &self->bias); | 135 | self->dbg = dwfl_module_getdwarf(self->mod, &self->bias); |
| 137 | if (!self->dbg) | 136 | if (!self->dbg) |
| 138 | goto error; | 137 | goto error; |
| 139 | 138 | ||
| @@ -676,37 +675,42 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
| 676 | } | 675 | } |
| 677 | 676 | ||
| 678 | /* Convert subprogram DIE to trace point */ | 677 | /* Convert subprogram DIE to trace point */ |
| 679 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | 678 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, |
| 680 | bool retprobe, struct probe_trace_point *tp) | 679 | Dwarf_Addr paddr, bool retprobe, |
| 680 | struct probe_trace_point *tp) | ||
| 681 | { | 681 | { |
| 682 | Dwarf_Addr eaddr, highaddr; | 682 | Dwarf_Addr eaddr, highaddr; |
| 683 | const char *name; | 683 | GElf_Sym sym; |
| 684 | 684 | const char *symbol; | |
| 685 | /* Copy the name of probe point */ | 685 | |
| 686 | name = dwarf_diename(sp_die); | 686 | /* Verify the address is correct */ |
| 687 | if (name) { | 687 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { |
| 688 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { | 688 | pr_warning("Failed to get entry address of %s\n", |
| 689 | pr_warning("Failed to get entry address of %s\n", | 689 | dwarf_diename(sp_die)); |
| 690 | dwarf_diename(sp_die)); | 690 | return -ENOENT; |
| 691 | return -ENOENT; | 691 | } |
| 692 | } | 692 | if (dwarf_highpc(sp_die, &highaddr) != 0) { |
| 693 | if (dwarf_highpc(sp_die, &highaddr) != 0) { | 693 | pr_warning("Failed to get end address of %s\n", |
| 694 | pr_warning("Failed to get end address of %s\n", | 694 | dwarf_diename(sp_die)); |
| 695 | dwarf_diename(sp_die)); | 695 | return -ENOENT; |
| 696 | return -ENOENT; | 696 | } |
| 697 | } | 697 | if (paddr > highaddr) { |
| 698 | if (paddr > highaddr) { | 698 | pr_warning("Offset specified is greater than size of %s\n", |
| 699 | pr_warning("Offset specified is greater than size of %s\n", | 699 | dwarf_diename(sp_die)); |
| 700 | dwarf_diename(sp_die)); | 700 | return -EINVAL; |
| 701 | return -EINVAL; | 701 | } |
| 702 | } | 702 | |
| 703 | tp->symbol = strdup(name); | 703 | /* Get an appropriate symbol from symtab */ |
| 704 | if (tp->symbol == NULL) | 704 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); |
| 705 | return -ENOMEM; | 705 | if (!symbol) { |
| 706 | tp->offset = (unsigned long)(paddr - eaddr); | 706 | pr_warning("Failed to find symbol at 0x%lx\n", |
| 707 | } else | 707 | (unsigned long)paddr); |
| 708 | /* This function has no name. */ | 708 | return -ENOENT; |
| 709 | tp->offset = (unsigned long)paddr; | 709 | } |
| 710 | tp->offset = (unsigned long)(paddr - sym.st_value); | ||
| 711 | tp->symbol = strdup(symbol); | ||
| 712 | if (!tp->symbol) | ||
| 713 | return -ENOMEM; | ||
| 710 | 714 | ||
| 711 | /* Return probe must be on the head of a subprogram */ | 715 | /* Return probe must be on the head of a subprogram */ |
| 712 | if (retprobe) { | 716 | if (retprobe) { |
| @@ -734,7 +738,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
| 734 | } | 738 | } |
| 735 | 739 | ||
| 736 | /* If not a real subprogram, find a real one */ | 740 | /* If not a real subprogram, find a real one */ |
| 737 | if (dwarf_tag(sc_die) != DW_TAG_subprogram) { | 741 | if (!die_is_func_def(sc_die)) { |
| 738 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | 742 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { |
| 739 | pr_warning("Failed to find probe point in any " | 743 | pr_warning("Failed to find probe point in any " |
| 740 | "functions.\n"); | 744 | "functions.\n"); |
| @@ -980,12 +984,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
| 980 | struct dwarf_callback_param *param = data; | 984 | struct dwarf_callback_param *param = data; |
| 981 | struct probe_finder *pf = param->data; | 985 | struct probe_finder *pf = param->data; |
| 982 | struct perf_probe_point *pp = &pf->pev->point; | 986 | struct perf_probe_point *pp = &pf->pev->point; |
| 983 | Dwarf_Attribute attr; | ||
| 984 | 987 | ||
| 985 | /* Check tag and diename */ | 988 | /* Check tag and diename */ |
| 986 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 989 | if (!die_is_func_def(sp_die) || |
| 987 | !die_compare_name(sp_die, pp->function) || | 990 | !die_compare_name(sp_die, pp->function)) |
| 988 | dwarf_attr(sp_die, DW_AT_declaration, &attr)) | ||
| 989 | return DWARF_CB_OK; | 991 | return DWARF_CB_OK; |
| 990 | 992 | ||
| 991 | /* Check declared file */ | 993 | /* Check declared file */ |
| @@ -1151,7 +1153,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
| 1151 | tev = &tf->tevs[tf->ntevs++]; | 1153 | tev = &tf->tevs[tf->ntevs++]; |
| 1152 | 1154 | ||
| 1153 | /* Trace point should be converted from subprogram DIE */ | 1155 | /* Trace point should be converted from subprogram DIE */ |
| 1154 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, | 1156 | ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, |
| 1155 | pf->pev->point.retprobe, &tev->point); | 1157 | pf->pev->point.retprobe, &tev->point); |
| 1156 | if (ret < 0) | 1158 | if (ret < 0) |
| 1157 | return ret; | 1159 | return ret; |
| @@ -1183,7 +1185,7 @@ int debuginfo__find_trace_events(struct debuginfo *self, | |||
| 1183 | { | 1185 | { |
| 1184 | struct trace_event_finder tf = { | 1186 | struct trace_event_finder tf = { |
| 1185 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1187 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
| 1186 | .max_tevs = max_tevs}; | 1188 | .mod = self->mod, .max_tevs = max_tevs}; |
| 1187 | int ret; | 1189 | int ret; |
| 1188 | 1190 | ||
| 1189 | /* Allocate result tevs array */ | 1191 | /* Allocate result tevs array */ |
| @@ -1252,7 +1254,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
| 1252 | vl = &af->vls[af->nvls++]; | 1254 | vl = &af->vls[af->nvls++]; |
| 1253 | 1255 | ||
| 1254 | /* Trace point should be converted from subprogram DIE */ | 1256 | /* Trace point should be converted from subprogram DIE */ |
| 1255 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, | 1257 | ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, |
| 1256 | pf->pev->point.retprobe, &vl->point); | 1258 | pf->pev->point.retprobe, &vl->point); |
| 1257 | if (ret < 0) | 1259 | if (ret < 0) |
| 1258 | return ret; | 1260 | return ret; |
| @@ -1291,6 +1293,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, | |||
| 1291 | { | 1293 | { |
| 1292 | struct available_var_finder af = { | 1294 | struct available_var_finder af = { |
| 1293 | .pf = {.pev = pev, .callback = add_available_vars}, | 1295 | .pf = {.pev = pev, .callback = add_available_vars}, |
| 1296 | .mod = self->mod, | ||
| 1294 | .max_vls = max_vls, .externs = externs}; | 1297 | .max_vls = max_vls, .externs = externs}; |
| 1295 | int ret; | 1298 | int ret; |
| 1296 | 1299 | ||
| @@ -1324,8 +1327,8 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
| 1324 | struct perf_probe_point *ppt) | 1327 | struct perf_probe_point *ppt) |
| 1325 | { | 1328 | { |
| 1326 | Dwarf_Die cudie, spdie, indie; | 1329 | Dwarf_Die cudie, spdie, indie; |
| 1327 | Dwarf_Addr _addr, baseaddr; | 1330 | Dwarf_Addr _addr = 0, baseaddr = 0; |
| 1328 | const char *fname = NULL, *func = NULL, *tmp; | 1331 | const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; |
| 1329 | int baseline = 0, lineno = 0, ret = 0; | 1332 | int baseline = 0, lineno = 0, ret = 0; |
| 1330 | 1333 | ||
| 1331 | /* Adjust address with bias */ | 1334 | /* Adjust address with bias */ |
| @@ -1346,27 +1349,36 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
| 1346 | /* Find a corresponding function (name, baseline and baseaddr) */ | 1349 | /* Find a corresponding function (name, baseline and baseaddr) */ |
| 1347 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1350 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { |
| 1348 | /* Get function entry information */ | 1351 | /* Get function entry information */ |
| 1349 | tmp = dwarf_diename(&spdie); | 1352 | func = basefunc = dwarf_diename(&spdie); |
| 1350 | if (!tmp || | 1353 | if (!func || |
| 1351 | dwarf_entrypc(&spdie, &baseaddr) != 0 || | 1354 | dwarf_entrypc(&spdie, &baseaddr) != 0 || |
| 1352 | dwarf_decl_line(&spdie, &baseline) != 0) | 1355 | dwarf_decl_line(&spdie, &baseline) != 0) { |
| 1356 | lineno = 0; | ||
| 1353 | goto post; | 1357 | goto post; |
| 1354 | func = tmp; | 1358 | } |
| 1355 | 1359 | ||
| 1356 | if (addr == (unsigned long)baseaddr) | 1360 | fname = dwarf_decl_file(&spdie); |
| 1361 | if (addr == (unsigned long)baseaddr) { | ||
| 1357 | /* Function entry - Relative line number is 0 */ | 1362 | /* Function entry - Relative line number is 0 */ |
| 1358 | lineno = baseline; | 1363 | lineno = baseline; |
| 1359 | else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, | 1364 | goto post; |
| 1360 | &indie)) { | 1365 | } |
| 1366 | |||
| 1367 | /* Track down the inline functions step by step */ | ||
| 1368 | while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, | ||
| 1369 | &indie)) { | ||
| 1370 | /* There is an inline function */ | ||
| 1361 | if (dwarf_entrypc(&indie, &_addr) == 0 && | 1371 | if (dwarf_entrypc(&indie, &_addr) == 0 && |
| 1362 | _addr == addr) | 1372 | _addr == addr) { |
| 1363 | /* | 1373 | /* |
| 1364 | * addr is at an inline function entry. | 1374 | * addr is at an inline function entry. |
| 1365 | * In this case, lineno should be the call-site | 1375 | * In this case, lineno should be the call-site |
| 1366 | * line number. | 1376 | * line number. (overwrite lineinfo) |
| 1367 | */ | 1377 | */ |
| 1368 | lineno = die_get_call_lineno(&indie); | 1378 | lineno = die_get_call_lineno(&indie); |
| 1369 | else { | 1379 | fname = die_get_call_file(&indie); |
| 1380 | break; | ||
| 1381 | } else { | ||
| 1370 | /* | 1382 | /* |
| 1371 | * addr is in an inline function body. | 1383 | * addr is in an inline function body. |
| 1372 | * Since lineno points one of the lines | 1384 | * Since lineno points one of the lines |
| @@ -1374,19 +1386,27 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
| 1374 | * be the entry line of the inline function. | 1386 | * be the entry line of the inline function. |
| 1375 | */ | 1387 | */ |
| 1376 | tmp = dwarf_diename(&indie); | 1388 | tmp = dwarf_diename(&indie); |
| 1377 | if (tmp && | 1389 | if (!tmp || |
| 1378 | dwarf_decl_line(&spdie, &baseline) == 0) | 1390 | dwarf_decl_line(&indie, &baseline) != 0) |
| 1379 | func = tmp; | 1391 | break; |
| 1392 | func = tmp; | ||
| 1393 | spdie = indie; | ||
| 1380 | } | 1394 | } |
| 1381 | } | 1395 | } |
| 1396 | /* Verify the lineno and baseline are in a same file */ | ||
| 1397 | tmp = dwarf_decl_file(&spdie); | ||
| 1398 | if (!tmp || strcmp(tmp, fname) != 0) | ||
| 1399 | lineno = 0; | ||
| 1382 | } | 1400 | } |
| 1383 | 1401 | ||
| 1384 | post: | 1402 | post: |
| 1385 | /* Make a relative line number or an offset */ | 1403 | /* Make a relative line number or an offset */ |
| 1386 | if (lineno) | 1404 | if (lineno) |
| 1387 | ppt->line = lineno - baseline; | 1405 | ppt->line = lineno - baseline; |
| 1388 | else if (func) | 1406 | else if (basefunc) { |
| 1389 | ppt->offset = addr - (unsigned long)baseaddr; | 1407 | ppt->offset = addr - (unsigned long)baseaddr; |
| 1408 | func = basefunc; | ||
| 1409 | } | ||
| 1390 | 1410 | ||
| 1391 | /* Duplicate strings */ | 1411 | /* Duplicate strings */ |
| 1392 | if (func) { | 1412 | if (func) { |
| @@ -1474,7 +1494,7 @@ static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | |||
| 1474 | return 0; | 1494 | return 0; |
| 1475 | } | 1495 | } |
| 1476 | 1496 | ||
| 1477 | /* Search function from function name */ | 1497 | /* Search function definition from function name */ |
| 1478 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | 1498 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
| 1479 | { | 1499 | { |
| 1480 | struct dwarf_callback_param *param = data; | 1500 | struct dwarf_callback_param *param = data; |
| @@ -1485,7 +1505,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
| 1485 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) | 1505 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) |
| 1486 | return DWARF_CB_OK; | 1506 | return DWARF_CB_OK; |
| 1487 | 1507 | ||
| 1488 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1508 | if (die_is_func_def(sp_die) && |
| 1489 | die_compare_name(sp_die, lr->function)) { | 1509 | die_compare_name(sp_die, lr->function)) { |
| 1490 | lf->fname = dwarf_decl_file(sp_die); | 1510 | lf->fname = dwarf_decl_file(sp_die); |
| 1491 | dwarf_decl_line(sp_die, &lr->offset); | 1511 | dwarf_decl_line(sp_die, &lr->offset); |
