Skip to content

Commit

Permalink
perf symbols: Add symfs option for off-box analysis using specified tree
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Ahern authored and Arnaldo Carvalho de Melo committed Dec 21, 2010
1 parent eac23d1 commit ec5761e
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 19 deletions.
2 changes: 2 additions & 0 deletions tools/perf/Documentation/perf-diff.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ OPTIONS
--force::
Don't complain, do it.

--symfs=<directory>::
Look for files with symbols relative to this directory.

SEE ALSO
--------
Expand Down
3 changes: 3 additions & 0 deletions tools/perf/Documentation/perf-report.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ OPTIONS
--force::
Don't complain, do it.

--symfs=<directory>::
Look for files with symbols relative to this directory.

SEE ALSO
--------
linkperf:perf-stat[1]
2 changes: 2 additions & 0 deletions tools/perf/Documentation/perf-timechart.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ OPTIONS
--process::
Select the processes to display, by name or PID

--symfs=<directory>::
Look for files with symbols relative to this directory.

SEE ALSO
--------
Expand Down
2 changes: 2 additions & 0 deletions tools/perf/builtin-diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ static const struct option options[] = {
OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_END()
};

Expand Down
2 changes: 2 additions & 0 deletions tools/perf/builtin-report.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ static const struct option options[] = {
"columns '.' is reserved."),
OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
"Only display entries resolved to a symbol"),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_END()
};

Expand Down
2 changes: 2 additions & 0 deletions tools/perf/builtin-timechart.c
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,8 @@ static const struct option options[] = {
OPT_CALLBACK('p', "process", NULL, "process",
"process selector. Pass a pid or process name.",
parse_process),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_END()
};

Expand Down
14 changes: 11 additions & 3 deletions tools/perf/util/hist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
FILE *file;
int err = 0;
u64 len;
char symfs_filename[PATH_MAX];

if (filename) {
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
symbol_conf.symfs, filename);
}

if (filename == NULL) {
if (dso->has_build_id) {
Expand All @@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
return -ENOMEM;
}
goto fallback;
} else if (readlink(filename, command, sizeof(command)) < 0 ||
} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
strstr(command, "[kernel.kallsyms]") ||
access(filename, R_OK)) {
access(symfs_filename, R_OK)) {
free(filename);
fallback:
/*
Expand All @@ -1111,6 +1117,8 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
* DSO is the same as when 'perf record' ran.
*/
filename = dso->long_name;
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
symbol_conf.symfs, filename);
free_filename = false;
}

Expand All @@ -1137,7 +1145,7 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
"objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
map__rip_2objdump(map, sym->start),
map__rip_2objdump(map, sym->end),
filename, filename);
symfs_filename, filename);

pr_debug("Executing: %s\n", command);

Expand Down
72 changes: 56 additions & 16 deletions tools/perf/util/symbol.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct symbol_conf symbol_conf = {
.exclude_other = true,
.use_modules = true,
.try_vmlinux_path = true,
.symfs = "",
};

int dso__name_len(const struct dso *self)
Expand Down Expand Up @@ -839,8 +840,11 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
char sympltname[1024];
Elf *elf;
int nr = 0, symidx, fd, err = 0;
char name[PATH_MAX];

fd = open(self->long_name, O_RDONLY);
snprintf(name, sizeof(name), "%s%s",
symbol_conf.symfs, self->long_name);
fd = open(name, O_RDONLY);
if (fd < 0)
goto out;

Expand Down Expand Up @@ -1452,16 +1456,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
self->origin++) {
switch (self->origin) {
case DSO__ORIG_BUILD_ID_CACHE:
if (dso__build_id_filename(self, name, size) == NULL)
/* skip the locally configured cache if a symfs is given */
if (symbol_conf.symfs[0] ||
(dso__build_id_filename(self, name, size) == NULL)) {
continue;
}
break;
case DSO__ORIG_FEDORA:
snprintf(name, size, "/usr/lib/debug%s.debug",
self->long_name);
snprintf(name, size, "%s/usr/lib/debug%s.debug",
symbol_conf.symfs, self->long_name);
break;
case DSO__ORIG_UBUNTU:
snprintf(name, size, "/usr/lib/debug%s",
self->long_name);
snprintf(name, size, "%s/usr/lib/debug%s",
symbol_conf.symfs, self->long_name);
break;
case DSO__ORIG_BUILDID: {
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
Expand All @@ -1473,19 +1480,26 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
sizeof(self->build_id),
build_id_hex);
snprintf(name, size,
"/usr/lib/debug/.build-id/%.2s/%s.debug",
build_id_hex, build_id_hex + 2);
"%s/usr/lib/debug/.build-id/%.2s/%s.debug",
symbol_conf.symfs, build_id_hex, build_id_hex + 2);
}
break;
case DSO__ORIG_DSO:
snprintf(name, size, "%s", self->long_name);
snprintf(name, size, "%s%s",
symbol_conf.symfs, self->long_name);
break;
case DSO__ORIG_GUEST_KMODULE:
if (map->groups && map->groups->machine)
root_dir = map->groups->machine->root_dir;
else
root_dir = "";
snprintf(name, size, "%s%s", root_dir, self->long_name);
snprintf(name, size, "%s%s%s", symbol_conf.symfs,
root_dir, self->long_name);
break;

case DSO__ORIG_KMODULE:
snprintf(name, size, "%s%s", symbol_conf.symfs,
self->long_name);
break;

default:
Expand Down Expand Up @@ -1784,17 +1798,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
const char *vmlinux, symbol_filter_t filter)
{
int err = -1, fd;
char symfs_vmlinux[PATH_MAX];

fd = open(vmlinux, O_RDONLY);
snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
symbol_conf.symfs, vmlinux);
fd = open(symfs_vmlinux, O_RDONLY);
if (fd < 0)
return -1;

dso__set_loaded(self, map->type);
err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
close(fd);

if (err > 0)
pr_debug("Using %s for symbols\n", vmlinux);
pr_debug("Using %s for symbols\n", symfs_vmlinux);

return err;
}
Expand Down Expand Up @@ -1872,6 +1889,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
goto out_fixup;
}

/* do not try local files if a symfs was given */
if (symbol_conf.symfs[0] != 0)
return -1;

/*
* Say the kernel DSO was created when processing the build-id header table,
* we have a build-id, so check if it is the same as the running kernel,
Expand Down Expand Up @@ -2262,9 +2283,6 @@ static int vmlinux_path__init(void)
struct utsname uts;
char bf[PATH_MAX];

if (uname(&uts) < 0)
return -1;

vmlinux_path = malloc(sizeof(char *) * 5);
if (vmlinux_path == NULL)
return -1;
Expand All @@ -2277,6 +2295,14 @@ static int vmlinux_path__init(void)
if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
goto out_fail;
++vmlinux_path__nr_entries;

/* only try running kernel version if no symfs was given */
if (symbol_conf.symfs[0] != 0)
return 0;

if (uname(&uts) < 0)
return -1;

snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
Expand Down Expand Up @@ -2336,6 +2362,8 @@ static int setup_list(struct strlist **list, const char *list_str,

int symbol__init(void)
{
const char *symfs;

if (symbol_conf.initialized)
return 0;

Expand Down Expand Up @@ -2364,6 +2392,18 @@ int symbol__init(void)
symbol_conf.sym_list_str, "symbol") < 0)
goto out_free_comm_list;

/*
* A path to symbols of "/" is identical to ""
* reset here for simplicity.
*/
symfs = realpath(symbol_conf.symfs, NULL);
if (symfs == NULL)
symfs = symbol_conf.symfs;
if (strcmp(symfs, "/") == 0)
symbol_conf.symfs = "";
if (symfs != symbol_conf.symfs)
free((void *)symfs);

symbol_conf.initialized = true;
return 0;

Expand Down
1 change: 1 addition & 0 deletions tools/perf/util/symbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct symbol_conf {
struct strlist *dso_list,
*comm_list,
*sym_list;
const char *symfs;
};

extern struct symbol_conf symbol_conf;
Expand Down

0 comments on commit ec5761e

Please sign in to comment.