diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index b73cf589c1093fdd63e7bb3e6daf4b9a6840e2ea..c64118a4de49be419c22655790dfbea62de45261 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -115,7 +115,21 @@ OPTIONS -f:: --fields Comma separated list of fields to print. Options are: - comm, tid, pid, time, cpu, event, trace + comm, tid, pid, time, cpu, event, trace, sym + +-k:: +--vmlinux=:: + vmlinux pathname + +--kallsyms=:: + kallsyms pathname + +--symfs=:: + Look for files with symbols relative to this directory. + +-G:: +--hide-call-graph:: + When printing symbols do not display call chain. SEE ALSO -------- diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c046e6e8600a61083b58b91b38395630c7b1bd67..b45b09c13f7afb863980eeec9bfad5e89f0a43ea 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -19,6 +19,7 @@ static bool debug_mode; static u64 last_timestamp; static u64 nr_unordered; extern const struct option record_options[]; +static bool no_callchain; enum perf_output_field { PERF_OUTPUT_COMM = 1U << 0, @@ -28,6 +29,7 @@ enum perf_output_field { PERF_OUTPUT_CPU = 1U << 4, PERF_OUTPUT_EVNAME = 1U << 5, PERF_OUTPUT_TRACE = 1U << 6, + PERF_OUTPUT_SYM = 1U << 7, }; struct output_option { @@ -41,6 +43,7 @@ struct output_option { {.str = "cpu", .field = PERF_OUTPUT_CPU}, {.str = "event", .field = PERF_OUTPUT_EVNAME}, {.str = "trace", .field = PERF_OUTPUT_TRACE}, + {.str = "sym", .field = PERF_OUTPUT_SYM}, }; /* default set to maintain compatibility with current format */ @@ -65,6 +68,8 @@ static void print_sample_start(struct perf_sample *sample, if (PRINT_FIELD(COMM)) { if (latency_format) printf("%8.8s ", thread->comm); + else if (PRINT_FIELD(SYM) && symbol_conf.use_callchain) + printf("%s ", thread->comm); else printf("%16s ", thread->comm); } @@ -112,6 +117,14 @@ static void process_event(union perf_event *event __unused, print_trace_event(sample->cpu, sample->raw_data, sample->raw_size); + if (PRINT_FIELD(SYM)) { + if (!symbol_conf.use_callchain) + printf(" "); + else + printf("\n"); + perf_session__print_symbols(event, sample, session); + } + printf("\n"); } @@ -190,7 +203,10 @@ static int process_sample_event(union perf_event *event, static struct perf_event_ops event_ops = { .sample = process_sample_event, + .mmap = perf_event__process_mmap, .comm = perf_event__process_comm, + .exit = perf_event__process_task, + .fork = perf_event__process_task, .attr = perf_event__process_attr, .event_type = perf_event__process_event_type, .tracing_data = perf_event__process_tracing_data, @@ -722,8 +738,16 @@ static const struct option options[] = { "input file name"), OPT_BOOLEAN('d', "debug-mode", &debug_mode, "do various checks like samples ordering and lost events"), + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), + OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, + "file", "kallsyms pathname"), + OPT_BOOLEAN('G', "hide-call-graph", &no_callchain, + "When printing symbols do not display call chain"), + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", + "Look for files with symbols relative to this directory"), OPT_CALLBACK('f', "fields", NULL, "str", - "comma separated output fields. Options: comm,tid,pid,time,cpu,event,trace", + "comma separated output fields. Options: comm,tid,pid,time,cpu,event,trace,sym", parse_output_fields), OPT_END() @@ -905,6 +929,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) if (session == NULL) return -ENOMEM; + if (!no_callchain && (session->sample_type & PERF_SAMPLE_CALLCHAIN)) + symbol_conf.use_callchain = true; + else + symbol_conf.use_callchain = false; + if (strcmp(input_name, "-") && !perf_session__has_traces(session, "record -R")) return -EINVAL; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f26639fa0fb3a1fd9f2c7376a44a386d5491f49a..c68cf40764f9b006a83ec45ee0e3a8e2520b24af 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1134,3 +1134,64 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) return ret; } + +void perf_session__print_symbols(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session) +{ + struct addr_location al; + const char *symname, *dsoname; + struct callchain_cursor *cursor = &session->callchain_cursor; + struct callchain_cursor_node *node; + + if (perf_event__preprocess_sample(event, session, &al, sample, + NULL) < 0) { + error("problem processing %d event, skipping it.\n", + event->header.type); + return; + } + + if (symbol_conf.use_callchain && sample->callchain) { + + if (perf_session__resolve_callchain(session, al.thread, + sample->callchain, NULL) != 0) { + if (verbose) + error("Failed to resolve callchain. Skipping\n"); + return; + } + callchain_cursor_commit(cursor); + + while (1) { + node = callchain_cursor_current(cursor); + if (!node) + break; + + if (node->sym && node->sym->name) + symname = node->sym->name; + else + symname = ""; + + if (node->map && node->map->dso && node->map->dso->name) + dsoname = node->map->dso->name; + else + dsoname = ""; + + printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); + + callchain_cursor_advance(cursor); + } + + } else { + if (al.sym && al.sym->name) + symname = al.sym->name; + else + symname = ""; + + if (al.map && al.map->dso && al.map->dso->name) + dsoname = al.map->dso->name; + else + dsoname = ""; + + printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); + } +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index b5b148b0aacaf937596fa23242bf330cad20769c..0b3c9afecaa9e22e5eac904a92c34742e0898532 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -159,4 +159,8 @@ static inline int perf_session__parse_sample(struct perf_session *session, session->sample_id_all, sample); } +void perf_session__print_symbols(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session); + #endif /* __PERF_SESSION_H */