perf tools: Add layout support for --symfs option

Add support for parsing an optional layout parameter in the --symfs
command line option. The format is:

  --symfs <directory[,layout]>

Where layout can be:
  - 'hierarchy': matches full path (default)
  - 'flat': only matches base name

When debugging symbol files from a copy of the filesystem (e.g., from a
container or remote machine), the debug files are often stored in a
flat directory structure with only filenames, not the full original
paths. In this case, using 'flat' layout allows perf to find debug
symbols by matching only the filename rather than the full path.

For example, given a binary path like:
  /build/output/lib/foo.so

With 'perf report --symfs /debug/files,flat', perf will look for:
  /debug/files/foo.so

Instead of:
  /debug/files/build/output/lib/foo.so

This is particularly useful when:
- Extracting debug files from containers with different directory layouts
- Working with build systems that flatten directory structures

Signed-off-by: Changbin Du <changbin.du@huawei.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
Changbin Du 2026-03-09 17:44:12 +00:00 committed by Namhyung Kim
parent 86ff690f45
commit f182573e06
20 changed files with 103 additions and 35 deletions

View File

@ -110,8 +110,11 @@ include::itrace.txt[]
Interleave source code with assembly code. Enabled by default,
disable with --no-source.
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-M::
--disassembler-style=:: Set disassembler style for objdump.

View File

@ -81,8 +81,11 @@ OPTIONS
--force::
Don't do ownership validation.
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-b::
--baseline-only::

View File

@ -169,8 +169,11 @@ OPTIONS for 'perf kwork timehist'
--max-stack::
Maximum number of functions to display in backtrace, default 5.
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
--time::
Only analyze samples within given time window: <start>,<stop>. Times

View File

@ -50,6 +50,12 @@ OPTIONS
--source=PATH::
Specify path to kernel source.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-v::
--verbose::
Be more verbose (show parsed arguments, etc).

View File

@ -368,8 +368,11 @@ OPTIONS
--force::
Don't do ownership validation.
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-C::
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can

View File

@ -437,8 +437,11 @@ OPTIONS for 'perf sched timehist'
Show all scheduling events followed by a summary by thread with min,
max, and average run times (in sec) and relative stddev.
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-V::
--cpu-visual::

View File

@ -307,8 +307,11 @@ OPTIONS
--kallsyms=<file>::
kallsyms pathname
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-G::
--hide-call-graph::

View File

@ -53,8 +53,11 @@ TIMECHART OPTIONS
-f::
--force::
Don't complain, do it.
--symfs=<directory>::
Look for files with symbols relative to this directory.
--symfs=<directory[,layout]>::
Look for files with symbols relative to this directory. The optional
layout can be 'hierarchy' (default, matches full path) or 'flat'
(only matches base name). This is useful when debug files are stored
in a flat directory structure.
-n::
--proc-num::
Print task info for at least given number of tasks.

View File

@ -11,7 +11,7 @@ Search options using a keyword: perf report -h <keyword>
Use parent filter to see specific call path: perf report -p <regex>
List events using substring match: perf list <keyword>
To see list of saved events and attributes: perf evlist -v
Use --symfs <dir> if your symbol files are in non-standard locations
Use --symfs <dir>[,layout] if your symbol files are in non-standard locations.
To see callchains in a more compact form: perf report -g folded
To see call chains by final symbol taking CPU time (bottom up) use perf report -G
Show individual samples with: perf script

View File

@ -744,8 +744,7 @@ int cmd_annotate(int argc, const char **argv)
&annotate.group_set,
"Show event group information together"),
OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"),
OPT_CALLBACK(0, "symfs", NULL, "directory",
"Look for files with symbols relative to this directory",
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_BOOLEAN(0, "source", &annotate_opts.annotate_src,
"Interleave source code with assembly code (default)"),

View File

@ -1280,8 +1280,7 @@ static const struct option options[] = {
OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
OPT_CALLBACK(0, "symfs", NULL, "directory",
"Look for files with symbols relative to this directory",
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",

View File

@ -2423,8 +2423,8 @@ int cmd_kwork(int argc, const char **argv)
"Display call chains if present"),
OPT_UINTEGER(0, "max-stack", &kwork.max_stack,
"Maximum number of functions to display backtrace."),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_STRING(0, "time", &kwork.time_str, "str",
"Time span for analysis (start,stop)"),
OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu",

View File

@ -597,8 +597,8 @@ __cmd_probe(int argc, const char **argv)
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
"Enable kernel symbol demangling"),
OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_CALLBACK(0, "target-ns", NULL, "pid",
"target pid for namespace contexts", opt_set_target_ns),
OPT_BOOLEAN(0, "bootconfig", &probe_conf.bootconfig,

View File

@ -1416,8 +1416,7 @@ int cmd_report(int argc, const char **argv)
"columns '.' is reserved."),
OPT_BOOLEAN('U', "hide-unresolved", &symbol_conf.hide_unresolved,
"Only display entries resolved to a symbol"),
OPT_CALLBACK(0, "symfs", NULL, "directory",
"Look for files with symbols relative to this directory",
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
"list of cpus to profile"),

View File

@ -4879,8 +4879,8 @@ int cmd_sched(int argc, const char **argv)
"Display call chains if present (default on)"),
OPT_UINTEGER(0, "max-stack", &sched.max_stack,
"Maximum number of functions to display backtrace."),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_BOOLEAN('s', "summary", &sched.summary_only,
"Show only syscall summary with statistics"),
OPT_BOOLEAN('S', "with-summary", &sched.summary,

View File

@ -4078,8 +4078,7 @@ int cmd_script(int argc, const char **argv)
"file", "kallsyms pathname"),
OPT_BOOLEAN('G', "hide-call-graph", &no_callchain,
"When printing symbols do not display call chain"),
OPT_CALLBACK(0, "symfs", NULL, "directory",
"Look for files with symbols relative to this directory",
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_CALLBACK('F', "fields", NULL, "str",
"comma separated output fields prepend with 'type:'. "

View File

@ -1951,8 +1951,7 @@ int cmd_timechart(int argc, const char **argv)
OPT_CALLBACK('p', "process", NULL, "process",
"process selector. Pass a pid or process name.",
parse_process),
OPT_CALLBACK(0, "symfs", NULL, "directory",
"Look for files with symbols relative to this directory",
OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
symbol__config_symfs),
OPT_INTEGER('n', "proc-num", &tchart.proc_num,
"min. number of tasks to print"),

View File

@ -66,6 +66,7 @@ struct symbol_conf symbol_conf = {
.time_quantum = 100 * NSEC_PER_MSEC, /* 100ms */
.show_hist_headers = true,
.symfs = "",
.symfs_layout_flat = false,
.event_group = true,
.inline_name = true,
.res_sample = 0,
@ -2491,16 +2492,42 @@ int symbol__config_symfs(const struct option *opt __maybe_unused,
const char *dir, int unset __maybe_unused)
{
char *bf = NULL;
char *layout_str;
char *dir_copy;
int ret;
symbol_conf.symfs = strdup(dir);
if (symbol_conf.symfs == NULL)
return -ENOMEM;
layout_str = strrchr(dir, ',');
if (layout_str) {
size_t dir_len = layout_str - dir;
dir_copy = strndup(dir, dir_len);
if (dir_copy == NULL)
return -ENOMEM;
symbol_conf.symfs = dir_copy;
layout_str++;
if (!strcmp(layout_str, "flat"))
symbol_conf.symfs_layout_flat = true;
else if (!strcmp(layout_str, "hierarchy"))
symbol_conf.symfs_layout_flat = false;
else {
pr_err("Invalid layout: '%s', use 'hierarchy' or 'flat'\n",
layout_str);
free(dir_copy);
return -EINVAL;
}
} else {
symbol_conf.symfs = strdup(dir);
if (symbol_conf.symfs == NULL)
return -ENOMEM;
symbol_conf.symfs_layout_flat = false;
}
/* skip the locally configured cache if a symfs is given, and
* config buildid dir to symfs/.debug
*/
ret = asprintf(&bf, "%s/%s", dir, ".debug");
ret = asprintf(&bf, "%s/%s", symbol_conf.symfs, ".debug");
if (ret < 0)
return -ENOMEM;

View File

@ -9,6 +9,7 @@
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdio.h>
#include <errno.h>
#include "addr_location.h"
#include "path.h"
#include "symbol_conf.h"
@ -96,6 +97,18 @@ struct intlist;
static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
{
if (symbol_conf.symfs_layout_flat) {
char *path_copy = strdup(path);
char *base;
int ret;
if (!path_copy)
return -ENOMEM;
base = basename(path_copy);
ret = path__join(bf, size, symbol_conf.symfs, base);
free(path_copy);
return ret;
}
return path__join(bf, size, symbol_conf.symfs, path);
}
@ -169,6 +182,11 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
size_t symbol__fprintf(struct symbol *sym, FILE *fp);
bool symbol__restricted_filename(const char *filename,
const char *restricted_filename);
#define SYMFS_HELP "setup root directory which contains debug files:\n" \
"\t\t\t\t" "directory:\tLook for files with symbols relative to this directory.\n" \
"\t\t\t\t" "layout: \tLayout of files, 'hierarchy' matches full path (default), 'flat' only matches base name.\n"
int symbol__config_symfs(const struct option *opt __maybe_unused,
const char *dir, int unset __maybe_unused);

View File

@ -93,6 +93,7 @@ struct symbol_conf {
*tid_list,
*addr_list;
const char *symfs;
bool symfs_layout_flat;
int res_sample;
int pad_output_len_dso;
int group_sort_idx;