Commit ced30bc9 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-20160310' of...

Merge tag 'perf-core-for-mingo-20160310' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - Implement 'perf stat --metric-only' (Andi Kleen)

  - Fix perf script python database export crash (Chris Phlipot)

Infrastructure changes:

  - perf top/report --hierarchy assorted fixes for problems introduced in this
    perf/core cycle (Namhyung Kim)

  - Support '~' operation in libtraceevent (Steven Rosted)

Build fixes:

  - Fix bulding of jitdump on opensuse on ubuntu systems when the DWARF
    devel files are not installed (Arnaldo Carvalho de Melo)

  - Do not try building jitdump on unsupported arches (Jiri Olsa)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 3a99e6db 206cab65
...@@ -2398,6 +2398,12 @@ static int arg_num_eval(struct print_arg *arg, long long *val) ...@@ -2398,6 +2398,12 @@ static int arg_num_eval(struct print_arg *arg, long long *val)
break; break;
*val = left + right; *val = left + right;
break; break;
case '~':
ret = arg_num_eval(arg->op.right, &right);
if (!ret)
break;
*val = ~right;
break;
default: default:
do_warning("unknown op '%s'", arg->op.op); do_warning("unknown op '%s'", arg->op.op);
ret = 0; ret = 0;
......
...@@ -147,6 +147,10 @@ Print count deltas every N milliseconds (minimum: 10ms) ...@@ -147,6 +147,10 @@ Print count deltas every N milliseconds (minimum: 10ms)
The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals. Use with caution. The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals. Use with caution.
example: 'perf stat -I 1000 -e cycles -a sleep 5' example: 'perf stat -I 1000 -e cycles -a sleep 5'
--metric-only::
Only print computed metrics. Print them in a single line.
Don't show any raw values. Not supported with --per-thread.
--per-socket:: --per-socket::
Aggregate counts per processor socket for system-wide mode measurements. This Aggregate counts per processor socket for system-wide mode measurements. This
is a useful mode to detect imbalance between sockets. To enable this mode, is a useful mode to detect imbalance between sockets. To enable this mode,
...@@ -219,6 +223,29 @@ $ perf stat -- make -j ...@@ -219,6 +223,29 @@ $ perf stat -- make -j
Wall-clock time elapsed: 719.554352 msecs Wall-clock time elapsed: 719.554352 msecs
CSV FORMAT
----------
With -x, perf stat is able to output a not-quite-CSV format output
Commas in the output are not put into "". To make it easy to parse
it is recommended to use a different character like -x \;
The fields are in this order:
- optional usec time stamp in fractions of second (with -I xxx)
- optional CPU, core, or socket identifier
- optional number of logical CPUs aggregated
- counter value
- unit of the counter value or empty
- event name
- run time of counter
- percentage of measurement time the counter was running
- optional variance if multiple values are collected with -r
- optional metric value
- optional unit of metric
Additional metrics may be printed with all earlier fields being empty.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-top[1], linkperf:perf-list[1] linkperf:perf-top[1], linkperf:perf-list[1]
ifndef NO_DWARF ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1 PERF_HAVE_DWARF_REGS := 1
endif endif
PERF_HAVE_JITDUMP := 1
ifndef NO_DWARF ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1 PERF_HAVE_DWARF_REGS := 1
endif endif
PERF_HAVE_JITDUMP := 1
...@@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1 ...@@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1
endif endif
HAVE_KVM_STAT_SUPPORT := 1 HAVE_KVM_STAT_SUPPORT := 1
PERF_HAVE_JITDUMP := 1
...@@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1 ...@@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1
endif endif
HAVE_KVM_STAT_SUPPORT := 1 HAVE_KVM_STAT_SUPPORT := 1
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
PERF_HAVE_JITDUMP := 1
...@@ -73,7 +73,7 @@ static int perf_event__repipe_oe_synth(struct perf_tool *tool, ...@@ -73,7 +73,7 @@ static int perf_event__repipe_oe_synth(struct perf_tool *tool,
return perf_event__repipe_synth(tool, event); return perf_event__repipe_synth(tool, event);
} }
#ifdef HAVE_LIBELF_SUPPORT #ifdef HAVE_JITDUMP
static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused, static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused, union perf_event *event __maybe_unused,
struct ordered_events *oe __maybe_unused) struct ordered_events *oe __maybe_unused)
...@@ -245,7 +245,7 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, ...@@ -245,7 +245,7 @@ static int perf_event__repipe_mmap(struct perf_tool *tool,
return err; return err;
} }
#ifdef HAVE_LIBELF_SUPPORT #ifdef HAVE_JITDUMP
static int perf_event__jit_repipe_mmap(struct perf_tool *tool, static int perf_event__jit_repipe_mmap(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -283,7 +283,7 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool, ...@@ -283,7 +283,7 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool,
return err; return err;
} }
#ifdef HAVE_LIBELF_SUPPORT #ifdef HAVE_JITDUMP
static int perf_event__jit_repipe_mmap2(struct perf_tool *tool, static int perf_event__jit_repipe_mmap2(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -778,7 +778,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -778,7 +778,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
"Merge sched-stat and sched-switch for getting events " "Merge sched-stat and sched-switch for getting events "
"where and how long tasks slept"), "where and how long tasks slept"),
#ifdef HAVE_JITDUMP
OPT_BOOLEAN('j', "jit", &inject.jit_mode, "merge jitdump files into perf.data file"), OPT_BOOLEAN('j', "jit", &inject.jit_mode, "merge jitdump files into perf.data file"),
#endif
OPT_INCR('v', "verbose", &verbose, OPT_INCR('v', "verbose", &verbose,
"be more verbose (show build ids, etc)"), "be more verbose (show build ids, etc)"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
...@@ -795,7 +797,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -795,7 +797,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
"perf inject [<options>]", "perf inject [<options>]",
NULL NULL
}; };
#ifndef HAVE_LIBELF_SUPPORT #ifndef HAVE_JITDUMP
set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true); set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true);
#endif #endif
argc = parse_options(argc, argv, options, inject_usage, 0); argc = parse_options(argc, argv, options, inject_usage, 0);
...@@ -833,7 +835,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -833,7 +835,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
inject.tool.ordered_events = true; inject.tool.ordered_events = true;
inject.tool.ordering_requires_timestamps = true; inject.tool.ordering_requires_timestamps = true;
} }
#ifdef HAVE_LIBELF_SUPPORT #ifdef HAVE_JITDUMP
if (inject.jit_mode) { if (inject.jit_mode) {
inject.tool.mmap2 = perf_event__jit_repipe_mmap2; inject.tool.mmap2 = perf_event__jit_repipe_mmap2;
inject.tool.mmap = perf_event__jit_repipe_mmap; inject.tool.mmap = perf_event__jit_repipe_mmap;
......
...@@ -122,6 +122,7 @@ static bool sync_run = false; ...@@ -122,6 +122,7 @@ static bool sync_run = false;
static unsigned int initial_delay = 0; static unsigned int initial_delay = 0;
static unsigned int unit_width = 4; /* strlen("unit") */ static unsigned int unit_width = 4; /* strlen("unit") */
static bool forever = false; static bool forever = false;
static bool metric_only = false;
static struct timespec ref_time; static struct timespec ref_time;
static struct cpu_map *aggr_map; static struct cpu_map *aggr_map;
static aggr_get_id_t aggr_get_id; static aggr_get_id_t aggr_get_id;
...@@ -827,6 +828,99 @@ static void print_metric_csv(void *ctx, ...@@ -827,6 +828,99 @@ static void print_metric_csv(void *ctx,
fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit);
} }
#define METRIC_ONLY_LEN 20
/* Filter out some columns that don't work well in metrics only mode */
static bool valid_only_metric(const char *unit)
{
if (!unit)
return false;
if (strstr(unit, "/sec") ||
strstr(unit, "hz") ||
strstr(unit, "Hz") ||
strstr(unit, "CPUs utilized"))
return false;
return true;
}
static const char *fixunit(char *buf, struct perf_evsel *evsel,
const char *unit)
{
if (!strncmp(unit, "of all", 6)) {
snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel),
unit);
return buf;
}
return unit;
}
static void print_metric_only(void *ctx, const char *color, const char *fmt,
const char *unit, double val)
{
struct outstate *os = ctx;
FILE *out = os->fh;
int n;
char buf[1024];
unsigned mlen = METRIC_ONLY_LEN;
if (!valid_only_metric(unit))
return;
unit = fixunit(buf, os->evsel, unit);
if (color)
n = color_fprintf(out, color, fmt, val);
else
n = fprintf(out, fmt, val);
if (n > METRIC_ONLY_LEN)
n = METRIC_ONLY_LEN;
if (mlen < strlen(unit))
mlen = strlen(unit) + 1;
fprintf(out, "%*s", mlen - n, "");
}
static void print_metric_only_csv(void *ctx, const char *color __maybe_unused,
const char *fmt,
const char *unit, double val)
{
struct outstate *os = ctx;
FILE *out = os->fh;
char buf[64], *vals, *ends;
char tbuf[1024];
if (!valid_only_metric(unit))
return;
unit = fixunit(tbuf, os->evsel, unit);
snprintf(buf, sizeof buf, fmt, val);
vals = buf;
while (isspace(*vals))
vals++;
ends = vals;
while (isdigit(*ends) || *ends == '.')
ends++;
*ends = 0;
fprintf(out, "%s%s", vals, csv_sep);
}
static void new_line_metric(void *ctx __maybe_unused)
{
}
static void print_metric_header(void *ctx, const char *color __maybe_unused,
const char *fmt __maybe_unused,
const char *unit, double val __maybe_unused)
{
struct outstate *os = ctx;
char tbuf[1024];
if (!valid_only_metric(unit))
return;
unit = fixunit(tbuf, os->evsel, unit);
if (csv_output)
fprintf(os->fh, "%s%s", unit, csv_sep);
else
fprintf(os->fh, "%-*s ", METRIC_ONLY_LEN, unit);
}
static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
{ {
FILE *output = stat_config.output; FILE *output = stat_config.output;
...@@ -921,9 +1015,16 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, ...@@ -921,9 +1015,16 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
print_metric_t pm = print_metric_std; print_metric_t pm = print_metric_std;
void (*nl)(void *); void (*nl)(void *);
nl = new_line_std; if (metric_only) {
nl = new_line_metric;
if (csv_output)
pm = print_metric_only_csv;
else
pm = print_metric_only;
} else
nl = new_line_std;
if (csv_output) { if (csv_output && !metric_only) {
static int aggr_fields[] = { static int aggr_fields[] = {
[AGGR_GLOBAL] = 0, [AGGR_GLOBAL] = 0,
[AGGR_THREAD] = 1, [AGGR_THREAD] = 1,
...@@ -940,6 +1041,10 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, ...@@ -940,6 +1041,10 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
os.nfields++; os.nfields++;
} }
if (run == 0 || ena == 0 || counter->counts->scaled == -1) { if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
if (metric_only) {
pm(&os, NULL, "", "", 0);
return;
}
aggr_printout(counter, id, nr); aggr_printout(counter, id, nr);
fprintf(stat_config.output, "%*s%s", fprintf(stat_config.output, "%*s%s",
...@@ -968,7 +1073,9 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, ...@@ -968,7 +1073,9 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
return; return;
} }
if (nsec_counter(counter)) if (metric_only)
/* nothing */;
else if (nsec_counter(counter))
nsec_printout(id, nr, counter, uval); nsec_printout(id, nr, counter, uval);
else else
abs_printout(id, nr, counter, uval); abs_printout(id, nr, counter, uval);
...@@ -977,7 +1084,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, ...@@ -977,7 +1084,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
out.new_line = nl; out.new_line = nl;
out.ctx = &os; out.ctx = &os;
if (csv_output) { if (csv_output && !metric_only) {
print_noise(counter, noise); print_noise(counter, noise);
print_running(run, ena); print_running(run, ena);
} }
...@@ -985,7 +1092,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, ...@@ -985,7 +1092,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
perf_stat__print_shadow_stats(counter, uval, perf_stat__print_shadow_stats(counter, uval,
first_shadow_cpu(counter, id), first_shadow_cpu(counter, id),
&out); &out);
if (!csv_output) { if (!csv_output && !metric_only) {
print_noise(counter, noise); print_noise(counter, noise);
print_running(run, ena); print_running(run, ena);
} }
...@@ -1021,14 +1128,23 @@ static void print_aggr(char *prefix) ...@@ -1021,14 +1128,23 @@ static void print_aggr(char *prefix)
int cpu, s, s2, id, nr; int cpu, s, s2, id, nr;
double uval; double uval;
u64 ena, run, val; u64 ena, run, val;
bool first;
if (!(aggr_map || aggr_get_id)) if (!(aggr_map || aggr_get_id))
return; return;
aggr_update_shadow(); aggr_update_shadow();
/*
* With metric_only everything is on a single line.
* Without each counter has its own line.
*/
for (s = 0; s < aggr_map->nr; s++) { for (s = 0; s < aggr_map->nr; s++) {
if (prefix && metric_only)
fprintf(output, "%s", prefix);
id = aggr_map->map[s]; id = aggr_map->map[s];
first = true;
evlist__for_each(evsel_list, counter) { evlist__for_each(evsel_list, counter) {
val = ena = run = 0; val = ena = run = 0;
nr = 0; nr = 0;
...@@ -1041,13 +1157,20 @@ static void print_aggr(char *prefix) ...@@ -1041,13 +1157,20 @@ static void print_aggr(char *prefix)
run += perf_counts(counter->counts, cpu, 0)->run; run += perf_counts(counter->counts, cpu, 0)->run;
nr++; nr++;
} }
if (prefix) if (first && metric_only) {
first = false;
aggr_printout(counter, id, nr);
}
if (prefix && !metric_only)
fprintf(output, "%s", prefix); fprintf(output, "%s", prefix);
uval = val * counter->scale; uval = val * counter->scale;
printout(id, nr, counter, uval, prefix, run, ena, 1.0); printout(id, nr, counter, uval, prefix, run, ena, 1.0);
fputc('\n', output); if (!metric_only)
fputc('\n', output);
} }
if (metric_only)
fputc('\n', output);
} }
} }
...@@ -1092,12 +1215,13 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) ...@@ -1092,12 +1215,13 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
avg_enabled = avg_stats(&ps->res_stats[1]); avg_enabled = avg_stats(&ps->res_stats[1]);
avg_running = avg_stats(&ps->res_stats[2]); avg_running = avg_stats(&ps->res_stats[2]);
if (prefix) if (prefix && !metric_only)
fprintf(output, "%s", prefix); fprintf(output, "%s", prefix);
uval = avg * counter->scale; uval = avg * counter->scale;
printout(-1, 0, counter, uval, prefix, avg_running, avg_enabled, avg); printout(-1, 0, counter, uval, prefix, avg_running, avg_enabled, avg);
fprintf(output, "\n"); if (!metric_only)
fprintf(output, "\n");
} }
/* /*
...@@ -1126,6 +1250,73 @@ static void print_counter(struct perf_evsel *counter, char *prefix) ...@@ -1126,6 +1250,73 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
} }
} }
static void print_no_aggr_metric(char *prefix)
{
int cpu;
int nrcpus = 0;
struct perf_evsel *counter;
u64 ena, run, val;
double uval;
nrcpus = evsel_list->cpus->nr;
for (cpu = 0; cpu < nrcpus; cpu++) {
bool first = true;
if (prefix)
fputs(prefix, stat_config.output);
evlist__for_each(evsel_list, counter) {
if (first) {
aggr_printout(counter, cpu, 0);
first = false;
}
val = perf_counts(counter->counts, cpu, 0)->val;
ena = perf_counts(counter->counts, cpu, 0)->ena;
run = perf_counts(counter->counts, cpu, 0)->run;
uval = val * counter->scale;
printout(cpu, 0, counter, uval, prefix, run, ena, 1.0);
}
fputc('\n', stat_config.output);
}
}
static int aggr_header_lens[] = {
[AGGR_CORE] = 18,
[AGGR_SOCKET] = 12,
[AGGR_NONE] = 6,
[AGGR_THREAD] = 24,
[AGGR_GLOBAL] = 0,
};
static void print_metric_headers(char *prefix)
{
struct perf_stat_output_ctx out;
struct perf_evsel *counter;
struct outstate os = {
.fh = stat_config.output
};
if (prefix)
fprintf(stat_config.output, "%s", prefix);
if (!csv_output)
fprintf(stat_config.output, "%*s",
aggr_header_lens[stat_config.aggr_mode], "");
/* Print metrics headers only */
evlist__for_each(evsel_list, counter) {
os.evsel = counter;
out.ctx = &os;
out.print_metric = print_metric_header;
out.new_line = new_line_metric;
os.evsel = counter;
perf_stat__print_shadow_stats(counter, 0,
0,
&out);
}
fputc('\n', stat_config.output);
}
static void print_interval(char *prefix, struct timespec *ts) static void print_interval(char *prefix, struct timespec *ts)
{ {
FILE *output = stat_config.output; FILE *output = stat_config.output;
...@@ -1133,7 +1324,7 @@ static void print_interval(char *prefix, struct timespec *ts) ...@@ -1133,7 +1324,7 @@ static void print_interval(char *prefix, struct timespec *ts)
sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep);
if (num_print_interval == 0 && !csv_output) { if (num_print_interval == 0 && !csv_output && !metric_only) {
switch (stat_config.aggr_mode) { switch (stat_config.aggr_mode) {
case AGGR_SOCKET: case AGGR_SOCKET:
fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit");
...@@ -1220,6 +1411,17 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) ...@@ -1220,6 +1411,17 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
else else
print_header(argc, argv); print_header(argc, argv);
if (metric_only) {
static int num_print_iv;
if (num_print_iv == 0)
print_metric_headers(prefix);
if (num_print_iv++ == 25)
num_print_iv = 0;
if (stat_config.aggr_mode == AGGR_GLOBAL && prefix)
fprintf(stat_config.output, "%s", prefix);
}
switch (stat_config.aggr_mode) { switch (stat_config.aggr_mode) {
case AGGR_CORE: case AGGR_CORE:
case AGGR_SOCKET: case AGGR_SOCKET:
...@@ -1232,10 +1434,16 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) ...@@ -1232,10 +1434,16 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
case AGGR_GLOBAL: case AGGR_GLOBAL:
evlist__for_each(evsel_list, counter) evlist__for_each(evsel_list, counter)
print_counter_aggr(counter, prefix); print_counter_aggr(counter, prefix);
if (metric_only)
fputc('\n', stat_config.output);
break; break;
case AGGR_NONE: case AGGR_NONE:
evlist__for_each(evsel_list, counter) if (metric_only)
print_counter(counter, prefix); print_no_aggr_metric(prefix);
else {
evlist__for_each(evsel_list, counter)
print_counter(counter, prefix);
}
break; break;
case AGGR_UNSET: case AGGR_UNSET:
default: default:
...@@ -1356,6 +1564,8 @@ static const struct option stat_options[] = { ...@@ -1356,6 +1564,8 @@ static const struct option stat_options[] = {
"aggregate counts per thread", AGGR_THREAD), "aggregate counts per thread", AGGR_THREAD),
OPT_UINTEGER('D', "delay", &initial_delay, OPT_UINTEGER('D', "delay", &initial_delay,
"ms to wait before starting measurement after program start"), "ms to wait before starting measurement after program start"),
OPT_BOOLEAN(0, "metric-only", &metric_only,
"Only print computed metrics. No raw values"),
OPT_END() OPT_END()
}; };
...@@ -1997,6 +2207,16 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1997,6 +2207,16 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
goto out; goto out;
} }
if (metric_only && stat_config.aggr_mode == AGGR_THREAD) {
fprintf(stderr, "--metric-only is not supported with --per-thread\n");
goto out;
}
if (metric_only && run_count > 1) {
fprintf(stderr, "--metric-only is not supported with -r\n");
goto out;
}
if (output_fd < 0) { if (output_fd < 0) {
fprintf(stderr, "argument to --log-fd must be a > 0\n"); fprintf(stderr, "argument to --log-fd must be a > 0\n");
parse_options_usage(stat_usage, stat_options, "log-fd", 0); parse_options_usage(stat_usage, stat_options, "log-fd", 0);
......
...@@ -328,6 +328,13 @@ ifndef NO_LIBELF ...@@ -328,6 +328,13 @@ ifndef NO_LIBELF
endif # NO_LIBBPF endif # NO_LIBBPF
endif # NO_LIBELF endif # NO_LIBELF
ifdef PERF_HAVE_JITDUMP
ifndef NO_DWARF
$(call detected,CONFIG_JITDUMP)
CFLAGS += -DHAVE_JITDUMP
endif
endif
ifeq ($(ARCH),powerpc) ifeq ($(ARCH),powerpc)
ifndef NO_DWARF ifndef NO_DWARF
CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
......
...@@ -1928,8 +1928,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, ...@@ -1928,8 +1928,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
struct hist_entry *he, struct hist_entry *he,
FILE *fp, int level, FILE *fp, int level)
int nr_sort_keys)
{ {
char s[8192]; char s[8192];
int printed = 0; int printed = 0;
...@@ -1939,23 +1938,20 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, ...@@ -1939,23 +1938,20 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
.size = sizeof(s), .size = sizeof(s),
}; };
struct perf_hpp_fmt *fmt; struct perf_hpp_fmt *fmt;
struct perf_hpp_list_node *fmt_node;
bool first = true; bool first = true;
int ret; int ret;
int hierarchy_indent = nr_sort_keys * HIERARCHY_INDENT; int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, ""); printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
folded_sign = hist_entry__folded(he); folded_sign = hist_entry__folded(he);
printed += fprintf(fp, "%c", folded_sign); printed += fprintf(fp, "%c", folded_sign);
hists__for_each_format(he->hists, fmt) { /* the first hpp_list_node is for overhead columns */
if (perf_hpp__should_skip(fmt, he->hists)) fmt_node = list_first_entry(&he->hists->hpp_formats,
continue; struct perf_hpp_list_node, list);
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
if (perf_hpp__is_sort_entry(fmt) ||
perf_hpp__is_dynamic_entry(fmt))
break;
if (!first) { if (!first) {
ret = scnprintf(hpp.buf, hpp.size, " "); ret = scnprintf(hpp.buf, hpp.size, " ");
advance_hpp(&hpp, ret); advance_hpp(&hpp, ret);
...@@ -1992,7 +1988,6 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) ...@@ -1992,7 +1988,6 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
browser->min_pcnt); browser->min_pcnt);
int printed = 0; int printed = 0;
int nr_sort = browser->hists->nr_sort_keys;
while (nd) { while (nd) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
...@@ -2000,8 +1995,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) ...@@ -2000,8 +1995,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
if (symbol_conf.report_hierarchy) { if (symbol_conf.report_hierarchy) {
printed += hist_browser__fprintf_hierarchy_entry(browser, printed += hist_browser__fprintf_hierarchy_entry(browser,
h, fp, h, fp,
h->depth, h->depth);
nr_sort);
} else { } else {
printed += hist_browser__fprintf_entry(browser, h, fp); printed += hist_browser__fprintf_entry(browser, h, fp);
} }
...@@ -2142,11 +2136,18 @@ static int hists__browser_title(struct hists *hists, ...@@ -2142,11 +2136,18 @@ static int hists__browser_title(struct hists *hists,
if (hists->uid_filter_str) if (hists->uid_filter_str)
printed += snprintf(bf + printed, size - printed, printed += snprintf(bf + printed, size - printed,
", UID: %s", hists->uid_filter_str); ", UID: %s", hists->uid_filter_str);
if (thread) if (thread) {
printed += scnprintf(bf + printed, size - printed, if (sort__has_thread) {
printed += scnprintf(bf + printed, size - printed,
", Thread: %s(%d)", ", Thread: %s(%d)",
(thread->comm_set ? thread__comm_str(thread) : ""), (thread->comm_set ? thread__comm_str(thread) : ""),
thread->tid); thread->tid);
} else {
printed += scnprintf(bf + printed, size - printed,
", Thread: %s",
(thread->comm_set ? thread__comm_str(thread) : ""));
}
}
if (dso) if (dso)
printed += scnprintf(bf + printed, size - printed, printed += scnprintf(bf + printed, size - printed,
", DSO: %s", dso->short_name); ", DSO: %s", dso->short_name);
...@@ -2321,15 +2322,24 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act) ...@@ -2321,15 +2322,24 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
{ {
struct thread *thread = act->thread; struct thread *thread = act->thread;
if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
return 0;
if (browser->hists->thread_filter) { if (browser->hists->thread_filter) {
pstack__remove(browser->pstack, &browser->hists->thread_filter); pstack__remove(browser->pstack, &browser->hists->thread_filter);
perf_hpp__set_elide(HISTC_THREAD, false); perf_hpp__set_elide(HISTC_THREAD, false);
thread__zput(browser->hists->thread_filter); thread__zput(browser->hists->thread_filter);
ui_helpline__pop(); ui_helpline__pop();
} else { } else {
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", if (sort__has_thread) {
thread->comm_set ? thread__comm_str(thread) : "", ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
thread->tid); thread->comm_set ? thread__comm_str(thread) : "",
thread->tid);
} else {
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
thread->comm_set ? thread__comm_str(thread) : "");
}
browser->hists->thread_filter = thread__get(thread); browser->hists->thread_filter = thread__get(thread);
perf_hpp__set_elide(HISTC_THREAD, false); perf_hpp__set_elide(HISTC_THREAD, false);
pstack__push(browser->pstack, &browser->hists->thread_filter); pstack__push(browser->pstack, &browser->hists->thread_filter);
...@@ -2344,13 +2354,22 @@ static int ...@@ -2344,13 +2354,22 @@ static int
add_thread_opt(struct hist_browser *browser, struct popup_action *act, add_thread_opt(struct hist_browser *browser, struct popup_action *act,
char **optstr, struct thread *thread) char **optstr, struct thread *thread)
{ {
if (!sort__has_thread || thread == NULL) int ret;
if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
return 0; return 0;
if (asprintf(optstr, "Zoom %s %s(%d) thread", if (sort__has_thread) {
browser->hists->thread_filter ? "out of" : "into", ret = asprintf(optstr, "Zoom %s %s(%d) thread",
thread->comm_set ? thread__comm_str(thread) : "", browser->hists->thread_filter ? "out of" : "into",
thread->tid) < 0) thread->comm_set ? thread__comm_str(thread) : "",
thread->tid);
} else {
ret = asprintf(optstr, "Zoom %s %s thread",
browser->hists->thread_filter ? "out of" : "into",
thread->comm_set ? thread__comm_str(thread) : "");
}
if (ret < 0)
return 0; return 0;
act->thread = thread; act->thread = thread;
...@@ -2363,6 +2382,9 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act) ...@@ -2363,6 +2382,9 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
{ {
struct map *map = act->ms.map; struct map *map = act->ms.map;
if (!sort__has_dso || map == NULL)
return 0;
if (browser->hists->dso_filter) { if (browser->hists->dso_filter) {
pstack__remove(browser->pstack, &browser->hists->dso_filter); pstack__remove(browser->pstack, &browser->hists->dso_filter);
perf_hpp__set_elide(HISTC_DSO, false); perf_hpp__set_elide(HISTC_DSO, false);
...@@ -2514,6 +2536,9 @@ add_exit_opt(struct hist_browser *browser __maybe_unused, ...@@ -2514,6 +2536,9 @@ add_exit_opt(struct hist_browser *browser __maybe_unused,
static int static int
do_zoom_socket(struct hist_browser *browser, struct popup_action *act) do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
{ {
if (!sort__has_socket || act->socket < 0)
return 0;
if (browser->hists->socket_filter > -1) { if (browser->hists->socket_filter > -1) {
pstack__remove(browser->pstack, &browser->hists->socket_filter); pstack__remove(browser->pstack, &browser->hists->socket_filter);
browser->hists->socket_filter = -1; browser->hists->socket_filter = -1;
......
...@@ -515,9 +515,6 @@ void perf_hpp_list__column_register(struct perf_hpp_list *list, ...@@ -515,9 +515,6 @@ void perf_hpp_list__column_register(struct perf_hpp_list *list,
void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
struct perf_hpp_fmt *format) struct perf_hpp_fmt *format)
{ {
if (perf_hpp__is_sort_entry(format) || perf_hpp__is_dynamic_entry(format))
list->nr_sort_keys++;
list_add_tail(&format->sort_list, &list->sorts); list_add_tail(&format->sort_list, &list->sorts);
} }
......
...@@ -107,9 +107,12 @@ libperf-y += scripting-engines/ ...@@ -107,9 +107,12 @@ libperf-y += scripting-engines/
libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_ZLIB) += zlib.o
libperf-$(CONFIG_LZMA) += lzma.o libperf-$(CONFIG_LZMA) += lzma.o
libperf-y += demangle-java.o libperf-y += demangle-java.o
ifdef CONFIG_JITDUMP
libperf-$(CONFIG_LIBELF) += jitdump.o libperf-$(CONFIG_LIBELF) += jitdump.o
libperf-$(CONFIG_LIBELF) += genelf.o libperf-$(CONFIG_LIBELF) += genelf.o
libperf-$(CONFIG_LIBELF) += genelf_debug.o libperf-$(CONFIG_LIBELF) += genelf_debug.o
endif
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
# avoid compiler warnings in 32-bit mode # avoid compiler warnings in 32-bit mode
......
...@@ -93,10 +93,8 @@ struct perf_evsel { ...@@ -93,10 +93,8 @@ struct perf_evsel {
const char *unit; const char *unit;
struct event_format *tp_format; struct event_format *tp_format;
off_t id_offset; off_t id_offset;
union { void *priv;
void *priv; u64 db_id;
u64 db_id;
};
struct cgroup_sel *cgrp; struct cgroup_sel *cgrp;
void *handler; void *handler;
struct cpu_map *cpus; struct cpu_map *cpus;
......
...@@ -1087,10 +1087,103 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, ...@@ -1087,10 +1087,103 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp,
*/ */
static void hists__apply_filters(struct hists *hists, struct hist_entry *he); static void hists__apply_filters(struct hists *hists, struct hist_entry *he);
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *he,
enum hist_filter type);
typedef bool (*fmt_chk_fn)(struct perf_hpp_fmt *fmt);
static bool check_thread_entry(struct perf_hpp_fmt *fmt)
{
return perf_hpp__is_thread_entry(fmt) || perf_hpp__is_comm_entry(fmt);
}
static void hist_entry__check_and_remove_filter(struct hist_entry *he,
enum hist_filter type,
fmt_chk_fn check)
{
struct perf_hpp_fmt *fmt;
bool type_match = false;
struct hist_entry *parent = he->parent_he;
switch (type) {
case HIST_FILTER__THREAD:
if (symbol_conf.comm_list == NULL &&
symbol_conf.pid_list == NULL &&
symbol_conf.tid_list == NULL)
return;
break;
case HIST_FILTER__DSO:
if (symbol_conf.dso_list == NULL)
return;
break;
case HIST_FILTER__SYMBOL:
if (symbol_conf.sym_list == NULL)
return;
break;
case HIST_FILTER__PARENT:
case HIST_FILTER__GUEST:
case HIST_FILTER__HOST:
case HIST_FILTER__SOCKET:
default:
return;
}
/* if it's filtered by own fmt, it has to have filter bits */
perf_hpp_list__for_each_format(he->hpp_list, fmt) {
if (check(fmt)) {
type_match = true;
break;
}
}
if (type_match) {
/*
* If the filter is for current level entry, propagate
* filter marker to parents. The marker bit was
* already set by default so it only needs to clear
* non-filtered entries.
*/
if (!(he->filtered & (1 << type))) {
while (parent) {
parent->filtered &= ~(1 << type);
parent = parent->parent_he;
}
}
} else {
/*
* If current entry doesn't have matching formats, set
* filter marker for upper level entries. it will be
* cleared if its lower level entries is not filtered.
*
* For lower-level entries, it inherits parent's
* filter bit so that lower level entries of a
* non-filtered entry won't set the filter marker.
*/
if (parent == NULL)
he->filtered |= (1 << type);
else
he->filtered |= (parent->filtered & (1 << type));
}
}
static void hist_entry__apply_hierarchy_filters(struct hist_entry *he)
{
hist_entry__check_and_remove_filter(he, HIST_FILTER__THREAD,
check_thread_entry);
hist_entry__check_and_remove_filter(he, HIST_FILTER__DSO,
perf_hpp__is_dso_entry);
hist_entry__check_and_remove_filter(he, HIST_FILTER__SYMBOL,
perf_hpp__is_sym_entry);
hists__apply_filters(he->hists, he);
}
static struct hist_entry *hierarchy_insert_entry(struct hists *hists, static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
struct rb_root *root, struct rb_root *root,
struct hist_entry *he, struct hist_entry *he,
struct hist_entry *parent_he,
struct perf_hpp_list *hpp_list) struct perf_hpp_list *hpp_list)
{ {
struct rb_node **p = &root->rb_node; struct rb_node **p = &root->rb_node;
...@@ -1125,11 +1218,13 @@ static struct hist_entry *hierarchy_insert_entry(struct hists *hists, ...@@ -1125,11 +1218,13 @@ static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
if (new == NULL) if (new == NULL)
return NULL; return NULL;
hists__apply_filters(hists, new);
hists->nr_entries++; hists->nr_entries++;
/* save related format list for output */ /* save related format list for output */
new->hpp_list = hpp_list; new->hpp_list = hpp_list;
new->parent_he = parent_he;
hist_entry__apply_hierarchy_filters(new);
/* some fields are now passed to 'new' */ /* some fields are now passed to 'new' */
perf_hpp_list__for_each_sort_list(hpp_list, fmt) { perf_hpp_list__for_each_sort_list(hpp_list, fmt) {
...@@ -1170,14 +1265,13 @@ static int hists__hierarchy_insert_entry(struct hists *hists, ...@@ -1170,14 +1265,13 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
continue; continue;
/* insert copy of 'he' for each fmt into the hierarchy */ /* insert copy of 'he' for each fmt into the hierarchy */
new_he = hierarchy_insert_entry(hists, root, he, &node->hpp); new_he = hierarchy_insert_entry(hists, root, he, parent, &node->hpp);
if (new_he == NULL) { if (new_he == NULL) {
ret = -1; ret = -1;
break; break;
} }
root = &new_he->hroot_in; root = &new_he->hroot_in;
new_he->parent_he = parent;
new_he->depth = depth++; new_he->depth = depth++;
parent = new_he; parent = new_he;
} }
...@@ -1359,6 +1453,31 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h) ...@@ -1359,6 +1453,31 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h)
hists->stats.total_period += h->stat.period; hists->stats.total_period += h->stat.period;
} }
static void hierarchy_recalc_total_periods(struct hists *hists)
{
struct rb_node *node;
struct hist_entry *he;
node = rb_first(&hists->entries);
hists->stats.total_period = 0;
hists->stats.total_non_filtered_period = 0;
/*
* recalculate total period using top-level entries only
* since lower level entries only see non-filtered entries
* but upper level entries have sum of both entries.
*/
while (node) {
he = rb_entry(node, struct hist_entry, rb_node);
node = rb_next(node);
hists->stats.total_period += he->stat.period;
if (!he->filtered)
hists->stats.total_non_filtered_period += he->stat.period;
}
}
static void hierarchy_insert_output_entry(struct rb_root *root, static void hierarchy_insert_output_entry(struct rb_root *root,
struct hist_entry *he) struct hist_entry *he)
{ {
...@@ -1424,11 +1543,6 @@ static void hists__hierarchy_output_resort(struct hists *hists, ...@@ -1424,11 +1543,6 @@ static void hists__hierarchy_output_resort(struct hists *hists,
continue; continue;
} }
/* only update stat for leaf entries to avoid duplication */
hists__inc_stats(hists, he);
if (!he->filtered)
hists__calc_col_len(hists, he);
if (!use_callchain) if (!use_callchain)
continue; continue;
...@@ -1508,11 +1622,13 @@ static void output_resort(struct hists *hists, struct ui_progress *prog, ...@@ -1508,11 +1622,13 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
hists__reset_col_len(hists); hists__reset_col_len(hists);
if (symbol_conf.report_hierarchy) { if (symbol_conf.report_hierarchy) {
return hists__hierarchy_output_resort(hists, prog, hists__hierarchy_output_resort(hists, prog,
&hists->entries_collapsed, &hists->entries_collapsed,
&hists->entries, &hists->entries,
min_callchain_hits, min_callchain_hits,
use_callchain); use_callchain);
hierarchy_recalc_total_periods(hists);
return;
} }
if (sort__need_collapse) if (sort__need_collapse)
...@@ -1833,6 +1949,8 @@ static void hists__filter_hierarchy(struct hists *hists, int type, const void *a ...@@ -1833,6 +1949,8 @@ static void hists__filter_hierarchy(struct hists *hists, int type, const void *a
} }
} }
hierarchy_recalc_total_periods(hists);
/* /*
* resort output after applying a new filter since filter in a lower * resort output after applying a new filter since filter in a lower
* hierarchy can change periods in a upper hierarchy. * hierarchy can change periods in a upper hierarchy.
......
...@@ -79,7 +79,6 @@ struct hists { ...@@ -79,7 +79,6 @@ struct hists {
int socket_filter; int socket_filter;
struct perf_hpp_list *hpp_list; struct perf_hpp_list *hpp_list;
struct list_head hpp_formats; struct list_head hpp_formats;
int nr_sort_keys;
int nr_hpp_node; int nr_hpp_node;
}; };
...@@ -241,7 +240,6 @@ struct perf_hpp_fmt { ...@@ -241,7 +240,6 @@ struct perf_hpp_fmt {
struct perf_hpp_list { struct perf_hpp_list {
struct list_head fields; struct list_head fields;
struct list_head sorts; struct list_head sorts;
int nr_sort_keys;
}; };
extern struct perf_hpp_list perf_hpp_list; extern struct perf_hpp_list perf_hpp_list;
...@@ -318,6 +316,10 @@ bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *his ...@@ -318,6 +316,10 @@ bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *his
bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt); bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt);
bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt); bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt);
bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt); bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt);
bool perf_hpp__is_thread_entry(struct perf_hpp_fmt *fmt);
bool perf_hpp__is_comm_entry(struct perf_hpp_fmt *fmt);
bool perf_hpp__is_dso_entry(struct perf_hpp_fmt *fmt);
bool perf_hpp__is_sym_entry(struct perf_hpp_fmt *fmt);
struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt); struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt);
......
...@@ -98,7 +98,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * ...@@ -98,7 +98,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
char scale[128]; char scale[128];
int fd, ret = -1; int fd, ret = -1;
char path[PATH_MAX]; char path[PATH_MAX];
const char *lc; char *lc;
snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); snprintf(path, PATH_MAX, "%s/%s.scale", dir, name);
...@@ -146,7 +146,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * ...@@ -146,7 +146,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
/* restore locale */ /* restore locale */
setlocale(LC_NUMERIC, lc); setlocale(LC_NUMERIC, lc);
free((char *) lc); free(lc);
ret = 0; ret = 0;
error: error:
......
...@@ -27,6 +27,7 @@ int sort__has_sym = 0; ...@@ -27,6 +27,7 @@ int sort__has_sym = 0;
int sort__has_dso = 0; int sort__has_dso = 0;
int sort__has_socket = 0; int sort__has_socket = 0;
int sort__has_thread = 0; int sort__has_thread = 0;
int sort__has_comm = 0;
enum sort_mode sort__mode = SORT_MODE__NORMAL; enum sort_mode sort__mode = SORT_MODE__NORMAL;
/* /*
...@@ -1488,38 +1489,26 @@ bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) ...@@ -1488,38 +1489,26 @@ bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
return format->header == __sort__hpp_header; return format->header == __sort__hpp_header;
} }
bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt) #define MK_SORT_ENTRY_CHK(key) \
{ bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
struct hpp_sort_entry *hse; { \
struct hpp_sort_entry *hse; \
if (!perf_hpp__is_sort_entry(fmt)) \
return false; if (!perf_hpp__is_sort_entry(fmt)) \
return false; \
hse = container_of(fmt, struct hpp_sort_entry, hpp); \
return hse->se == &sort_trace; hse = container_of(fmt, struct hpp_sort_entry, hpp); \
return hse->se == &sort_ ## key ; \
} }
bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt) MK_SORT_ENTRY_CHK(trace)
{ MK_SORT_ENTRY_CHK(srcline)
struct hpp_sort_entry *hse; MK_SORT_ENTRY_CHK(srcfile)
MK_SORT_ENTRY_CHK(thread)
MK_SORT_ENTRY_CHK(comm)
MK_SORT_ENTRY_CHK(dso)
MK_SORT_ENTRY_CHK(sym)
if (!perf_hpp__is_sort_entry(fmt))
return false;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
return hse->se == &sort_srcline;
}
bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt)
{
struct hpp_sort_entry *hse;
if (!perf_hpp__is_sort_entry(fmt))
return false;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
return hse->se == &sort_srcfile;
}
static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
{ {
...@@ -1602,31 +1591,47 @@ int hist_entry__filter(struct hist_entry *he, int type, const void *arg) ...@@ -1602,31 +1591,47 @@ int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
{ {
struct perf_hpp_fmt *fmt; struct perf_hpp_fmt *fmt;
struct hpp_sort_entry *hse; struct hpp_sort_entry *hse;
int ret = -1;
int r;
fmt = he->fmt; perf_hpp_list__for_each_format(he->hpp_list, fmt) {
if (fmt == NULL || !perf_hpp__is_sort_entry(fmt)) if (!perf_hpp__is_sort_entry(fmt))
return -1; continue;
hse = container_of(fmt, struct hpp_sort_entry, hpp); hse = container_of(fmt, struct hpp_sort_entry, hpp);
if (hse->se->se_filter == NULL) if (hse->se->se_filter == NULL)
return -1; continue;
/*
* hist entry is filtered if any of sort key in the hpp list
* is applied. But it should skip non-matched filter types.
*/
r = hse->se->se_filter(he, type, arg);
if (r >= 0) {
if (ret < 0)
ret = 0;
ret |= r;
}
}
return hse->se->se_filter(he, type, arg); return ret;
} }
static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd, int level) static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd,
struct perf_hpp_list *list,
int level)
{ {
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level); struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
if (hse == NULL) if (hse == NULL)
return -1; return -1;
perf_hpp__register_sort_field(&hse->hpp); perf_hpp_list__register_sort_field(list, &hse->hpp);
return 0; return 0;
} }
static int __sort_dimension__add_hpp_output(struct perf_hpp_list *list, static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
struct sort_dimension *sd) struct perf_hpp_list *list)
{ {
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0); struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0);
...@@ -2147,12 +2152,14 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok, ...@@ -2147,12 +2152,14 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok,
return ret; return ret;
} }
static int __sort_dimension__add(struct sort_dimension *sd, int level) static int __sort_dimension__add(struct sort_dimension *sd,
struct perf_hpp_list *list,
int level)
{ {
if (sd->taken) if (sd->taken)
return 0; return 0;
if (__sort_dimension__add_hpp_sort(sd, level) < 0) if (__sort_dimension__add_hpp_sort(sd, list, level) < 0)
return -1; return -1;
if (sd->entry->se_collapse) if (sd->entry->se_collapse)
...@@ -2163,7 +2170,9 @@ static int __sort_dimension__add(struct sort_dimension *sd, int level) ...@@ -2163,7 +2170,9 @@ static int __sort_dimension__add(struct sort_dimension *sd, int level)
return 0; return 0;
} }
static int __hpp_dimension__add(struct hpp_dimension *hd, int level) static int __hpp_dimension__add(struct hpp_dimension *hd,
struct perf_hpp_list *list,
int level)
{ {
struct perf_hpp_fmt *fmt; struct perf_hpp_fmt *fmt;
...@@ -2175,7 +2184,7 @@ static int __hpp_dimension__add(struct hpp_dimension *hd, int level) ...@@ -2175,7 +2184,7 @@ static int __hpp_dimension__add(struct hpp_dimension *hd, int level)
return -1; return -1;
hd->taken = 1; hd->taken = 1;
perf_hpp__register_sort_field(fmt); perf_hpp_list__register_sort_field(list, fmt);
return 0; return 0;
} }
...@@ -2185,7 +2194,7 @@ static int __sort_dimension__add_output(struct perf_hpp_list *list, ...@@ -2185,7 +2194,7 @@ static int __sort_dimension__add_output(struct perf_hpp_list *list,
if (sd->taken) if (sd->taken)
return 0; return 0;
if (__sort_dimension__add_hpp_output(list, sd) < 0) if (__sort_dimension__add_hpp_output(sd, list) < 0)
return -1; return -1;
sd->taken = 1; sd->taken = 1;
...@@ -2215,7 +2224,8 @@ int hpp_dimension__add_output(unsigned col) ...@@ -2215,7 +2224,8 @@ int hpp_dimension__add_output(unsigned col)
return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]); return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]);
} }
static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
struct perf_evlist *evlist __maybe_unused,
int level) int level)
{ {
unsigned int i; unsigned int i;
...@@ -2253,9 +2263,11 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, ...@@ -2253,9 +2263,11 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
sort__has_socket = 1; sort__has_socket = 1;
} else if (sd->entry == &sort_thread) { } else if (sd->entry == &sort_thread) {
sort__has_thread = 1; sort__has_thread = 1;
} else if (sd->entry == &sort_comm) {
sort__has_comm = 1;
} }
return __sort_dimension__add(sd, level); return __sort_dimension__add(sd, list, level);
} }
for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
...@@ -2264,7 +2276,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, ...@@ -2264,7 +2276,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
if (strncasecmp(tok, hd->name, strlen(tok))) if (strncasecmp(tok, hd->name, strlen(tok)))
continue; continue;
return __hpp_dimension__add(hd, level); return __hpp_dimension__add(hd, list, level);
} }
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
...@@ -2279,7 +2291,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, ...@@ -2279,7 +2291,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
sort__has_sym = 1; sort__has_sym = 1;
__sort_dimension__add(sd, level); __sort_dimension__add(sd, list, level);
return 0; return 0;
} }
...@@ -2295,7 +2307,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, ...@@ -2295,7 +2307,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
if (sd->entry == &sort_mem_daddr_sym) if (sd->entry == &sort_mem_daddr_sym)
sort__has_sym = 1; sort__has_sym = 1;
__sort_dimension__add(sd, level); __sort_dimension__add(sd, list, level);
return 0; return 0;
} }
...@@ -2305,7 +2317,8 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, ...@@ -2305,7 +2317,8 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
return -ESRCH; return -ESRCH;
} }
static int setup_sort_list(char *str, struct perf_evlist *evlist) static int setup_sort_list(struct perf_hpp_list *list, char *str,
struct perf_evlist *evlist)
{ {
char *tmp, *tok; char *tmp, *tok;
int ret = 0; int ret = 0;
...@@ -2332,7 +2345,7 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist) ...@@ -2332,7 +2345,7 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist)
} }
if (*tok) { if (*tok) {
ret = sort_dimension__add(tok, evlist, level); ret = sort_dimension__add(list, tok, evlist, level);
if (ret == -EINVAL) { if (ret == -EINVAL) {
error("Invalid --sort key: `%s'", tok); error("Invalid --sort key: `%s'", tok);
break; break;
...@@ -2480,7 +2493,7 @@ static int __setup_sorting(struct perf_evlist *evlist) ...@@ -2480,7 +2493,7 @@ static int __setup_sorting(struct perf_evlist *evlist)
} }
} }
ret = setup_sort_list(str, evlist); ret = setup_sort_list(&perf_hpp_list, str, evlist);
free(str); free(str);
return ret; return ret;
...@@ -2693,29 +2706,6 @@ static int __setup_output_field(void) ...@@ -2693,29 +2706,6 @@ static int __setup_output_field(void)
return ret; return ret;
} }
static void evlist__set_hists_nr_sort_keys(struct perf_evlist *evlist)
{
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel) {
struct perf_hpp_fmt *fmt;
struct hists *hists = evsel__hists(evsel);
hists->nr_sort_keys = perf_hpp_list.nr_sort_keys;
/*
* If dynamic entries were used, it might add multiple
* entries to each evsel for a single field name. Set
* actual number of sort keys for each hists.
*/
perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) {
if (perf_hpp__is_dynamic_entry(fmt) &&
!perf_hpp__defined_dynamic_entry(fmt, hists))
hists->nr_sort_keys--;
}
}
}
int setup_sorting(struct perf_evlist *evlist) int setup_sorting(struct perf_evlist *evlist)
{ {
int err; int err;
...@@ -2725,14 +2715,11 @@ int setup_sorting(struct perf_evlist *evlist) ...@@ -2725,14 +2715,11 @@ int setup_sorting(struct perf_evlist *evlist)
return err; return err;
if (parent_pattern != default_parent_pattern) { if (parent_pattern != default_parent_pattern) {
err = sort_dimension__add("parent", evlist, -1); err = sort_dimension__add(&perf_hpp_list, "parent", evlist, -1);
if (err < 0) if (err < 0)
return err; return err;
} }
if (evlist != NULL)
evlist__set_hists_nr_sort_keys(evlist);
reset_dimensions(); reset_dimensions();
/* /*
......
...@@ -37,6 +37,7 @@ extern int sort__has_parent; ...@@ -37,6 +37,7 @@ extern int sort__has_parent;
extern int sort__has_sym; extern int sort__has_sym;
extern int sort__has_socket; extern int sort__has_socket;
extern int sort__has_thread; extern int sort__has_thread;
extern int sort__has_comm;
extern enum sort_mode sort__mode; extern enum sort_mode sort__mode;
extern struct sort_entry sort_comm; extern struct sort_entry sort_comm;
extern struct sort_entry sort_dso; extern struct sort_entry sort_dso;
...@@ -129,7 +130,6 @@ struct hist_entry { ...@@ -129,7 +130,6 @@ struct hist_entry {
void *raw_data; void *raw_data;
u32 raw_size; u32 raw_size;
void *trace_output; void *trace_output;
struct perf_hpp_fmt *fmt;
struct perf_hpp_list *hpp_list; struct perf_hpp_list *hpp_list;
struct hist_entry *parent_he; struct hist_entry *parent_he;
union { union {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment