Commit ec5761eab318e50e69fcf8e63e9edaef5949c067
Committed by
Arnaldo Carvalho de Melo
1 parent
eac23d1c38
perf symbols: Add symfs option for off-box analysis using specified tree
The symfs argument allows analysis of perf.data file using a locally accessible filesystem tree with debug symbols - e.g., tree created during image builds, sshfs mount, loop mounted KVM disk images, USB keys, initrds, etc. Anything with an OS tree can be analyzed from anywhere without the need to populate a local data store with build-ids. Commiter notes: o Fixed up symfs="/" variants handling. o prefixed DSO__ORIG_GUEST_KMODULE case with symfs too, avoiding use of files outside the symfs directory. LKML-Reference: <1291926427-28846-1-git-send-email-daahern@cisco.com> Signed-off-by: David Ahern <daahern@cisco.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Showing 9 changed files with 81 additions and 19 deletions Side-by-side Diff
tools/perf/Documentation/perf-diff.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-timechart.txt
tools/perf/builtin-diff.c
... | ... | @@ -194,6 +194,8 @@ |
194 | 194 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
195 | 195 | "separator for columns, no spaces will be added between " |
196 | 196 | "columns '.' is reserved."), |
197 | + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | |
198 | + "Look for files with symbols relative to this directory"), | |
197 | 199 | OPT_END() |
198 | 200 | }; |
199 | 201 |
tools/perf/builtin-report.c
... | ... | @@ -483,6 +483,8 @@ |
483 | 483 | "columns '.' is reserved."), |
484 | 484 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, |
485 | 485 | "Only display entries resolved to a symbol"), |
486 | + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | |
487 | + "Look for files with symbols relative to this directory"), | |
486 | 488 | OPT_END() |
487 | 489 | }; |
488 | 490 |
tools/perf/builtin-timechart.c
... | ... | @@ -1022,6 +1022,8 @@ |
1022 | 1022 | OPT_CALLBACK('p', "process", NULL, "process", |
1023 | 1023 | "process selector. Pass a pid or process name.", |
1024 | 1024 | parse_process), |
1025 | + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | |
1026 | + "Look for files with symbols relative to this directory"), | |
1025 | 1027 | OPT_END() |
1026 | 1028 | }; |
1027 | 1029 |
tools/perf/util/hist.c
... | ... | @@ -1092,7 +1092,13 @@ |
1092 | 1092 | FILE *file; |
1093 | 1093 | int err = 0; |
1094 | 1094 | u64 len; |
1095 | + char symfs_filename[PATH_MAX]; | |
1095 | 1096 | |
1097 | + if (filename) { | |
1098 | + snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | |
1099 | + symbol_conf.symfs, filename); | |
1100 | + } | |
1101 | + | |
1096 | 1102 | if (filename == NULL) { |
1097 | 1103 | if (dso->has_build_id) { |
1098 | 1104 | pr_err("Can't annotate %s: not enough memory\n", |
1099 | 1105 | |
... | ... | @@ -1100,9 +1106,9 @@ |
1100 | 1106 | return -ENOMEM; |
1101 | 1107 | } |
1102 | 1108 | goto fallback; |
1103 | - } else if (readlink(filename, command, sizeof(command)) < 0 || | |
1109 | + } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | |
1104 | 1110 | strstr(command, "[kernel.kallsyms]") || |
1105 | - access(filename, R_OK)) { | |
1111 | + access(symfs_filename, R_OK)) { | |
1106 | 1112 | free(filename); |
1107 | 1113 | fallback: |
1108 | 1114 | /* |
... | ... | @@ -1111,6 +1117,8 @@ |
1111 | 1117 | * DSO is the same as when 'perf record' ran. |
1112 | 1118 | */ |
1113 | 1119 | filename = dso->long_name; |
1120 | + snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | |
1121 | + symbol_conf.symfs, filename); | |
1114 | 1122 | free_filename = false; |
1115 | 1123 | } |
1116 | 1124 | |
... | ... | @@ -1137,7 +1145,7 @@ |
1137 | 1145 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", |
1138 | 1146 | map__rip_2objdump(map, sym->start), |
1139 | 1147 | map__rip_2objdump(map, sym->end), |
1140 | - filename, filename); | |
1148 | + symfs_filename, filename); | |
1141 | 1149 | |
1142 | 1150 | pr_debug("Executing: %s\n", command); |
1143 | 1151 |
tools/perf/util/symbol.c
... | ... | @@ -41,6 +41,7 @@ |
41 | 41 | .exclude_other = true, |
42 | 42 | .use_modules = true, |
43 | 43 | .try_vmlinux_path = true, |
44 | + .symfs = "", | |
44 | 45 | }; |
45 | 46 | |
46 | 47 | int dso__name_len(const struct dso *self) |
47 | 48 | |
... | ... | @@ -839,8 +840,11 @@ |
839 | 840 | char sympltname[1024]; |
840 | 841 | Elf *elf; |
841 | 842 | int nr = 0, symidx, fd, err = 0; |
843 | + char name[PATH_MAX]; | |
842 | 844 | |
843 | - fd = open(self->long_name, O_RDONLY); | |
845 | + snprintf(name, sizeof(name), "%s%s", | |
846 | + symbol_conf.symfs, self->long_name); | |
847 | + fd = open(name, O_RDONLY); | |
844 | 848 | if (fd < 0) |
845 | 849 | goto out; |
846 | 850 | |
847 | 851 | |
848 | 852 | |
849 | 853 | |
... | ... | @@ -1452,16 +1456,19 @@ |
1452 | 1456 | self->origin++) { |
1453 | 1457 | switch (self->origin) { |
1454 | 1458 | case DSO__ORIG_BUILD_ID_CACHE: |
1455 | - if (dso__build_id_filename(self, name, size) == NULL) | |
1459 | + /* skip the locally configured cache if a symfs is given */ | |
1460 | + if (symbol_conf.symfs[0] || | |
1461 | + (dso__build_id_filename(self, name, size) == NULL)) { | |
1456 | 1462 | continue; |
1463 | + } | |
1457 | 1464 | break; |
1458 | 1465 | case DSO__ORIG_FEDORA: |
1459 | - snprintf(name, size, "/usr/lib/debug%s.debug", | |
1460 | - self->long_name); | |
1466 | + snprintf(name, size, "%s/usr/lib/debug%s.debug", | |
1467 | + symbol_conf.symfs, self->long_name); | |
1461 | 1468 | break; |
1462 | 1469 | case DSO__ORIG_UBUNTU: |
1463 | - snprintf(name, size, "/usr/lib/debug%s", | |
1464 | - self->long_name); | |
1470 | + snprintf(name, size, "%s/usr/lib/debug%s", | |
1471 | + symbol_conf.symfs, self->long_name); | |
1465 | 1472 | break; |
1466 | 1473 | case DSO__ORIG_BUILDID: { |
1467 | 1474 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1468 | 1475 | |
1469 | 1476 | |
1470 | 1477 | |
... | ... | @@ -1473,21 +1480,28 @@ |
1473 | 1480 | sizeof(self->build_id), |
1474 | 1481 | build_id_hex); |
1475 | 1482 | snprintf(name, size, |
1476 | - "/usr/lib/debug/.build-id/%.2s/%s.debug", | |
1477 | - build_id_hex, build_id_hex + 2); | |
1483 | + "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | |
1484 | + symbol_conf.symfs, build_id_hex, build_id_hex + 2); | |
1478 | 1485 | } |
1479 | 1486 | break; |
1480 | 1487 | case DSO__ORIG_DSO: |
1481 | - snprintf(name, size, "%s", self->long_name); | |
1488 | + snprintf(name, size, "%s%s", | |
1489 | + symbol_conf.symfs, self->long_name); | |
1482 | 1490 | break; |
1483 | 1491 | case DSO__ORIG_GUEST_KMODULE: |
1484 | 1492 | if (map->groups && map->groups->machine) |
1485 | 1493 | root_dir = map->groups->machine->root_dir; |
1486 | 1494 | else |
1487 | 1495 | root_dir = ""; |
1488 | - snprintf(name, size, "%s%s", root_dir, self->long_name); | |
1496 | + snprintf(name, size, "%s%s%s", symbol_conf.symfs, | |
1497 | + root_dir, self->long_name); | |
1489 | 1498 | break; |
1490 | 1499 | |
1500 | + case DSO__ORIG_KMODULE: | |
1501 | + snprintf(name, size, "%s%s", symbol_conf.symfs, | |
1502 | + self->long_name); | |
1503 | + break; | |
1504 | + | |
1491 | 1505 | default: |
1492 | 1506 | /* |
1493 | 1507 | * If we wanted a full symtab but no image had one, |
1494 | 1508 | |
1495 | 1509 | |
1496 | 1510 | |
... | ... | @@ -1784,17 +1798,20 @@ |
1784 | 1798 | const char *vmlinux, symbol_filter_t filter) |
1785 | 1799 | { |
1786 | 1800 | int err = -1, fd; |
1801 | + char symfs_vmlinux[PATH_MAX]; | |
1787 | 1802 | |
1788 | - fd = open(vmlinux, O_RDONLY); | |
1803 | + snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s", | |
1804 | + symbol_conf.symfs, vmlinux); | |
1805 | + fd = open(symfs_vmlinux, O_RDONLY); | |
1789 | 1806 | if (fd < 0) |
1790 | 1807 | return -1; |
1791 | 1808 | |
1792 | 1809 | dso__set_loaded(self, map->type); |
1793 | - err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0); | |
1810 | + err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0); | |
1794 | 1811 | close(fd); |
1795 | 1812 | |
1796 | 1813 | if (err > 0) |
1797 | - pr_debug("Using %s for symbols\n", vmlinux); | |
1814 | + pr_debug("Using %s for symbols\n", symfs_vmlinux); | |
1798 | 1815 | |
1799 | 1816 | return err; |
1800 | 1817 | } |
... | ... | @@ -1872,6 +1889,10 @@ |
1872 | 1889 | goto out_fixup; |
1873 | 1890 | } |
1874 | 1891 | |
1892 | + /* do not try local files if a symfs was given */ | |
1893 | + if (symbol_conf.symfs[0] != 0) | |
1894 | + return -1; | |
1895 | + | |
1875 | 1896 | /* |
1876 | 1897 | * Say the kernel DSO was created when processing the build-id header table, |
1877 | 1898 | * we have a build-id, so check if it is the same as the running kernel, |
... | ... | @@ -2262,9 +2283,6 @@ |
2262 | 2283 | struct utsname uts; |
2263 | 2284 | char bf[PATH_MAX]; |
2264 | 2285 | |
2265 | - if (uname(&uts) < 0) | |
2266 | - return -1; | |
2267 | - | |
2268 | 2286 | vmlinux_path = malloc(sizeof(char *) * 5); |
2269 | 2287 | if (vmlinux_path == NULL) |
2270 | 2288 | return -1; |
... | ... | @@ -2277,6 +2295,14 @@ |
2277 | 2295 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2278 | 2296 | goto out_fail; |
2279 | 2297 | ++vmlinux_path__nr_entries; |
2298 | + | |
2299 | + /* only try running kernel version if no symfs was given */ | |
2300 | + if (symbol_conf.symfs[0] != 0) | |
2301 | + return 0; | |
2302 | + | |
2303 | + if (uname(&uts) < 0) | |
2304 | + return -1; | |
2305 | + | |
2280 | 2306 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); |
2281 | 2307 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
2282 | 2308 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
... | ... | @@ -2336,6 +2362,8 @@ |
2336 | 2362 | |
2337 | 2363 | int symbol__init(void) |
2338 | 2364 | { |
2365 | + const char *symfs; | |
2366 | + | |
2339 | 2367 | if (symbol_conf.initialized) |
2340 | 2368 | return 0; |
2341 | 2369 | |
... | ... | @@ -2363,6 +2391,18 @@ |
2363 | 2391 | if (setup_list(&symbol_conf.sym_list, |
2364 | 2392 | symbol_conf.sym_list_str, "symbol") < 0) |
2365 | 2393 | goto out_free_comm_list; |
2394 | + | |
2395 | + /* | |
2396 | + * A path to symbols of "/" is identical to "" | |
2397 | + * reset here for simplicity. | |
2398 | + */ | |
2399 | + symfs = realpath(symbol_conf.symfs, NULL); | |
2400 | + if (symfs == NULL) | |
2401 | + symfs = symbol_conf.symfs; | |
2402 | + if (strcmp(symfs, "/") == 0) | |
2403 | + symbol_conf.symfs = ""; | |
2404 | + if (symfs != symbol_conf.symfs) | |
2405 | + free((void *)symfs); | |
2366 | 2406 | |
2367 | 2407 | symbol_conf.initialized = true; |
2368 | 2408 | return 0; |