Commit ec5761eab318e50e69fcf8e63e9edaef5949c067

Authored by David Ahern
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
... ... @@ -66,6 +66,8 @@
66 66 --force::
67 67 Don't complain, do it.
68 68  
  69 +--symfs=<directory>::
  70 + Look for files with symbols relative to this directory.
69 71  
70 72 SEE ALSO
71 73 --------
tools/perf/Documentation/perf-report.txt
... ... @@ -116,6 +116,9 @@
116 116 --force::
117 117 Don't complain, do it.
118 118  
  119 +--symfs=<directory>::
  120 + Look for files with symbols relative to this directory.
  121 +
119 122 SEE ALSO
120 123 --------
121 124 linkperf:perf-stat[1]
tools/perf/Documentation/perf-timechart.txt
... ... @@ -38,6 +38,8 @@
38 38 --process::
39 39 Select the processes to display, by name or PID
40 40  
  41 +--symfs=<directory>::
  42 + Look for files with symbols relative to this directory.
41 43  
42 44 SEE ALSO
43 45 --------
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;
tools/perf/util/symbol.h
... ... @@ -86,6 +86,7 @@
86 86 struct strlist *dso_list,
87 87 *comm_list,
88 88 *sym_list;
  89 + const char *symfs;
89 90 };
90 91  
91 92 extern struct symbol_conf symbol_conf;