forked from Minki/linux
perf/core improvements and fixes:
User visible: - Implement 'perf stat --metric-only' (Andi Kleen) - Fix perf script python database export crash (Chris Phlipot) Infrastructure: - 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: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJW4d7RAAoJENZQFvNTUqpA//UP/R6gwMFA5+z7bUXTUqDNGjw2 Zc2F7rKCqX46sswow814uvaQsNj/q7JtoHV3AR2L+lQSkB4RTojZ69fT5EKL8di1 Uvo/0hvc5TF+iD3gRQARLha04qtnR84qikToxXfiZRJPCMw5dCtS8Ibxk6NtiezC gfQagJcOBgx0j9KS4Oj7SPK3vidg9eHOL52ukTjgU0D0xu7S1Gyg5tPUwC0KgkSF rW+ncQPVsgmt9GNcEQKuwke49eBOcdB2TiYu6KSY88HzKj0oHoCQKJhU/CABbcDj SSmttVEPAhONk375P3ARkPWVgllgYO8ek6NAsXWXtMnhTqDmrgpmP1Po/sCEG7pr NvYCAyWW2GBkpRTcrp8C9Zh8dibgcFT+D7uccfeNj0boSsjmZum20DgTbATQyPB+ P4BgyfjrDieGXE4RpWHbahvdGmmfTFuPszur/5JSO1fjeQ/8GE4uuvVYyD8XEtOf y0jkd4jTSpSyEJt6Sc0t09AWDWH2Tb2kNw3EHeTDtJNAZXxsFzSnRjmqw5R5MdfR mXBvXxkNqXh4+EwtkYU7Sdxm27zNBCKIr9kaDH+rndpvs6rorNAvRxRIVViNMw9k Uq+NbI9J/h+Q9y4L60gJi8JQFVU5y5EfyCcp3PxpKH0Lw7e8rgNX3qhvQ8BIWwvH lcRzdu5GRYR9sjfDbJ1g =UVtX -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
ced30bc912
@ -2398,6 +2398,12 @@ static int arg_num_eval(struct print_arg *arg, long long *val)
|
||||
break;
|
||||
*val = left + right;
|
||||
break;
|
||||
case '~':
|
||||
ret = arg_num_eval(arg->op.right, &right);
|
||||
if (!ret)
|
||||
break;
|
||||
*val = ~right;
|
||||
break;
|
||||
default:
|
||||
do_warning("unknown op '%s'", arg->op.op);
|
||||
ret = 0;
|
||||
|
@ -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.
|
||||
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::
|
||||
Aggregate counts per processor socket for system-wide mode measurements. This
|
||||
is a useful mode to detect imbalance between sockets. To enable this mode,
|
||||
@ -219,6 +223,29 @@ $ perf stat -- make -j
|
||||
|
||||
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
|
||||
--------
|
||||
linkperf:perf-top[1], linkperf:perf-list[1]
|
||||
|
@ -1,3 +1,4 @@
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
@ -1,3 +1,4 @@
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
HAVE_KVM_STAT_SUPPORT := 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,
|
||||
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,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct ordered_events *oe __maybe_unused)
|
||||
@ -245,7 +245,7 @@ static int perf_event__repipe_mmap(struct perf_tool *tool,
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
#ifdef HAVE_JITDUMP
|
||||
static int perf_event__jit_repipe_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
@ -283,7 +283,7 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool,
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
#ifdef HAVE_JITDUMP
|
||||
static int perf_event__jit_repipe_mmap2(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
@ -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,
|
||||
"Merge sched-stat and sched-switch for getting events "
|
||||
"where and how long tasks slept"),
|
||||
#ifdef HAVE_JITDUMP
|
||||
OPT_BOOLEAN('j', "jit", &inject.jit_mode, "merge jitdump files into perf.data file"),
|
||||
#endif
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show build ids, etc)"),
|
||||
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)
|
||||
"perf inject [<options>]",
|
||||
NULL
|
||||
};
|
||||
#ifndef HAVE_LIBELF_SUPPORT
|
||||
#ifndef HAVE_JITDUMP
|
||||
set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true);
|
||||
#endif
|
||||
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)
|
||||
inject.tool.ordered_events = true;
|
||||
inject.tool.ordering_requires_timestamps = true;
|
||||
}
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
#ifdef HAVE_JITDUMP
|
||||
if (inject.jit_mode) {
|
||||
inject.tool.mmap2 = perf_event__jit_repipe_mmap2;
|
||||
inject.tool.mmap = perf_event__jit_repipe_mmap;
|
||||
|
@ -122,6 +122,7 @@ static bool sync_run = false;
|
||||
static unsigned int initial_delay = 0;
|
||||
static unsigned int unit_width = 4; /* strlen("unit") */
|
||||
static bool forever = false;
|
||||
static bool metric_only = false;
|
||||
static struct timespec ref_time;
|
||||
static struct cpu_map *aggr_map;
|
||||
static aggr_get_id_t aggr_get_id;
|
||||
@ -827,6 +828,99 @@ static void print_metric_csv(void *ctx,
|
||||
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)
|
||||
{
|
||||
FILE *output = stat_config.output;
|
||||
@ -921,9 +1015,16 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
||||
print_metric_t pm = print_metric_std;
|
||||
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[] = {
|
||||
[AGGR_GLOBAL] = 0,
|
||||
[AGGR_THREAD] = 1,
|
||||
@ -940,6 +1041,10 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
||||
os.nfields++;
|
||||
}
|
||||
if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
|
||||
if (metric_only) {
|
||||
pm(&os, NULL, "", "", 0);
|
||||
return;
|
||||
}
|
||||
aggr_printout(counter, id, nr);
|
||||
|
||||
fprintf(stat_config.output, "%*s%s",
|
||||
@ -968,7 +1073,9 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
if (metric_only)
|
||||
/* nothing */;
|
||||
else if (nsec_counter(counter))
|
||||
nsec_printout(id, nr, counter, uval);
|
||||
else
|
||||
abs_printout(id, nr, counter, uval);
|
||||
@ -977,7 +1084,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
||||
out.new_line = nl;
|
||||
out.ctx = &os;
|
||||
|
||||
if (csv_output) {
|
||||
if (csv_output && !metric_only) {
|
||||
print_noise(counter, noise);
|
||||
print_running(run, ena);
|
||||
}
|
||||
@ -985,7 +1092,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
||||
perf_stat__print_shadow_stats(counter, uval,
|
||||
first_shadow_cpu(counter, id),
|
||||
&out);
|
||||
if (!csv_output) {
|
||||
if (!csv_output && !metric_only) {
|
||||
print_noise(counter, noise);
|
||||
print_running(run, ena);
|
||||
}
|
||||
@ -1021,14 +1128,23 @@ static void print_aggr(char *prefix)
|
||||
int cpu, s, s2, id, nr;
|
||||
double uval;
|
||||
u64 ena, run, val;
|
||||
bool first;
|
||||
|
||||
if (!(aggr_map || aggr_get_id))
|
||||
return;
|
||||
|
||||
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++) {
|
||||
if (prefix && metric_only)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
id = aggr_map->map[s];
|
||||
first = true;
|
||||
evlist__for_each(evsel_list, counter) {
|
||||
val = ena = run = 0;
|
||||
nr = 0;
|
||||
@ -1041,13 +1157,20 @@ static void print_aggr(char *prefix)
|
||||
run += perf_counts(counter->counts, cpu, 0)->run;
|
||||
nr++;
|
||||
}
|
||||
if (prefix)
|
||||
if (first && metric_only) {
|
||||
first = false;
|
||||
aggr_printout(counter, id, nr);
|
||||
}
|
||||
if (prefix && !metric_only)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
uval = val * counter->scale;
|
||||
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)
|
||||
avg_enabled = avg_stats(&ps->res_stats[1]);
|
||||
avg_running = avg_stats(&ps->res_stats[2]);
|
||||
|
||||
if (prefix)
|
||||
if (prefix && !metric_only)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
uval = avg * counter->scale;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
FILE *output = stat_config.output;
|
||||
@ -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);
|
||||
|
||||
if (num_print_interval == 0 && !csv_output) {
|
||||
if (num_print_interval == 0 && !csv_output && !metric_only) {
|
||||
switch (stat_config.aggr_mode) {
|
||||
case AGGR_SOCKET:
|
||||
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)
|
||||
else
|
||||
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) {
|
||||
case AGGR_CORE:
|
||||
case AGGR_SOCKET:
|
||||
@ -1232,10 +1434,16 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
|
||||
case AGGR_GLOBAL:
|
||||
evlist__for_each(evsel_list, counter)
|
||||
print_counter_aggr(counter, prefix);
|
||||
if (metric_only)
|
||||
fputc('\n', stat_config.output);
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
evlist__for_each(evsel_list, counter)
|
||||
print_counter(counter, prefix);
|
||||
if (metric_only)
|
||||
print_no_aggr_metric(prefix);
|
||||
else {
|
||||
evlist__for_each(evsel_list, counter)
|
||||
print_counter(counter, prefix);
|
||||
}
|
||||
break;
|
||||
case AGGR_UNSET:
|
||||
default:
|
||||
@ -1356,6 +1564,8 @@ static const struct option stat_options[] = {
|
||||
"aggregate counts per thread", AGGR_THREAD),
|
||||
OPT_UINTEGER('D', "delay", &initial_delay,
|
||||
"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()
|
||||
};
|
||||
|
||||
@ -1997,6 +2207,16 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
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) {
|
||||
fprintf(stderr, "argument to --log-fd must be a > 0\n");
|
||||
parse_options_usage(stat_usage, stat_options, "log-fd", 0);
|
||||
|
@ -328,6 +328,13 @@ ifndef NO_LIBELF
|
||||
endif # NO_LIBBPF
|
||||
endif # NO_LIBELF
|
||||
|
||||
ifdef PERF_HAVE_JITDUMP
|
||||
ifndef NO_DWARF
|
||||
$(call detected,CONFIG_JITDUMP)
|
||||
CFLAGS += -DHAVE_JITDUMP
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),powerpc)
|
||||
ifndef NO_DWARF
|
||||
CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
|
||||
|
@ -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,
|
||||
struct hist_entry *he,
|
||||
FILE *fp, int level,
|
||||
int nr_sort_keys)
|
||||
FILE *fp, int level)
|
||||
{
|
||||
char s[8192];
|
||||
int printed = 0;
|
||||
@ -1939,23 +1938,20 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
|
||||
.size = sizeof(s),
|
||||
};
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
bool first = true;
|
||||
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, "");
|
||||
|
||||
folded_sign = hist_entry__folded(he);
|
||||
printed += fprintf(fp, "%c", folded_sign);
|
||||
|
||||
hists__for_each_format(he->hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, he->hists))
|
||||
continue;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt) ||
|
||||
perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&he->hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (!first) {
|
||||
ret = scnprintf(hpp.buf, hpp.size, " ");
|
||||
advance_hpp(&hpp, ret);
|
||||
@ -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),
|
||||
browser->min_pcnt);
|
||||
int printed = 0;
|
||||
int nr_sort = browser->hists->nr_sort_keys;
|
||||
|
||||
while (nd) {
|
||||
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)
|
||||
if (symbol_conf.report_hierarchy) {
|
||||
printed += hist_browser__fprintf_hierarchy_entry(browser,
|
||||
h, fp,
|
||||
h->depth,
|
||||
nr_sort);
|
||||
h->depth);
|
||||
} else {
|
||||
printed += hist_browser__fprintf_entry(browser, h, fp);
|
||||
}
|
||||
@ -2142,11 +2136,18 @@ static int hists__browser_title(struct hists *hists,
|
||||
if (hists->uid_filter_str)
|
||||
printed += snprintf(bf + printed, size - printed,
|
||||
", UID: %s", hists->uid_filter_str);
|
||||
if (thread)
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
if (thread) {
|
||||
if (sort__has_thread) {
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
", Thread: %s(%d)",
|
||||
(thread->comm_set ? thread__comm_str(thread) : ""),
|
||||
thread->tid);
|
||||
} else {
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
", Thread: %s",
|
||||
(thread->comm_set ? thread__comm_str(thread) : ""));
|
||||
}
|
||||
}
|
||||
if (dso)
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
", DSO: %s", dso->short_name);
|
||||
@ -2321,15 +2322,24 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
|
||||
{
|
||||
struct thread *thread = act->thread;
|
||||
|
||||
if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
|
||||
return 0;
|
||||
|
||||
if (browser->hists->thread_filter) {
|
||||
pstack__remove(browser->pstack, &browser->hists->thread_filter);
|
||||
perf_hpp__set_elide(HISTC_THREAD, false);
|
||||
thread__zput(browser->hists->thread_filter);
|
||||
ui_helpline__pop();
|
||||
} else {
|
||||
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
|
||||
thread->comm_set ? thread__comm_str(thread) : "",
|
||||
thread->tid);
|
||||
if (sort__has_thread) {
|
||||
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
|
||||
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);
|
||||
perf_hpp__set_elide(HISTC_THREAD, false);
|
||||
pstack__push(browser->pstack, &browser->hists->thread_filter);
|
||||
@ -2344,13 +2354,22 @@ static int
|
||||
add_thread_opt(struct hist_browser *browser, struct popup_action *act,
|
||||
char **optstr, struct thread *thread)
|
||||
{
|
||||
if (!sort__has_thread || thread == NULL)
|
||||
int ret;
|
||||
|
||||
if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
|
||||
return 0;
|
||||
|
||||
if (asprintf(optstr, "Zoom %s %s(%d) thread",
|
||||
browser->hists->thread_filter ? "out of" : "into",
|
||||
thread->comm_set ? thread__comm_str(thread) : "",
|
||||
thread->tid) < 0)
|
||||
if (sort__has_thread) {
|
||||
ret = asprintf(optstr, "Zoom %s %s(%d) thread",
|
||||
browser->hists->thread_filter ? "out of" : "into",
|
||||
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;
|
||||
|
||||
act->thread = thread;
|
||||
@ -2363,6 +2382,9 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
|
||||
{
|
||||
struct map *map = act->ms.map;
|
||||
|
||||
if (!sort__has_dso || map == NULL)
|
||||
return 0;
|
||||
|
||||
if (browser->hists->dso_filter) {
|
||||
pstack__remove(browser->pstack, &browser->hists->dso_filter);
|
||||
perf_hpp__set_elide(HISTC_DSO, false);
|
||||
@ -2514,6 +2536,9 @@ add_exit_opt(struct hist_browser *browser __maybe_unused,
|
||||
static int
|
||||
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) {
|
||||
pstack__remove(browser->pstack, &browser->hists->socket_filter);
|
||||
browser->hists->socket_filter = -1;
|
||||
|
@ -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,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -107,9 +107,12 @@ libperf-y += scripting-engines/
|
||||
libperf-$(CONFIG_ZLIB) += zlib.o
|
||||
libperf-$(CONFIG_LZMA) += lzma.o
|
||||
libperf-y += demangle-java.o
|
||||
|
||||
ifdef CONFIG_JITDUMP
|
||||
libperf-$(CONFIG_LIBELF) += jitdump.o
|
||||
libperf-$(CONFIG_LIBELF) += genelf.o
|
||||
libperf-$(CONFIG_LIBELF) += genelf_debug.o
|
||||
endif
|
||||
|
||||
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
# avoid compiler warnings in 32-bit mode
|
||||
|
@ -93,10 +93,8 @@ struct perf_evsel {
|
||||
const char *unit;
|
||||
struct event_format *tp_format;
|
||||
off_t id_offset;
|
||||
union {
|
||||
void *priv;
|
||||
u64 db_id;
|
||||
};
|
||||
void *priv;
|
||||
u64 db_id;
|
||||
struct cgroup_sel *cgrp;
|
||||
void *handler;
|
||||
struct cpu_map *cpus;
|
||||
|
@ -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__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,
|
||||
struct rb_root *root,
|
||||
struct hist_entry *he,
|
||||
struct hist_entry *parent_he,
|
||||
struct perf_hpp_list *hpp_list)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
@ -1125,11 +1218,13 @@ static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
|
||||
if (new == NULL)
|
||||
return NULL;
|
||||
|
||||
hists__apply_filters(hists, new);
|
||||
hists->nr_entries++;
|
||||
|
||||
/* save related format list for output */
|
||||
new->hpp_list = hpp_list;
|
||||
new->parent_he = parent_he;
|
||||
|
||||
hist_entry__apply_hierarchy_filters(new);
|
||||
|
||||
/* some fields are now passed to 'new' */
|
||||
perf_hpp_list__for_each_sort_list(hpp_list, fmt) {
|
||||
@ -1170,14 +1265,13 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
|
||||
continue;
|
||||
|
||||
/* 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) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
root = &new_he->hroot_in;
|
||||
new_he->parent_he = parent;
|
||||
new_he->depth = depth++;
|
||||
parent = new_he;
|
||||
}
|
||||
@ -1359,6 +1453,31 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h)
|
||||
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,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
@ -1424,11 +1543,6 @@ static void hists__hierarchy_output_resort(struct hists *hists,
|
||||
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)
|
||||
continue;
|
||||
|
||||
@ -1508,11 +1622,13 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
|
||||
hists__reset_col_len(hists);
|
||||
|
||||
if (symbol_conf.report_hierarchy) {
|
||||
return hists__hierarchy_output_resort(hists, prog,
|
||||
&hists->entries_collapsed,
|
||||
&hists->entries,
|
||||
min_callchain_hits,
|
||||
use_callchain);
|
||||
hists__hierarchy_output_resort(hists, prog,
|
||||
&hists->entries_collapsed,
|
||||
&hists->entries,
|
||||
min_callchain_hits,
|
||||
use_callchain);
|
||||
hierarchy_recalc_total_periods(hists);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sort__need_collapse)
|
||||
@ -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
|
||||
* hierarchy can change periods in a upper hierarchy.
|
||||
|
@ -79,7 +79,6 @@ struct hists {
|
||||
int socket_filter;
|
||||
struct perf_hpp_list *hpp_list;
|
||||
struct list_head hpp_formats;
|
||||
int nr_sort_keys;
|
||||
int nr_hpp_node;
|
||||
};
|
||||
|
||||
@ -241,7 +240,6 @@ struct perf_hpp_fmt {
|
||||
struct perf_hpp_list {
|
||||
struct list_head fields;
|
||||
struct list_head sorts;
|
||||
int nr_sort_keys;
|
||||
};
|
||||
|
||||
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
|
||||
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_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);
|
||||
|
||||
|
@ -98,7 +98,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
|
||||
char scale[128];
|
||||
int fd, ret = -1;
|
||||
char path[PATH_MAX];
|
||||
const char *lc;
|
||||
char *lc;
|
||||
|
||||
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 *
|
||||
/* restore locale */
|
||||
setlocale(LC_NUMERIC, lc);
|
||||
|
||||
free((char *) lc);
|
||||
free(lc);
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
|
@ -27,6 +27,7 @@ int sort__has_sym = 0;
|
||||
int sort__has_dso = 0;
|
||||
int sort__has_socket = 0;
|
||||
int sort__has_thread = 0;
|
||||
int sort__has_comm = 0;
|
||||
enum sort_mode sort__mode = SORT_MODE__NORMAL;
|
||||
|
||||
/*
|
||||
@ -1488,38 +1489,26 @@ bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
|
||||
return format->header == __sort__hpp_header;
|
||||
}
|
||||
|
||||
bool perf_hpp__is_trace_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_trace;
|
||||
#define MK_SORT_ENTRY_CHK(key) \
|
||||
bool perf_hpp__is_ ## key ## _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_ ## key ; \
|
||||
}
|
||||
|
||||
bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
MK_SORT_ENTRY_CHK(trace)
|
||||
MK_SORT_ENTRY_CHK(srcline)
|
||||
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)
|
||||
{
|
||||
@ -1602,31 +1591,47 @@ int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct hpp_sort_entry *hse;
|
||||
int ret = -1;
|
||||
int r;
|
||||
|
||||
fmt = he->fmt;
|
||||
if (fmt == NULL || !perf_hpp__is_sort_entry(fmt))
|
||||
return -1;
|
||||
perf_hpp_list__for_each_format(he->hpp_list, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
if (hse->se->se_filter == NULL)
|
||||
return -1;
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
if (hse->se->se_filter == NULL)
|
||||
continue;
|
||||
|
||||
return hse->se->se_filter(he, type, arg);
|
||||
/*
|
||||
* 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 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);
|
||||
|
||||
if (hse == NULL)
|
||||
return -1;
|
||||
|
||||
perf_hpp__register_sort_field(&hse->hpp);
|
||||
perf_hpp_list__register_sort_field(list, &hse->hpp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_hpp_output(struct perf_hpp_list *list,
|
||||
struct sort_dimension *sd)
|
||||
static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
|
||||
struct perf_hpp_list *list)
|
||||
{
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0);
|
||||
|
||||
@ -2147,12 +2152,14 @@ out:
|
||||
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)
|
||||
return 0;
|
||||
|
||||
if (__sort_dimension__add_hpp_sort(sd, level) < 0)
|
||||
if (__sort_dimension__add_hpp_sort(sd, list, level) < 0)
|
||||
return -1;
|
||||
|
||||
if (sd->entry->se_collapse)
|
||||
@ -2163,7 +2170,9 @@ static int __sort_dimension__add(struct sort_dimension *sd, int level)
|
||||
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;
|
||||
|
||||
@ -2175,7 +2184,7 @@ static int __hpp_dimension__add(struct hpp_dimension *hd, int level)
|
||||
return -1;
|
||||
|
||||
hd->taken = 1;
|
||||
perf_hpp__register_sort_field(fmt);
|
||||
perf_hpp_list__register_sort_field(list, fmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2185,7 +2194,7 @@ static int __sort_dimension__add_output(struct perf_hpp_list *list,
|
||||
if (sd->taken)
|
||||
return 0;
|
||||
|
||||
if (__sort_dimension__add_hpp_output(list, sd) < 0)
|
||||
if (__sort_dimension__add_hpp_output(sd, list) < 0)
|
||||
return -1;
|
||||
|
||||
sd->taken = 1;
|
||||
@ -2215,7 +2224,8 @@ int hpp_dimension__add_output(unsigned 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)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -2253,9 +2263,11 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
|
||||
sort__has_socket = 1;
|
||||
} else if (sd->entry == &sort_thread) {
|
||||
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++) {
|
||||
@ -2264,7 +2276,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
|
||||
if (strncasecmp(tok, hd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __hpp_dimension__add(hd, level);
|
||||
return __hpp_dimension__add(hd, list, level);
|
||||
}
|
||||
|
||||
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,
|
||||
if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
|
||||
sort__has_sym = 1;
|
||||
|
||||
__sort_dimension__add(sd, level);
|
||||
__sort_dimension__add(sd, list, level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2295,7 +2307,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
|
||||
if (sd->entry == &sort_mem_daddr_sym)
|
||||
sort__has_sym = 1;
|
||||
|
||||
__sort_dimension__add(sd, level);
|
||||
__sort_dimension__add(sd, list, level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2305,7 +2317,8 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
|
||||
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;
|
||||
int ret = 0;
|
||||
@ -2332,7 +2345,7 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist)
|
||||
}
|
||||
|
||||
if (*tok) {
|
||||
ret = sort_dimension__add(tok, evlist, level);
|
||||
ret = sort_dimension__add(list, tok, evlist, level);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --sort key: `%s'", tok);
|
||||
break;
|
||||
@ -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);
|
||||
return ret;
|
||||
@ -2693,29 +2706,6 @@ out:
|
||||
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 err;
|
||||
@ -2725,14 +2715,11 @@ int setup_sorting(struct perf_evlist *evlist)
|
||||
return err;
|
||||
|
||||
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)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (evlist != NULL)
|
||||
evlist__set_hists_nr_sort_keys(evlist);
|
||||
|
||||
reset_dimensions();
|
||||
|
||||
/*
|
||||
|
@ -37,6 +37,7 @@ extern int sort__has_parent;
|
||||
extern int sort__has_sym;
|
||||
extern int sort__has_socket;
|
||||
extern int sort__has_thread;
|
||||
extern int sort__has_comm;
|
||||
extern enum sort_mode sort__mode;
|
||||
extern struct sort_entry sort_comm;
|
||||
extern struct sort_entry sort_dso;
|
||||
@ -129,7 +130,6 @@ struct hist_entry {
|
||||
void *raw_data;
|
||||
u32 raw_size;
|
||||
void *trace_output;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list *hpp_list;
|
||||
struct hist_entry *parent_he;
|
||||
union {
|
||||
|
Loading…
Reference in New Issue
Block a user