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;
|
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)
|
|||||||
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
|
|||||||
|
|
||||||
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]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
ifndef NO_DWARF
|
ifndef NO_DWARF
|
||||||
PERF_HAVE_DWARF_REGS := 1
|
PERF_HAVE_DWARF_REGS := 1
|
||||||
endif
|
endif
|
||||||
|
PERF_HAVE_JITDUMP := 1
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
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
|
|||||||
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
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
"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)
|
|||||||
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;
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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[] = {
|
|||||||
"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)
|
|||||||
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
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
.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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
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/
|
|||||||
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 {
|
|||||||
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,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
|||||||
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 {
|
|||||||
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
|
|||||||
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 *
|
|||||||
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 *
|
|||||||
/* 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;
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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;
|
||||||
|
|
||||||
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);
|
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 @@ out:
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@ out:
|
|||||||
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)
|
|||||||
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;
|
|||||||
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 {
|
|||||||
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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user