mirror of https://gitee.com/openkylin/linux.git
perf/core improvements and fixes:
New features: - Add 'L' hotkey to dynamicly set the percent threshold for histogram entries and callchains, i.e. dynamicly do what the --percent-limit command line option to 'top' and 'report' does. (Namhyung Kim) Infrastructure: - Per hists field and sort lists, that will be used, for instance, in the c2c tool (Jiri Olsa) Documentation: - Update documentation of --sort and --perf-limit options for 'perf report' (Namhyung Kim) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWsiK5AAoJENZQFvNTUqpADJoQAIVBlW29iw+qshg0bWM+1xw/ RBKgTCLT2VPK9wE00qIQOmZjf4ZRHLWDBVDf8iB36UMhY4q4vUaWYRJu8dZ/IoPx AQPg5JFe8INd2voDHo6E8vCdS58Mk7NWRdWs9dctVHGa7LjdDdLhYNLcghIps8Pb 4cSLltmaa/XGxBvu526g6TRSaoaCIVO1W0wxNvoc3XFipI7zqtgCasLr5Hb6RxdD BUC7WYuJ5n96EqR0vjeV7ajv9m65+RaLFIIa6vjUIEdAB+3sDIpfiMpkIprAzoze TcsGpSz5+5yAbnezgIoHaRvFWzd/hune62sZmCySd1E3+5Vb8JbUo4WxNTAlTwdS XbwY1VyLYDnKFVqQSAElFbeXad8kbkd06tzLgUOTfoeHWKMp5qxfH/myLNjF42pK d5mQ5TnN/ESsj3D/3TcgBzmIOGIS5R2evKsIIwip4rSGAP901U+RRBMDE87vn0M2 TsqrkKPZgQcYTA9FKlWIPCdT0Y+yYr0jvgRabN9rQMCp5r7Wb7hrpwHGMPp16hbT 1mnMm8N+moIMv1Wp0R5C75bls2uFaOiS3Y7XyORjqfwHMfzLUqPWPSmDwtSrbE8a FTDGInJubTJRXdjTE/0+d+zZbpiEqTybfjOce3AcB2KBMfSJaQRvLcvAei21GMWC hrBhLY6ngbgd7jD1RTHg =foQI -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' 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: New features: - Add 'L' hotkey to dynamicly set the percent threshold for histogram entries and callchains, i.e. dynamicly do what the --percent-limit command line option to 'top' and 'report' does. (Namhyung Kim) Infrastructure changes: - Per hists field and sort lists, that will be used, for instance, in the c2c tool (Jiri Olsa) Documentation changes: - Update documentation of --sort and --perf-limit options for 'perf report' (Namhyung Kim) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
d3aaf09f88
|
@ -117,6 +117,22 @@ OPTIONS
|
|||
And default sort keys are changed to comm, dso_from, symbol_from, dso_to
|
||||
and symbol_to, see '--branch-stack'.
|
||||
|
||||
If the --mem-mode option is used, the following sort keys are also available
|
||||
(incompatible with --branch-stack):
|
||||
symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline.
|
||||
|
||||
- symbol_daddr: name of data symbol being executed on at the time of sample
|
||||
- dso_daddr: name of library or module containing the data being executed
|
||||
on at the time of the sample
|
||||
- locked: whether the bus was locked at the time of the sample
|
||||
- tlb: type of tlb access for the data at the time of the sample
|
||||
- mem: type of memory access for the data at the time of the sample
|
||||
- snoop: type of snoop (if any) for the data at the time of the sample
|
||||
- dcacheline: the cacheline the data address is on at the time of the sample
|
||||
|
||||
And the default sort keys are changed to local_weight, mem, sym, dso,
|
||||
symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'.
|
||||
|
||||
If the data file has tracepoint event(s), following (dynamic) sort keys
|
||||
are also available:
|
||||
trace, trace_fields, [<event>.]<field>[/raw]
|
||||
|
@ -151,22 +167,6 @@ OPTIONS
|
|||
By default, every sort keys not specified in -F will be appended
|
||||
automatically.
|
||||
|
||||
If --mem-mode option is used, following sort keys are also available
|
||||
(incompatible with --branch-stack):
|
||||
symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline.
|
||||
|
||||
- symbol_daddr: name of data symbol being executed on at the time of sample
|
||||
- dso_daddr: name of library or module containing the data being executed
|
||||
on at the time of sample
|
||||
- locked: whether the bus was locked at the time of sample
|
||||
- tlb: type of tlb access for the data at the time of sample
|
||||
- mem: type of memory access for the data at the time of sample
|
||||
- snoop: type of snoop (if any) for the data at the time of sample
|
||||
- dcacheline: the cacheline the data address is on at the time of sample
|
||||
|
||||
And default sort keys are changed to local_weight, mem, sym, dso,
|
||||
symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'.
|
||||
|
||||
-p::
|
||||
--parent=<regex>::
|
||||
A regex filter to identify parent. The parent is a caller of this
|
||||
|
@ -351,7 +351,10 @@ OPTIONS
|
|||
|
||||
--percent-limit::
|
||||
Do not show entries which have an overhead under that percent.
|
||||
(Default: 0).
|
||||
(Default: 0). Note that this option also sets the percent limit (threshold)
|
||||
of callchains. However the default value of callchain threshold is
|
||||
different than the default value of hist entries. Please see the
|
||||
--call-graph option for details.
|
||||
|
||||
--percentage::
|
||||
Determine how to display the overhead percentage of filtered entries.
|
||||
|
|
|
@ -245,7 +245,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
|||
hists__collapse_resort(hists, NULL);
|
||||
/* Don't sort callchain */
|
||||
perf_evsel__reset_sample_bit(pos, CALLCHAIN);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(pos, NULL);
|
||||
|
||||
if (symbol_conf.event_group &&
|
||||
!perf_evsel__is_group_leader(pos))
|
||||
|
|
|
@ -507,7 +507,7 @@ static void report__output_resort(struct report *rep)
|
|||
ui_progress__init(&prog, rep->nr_entries, "Sorting events for output...");
|
||||
|
||||
evlist__for_each(rep->session->evlist, pos)
|
||||
hists__output_resort(evsel__hists(pos), &prog);
|
||||
perf_evsel__output_resort(pos, &prog);
|
||||
|
||||
ui_progress__finish();
|
||||
}
|
||||
|
@ -912,15 +912,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
symbol_conf.cumulate_callchain = false;
|
||||
}
|
||||
|
||||
if (setup_sorting(session->evlist) < 0) {
|
||||
if (sort_order)
|
||||
parse_options_usage(report_usage, options, "s", 1);
|
||||
if (field_order)
|
||||
parse_options_usage(sort_order ? NULL : report_usage,
|
||||
options, "F", 1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Force tty output for header output and per-thread stat. */
|
||||
if (report.header || report.header_only || report.show_threads)
|
||||
use_browser = 0;
|
||||
|
@ -930,6 +921,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
else
|
||||
use_browser = 0;
|
||||
|
||||
if (setup_sorting(session->evlist) < 0) {
|
||||
if (sort_order)
|
||||
parse_options_usage(report_usage, options, "s", 1);
|
||||
if (field_order)
|
||||
parse_options_usage(sort_order ? NULL : report_usage,
|
||||
options, "F", 1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (report.header || report.header_only) {
|
||||
perf_session__fprintf_info(session, stdout,
|
||||
report.show_full_info);
|
||||
|
|
|
@ -252,7 +252,8 @@ static void perf_top__print_sym_table(struct perf_top *top)
|
|||
char bf[160];
|
||||
int printed = 0;
|
||||
const int win_width = top->winsize.ws_col - 1;
|
||||
struct hists *hists = evsel__hists(top->sym_evsel);
|
||||
struct perf_evsel *evsel = top->sym_evsel;
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
|
||||
puts(CONSOLE_CLEAR);
|
||||
|
||||
|
@ -288,7 +289,7 @@ static void perf_top__print_sym_table(struct perf_top *top)
|
|||
}
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
hists__output_recalc_col_len(hists, top->print_entries - printed);
|
||||
putchar('\n');
|
||||
|
@ -540,6 +541,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
|
|||
static void perf_top__sort_new_samples(void *arg)
|
||||
{
|
||||
struct perf_top *t = arg;
|
||||
struct perf_evsel *evsel = t->sym_evsel;
|
||||
struct hists *hists;
|
||||
|
||||
perf_top__reset_sample_counters(t);
|
||||
|
@ -547,7 +549,7 @@ static void perf_top__sort_new_samples(void *arg)
|
|||
if (t->evlist->selected != NULL)
|
||||
t->sym_evsel = t->evlist->selected;
|
||||
|
||||
hists = evsel__hists(t->sym_evsel);
|
||||
hists = evsel__hists(evsel);
|
||||
|
||||
if (t->evlist->enabled) {
|
||||
if (t->zero) {
|
||||
|
@ -559,7 +561,7 @@ static void perf_top__sort_new_samples(void *arg)
|
|||
}
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
}
|
||||
|
||||
static void *display_thread_tui(void *arg)
|
||||
|
@ -1243,6 +1245,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
/* display thread wants entries to be collapsed in a different tree */
|
||||
sort__need_collapse = 1;
|
||||
|
||||
if (top.use_stdio)
|
||||
use_browser = 0;
|
||||
else if (top.use_tui)
|
||||
use_browser = 1;
|
||||
|
||||
setup_browser(false);
|
||||
|
||||
if (setup_sorting(top.evlist) < 0) {
|
||||
if (sort_order)
|
||||
parse_options_usage(top_usage, options, "s", 1);
|
||||
|
@ -1252,13 +1261,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
if (top.use_stdio)
|
||||
use_browser = 0;
|
||||
else if (top.use_tui)
|
||||
use_browser = 1;
|
||||
|
||||
setup_browser(false);
|
||||
|
||||
status = target__validate(target);
|
||||
if (status) {
|
||||
target__strerror(target, status, errbuf, BUFSIZ);
|
||||
|
|
|
@ -191,7 +191,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
|
|||
* function since TEST_ASSERT_VAL() returns in case of failure.
|
||||
*/
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(hists_to_evsel(hists), NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("use callchain: %d, cumulate callchain: %d\n",
|
||||
|
|
|
@ -145,7 +145,7 @@ int test__hists_filter(int subtest __maybe_unused)
|
|||
struct hists *hists = evsel__hists(evsel);
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("Normal histogram\n");
|
||||
|
|
|
@ -156,7 +156,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -256,7 +256,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -310,7 +310,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -388,7 +388,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -491,7 +491,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
|
|
@ -1095,7 +1095,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
|||
|
||||
hist_browser__gotorc(browser, row, 0);
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(browser->hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, entry->hists) ||
|
||||
column++ < browser->b.horiz_scroll)
|
||||
continue;
|
||||
|
@ -1175,7 +1175,7 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *
|
|||
return ret;
|
||||
}
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(browser->hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
|
||||
continue;
|
||||
|
||||
|
@ -1441,7 +1441,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
|||
if (symbol_conf.use_callchain)
|
||||
printed += fprintf(fp, "%c ", folded_sign);
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(browser->hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, he->hists))
|
||||
continue;
|
||||
|
||||
|
@ -2029,6 +2029,42 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb)
|
|||
hb->nr_non_filtered_entries = nr_entries;
|
||||
}
|
||||
|
||||
static void hist_browser__update_percent_limit(struct hist_browser *hb,
|
||||
double percent)
|
||||
{
|
||||
struct hist_entry *he;
|
||||
struct rb_node *nd = rb_first(&hb->hists->entries);
|
||||
u64 total = hists__total_period(hb->hists);
|
||||
u64 min_callchain_hits = total * (percent / 100);
|
||||
|
||||
hb->min_pcnt = callchain_param.min_percent = percent;
|
||||
|
||||
if (!symbol_conf.use_callchain)
|
||||
return;
|
||||
|
||||
while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
|
||||
he = rb_entry(nd, struct hist_entry, rb_node);
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL) {
|
||||
total = he->stat.period;
|
||||
|
||||
if (symbol_conf.cumulate_callchain)
|
||||
total = he->stat_acc->period;
|
||||
|
||||
min_callchain_hits = total * (percent / 100);
|
||||
}
|
||||
|
||||
callchain_param.sort(&he->sorted_chain, he->callchain,
|
||||
min_callchain_hits, &callchain_param);
|
||||
|
||||
/* force to re-evaluate folding state of callchains */
|
||||
he->init_have_children = false;
|
||||
hist_entry__set_folding(he, false);
|
||||
|
||||
nd = rb_next(nd);
|
||||
}
|
||||
}
|
||||
|
||||
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
const char *helpline,
|
||||
bool left_exits,
|
||||
|
@ -2064,6 +2100,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
"E Expand all callchains\n" \
|
||||
"F Toggle percentage of filtered entries\n" \
|
||||
"H Display column headers\n" \
|
||||
"L Change percent limit\n" \
|
||||
"m Display context menu\n" \
|
||||
"S Zoom into current Processor Socket\n" \
|
||||
|
||||
|
@ -2104,7 +2141,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
memset(options, 0, sizeof(options));
|
||||
memset(actions, 0, sizeof(actions));
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(browser->hists, fmt) {
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
/*
|
||||
* This is done just once, and activates the horizontal scrolling
|
||||
|
@ -2219,6 +2256,24 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
top->zero = !top->zero;
|
||||
}
|
||||
continue;
|
||||
case 'L':
|
||||
if (ui_browser__input_window("Percent Limit",
|
||||
"Please enter the value you want to hide entries under that percent.",
|
||||
buf, "ENTER: OK, ESC: Cancel",
|
||||
delay_secs * 2) == K_ENTER) {
|
||||
char *end;
|
||||
double new_percent = strtod(buf, &end);
|
||||
|
||||
if (new_percent < 0 || new_percent > 100) {
|
||||
ui_browser__warning(&browser->b, delay_secs * 2,
|
||||
"Invalid percent: %.2f", new_percent);
|
||||
continue;
|
||||
}
|
||||
|
||||
hist_browser__update_percent_limit(browser, new_percent);
|
||||
hist_browser__reset(browser);
|
||||
}
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
|
|
|
@ -306,7 +306,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
|
||||
nr_cols = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
hists__for_each_format(hists, fmt)
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
|
||||
store = gtk_tree_store_newv(nr_cols, col_types);
|
||||
|
@ -317,7 +317,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
|
||||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
|
@ -367,7 +367,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
|
||||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, h->hists))
|
||||
continue;
|
||||
|
||||
|
|
|
@ -371,7 +371,20 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name, _fn) \
|
||||
static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a)
|
||||
{
|
||||
return a->header == hpp__header_fn;
|
||||
}
|
||||
|
||||
static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
|
||||
{
|
||||
if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b))
|
||||
return false;
|
||||
|
||||
return a->idx == b->idx;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
|
@ -381,9 +394,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
|
|||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
.idx = PERF_HPP__ ## _idx, \
|
||||
.equal = hpp__equal, \
|
||||
}
|
||||
|
||||
#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \
|
||||
#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
|
@ -393,9 +408,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
|
|||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
.idx = PERF_HPP__ ## _idx, \
|
||||
.equal = hpp__equal, \
|
||||
}
|
||||
|
||||
#define HPP__PRINT_FNS(_name, _fn) \
|
||||
#define HPP__PRINT_FNS(_name, _fn, _idx) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
|
@ -404,22 +421,25 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
|
|||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
.idx = PERF_HPP__ ## _idx, \
|
||||
.equal = hpp__equal, \
|
||||
}
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
HPP__COLOR_PRINT_FNS("Overhead", overhead),
|
||||
HPP__COLOR_PRINT_FNS("sys", overhead_sys),
|
||||
HPP__COLOR_PRINT_FNS("usr", overhead_us),
|
||||
HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys),
|
||||
HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us),
|
||||
HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc),
|
||||
HPP__PRINT_FNS("Samples", samples),
|
||||
HPP__PRINT_FNS("Period", period)
|
||||
HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD),
|
||||
HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS),
|
||||
HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US),
|
||||
HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS),
|
||||
HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US),
|
||||
HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC),
|
||||
HPP__PRINT_FNS("Samples", samples, SAMPLES),
|
||||
HPP__PRINT_FNS("Period", period, PERIOD)
|
||||
};
|
||||
|
||||
LIST_HEAD(perf_hpp__list);
|
||||
LIST_HEAD(perf_hpp__sort_list);
|
||||
|
||||
struct perf_hpp_list perf_hpp_list = {
|
||||
.fields = LIST_HEAD_INIT(perf_hpp_list.fields),
|
||||
.sorts = LIST_HEAD_INIT(perf_hpp_list.sorts),
|
||||
};
|
||||
|
||||
#undef HPP__COLOR_PRINT_FNS
|
||||
#undef HPP__COLOR_ACC_PRINT_FNS
|
||||
|
@ -485,9 +505,16 @@ void perf_hpp__init(void)
|
|||
hpp_dimension__add_output(PERF_HPP__PERIOD);
|
||||
}
|
||||
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format)
|
||||
void perf_hpp_list__column_register(struct perf_hpp_list *list,
|
||||
struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_add_tail(&format->list, &perf_hpp__list);
|
||||
list_add_tail(&format->list, &list->fields);
|
||||
}
|
||||
|
||||
void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
|
||||
struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_add_tail(&format->sort_list, &list->sorts);
|
||||
}
|
||||
|
||||
void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
|
||||
|
@ -495,53 +522,43 @@ void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
|
|||
list_del(&format->list);
|
||||
}
|
||||
|
||||
void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_add_tail(&format->sort_list, &perf_hpp__sort_list);
|
||||
}
|
||||
|
||||
void perf_hpp__column_enable(unsigned col)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
perf_hpp__column_register(&perf_hpp__format[col]);
|
||||
}
|
||||
|
||||
void perf_hpp__column_disable(unsigned col)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
perf_hpp__column_unregister(&perf_hpp__format[col]);
|
||||
}
|
||||
|
||||
void perf_hpp__cancel_cumulate(void)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt, *acc, *ovh, *tmp;
|
||||
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead";
|
||||
ovh = &perf_hpp__format[PERF_HPP__OVERHEAD];
|
||||
acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC];
|
||||
|
||||
perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) {
|
||||
if (acc->equal(acc, fmt)) {
|
||||
perf_hpp__column_unregister(fmt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ovh->equal(ovh, fmt))
|
||||
fmt->name = "Overhead";
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__setup_output_field(void)
|
||||
static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
|
||||
{
|
||||
return a->equal && a->equal(a, b);
|
||||
}
|
||||
|
||||
void perf_hpp__setup_output_field(struct perf_hpp_list *list)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
/* append sort keys to output field */
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (!list_empty(&fmt->list))
|
||||
continue;
|
||||
perf_hpp_list__for_each_sort_list(list, fmt) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
/*
|
||||
* sort entry fields are dynamically created,
|
||||
* so they can share a same sort key even though
|
||||
* the list is empty.
|
||||
*/
|
||||
if (perf_hpp__is_sort_entry(fmt)) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
perf_hpp__for_each_format(pos) {
|
||||
if (perf_hpp__same_sort_entry(pos, fmt))
|
||||
goto next;
|
||||
}
|
||||
perf_hpp_list__for_each_format(list, pos) {
|
||||
if (fmt_equal(fmt, pos))
|
||||
goto next;
|
||||
}
|
||||
|
||||
perf_hpp__column_register(fmt);
|
||||
|
@ -550,27 +567,17 @@ void perf_hpp__setup_output_field(void)
|
|||
}
|
||||
}
|
||||
|
||||
void perf_hpp__append_sort_keys(void)
|
||||
void perf_hpp__append_sort_keys(struct perf_hpp_list *list)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
/* append output fields to sort keys */
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!list_empty(&fmt->sort_list))
|
||||
continue;
|
||||
perf_hpp_list__for_each_format(list, fmt) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
/*
|
||||
* sort entry fields are dynamically created,
|
||||
* so they can share a same sort key even though
|
||||
* the list is empty.
|
||||
*/
|
||||
if (perf_hpp__is_sort_entry(fmt)) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
perf_hpp__for_each_sort_list(pos) {
|
||||
if (perf_hpp__same_sort_entry(pos, fmt))
|
||||
goto next;
|
||||
}
|
||||
perf_hpp_list__for_each_sort_list(list, pos) {
|
||||
if (fmt_equal(fmt, pos))
|
||||
goto next;
|
||||
}
|
||||
|
||||
perf_hpp__register_sort_field(fmt);
|
||||
|
@ -579,20 +586,29 @@ void perf_hpp__append_sort_keys(void)
|
|||
}
|
||||
}
|
||||
|
||||
void perf_hpp__reset_output_field(void)
|
||||
|
||||
static void fmt_free(struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
if (fmt->free)
|
||||
fmt->free(fmt);
|
||||
}
|
||||
|
||||
void perf_hpp__reset_output_field(struct perf_hpp_list *list)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt, *tmp;
|
||||
|
||||
/* reset output fields */
|
||||
perf_hpp__for_each_format_safe(fmt, tmp) {
|
||||
perf_hpp_list__for_each_format_safe(list, fmt, tmp) {
|
||||
list_del_init(&fmt->list);
|
||||
list_del_init(&fmt->sort_list);
|
||||
fmt_free(fmt);
|
||||
}
|
||||
|
||||
/* reset sort keys */
|
||||
perf_hpp__for_each_sort_list_safe(fmt, tmp) {
|
||||
perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) {
|
||||
list_del_init(&fmt->list);
|
||||
list_del_init(&fmt->sort_list);
|
||||
fmt_free(fmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -606,7 +622,7 @@ unsigned int hists__sort_list_width(struct hists *hists)
|
|||
bool first = true;
|
||||
struct perf_hpp dummy_hpp;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
|
@ -626,20 +642,12 @@ unsigned int hists__sort_list_width(struct hists *hists)
|
|||
|
||||
void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt))
|
||||
return perf_hpp__reset_sort_width(fmt, hists);
|
||||
|
||||
for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
|
||||
if (fmt == &perf_hpp__format[idx])
|
||||
break;
|
||||
}
|
||||
BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX);
|
||||
|
||||
if (idx == PERF_HPP__MAX_INDEX)
|
||||
return;
|
||||
|
||||
switch (idx) {
|
||||
switch (fmt->idx) {
|
||||
case PERF_HPP__OVERHEAD:
|
||||
case PERF_HPP__OVERHEAD_SYS:
|
||||
case PERF_HPP__OVERHEAD_US:
|
||||
|
@ -667,7 +675,7 @@ void perf_hpp__set_user_width(const char *width_list_str)
|
|||
struct perf_hpp_fmt *fmt;
|
||||
const char *ptr = width_list_str;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
||||
char *p;
|
||||
|
||||
int len = strtol(ptr, &p, 10);
|
||||
|
|
|
@ -384,7 +384,7 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
|
|||
if (symbol_conf.exclude_other && !he->parent)
|
||||
return 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(he->hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, he->hists))
|
||||
continue;
|
||||
|
||||
|
@ -453,7 +453,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
|
||||
init_rem_hits();
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
hists__for_each_format(hists, fmt)
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
|
||||
if (symbol_conf.col_width_list_str)
|
||||
|
@ -464,7 +464,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
|
||||
fprintf(fp, "# ");
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
|
@ -488,7 +488,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
|
||||
fprintf(fp, "# ");
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
hists__for_each_format(hists, fmt) {
|
||||
unsigned int i;
|
||||
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
|
|
|
@ -958,10 +958,11 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
|
|||
int64_t
|
||||
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
struct hists *hists = left->hists;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp = 0;
|
||||
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
hists__for_each_sort_list(hists, fmt) {
|
||||
cmp = fmt->cmp(fmt, left, right);
|
||||
if (cmp)
|
||||
break;
|
||||
|
@ -973,10 +974,11 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
int64_t
|
||||
hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
struct hists *hists = left->hists;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp = 0;
|
||||
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
hists__for_each_sort_list(hists, fmt) {
|
||||
cmp = fmt->collapse(fmt, left, right);
|
||||
if (cmp)
|
||||
break;
|
||||
|
@ -1117,10 +1119,11 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
|
|||
|
||||
static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
|
||||
{
|
||||
struct hists *hists = a->hists;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp = 0;
|
||||
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
hists__for_each_sort_list(hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, a->hists))
|
||||
continue;
|
||||
|
||||
|
@ -1197,19 +1200,13 @@ static void __hists__insert_output_entry(struct rb_root *entries,
|
|||
rb_insert_color(&he->rb_node, entries);
|
||||
}
|
||||
|
||||
void hists__output_resort(struct hists *hists, struct ui_progress *prog)
|
||||
static void output_resort(struct hists *hists, struct ui_progress *prog,
|
||||
bool use_callchain)
|
||||
{
|
||||
struct rb_root *root;
|
||||
struct rb_node *next;
|
||||
struct hist_entry *n;
|
||||
u64 min_callchain_hits;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
bool use_callchain;
|
||||
|
||||
if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph)
|
||||
use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN;
|
||||
else
|
||||
use_callchain = symbol_conf.use_callchain;
|
||||
|
||||
min_callchain_hits = hists__total_period(hists) * (callchain_param.min_percent / 100);
|
||||
|
||||
|
@ -1239,6 +1236,23 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog)
|
|||
}
|
||||
}
|
||||
|
||||
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog)
|
||||
{
|
||||
bool use_callchain;
|
||||
|
||||
if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph)
|
||||
use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN;
|
||||
else
|
||||
use_callchain = symbol_conf.use_callchain;
|
||||
|
||||
output_resort(evsel__hists(evsel), prog, use_callchain);
|
||||
}
|
||||
|
||||
void hists__output_resort(struct hists *hists, struct ui_progress *prog)
|
||||
{
|
||||
output_resort(hists, prog, symbol_conf.use_callchain);
|
||||
}
|
||||
|
||||
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
|
||||
enum hist_filter filter)
|
||||
{
|
||||
|
@ -1567,7 +1581,7 @@ int perf_hist_config(const char *var, const char *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __hists__init(struct hists *hists)
|
||||
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list)
|
||||
{
|
||||
memset(hists, 0, sizeof(*hists));
|
||||
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
|
||||
|
@ -1576,6 +1590,7 @@ int __hists__init(struct hists *hists)
|
|||
hists->entries = RB_ROOT;
|
||||
pthread_mutex_init(&hists->lock, NULL);
|
||||
hists->socket_filter = -1;
|
||||
hists->hpp_list = hpp_list;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1612,7 +1627,7 @@ static int hists_evsel__init(struct perf_evsel *evsel)
|
|||
{
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
|
||||
__hists__init(hists);
|
||||
__hists__init(hists, &perf_hpp_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1631,3 +1646,9 @@ int hists__init(void)
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
void perf_hpp_list__init(struct perf_hpp_list *list)
|
||||
{
|
||||
INIT_LIST_HEAD(&list->fields);
|
||||
INIT_LIST_HEAD(&list->sorts);
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ struct hists {
|
|||
u64 event_stream;
|
||||
u16 col_len[HISTC_NR_COLS];
|
||||
int socket_filter;
|
||||
struct perf_hpp_list *hpp_list;
|
||||
};
|
||||
|
||||
struct hist_entry_iter;
|
||||
|
@ -128,6 +129,7 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,
|
|||
struct hists *hists);
|
||||
void hist_entry__delete(struct hist_entry *he);
|
||||
|
||||
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog);
|
||||
void hists__output_resort(struct hists *hists, struct ui_progress *prog);
|
||||
void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
|
||||
|
||||
|
@ -185,7 +187,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel)
|
|||
}
|
||||
|
||||
int hists__init(void);
|
||||
int __hists__init(struct hists *hists);
|
||||
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list);
|
||||
|
||||
struct rb_root *hists__get_rotate_entries_in(struct hists *hists);
|
||||
bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
|
||||
|
@ -214,28 +216,56 @@ struct perf_hpp_fmt {
|
|||
struct hist_entry *a, struct hist_entry *b);
|
||||
int64_t (*sort)(struct perf_hpp_fmt *fmt,
|
||||
struct hist_entry *a, struct hist_entry *b);
|
||||
bool (*equal)(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
|
||||
void (*free)(struct perf_hpp_fmt *fmt);
|
||||
|
||||
struct list_head list;
|
||||
struct list_head sort_list;
|
||||
bool elide;
|
||||
int len;
|
||||
int user_len;
|
||||
int idx;
|
||||
};
|
||||
|
||||
extern struct list_head perf_hpp__list;
|
||||
extern struct list_head perf_hpp__sort_list;
|
||||
struct perf_hpp_list {
|
||||
struct list_head fields;
|
||||
struct list_head sorts;
|
||||
};
|
||||
|
||||
#define perf_hpp__for_each_format(format) \
|
||||
list_for_each_entry(format, &perf_hpp__list, list)
|
||||
extern struct perf_hpp_list perf_hpp_list;
|
||||
|
||||
#define perf_hpp__for_each_format_safe(format, tmp) \
|
||||
list_for_each_entry_safe(format, tmp, &perf_hpp__list, list)
|
||||
void perf_hpp_list__column_register(struct perf_hpp_list *list,
|
||||
struct perf_hpp_fmt *format);
|
||||
void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
|
||||
struct perf_hpp_fmt *format);
|
||||
|
||||
#define perf_hpp__for_each_sort_list(format) \
|
||||
list_for_each_entry(format, &perf_hpp__sort_list, sort_list)
|
||||
static inline void perf_hpp__column_register(struct perf_hpp_fmt *format)
|
||||
{
|
||||
perf_hpp_list__column_register(&perf_hpp_list, format);
|
||||
}
|
||||
|
||||
#define perf_hpp__for_each_sort_list_safe(format, tmp) \
|
||||
list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list)
|
||||
static inline void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
|
||||
{
|
||||
perf_hpp_list__register_sort_field(&perf_hpp_list, format);
|
||||
}
|
||||
|
||||
#define perf_hpp_list__for_each_format(_list, format) \
|
||||
list_for_each_entry(format, &(_list)->fields, list)
|
||||
|
||||
#define perf_hpp_list__for_each_format_safe(_list, format, tmp) \
|
||||
list_for_each_entry_safe(format, tmp, &(_list)->fields, list)
|
||||
|
||||
#define perf_hpp_list__for_each_sort_list(_list, format) \
|
||||
list_for_each_entry(format, &(_list)->sorts, sort_list)
|
||||
|
||||
#define perf_hpp_list__for_each_sort_list_safe(_list, format, tmp) \
|
||||
list_for_each_entry_safe(format, tmp, &(_list)->sorts, sort_list)
|
||||
|
||||
#define hists__for_each_format(hists, format) \
|
||||
perf_hpp_list__for_each_format((hists)->hpp_list, fmt)
|
||||
|
||||
#define hists__for_each_sort_list(hists, format) \
|
||||
perf_hpp_list__for_each_sort_list((hists)->hpp_list, fmt)
|
||||
|
||||
extern struct perf_hpp_fmt perf_hpp__format[];
|
||||
|
||||
|
@ -254,19 +284,14 @@ enum {
|
|||
};
|
||||
|
||||
void perf_hpp__init(void);
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__column_unregister(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__column_enable(unsigned col);
|
||||
void perf_hpp__column_disable(unsigned col);
|
||||
void perf_hpp__cancel_cumulate(void);
|
||||
void perf_hpp__setup_output_field(struct perf_hpp_list *list);
|
||||
void perf_hpp__reset_output_field(struct perf_hpp_list *list);
|
||||
void perf_hpp__append_sort_keys(struct perf_hpp_list *list);
|
||||
|
||||
void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__setup_output_field(void);
|
||||
void perf_hpp__reset_output_field(void);
|
||||
void perf_hpp__append_sort_keys(void);
|
||||
|
||||
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
|
||||
bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
|
||||
bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *format);
|
||||
bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists);
|
||||
|
||||
|
@ -381,4 +406,6 @@ int parse_filter_percentage(const struct option *opt __maybe_unused,
|
|||
const char *arg, int unset __maybe_unused);
|
||||
int perf_hist_config(const char *var, const char *value);
|
||||
|
||||
void perf_hpp_list__init(struct perf_hpp_list *list);
|
||||
|
||||
#endif /* __PERF_HIST_H */
|
||||
|
|
|
@ -1441,20 +1441,6 @@ struct hpp_sort_entry {
|
|||
struct sort_entry *se;
|
||||
};
|
||||
|
||||
bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
|
||||
{
|
||||
struct hpp_sort_entry *hse_a;
|
||||
struct hpp_sort_entry *hse_b;
|
||||
|
||||
if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
|
||||
return false;
|
||||
|
||||
hse_a = container_of(a, struct hpp_sort_entry, hpp);
|
||||
hse_b = container_of(b, struct hpp_sort_entry, hpp);
|
||||
|
||||
return hse_a->se == hse_b->se;
|
||||
}
|
||||
|
||||
void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
|
@ -1540,6 +1526,33 @@ static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
|
|||
return sort_fn(a, b);
|
||||
}
|
||||
|
||||
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
|
||||
{
|
||||
return format->header == __sort__hpp_header;
|
||||
}
|
||||
|
||||
static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
|
||||
{
|
||||
struct hpp_sort_entry *hse_a;
|
||||
struct hpp_sort_entry *hse_b;
|
||||
|
||||
if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
|
||||
return false;
|
||||
|
||||
hse_a = container_of(a, struct hpp_sort_entry, hpp);
|
||||
hse_b = container_of(b, struct hpp_sort_entry, hpp);
|
||||
|
||||
return hse_a->se == hse_b->se;
|
||||
}
|
||||
|
||||
static void hse_free(struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
free(hse);
|
||||
}
|
||||
|
||||
static struct hpp_sort_entry *
|
||||
__sort_dimension__alloc_hpp(struct sort_dimension *sd)
|
||||
{
|
||||
|
@ -1561,6 +1574,8 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
|
|||
hse->hpp.cmp = __sort__hpp_cmp;
|
||||
hse->hpp.collapse = __sort__hpp_collapse;
|
||||
hse->hpp.sort = __sort__hpp_sort;
|
||||
hse->hpp.equal = __sort__hpp_equal;
|
||||
hse->hpp.free = hse_free;
|
||||
|
||||
INIT_LIST_HEAD(&hse->hpp.list);
|
||||
INIT_LIST_HEAD(&hse->hpp.sort_list);
|
||||
|
@ -1571,9 +1586,23 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
|
|||
return hse;
|
||||
}
|
||||
|
||||
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
|
||||
static void hpp_free(struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
return format->header == __sort__hpp_header;
|
||||
free(fmt);
|
||||
}
|
||||
|
||||
static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
fmt = memdup(hd->fmt, sizeof(*fmt));
|
||||
if (fmt) {
|
||||
INIT_LIST_HEAD(&fmt->list);
|
||||
INIT_LIST_HEAD(&fmt->sort_list);
|
||||
fmt->free = hpp_free;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
|
||||
|
@ -1587,14 +1616,15 @@ static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
|
||||
static int __sort_dimension__add_hpp_output(struct perf_hpp_list *list,
|
||||
struct sort_dimension *sd)
|
||||
{
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
|
||||
|
||||
if (hse == NULL)
|
||||
return -1;
|
||||
|
||||
perf_hpp__column_register(&hse->hpp);
|
||||
perf_hpp_list__column_register(list, &hse->hpp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1804,6 +1834,14 @@ bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
|
|||
return fmt->cmp == __sort__hde_cmp;
|
||||
}
|
||||
|
||||
static void hde_free(struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
struct hpp_dynamic_entry *hde;
|
||||
|
||||
hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
||||
free(hde);
|
||||
}
|
||||
|
||||
static struct hpp_dynamic_entry *
|
||||
__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field)
|
||||
{
|
||||
|
@ -1828,6 +1866,7 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field)
|
|||
hde->hpp.cmp = __sort__hde_cmp;
|
||||
hde->hpp.collapse = __sort__hde_cmp;
|
||||
hde->hpp.sort = __sort__hde_cmp;
|
||||
hde->hpp.free = hde_free;
|
||||
|
||||
INIT_LIST_HEAD(&hde->hpp.list);
|
||||
INIT_LIST_HEAD(&hde->hpp.sort_list);
|
||||
|
@ -2065,40 +2104,54 @@ static int __sort_dimension__add(struct sort_dimension *sd)
|
|||
|
||||
static int __hpp_dimension__add(struct hpp_dimension *hd)
|
||||
{
|
||||
if (!hd->taken) {
|
||||
hd->taken = 1;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
perf_hpp__register_sort_field(hd->fmt);
|
||||
}
|
||||
if (hd->taken)
|
||||
return 0;
|
||||
|
||||
fmt = __hpp_dimension__alloc_hpp(hd);
|
||||
if (!fmt)
|
||||
return -1;
|
||||
|
||||
hd->taken = 1;
|
||||
perf_hpp__register_sort_field(fmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_output(struct sort_dimension *sd)
|
||||
static int __sort_dimension__add_output(struct perf_hpp_list *list,
|
||||
struct sort_dimension *sd)
|
||||
{
|
||||
if (sd->taken)
|
||||
return 0;
|
||||
|
||||
if (__sort_dimension__add_hpp_output(sd) < 0)
|
||||
if (__sort_dimension__add_hpp_output(list, sd) < 0)
|
||||
return -1;
|
||||
|
||||
sd->taken = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp_dimension__add_output(struct hpp_dimension *hd)
|
||||
static int __hpp_dimension__add_output(struct perf_hpp_list *list,
|
||||
struct hpp_dimension *hd)
|
||||
{
|
||||
if (!hd->taken) {
|
||||
hd->taken = 1;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
perf_hpp__column_register(hd->fmt);
|
||||
}
|
||||
if (hd->taken)
|
||||
return 0;
|
||||
|
||||
fmt = __hpp_dimension__alloc_hpp(hd);
|
||||
if (!fmt)
|
||||
return -1;
|
||||
|
||||
hd->taken = 1;
|
||||
perf_hpp_list__column_register(list, fmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hpp_dimension__add_output(unsigned col)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
return __hpp_dimension__add_output(&hpp_sort_dimensions[col]);
|
||||
return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]);
|
||||
}
|
||||
|
||||
static int sort_dimension__add(const char *tok,
|
||||
|
@ -2191,6 +2244,26 @@ static int sort_dimension__add(const char *tok,
|
|||
return -ESRCH;
|
||||
}
|
||||
|
||||
static int setup_sort_list(char *str, struct perf_evlist *evlist)
|
||||
{
|
||||
char *tmp, *tok;
|
||||
int ret = 0;
|
||||
|
||||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
ret = sort_dimension__add(tok, evlist);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --sort key: `%s'", tok);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --sort key: `%s'", tok);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *get_default_sort_order(struct perf_evlist *evlist)
|
||||
{
|
||||
const char *default_sort_orders[] = {
|
||||
|
@ -2285,7 +2358,7 @@ static char *setup_overhead(char *keys)
|
|||
|
||||
static int __setup_sorting(struct perf_evlist *evlist)
|
||||
{
|
||||
char *tmp, *tok, *str;
|
||||
char *str;
|
||||
const char *sort_keys;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -2323,17 +2396,7 @@ static int __setup_sorting(struct perf_evlist *evlist)
|
|||
}
|
||||
}
|
||||
|
||||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
ret = sort_dimension__add(tok, evlist);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --sort key: `%s'", tok);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --sort key: `%s'", tok);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = setup_sort_list(str, evlist);
|
||||
|
||||
free(str);
|
||||
return ret;
|
||||
|
@ -2344,7 +2407,7 @@ void perf_hpp__set_elide(int idx, bool elide)
|
|||
struct perf_hpp_fmt *fmt;
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
|
@ -2404,7 +2467,7 @@ void sort__setup_elide(FILE *output)
|
|||
struct perf_hpp_fmt *fmt;
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
|
@ -2416,7 +2479,7 @@ void sort__setup_elide(FILE *output)
|
|||
* It makes no sense to elide all of sort entries.
|
||||
* Just revert them to show up again.
|
||||
*/
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
|
@ -2424,7 +2487,7 @@ void sort__setup_elide(FILE *output)
|
|||
return;
|
||||
}
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
|
@ -2432,7 +2495,7 @@ void sort__setup_elide(FILE *output)
|
|||
}
|
||||
}
|
||||
|
||||
static int output_field_add(char *tok)
|
||||
static int output_field_add(struct perf_hpp_list *list, char *tok)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
|
@ -2442,7 +2505,7 @@ static int output_field_add(char *tok)
|
|||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __sort_dimension__add_output(sd);
|
||||
return __sort_dimension__add_output(list, sd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
||||
|
@ -2451,7 +2514,7 @@ static int output_field_add(char *tok)
|
|||
if (strncasecmp(tok, hd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __hpp_dimension__add_output(hd);
|
||||
return __hpp_dimension__add_output(list, hd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
|
||||
|
@ -2460,7 +2523,7 @@ static int output_field_add(char *tok)
|
|||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __sort_dimension__add_output(sd);
|
||||
return __sort_dimension__add_output(list, sd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
|
||||
|
@ -2469,12 +2532,32 @@ static int output_field_add(char *tok)
|
|||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __sort_dimension__add_output(sd);
|
||||
return __sort_dimension__add_output(list, sd);
|
||||
}
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
static int setup_output_list(struct perf_hpp_list *list, char *str)
|
||||
{
|
||||
char *tmp, *tok;
|
||||
int ret = 0;
|
||||
|
||||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
ret = output_field_add(list, tok);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --fields key: `%s'", tok);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --fields key: `%s'", tok);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void reset_dimensions(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -2499,7 +2582,7 @@ bool is_strict_order(const char *order)
|
|||
|
||||
static int __setup_output_field(void)
|
||||
{
|
||||
char *tmp, *tok, *str, *strp;
|
||||
char *str, *strp;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (field_order == NULL)
|
||||
|
@ -2519,17 +2602,7 @@ static int __setup_output_field(void)
|
|||
goto out;
|
||||
}
|
||||
|
||||
for (tok = strtok_r(strp, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
ret = output_field_add(tok);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --fields key: `%s'", tok);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --fields key: `%s'", tok);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = setup_output_list(&perf_hpp_list, strp);
|
||||
|
||||
out:
|
||||
free(str);
|
||||
|
@ -2563,9 +2636,9 @@ int setup_sorting(struct perf_evlist *evlist)
|
|||
return err;
|
||||
|
||||
/* copy sort keys to output fields */
|
||||
perf_hpp__setup_output_field();
|
||||
perf_hpp__setup_output_field(&perf_hpp_list);
|
||||
/* and then copy output fields to sort keys */
|
||||
perf_hpp__append_sort_keys();
|
||||
perf_hpp__append_sort_keys(&perf_hpp_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2581,5 +2654,5 @@ void reset_output_field(void)
|
|||
sort_order = NULL;
|
||||
|
||||
reset_dimensions();
|
||||
perf_hpp__reset_output_field();
|
||||
perf_hpp__reset_output_field(&perf_hpp_list);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue