Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull more perf tooling updates from Thomas Gleixner: "Perf tool updates and fixes: perf stat: - Display user and system time for workload targets (Jiri Olsa) perf record: - Enable arbitrary event names thru name= modifier (Alexey Budankov) PowerPC: - Add a python script for hypervisor call statistics (Ravi Bangoria) Intel PT: (Adrian Hunter) - Fix sync_switch INTEL_PT_SS_NOT_TRACING - Fix decoding to accept CBR between FUP and corresponding TIP - Fix MTC timing after overflow - Fix "Unexpected indirect branch" error perf test: - record+probe_libc_inet_pton: - To get the symbol table for dynamic shared objects on ubuntu we need to pass the -D/--dynamic command line option, unlike with the fedora distros (Arnaldo Carvalho de Melo) - code-reading: - Fix perf_env setup for PTI entry trampolines (Adrian Hunter) - kmod-path: - Add tests for vdso32 and vdsox32 (Adrian Hunter) - Use header file util/debug.h (Thomas Richter) perf annotate: - Make the various UI backends (stdio, TUI, gtk) use more consistently structs with annotation options as specified by the user (Arnaldo Carvalho de Melo) - Move annotation specific knobs from the symbol_conf global kitchen sink to the annotation option structs (Arnaldo Carvalho de Melo) perf script: - Add more PMU fields to python scripts event handler dict (Jin Yao) Core: - Fix misleading error for some unparsable events mentioning PMUs when those are not involved in the problem (Jiri Olsa) - Consider BSS symbols when processing /proc/kallsyms ('B' and 'b') (Arnaldo Carvalho de Melo) - Be more robust when trying to use per-symbol histograms, checking for unlikely but possible cases where the space for the histograms wasn't allocated, print a debug message for such cases (Arnaldo Carvalho de Melo) - Fix symbol and object code resolution for vdso32 and vdsox32 (Adrian Hunter) - No need to check for null when passing pointers to foo__get() style refcount grabbing helpers, just like in the kernel and with free(), its safe to pass a NULL pointer to avoid having to check it before each and every foo__get() call (Arnaldo Carvalho de Melo) - Remove some dead code (quote.[ch]) (Arnaldo Carvalho de Melo) - Remove some needless globals, making them local (Arnaldo Carvalho de Melo) - Reduce usage of symbol_conf.use_callchain, using other means of finding out if callchains are in use or available for specific events, as we evolved this codebase to allow requesting callchains for just a subset of the monitored events. In time it will help polish recording and showing mixed sets accross the various tools: perf record -e cycles/call-graph=fp/,cache-misses/call-graph=dwarf/,instructions' (Arnaldo Carvalho de Melo) - Consider PTI entry trampolines in map__rip_2objdump() (Adrian Hunter)" * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (50 commits) perf script python: Add dict fields introduction to Documentation perf script python: Add more PMU fields to event handler dict perf script python: Move dsoname code to a new function perf symbols: Add BSS symbols when reading from /proc/kallsyms perf annnotate: Make __symbol__inc_addr_samples handle src->histograms == NULL perf intel-pt: Fix "Unexpected indirect branch" error perf intel-pt: Fix MTC timing after overflow perf intel-pt: Fix decoding to accept CBR between FUP and corresponding TIP perf intel-pt: Fix sync_switch INTEL_PT_SS_NOT_TRACING perf script powerpc: Python script for hypervisor call statistics perf test record+probe_libc_inet_pton: Ask 'nm' for dynamic symbols perf map: Consider PTI entry trampolines in rip_2objdump() perf test code-reading: Fix perf_env setup for PTI entry trampolines perf tools: Fix pmu events parsing rule perf stat: Display user and system time perf record: Enable arbitrary event names thru name= modifier perf tools: Fix symbol and object code resolution for vdso32 and vdsox32 perf tests kmod-path: Add tests for vdso32 and vdsox32 perf hists: Check if a hist_entry has callchains before using them perf hists: Introduce hist_entry__has_callchain() method ...
This commit is contained in:
commit
2322d6c5c7
@ -124,7 +124,11 @@ The available PMUs and their raw parameters can be listed with
|
||||
For example the raw event "LSD.UOPS" core pmu event above could
|
||||
be specified as
|
||||
|
||||
perf stat -e cpu/event=0xa8,umask=0x1,name=LSD.UOPS_CYCLES,cmask=1/ ...
|
||||
perf stat -e cpu/event=0xa8,umask=0x1,name=LSD.UOPS_CYCLES,cmask=0x1/ ...
|
||||
|
||||
or using extended name syntax
|
||||
|
||||
perf stat -e cpu/event=0xa8,umask=0x1,cmask=0x1,name=\'LSD.UOPS_CYCLES:cmask=0x1\'/ ...
|
||||
|
||||
PER SOCKET PMUS
|
||||
---------------
|
||||
|
@ -57,6 +57,9 @@ OPTIONS
|
||||
FP mode, "dwarf" for DWARF mode, "lbr" for LBR mode and
|
||||
"no" for disable callgraph.
|
||||
- 'stack-size': user stack size for dwarf mode
|
||||
- 'name' : User defined event name. Single quotes (') may be used to
|
||||
escape symbols in the name from parsing by shell and tool
|
||||
like this: name=\'CPU_CLK_UNHALTED.THREAD:cmask=0x1\'.
|
||||
|
||||
See the linkperf:perf-list[1] man page for more parameters.
|
||||
|
||||
|
@ -610,6 +610,32 @@ Various utility functions for use with perf script:
|
||||
nsecs_str(nsecs) - returns printable string in the form secs.nsecs
|
||||
avg(total, n) - returns average given a sum and a total number of values
|
||||
|
||||
SUPPORTED FIELDS
|
||||
----------------
|
||||
|
||||
Currently supported fields:
|
||||
|
||||
ev_name, comm, pid, tid, cpu, ip, time, period, phys_addr, addr,
|
||||
symbol, dso, time_enabled, time_running, values, callchain,
|
||||
brstack, brstacksym, datasrc, datasrc_decode, iregs, uregs,
|
||||
weight, transaction, raw_buf, attr.
|
||||
|
||||
Some fields have sub items:
|
||||
|
||||
brstack:
|
||||
from, to, from_dsoname, to_dsoname, mispred,
|
||||
predicted, in_tx, abort, cycles.
|
||||
|
||||
brstacksym:
|
||||
items: from, to, pred, in_tx, abort (converted string)
|
||||
|
||||
For example,
|
||||
We can use this code to print brstack "from", "to", "cycles".
|
||||
|
||||
if 'brstack' in dict:
|
||||
for entry in dict['brstack']:
|
||||
print "from %s, to %s, cycles %s" % (entry["from"], entry["to"], entry["cycles"])
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-script[1]
|
||||
|
@ -310,20 +310,38 @@ Users who wants to get the actual value can apply --no-metric-only.
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
$ perf stat -- make -j
|
||||
$ perf stat -- make
|
||||
|
||||
Performance counter stats for 'make -j':
|
||||
Performance counter stats for 'make':
|
||||
|
||||
8117.370256 task clock ticks # 11.281 CPU utilization factor
|
||||
678 context switches # 0.000 M/sec
|
||||
133 CPU migrations # 0.000 M/sec
|
||||
235724 pagefaults # 0.029 M/sec
|
||||
24821162526 CPU cycles # 3057.784 M/sec
|
||||
18687303457 instructions # 2302.138 M/sec
|
||||
172158895 cache references # 21.209 M/sec
|
||||
27075259 cache misses # 3.335 M/sec
|
||||
83723.452481 task-clock:u (msec) # 1.004 CPUs utilized
|
||||
0 context-switches:u # 0.000 K/sec
|
||||
0 cpu-migrations:u # 0.000 K/sec
|
||||
3,228,188 page-faults:u # 0.039 M/sec
|
||||
229,570,665,834 cycles:u # 2.742 GHz
|
||||
313,163,853,778 instructions:u # 1.36 insn per cycle
|
||||
69,704,684,856 branches:u # 832.559 M/sec
|
||||
2,078,861,393 branch-misses:u # 2.98% of all branches
|
||||
|
||||
Wall-clock time elapsed: 719.554352 msecs
|
||||
83.409183620 seconds time elapsed
|
||||
|
||||
74.684747000 seconds user
|
||||
8.739217000 seconds sys
|
||||
|
||||
TIMINGS
|
||||
-------
|
||||
As displayed in the example above we can display 3 types of timings.
|
||||
We always display the time the counters were enabled/alive:
|
||||
|
||||
83.409183620 seconds time elapsed
|
||||
|
||||
For workload sessions we also display time the workloads spent in
|
||||
user/system lands:
|
||||
|
||||
74.684747000 seconds user
|
||||
8.739217000 seconds sys
|
||||
|
||||
Those times are the very same as displayed by the 'time' tool.
|
||||
|
||||
CSV FORMAT
|
||||
----------
|
||||
|
@ -189,7 +189,7 @@ out_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int perf_env__lookup_objdump(struct perf_env *env)
|
||||
int perf_env__lookup_objdump(struct perf_env *env, const char **path)
|
||||
{
|
||||
/*
|
||||
* For live mode, env->arch will be NULL and we can use
|
||||
@ -198,5 +198,5 @@ int perf_env__lookup_objdump(struct perf_env *env)
|
||||
if (env->arch == NULL)
|
||||
return 0;
|
||||
|
||||
return perf_env__lookup_binutils_path(env, "objdump", &objdump_path);
|
||||
return perf_env__lookup_binutils_path(env, "objdump", path);
|
||||
}
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
#include "../util/env.h"
|
||||
|
||||
extern const char *objdump_path;
|
||||
|
||||
int perf_env__lookup_objdump(struct perf_env *env);
|
||||
int perf_env__lookup_objdump(struct perf_env *env, const char **path);
|
||||
|
||||
#endif /* ARCH_PERF_COMMON_H */
|
||||
|
@ -40,9 +40,8 @@
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
struct annotation_options opts;
|
||||
bool use_tui, use_stdio, use_stdio2, use_gtk;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
bool skip_missing;
|
||||
bool has_br_stack;
|
||||
bool group_set;
|
||||
@ -162,12 +161,12 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
|
||||
hist__account_cycles(sample->branch_stack, al, sample, false);
|
||||
|
||||
bi = he->branch_info;
|
||||
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel);
|
||||
|
||||
out:
|
||||
return err;
|
||||
@ -249,7 +248,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
||||
if (he == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hist_entry__inc_addr_samples(he, sample, evsel->idx, al->addr);
|
||||
ret = hist_entry__inc_addr_samples(he, sample, evsel, al->addr);
|
||||
hists__inc_nr_samples(hists, true);
|
||||
return ret;
|
||||
}
|
||||
@ -289,10 +288,9 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
if (!ann->use_stdio2)
|
||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
|
||||
ann->print_line, ann->full_paths, 0, 0);
|
||||
return symbol__tty_annotate2(he->ms.sym, he->ms.map, evsel,
|
||||
ann->print_line, ann->full_paths);
|
||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, &ann->opts);
|
||||
|
||||
return symbol__tty_annotate2(he->ms.sym, he->ms.map, evsel, &ann->opts);
|
||||
}
|
||||
|
||||
static void hists__find_annotations(struct hists *hists,
|
||||
@ -343,7 +341,7 @@ find_next:
|
||||
/* skip missing symbols */
|
||||
nd = rb_next(nd);
|
||||
} else if (use_browser == 1) {
|
||||
key = hist_entry__tui_annotate(he, evsel, NULL);
|
||||
key = hist_entry__tui_annotate(he, evsel, NULL, &ann->opts);
|
||||
|
||||
switch (key) {
|
||||
case -1:
|
||||
@ -390,8 +388,9 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_env__lookup_objdump(&session->header.env);
|
||||
if (!ann->opts.objdump_path) {
|
||||
ret = perf_env__lookup_objdump(&session->header.env,
|
||||
&ann->opts.objdump_path);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@ -476,6 +475,7 @@ int cmd_annotate(int argc, const char **argv)
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
.opts = annotation__default_options,
|
||||
};
|
||||
struct perf_data data = {
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
@ -503,9 +503,9 @@ int cmd_annotate(int argc, const char **argv)
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
||||
"load module symbols - WARNING: use only with -k and LIVE kernel"),
|
||||
OPT_BOOLEAN('l', "print-line", &annotate.print_line,
|
||||
OPT_BOOLEAN('l', "print-line", &annotate.opts.print_lines,
|
||||
"print matching source lines (may be slow)"),
|
||||
OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,
|
||||
OPT_BOOLEAN('P', "full-paths", &annotate.opts.full_path,
|
||||
"Don't shorten the displayed pathnames"),
|
||||
OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing,
|
||||
"Skip symbols that cannot be annotated"),
|
||||
@ -516,13 +516,13 @@ int cmd_annotate(int argc, const char **argv)
|
||||
OPT_CALLBACK(0, "symfs", NULL, "directory",
|
||||
"Look for files with symbols relative to this directory",
|
||||
symbol__config_symfs),
|
||||
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
|
||||
OPT_BOOLEAN(0, "source", &annotate.opts.annotate_src,
|
||||
"Interleave source code with assembly code (default)"),
|
||||
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
|
||||
OPT_BOOLEAN(0, "asm-raw", &annotate.opts.show_asm_raw,
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
OPT_STRING('M', "disassembler-style", &annotate.opts.disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
OPT_STRING(0, "objdump", &annotate.opts.objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
|
||||
"Show event group information together"),
|
||||
|
@ -1976,7 +1976,7 @@ static int filter_cb(struct hist_entry *he)
|
||||
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||
|
||||
if (c2c.show_src && !he->srcline)
|
||||
he->srcline = hist_entry__get_srcline(he);
|
||||
he->srcline = hist_entry__srcline(he);
|
||||
|
||||
calc_width(c2c_he);
|
||||
|
||||
|
@ -1438,8 +1438,6 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
goto out;
|
||||
}
|
||||
|
||||
symbol_conf.nr_events = kvm->evlist->nr_entries;
|
||||
|
||||
if (perf_evlist__create_maps(kvm->evlist, &kvm->opts.target) < 0)
|
||||
usage_with_options(live_usage, live_options);
|
||||
|
||||
|
@ -81,8 +81,7 @@ static int parse_probe_event(const char *str)
|
||||
params.target_used = true;
|
||||
}
|
||||
|
||||
if (params.nsi)
|
||||
pev->nsi = nsinfo__get(params.nsi);
|
||||
pev->nsi = nsinfo__get(params.nsi);
|
||||
|
||||
/* Parse a perf-probe command into event */
|
||||
ret = parse_perf_probe_command(str, pev);
|
||||
|
@ -71,6 +71,7 @@ struct report {
|
||||
bool group_set;
|
||||
int max_stack;
|
||||
struct perf_read_values show_threads_values;
|
||||
struct annotation_options annotation_opts;
|
||||
const char *pretty_printing_style;
|
||||
const char *cpu_list;
|
||||
const char *symbol_filter_str;
|
||||
@ -136,26 +137,25 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter,
|
||||
|
||||
if (sort__mode == SORT_MODE__BRANCH) {
|
||||
bi = he->branch_info;
|
||||
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel);
|
||||
|
||||
} else if (rep->mem_mode) {
|
||||
mi = he->mem_info;
|
||||
err = addr_map_symbol__inc_samples(&mi->daddr, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&mi->daddr, sample, evsel);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel->idx, al->addr);
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel, al->addr);
|
||||
|
||||
} else if (symbol_conf.cumulate_callchain) {
|
||||
if (single)
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel->idx,
|
||||
al->addr);
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel, al->addr);
|
||||
} else {
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel->idx, al->addr);
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel, al->addr);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -181,11 +181,11 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
|
||||
rep->nonany_branch_mode);
|
||||
|
||||
bi = he->branch_info;
|
||||
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
|
||||
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel);
|
||||
|
||||
branch_type_count(&rep->brtype_stat, &bi->flags,
|
||||
bi->from.addr, bi->to.addr);
|
||||
@ -561,7 +561,7 @@ static int report__browse_hists(struct report *rep)
|
||||
ret = perf_evlist__tui_browse_hists(evlist, help, NULL,
|
||||
rep->min_percent,
|
||||
&session->header.env,
|
||||
true);
|
||||
true, &rep->annotation_opts);
|
||||
/*
|
||||
* Usually "ret" is the last pressed key, and we only
|
||||
* care if the key notifies us to switch data file.
|
||||
@ -946,12 +946,6 @@ parse_percent_limit(const struct option *opt, const char *str,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
|
||||
|
||||
const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
|
||||
CALLCHAIN_REPORT_HELP
|
||||
"\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
|
||||
|
||||
int cmd_report(int argc, const char **argv)
|
||||
{
|
||||
struct perf_session *session;
|
||||
@ -960,6 +954,10 @@ int cmd_report(int argc, const char **argv)
|
||||
bool has_br_stack = false;
|
||||
int branch_mode = -1;
|
||||
bool branch_call_mode = false;
|
||||
#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
|
||||
const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
|
||||
CALLCHAIN_REPORT_HELP
|
||||
"\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
|
||||
char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
|
||||
const char * const report_usage[] = {
|
||||
"perf report [<options>]",
|
||||
@ -989,6 +987,7 @@ int cmd_report(int argc, const char **argv)
|
||||
.max_stack = PERF_MAX_STACK_DEPTH,
|
||||
.pretty_printing_style = "normal",
|
||||
.socket_filter = -1,
|
||||
.annotation_opts = annotation__default_options,
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
@ -1078,11 +1077,11 @@ int cmd_report(int argc, const char **argv)
|
||||
"list of cpus to profile"),
|
||||
OPT_BOOLEAN('I', "show-info", &report.show_full_info,
|
||||
"Display extended information about perf.data file"),
|
||||
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
|
||||
OPT_BOOLEAN(0, "source", &report.annotation_opts.annotate_src,
|
||||
"Interleave source code with assembly code (default)"),
|
||||
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
|
||||
OPT_BOOLEAN(0, "asm-raw", &report.annotation_opts.show_asm_raw,
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
OPT_STRING('M', "disassembler-style", &report.annotation_opts.disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
||||
"Show a column with the sum of periods"),
|
||||
@ -1093,7 +1092,7 @@ int cmd_report(int argc, const char **argv)
|
||||
parse_branch_mode),
|
||||
OPT_BOOLEAN(0, "branch-history", &branch_call_mode,
|
||||
"add last branch records to call history"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
OPT_STRING(0, "objdump", &report.annotation_opts.objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
|
||||
"Disable symbol demangling"),
|
||||
|
@ -2143,7 +2143,7 @@ static void save_task_callchain(struct perf_sched *sched,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!symbol_conf.use_callchain || sample->callchain == NULL)
|
||||
if (!sched->show_callchain || sample->callchain == NULL)
|
||||
return;
|
||||
|
||||
if (thread__resolve_callchain(thread, cursor, evsel, sample,
|
||||
@ -2271,10 +2271,11 @@ static struct thread *get_idle_thread(int cpu)
|
||||
return idle_threads[cpu];
|
||||
}
|
||||
|
||||
static void save_idle_callchain(struct idle_thread_runtime *itr,
|
||||
static void save_idle_callchain(struct perf_sched *sched,
|
||||
struct idle_thread_runtime *itr,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
if (!symbol_conf.use_callchain || sample->callchain == NULL)
|
||||
if (!sched->show_callchain || sample->callchain == NULL)
|
||||
return;
|
||||
|
||||
callchain_cursor__copy(&itr->cursor, &callchain_cursor);
|
||||
@ -2320,7 +2321,7 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
|
||||
|
||||
/* copy task callchain when entering to idle */
|
||||
if (perf_evsel__intval(evsel, sample, "next_pid") == 0)
|
||||
save_idle_callchain(itr, sample);
|
||||
save_idle_callchain(sched, itr, sample);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2849,7 +2850,7 @@ static void timehist_print_summary(struct perf_sched *sched,
|
||||
printf(" CPU %2d idle entire time window\n", i);
|
||||
}
|
||||
|
||||
if (sched->idle_hist && symbol_conf.use_callchain) {
|
||||
if (sched->idle_hist && sched->show_callchain) {
|
||||
callchain_param.mode = CHAIN_FOLDED;
|
||||
callchain_param.value = CCVAL_PERIOD;
|
||||
|
||||
@ -2933,8 +2934,7 @@ static int timehist_check_attr(struct perf_sched *sched,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sched->show_callchain &&
|
||||
!(evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN)) {
|
||||
if (sched->show_callchain && !evsel__has_callchain(evsel)) {
|
||||
pr_info("Samples do not have callchains.\n");
|
||||
sched->show_callchain = 0;
|
||||
symbol_conf.use_callchain = 0;
|
||||
|
@ -517,7 +517,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
|
||||
evlist__for_each_entry(session->evlist, evsel) {
|
||||
not_pipe = true;
|
||||
if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
|
||||
if (evsel__has_callchain(evsel)) {
|
||||
use_callchain = true;
|
||||
break;
|
||||
}
|
||||
@ -532,22 +532,18 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
*/
|
||||
if (symbol_conf.use_callchain &&
|
||||
!output[PERF_TYPE_TRACEPOINT].user_set) {
|
||||
struct perf_event_attr *attr;
|
||||
|
||||
j = PERF_TYPE_TRACEPOINT;
|
||||
|
||||
evlist__for_each_entry(session->evlist, evsel) {
|
||||
if (evsel->attr.type != j)
|
||||
continue;
|
||||
|
||||
attr = &evsel->attr;
|
||||
|
||||
if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
|
||||
if (evsel__has_callchain(evsel)) {
|
||||
output[j].fields |= PERF_OUTPUT_IP;
|
||||
output[j].fields |= PERF_OUTPUT_SYM;
|
||||
output[j].fields |= PERF_OUTPUT_SYMOFFSET;
|
||||
output[j].fields |= PERF_OUTPUT_DSO;
|
||||
set_print_ip_opts(attr);
|
||||
set_print_ip_opts(&evsel->attr);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -610,7 +606,7 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
|
||||
if (PRINT_FIELD(COMM)) {
|
||||
if (latency_format)
|
||||
printed += fprintf(fp, "%8.8s ", thread__comm_str(thread));
|
||||
else if (PRINT_FIELD(IP) && symbol_conf.use_callchain)
|
||||
else if (PRINT_FIELD(IP) && evsel__has_callchain(evsel) && symbol_conf.use_callchain)
|
||||
printed += fprintf(fp, "%s ", thread__comm_str(thread));
|
||||
else
|
||||
printed += fprintf(fp, "%16s ", thread__comm_str(thread));
|
||||
|
@ -80,6 +80,9 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "sane_ctype.h"
|
||||
|
||||
@ -175,6 +178,8 @@ static int output_fd;
|
||||
static int print_free_counters_hint;
|
||||
static int print_mixed_hw_group_error;
|
||||
static u64 *walltime_run;
|
||||
static bool ru_display = false;
|
||||
static struct rusage ru_data;
|
||||
|
||||
struct perf_stat {
|
||||
bool record;
|
||||
@ -726,7 +731,7 @@ try_again:
|
||||
break;
|
||||
}
|
||||
}
|
||||
waitpid(child_pid, &status, 0);
|
||||
wait4(child_pid, &status, 0, &ru_data);
|
||||
|
||||
if (workload_exec_errno) {
|
||||
const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg));
|
||||
@ -1804,6 +1809,11 @@ static void print_table(FILE *output, int precision, double avg)
|
||||
fprintf(output, "\n%*s# Final result:\n", indent, "");
|
||||
}
|
||||
|
||||
static double timeval2double(struct timeval *t)
|
||||
{
|
||||
return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC;
|
||||
}
|
||||
|
||||
static void print_footer(void)
|
||||
{
|
||||
double avg = avg_stats(&walltime_nsecs_stats) / NSEC_PER_SEC;
|
||||
@ -1815,6 +1825,15 @@ static void print_footer(void)
|
||||
|
||||
if (run_count == 1) {
|
||||
fprintf(output, " %17.9f seconds time elapsed", avg);
|
||||
|
||||
if (ru_display) {
|
||||
double ru_utime = timeval2double(&ru_data.ru_utime);
|
||||
double ru_stime = timeval2double(&ru_data.ru_stime);
|
||||
|
||||
fprintf(output, "\n\n");
|
||||
fprintf(output, " %17.9f seconds user\n", ru_utime);
|
||||
fprintf(output, " %17.9f seconds sys\n", ru_stime);
|
||||
}
|
||||
} else {
|
||||
double sd = stddev_stats(&walltime_nsecs_stats) / NSEC_PER_SEC;
|
||||
/*
|
||||
@ -2950,6 +2969,13 @@ int cmd_stat(int argc, const char **argv)
|
||||
|
||||
setup_system_wide(argc);
|
||||
|
||||
/*
|
||||
* Display user/system times only for single
|
||||
* run and when there's specified tracee.
|
||||
*/
|
||||
if ((run_count == 1) && target__none(&target))
|
||||
ru_display = true;
|
||||
|
||||
if (run_count < 0) {
|
||||
pr_err("Run count must be a positive number\n");
|
||||
parse_options_usage(stat_usage, stat_options, "r", 1);
|
||||
|
@ -123,14 +123,9 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
|
||||
}
|
||||
|
||||
notes = symbol__annotation(sym);
|
||||
if (notes->src != NULL) {
|
||||
pthread_mutex_lock(¬es->lock);
|
||||
goto out_assign;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(¬es->lock);
|
||||
|
||||
if (symbol__alloc_hist(sym) < 0) {
|
||||
if (!symbol__hists(sym, top->evlist->nr_entries)) {
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
pr_err("Not enough memory for annotating '%s' symbol!\n",
|
||||
sym->name);
|
||||
@ -138,9 +133,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = symbol__annotate(sym, map, evsel, 0, NULL);
|
||||
err = symbol__annotate(sym, map, evsel, 0, &top->annotation_opts, NULL);
|
||||
if (err == 0) {
|
||||
out_assign:
|
||||
top->sym_filter_entry = he;
|
||||
} else {
|
||||
char msg[BUFSIZ];
|
||||
@ -188,7 +182,7 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
|
||||
static void perf_top__record_precise_ip(struct perf_top *top,
|
||||
struct hist_entry *he,
|
||||
struct perf_sample *sample,
|
||||
int counter, u64 ip)
|
||||
struct perf_evsel *evsel, u64 ip)
|
||||
{
|
||||
struct annotation *notes;
|
||||
struct symbol *sym = he->ms.sym;
|
||||
@ -204,7 +198,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
|
||||
if (pthread_mutex_trylock(¬es->lock))
|
||||
return;
|
||||
|
||||
err = hist_entry__inc_addr_samples(he, sample, counter, ip);
|
||||
err = hist_entry__inc_addr_samples(he, sample, evsel, ip);
|
||||
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
|
||||
@ -249,10 +243,9 @@ static void perf_top__show_details(struct perf_top *top)
|
||||
goto out_unlock;
|
||||
|
||||
printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);
|
||||
printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
|
||||
printf(" Events Pcnt (>=%d%%)\n", top->annotation_opts.min_pcnt);
|
||||
|
||||
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel,
|
||||
0, top->sym_pcnt_filter, top->print_entries, 4);
|
||||
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel, &top->annotation_opts);
|
||||
|
||||
if (top->evlist->enabled) {
|
||||
if (top->zero)
|
||||
@ -412,7 +405,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)
|
||||
|
||||
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter);
|
||||
|
||||
fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", top->sym_pcnt_filter);
|
||||
fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", top->annotation_opts.min_pcnt);
|
||||
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
|
||||
fprintf(stdout, "\t[S] stop annotation.\n");
|
||||
|
||||
@ -515,7 +508,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
prompt_integer(&top->count_filter, "Enter display event count filter");
|
||||
break;
|
||||
case 'F':
|
||||
prompt_percent(&top->sym_pcnt_filter,
|
||||
prompt_percent(&top->annotation_opts.min_pcnt,
|
||||
"Enter details display event filter (percent)");
|
||||
break;
|
||||
case 'K':
|
||||
@ -613,7 +606,8 @@ static void *display_thread_tui(void *arg)
|
||||
perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
|
||||
top->min_percent,
|
||||
&top->session->header.env,
|
||||
!top->record_opts.overwrite);
|
||||
!top->record_opts.overwrite,
|
||||
&top->annotation_opts);
|
||||
|
||||
done = 1;
|
||||
return NULL;
|
||||
@ -691,7 +685,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
|
||||
struct perf_evsel *evsel = iter->evsel;
|
||||
|
||||
if (perf_hpp_list.sym && single)
|
||||
perf_top__record_precise_ip(top, he, iter->sample, evsel->idx, al->addr);
|
||||
perf_top__record_precise_ip(top, he, iter->sample, evsel, al->addr);
|
||||
|
||||
hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
|
||||
!(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY));
|
||||
@ -1083,8 +1077,9 @@ static int __cmd_top(struct perf_top *top)
|
||||
if (top->session == NULL)
|
||||
return -1;
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_env__lookup_objdump(&top->session->header.env);
|
||||
if (!top->annotation_opts.objdump_path) {
|
||||
ret = perf_env__lookup_objdump(&top->session->header.env,
|
||||
&top->annotation_opts.objdump_path);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
@ -1265,7 +1260,7 @@ int cmd_top(int argc, const char **argv)
|
||||
.overwrite = 1,
|
||||
},
|
||||
.max_stack = sysctl__max_stack(),
|
||||
.sym_pcnt_filter = 5,
|
||||
.annotation_opts = annotation__default_options,
|
||||
.nr_threads_synthesize = UINT_MAX,
|
||||
};
|
||||
struct record_opts *opts = &top.record_opts;
|
||||
@ -1347,15 +1342,15 @@ int cmd_top(int argc, const char **argv)
|
||||
"only consider symbols in these comms"),
|
||||
OPT_STRING(0, "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
|
||||
OPT_BOOLEAN(0, "source", &top.annotation_opts.annotate_src,
|
||||
"Interleave source code with assembly code (default)"),
|
||||
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
|
||||
OPT_BOOLEAN(0, "asm-raw", &top.annotation_opts.show_asm_raw,
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
"Enable kernel symbol demangling"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
OPT_STRING(0, "objdump", &top.annotation_opts.objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
OPT_STRING('M', "disassembler-style", &top.annotation_opts.disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
|
||||
OPT_CALLBACK(0, "percent-limit", &top, "percent",
|
||||
@ -1391,6 +1386,9 @@ int cmd_top(int argc, const char **argv)
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
top.annotation_opts.min_pcnt = 5;
|
||||
top.annotation_opts.context = 4;
|
||||
|
||||
top.evlist = perf_evlist__new();
|
||||
if (top.evlist == NULL)
|
||||
return -ENOMEM;
|
||||
@ -1468,8 +1466,6 @@ int cmd_top(int argc, const char **argv)
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
symbol_conf.nr_events = top.evlist->nr_entries;
|
||||
|
||||
if (top.delay_secs < 1)
|
||||
top.delay_secs = 1;
|
||||
|
||||
|
@ -2491,7 +2491,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
* to override an explicitely set --max-stack global setting.
|
||||
*/
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if ((evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) &&
|
||||
if (evsel__has_callchain(evsel) &&
|
||||
evsel->attr.sample_max_stack == 0)
|
||||
evsel->attr.sample_max_stack = trace->max_stack;
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "util/env.h"
|
||||
#include <subcmd/exec-cmd.h>
|
||||
#include "util/config.h"
|
||||
#include "util/quote.h"
|
||||
#include <subcmd/run-command.h>
|
||||
#include "util/parse-events.h"
|
||||
#include <subcmd/parse-options.h>
|
||||
|
2
tools/perf/scripts/python/bin/powerpc-hcalls-record
Normal file
2
tools/perf/scripts/python/bin/powerpc-hcalls-record
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
perf record -e "{powerpc:hcall_entry,powerpc:hcall_exit}" $@
|
2
tools/perf/scripts/python/bin/powerpc-hcalls-report
Normal file
2
tools/perf/scripts/python/bin/powerpc-hcalls-report
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/powerpc-hcalls.py
|
200
tools/perf/scripts/python/powerpc-hcalls.py
Normal file
200
tools/perf/scripts/python/powerpc-hcalls.py
Normal file
@ -0,0 +1,200 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2018 Ravi Bangoria, IBM Corporation
|
||||
#
|
||||
# Hypervisor call statisics
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
from Util import *
|
||||
|
||||
# output: {
|
||||
# opcode: {
|
||||
# 'min': minimum time nsec
|
||||
# 'max': maximum time nsec
|
||||
# 'time': average time nsec
|
||||
# 'cnt': counter
|
||||
# } ...
|
||||
# }
|
||||
output = {}
|
||||
|
||||
# d_enter: {
|
||||
# cpu: {
|
||||
# opcode: nsec
|
||||
# } ...
|
||||
# }
|
||||
d_enter = {}
|
||||
|
||||
hcall_table = {
|
||||
4: 'H_REMOVE',
|
||||
8: 'H_ENTER',
|
||||
12: 'H_READ',
|
||||
16: 'H_CLEAR_MOD',
|
||||
20: 'H_CLEAR_REF',
|
||||
24: 'H_PROTECT',
|
||||
28: 'H_GET_TCE',
|
||||
32: 'H_PUT_TCE',
|
||||
36: 'H_SET_SPRG0',
|
||||
40: 'H_SET_DABR',
|
||||
44: 'H_PAGE_INIT',
|
||||
48: 'H_SET_ASR',
|
||||
52: 'H_ASR_ON',
|
||||
56: 'H_ASR_OFF',
|
||||
60: 'H_LOGICAL_CI_LOAD',
|
||||
64: 'H_LOGICAL_CI_STORE',
|
||||
68: 'H_LOGICAL_CACHE_LOAD',
|
||||
72: 'H_LOGICAL_CACHE_STORE',
|
||||
76: 'H_LOGICAL_ICBI',
|
||||
80: 'H_LOGICAL_DCBF',
|
||||
84: 'H_GET_TERM_CHAR',
|
||||
88: 'H_PUT_TERM_CHAR',
|
||||
92: 'H_REAL_TO_LOGICAL',
|
||||
96: 'H_HYPERVISOR_DATA',
|
||||
100: 'H_EOI',
|
||||
104: 'H_CPPR',
|
||||
108: 'H_IPI',
|
||||
112: 'H_IPOLL',
|
||||
116: 'H_XIRR',
|
||||
120: 'H_MIGRATE_DMA',
|
||||
124: 'H_PERFMON',
|
||||
220: 'H_REGISTER_VPA',
|
||||
224: 'H_CEDE',
|
||||
228: 'H_CONFER',
|
||||
232: 'H_PROD',
|
||||
236: 'H_GET_PPP',
|
||||
240: 'H_SET_PPP',
|
||||
244: 'H_PURR',
|
||||
248: 'H_PIC',
|
||||
252: 'H_REG_CRQ',
|
||||
256: 'H_FREE_CRQ',
|
||||
260: 'H_VIO_SIGNAL',
|
||||
264: 'H_SEND_CRQ',
|
||||
272: 'H_COPY_RDMA',
|
||||
276: 'H_REGISTER_LOGICAL_LAN',
|
||||
280: 'H_FREE_LOGICAL_LAN',
|
||||
284: 'H_ADD_LOGICAL_LAN_BUFFER',
|
||||
288: 'H_SEND_LOGICAL_LAN',
|
||||
292: 'H_BULK_REMOVE',
|
||||
304: 'H_MULTICAST_CTRL',
|
||||
308: 'H_SET_XDABR',
|
||||
312: 'H_STUFF_TCE',
|
||||
316: 'H_PUT_TCE_INDIRECT',
|
||||
332: 'H_CHANGE_LOGICAL_LAN_MAC',
|
||||
336: 'H_VTERM_PARTNER_INFO',
|
||||
340: 'H_REGISTER_VTERM',
|
||||
344: 'H_FREE_VTERM',
|
||||
348: 'H_RESET_EVENTS',
|
||||
352: 'H_ALLOC_RESOURCE',
|
||||
356: 'H_FREE_RESOURCE',
|
||||
360: 'H_MODIFY_QP',
|
||||
364: 'H_QUERY_QP',
|
||||
368: 'H_REREGISTER_PMR',
|
||||
372: 'H_REGISTER_SMR',
|
||||
376: 'H_QUERY_MR',
|
||||
380: 'H_QUERY_MW',
|
||||
384: 'H_QUERY_HCA',
|
||||
388: 'H_QUERY_PORT',
|
||||
392: 'H_MODIFY_PORT',
|
||||
396: 'H_DEFINE_AQP1',
|
||||
400: 'H_GET_TRACE_BUFFER',
|
||||
404: 'H_DEFINE_AQP0',
|
||||
408: 'H_RESIZE_MR',
|
||||
412: 'H_ATTACH_MCQP',
|
||||
416: 'H_DETACH_MCQP',
|
||||
420: 'H_CREATE_RPT',
|
||||
424: 'H_REMOVE_RPT',
|
||||
428: 'H_REGISTER_RPAGES',
|
||||
432: 'H_DISABLE_AND_GETC',
|
||||
436: 'H_ERROR_DATA',
|
||||
440: 'H_GET_HCA_INFO',
|
||||
444: 'H_GET_PERF_COUNT',
|
||||
448: 'H_MANAGE_TRACE',
|
||||
468: 'H_FREE_LOGICAL_LAN_BUFFER',
|
||||
472: 'H_POLL_PENDING',
|
||||
484: 'H_QUERY_INT_STATE',
|
||||
580: 'H_ILLAN_ATTRIBUTES',
|
||||
592: 'H_MODIFY_HEA_QP',
|
||||
596: 'H_QUERY_HEA_QP',
|
||||
600: 'H_QUERY_HEA',
|
||||
604: 'H_QUERY_HEA_PORT',
|
||||
608: 'H_MODIFY_HEA_PORT',
|
||||
612: 'H_REG_BCMC',
|
||||
616: 'H_DEREG_BCMC',
|
||||
620: 'H_REGISTER_HEA_RPAGES',
|
||||
624: 'H_DISABLE_AND_GET_HEA',
|
||||
628: 'H_GET_HEA_INFO',
|
||||
632: 'H_ALLOC_HEA_RESOURCE',
|
||||
644: 'H_ADD_CONN',
|
||||
648: 'H_DEL_CONN',
|
||||
664: 'H_JOIN',
|
||||
676: 'H_VASI_STATE',
|
||||
688: 'H_ENABLE_CRQ',
|
||||
696: 'H_GET_EM_PARMS',
|
||||
720: 'H_SET_MPP',
|
||||
724: 'H_GET_MPP',
|
||||
748: 'H_HOME_NODE_ASSOCIATIVITY',
|
||||
756: 'H_BEST_ENERGY',
|
||||
764: 'H_XIRR_X',
|
||||
768: 'H_RANDOM',
|
||||
772: 'H_COP',
|
||||
788: 'H_GET_MPP_X',
|
||||
796: 'H_SET_MODE',
|
||||
61440: 'H_RTAS',
|
||||
}
|
||||
|
||||
def hcall_table_lookup(opcode):
|
||||
if (hcall_table.has_key(opcode)):
|
||||
return hcall_table[opcode]
|
||||
else:
|
||||
return opcode
|
||||
|
||||
print_ptrn = '%-28s%10s%10s%10s%10s'
|
||||
|
||||
def trace_end():
|
||||
print print_ptrn % ('hcall', 'count', 'min(ns)', 'max(ns)', 'avg(ns)')
|
||||
print '-' * 68
|
||||
for opcode in output:
|
||||
h_name = hcall_table_lookup(opcode)
|
||||
time = output[opcode]['time']
|
||||
cnt = output[opcode]['cnt']
|
||||
min_t = output[opcode]['min']
|
||||
max_t = output[opcode]['max']
|
||||
|
||||
print print_ptrn % (h_name, cnt, min_t, max_t, time/cnt)
|
||||
|
||||
def powerpc__hcall_exit(name, context, cpu, sec, nsec, pid, comm, callchain,
|
||||
opcode, retval):
|
||||
if (d_enter.has_key(cpu) and d_enter[cpu].has_key(opcode)):
|
||||
diff = nsecs(sec, nsec) - d_enter[cpu][opcode]
|
||||
|
||||
if (output.has_key(opcode)):
|
||||
output[opcode]['time'] += diff
|
||||
output[opcode]['cnt'] += 1
|
||||
if (output[opcode]['min'] > diff):
|
||||
output[opcode]['min'] = diff
|
||||
if (output[opcode]['max'] < diff):
|
||||
output[opcode]['max'] = diff
|
||||
else:
|
||||
output[opcode] = {
|
||||
'time': diff,
|
||||
'cnt': 1,
|
||||
'min': diff,
|
||||
'max': diff,
|
||||
}
|
||||
|
||||
del d_enter[cpu][opcode]
|
||||
# else:
|
||||
# print "Can't find matching hcall_enter event. Ignoring sample"
|
||||
|
||||
def powerpc__hcall_entry(event_name, context, cpu, sec, nsec, pid, comm,
|
||||
callchain, opcode):
|
||||
if (d_enter.has_key(cpu)):
|
||||
d_enter[cpu][opcode] = nsecs(sec, nsec)
|
||||
else:
|
||||
d_enter[cpu] = {opcode: nsecs(sec, nsec)}
|
@ -560,6 +560,7 @@ static int do_test_code_reading(bool try_kcore)
|
||||
pid = getpid();
|
||||
|
||||
machine = machine__new_host();
|
||||
machine->env = &perf_env;
|
||||
|
||||
ret = machine__create_kernel_maps(machine);
|
||||
if (ret < 0) {
|
||||
|
@ -127,6 +127,22 @@ int test__kmod_path__parse(struct test *t __maybe_unused, int subtest __maybe_un
|
||||
M("[vdso]", PERF_RECORD_MISC_KERNEL, false);
|
||||
M("[vdso]", PERF_RECORD_MISC_USER, false);
|
||||
|
||||
T("[vdso32]", true , true , false, false, "[vdso32]", NULL);
|
||||
T("[vdso32]", false , true , false, false, NULL , NULL);
|
||||
T("[vdso32]", true , false , false, false, "[vdso32]", NULL);
|
||||
T("[vdso32]", false , false , false, false, NULL , NULL);
|
||||
M("[vdso32]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
|
||||
M("[vdso32]", PERF_RECORD_MISC_KERNEL, false);
|
||||
M("[vdso32]", PERF_RECORD_MISC_USER, false);
|
||||
|
||||
T("[vdsox32]", true , true , false, false, "[vdsox32]", NULL);
|
||||
T("[vdsox32]", false , true , false, false, NULL , NULL);
|
||||
T("[vdsox32]", true , false , false, false, "[vdsox32]", NULL);
|
||||
T("[vdsox32]", false , false , false, false, NULL , NULL);
|
||||
M("[vdsox32]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
|
||||
M("[vdsox32]", PERF_RECORD_MISC_KERNEL, false);
|
||||
M("[vdsox32]", PERF_RECORD_MISC_USER, false);
|
||||
|
||||
/* path alloc_name alloc_ext kmod comp name ext */
|
||||
T("[vsyscall]", true , true , false, false, "[vsyscall]", NULL);
|
||||
T("[vsyscall]", false , true , false, false, NULL , NULL);
|
||||
|
@ -499,7 +499,7 @@ static int test__checkevent_pmu_partial_time_callgraph(struct perf_evlist *evlis
|
||||
* while this test executes only parse events method.
|
||||
*/
|
||||
TEST_ASSERT_VAL("wrong period", 0 == evsel->attr.sample_period);
|
||||
TEST_ASSERT_VAL("wrong callgraph", !(PERF_SAMPLE_CALLCHAIN & evsel->attr.sample_type));
|
||||
TEST_ASSERT_VAL("wrong callgraph", !evsel__has_callchain(evsel));
|
||||
TEST_ASSERT_VAL("wrong time", !(PERF_SAMPLE_TIME & evsel->attr.sample_type));
|
||||
|
||||
/* cpu/config=2,call-graph=no,time=0,period=2000/ */
|
||||
@ -512,7 +512,7 @@ static int test__checkevent_pmu_partial_time_callgraph(struct perf_evlist *evlis
|
||||
* while this test executes only parse events method.
|
||||
*/
|
||||
TEST_ASSERT_VAL("wrong period", 0 == evsel->attr.sample_period);
|
||||
TEST_ASSERT_VAL("wrong callgraph", !(PERF_SAMPLE_CALLCHAIN & evsel->attr.sample_type));
|
||||
TEST_ASSERT_VAL("wrong callgraph", !evsel__has_callchain(evsel));
|
||||
TEST_ASSERT_VAL("wrong time", !(PERF_SAMPLE_TIME & evsel->attr.sample_type));
|
||||
|
||||
return 0;
|
||||
|
@ -7,8 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <linux/compiler.h>
|
||||
#include "tests.h"
|
||||
|
||||
extern int verbose;
|
||||
#include "util/debug.h"
|
||||
|
||||
int test__python_use(struct test *test __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
|
@ -11,7 +11,7 @@
|
||||
. $(dirname $0)/lib/probe.sh
|
||||
|
||||
libc=$(grep -w libc /proc/self/maps | head -1 | sed -r 's/.*[[:space:]](\/.*)/\1/g')
|
||||
nm -g $libc 2>/dev/null | fgrep -q inet_pton || exit 254
|
||||
nm -Dg $libc 2>/dev/null | fgrep -q inet_pton || exit 254
|
||||
|
||||
trace_libc_inet_pton_backtrace() {
|
||||
idx=0
|
||||
|
@ -29,6 +29,7 @@ struct annotate_browser {
|
||||
struct rb_node *curr_hot;
|
||||
struct annotation_line *selection;
|
||||
struct arch *arch;
|
||||
struct annotation_options *opts;
|
||||
bool searching_backwards;
|
||||
char search_bf[128];
|
||||
};
|
||||
@ -410,7 +411,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
notes = symbol__annotation(dl->ops.target.sym);
|
||||
pthread_mutex_lock(¬es->lock);
|
||||
|
||||
if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
|
||||
if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
ui__warning("Not enough memory for annotating '%s' symbol!\n",
|
||||
dl->ops.target.sym->name);
|
||||
@ -418,7 +419,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
|
||||
symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
|
||||
sym_title(ms->sym, ms->map, title, sizeof(title));
|
||||
ui_browser__show_title(&browser->b, title);
|
||||
return true;
|
||||
@ -817,24 +818,27 @@ out:
|
||||
}
|
||||
|
||||
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct annotation_options *opts)
|
||||
{
|
||||
return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
|
||||
return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
|
||||
}
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct annotation_options *opts)
|
||||
{
|
||||
/* reset abort key so that it can get Ctrl-C as a key */
|
||||
SLang_reset_tty();
|
||||
SLang_init_tty(0, 0, 0);
|
||||
|
||||
return map_symbol__tui_annotate(&he->ms, evsel, hbt);
|
||||
return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
|
||||
}
|
||||
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct annotation_options *opts)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct map_symbol ms = {
|
||||
@ -851,6 +855,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
|
||||
.priv = &ms,
|
||||
.use_navkeypressed = true,
|
||||
},
|
||||
.opts = opts,
|
||||
};
|
||||
int ret = -1, err;
|
||||
|
||||
@ -860,7 +865,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
|
||||
if (map->dso->annotate_warned)
|
||||
return -1;
|
||||
|
||||
err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
|
||||
err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
|
||||
if (err) {
|
||||
char msg[BUFSIZ];
|
||||
symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
|
||||
|
@ -1231,6 +1231,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
int width = browser->b.width;
|
||||
char folded_sign = ' ';
|
||||
bool current_entry = ui_browser__is_current_entry(&browser->b, row);
|
||||
bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
|
||||
off_t row_offset = entry->row_offset;
|
||||
bool first = true;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
@ -1240,7 +1241,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
browser->selection = &entry->ms;
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (use_callchain) {
|
||||
hist_entry__init_have_children(entry);
|
||||
folded_sign = hist_entry__folded(entry);
|
||||
}
|
||||
@ -1276,7 +1277,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
}
|
||||
|
||||
if (first) {
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (use_callchain) {
|
||||
ui_browser__printf(&browser->b, "%c ", folded_sign);
|
||||
width -= 2;
|
||||
}
|
||||
@ -1583,7 +1584,7 @@ hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
|
||||
int column = 0;
|
||||
int span = 0;
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
|
||||
ret = scnprintf(buf, size, " ");
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
return ret;
|
||||
@ -1987,7 +1988,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
bool first = true;
|
||||
int ret;
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
|
||||
folded_sign = hist_entry__folded(he);
|
||||
printed += fprintf(fp, "%c ", folded_sign);
|
||||
}
|
||||
@ -2175,7 +2176,8 @@ struct hist_browser *hist_browser__new(struct hists *hists)
|
||||
static struct hist_browser *
|
||||
perf_evsel_browser__new(struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_env *env)
|
||||
struct perf_env *env,
|
||||
struct annotation_options *annotation_opts)
|
||||
{
|
||||
struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
|
||||
|
||||
@ -2183,6 +2185,7 @@ perf_evsel_browser__new(struct perf_evsel *evsel,
|
||||
browser->hbt = hbt;
|
||||
browser->env = env;
|
||||
browser->title = hists_browser__scnprintf_title;
|
||||
browser->annotation_opts = annotation_opts;
|
||||
}
|
||||
return browser;
|
||||
}
|
||||
@ -2336,7 +2339,8 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
|
||||
struct hist_entry *he;
|
||||
int err;
|
||||
|
||||
if (!objdump_path && perf_env__lookup_objdump(browser->env))
|
||||
if (!browser->annotation_opts->objdump_path &&
|
||||
perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
|
||||
return 0;
|
||||
|
||||
notes = symbol__annotation(act->ms.sym);
|
||||
@ -2344,7 +2348,8 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
|
||||
return 0;
|
||||
|
||||
evsel = hists_to_evsel(browser->hists);
|
||||
err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
|
||||
err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
|
||||
browser->annotation_opts);
|
||||
he = hist_browser__selected_entry(browser);
|
||||
/*
|
||||
* offer option to annotate the other branch source or target
|
||||
@ -2667,7 +2672,7 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb,
|
||||
he->nr_rows = 0;
|
||||
}
|
||||
|
||||
if (!he->leaf || !symbol_conf.use_callchain)
|
||||
if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
|
||||
goto next;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL) {
|
||||
@ -2697,10 +2702,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
struct hist_browser_timer *hbt,
|
||||
float min_pcnt,
|
||||
struct perf_env *env,
|
||||
bool warn_lost_event)
|
||||
bool warn_lost_event,
|
||||
struct annotation_options *annotation_opts)
|
||||
{
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
|
||||
struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
|
||||
struct branch_info *bi;
|
||||
#define MAX_OPTIONS 16
|
||||
char *options[MAX_OPTIONS];
|
||||
@ -3062,6 +3068,7 @@ out:
|
||||
struct perf_evsel_menu {
|
||||
struct ui_browser b;
|
||||
struct perf_evsel *selection;
|
||||
struct annotation_options *annotation_opts;
|
||||
bool lost_events, lost_events_warned;
|
||||
float min_pcnt;
|
||||
struct perf_env *env;
|
||||
@ -3163,7 +3170,8 @@ browse_hists:
|
||||
true, hbt,
|
||||
menu->min_pcnt,
|
||||
menu->env,
|
||||
warn_lost_event);
|
||||
warn_lost_event,
|
||||
menu->annotation_opts);
|
||||
ui_browser__show_title(&menu->b, title);
|
||||
switch (key) {
|
||||
case K_TAB:
|
||||
@ -3222,7 +3230,8 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
struct hist_browser_timer *hbt,
|
||||
float min_pcnt,
|
||||
struct perf_env *env,
|
||||
bool warn_lost_event)
|
||||
bool warn_lost_event,
|
||||
struct annotation_options *annotation_opts)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evsel_menu menu = {
|
||||
@ -3237,6 +3246,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
},
|
||||
.min_pcnt = min_pcnt,
|
||||
.env = env,
|
||||
.annotation_opts = annotation_opts,
|
||||
};
|
||||
|
||||
ui_helpline__push("Press ESC to exit");
|
||||
@ -3257,7 +3267,8 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
struct hist_browser_timer *hbt,
|
||||
float min_pcnt,
|
||||
struct perf_env *env,
|
||||
bool warn_lost_event)
|
||||
bool warn_lost_event,
|
||||
struct annotation_options *annotation_opts)
|
||||
{
|
||||
int nr_entries = evlist->nr_entries;
|
||||
|
||||
@ -3267,7 +3278,8 @@ single_entry:
|
||||
|
||||
return perf_evsel__hists_browse(first, nr_entries, help,
|
||||
false, hbt, min_pcnt,
|
||||
env, warn_lost_event);
|
||||
env, warn_lost_event,
|
||||
annotation_opts);
|
||||
}
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
@ -3285,5 +3297,6 @@ single_entry:
|
||||
|
||||
return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
|
||||
hbt, min_pcnt, env,
|
||||
warn_lost_event);
|
||||
warn_lost_event,
|
||||
annotation_opts);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "ui/browser.h"
|
||||
|
||||
struct annotation_options;
|
||||
|
||||
struct hist_browser {
|
||||
struct ui_browser b;
|
||||
struct hists *hists;
|
||||
@ -12,6 +14,7 @@ struct hist_browser {
|
||||
struct hist_browser_timer *hbt;
|
||||
struct pstack *pstack;
|
||||
struct perf_env *env;
|
||||
struct annotation_options *annotation_opts;
|
||||
int print_seq;
|
||||
bool show_dso;
|
||||
bool show_headers;
|
||||
|
@ -169,7 +169,7 @@ static int symbol__gtk_annotate(struct symbol *sym, struct map *map,
|
||||
if (map->dso->annotate_warned)
|
||||
return -1;
|
||||
|
||||
err = symbol__annotate(sym, map, evsel, 0, NULL);
|
||||
err = symbol__annotate(sym, map, evsel, 0, &annotation__default_options, NULL);
|
||||
if (err) {
|
||||
char msg[BUFSIZ];
|
||||
symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
|
||||
|
@ -382,7 +382,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
||||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain && hists__has(hists, sym)) {
|
||||
if (hists__has_callchains(hists) &&
|
||||
symbol_conf.use_callchain && hists__has(hists, sym)) {
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = symbol_conf.cumulate_callchain ?
|
||||
h->stat_acc->period : h->stat.period;
|
||||
@ -479,7 +480,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain && he->leaf) {
|
||||
if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = symbol_conf.cumulate_callchain ?
|
||||
he->stat_acc->period : he->stat.period;
|
||||
|
@ -207,7 +207,7 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (a->thread != b->thread || !symbol_conf.use_callchain)
|
||||
if (a->thread != b->thread || !hist_entry__has_callchains(a) || !symbol_conf.use_callchain)
|
||||
return 0;
|
||||
|
||||
ret = b->callchain->max_depth - a->callchain->max_depth;
|
||||
|
@ -516,7 +516,7 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
|
||||
}
|
||||
printed += putc('\n', fp);
|
||||
|
||||
if (symbol_conf.use_callchain && he->leaf) {
|
||||
if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
|
||||
u64 total = hists__total_period(hists);
|
||||
|
||||
printed += hist_entry_callchain__fprintf(he, total, 0, fp);
|
||||
@ -550,7 +550,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
|
||||
ret = fprintf(fp, "%s\n", bf);
|
||||
|
||||
if (use_callchain)
|
||||
if (hist_entry__has_callchains(he) && use_callchain)
|
||||
callchain_ret = hist_entry_callchain__fprintf(he, total_period,
|
||||
0, fp);
|
||||
|
||||
|
@ -24,7 +24,6 @@ libperf-y += libstring.o
|
||||
libperf-y += bitmap.o
|
||||
libperf-y += hweight.o
|
||||
libperf-y += smt.o
|
||||
libperf-y += quote.o
|
||||
libperf-y += strbuf.o
|
||||
libperf-y += string.o
|
||||
libperf-y += strlist.o
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "debug.h"
|
||||
#include "annotate.h"
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "block-range.h"
|
||||
#include "string2.h"
|
||||
#include "arch/common.h"
|
||||
@ -46,11 +47,10 @@
|
||||
struct annotation_options annotation__default_options = {
|
||||
.use_offset = true,
|
||||
.jump_arrows = true,
|
||||
.annotate_src = true,
|
||||
.offset_level = ANNOTATION__OFFSET_JUMP_TARGETS,
|
||||
};
|
||||
|
||||
const char *disassembler_style;
|
||||
const char *objdump_path;
|
||||
static regex_t file_lineno;
|
||||
|
||||
static struct ins_ops *ins__find(struct arch *arch, const char *name);
|
||||
@ -678,10 +678,28 @@ static struct arch *arch__find(const char *name)
|
||||
return bsearch(name, architectures, nmemb, sizeof(struct arch), arch__key_cmp);
|
||||
}
|
||||
|
||||
int symbol__alloc_hist(struct symbol *sym)
|
||||
static struct annotated_source *annotated_source__new(void)
|
||||
{
|
||||
struct annotated_source *src = zalloc(sizeof(*src));
|
||||
|
||||
if (src != NULL)
|
||||
INIT_LIST_HEAD(&src->source);
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
static __maybe_unused void annotated_source__delete(struct annotated_source *src)
|
||||
{
|
||||
if (src == NULL)
|
||||
return;
|
||||
zfree(&src->histograms);
|
||||
zfree(&src->cycles_hist);
|
||||
free(src);
|
||||
}
|
||||
|
||||
static int annotated_source__alloc_histograms(struct annotated_source *src,
|
||||
size_t size, int nr_hists)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
size_t size = symbol__size(sym);
|
||||
size_t sizeof_sym_hist;
|
||||
|
||||
/*
|
||||
@ -701,17 +719,13 @@ int symbol__alloc_hist(struct symbol *sym)
|
||||
sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(struct sym_hist_entry));
|
||||
|
||||
/* Check for overflow in zalloc argument */
|
||||
if (sizeof_sym_hist > (SIZE_MAX - sizeof(*notes->src))
|
||||
/ symbol_conf.nr_events)
|
||||
if (sizeof_sym_hist > SIZE_MAX / nr_hists)
|
||||
return -1;
|
||||
|
||||
notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
|
||||
if (notes->src == NULL)
|
||||
return -1;
|
||||
notes->src->sizeof_sym_hist = sizeof_sym_hist;
|
||||
notes->src->nr_histograms = symbol_conf.nr_events;
|
||||
INIT_LIST_HEAD(¬es->src->source);
|
||||
return 0;
|
||||
src->sizeof_sym_hist = sizeof_sym_hist;
|
||||
src->nr_histograms = nr_hists;
|
||||
src->histograms = calloc(nr_hists, sizeof_sym_hist) ;
|
||||
return src->histograms ? 0 : -1;
|
||||
}
|
||||
|
||||
/* The cycles histogram is lazily allocated. */
|
||||
@ -741,14 +755,11 @@ void symbol__annotate_zero_histograms(struct symbol *sym)
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
}
|
||||
|
||||
static int __symbol__account_cycles(struct annotation *notes,
|
||||
static int __symbol__account_cycles(struct cyc_hist *ch,
|
||||
u64 start,
|
||||
unsigned offset, unsigned cycles,
|
||||
unsigned have_start)
|
||||
{
|
||||
struct cyc_hist *ch;
|
||||
|
||||
ch = notes->src->cycles_hist;
|
||||
/*
|
||||
* For now we can only account one basic block per
|
||||
* final jump. But multiple could be overlapping.
|
||||
@ -791,7 +802,7 @@ static int __symbol__account_cycles(struct annotation *notes,
|
||||
}
|
||||
|
||||
static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
|
||||
struct annotation *notes, int evidx, u64 addr,
|
||||
struct annotated_source *src, int evidx, u64 addr,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
unsigned offset;
|
||||
@ -807,7 +818,12 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
|
||||
}
|
||||
|
||||
offset = addr - sym->start;
|
||||
h = annotation__histogram(notes, evidx);
|
||||
h = annotated_source__histogram(src, evidx);
|
||||
if (h == NULL) {
|
||||
pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n",
|
||||
__func__, __LINE__, sym->name, sym->start, addr, sym->end, sym->type == STT_FUNC);
|
||||
return -ENOMEM;
|
||||
}
|
||||
h->nr_samples++;
|
||||
h->addr[offset].nr_samples++;
|
||||
h->period += sample->period;
|
||||
@ -820,45 +836,69 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct annotation *symbol__get_annotation(struct symbol *sym, bool cycles)
|
||||
static struct cyc_hist *symbol__cycles_hist(struct symbol *sym)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
|
||||
if (notes->src == NULL) {
|
||||
if (symbol__alloc_hist(sym) < 0)
|
||||
notes->src = annotated_source__new();
|
||||
if (notes->src == NULL)
|
||||
return NULL;
|
||||
goto alloc_cycles_hist;
|
||||
}
|
||||
if (!notes->src->cycles_hist && cycles) {
|
||||
if (symbol__alloc_hist_cycles(sym) < 0)
|
||||
|
||||
if (!notes->src->cycles_hist) {
|
||||
alloc_cycles_hist:
|
||||
symbol__alloc_hist_cycles(sym);
|
||||
}
|
||||
|
||||
return notes->src->cycles_hist;
|
||||
}
|
||||
|
||||
struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
|
||||
if (notes->src == NULL) {
|
||||
notes->src = annotated_source__new();
|
||||
if (notes->src == NULL)
|
||||
return NULL;
|
||||
goto alloc_histograms;
|
||||
}
|
||||
return notes;
|
||||
|
||||
if (notes->src->histograms == NULL) {
|
||||
alloc_histograms:
|
||||
annotated_source__alloc_histograms(notes->src, symbol__size(sym),
|
||||
nr_hists);
|
||||
}
|
||||
|
||||
return notes->src;
|
||||
}
|
||||
|
||||
static int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
|
||||
int evidx, u64 addr,
|
||||
struct perf_evsel *evsel, u64 addr,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct annotation *notes;
|
||||
struct annotated_source *src;
|
||||
|
||||
if (sym == NULL)
|
||||
return 0;
|
||||
notes = symbol__get_annotation(sym, false);
|
||||
if (notes == NULL)
|
||||
src = symbol__hists(sym, evsel->evlist->nr_entries);
|
||||
if (src == NULL)
|
||||
return -ENOMEM;
|
||||
return __symbol__inc_addr_samples(sym, map, notes, evidx, addr, sample);
|
||||
return __symbol__inc_addr_samples(sym, map, src, evsel->idx, addr, sample);
|
||||
}
|
||||
|
||||
static int symbol__account_cycles(u64 addr, u64 start,
|
||||
struct symbol *sym, unsigned cycles)
|
||||
{
|
||||
struct annotation *notes;
|
||||
struct cyc_hist *cycles_hist;
|
||||
unsigned offset;
|
||||
|
||||
if (sym == NULL)
|
||||
return 0;
|
||||
notes = symbol__get_annotation(sym, true);
|
||||
if (notes == NULL)
|
||||
cycles_hist = symbol__cycles_hist(sym);
|
||||
if (cycles_hist == NULL)
|
||||
return -ENOMEM;
|
||||
if (addr < sym->start || addr >= sym->end)
|
||||
return -ERANGE;
|
||||
@ -870,7 +910,7 @@ static int symbol__account_cycles(u64 addr, u64 start,
|
||||
start = 0;
|
||||
}
|
||||
offset = addr - sym->start;
|
||||
return __symbol__account_cycles(notes,
|
||||
return __symbol__account_cycles(cycles_hist,
|
||||
start ? start - sym->start : 0,
|
||||
offset, cycles,
|
||||
!!start);
|
||||
@ -974,15 +1014,15 @@ void annotation__compute_ipc(struct annotation *notes, size_t size)
|
||||
}
|
||||
|
||||
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
|
||||
int evidx)
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr, sample);
|
||||
return symbol__inc_addr_samples(ams->sym, ams->map, evsel, ams->al_addr, sample);
|
||||
}
|
||||
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
|
||||
int evidx, u64 ip)
|
||||
struct perf_evsel *evsel, u64 ip)
|
||||
{
|
||||
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip, sample);
|
||||
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evsel, ip, sample);
|
||||
}
|
||||
|
||||
static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms)
|
||||
@ -1031,6 +1071,7 @@ struct annotate_args {
|
||||
struct arch *arch;
|
||||
struct map_symbol ms;
|
||||
struct perf_evsel *evsel;
|
||||
struct annotation_options *options;
|
||||
s64 offset;
|
||||
char *line;
|
||||
int line_nr;
|
||||
@ -1572,6 +1613,7 @@ fallback:
|
||||
|
||||
static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
|
||||
{
|
||||
struct annotation_options *opts = args->options;
|
||||
struct map *map = args->ms.map;
|
||||
struct dso *dso = map->dso;
|
||||
char *command;
|
||||
@ -1619,13 +1661,13 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
|
||||
"%s %s%s --start-address=0x%016" PRIx64
|
||||
" --stop-address=0x%016" PRIx64
|
||||
" -l -d %s %s -C \"%s\" 2>/dev/null|grep -v \"%s:\"|expand",
|
||||
objdump_path ? objdump_path : "objdump",
|
||||
disassembler_style ? "-M " : "",
|
||||
disassembler_style ? disassembler_style : "",
|
||||
opts->objdump_path ?: "objdump",
|
||||
opts->disassembler_style ? "-M " : "",
|
||||
opts->disassembler_style ?: "",
|
||||
map__rip_2objdump(map, sym->start),
|
||||
map__rip_2objdump(map, sym->end),
|
||||
symbol_conf.annotate_asm_raw ? "" : "--no-show-raw",
|
||||
symbol_conf.annotate_src ? "-S" : "",
|
||||
opts->show_asm_raw ? "" : "--no-show-raw",
|
||||
opts->annotate_src ? "-S" : "",
|
||||
symfs_filename, symfs_filename);
|
||||
|
||||
if (err < 0) {
|
||||
@ -1767,11 +1809,13 @@ void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel)
|
||||
|
||||
int symbol__annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, size_t privsize,
|
||||
struct annotation_options *options,
|
||||
struct arch **parch)
|
||||
{
|
||||
struct annotate_args args = {
|
||||
.privsize = privsize,
|
||||
.evsel = evsel,
|
||||
.options = options,
|
||||
};
|
||||
struct perf_env *env = perf_evsel__env(evsel);
|
||||
const char *arch_name = perf_env__arch(env);
|
||||
@ -1949,8 +1993,8 @@ static int annotated_source__addr_fmt_width(struct list_head *lines, u64 start)
|
||||
}
|
||||
|
||||
int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool full_paths,
|
||||
int min_pcnt, int max_lines, int context)
|
||||
struct perf_evsel *evsel,
|
||||
struct annotation_options *opts)
|
||||
{
|
||||
struct dso *dso = map->dso;
|
||||
char *filename;
|
||||
@ -1962,6 +2006,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
u64 start = map__rip_2objdump(map, sym->start);
|
||||
int printed = 2, queue_len = 0, addr_fmt_width;
|
||||
int more = 0;
|
||||
bool context = opts->context;
|
||||
u64 len;
|
||||
int width = symbol_conf.show_total_period ? 12 : 8;
|
||||
int graph_dotted_len;
|
||||
@ -1971,7 +2016,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
if (!filename)
|
||||
return -ENOMEM;
|
||||
|
||||
if (full_paths)
|
||||
if (opts->full_path)
|
||||
d_filename = filename;
|
||||
else
|
||||
d_filename = basename(filename);
|
||||
@ -2006,7 +2051,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
}
|
||||
|
||||
err = annotation_line__print(pos, sym, start, evsel, len,
|
||||
min_pcnt, printed, max_lines,
|
||||
opts->min_pcnt, printed, opts->max_lines,
|
||||
queue, addr_fmt_width);
|
||||
|
||||
switch (err) {
|
||||
@ -2339,20 +2384,19 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map,
|
||||
}
|
||||
|
||||
int symbol__tty_annotate2(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool print_lines,
|
||||
bool full_paths)
|
||||
struct perf_evsel *evsel,
|
||||
struct annotation_options *opts)
|
||||
{
|
||||
struct dso *dso = map->dso;
|
||||
struct rb_root source_line = RB_ROOT;
|
||||
struct annotation_options opts = annotation__default_options;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
char buf[1024];
|
||||
|
||||
if (symbol__annotate2(sym, map, evsel, &opts, NULL) < 0)
|
||||
if (symbol__annotate2(sym, map, evsel, opts, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (print_lines) {
|
||||
srcline_full_filename = full_paths;
|
||||
if (opts->print_lines) {
|
||||
srcline_full_filename = opts->full_path;
|
||||
symbol__calc_lines(sym, map, &source_line);
|
||||
print_summary(&source_line, dso->long_name);
|
||||
}
|
||||
@ -2367,25 +2411,24 @@ int symbol__tty_annotate2(struct symbol *sym, struct map *map,
|
||||
}
|
||||
|
||||
int symbol__tty_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool print_lines,
|
||||
bool full_paths, int min_pcnt, int max_lines)
|
||||
struct perf_evsel *evsel,
|
||||
struct annotation_options *opts)
|
||||
{
|
||||
struct dso *dso = map->dso;
|
||||
struct rb_root source_line = RB_ROOT;
|
||||
|
||||
if (symbol__annotate(sym, map, evsel, 0, NULL) < 0)
|
||||
if (symbol__annotate(sym, map, evsel, 0, opts, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
symbol__calc_percent(sym, evsel);
|
||||
|
||||
if (print_lines) {
|
||||
srcline_full_filename = full_paths;
|
||||
if (opts->print_lines) {
|
||||
srcline_full_filename = opts->full_path;
|
||||
symbol__calc_lines(sym, map, &source_line);
|
||||
print_summary(&source_line, dso->long_name);
|
||||
}
|
||||
|
||||
symbol__annotate_printf(sym, map, evsel, full_paths,
|
||||
min_pcnt, max_lines, 0);
|
||||
symbol__annotate_printf(sym, map, evsel, opts);
|
||||
|
||||
annotated_source__purge(symbol__annotation(sym)->src);
|
||||
|
||||
@ -2620,7 +2663,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *ev
|
||||
if (perf_evsel__is_group_event(evsel))
|
||||
nr_pcnt = evsel->nr_members;
|
||||
|
||||
err = symbol__annotate(sym, map, evsel, 0, parch);
|
||||
err = symbol__annotate(sym, map, evsel, 0, options, parch);
|
||||
if (err)
|
||||
goto out_free_offsets;
|
||||
|
||||
|
@ -67,12 +67,21 @@ struct annotation_options {
|
||||
bool hide_src_code,
|
||||
use_offset,
|
||||
jump_arrows,
|
||||
print_lines,
|
||||
full_path,
|
||||
show_linenr,
|
||||
show_nr_jumps,
|
||||
show_nr_samples,
|
||||
show_total_period,
|
||||
show_minmax_cycle;
|
||||
show_minmax_cycle,
|
||||
show_asm_raw,
|
||||
annotate_src;
|
||||
u8 offset_level;
|
||||
int min_pcnt;
|
||||
int max_lines;
|
||||
int context;
|
||||
const char *objdump_path;
|
||||
const char *disassembler_style;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -201,7 +210,11 @@ struct cyc_hist {
|
||||
|
||||
/** struct annotated_source - symbols with hits have this attached as in sannotation
|
||||
*
|
||||
* @histogram: Array of addr hit histograms per event being monitored
|
||||
* @histograms: Array of addr hit histograms per event being monitored
|
||||
* nr_histograms: This may not be the same as evsel->evlist->nr_entries if
|
||||
* we have more than a group in a evlist, where we will want
|
||||
* to see each group separately, that is why symbol__annotate2()
|
||||
* sets src->nr_histograms to evsel->nr_members.
|
||||
* @lines: If 'print_lines' is specified, per source code line percentages
|
||||
* @source: source parsed from a disassembler like objdump -dS
|
||||
* @cyc_hist: Average cycles per basic block
|
||||
@ -217,7 +230,7 @@ struct annotated_source {
|
||||
int nr_histograms;
|
||||
size_t sizeof_sym_hist;
|
||||
struct cyc_hist *cycles_hist;
|
||||
struct sym_hist histograms[0];
|
||||
struct sym_hist *histograms;
|
||||
};
|
||||
|
||||
struct annotation {
|
||||
@ -267,10 +280,14 @@ void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
|
||||
void annotation__update_column_widths(struct annotation *notes);
|
||||
void annotation__init_column_widths(struct annotation *notes, struct symbol *sym);
|
||||
|
||||
static inline struct sym_hist *annotated_source__histogram(struct annotated_source *src, int idx)
|
||||
{
|
||||
return ((void *)src->histograms) + (src->sizeof_sym_hist * idx);
|
||||
}
|
||||
|
||||
static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx)
|
||||
{
|
||||
return (((void *)¬es->src->histograms) +
|
||||
(notes->src->sizeof_sym_hist * idx));
|
||||
return annotated_source__histogram(notes->src, idx);
|
||||
}
|
||||
|
||||
static inline struct annotation *symbol__annotation(struct symbol *sym)
|
||||
@ -279,20 +296,21 @@ static inline struct annotation *symbol__annotation(struct symbol *sym)
|
||||
}
|
||||
|
||||
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
|
||||
int evidx);
|
||||
struct perf_evsel *evsel);
|
||||
|
||||
int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
|
||||
struct addr_map_symbol *start,
|
||||
unsigned cycles);
|
||||
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
|
||||
int evidx, u64 addr);
|
||||
struct perf_evsel *evsel, u64 addr);
|
||||
|
||||
int symbol__alloc_hist(struct symbol *sym);
|
||||
struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists);
|
||||
void symbol__annotate_zero_histograms(struct symbol *sym);
|
||||
|
||||
int symbol__annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, size_t privsize,
|
||||
struct annotation_options *options,
|
||||
struct arch **parch);
|
||||
int symbol__annotate2(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
@ -320,8 +338,8 @@ int symbol__strerror_disassemble(struct symbol *sym, struct map *map,
|
||||
int errnum, char *buf, size_t buflen);
|
||||
|
||||
int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool full_paths,
|
||||
int min_pcnt, int max_lines, int context);
|
||||
struct perf_evsel *evsel,
|
||||
struct annotation_options *options);
|
||||
int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp);
|
||||
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
|
||||
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
|
||||
@ -332,30 +350,27 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel)
|
||||
bool ui__has_annotation(void);
|
||||
|
||||
int symbol__tty_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool print_lines,
|
||||
bool full_paths, int min_pcnt, int max_lines);
|
||||
struct perf_evsel *evsel, struct annotation_options *opts);
|
||||
|
||||
int symbol__tty_annotate2(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool print_lines,
|
||||
bool full_paths);
|
||||
struct perf_evsel *evsel, struct annotation_options *opts);
|
||||
|
||||
#ifdef HAVE_SLANG_SUPPORT
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
struct hist_browser_timer *hbt,
|
||||
struct annotation_options *opts);
|
||||
#else
|
||||
static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
|
||||
struct map *map __maybe_unused,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused,
|
||||
struct annotation_options *opts __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern const char *disassembler_style;
|
||||
|
||||
void annotation_config__init(void);
|
||||
|
||||
#endif /* __PERF_ANNOTATE_H */
|
||||
|
@ -93,20 +93,17 @@ static int open_cgroup(const char *name)
|
||||
static struct cgroup *evlist__find_cgroup(struct perf_evlist *evlist, const char *str)
|
||||
{
|
||||
struct perf_evsel *counter;
|
||||
struct cgroup *cgrp = NULL;
|
||||
/*
|
||||
* check if cgrp is already defined, if so we reuse it
|
||||
*/
|
||||
evlist__for_each_entry(evlist, counter) {
|
||||
if (!counter->cgrp)
|
||||
continue;
|
||||
if (!strcmp(counter->cgrp->name, str)) {
|
||||
cgrp = cgroup__get(counter->cgrp);
|
||||
break;
|
||||
}
|
||||
if (!strcmp(counter->cgrp->name, str))
|
||||
return cgroup__get(counter->cgrp);
|
||||
}
|
||||
|
||||
return cgrp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct cgroup *cgroup__new(const char *name)
|
||||
|
@ -354,6 +354,8 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
|
||||
if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) ||
|
||||
(strncmp(name, "[guest.kernel.kallsyms", 22) == 0) ||
|
||||
(strncmp(name, "[vdso]", 6) == 0) ||
|
||||
(strncmp(name, "[vdso32]", 8) == 0) ||
|
||||
(strncmp(name, "[vdsox32]", 9) == 0) ||
|
||||
(strncmp(name, "[vsyscall]", 10) == 0)) {
|
||||
m->kmod = false;
|
||||
|
||||
|
@ -2197,7 +2197,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_CALLCHAIN) {
|
||||
if (evsel__has_callchain(evsel)) {
|
||||
const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
|
||||
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
@ -2857,7 +2857,7 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
|
||||
"Hint: Try again after reducing the number of events.\n"
|
||||
"Hint: Try increasing the limit with 'ulimit -n <limit>'");
|
||||
case ENOMEM:
|
||||
if ((evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0 &&
|
||||
if (evsel__has_callchain(evsel) &&
|
||||
access("/proc/sys/kernel/perf_event_max_stack", F_OK) == 0)
|
||||
return scnprintf(msg, size,
|
||||
"Not enough memory to setup event with callchain.\n"
|
||||
|
@ -459,6 +459,11 @@ static inline bool perf_evsel__has_branch_callstack(const struct perf_evsel *evs
|
||||
return evsel->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;
|
||||
}
|
||||
|
||||
static inline bool evsel__has_callchain(const struct perf_evsel *evsel)
|
||||
{
|
||||
return (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0;
|
||||
}
|
||||
|
||||
typedef int (*attr__fprintf_f)(FILE *, const char *, const char *, void *);
|
||||
|
||||
int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
|
||||
|
@ -1459,8 +1459,24 @@ static void print_cmdline(struct feat_fd *ff, FILE *fp)
|
||||
|
||||
fprintf(fp, "# cmdline : ");
|
||||
|
||||
for (i = 0; i < nr; i++)
|
||||
fprintf(fp, "%s ", ff->ph->env.cmdline_argv[i]);
|
||||
for (i = 0; i < nr; i++) {
|
||||
char *argv_i = strdup(ff->ph->env.cmdline_argv[i]);
|
||||
if (!argv_i) {
|
||||
fprintf(fp, "%s ", ff->ph->env.cmdline_argv[i]);
|
||||
} else {
|
||||
char *mem = argv_i;
|
||||
do {
|
||||
char *quote = strchr(argv_i, '\'');
|
||||
if (!quote)
|
||||
break;
|
||||
*quote++ = '\0';
|
||||
fprintf(fp, "%s\\\'", argv_i);
|
||||
argv_i = quote;
|
||||
} while (1);
|
||||
fprintf(fp, "%s ", argv_i);
|
||||
free(mem);
|
||||
}
|
||||
}
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
@ -3312,8 +3328,6 @@ int perf_session__read_header(struct perf_session *session)
|
||||
lseek(fd, tmp, SEEK_SET);
|
||||
}
|
||||
|
||||
symbol_conf.nr_events = nr_attrs;
|
||||
|
||||
perf_header__process_sections(header, fd, &session->tevent,
|
||||
perf_file_section__process);
|
||||
|
||||
@ -3739,8 +3753,6 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
|
||||
perf_evlist__id_add(evlist, evsel, 0, i, event->attr.id[i]);
|
||||
}
|
||||
|
||||
symbol_conf.nr_events = evlist->nr_entries;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -410,7 +410,7 @@ static int hist_entry__init(struct hist_entry *he,
|
||||
map__get(he->mem_info->daddr.map);
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain)
|
||||
callchain_init(he->callchain);
|
||||
|
||||
if (he->raw_data) {
|
||||
@ -492,7 +492,7 @@ static u8 symbol__parent_filter(const struct symbol *parent)
|
||||
|
||||
static void hist_entry__add_callchain_period(struct hist_entry *he, u64 period)
|
||||
{
|
||||
if (!symbol_conf.use_callchain)
|
||||
if (!hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
|
||||
return;
|
||||
|
||||
he->hists->callchain_period += period;
|
||||
@ -986,7 +986,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
|
||||
iter->he = he;
|
||||
he_cache[iter->curr++] = he;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain)
|
||||
callchain_append(he->callchain, &cursor, sample->period);
|
||||
return 0;
|
||||
}
|
||||
@ -1039,7 +1039,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
|
||||
int err, err2;
|
||||
struct map *alm = NULL;
|
||||
|
||||
if (al && al->map)
|
||||
if (al)
|
||||
alm = map__get(al->map);
|
||||
|
||||
err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent,
|
||||
@ -1373,7 +1373,8 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
|
||||
if (new_he) {
|
||||
new_he->leaf = true;
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (hist_entry__has_callchains(new_he) &&
|
||||
symbol_conf.use_callchain) {
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
if (callchain_merge(&callchain_cursor,
|
||||
new_he->callchain,
|
||||
@ -1414,7 +1415,7 @@ static int hists__collapse_insert_entry(struct hists *hists,
|
||||
if (symbol_conf.cumulate_callchain)
|
||||
he_stat__add_stat(iter->stat_acc, he->stat_acc);
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
if (callchain_merge(&callchain_cursor,
|
||||
iter->callchain,
|
||||
@ -1757,7 +1758,7 @@ void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *pro
|
||||
bool use_callchain;
|
||||
|
||||
if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph)
|
||||
use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN;
|
||||
use_callchain = evsel__has_callchain(evsel);
|
||||
else
|
||||
use_callchain = symbol_conf.use_callchain;
|
||||
|
||||
|
@ -220,6 +220,12 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel)
|
||||
return &hevsel->hists;
|
||||
}
|
||||
|
||||
static __pure inline bool hists__has_callchains(struct hists *hists)
|
||||
{
|
||||
const struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
return evsel__has_callchain(evsel);
|
||||
}
|
||||
|
||||
int hists__init(void);
|
||||
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list);
|
||||
|
||||
@ -419,19 +425,24 @@ struct hist_browser_timer {
|
||||
int refresh;
|
||||
};
|
||||
|
||||
struct annotation_options;
|
||||
|
||||
#ifdef HAVE_SLANG_SUPPORT
|
||||
#include "../ui/keysyms.h"
|
||||
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
struct hist_browser_timer *hbt,
|
||||
struct annotation_options *annotation_opts);
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
struct hist_browser_timer *hbt,
|
||||
struct annotation_options *annotation_opts);
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
struct hist_browser_timer *hbt,
|
||||
float min_pcnt,
|
||||
struct perf_env *env,
|
||||
bool warn_lost_event);
|
||||
bool warn_lost_event,
|
||||
struct annotation_options *annotation_options);
|
||||
int script_browse(const char *script_opt);
|
||||
#else
|
||||
static inline
|
||||
@ -440,20 +451,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
struct hist_browser_timer *hbt __maybe_unused,
|
||||
float min_pcnt __maybe_unused,
|
||||
struct perf_env *env __maybe_unused,
|
||||
bool warn_lost_event __maybe_unused)
|
||||
bool warn_lost_event __maybe_unused,
|
||||
struct annotation_options *annotation_options __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int map_symbol__tui_annotate(struct map_symbol *ms __maybe_unused,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused,
|
||||
struct annotation_options *annotation_options __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused,
|
||||
struct annotation_options *annotation_opts __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -113,6 +113,7 @@ struct intel_pt_decoder {
|
||||
bool have_cyc;
|
||||
bool fixup_last_mtc;
|
||||
bool have_last_ip;
|
||||
enum intel_pt_param_flags flags;
|
||||
uint64_t pos;
|
||||
uint64_t last_ip;
|
||||
uint64_t ip;
|
||||
@ -226,6 +227,8 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params)
|
||||
decoder->return_compression = params->return_compression;
|
||||
decoder->branch_enable = params->branch_enable;
|
||||
|
||||
decoder->flags = params->flags;
|
||||
|
||||
decoder->period = params->period;
|
||||
decoder->period_type = params->period_type;
|
||||
|
||||
@ -1097,6 +1100,15 @@ static bool intel_pt_fup_event(struct intel_pt_decoder *decoder)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool intel_pt_fup_with_nlip(struct intel_pt_decoder *decoder,
|
||||
struct intel_pt_insn *intel_pt_insn,
|
||||
uint64_t ip, int err)
|
||||
{
|
||||
return decoder->flags & INTEL_PT_FUP_WITH_NLIP && !err &&
|
||||
intel_pt_insn->branch == INTEL_PT_BR_INDIRECT &&
|
||||
ip == decoder->ip + intel_pt_insn->length;
|
||||
}
|
||||
|
||||
static int intel_pt_walk_fup(struct intel_pt_decoder *decoder)
|
||||
{
|
||||
struct intel_pt_insn intel_pt_insn;
|
||||
@ -1109,10 +1121,11 @@ static int intel_pt_walk_fup(struct intel_pt_decoder *decoder)
|
||||
err = intel_pt_walk_insn(decoder, &intel_pt_insn, ip);
|
||||
if (err == INTEL_PT_RETURN)
|
||||
return 0;
|
||||
if (err == -EAGAIN) {
|
||||
if (err == -EAGAIN ||
|
||||
intel_pt_fup_with_nlip(decoder, &intel_pt_insn, ip, err)) {
|
||||
if (intel_pt_fup_event(decoder))
|
||||
return 0;
|
||||
return err;
|
||||
return -EAGAIN;
|
||||
}
|
||||
decoder->set_fup_tx_flags = false;
|
||||
if (err)
|
||||
@ -1376,7 +1389,6 @@ static int intel_pt_overflow(struct intel_pt_decoder *decoder)
|
||||
{
|
||||
intel_pt_log("ERROR: Buffer overflow\n");
|
||||
intel_pt_clear_tx_flags(decoder);
|
||||
decoder->have_tma = false;
|
||||
decoder->cbr = 0;
|
||||
decoder->timestamp_insn_cnt = 0;
|
||||
decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
|
||||
@ -1604,7 +1616,6 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
|
||||
case INTEL_PT_PSB:
|
||||
case INTEL_PT_TSC:
|
||||
case INTEL_PT_TMA:
|
||||
case INTEL_PT_CBR:
|
||||
case INTEL_PT_MODE_TSX:
|
||||
case INTEL_PT_BAD:
|
||||
case INTEL_PT_PSBEND:
|
||||
@ -1620,6 +1631,10 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
|
||||
decoder->pkt_step = 0;
|
||||
return -ENOENT;
|
||||
|
||||
case INTEL_PT_CBR:
|
||||
intel_pt_calc_cbr(decoder);
|
||||
break;
|
||||
|
||||
case INTEL_PT_OVF:
|
||||
return intel_pt_overflow(decoder);
|
||||
|
||||
|
@ -60,6 +60,14 @@ enum {
|
||||
INTEL_PT_ERR_MAX,
|
||||
};
|
||||
|
||||
enum intel_pt_param_flags {
|
||||
/*
|
||||
* FUP packet can contain next linear instruction pointer instead of
|
||||
* current linear instruction pointer.
|
||||
*/
|
||||
INTEL_PT_FUP_WITH_NLIP = 1 << 0,
|
||||
};
|
||||
|
||||
struct intel_pt_state {
|
||||
enum intel_pt_sample_type type;
|
||||
int err;
|
||||
@ -106,6 +114,7 @@ struct intel_pt_params {
|
||||
unsigned int mtc_period;
|
||||
uint32_t tsc_ctc_ratio_n;
|
||||
uint32_t tsc_ctc_ratio_d;
|
||||
enum intel_pt_param_flags flags;
|
||||
};
|
||||
|
||||
struct intel_pt_decoder;
|
||||
|
@ -749,6 +749,7 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
|
||||
unsigned int queue_nr)
|
||||
{
|
||||
struct intel_pt_params params = { .get_trace = 0, };
|
||||
struct perf_env *env = pt->machine->env;
|
||||
struct intel_pt_queue *ptq;
|
||||
|
||||
ptq = zalloc(sizeof(struct intel_pt_queue));
|
||||
@ -830,6 +831,9 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
|
||||
}
|
||||
}
|
||||
|
||||
if (env->cpuid && !strncmp(env->cpuid, "GenuineIntel,6,92,", 18))
|
||||
params.flags |= INTEL_PT_FUP_WITH_NLIP;
|
||||
|
||||
ptq->decoder = intel_pt_decoder_new(¶ms);
|
||||
if (!ptq->decoder)
|
||||
goto out_free;
|
||||
@ -1521,6 +1525,7 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
|
||||
|
||||
if (intel_pt_is_switch_ip(ptq, state->to_ip)) {
|
||||
switch (ptq->switch_state) {
|
||||
case INTEL_PT_SS_NOT_TRACING:
|
||||
case INTEL_PT_SS_UNKNOWN:
|
||||
case INTEL_PT_SS_EXPECTING_SWITCH_IP:
|
||||
err = intel_pt_next_tid(pt, ptq);
|
||||
|
@ -415,16 +415,20 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp)
|
||||
return fprintf(fp, "%s", dsoname);
|
||||
}
|
||||
|
||||
char *map__srcline(struct map *map, u64 addr, struct symbol *sym)
|
||||
{
|
||||
if (map == NULL)
|
||||
return SRCLINE_UNKNOWN;
|
||||
return get_srcline(map->dso, map__rip_2objdump(map, addr), sym, true, true, addr);
|
||||
}
|
||||
|
||||
int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
|
||||
FILE *fp)
|
||||
{
|
||||
char *srcline;
|
||||
int ret = 0;
|
||||
|
||||
if (map && map->dso) {
|
||||
srcline = get_srcline(map->dso,
|
||||
map__rip_2objdump(map, addr), NULL,
|
||||
true, true, addr);
|
||||
char *srcline = map__srcline(map, addr, NULL);
|
||||
if (srcline != SRCLINE_UNKNOWN)
|
||||
ret = fprintf(fp, "%s%s", prefix, srcline);
|
||||
free_srcline(srcline);
|
||||
@ -445,6 +449,20 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
|
||||
*/
|
||||
u64 map__rip_2objdump(struct map *map, u64 rip)
|
||||
{
|
||||
struct kmap *kmap = __map__kmap(map);
|
||||
|
||||
/*
|
||||
* vmlinux does not have program headers for PTI entry trampolines and
|
||||
* kcore may not either. However the trampoline object code is on the
|
||||
* main kernel map, so just use that instead.
|
||||
*/
|
||||
if (kmap && is_entry_trampoline(kmap->name) && kmap->kmaps && kmap->kmaps->machine) {
|
||||
struct map *kernel_map = machine__kernel_map(kmap->kmaps->machine);
|
||||
|
||||
if (kernel_map)
|
||||
map = kernel_map;
|
||||
}
|
||||
|
||||
if (!map->dso->adjust_symbols)
|
||||
return rip;
|
||||
|
||||
|
@ -169,6 +169,7 @@ static inline void __map__zput(struct map **map)
|
||||
int map__overlap(struct map *l, struct map *r);
|
||||
size_t map__fprintf(struct map *map, FILE *fp);
|
||||
size_t map__fprintf_dsoname(struct map *map, FILE *fp);
|
||||
char *map__srcline(struct map *map, u64 addr, struct symbol *sym);
|
||||
int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
|
||||
FILE *fp);
|
||||
|
||||
|
@ -53,7 +53,21 @@ static int str(yyscan_t scanner, int token)
|
||||
YYSTYPE *yylval = parse_events_get_lval(scanner);
|
||||
char *text = parse_events_get_text(scanner);
|
||||
|
||||
yylval->str = strdup(text);
|
||||
if (text[0] != '\'') {
|
||||
yylval->str = strdup(text);
|
||||
} else {
|
||||
/*
|
||||
* If a text tag specified on the command line
|
||||
* contains opening single quite ' then it is
|
||||
* expected that the tag ends with single quote
|
||||
* as well, like this:
|
||||
* name=\'CPU_CLK_UNHALTED.THREAD:cmask=1\'
|
||||
* quotes need to be escaped to bypass shell
|
||||
* processing.
|
||||
*/
|
||||
yylval->str = strndup(&text[1], strlen(text) - 2);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
@ -176,6 +190,7 @@ num_dec [0-9]+
|
||||
num_hex 0x[a-fA-F0-9]+
|
||||
num_raw_hex [a-fA-F0-9]+
|
||||
name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]]*
|
||||
name_tag [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\']
|
||||
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]*
|
||||
drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)?
|
||||
/* If you add a modifier you need to update check_modifier() */
|
||||
@ -344,6 +359,7 @@ r{num_raw_hex} { return raw(yyscanner); }
|
||||
{bpf_object} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); }
|
||||
{bpf_source} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); }
|
||||
{name} { return pmu_str_check(yyscanner); }
|
||||
{name_tag} { return str(yyscanner, PE_NAME); }
|
||||
"/" { BEGIN(config); return '/'; }
|
||||
- { return '-'; }
|
||||
, { BEGIN(event); return ','; }
|
||||
|
@ -73,6 +73,7 @@ static void inc_group_count(struct list_head *list,
|
||||
%type <num> value_sym
|
||||
%type <head> event_config
|
||||
%type <head> opt_event_config
|
||||
%type <head> opt_pmu_config
|
||||
%type <term> event_term
|
||||
%type <head> event_pmu
|
||||
%type <head> event_legacy_symbol
|
||||
@ -224,7 +225,7 @@ event_def: event_pmu |
|
||||
event_bpf_file
|
||||
|
||||
event_pmu:
|
||||
PE_NAME opt_event_config
|
||||
PE_NAME opt_pmu_config
|
||||
{
|
||||
struct list_head *list, *orig_terms, *terms;
|
||||
|
||||
@ -496,6 +497,17 @@ opt_event_config:
|
||||
$$ = NULL;
|
||||
}
|
||||
|
||||
opt_pmu_config:
|
||||
'/' event_config '/'
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
|
|
||||
'/' '/'
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
|
||||
start_terms: event_config
|
||||
{
|
||||
struct parse_events_state *parse_state = _parse_state;
|
||||
|
@ -165,8 +165,7 @@ static struct map *kernel_get_module_map(const char *module)
|
||||
if (strncmp(pos->dso->short_name + 1, module,
|
||||
pos->dso->short_name_len - 2) == 0 &&
|
||||
module[pos->dso->short_name_len - 2] == '\0') {
|
||||
map__get(pos);
|
||||
return pos;
|
||||
return map__get(pos);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
@ -1,62 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Help to copy the thing properly quoted for the shell safety.
|
||||
* any single quote is replaced with '\'', any exclamation point
|
||||
* is replaced with '\!', and the whole thing is enclosed in a
|
||||
*
|
||||
* E.g.
|
||||
* original sq_quote result
|
||||
* name ==> name ==> 'name'
|
||||
* a b ==> a b ==> 'a b'
|
||||
* a'b ==> a'\''b ==> 'a'\''b'
|
||||
* a!b ==> a'\!'b ==> 'a'\!'b'
|
||||
*/
|
||||
static inline int need_bs_quote(char c)
|
||||
{
|
||||
return (c == '\'' || c == '!');
|
||||
}
|
||||
|
||||
static int sq_quote_buf(struct strbuf *dst, const char *src)
|
||||
{
|
||||
char *to_free = NULL;
|
||||
int ret;
|
||||
|
||||
if (dst->buf == src)
|
||||
to_free = strbuf_detach(dst, NULL);
|
||||
|
||||
ret = strbuf_addch(dst, '\'');
|
||||
while (!ret && *src) {
|
||||
size_t len = strcspn(src, "'!");
|
||||
ret = strbuf_add(dst, src, len);
|
||||
src += len;
|
||||
while (!ret && need_bs_quote(*src))
|
||||
ret = strbuf_addf(dst, "'\\%c\'", *src++);
|
||||
}
|
||||
if (!ret)
|
||||
ret = strbuf_addch(dst, '\'');
|
||||
free(to_free);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
/* Copy into destination buffer. */
|
||||
ret = strbuf_grow(dst, 255);
|
||||
for (i = 0; !ret && argv[i]; ++i) {
|
||||
ret = strbuf_addch(dst, ' ');
|
||||
if (ret)
|
||||
break;
|
||||
ret = sq_quote_buf(dst, argv[i]);
|
||||
if (maxlen && dst->len > maxlen)
|
||||
return -ENOSPC;
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __PERF_QUOTE_H
|
||||
#define __PERF_QUOTE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Help to copy the thing properly quoted for the shell safety.
|
||||
* any single quote is replaced with '\'', any exclamation point
|
||||
* is replaced with '\!', and the whole thing is enclosed in a
|
||||
* single quote pair.
|
||||
*
|
||||
* For example, if you are passing the result to system() as an
|
||||
* argument:
|
||||
*
|
||||
* sprintf(cmd, "foobar %s %s", sq_quote(arg0), sq_quote(arg1))
|
||||
*
|
||||
* would be appropriate. If the system() is going to call ssh to
|
||||
* run the command on the other side:
|
||||
*
|
||||
* sprintf(cmd, "git-diff-tree %s %s", sq_quote(arg0), sq_quote(arg1));
|
||||
* sprintf(rcmd, "ssh %s %s", sq_util/quote.host), sq_quote(cmd));
|
||||
*
|
||||
* Note that the above examples leak memory! Remember to free result from
|
||||
* sq_quote() in a real application.
|
||||
*/
|
||||
|
||||
struct strbuf;
|
||||
|
||||
int sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
|
||||
|
||||
#endif /* __PERF_QUOTE_H */
|
@ -48,6 +48,7 @@
|
||||
#include "cpumap.h"
|
||||
#include "print_binary.h"
|
||||
#include "stat.h"
|
||||
#include "mem-events.h"
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
#define _PyUnicode_FromString(arg) \
|
||||
@ -372,6 +373,19 @@ static PyObject *get_field_numeric_entry(struct event_format *event,
|
||||
return obj;
|
||||
}
|
||||
|
||||
static const char *get_dsoname(struct map *map)
|
||||
{
|
||||
const char *dsoname = "[unknown]";
|
||||
|
||||
if (map && map->dso) {
|
||||
if (symbol_conf.show_kernel_path && map->dso->long_name)
|
||||
dsoname = map->dso->long_name;
|
||||
else
|
||||
dsoname = map->dso->name;
|
||||
}
|
||||
|
||||
return dsoname;
|
||||
}
|
||||
|
||||
static PyObject *python_process_callchain(struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
@ -427,14 +441,8 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
|
||||
}
|
||||
|
||||
if (node->map) {
|
||||
struct map *map = node->map;
|
||||
const char *dsoname = "[unknown]";
|
||||
if (map && map->dso) {
|
||||
if (symbol_conf.show_kernel_path && map->dso->long_name)
|
||||
dsoname = map->dso->long_name;
|
||||
else
|
||||
dsoname = map->dso->name;
|
||||
}
|
||||
const char *dsoname = get_dsoname(node->map);
|
||||
|
||||
pydict_set_item_string_decref(pyelem, "dso",
|
||||
_PyUnicode_FromString(dsoname));
|
||||
}
|
||||
@ -448,6 +456,166 @@ exit:
|
||||
return pylist;
|
||||
}
|
||||
|
||||
static PyObject *python_process_brstack(struct perf_sample *sample,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct branch_stack *br = sample->branch_stack;
|
||||
PyObject *pylist;
|
||||
u64 i;
|
||||
|
||||
pylist = PyList_New(0);
|
||||
if (!pylist)
|
||||
Py_FatalError("couldn't create Python list");
|
||||
|
||||
if (!(br && br->nr))
|
||||
goto exit;
|
||||
|
||||
for (i = 0; i < br->nr; i++) {
|
||||
PyObject *pyelem;
|
||||
struct addr_location al;
|
||||
const char *dsoname;
|
||||
|
||||
pyelem = PyDict_New();
|
||||
if (!pyelem)
|
||||
Py_FatalError("couldn't create Python dictionary");
|
||||
|
||||
pydict_set_item_string_decref(pyelem, "from",
|
||||
PyLong_FromUnsignedLongLong(br->entries[i].from));
|
||||
pydict_set_item_string_decref(pyelem, "to",
|
||||
PyLong_FromUnsignedLongLong(br->entries[i].to));
|
||||
pydict_set_item_string_decref(pyelem, "mispred",
|
||||
PyBool_FromLong(br->entries[i].flags.mispred));
|
||||
pydict_set_item_string_decref(pyelem, "predicted",
|
||||
PyBool_FromLong(br->entries[i].flags.predicted));
|
||||
pydict_set_item_string_decref(pyelem, "in_tx",
|
||||
PyBool_FromLong(br->entries[i].flags.in_tx));
|
||||
pydict_set_item_string_decref(pyelem, "abort",
|
||||
PyBool_FromLong(br->entries[i].flags.abort));
|
||||
pydict_set_item_string_decref(pyelem, "cycles",
|
||||
PyLong_FromUnsignedLongLong(br->entries[i].flags.cycles));
|
||||
|
||||
thread__find_map(thread, sample->cpumode,
|
||||
br->entries[i].from, &al);
|
||||
dsoname = get_dsoname(al.map);
|
||||
pydict_set_item_string_decref(pyelem, "from_dsoname",
|
||||
_PyUnicode_FromString(dsoname));
|
||||
|
||||
thread__find_map(thread, sample->cpumode,
|
||||
br->entries[i].to, &al);
|
||||
dsoname = get_dsoname(al.map);
|
||||
pydict_set_item_string_decref(pyelem, "to_dsoname",
|
||||
_PyUnicode_FromString(dsoname));
|
||||
|
||||
PyList_Append(pylist, pyelem);
|
||||
Py_DECREF(pyelem);
|
||||
}
|
||||
|
||||
exit:
|
||||
return pylist;
|
||||
}
|
||||
|
||||
static unsigned long get_offset(struct symbol *sym, struct addr_location *al)
|
||||
{
|
||||
unsigned long offset;
|
||||
|
||||
if (al->addr < sym->end)
|
||||
offset = al->addr - sym->start;
|
||||
else
|
||||
offset = al->addr - al->map->start - sym->start;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int get_symoff(struct symbol *sym, struct addr_location *al,
|
||||
bool print_off, char *bf, int size)
|
||||
{
|
||||
unsigned long offset;
|
||||
|
||||
if (!sym || !sym->name[0])
|
||||
return scnprintf(bf, size, "%s", "[unknown]");
|
||||
|
||||
if (!print_off)
|
||||
return scnprintf(bf, size, "%s", sym->name);
|
||||
|
||||
offset = get_offset(sym, al);
|
||||
|
||||
return scnprintf(bf, size, "%s+0x%x", sym->name, offset);
|
||||
}
|
||||
|
||||
static int get_br_mspred(struct branch_flags *flags, char *bf, int size)
|
||||
{
|
||||
if (!flags->mispred && !flags->predicted)
|
||||
return scnprintf(bf, size, "%s", "-");
|
||||
|
||||
if (flags->mispred)
|
||||
return scnprintf(bf, size, "%s", "M");
|
||||
|
||||
return scnprintf(bf, size, "%s", "P");
|
||||
}
|
||||
|
||||
static PyObject *python_process_brstacksym(struct perf_sample *sample,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct branch_stack *br = sample->branch_stack;
|
||||
PyObject *pylist;
|
||||
u64 i;
|
||||
char bf[512];
|
||||
struct addr_location al;
|
||||
|
||||
pylist = PyList_New(0);
|
||||
if (!pylist)
|
||||
Py_FatalError("couldn't create Python list");
|
||||
|
||||
if (!(br && br->nr))
|
||||
goto exit;
|
||||
|
||||
for (i = 0; i < br->nr; i++) {
|
||||
PyObject *pyelem;
|
||||
|
||||
pyelem = PyDict_New();
|
||||
if (!pyelem)
|
||||
Py_FatalError("couldn't create Python dictionary");
|
||||
|
||||
thread__find_symbol(thread, sample->cpumode,
|
||||
br->entries[i].from, &al);
|
||||
get_symoff(al.sym, &al, true, bf, sizeof(bf));
|
||||
pydict_set_item_string_decref(pyelem, "from",
|
||||
_PyUnicode_FromString(bf));
|
||||
|
||||
thread__find_symbol(thread, sample->cpumode,
|
||||
br->entries[i].to, &al);
|
||||
get_symoff(al.sym, &al, true, bf, sizeof(bf));
|
||||
pydict_set_item_string_decref(pyelem, "to",
|
||||
_PyUnicode_FromString(bf));
|
||||
|
||||
get_br_mspred(&br->entries[i].flags, bf, sizeof(bf));
|
||||
pydict_set_item_string_decref(pyelem, "pred",
|
||||
_PyUnicode_FromString(bf));
|
||||
|
||||
if (br->entries[i].flags.in_tx) {
|
||||
pydict_set_item_string_decref(pyelem, "in_tx",
|
||||
_PyUnicode_FromString("X"));
|
||||
} else {
|
||||
pydict_set_item_string_decref(pyelem, "in_tx",
|
||||
_PyUnicode_FromString("-"));
|
||||
}
|
||||
|
||||
if (br->entries[i].flags.abort) {
|
||||
pydict_set_item_string_decref(pyelem, "abort",
|
||||
_PyUnicode_FromString("A"));
|
||||
} else {
|
||||
pydict_set_item_string_decref(pyelem, "abort",
|
||||
_PyUnicode_FromString("-"));
|
||||
}
|
||||
|
||||
PyList_Append(pylist, pyelem);
|
||||
Py_DECREF(pyelem);
|
||||
}
|
||||
|
||||
exit:
|
||||
return pylist;
|
||||
}
|
||||
|
||||
static PyObject *get_sample_value_as_tuple(struct sample_read_value *value)
|
||||
{
|
||||
PyObject *t;
|
||||
@ -498,12 +666,63 @@ static void set_sample_read_in_dict(PyObject *dict_sample,
|
||||
pydict_set_item_string_decref(dict_sample, "values", values);
|
||||
}
|
||||
|
||||
static void set_sample_datasrc_in_dict(PyObject *dict,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct mem_info mi = { .data_src.val = sample->data_src };
|
||||
char decode[100];
|
||||
|
||||
pydict_set_item_string_decref(dict, "datasrc",
|
||||
PyLong_FromUnsignedLongLong(sample->data_src));
|
||||
|
||||
perf_script__meminfo_scnprintf(decode, 100, &mi);
|
||||
|
||||
pydict_set_item_string_decref(dict, "datasrc_decode",
|
||||
_PyUnicode_FromString(decode));
|
||||
}
|
||||
|
||||
static int regs_map(struct regs_dump *regs, uint64_t mask, char *bf, int size)
|
||||
{
|
||||
unsigned int i = 0, r;
|
||||
int printed = 0;
|
||||
|
||||
bf[0] = 0;
|
||||
|
||||
for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) {
|
||||
u64 val = regs->regs[i++];
|
||||
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%5s:0x%" PRIx64 " ",
|
||||
perf_reg_name(r), val);
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static void set_regs_in_dict(PyObject *dict,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
char bf[512];
|
||||
|
||||
regs_map(&sample->intr_regs, attr->sample_regs_intr, bf, sizeof(bf));
|
||||
|
||||
pydict_set_item_string_decref(dict, "iregs",
|
||||
_PyUnicode_FromString(bf));
|
||||
|
||||
regs_map(&sample->user_regs, attr->sample_regs_user, bf, sizeof(bf));
|
||||
|
||||
pydict_set_item_string_decref(dict, "uregs",
|
||||
_PyUnicode_FromString(bf));
|
||||
}
|
||||
|
||||
static PyObject *get_perf_sample_dict(struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct addr_location *al,
|
||||
PyObject *callchain)
|
||||
{
|
||||
PyObject *dict, *dict_sample;
|
||||
PyObject *dict, *dict_sample, *brstack, *brstacksym;
|
||||
|
||||
dict = PyDict_New();
|
||||
if (!dict)
|
||||
@ -534,6 +753,11 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
|
||||
pydict_set_item_string_decref(dict_sample, "addr",
|
||||
PyLong_FromUnsignedLongLong(sample->addr));
|
||||
set_sample_read_in_dict(dict_sample, sample, evsel);
|
||||
pydict_set_item_string_decref(dict_sample, "weight",
|
||||
PyLong_FromUnsignedLongLong(sample->weight));
|
||||
pydict_set_item_string_decref(dict_sample, "transaction",
|
||||
PyLong_FromUnsignedLongLong(sample->transaction));
|
||||
set_sample_datasrc_in_dict(dict_sample, sample);
|
||||
pydict_set_item_string_decref(dict, "sample", dict_sample);
|
||||
|
||||
pydict_set_item_string_decref(dict, "raw_buf", _PyBytes_FromStringAndSize(
|
||||
@ -551,6 +775,14 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
|
||||
|
||||
pydict_set_item_string_decref(dict, "callchain", callchain);
|
||||
|
||||
brstack = python_process_brstack(sample, al->thread);
|
||||
pydict_set_item_string_decref(dict, "brstack", brstack);
|
||||
|
||||
brstacksym = python_process_brstacksym(sample, al->thread);
|
||||
pydict_set_item_string_decref(dict, "brstacksym", brstacksym);
|
||||
|
||||
set_regs_in_dict(dict, sample, evsel);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
@ -1094,7 +1094,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
|
||||
sample_type = evsel->attr.sample_type;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_CALLCHAIN)
|
||||
if (evsel__has_callchain(evsel))
|
||||
callchain__printf(evsel, sample);
|
||||
|
||||
if ((sample_type & PERF_SAMPLE_BRANCH_STACK) && !perf_evsel__has_branch_callstack(evsel))
|
||||
|
@ -331,24 +331,18 @@ struct sort_entry sort_sym = {
|
||||
|
||||
/* --sort srcline */
|
||||
|
||||
char *hist_entry__get_srcline(struct hist_entry *he)
|
||||
char *hist_entry__srcline(struct hist_entry *he)
|
||||
{
|
||||
struct map *map = he->ms.map;
|
||||
|
||||
if (!map)
|
||||
return SRCLINE_UNKNOWN;
|
||||
|
||||
return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
|
||||
he->ms.sym, true, true, he->ip);
|
||||
return map__srcline(he->ms.map, he->ip, he->ms.sym);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
if (!left->srcline)
|
||||
left->srcline = hist_entry__get_srcline(left);
|
||||
left->srcline = hist_entry__srcline(left);
|
||||
if (!right->srcline)
|
||||
right->srcline = hist_entry__get_srcline(right);
|
||||
right->srcline = hist_entry__srcline(right);
|
||||
|
||||
return strcmp(right->srcline, left->srcline);
|
||||
}
|
||||
@ -357,7 +351,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
if (!he->srcline)
|
||||
he->srcline = hist_entry__get_srcline(he);
|
||||
he->srcline = hist_entry__srcline(he);
|
||||
|
||||
return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
|
||||
}
|
||||
@ -371,33 +365,20 @@ struct sort_entry sort_srcline = {
|
||||
|
||||
/* --sort srcline_from */
|
||||
|
||||
static char *addr_map_symbol__srcline(struct addr_map_symbol *ams)
|
||||
{
|
||||
return map__srcline(ams->map, ams->al_addr, ams->sym);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
if (!left->branch_info->srcline_from) {
|
||||
struct map *map = left->branch_info->from.map;
|
||||
if (!map)
|
||||
left->branch_info->srcline_from = SRCLINE_UNKNOWN;
|
||||
else
|
||||
left->branch_info->srcline_from = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
left->branch_info->from.al_addr),
|
||||
left->branch_info->from.sym,
|
||||
true, true,
|
||||
left->branch_info->from.al_addr);
|
||||
}
|
||||
if (!right->branch_info->srcline_from) {
|
||||
struct map *map = right->branch_info->from.map;
|
||||
if (!map)
|
||||
right->branch_info->srcline_from = SRCLINE_UNKNOWN;
|
||||
else
|
||||
right->branch_info->srcline_from = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
right->branch_info->from.al_addr),
|
||||
right->branch_info->from.sym,
|
||||
true, true,
|
||||
right->branch_info->from.al_addr);
|
||||
}
|
||||
if (!left->branch_info->srcline_from)
|
||||
left->branch_info->srcline_from = addr_map_symbol__srcline(&left->branch_info->from);
|
||||
|
||||
if (!right->branch_info->srcline_from)
|
||||
right->branch_info->srcline_from = addr_map_symbol__srcline(&right->branch_info->from);
|
||||
|
||||
return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
|
||||
}
|
||||
|
||||
@ -419,30 +400,12 @@ struct sort_entry sort_srcline_from = {
|
||||
static int64_t
|
||||
sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
if (!left->branch_info->srcline_to) {
|
||||
struct map *map = left->branch_info->to.map;
|
||||
if (!map)
|
||||
left->branch_info->srcline_to = SRCLINE_UNKNOWN;
|
||||
else
|
||||
left->branch_info->srcline_to = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
left->branch_info->to.al_addr),
|
||||
left->branch_info->from.sym,
|
||||
true, true,
|
||||
left->branch_info->to.al_addr);
|
||||
}
|
||||
if (!right->branch_info->srcline_to) {
|
||||
struct map *map = right->branch_info->to.map;
|
||||
if (!map)
|
||||
right->branch_info->srcline_to = SRCLINE_UNKNOWN;
|
||||
else
|
||||
right->branch_info->srcline_to = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
right->branch_info->to.al_addr),
|
||||
right->branch_info->to.sym,
|
||||
true, true,
|
||||
right->branch_info->to.al_addr);
|
||||
}
|
||||
if (!left->branch_info->srcline_to)
|
||||
left->branch_info->srcline_to = addr_map_symbol__srcline(&left->branch_info->to);
|
||||
|
||||
if (!right->branch_info->srcline_to)
|
||||
right->branch_info->srcline_to = addr_map_symbol__srcline(&right->branch_info->to);
|
||||
|
||||
return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,11 @@ struct hist_entry {
|
||||
struct callchain_root callchain[0]; /* must be last member */
|
||||
};
|
||||
|
||||
static __pure inline bool hist_entry__has_callchains(struct hist_entry *he)
|
||||
{
|
||||
return hists__has_callchains(he->hists);
|
||||
}
|
||||
|
||||
static inline bool hist_entry__has_pairs(struct hist_entry *he)
|
||||
{
|
||||
return !list_empty(&he->pairs.node);
|
||||
@ -292,5 +297,5 @@ int64_t
|
||||
sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right);
|
||||
int64_t
|
||||
sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right);
|
||||
char *hist_entry__get_srcline(struct hist_entry *he);
|
||||
char *hist_entry__srcline(struct hist_entry *he);
|
||||
#endif /* __PERF_SORT_H */
|
||||
|
@ -40,7 +40,6 @@ char **vmlinux_path;
|
||||
struct symbol_conf symbol_conf = {
|
||||
.use_modules = true,
|
||||
.try_vmlinux_path = true,
|
||||
.annotate_src = true,
|
||||
.demangle = true,
|
||||
.demangle_kernel = false,
|
||||
.cumulate_callchain = true,
|
||||
@ -74,7 +73,7 @@ static enum dso_binary_type binary_type_symtab[] = {
|
||||
static bool symbol_type__filter(char symbol_type)
|
||||
{
|
||||
symbol_type = toupper(symbol_type);
|
||||
return symbol_type == 'T' || symbol_type == 'W' || symbol_type == 'D';
|
||||
return symbol_type == 'T' || symbol_type == 'W' || symbol_type == 'D' || symbol_type == 'B';
|
||||
}
|
||||
|
||||
static int prefix_underscores_count(const char *str)
|
||||
|
@ -90,7 +90,6 @@ struct intlist;
|
||||
|
||||
struct symbol_conf {
|
||||
unsigned short priv_size;
|
||||
unsigned short nr_events;
|
||||
bool try_vmlinux_path,
|
||||
init_annotation,
|
||||
force,
|
||||
@ -109,8 +108,6 @@ struct symbol_conf {
|
||||
show_cpu_utilization,
|
||||
initialized,
|
||||
kptr_restrict,
|
||||
annotate_asm_raw,
|
||||
annotate_src,
|
||||
event_group,
|
||||
demangle,
|
||||
demangle_kernel,
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define __PERF_TOP_H 1
|
||||
|
||||
#include "tool.h"
|
||||
#include "annotate.h"
|
||||
#include <linux/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
@ -16,6 +17,7 @@ struct perf_top {
|
||||
struct perf_tool tool;
|
||||
struct perf_evlist *evlist;
|
||||
struct record_opts record_opts;
|
||||
struct annotation_options annotation_opts;
|
||||
/*
|
||||
* Symbols will be added here in perf_event__process_sample and will
|
||||
* get out after decayed.
|
||||
@ -35,7 +37,6 @@ struct perf_top {
|
||||
struct perf_session *session;
|
||||
struct winsize winsize;
|
||||
int realtime_prio;
|
||||
int sym_pcnt_filter;
|
||||
const char *sym_filter;
|
||||
float min_percent;
|
||||
unsigned int nr_threads_synthesize;
|
||||
|
Loading…
Reference in New Issue
Block a user