perf/core improvements and fixes:
. Android related fixes for pager and map dso resolving (Michael Lentine) . Add -F option for specifying output fields (Namhyung Kim) Signed-off-by: Jiri Olsa <jolsa@kernel.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTfKSDAAoJEPZqUSBWB3s92ZkQAMCNPdvrwvxjjxQG7pMOapD6 lGmIMKGxKDxK6/MsgfqwVZXNsEzI5dcZdhHriJegK94XHCEssGSD8ZUsyOo6H79S 7tACPU0BVQFMmx4l+wNrJU5eFrjWo5/RUUBEpnf/xhViXqU0wZvBR3DRs/reJz0l AFOiN0w4kS+L1DQI7Ol2aYM6CgJUJaX4gqCD3M7NG6QGINRWLJAUNoTGKVVAtrdb JsJwgRuQoLZqfHXbM1xJVbVcfZaPteriZushAwLZh+idSupABCqZvoIkdoe3lLAF uv5gkofnJb5USaAwUDrON2fRuNWWC7f5lWd4yAVGXx4HMKisfOveTtCZJcM8nbic d9ERR+fNFG+wYDu1dm0uqvFmkbDaBqdBIIV7GLMiQrCx1aXpobhsIAj4g1nbO9BX g/IkneAuVxcDrhuGW7nDo64albU9b1XcM+cIUEdEtu9lY8Ve3odLm0UqLy7W+iQK JX35Yae3Y5oOSt6oHp4xo3Le4k7XlBNSislzW5syCegDiGDLWgpet1n5ipRusrx4 XM0VBUSorvfHrG40DUOfFj5lSmRLFNdhbhcKMabRkj9+eM6ciAauWhPpDzwWHdMx wGvBb2xKHDArClweSI9NCGjuwH6drThosHBYIOhA32xuAVRlhlLPS9fif2ap87Qi A2G1Ht6FEArA2ymOy8kG =F/0a -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf into perf/core Pull perf/core improvements and fixes from Jiri Olsa: * Android related fixes for pager and map dso resolving (Michael Lentine) * Add -F option for specifying output fields (Namhyung Kim) Signed-off-by: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
e450f90e8c
25 changed files with 1604 additions and 389 deletions
|
@ -50,7 +50,8 @@ OPTIONS
|
|||
|
||||
-s::
|
||||
--sort=::
|
||||
Sort by key(s): pid, comm, dso, symbol.
|
||||
Sort by key(s): pid, comm, dso, symbol, cpu, parent, srcline.
|
||||
Please see description of --sort in the perf-report man page.
|
||||
|
||||
-t::
|
||||
--field-separator=::
|
||||
|
@ -202,4 +203,4 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
|
|||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
|
|
|
@ -79,6 +79,15 @@ OPTIONS
|
|||
abort cost. This is the global weight.
|
||||
- local_weight: Local weight version of the weight above.
|
||||
- transaction: Transaction abort flags.
|
||||
- overhead: Overhead percentage of sample
|
||||
- overhead_sys: Overhead percentage of sample running in system mode
|
||||
- overhead_us: Overhead percentage of sample running in user mode
|
||||
- overhead_guest_sys: Overhead percentage of sample running in system mode
|
||||
on guest machine
|
||||
- overhead_guest_us: Overhead percentage of sample running in user mode on
|
||||
guest machine
|
||||
- sample: Number of sample
|
||||
- period: Raw number of event count of sample
|
||||
|
||||
By default, comm, dso and symbol keys are used.
|
||||
(i.e. --sort comm,dso,symbol)
|
||||
|
@ -98,6 +107,16 @@ OPTIONS
|
|||
And default sort keys are changed to comm, dso_from, symbol_from, dso_to
|
||||
and symbol_to, see '--branch-stack'.
|
||||
|
||||
-F::
|
||||
--fields=::
|
||||
Specify output field - multiple keys can be specified in CSV format.
|
||||
Following fields are available:
|
||||
overhead, overhead_sys, overhead_us, sample and period.
|
||||
Also it can contain any sort key(s).
|
||||
|
||||
By default, every sort keys not specified in -F will be appended
|
||||
automatically.
|
||||
|
||||
-p::
|
||||
--parent=<regex>::
|
||||
A regex filter to identify parent. The parent is a caller of this
|
||||
|
|
|
@ -113,7 +113,17 @@ Default is to monitor all CPUS.
|
|||
-s::
|
||||
--sort::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight,
|
||||
local_weight, abort, in_tx, transaction
|
||||
local_weight, abort, in_tx, transaction, overhead, sample, period.
|
||||
Please see description of --sort in the perf-report man page.
|
||||
|
||||
--fields=::
|
||||
Specify output field - multiple keys can be specified in CSV format.
|
||||
Following fields are available:
|
||||
overhead, overhead_sys, overhead_us, sample and period.
|
||||
Also it can contain any sort key(s).
|
||||
|
||||
By default, every sort keys not specified in --field will be appended
|
||||
automatically.
|
||||
|
||||
-n::
|
||||
--show-nr-samples::
|
||||
|
@ -212,4 +222,4 @@ Pressing any unmapped key displays a menu, and prompts for input.
|
|||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1]
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1]
|
||||
|
|
|
@ -399,6 +399,7 @@ LIB_OBJS += $(OUTPUT)tests/pmu.o
|
|||
LIB_OBJS += $(OUTPUT)tests/hists_common.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_link.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_filter.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_output.o
|
||||
LIB_OBJS += $(OUTPUT)tests/python-use.o
|
||||
LIB_OBJS += $(OUTPUT)tests/bp_signal.o
|
||||
LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
|
||||
|
|
|
@ -60,7 +60,6 @@ static int data__files_cnt;
|
|||
#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
|
||||
#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
|
||||
|
||||
static char diff__default_sort_order[] = "dso,symbol";
|
||||
static bool force;
|
||||
static bool show_period;
|
||||
static bool show_formula;
|
||||
|
@ -741,7 +740,8 @@ static const struct option options[] = {
|
|||
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent"),
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
|
||||
" Please refer the man page for the complete list."),
|
||||
OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
|
||||
"separator for columns, no spaces will be added between "
|
||||
"columns '.' is reserved."),
|
||||
|
@ -1141,7 +1141,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
{
|
||||
perf_config(perf_default_config, NULL);
|
||||
|
||||
sort_order = diff__default_sort_order;
|
||||
argc = parse_options(argc, argv, options, diff_usage, 0);
|
||||
|
||||
if (symbol__init() < 0)
|
||||
|
@ -1152,6 +1151,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
ui_init();
|
||||
|
||||
sort__mode = SORT_MODE__DIFF;
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(diff_usage, options);
|
||||
|
||||
|
|
|
@ -699,10 +699,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_BOOLEAN(0, "header-only", &report.header_only,
|
||||
"Show only data header."),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
|
||||
" dso_to, dso_from, symbol_to, symbol_from, mispredict,"
|
||||
" weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, "
|
||||
"snoop, locked, abort, in_tx, transaction"),
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
|
||||
" Please refer the man page for the complete list."),
|
||||
OPT_STRING('F', "fields", &field_order, "key[,keys...]",
|
||||
"output field(s): overhead, period, sample plus all of sort keys"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
"Show sample percentage for different cpu modes"),
|
||||
OPT_STRING('p', "parent", &parent_pattern, "regex",
|
||||
|
@ -807,52 +807,31 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (branch_mode == -1 && has_br_stack)
|
||||
sort__mode = SORT_MODE__BRANCH;
|
||||
|
||||
/* sort__mode could be NORMAL if --no-branch-stack */
|
||||
if (sort__mode == SORT_MODE__BRANCH) {
|
||||
/*
|
||||
* if no sort_order is provided, then specify
|
||||
* branch-mode specific order
|
||||
*/
|
||||
if (sort_order == default_sort_order)
|
||||
sort_order = "comm,dso_from,symbol_from,"
|
||||
"dso_to,symbol_to";
|
||||
|
||||
}
|
||||
if (report.mem_mode) {
|
||||
if (sort__mode == SORT_MODE__BRANCH) {
|
||||
pr_err("branch and mem mode incompatible\n");
|
||||
goto error;
|
||||
}
|
||||
sort__mode = SORT_MODE__MEMORY;
|
||||
|
||||
/*
|
||||
* if no sort_order is provided, then specify
|
||||
* branch-mode specific order
|
||||
*/
|
||||
if (sort_order == default_sort_order)
|
||||
sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
|
||||
}
|
||||
|
||||
if (setup_sorting() < 0) {
|
||||
parse_options_usage(report_usage, options, "s", 1);
|
||||
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 (parent_pattern != default_parent_pattern) {
|
||||
if (sort_dimension__add("parent") < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Force tty output for header output. */
|
||||
if (report.header || report.header_only)
|
||||
use_browser = 0;
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
else
|
||||
use_browser = 0;
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
if (report.header || report.header_only) {
|
||||
perf_session__fprintf_info(session, stdout,
|
||||
|
|
|
@ -1083,8 +1083,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight,"
|
||||
" abort, in_tx, transaction"),
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
|
||||
" Please refer the man page for the complete list."),
|
||||
OPT_STRING(0, "fields", &field_order, "key[,keys...]",
|
||||
"output field(s): overhead, period, sample plus all of sort keys"),
|
||||
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
|
||||
"Show a column with the number of samples"),
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
|
||||
|
@ -1137,17 +1139,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (argc)
|
||||
usage_with_options(top_usage, options);
|
||||
|
||||
if (sort_order == default_sort_order)
|
||||
sort_order = "dso,symbol";
|
||||
|
||||
if (setup_sorting() < 0) {
|
||||
parse_options_usage(top_usage, options, "s", 1);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
sort__mode = SORT_MODE__TOP;
|
||||
/* display thread wants entries to be collapsed in a different tree */
|
||||
sort__need_collapse = 1;
|
||||
|
||||
if (setup_sorting() < 0) {
|
||||
if (sort_order)
|
||||
parse_options_usage(top_usage, options, "s", 1);
|
||||
if (field_order)
|
||||
parse_options_usage(sort_order ? NULL : top_usage,
|
||||
options, "fields", 0);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
if (top.use_stdio)
|
||||
use_browser = 0;
|
||||
else if (top.use_tui)
|
||||
|
|
|
@ -135,6 +135,10 @@ static struct test {
|
|||
.desc = "Test thread mg sharing",
|
||||
.func = test__thread_mg_share,
|
||||
},
|
||||
{
|
||||
.desc = "Test output sorting of hist entries",
|
||||
.func = test__hists_output,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
|
@ -146,3 +146,60 @@ struct machine *setup_fake_machine(struct machines *machines)
|
|||
machine__delete(machine);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void print_hists_in(struct hists *hists)
|
||||
{
|
||||
int i = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
pr_info("----- %s --------\n", __func__);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node_in);
|
||||
|
||||
if (!he->filtered) {
|
||||
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
|
||||
i, thread__comm_str(he->thread),
|
||||
he->ms.map->dso->short_name,
|
||||
he->ms.sym->name, he->stat.period);
|
||||
}
|
||||
|
||||
i++;
|
||||
node = rb_next(node);
|
||||
}
|
||||
}
|
||||
|
||||
void print_hists_out(struct hists *hists)
|
||||
{
|
||||
int i = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
root = &hists->entries;
|
||||
|
||||
pr_info("----- %s --------\n", __func__);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
|
||||
if (!he->filtered) {
|
||||
pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"\n",
|
||||
i, thread__comm_str(he->thread), he->thread->tid,
|
||||
he->ms.map->dso->short_name,
|
||||
he->ms.sym->name, he->stat.period);
|
||||
}
|
||||
|
||||
i++;
|
||||
node = rb_next(node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,4 +41,7 @@ struct machines;
|
|||
*/
|
||||
struct machine *setup_fake_machine(struct machines *machines);
|
||||
|
||||
void print_hists_in(struct hists *hists);
|
||||
void print_hists_out(struct hists *hists);
|
||||
|
||||
#endif /* __PERF_TESTS__HISTS_COMMON_H__ */
|
||||
|
|
|
@ -98,33 +98,6 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
|||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
static void print_hists(struct hists *hists)
|
||||
{
|
||||
int i = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
root = &hists->entries;
|
||||
|
||||
pr_info("----- %s --------\n", __func__);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
|
||||
if (!he->filtered) {
|
||||
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
|
||||
i, thread__comm_str(he->thread),
|
||||
he->ms.map->dso->short_name,
|
||||
he->ms.sym->name, he->stat.period);
|
||||
}
|
||||
|
||||
i++;
|
||||
node = rb_next(node);
|
||||
}
|
||||
}
|
||||
|
||||
int test__hists_filter(void)
|
||||
{
|
||||
int err = TEST_FAIL;
|
||||
|
@ -169,7 +142,7 @@ int test__hists_filter(void)
|
|||
|
||||
if (verbose > 2) {
|
||||
pr_info("Normal histogram\n");
|
||||
print_hists(hists);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
TEST_ASSERT_VAL("Invalid nr samples",
|
||||
|
@ -193,7 +166,7 @@ int test__hists_filter(void)
|
|||
|
||||
if (verbose > 2) {
|
||||
pr_info("Histogram for thread filter\n");
|
||||
print_hists(hists);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
/* normal stats should be invariant */
|
||||
|
@ -222,7 +195,7 @@ int test__hists_filter(void)
|
|||
|
||||
if (verbose > 2) {
|
||||
pr_info("Histogram for dso filter\n");
|
||||
print_hists(hists);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
/* normal stats should be invariant */
|
||||
|
@ -257,7 +230,7 @@ int test__hists_filter(void)
|
|||
|
||||
if (verbose > 2) {
|
||||
pr_info("Histogram for symbol filter\n");
|
||||
print_hists(hists);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
/* normal stats should be invariant */
|
||||
|
@ -284,7 +257,7 @@ int test__hists_filter(void)
|
|||
|
||||
if (verbose > 2) {
|
||||
pr_info("Histogram for all filters\n");
|
||||
print_hists(hists);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
/* normal stats should be invariant */
|
||||
|
@ -310,6 +283,7 @@ int test__hists_filter(void)
|
|||
out:
|
||||
/* tear down everything */
|
||||
perf_evlist__delete(evlist);
|
||||
reset_output_field();
|
||||
machines__exit(&machines);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -268,33 +268,6 @@ static int validate_link(struct hists *leader, struct hists *other)
|
|||
return __validate_link(leader, 0) || __validate_link(other, 1);
|
||||
}
|
||||
|
||||
static void print_hists(struct hists *hists)
|
||||
{
|
||||
int i = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
pr_info("----- %s --------\n", __func__);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node_in);
|
||||
|
||||
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
|
||||
i, thread__comm_str(he->thread), he->ms.map->dso->short_name,
|
||||
he->ms.sym->name, he->stat.period);
|
||||
|
||||
i++;
|
||||
node = rb_next(node);
|
||||
}
|
||||
}
|
||||
|
||||
int test__hists_link(void)
|
||||
{
|
||||
int err = -1;
|
||||
|
@ -336,7 +309,7 @@ int test__hists_link(void)
|
|||
hists__collapse_resort(&evsel->hists, NULL);
|
||||
|
||||
if (verbose > 2)
|
||||
print_hists(&evsel->hists);
|
||||
print_hists_in(&evsel->hists);
|
||||
}
|
||||
|
||||
first = perf_evlist__first(evlist);
|
||||
|
@ -359,6 +332,7 @@ int test__hists_link(void)
|
|||
out:
|
||||
/* tear down everything */
|
||||
perf_evlist__delete(evlist);
|
||||
reset_output_field();
|
||||
machines__exit(&machines);
|
||||
|
||||
return err;
|
||||
|
|
618
tools/perf/tests/hists_output.c
Normal file
618
tools/perf/tests/hists_output.c
Normal file
|
@ -0,0 +1,618 @@
|
|||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "tests/tests.h"
|
||||
#include "tests/hists_common.h"
|
||||
|
||||
struct sample {
|
||||
u32 cpu;
|
||||
u32 pid;
|
||||
u64 ip;
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
struct symbol *sym;
|
||||
};
|
||||
|
||||
/* For the numbers, see hists_common.c */
|
||||
static struct sample fake_samples[] = {
|
||||
/* perf [kernel] schedule() */
|
||||
{ .cpu = 0, .pid = 100, .ip = 0xf0000 + 700, },
|
||||
/* perf [perf] main() */
|
||||
{ .cpu = 1, .pid = 100, .ip = 0x40000 + 700, },
|
||||
/* perf [perf] cmd_record() */
|
||||
{ .cpu = 1, .pid = 100, .ip = 0x40000 + 900, },
|
||||
/* perf [libc] malloc() */
|
||||
{ .cpu = 1, .pid = 100, .ip = 0x50000 + 700, },
|
||||
/* perf [libc] free() */
|
||||
{ .cpu = 2, .pid = 100, .ip = 0x50000 + 800, },
|
||||
/* perf [perf] main() */
|
||||
{ .cpu = 2, .pid = 200, .ip = 0x40000 + 700, },
|
||||
/* perf [kernel] page_fault() */
|
||||
{ .cpu = 2, .pid = 200, .ip = 0xf0000 + 800, },
|
||||
/* bash [bash] main() */
|
||||
{ .cpu = 3, .pid = 300, .ip = 0x40000 + 700, },
|
||||
/* bash [bash] xmalloc() */
|
||||
{ .cpu = 0, .pid = 300, .ip = 0x40000 + 800, },
|
||||
/* bash [kernel] page_fault() */
|
||||
{ .cpu = 1, .pid = 300, .ip = 0xf0000 + 800, },
|
||||
};
|
||||
|
||||
static int add_hist_entries(struct hists *hists, struct machine *machine)
|
||||
{
|
||||
struct addr_location al;
|
||||
struct hist_entry *he;
|
||||
struct perf_sample sample = { .period = 100, };
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
|
||||
const union perf_event event = {
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
};
|
||||
|
||||
sample.cpu = fake_samples[i].cpu;
|
||||
sample.pid = fake_samples[i].pid;
|
||||
sample.tid = fake_samples[i].pid;
|
||||
sample.ip = fake_samples[i].ip;
|
||||
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(hists, &al, NULL, NULL, NULL,
|
||||
sample.period, 1, 0);
|
||||
if (he == NULL)
|
||||
goto out;
|
||||
|
||||
fake_samples[i].thread = al.thread;
|
||||
fake_samples[i].map = al.map;
|
||||
fake_samples[i].sym = al.sym;
|
||||
}
|
||||
|
||||
return TEST_OK;
|
||||
|
||||
out:
|
||||
pr_debug("Not enough memory for adding a hist entry\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
static void del_hist_entries(struct hists *hists)
|
||||
{
|
||||
struct hist_entry *he;
|
||||
struct rb_root *root_in;
|
||||
struct rb_root *root_out;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root_in = &hists->entries_collapsed;
|
||||
else
|
||||
root_in = hists->entries_in;
|
||||
|
||||
root_out = &hists->entries;
|
||||
|
||||
while (!RB_EMPTY_ROOT(root_out)) {
|
||||
node = rb_first(root_out);
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
rb_erase(node, root_out);
|
||||
rb_erase(&he->rb_node_in, root_in);
|
||||
hist_entry__free(he);
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
|
||||
|
||||
#define COMM(he) (thread__comm_str(he->thread))
|
||||
#define DSO(he) (he->ms.map->dso->short_name)
|
||||
#define SYM(he) (he->ms.sym->name)
|
||||
#define CPU(he) (he->cpu)
|
||||
#define PID(he) (he->thread->tid)
|
||||
|
||||
/* default sort keys (no field) */
|
||||
static int test1(struct perf_evsel *evsel, struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_entry *he;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
field_order = NULL;
|
||||
sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */
|
||||
|
||||
setup_sorting();
|
||||
|
||||
/*
|
||||
* expected output:
|
||||
*
|
||||
* Overhead Command Shared Object Symbol
|
||||
* ======== ======= ============= ==============
|
||||
* 20.00% perf perf [.] main
|
||||
* 10.00% bash [kernel] [k] page_fault
|
||||
* 10.00% bash bash [.] main
|
||||
* 10.00% bash bash [.] xmalloc
|
||||
* 10.00% perf [kernel] [k] page_fault
|
||||
* 10.00% perf [kernel] [k] schedule
|
||||
* 10.00% perf libc [.] free
|
||||
* 10.00% perf libc [.] malloc
|
||||
* 10.00% perf perf [.] cmd_record
|
||||
*/
|
||||
err = add_hist_entries(hists, machine);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
root = &evsel->hists.entries;
|
||||
node = rb_first(root);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
|
||||
!strcmp(SYM(he), "main") && he->stat.period == 200);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
|
||||
!strcmp(SYM(he), "page_fault") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
|
||||
!strcmp(SYM(he), "main") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
|
||||
!strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
|
||||
!strcmp(SYM(he), "page_fault") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
|
||||
!strcmp(SYM(he), "schedule") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
|
||||
!strcmp(SYM(he), "free") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
|
||||
!strcmp(SYM(he), "malloc") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
|
||||
!strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
|
||||
|
||||
out:
|
||||
del_hist_entries(hists);
|
||||
reset_output_field();
|
||||
return err;
|
||||
}
|
||||
|
||||
/* mixed fields and sort keys */
|
||||
static int test2(struct perf_evsel *evsel, struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_entry *he;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
field_order = "overhead,cpu";
|
||||
sort_order = "pid";
|
||||
|
||||
setup_sorting();
|
||||
|
||||
/*
|
||||
* expected output:
|
||||
*
|
||||
* Overhead CPU Command: Pid
|
||||
* ======== === =============
|
||||
* 30.00% 1 perf : 100
|
||||
* 10.00% 0 perf : 100
|
||||
* 10.00% 2 perf : 100
|
||||
* 20.00% 2 perf : 200
|
||||
* 10.00% 0 bash : 300
|
||||
* 10.00% 1 bash : 300
|
||||
* 10.00% 3 bash : 300
|
||||
*/
|
||||
err = add_hist_entries(hists, machine);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
root = &evsel->hists.entries;
|
||||
node = rb_first(root);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100);
|
||||
|
||||
out:
|
||||
del_hist_entries(hists);
|
||||
reset_output_field();
|
||||
return err;
|
||||
}
|
||||
|
||||
/* fields only (no sort key) */
|
||||
static int test3(struct perf_evsel *evsel, struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_entry *he;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
field_order = "comm,overhead,dso";
|
||||
sort_order = NULL;
|
||||
|
||||
setup_sorting();
|
||||
|
||||
/*
|
||||
* expected output:
|
||||
*
|
||||
* Command Overhead Shared Object
|
||||
* ======= ======== =============
|
||||
* bash 20.00% bash
|
||||
* bash 10.00% [kernel]
|
||||
* perf 30.00% perf
|
||||
* perf 20.00% [kernel]
|
||||
* perf 20.00% libc
|
||||
*/
|
||||
err = add_hist_entries(hists, machine);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
root = &evsel->hists.entries;
|
||||
node = rb_first(root);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
|
||||
he->stat.period == 200);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
|
||||
he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
|
||||
he->stat.period == 300);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
|
||||
he->stat.period == 200);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
|
||||
he->stat.period == 200);
|
||||
|
||||
out:
|
||||
del_hist_entries(hists);
|
||||
reset_output_field();
|
||||
return err;
|
||||
}
|
||||
|
||||
/* handle duplicate 'dso' field */
|
||||
static int test4(struct perf_evsel *evsel, struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_entry *he;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
field_order = "dso,sym,comm,overhead,dso";
|
||||
sort_order = "sym";
|
||||
|
||||
setup_sorting();
|
||||
|
||||
/*
|
||||
* expected output:
|
||||
*
|
||||
* Shared Object Symbol Command Overhead
|
||||
* ============= ============== ======= ========
|
||||
* perf [.] cmd_record perf 10.00%
|
||||
* libc [.] free perf 10.00%
|
||||
* bash [.] main bash 10.00%
|
||||
* perf [.] main perf 20.00%
|
||||
* libc [.] malloc perf 10.00%
|
||||
* [kernel] [k] page_fault bash 10.00%
|
||||
* [kernel] [k] page_fault perf 10.00%
|
||||
* [kernel] [k] schedule perf 10.00%
|
||||
* bash [.] xmalloc bash 10.00%
|
||||
*/
|
||||
err = add_hist_entries(hists, machine);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
root = &evsel->hists.entries;
|
||||
node = rb_first(root);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") &&
|
||||
!strcmp(COMM(he), "perf") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") &&
|
||||
!strcmp(COMM(he), "perf") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") &&
|
||||
!strcmp(COMM(he), "bash") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") &&
|
||||
!strcmp(COMM(he), "perf") && he->stat.period == 200);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") &&
|
||||
!strcmp(COMM(he), "perf") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
|
||||
!strcmp(COMM(he), "bash") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
|
||||
!strcmp(COMM(he), "perf") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") &&
|
||||
!strcmp(COMM(he), "perf") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
!strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") &&
|
||||
!strcmp(COMM(he), "bash") && he->stat.period == 100);
|
||||
|
||||
out:
|
||||
del_hist_entries(hists);
|
||||
reset_output_field();
|
||||
return err;
|
||||
}
|
||||
|
||||
/* full sort keys w/o overhead field */
|
||||
static int test5(struct perf_evsel *evsel, struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_entry *he;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
field_order = "cpu,pid,comm,dso,sym";
|
||||
sort_order = "dso,pid";
|
||||
|
||||
setup_sorting();
|
||||
|
||||
/*
|
||||
* expected output:
|
||||
*
|
||||
* CPU Command: Pid Command Shared Object Symbol
|
||||
* === ============= ======= ============= ==============
|
||||
* 0 perf: 100 perf [kernel] [k] schedule
|
||||
* 2 perf: 200 perf [kernel] [k] page_fault
|
||||
* 1 bash: 300 bash [kernel] [k] page_fault
|
||||
* 0 bash: 300 bash bash [.] xmalloc
|
||||
* 3 bash: 300 bash bash [.] main
|
||||
* 1 perf: 100 perf libc [.] malloc
|
||||
* 2 perf: 100 perf libc [.] free
|
||||
* 1 perf: 100 perf perf [.] cmd_record
|
||||
* 1 perf: 100 perf perf [.] main
|
||||
* 2 perf: 200 perf perf [.] main
|
||||
*/
|
||||
err = add_hist_entries(hists, machine);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
print_hists_out(hists);
|
||||
}
|
||||
|
||||
root = &evsel->hists.entries;
|
||||
node = rb_first(root);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 0 && PID(he) == 100 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
|
||||
!strcmp(SYM(he), "schedule") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 2 && PID(he) == 200 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
|
||||
!strcmp(SYM(he), "page_fault") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 1 && PID(he) == 300 &&
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
|
||||
!strcmp(SYM(he), "page_fault") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 0 && PID(he) == 300 &&
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
|
||||
!strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 3 && PID(he) == 300 &&
|
||||
!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
|
||||
!strcmp(SYM(he), "main") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 1 && PID(he) == 100 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
|
||||
!strcmp(SYM(he), "malloc") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 2 && PID(he) == 100 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
|
||||
!strcmp(SYM(he), "free") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 1 && PID(he) == 100 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
|
||||
!strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 1 && PID(he) == 100 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
|
||||
!strcmp(SYM(he), "main") && he->stat.period == 100);
|
||||
|
||||
node = rb_next(node);
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
TEST_ASSERT_VAL("Invalid hist entry",
|
||||
CPU(he) == 2 && PID(he) == 200 &&
|
||||
!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
|
||||
!strcmp(SYM(he), "main") && he->stat.period == 100);
|
||||
|
||||
out:
|
||||
del_hist_entries(hists);
|
||||
reset_output_field();
|
||||
return err;
|
||||
}
|
||||
|
||||
int test__hists_output(void)
|
||||
{
|
||||
int err = TEST_FAIL;
|
||||
struct machines machines;
|
||||
struct machine *machine;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
size_t i;
|
||||
test_fn_t testcases[] = {
|
||||
test1,
|
||||
test2,
|
||||
test3,
|
||||
test4,
|
||||
test5,
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("No memory", evlist);
|
||||
|
||||
err = parse_events(evlist, "cpu-clock");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
machines__init(&machines);
|
||||
|
||||
/* setup threads/dso/map/symbols also */
|
||||
machine = setup_fake_machine(&machines);
|
||||
if (!machine)
|
||||
goto out;
|
||||
|
||||
if (verbose > 1)
|
||||
machine__fprintf(machine, stderr);
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(testcases); i++) {
|
||||
err = testcases[i](evsel, machine);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
/* tear down everything */
|
||||
perf_evlist__delete(evlist);
|
||||
machines__exit(&machines);
|
||||
|
||||
return err;
|
||||
}
|
|
@ -44,6 +44,7 @@ int test__dwarf_unwind(void);
|
|||
int test__hists_filter(void);
|
||||
int test__mmap_thread_lookup(void);
|
||||
int test__thread_mg_share(void);
|
||||
int test__hists_output(void);
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
|
|
|
@ -616,35 +616,6 @@ struct hpp_arg {
|
|||
bool current_entry;
|
||||
};
|
||||
|
||||
static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
|
||||
{
|
||||
struct hpp_arg *arg = hpp->ptr;
|
||||
|
||||
if (arg->current_entry && arg->b->navkeypressed)
|
||||
ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
|
||||
else
|
||||
ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
|
||||
|
||||
if (front) {
|
||||
if (!symbol_conf.use_callchain)
|
||||
return 0;
|
||||
|
||||
slsmg_printf("%c ", arg->folded_sign);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
|
||||
{
|
||||
struct hpp_arg *arg = hpp->ptr;
|
||||
|
||||
if (!arg->current_entry || !arg->b->navkeypressed)
|
||||
ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
{
|
||||
struct hpp_arg *arg = hpp->ptr;
|
||||
|
@ -665,7 +636,7 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
|
||||
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
static u64 __hpp_get_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat._field; \
|
||||
|
@ -676,22 +647,20 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
|
|||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \
|
||||
return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
|
||||
__hpp__slsmg_color_printf, true); \
|
||||
}
|
||||
|
||||
__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
|
||||
__HPP_COLOR_PERCENT_FN(overhead, period)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
|
||||
|
||||
#undef __HPP_COLOR_PERCENT_FN
|
||||
|
||||
void hist_browser__init_hpp(void)
|
||||
{
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
hist_browser__hpp_color_overhead;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
|
||||
|
@ -729,7 +698,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
|||
|
||||
if (row_offset == 0) {
|
||||
struct hpp_arg arg = {
|
||||
.b = &browser->b,
|
||||
.b = &browser->b,
|
||||
.folded_sign = folded_sign,
|
||||
.current_entry = current_entry,
|
||||
};
|
||||
|
@ -742,11 +711,27 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
|||
ui_browser__gotorc(&browser->b, row, 0);
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!first) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (current_entry && browser->b.navkeypressed) {
|
||||
ui_browser__set_color(&browser->b,
|
||||
HE_COLORSET_SELECTED);
|
||||
} else {
|
||||
ui_browser__set_color(&browser->b,
|
||||
HE_COLORSET_NORMAL);
|
||||
}
|
||||
|
||||
if (first) {
|
||||
if (symbol_conf.use_callchain) {
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
width -= 2;
|
||||
}
|
||||
first = false;
|
||||
} else {
|
||||
slsmg_printf(" ");
|
||||
width -= 2;
|
||||
}
|
||||
first = false;
|
||||
|
||||
if (fmt->color) {
|
||||
width -= fmt->color(fmt, &hpp, entry);
|
||||
|
@ -760,8 +745,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
|||
if (!browser->b.navkeypressed)
|
||||
width += 1;
|
||||
|
||||
hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
|
||||
slsmg_write_nstring(s, width);
|
||||
slsmg_write_nstring("", width);
|
||||
|
||||
++row;
|
||||
++printed;
|
||||
} else
|
||||
|
@ -830,10 +815,7 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
|
|||
if (total)
|
||||
percent = h->stat.period * 100.0 / total;
|
||||
|
||||
if (percent < min_pcnt)
|
||||
return NULL;
|
||||
|
||||
if (!h->filtered)
|
||||
if (!h->filtered && percent >= min_pcnt)
|
||||
return nd;
|
||||
|
||||
nd = rb_next(nd);
|
||||
|
@ -1104,27 +1086,35 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
|||
struct hist_entry *he, FILE *fp)
|
||||
{
|
||||
char s[8192];
|
||||
double percent;
|
||||
int printed = 0;
|
||||
char folded_sign = ' ';
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
};
|
||||
struct perf_hpp_fmt *fmt;
|
||||
bool first = true;
|
||||
int ret;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
folded_sign = hist_entry__folded(he);
|
||||
|
||||
hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
|
||||
percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
printed += fprintf(fp, "%c ", folded_sign);
|
||||
|
||||
printed += fprintf(fp, " %5.2f%%", percent);
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (symbol_conf.show_nr_samples)
|
||||
printed += fprintf(fp, " %11u", he->stat.nr_events);
|
||||
|
||||
if (symbol_conf.show_total_period)
|
||||
printed += fprintf(fp, " %12" PRIu64, he->stat.period);
|
||||
if (!first) {
|
||||
ret = scnprintf(hpp.buf, hpp.size, " ");
|
||||
advance_hpp(&hpp, ret);
|
||||
} else
|
||||
first = false;
|
||||
|
||||
ret = fmt->entry(fmt, &hpp, he);
|
||||
advance_hpp(&hpp, ret);
|
||||
}
|
||||
printed += fprintf(fp, "%s\n", rtrim(s));
|
||||
|
||||
if (folded_sign == '-')
|
||||
|
|
|
@ -43,7 +43,7 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
|
|||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
|
|||
|
||||
void perf_gtk__init_hpp(void)
|
||||
{
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
perf_gtk__hpp_color_overhead;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
|
||||
|
@ -153,7 +151,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
struct perf_hpp_fmt *fmt;
|
||||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
struct sort_entry *se;
|
||||
GtkTreeStore *store;
|
||||
struct rb_node *nd;
|
||||
GtkWidget *view;
|
||||
|
@ -172,16 +169,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
perf_hpp__for_each_format(fmt)
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
if (se == &sort_sym)
|
||||
sym_col = nr_cols;
|
||||
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
}
|
||||
|
||||
store = gtk_tree_store_newv(nr_cols, col_types);
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
@ -191,6 +178,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
fmt->header(fmt, &hpp, hists_to_evsel(hists));
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
|
@ -199,16 +189,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, se->se_header,
|
||||
renderer, "text",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
for (col_idx = 0; col_idx < nr_cols; col_idx++) {
|
||||
GtkTreeViewColumn *column;
|
||||
|
||||
|
@ -245,6 +225,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (fmt->color)
|
||||
fmt->color(fmt, &hpp, h);
|
||||
else
|
||||
|
@ -253,16 +236,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
se->se_snprintf(h, s, ARRAY_SIZE(s),
|
||||
hists__col_len(hists, se->se_width_idx));
|
||||
|
||||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain && sort__has_sym) {
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = h->stat.period;
|
||||
|
|
|
@ -16,20 +16,15 @@
|
|||
})
|
||||
|
||||
int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
hpp_field_fn get_field, hpp_callback_fn callback,
|
||||
const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
hpp_field_fn get_field, const char *fmt,
|
||||
hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
struct hists *hists = he->hists;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
char *buf = hpp->buf;
|
||||
size_t size = hpp->size;
|
||||
|
||||
if (callback) {
|
||||
ret = callback(hpp, true);
|
||||
advance_hpp(hpp, ret);
|
||||
}
|
||||
|
||||
if (fmt_percent) {
|
||||
double percent = 0.0;
|
||||
u64 total = hists__total_period(hists);
|
||||
|
@ -37,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
if (total)
|
||||
percent = 100.0 * get_field(he) / total;
|
||||
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt, percent);
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, percent);
|
||||
} else
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
|
||||
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
int prev_idx, idx_delta;
|
||||
|
@ -99,13 +94,6 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
}
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
int __ret = callback(hpp, false);
|
||||
|
||||
advance_hpp(hpp, __ret);
|
||||
ret += __ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore original buf and size as it's where caller expects
|
||||
* the result will be saved.
|
||||
|
@ -116,6 +104,62 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int field_cmp(u64 field_a, u64 field_b)
|
||||
{
|
||||
if (field_a > field_b)
|
||||
return 1;
|
||||
if (field_a < field_b)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
|
||||
hpp_field_fn get_field)
|
||||
{
|
||||
s64 ret;
|
||||
int i, nr_members;
|
||||
struct perf_evsel *evsel;
|
||||
struct hist_entry *pair;
|
||||
u64 *fields_a, *fields_b;
|
||||
|
||||
ret = field_cmp(get_field(a), get_field(b));
|
||||
if (ret || !symbol_conf.event_group)
|
||||
return ret;
|
||||
|
||||
evsel = hists_to_evsel(a->hists);
|
||||
if (!perf_evsel__is_group_event(evsel))
|
||||
return ret;
|
||||
|
||||
nr_members = evsel->nr_members;
|
||||
fields_a = calloc(sizeof(*fields_a), nr_members);
|
||||
fields_b = calloc(sizeof(*fields_b), nr_members);
|
||||
|
||||
if (!fields_a || !fields_b)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(pair, &a->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
|
||||
}
|
||||
|
||||
list_for_each_entry(pair, &b->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
|
||||
}
|
||||
|
||||
for (i = 1; i < nr_members; i++) {
|
||||
ret = field_cmp(fields_a[i], fields_b[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(fields_a);
|
||||
free(fields_b);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, \
|
||||
|
@ -179,7 +223,7 @@ static u64 he_get_##_field(struct hist_entry *he) \
|
|||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
}
|
||||
|
||||
|
@ -188,10 +232,16 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
|||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt, \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
|
||||
hpp_entry_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_FN(_type, _field) \
|
||||
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
|
||||
{ \
|
||||
return __hpp__sort(a, b, he_get_##_field); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_RAW_FN(_type, _field) \
|
||||
static u64 he_get_raw_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
|
@ -202,20 +252,29 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
|||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
|
||||
return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt, \
|
||||
return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \
|
||||
hpp_entry_scnprintf, false); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_RAW_FN(_type, _field) \
|
||||
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
|
||||
{ \
|
||||
return __hpp__sort(a, b, he_get_raw_##_field); \
|
||||
}
|
||||
|
||||
|
||||
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
__HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
__HPP_ENTRY_PERCENT_FN(_type, _field)
|
||||
__HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
__HPP_SORT_FN(_type, _field)
|
||||
|
||||
#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
__HPP_ENTRY_RAW_FN(_type, _field)
|
||||
__HPP_ENTRY_RAW_FN(_type, _field) \
|
||||
__HPP_SORT_RAW_FN(_type, _field)
|
||||
|
||||
|
||||
HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
|
||||
|
@ -227,19 +286,31 @@ HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
|
|||
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
|
||||
HPP_RAW_FNS(period, "Period", period, 12, 12)
|
||||
|
||||
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
|
||||
struct hist_entry *b __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.color = hpp__color_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name \
|
||||
.entry = hpp__entry_ ## _name, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _name, \
|
||||
}
|
||||
|
||||
#define HPP__PRINT_FNS(_name) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name \
|
||||
.entry = hpp__entry_ ## _name, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _name, \
|
||||
}
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
|
@ -253,6 +324,7 @@ struct perf_hpp_fmt perf_hpp__format[] = {
|
|||
};
|
||||
|
||||
LIST_HEAD(perf_hpp__list);
|
||||
LIST_HEAD(perf_hpp__sort_list);
|
||||
|
||||
|
||||
#undef HPP__COLOR_PRINT_FNS
|
||||
|
@ -270,6 +342,25 @@ LIST_HEAD(perf_hpp__list);
|
|||
|
||||
void perf_hpp__init(void)
|
||||
{
|
||||
struct list_head *list;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
struct perf_hpp_fmt *fmt = &perf_hpp__format[i];
|
||||
|
||||
INIT_LIST_HEAD(&fmt->list);
|
||||
|
||||
/* sort_list may be linked by setup_sorting() */
|
||||
if (fmt->sort_list.next == NULL)
|
||||
INIT_LIST_HEAD(&fmt->sort_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* If user specified field order, no need to setup default fields.
|
||||
*/
|
||||
if (field_order)
|
||||
return;
|
||||
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
|
||||
if (symbol_conf.show_cpu_utilization) {
|
||||
|
@ -287,6 +378,11 @@ void perf_hpp__init(void)
|
|||
|
||||
if (symbol_conf.show_total_period)
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD);
|
||||
|
||||
/* prepend overhead field for backward compatiblity. */
|
||||
list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
|
||||
if (list_empty(list))
|
||||
list_add(list, &perf_hpp__sort_list);
|
||||
}
|
||||
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format)
|
||||
|
@ -294,29 +390,90 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format)
|
|||
list_add_tail(&format->list, &perf_hpp__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]);
|
||||
}
|
||||
|
||||
int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
|
||||
struct hists *hists)
|
||||
void perf_hpp__setup_output_field(void)
|
||||
{
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
struct sort_entry *se;
|
||||
int ret = 0;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
/* append sort keys to output field */
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (!list_empty(&fmt->list))
|
||||
continue;
|
||||
|
||||
ret += scnprintf(s + ret, size - ret, "%s", sep ?: " ");
|
||||
ret += se->se_snprintf(he, s + ret, size - ret,
|
||||
hists__col_len(hists, se->se_width_idx));
|
||||
/*
|
||||
* 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__column_register(fmt);
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__append_sort_keys(void)
|
||||
{
|
||||
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)) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
perf_hpp__for_each_sort_list(pos) {
|
||||
if (perf_hpp__same_sort_entry(pos, fmt))
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
perf_hpp__register_sort_field(fmt);
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__reset_output_field(void)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt, *tmp;
|
||||
|
||||
/* reset output fields */
|
||||
perf_hpp__for_each_format_safe(fmt, tmp) {
|
||||
list_del_init(&fmt->list);
|
||||
list_del_init(&fmt->sort_list);
|
||||
}
|
||||
|
||||
return ret;
|
||||
/* reset sort keys */
|
||||
perf_hpp__for_each_sort_list_safe(fmt, tmp) {
|
||||
list_del_init(&fmt->list);
|
||||
list_del_init(&fmt->sort_list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -325,22 +482,23 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
|
|||
unsigned int hists__sort_list_width(struct hists *hists)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct sort_entry *se;
|
||||
int i = 0, ret = 0;
|
||||
int ret = 0;
|
||||
bool first = true;
|
||||
struct perf_hpp dummy_hpp;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (i)
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret += 2;
|
||||
|
||||
ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list)
|
||||
if (!se->elide)
|
||||
ret += 2 + hists__col_len(hists, se->se_width_idx);
|
||||
|
||||
if (verbose) /* Addr + origin */
|
||||
if (verbose && sort__has_sym) /* Addr + origin */
|
||||
ret += 3 + BITS_PER_LONG / 4;
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -86,8 +86,6 @@ void setup_browser(bool fallback_to_pager)
|
|||
use_browser = 0;
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
|
||||
perf_hpp__init();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
|
|||
* the symbol. No need to print it otherwise it appears as
|
||||
* displayed twice.
|
||||
*/
|
||||
if (!i++ && sort__first_dimension == SORT_SYM)
|
||||
if (!i++ && field_order == NULL &&
|
||||
sort_order && !prefixcmp(sort_order, "sym"))
|
||||
continue;
|
||||
if (!printed) {
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
|
@ -296,18 +297,24 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
|
|||
int left_margin = 0;
|
||||
u64 total_period = hists->stats.total_period;
|
||||
|
||||
if (sort__first_dimension == SORT_COMM) {
|
||||
struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
|
||||
typeof(*se), list);
|
||||
left_margin = hists__col_len(hists, se->se_width_idx);
|
||||
left_margin -= thread__comm_len(he->thread);
|
||||
}
|
||||
if (field_order == NULL && (sort_order == NULL ||
|
||||
!prefixcmp(sort_order, "comm"))) {
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
/* must be 'comm' sort entry */
|
||||
left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists));
|
||||
left_margin -= thread__comm_len(he->thread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
|
||||
}
|
||||
|
||||
static int hist_entry__period_snprintf(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
|
||||
{
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
@ -319,6 +326,9 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,
|
|||
return 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If there's no field_sep, we still need
|
||||
* to display initial ' '.
|
||||
|
@ -353,8 +363,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
|||
if (size == 0 || size > bfsz)
|
||||
size = hpp.size = bfsz;
|
||||
|
||||
ret = hist_entry__period_snprintf(&hpp, he);
|
||||
hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
|
||||
hist_entry__snprintf(he, &hpp);
|
||||
|
||||
ret = fprintf(fp, "%s\n", bf);
|
||||
|
||||
|
@ -368,12 +377,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
int max_cols, float min_pcnt, FILE *fp)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct sort_entry *se;
|
||||
struct rb_node *nd;
|
||||
size_t ret = 0;
|
||||
unsigned int width;
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
const char *col_width = symbol_conf.col_width_list_str;
|
||||
int nr_rows = 0;
|
||||
char bf[96];
|
||||
struct perf_hpp dummy_hpp = {
|
||||
|
@ -386,12 +393,19 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
|
||||
init_rem_hits();
|
||||
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
|
||||
if (!show_header)
|
||||
goto print_entries;
|
||||
|
||||
fprintf(fp, "# ");
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
|
@ -401,28 +415,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
fprintf(fp, "%s", bf);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
if (sep) {
|
||||
fprintf(fp, "%c%s", *sep, se->se_header);
|
||||
continue;
|
||||
}
|
||||
width = strlen(se->se_header);
|
||||
if (symbol_conf.col_width_list_str) {
|
||||
if (col_width) {
|
||||
hists__set_col_len(hists, se->se_width_idx,
|
||||
atoi(col_width));
|
||||
col_width = strchr(col_width, ',');
|
||||
if (col_width)
|
||||
++col_width;
|
||||
}
|
||||
}
|
||||
if (!hists__new_col_len(hists, se->se_width_idx, width))
|
||||
width = hists__col_len(hists, se->se_width_idx);
|
||||
fprintf(fp, " %*s", width, se->se_header);
|
||||
}
|
||||
|
||||
fprintf(fp, "\n");
|
||||
if (max_rows && ++nr_rows >= max_rows)
|
||||
goto out;
|
||||
|
@ -437,6 +429,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
perf_hpp__for_each_format(fmt) {
|
||||
unsigned int i;
|
||||
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
|
@ -447,20 +442,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
fprintf(fp, ".");
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
unsigned int i;
|
||||
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
fprintf(fp, " ");
|
||||
width = hists__col_len(hists, se->se_width_idx);
|
||||
if (width == 0)
|
||||
width = strlen(se->se_header);
|
||||
for (i = 0; i < width; i++)
|
||||
fprintf(fp, ".");
|
||||
}
|
||||
|
||||
fprintf(fp, "\n");
|
||||
if (max_rows && ++nr_rows >= max_rows)
|
||||
goto out;
|
||||
|
|
|
@ -432,11 +432,14 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
|
|||
int64_t
|
||||
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
struct sort_entry *se;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp = 0;
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
cmp = se->se_cmp(left, right);
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
cmp = fmt->cmp(left, right);
|
||||
if (cmp)
|
||||
break;
|
||||
}
|
||||
|
@ -447,15 +450,14 @@ 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 sort_entry *se;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp = 0;
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
int64_t (*f)(struct hist_entry *, struct hist_entry *);
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
f = se->se_collapse ?: se->se_cmp;
|
||||
|
||||
cmp = f(left, right);
|
||||
cmp = fmt->collapse(left, right);
|
||||
if (cmp)
|
||||
break;
|
||||
}
|
||||
|
@ -568,64 +570,21 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* reverse the map, sort on period.
|
||||
*/
|
||||
|
||||
static int period_cmp(u64 period_a, u64 period_b)
|
||||
static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
|
||||
{
|
||||
if (period_a > period_b)
|
||||
return 1;
|
||||
if (period_a < period_b)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp = 0;
|
||||
|
||||
static int hist_entry__sort_on_period(struct hist_entry *a,
|
||||
struct hist_entry *b)
|
||||
{
|
||||
int ret;
|
||||
int i, nr_members;
|
||||
struct perf_evsel *evsel;
|
||||
struct hist_entry *pair;
|
||||
u64 *periods_a, *periods_b;
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
ret = period_cmp(a->stat.period, b->stat.period);
|
||||
if (ret || !symbol_conf.event_group)
|
||||
return ret;
|
||||
|
||||
evsel = hists_to_evsel(a->hists);
|
||||
nr_members = evsel->nr_members;
|
||||
if (nr_members <= 1)
|
||||
return ret;
|
||||
|
||||
periods_a = zalloc(sizeof(periods_a) * nr_members);
|
||||
periods_b = zalloc(sizeof(periods_b) * nr_members);
|
||||
|
||||
if (!periods_a || !periods_b)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(pair, &a->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period;
|
||||
}
|
||||
|
||||
list_for_each_entry(pair, &b->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period;
|
||||
}
|
||||
|
||||
for (i = 1; i < nr_members; i++) {
|
||||
ret = period_cmp(periods_a[i], periods_b[i]);
|
||||
if (ret)
|
||||
cmp = fmt->sort(a, b);
|
||||
if (cmp)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(periods_a);
|
||||
free(periods_b);
|
||||
|
||||
return ret;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
static void hists__reset_filter_stats(struct hists *hists)
|
||||
|
@ -673,7 +632,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
|
|||
parent = *p;
|
||||
iter = rb_entry(parent, struct hist_entry, rb_node);
|
||||
|
||||
if (hist_entry__sort_on_period(he, iter) > 0)
|
||||
if (hist_entry__sort(he, iter) > 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
|
|
|
@ -160,15 +160,29 @@ struct perf_hpp_fmt {
|
|||
struct hist_entry *he);
|
||||
int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he);
|
||||
int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b);
|
||||
int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b);
|
||||
int64_t (*sort)(struct hist_entry *a, struct hist_entry *b);
|
||||
|
||||
struct list_head list;
|
||||
struct list_head sort_list;
|
||||
};
|
||||
|
||||
extern struct list_head perf_hpp__list;
|
||||
extern struct list_head perf_hpp__sort_list;
|
||||
|
||||
#define perf_hpp__for_each_format(format) \
|
||||
list_for_each_entry(format, &perf_hpp__list, list)
|
||||
|
||||
#define perf_hpp__for_each_format_safe(format, tmp) \
|
||||
list_for_each_entry_safe(format, tmp, &perf_hpp__list, list)
|
||||
|
||||
#define perf_hpp__for_each_sort_list(format) \
|
||||
list_for_each_entry(format, &perf_hpp__sort_list, 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)
|
||||
|
||||
extern struct perf_hpp_fmt perf_hpp__format[];
|
||||
|
||||
enum {
|
||||
|
@ -187,14 +201,23 @@ enum {
|
|||
void perf_hpp__init(void);
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__column_enable(unsigned col);
|
||||
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__should_skip(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
|
||||
|
||||
typedef u64 (*hpp_field_fn)(struct hist_entry *he);
|
||||
typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front);
|
||||
typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
|
||||
|
||||
int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
hpp_field_fn get_field, hpp_callback_fn callback,
|
||||
const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent);
|
||||
hpp_field_fn get_field, const char *fmt,
|
||||
hpp_snprint_fn print_fn, bool fmt_percent);
|
||||
|
||||
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
|
||||
{
|
||||
|
|
|
@ -32,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename)
|
|||
!strcmp(filename, "[heap]");
|
||||
}
|
||||
|
||||
static inline int is_android_lib(const char *filename)
|
||||
{
|
||||
return !strncmp(filename, "/data/app-lib", 13) ||
|
||||
!strncmp(filename, "/system/lib", 11);
|
||||
}
|
||||
|
||||
static inline bool replace_android_lib(const char *filename, char *newfilename)
|
||||
{
|
||||
const char *libname;
|
||||
char *app_abi;
|
||||
size_t app_abi_length, new_length;
|
||||
size_t lib_length = 0;
|
||||
|
||||
libname = strrchr(filename, '/');
|
||||
if (libname)
|
||||
lib_length = strlen(libname);
|
||||
|
||||
app_abi = getenv("APP_ABI");
|
||||
if (!app_abi)
|
||||
return false;
|
||||
|
||||
app_abi_length = strlen(app_abi);
|
||||
|
||||
if (!strncmp(filename, "/data/app-lib", 13)) {
|
||||
char *apk_path;
|
||||
|
||||
if (!app_abi_length)
|
||||
return false;
|
||||
|
||||
new_length = 7 + app_abi_length + lib_length;
|
||||
|
||||
apk_path = getenv("APK_PATH");
|
||||
if (apk_path) {
|
||||
new_length += strlen(apk_path) + 1;
|
||||
if (new_length > PATH_MAX)
|
||||
return false;
|
||||
snprintf(newfilename, new_length,
|
||||
"%s/libs/%s/%s", apk_path, app_abi, libname);
|
||||
} else {
|
||||
if (new_length > PATH_MAX)
|
||||
return false;
|
||||
snprintf(newfilename, new_length,
|
||||
"libs/%s/%s", app_abi, libname);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strncmp(filename, "/system/lib/", 11)) {
|
||||
char *ndk, *app;
|
||||
const char *arch;
|
||||
size_t ndk_length;
|
||||
size_t app_length;
|
||||
|
||||
ndk = getenv("NDK_ROOT");
|
||||
app = getenv("APP_PLATFORM");
|
||||
|
||||
if (!(ndk && app))
|
||||
return false;
|
||||
|
||||
ndk_length = strlen(ndk);
|
||||
app_length = strlen(app);
|
||||
|
||||
if (!(ndk_length && app_length && app_abi_length))
|
||||
return false;
|
||||
|
||||
arch = !strncmp(app_abi, "arm", 3) ? "arm" :
|
||||
!strncmp(app_abi, "mips", 4) ? "mips" :
|
||||
!strncmp(app_abi, "x86", 3) ? "x86" : NULL;
|
||||
|
||||
if (!arch)
|
||||
return false;
|
||||
|
||||
new_length = 27 + ndk_length +
|
||||
app_length + lib_length
|
||||
+ strlen(arch);
|
||||
|
||||
if (new_length > PATH_MAX)
|
||||
return false;
|
||||
snprintf(newfilename, new_length,
|
||||
"%s/platforms/%s/arch-%s/usr/lib/%s",
|
||||
ndk, app, arch, libname);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void map__init(struct map *map, enum map_type type,
|
||||
u64 start, u64 end, u64 pgoff, struct dso *dso)
|
||||
{
|
||||
|
@ -59,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
|
|||
if (map != NULL) {
|
||||
char newfilename[PATH_MAX];
|
||||
struct dso *dso;
|
||||
int anon, no_dso, vdso;
|
||||
int anon, no_dso, vdso, android;
|
||||
|
||||
android = is_android_lib(filename);
|
||||
anon = is_anon_memory(filename);
|
||||
vdso = is_vdso_map(filename);
|
||||
no_dso = is_no_dso_memory(filename);
|
||||
|
@ -75,6 +163,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
|
|||
filename = newfilename;
|
||||
}
|
||||
|
||||
if (android) {
|
||||
if (replace_android_lib(filename, newfilename))
|
||||
filename = newfilename;
|
||||
}
|
||||
|
||||
if (vdso) {
|
||||
pgoff = 0;
|
||||
dso = vdso__dso_findnew(dsos__list);
|
||||
|
|
|
@ -57,13 +57,13 @@ void setup_pager(void)
|
|||
}
|
||||
if (!pager)
|
||||
pager = getenv("PAGER");
|
||||
if (!pager) {
|
||||
if (!access("/usr/bin/pager", X_OK))
|
||||
pager = "/usr/bin/pager";
|
||||
}
|
||||
if (!(pager || access("/usr/bin/pager", X_OK)))
|
||||
pager = "/usr/bin/pager";
|
||||
if (!(pager || access("/usr/bin/less", X_OK)))
|
||||
pager = "/usr/bin/less";
|
||||
if (!pager)
|
||||
pager = "less";
|
||||
else if (!*pager || !strcmp(pager, "cat"))
|
||||
pager = "cat";
|
||||
if (!*pager || !strcmp(pager, "cat"))
|
||||
return;
|
||||
|
||||
spawned_pager = 1; /* means we are emitting to terminal */
|
||||
|
|
|
@ -2,12 +2,18 @@
|
|||
#include "hist.h"
|
||||
#include "comm.h"
|
||||
#include "symbol.h"
|
||||
#include "evsel.h"
|
||||
|
||||
regex_t parent_regex;
|
||||
const char default_parent_pattern[] = "^sys_|^do_page_fault";
|
||||
const char *parent_pattern = default_parent_pattern;
|
||||
const char default_sort_order[] = "comm,dso,symbol";
|
||||
const char *sort_order = default_sort_order;
|
||||
const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
|
||||
const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
|
||||
const char default_top_sort_order[] = "dso,symbol";
|
||||
const char default_diff_sort_order[] = "dso,symbol";
|
||||
const char *sort_order;
|
||||
const char *field_order;
|
||||
regex_t ignore_callees_regex;
|
||||
int have_ignore_callees = 0;
|
||||
int sort__need_collapse = 0;
|
||||
|
@ -16,9 +22,6 @@ int sort__has_sym = 0;
|
|||
int sort__has_dso = 0;
|
||||
enum sort_mode sort__mode = SORT_MODE__NORMAL;
|
||||
|
||||
enum sort_type sort__first_dimension;
|
||||
|
||||
LIST_HEAD(hist_entry__sort_list);
|
||||
|
||||
static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
|
||||
{
|
||||
|
@ -93,6 +96,12 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
|
|||
return comm__str(right->comm) - comm__str(left->comm);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return strcmp(comm__str(right->comm), comm__str(left->comm));
|
||||
}
|
||||
|
||||
static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
|
@ -103,6 +112,7 @@ struct sort_entry sort_comm = {
|
|||
.se_header = "Command",
|
||||
.se_cmp = sort__comm_cmp,
|
||||
.se_collapse = sort__comm_collapse,
|
||||
.se_sort = sort__comm_sort,
|
||||
.se_snprintf = hist_entry__comm_snprintf,
|
||||
.se_width_idx = HISTC_COMM,
|
||||
};
|
||||
|
@ -116,7 +126,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
|
|||
const char *dso_name_l, *dso_name_r;
|
||||
|
||||
if (!dso_l || !dso_r)
|
||||
return cmp_null(dso_l, dso_r);
|
||||
return cmp_null(dso_r, dso_l);
|
||||
|
||||
if (verbose) {
|
||||
dso_name_l = dso_l->long_name;
|
||||
|
@ -132,7 +142,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
|
|||
static int64_t
|
||||
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return _sort__dso_cmp(left->ms.map, right->ms.map);
|
||||
return _sort__dso_cmp(right->ms.map, left->ms.map);
|
||||
}
|
||||
|
||||
static int _hist_entry__dso_snprintf(struct map *map, char *bf,
|
||||
|
@ -204,6 +214,15 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
return _sort__sym_cmp(left->ms.sym, right->ms.sym);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
if (!left->ms.sym || !right->ms.sym)
|
||||
return cmp_null(left->ms.sym, right->ms.sym);
|
||||
|
||||
return strcmp(right->ms.sym->name, left->ms.sym->name);
|
||||
}
|
||||
|
||||
static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
|
||||
u64 ip, char level, char *bf, size_t size,
|
||||
unsigned int width)
|
||||
|
@ -250,6 +269,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
|
|||
struct sort_entry sort_sym = {
|
||||
.se_header = "Symbol",
|
||||
.se_cmp = sort__sym_cmp,
|
||||
.se_sort = sort__sym_sort,
|
||||
.se_snprintf = hist_entry__sym_snprintf,
|
||||
.se_width_idx = HISTC_SYMBOL,
|
||||
};
|
||||
|
@ -277,7 +297,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
map__rip_2objdump(map, right->ip));
|
||||
}
|
||||
}
|
||||
return strcmp(left->srcline, right->srcline);
|
||||
return strcmp(right->srcline, left->srcline);
|
||||
}
|
||||
|
||||
static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
|
||||
|
@ -305,7 +325,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
if (!sym_l || !sym_r)
|
||||
return cmp_null(sym_l, sym_r);
|
||||
|
||||
return strcmp(sym_l->name, sym_r->name);
|
||||
return strcmp(sym_r->name, sym_l->name);
|
||||
}
|
||||
|
||||
static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
|
||||
|
@ -1027,19 +1047,192 @@ static struct sort_dimension memory_sort_dimensions[] = {
|
|||
|
||||
#undef DIM
|
||||
|
||||
static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
|
||||
struct hpp_dimension {
|
||||
const char *name;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int taken;
|
||||
};
|
||||
|
||||
#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
|
||||
|
||||
static struct hpp_dimension hpp_sort_dimensions[] = {
|
||||
DIM(PERF_HPP__OVERHEAD, "overhead"),
|
||||
DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
|
||||
DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
|
||||
DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
|
||||
DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
|
||||
DIM(PERF_HPP__SAMPLES, "sample"),
|
||||
DIM(PERF_HPP__PERIOD, "period"),
|
||||
};
|
||||
|
||||
#undef DIM
|
||||
|
||||
struct hpp_sort_entry {
|
||||
struct perf_hpp_fmt hpp;
|
||||
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_width(struct perf_hpp_fmt *fmt, struct hists *hists)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
return;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
hists__new_col_len(hists, hse->se->se_width_idx,
|
||||
strlen(hse->se->se_header));
|
||||
}
|
||||
|
||||
static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
size_t len;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header);
|
||||
}
|
||||
|
||||
static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
|
||||
struct perf_hpp *hpp __maybe_unused,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
|
||||
return hists__col_len(&evsel->hists, hse->se->se_width_idx);
|
||||
}
|
||||
|
||||
static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
size_t len;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
len = hists__col_len(he->hists, hse->se->se_width_idx);
|
||||
|
||||
return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
|
||||
}
|
||||
|
||||
static struct hpp_sort_entry *
|
||||
__sort_dimension__alloc_hpp(struct sort_dimension *sd)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
hse = malloc(sizeof(*hse));
|
||||
if (hse == NULL) {
|
||||
pr_err("Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hse->se = sd->entry;
|
||||
hse->hpp.header = __sort__hpp_header;
|
||||
hse->hpp.width = __sort__hpp_width;
|
||||
hse->hpp.entry = __sort__hpp_entry;
|
||||
hse->hpp.color = NULL;
|
||||
|
||||
hse->hpp.cmp = sd->entry->se_cmp;
|
||||
hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
|
||||
hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
|
||||
|
||||
INIT_LIST_HEAD(&hse->hpp.list);
|
||||
INIT_LIST_HEAD(&hse->hpp.sort_list);
|
||||
|
||||
return hse;
|
||||
}
|
||||
|
||||
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
|
||||
{
|
||||
return format->header == __sort__hpp_header;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
|
||||
{
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
|
||||
|
||||
if (hse == NULL)
|
||||
return -1;
|
||||
|
||||
perf_hpp__register_sort_field(&hse->hpp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_hpp_output(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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add(struct sort_dimension *sd)
|
||||
{
|
||||
if (sd->taken)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (__sort_dimension__add_hpp_sort(sd) < 0)
|
||||
return -1;
|
||||
|
||||
if (sd->entry->se_collapse)
|
||||
sort__need_collapse = 1;
|
||||
|
||||
if (list_empty(&hist_entry__sort_list))
|
||||
sort__first_dimension = idx;
|
||||
|
||||
list_add_tail(&sd->entry->list, &hist_entry__sort_list);
|
||||
sd->taken = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp_dimension__add(struct hpp_dimension *hd)
|
||||
{
|
||||
if (!hd->taken) {
|
||||
hd->taken = 1;
|
||||
|
||||
perf_hpp__register_sort_field(hd->fmt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_output(struct sort_dimension *sd)
|
||||
{
|
||||
if (sd->taken)
|
||||
return 0;
|
||||
|
||||
if (__sort_dimension__add_hpp_output(sd) < 0)
|
||||
return -1;
|
||||
|
||||
sd->taken = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp_dimension__add_output(struct hpp_dimension *hd)
|
||||
{
|
||||
if (!hd->taken) {
|
||||
hd->taken = 1;
|
||||
|
||||
perf_hpp__column_register(hd->fmt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sort_dimension__add(const char *tok)
|
||||
|
@ -1068,8 +1261,16 @@ int sort_dimension__add(const char *tok)
|
|||
sort__has_dso = 1;
|
||||
}
|
||||
|
||||
__sort_dimension__add(sd, i);
|
||||
return 0;
|
||||
return __sort_dimension__add(sd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
||||
struct hpp_dimension *hd = &hpp_sort_dimensions[i];
|
||||
|
||||
if (strncasecmp(tok, hd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __hpp_dimension__add(hd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
|
||||
|
@ -1084,7 +1285,7 @@ int sort_dimension__add(const char *tok)
|
|||
if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
|
||||
sort__has_sym = 1;
|
||||
|
||||
__sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
|
||||
__sort_dimension__add(sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1100,18 +1301,47 @@ int sort_dimension__add(const char *tok)
|
|||
if (sd->entry == &sort_mem_daddr_sym)
|
||||
sort__has_sym = 1;
|
||||
|
||||
__sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
|
||||
__sort_dimension__add(sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
int setup_sorting(void)
|
||||
static const char *get_default_sort_order(void)
|
||||
{
|
||||
char *tmp, *tok, *str = strdup(sort_order);
|
||||
const char *default_sort_orders[] = {
|
||||
default_sort_order,
|
||||
default_branch_sort_order,
|
||||
default_mem_sort_order,
|
||||
default_top_sort_order,
|
||||
default_diff_sort_order,
|
||||
};
|
||||
|
||||
BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
|
||||
|
||||
return default_sort_orders[sort__mode];
|
||||
}
|
||||
|
||||
static int __setup_sorting(void)
|
||||
{
|
||||
char *tmp, *tok, *str;
|
||||
const char *sort_keys = sort_order;
|
||||
int ret = 0;
|
||||
|
||||
if (sort_keys == NULL) {
|
||||
if (field_order) {
|
||||
/*
|
||||
* If user specified field order but no sort order,
|
||||
* we'll honor it and not add default sort orders.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
sort_keys = get_default_sort_order();
|
||||
}
|
||||
|
||||
str = strdup(sort_keys);
|
||||
if (str == NULL) {
|
||||
error("Not enough memory to setup sort keys");
|
||||
return -ENOMEM;
|
||||
|
@ -1133,6 +1363,17 @@ int setup_sorting(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
|
||||
{
|
||||
if (perf_hpp__is_sort_entry(format)) {
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
hse = container_of(format, struct hpp_sort_entry, hpp);
|
||||
return hse->se->elide;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sort_entry__setup_elide(struct sort_entry *se,
|
||||
struct strlist *list,
|
||||
const char *list_name, FILE *fp)
|
||||
|
@ -1147,7 +1388,8 @@ static void sort_entry__setup_elide(struct sort_entry *se,
|
|||
|
||||
void sort__setup_elide(FILE *output)
|
||||
{
|
||||
struct sort_entry *se;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
|
||||
"dso", output);
|
||||
|
@ -1188,11 +1430,157 @@ void sort__setup_elide(FILE *output)
|
|||
* It makes no sense to elide all of sort entries.
|
||||
* Just revert them to show up again.
|
||||
*/
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (!se->elide)
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
if (!hse->se->elide)
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list)
|
||||
se->elide = false;
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
hse->se->elide = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int output_field_add(char *tok)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
|
||||
struct sort_dimension *sd = &common_sort_dimensions[i];
|
||||
|
||||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __sort_dimension__add_output(sd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
||||
struct hpp_dimension *hd = &hpp_sort_dimensions[i];
|
||||
|
||||
if (strncasecmp(tok, hd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __hpp_dimension__add_output(hd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
|
||||
struct sort_dimension *sd = &bstack_sort_dimensions[i];
|
||||
|
||||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __sort_dimension__add_output(sd);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
|
||||
struct sort_dimension *sd = &memory_sort_dimensions[i];
|
||||
|
||||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __sort_dimension__add_output(sd);
|
||||
}
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
static void reset_dimensions(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
|
||||
common_sort_dimensions[i].taken = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
|
||||
hpp_sort_dimensions[i].taken = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
|
||||
bstack_sort_dimensions[i].taken = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
|
||||
memory_sort_dimensions[i].taken = 0;
|
||||
}
|
||||
|
||||
static int __setup_output_field(void)
|
||||
{
|
||||
char *tmp, *tok, *str;
|
||||
int ret = 0;
|
||||
|
||||
if (field_order == NULL)
|
||||
return 0;
|
||||
|
||||
reset_dimensions();
|
||||
|
||||
str = strdup(field_order);
|
||||
if (str == NULL) {
|
||||
error("Not enough memory to setup output fields");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (tok = strtok_r(str, ", ", &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;
|
||||
}
|
||||
}
|
||||
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int setup_sorting(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __setup_sorting();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (parent_pattern != default_parent_pattern) {
|
||||
err = sort_dimension__add("parent");
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
reset_dimensions();
|
||||
|
||||
/*
|
||||
* perf diff doesn't use default hpp output fields.
|
||||
*/
|
||||
if (sort__mode != SORT_MODE__DIFF)
|
||||
perf_hpp__init();
|
||||
|
||||
err = __setup_output_field();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* copy sort keys to output fields */
|
||||
perf_hpp__setup_output_field();
|
||||
/* and then copy output fields to sort keys */
|
||||
perf_hpp__append_sort_keys();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void reset_output_field(void)
|
||||
{
|
||||
sort__need_collapse = 0;
|
||||
sort__has_parent = 0;
|
||||
sort__has_sym = 0;
|
||||
sort__has_dso = 0;
|
||||
|
||||
reset_dimensions();
|
||||
perf_hpp__reset_output_field();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
extern regex_t parent_regex;
|
||||
extern const char *sort_order;
|
||||
extern const char *field_order;
|
||||
extern const char default_parent_pattern[];
|
||||
extern const char *parent_pattern;
|
||||
extern const char default_sort_order[];
|
||||
|
@ -133,6 +134,8 @@ enum sort_mode {
|
|||
SORT_MODE__NORMAL,
|
||||
SORT_MODE__BRANCH,
|
||||
SORT_MODE__MEMORY,
|
||||
SORT_MODE__TOP,
|
||||
SORT_MODE__DIFF,
|
||||
};
|
||||
|
||||
enum sort_type {
|
||||
|
@ -179,6 +182,7 @@ struct sort_entry {
|
|||
|
||||
int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
|
||||
int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
|
||||
int64_t (*se_sort)(struct hist_entry *, struct hist_entry *);
|
||||
int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size,
|
||||
unsigned int width);
|
||||
u8 se_width_idx;
|
||||
|
@ -189,6 +193,8 @@ extern struct sort_entry sort_thread;
|
|||
extern struct list_head hist_entry__sort_list;
|
||||
|
||||
int setup_sorting(void);
|
||||
int setup_output_field(void);
|
||||
void reset_output_field(void);
|
||||
extern int sort_dimension__add(const char *);
|
||||
void sort__setup_elide(FILE *fp);
|
||||
|
||||
|
|
Loading…
Reference in a new issue