Commit d3aaf09f authored by Ingo Molnar's avatar Ingo Molnar

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

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents b83ea91f b62e8dfc
......@@ -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,64 +505,61 @@ void perf_hpp__init(void)
hpp_dimension__add_output(PERF_HPP__PERIOD);
}
void perf_hpp__column_register(struct perf_hpp_fmt *format)
{
list_add_tail(&format->list, &perf_hpp__list);
}
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)
void perf_hpp_list__column_register(struct perf_hpp_list *list,
struct perf_hpp_fmt *format)
{
list_add_tail(&format->sort_list, &perf_hpp__sort_list);
list_add_tail(&format->list, &list->fields);
}
void perf_hpp__column_enable(unsigned col)
void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
struct perf_hpp_fmt *format)
{
BUG_ON(col >= PERF_HPP__MAX_INDEX);
perf_hpp__column_register(&perf_hpp__format[col]);
list_add_tail(&format->sort_list, &list->sorts);
}
void perf_hpp__column_disable(unsigned col)
void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
{
BUG_ON(col >= PERF_HPP__MAX_INDEX);
perf_hpp__column_unregister(&perf_hpp__format[col]);
list_del(&format->list);
}
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";
}
}
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(void)
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;
/*
* 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)) {
perf_hpp_list__for_each_sort_list(list, fmt) {
struct perf_hpp_fmt *pos;
perf_hpp__for_each_format(pos) {
if (perf_hpp__same_sort_entry(pos, fmt))
perf_hpp_list__for_each_format(list, pos) {
if (fmt_equal(fmt, pos))
goto next;
}
}
perf_hpp__column_register(fmt);
next:
......@@ -550,28 +567,18 @@ 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;
/*
* 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)) {
perf_hpp_list__for_each_format(list, fmt) {
struct perf_hpp_fmt *pos;
perf_hpp__for_each_sort_list(pos) {
if (perf_hpp__same_sort_entry(pos, fmt))
perf_hpp_list__for_each_sort_list(list, pos) {
if (fmt_equal(fmt, pos))
goto next;
}
}
perf_hpp__register_sort_field(fmt);
next:
......@@ -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;
}
if (idx == PERF_HPP__MAX_INDEX)
return;
BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX);
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;
};
extern struct perf_hpp_list perf_hpp_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);
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_format(format) \
list_for_each_entry(format, &perf_hpp__list, 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__for_each_format_safe(format, tmp) \
list_for_each_entry_safe(format, tmp, &perf_hpp__list, 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__for_each_sort_list(format) \
list_for_each_entry(format, &perf_hpp__sort_list, sort_list)
#define perf_hpp_list__for_each_sort_list(_list, format) \
list_for_each_entry(format, &(_list)->sorts, sort_list)
#define perf_hpp__for_each_sort_list_safe(format, tmp) \
list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment