forked from Minki/linux
Merge branch 'email/acme' into perf/core
Merge perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: - Allow grouping multiple sort keys per 'perf report/top --hierarchy' level (Namhyung Kim) - Document 'perf stat --detailed' option (Borislav Petkov) Infrastructure changes: - jitdump prep work for supporting it with Intel PT (Adrian Hunter) - Use 64-bit shifts with (TSC) time conversion (Adrian Hunter) Fixes: - Explicitly declare inc_group_count as a void function (Colin Ian King) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
b9461ba85f
@ -69,6 +69,14 @@ report::
|
||||
--scale::
|
||||
scale/normalize counter values
|
||||
|
||||
-d::
|
||||
--detailed::
|
||||
print more detailed statistics, can be specified up to 3 times
|
||||
|
||||
-d: detailed events, L1 and LLC data cache
|
||||
-d -d: more detailed events, dTLB and iTLB events
|
||||
-d -d -d: very detailed events, adding prefetch events
|
||||
|
||||
-r::
|
||||
--repeat=<n>::
|
||||
repeat command and print average + stddev (max: 100). 0 means forever.
|
||||
|
@ -59,7 +59,7 @@ static u64 mmap_read_self(void *addr)
|
||||
u64 quot, rem;
|
||||
|
||||
quot = (cyc >> time_shift);
|
||||
rem = cyc & ((1 << time_shift) - 1);
|
||||
rem = cyc & (((u64)1 << time_shift) - 1);
|
||||
delta = time_offset + quot * time_mult +
|
||||
((rem * time_mult) >> time_shift);
|
||||
|
||||
|
@ -253,12 +253,16 @@ static int perf_event__jit_repipe_mmap(struct perf_tool *tool,
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
u64 n = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* if jit marker, then inject jit mmaps and generate ELF images
|
||||
*/
|
||||
if (!jit_process(inject->session, &inject->output, machine,
|
||||
event->mmap.filename, sample->pid, &n)) {
|
||||
ret = jit_process(inject->session, &inject->output, machine,
|
||||
event->mmap.filename, sample->pid, &n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret) {
|
||||
inject->bytes_written += n;
|
||||
return 0;
|
||||
}
|
||||
@ -287,12 +291,16 @@ static int perf_event__jit_repipe_mmap2(struct perf_tool *tool,
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
u64 n = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* if jit marker, then inject jit mmaps and generate ELF images
|
||||
*/
|
||||
if (!jit_process(inject->session, &inject->output, machine,
|
||||
event->mmap2.filename, sample->pid, &n)) {
|
||||
ret = jit_process(inject->session, &inject->output, machine,
|
||||
event->mmap2.filename, sample->pid, &n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret) {
|
||||
inject->bytes_written += n;
|
||||
return 0;
|
||||
}
|
||||
@ -679,12 +687,16 @@ static int __cmd_inject(struct perf_inject *inject)
|
||||
ret = perf_session__process_events(session);
|
||||
|
||||
if (!file_out->is_pipe) {
|
||||
if (inject->build_ids) {
|
||||
if (inject->build_ids)
|
||||
perf_header__set_feat(&session->header,
|
||||
HEADER_BUILD_ID);
|
||||
if (inject->have_auxtrace)
|
||||
dsos__hit_all(session);
|
||||
}
|
||||
/*
|
||||
* Keep all buildids when there is unprocessed AUX data because
|
||||
* it is not known which ones the AUX trace hits.
|
||||
*/
|
||||
if (perf_header__has_feat(&session->header, HEADER_BUILD_ID) &&
|
||||
inject->have_auxtrace && !inject->itrace_synth_opts.set)
|
||||
dsos__hit_all(session);
|
||||
/*
|
||||
* The AUX areas have been removed and replaced with
|
||||
* synthesized hardware events, so clear the feature flag and
|
||||
@ -717,23 +729,6 @@ static int __cmd_inject(struct perf_inject *inject)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
static int
|
||||
jit_validate_events(struct perf_session *session)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
/*
|
||||
* check that all events use CLOCK_MONOTONIC
|
||||
*/
|
||||
evlist__for_each(session->evlist, evsel) {
|
||||
if (evsel->attr.use_clockid == 0 || evsel->attr.clockid != CLOCK_MONOTONIC)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_inject inject = {
|
||||
@ -840,13 +835,6 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
}
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
if (inject.jit_mode) {
|
||||
/*
|
||||
* validate event is using the correct clockid
|
||||
*/
|
||||
if (jit_validate_events(inject.session)) {
|
||||
fprintf(stderr, "error, jitted code must be sampled with perf record -k 1\n");
|
||||
return -1;
|
||||
}
|
||||
inject.tool.mmap2 = perf_event__jit_repipe_mmap2;
|
||||
inject.tool.mmap = perf_event__jit_repipe_mmap;
|
||||
inject.tool.ordered_events = true;
|
||||
|
@ -1280,7 +1280,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
|
||||
struct hist_entry *entry,
|
||||
unsigned short row,
|
||||
int level, int nr_sort_keys)
|
||||
int level)
|
||||
{
|
||||
int printed = 0;
|
||||
int width = browser->b.width;
|
||||
@ -1289,12 +1289,13 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
|
||||
off_t row_offset = entry->row_offset;
|
||||
bool first = true;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
struct hpp_arg arg = {
|
||||
.b = &browser->b,
|
||||
.current_entry = current_entry,
|
||||
};
|
||||
int column = 0;
|
||||
int hierarchy_indent = (nr_sort_keys - 1) * HIERARCHY_INDENT;
|
||||
int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
|
||||
|
||||
if (current_entry) {
|
||||
browser->he_selection = entry;
|
||||
@ -1320,7 +1321,10 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
|
||||
ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
|
||||
width -= level * HIERARCHY_INDENT;
|
||||
|
||||
hists__for_each_format(entry->hists, fmt) {
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&entry->hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
char s[2048];
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
@ -1332,10 +1336,6 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
|
||||
column++ < browser->b.horiz_scroll)
|
||||
continue;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt) ||
|
||||
perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
if (current_entry && browser->b.navkeypressed) {
|
||||
ui_browser__set_color(&browser->b,
|
||||
HE_COLORSET_SELECTED);
|
||||
@ -1388,25 +1388,26 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
|
||||
HE_COLORSET_NORMAL);
|
||||
}
|
||||
|
||||
ui_browser__write_nstring(&browser->b, "", 2);
|
||||
width -= 2;
|
||||
perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
|
||||
ui_browser__write_nstring(&browser->b, "", 2);
|
||||
width -= 2;
|
||||
|
||||
/*
|
||||
* No need to call hist_entry__snprintf_alignment()
|
||||
* since this fmt is always the last column in the
|
||||
* hierarchy mode.
|
||||
*/
|
||||
fmt = entry->fmt;
|
||||
if (fmt->color) {
|
||||
width -= fmt->color(fmt, &hpp, entry);
|
||||
} else {
|
||||
int i = 0;
|
||||
/*
|
||||
* No need to call hist_entry__snprintf_alignment()
|
||||
* since this fmt is always the last column in the
|
||||
* hierarchy mode.
|
||||
*/
|
||||
if (fmt->color) {
|
||||
width -= fmt->color(fmt, &hpp, entry);
|
||||
} else {
|
||||
int i = 0;
|
||||
|
||||
width -= fmt->entry(fmt, &hpp, entry);
|
||||
ui_browser__printf(&browser->b, "%s", ltrim(s));
|
||||
width -= fmt->entry(fmt, &hpp, entry);
|
||||
ui_browser__printf(&browser->b, "%s", ltrim(s));
|
||||
|
||||
while (isspace(s[i++]))
|
||||
width++;
|
||||
while (isspace(s[i++]))
|
||||
width++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1435,8 +1436,7 @@ show_callchain:
|
||||
}
|
||||
|
||||
static int hist_browser__show_no_entry(struct hist_browser *browser,
|
||||
unsigned short row,
|
||||
int level, int nr_sort_keys)
|
||||
unsigned short row, int level)
|
||||
{
|
||||
int width = browser->b.width;
|
||||
bool current_entry = ui_browser__is_current_entry(&browser->b, row);
|
||||
@ -1444,6 +1444,8 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,
|
||||
int column = 0;
|
||||
int ret;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
int indent = browser->hists->nr_hpp_node - 2;
|
||||
|
||||
if (current_entry) {
|
||||
browser->he_selection = NULL;
|
||||
@ -1460,15 +1462,14 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,
|
||||
ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
|
||||
width -= level * HIERARCHY_INDENT;
|
||||
|
||||
hists__for_each_format(browser->hists, fmt) {
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&browser->hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, browser->hists) ||
|
||||
column++ < browser->b.horiz_scroll)
|
||||
continue;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt) ||
|
||||
perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
ret = fmt->width(fmt, NULL, hists_to_evsel(browser->hists));
|
||||
|
||||
if (first) {
|
||||
@ -1484,8 +1485,8 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,
|
||||
width -= ret;
|
||||
}
|
||||
|
||||
ui_browser__write_nstring(&browser->b, "", nr_sort_keys * HIERARCHY_INDENT);
|
||||
width -= nr_sort_keys * HIERARCHY_INDENT;
|
||||
ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
|
||||
width -= indent * HIERARCHY_INDENT;
|
||||
|
||||
if (column >= browser->b.horiz_scroll) {
|
||||
char buf[32];
|
||||
@ -1550,22 +1551,23 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
|
||||
.size = size,
|
||||
};
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
size_t ret = 0;
|
||||
int column = 0;
|
||||
int nr_sort_keys = hists->nr_sort_keys;
|
||||
bool first = true;
|
||||
int indent = hists->nr_hpp_node - 2;
|
||||
bool first_node, first_col;
|
||||
|
||||
ret = scnprintf(buf, size, " ");
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
return ret;
|
||||
|
||||
hists__for_each_format(hists, fmt) {
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (column++ < browser->b.horiz_scroll)
|
||||
continue;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
break;
|
||||
@ -1576,38 +1578,46 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
|
||||
}
|
||||
|
||||
ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
|
||||
(nr_sort_keys - 1) * HIERARCHY_INDENT, "");
|
||||
indent * HIERARCHY_INDENT, "");
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
return ret;
|
||||
|
||||
hists__for_each_format(hists, fmt) {
|
||||
char *start;
|
||||
|
||||
if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt))
|
||||
continue;
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
first_node = true;
|
||||
list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
|
||||
if (!first_node) {
|
||||
ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
break;
|
||||
}
|
||||
first_node = false;
|
||||
|
||||
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
dummy_hpp.buf[ret] = '\0';
|
||||
rtrim(dummy_hpp.buf);
|
||||
first_col = true;
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
char *start;
|
||||
|
||||
start = ltrim(dummy_hpp.buf);
|
||||
ret = strlen(start);
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (start != dummy_hpp.buf)
|
||||
memmove(dummy_hpp.buf, start, ret + 1);
|
||||
if (!first_col) {
|
||||
ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
break;
|
||||
}
|
||||
first_col = false;
|
||||
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
break;
|
||||
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
dummy_hpp.buf[ret] = '\0';
|
||||
rtrim(dummy_hpp.buf);
|
||||
|
||||
start = ltrim(dummy_hpp.buf);
|
||||
ret = strlen(start);
|
||||
|
||||
if (start != dummy_hpp.buf)
|
||||
memmove(dummy_hpp.buf, start, ret + 1);
|
||||
|
||||
if (advance_hpp_check(&dummy_hpp, ret))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1644,7 +1654,6 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
|
||||
u16 header_offset = 0;
|
||||
struct rb_node *nd;
|
||||
struct hist_browser *hb = container_of(browser, struct hist_browser, b);
|
||||
int nr_sort = hb->hists->nr_sort_keys;
|
||||
|
||||
if (hb->show_headers) {
|
||||
hist_browser__show_headers(hb);
|
||||
@ -1671,14 +1680,12 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
|
||||
|
||||
if (symbol_conf.report_hierarchy) {
|
||||
row += hist_browser__show_hierarchy_entry(hb, h, row,
|
||||
h->depth,
|
||||
nr_sort);
|
||||
h->depth);
|
||||
if (row == browser->rows)
|
||||
break;
|
||||
|
||||
if (h->has_no_entry) {
|
||||
hist_browser__show_no_entry(hb, row, h->depth,
|
||||
nr_sort);
|
||||
hist_browser__show_no_entry(hb, row, h->depth + 1);
|
||||
row++;
|
||||
}
|
||||
} else {
|
||||
@ -1934,7 +1941,7 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
|
||||
struct perf_hpp_fmt *fmt;
|
||||
bool first = true;
|
||||
int ret;
|
||||
int hierarchy_indent = (nr_sort_keys + 1) * HIERARCHY_INDENT;
|
||||
int hierarchy_indent = nr_sort_keys * HIERARCHY_INDENT;
|
||||
|
||||
printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
|
||||
|
||||
@ -1962,9 +1969,13 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
|
||||
ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
|
||||
advance_hpp(&hpp, ret);
|
||||
|
||||
fmt = he->fmt;
|
||||
ret = fmt->entry(fmt, &hpp, he);
|
||||
advance_hpp(&hpp, ret);
|
||||
perf_hpp_list__for_each_format(he->hpp_list, fmt) {
|
||||
ret = scnprintf(hpp.buf, hpp.size, " ");
|
||||
advance_hpp(&hpp, ret);
|
||||
|
||||
ret = fmt->entry(fmt, &hpp, he);
|
||||
advance_hpp(&hpp, ret);
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%s\n", rtrim(s));
|
||||
|
||||
|
@ -407,11 +407,14 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
|
||||
struct rb_node *node;
|
||||
struct hist_entry *he;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
u64 total = hists__total_period(hists);
|
||||
int size;
|
||||
|
||||
for (node = rb_first(root); node; node = rb_next(node)) {
|
||||
GtkTreeIter iter;
|
||||
float percent;
|
||||
char *bf;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
if (he->filtered)
|
||||
@ -424,11 +427,11 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
|
||||
gtk_tree_store_append(store, &iter, parent);
|
||||
|
||||
col_idx = 0;
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__is_sort_entry(fmt) ||
|
||||
perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (fmt->color)
|
||||
fmt->color(fmt, hpp, he);
|
||||
else
|
||||
@ -437,15 +440,26 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
|
||||
gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1);
|
||||
}
|
||||
|
||||
fmt = he->fmt;
|
||||
if (fmt->color)
|
||||
fmt->color(fmt, hpp, he);
|
||||
else
|
||||
fmt->entry(fmt, hpp, he);
|
||||
bf = hpp->buf;
|
||||
size = hpp->size;
|
||||
perf_hpp_list__for_each_format(he->hpp_list, fmt) {
|
||||
int ret;
|
||||
|
||||
gtk_tree_store_set(store, &iter, col_idx, rtrim(hpp->buf), -1);
|
||||
if (fmt->color)
|
||||
ret = fmt->color(fmt, hpp, he);
|
||||
else
|
||||
ret = fmt->entry(fmt, hpp, he);
|
||||
|
||||
snprintf(hpp->buf + ret, hpp->size - ret, " ");
|
||||
advance_hpp(hpp, ret + 2);
|
||||
}
|
||||
|
||||
gtk_tree_store_set(store, &iter, col_idx, ltrim(rtrim(bf)), -1);
|
||||
|
||||
if (!he->leaf) {
|
||||
hpp->buf = bf;
|
||||
hpp->size = size;
|
||||
|
||||
perf_gtk__add_hierarchy_entries(hists, &he->hroot_out,
|
||||
store, &iter, hpp,
|
||||
min_pcnt);
|
||||
@ -478,6 +492,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
|
||||
float min_pcnt)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeStore *store;
|
||||
@ -486,7 +501,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
|
||||
int nr_cols = 0;
|
||||
char s[512];
|
||||
char buf[512];
|
||||
bool first = true;
|
||||
bool first_node, first_col;
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
@ -506,11 +521,11 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
|
||||
col_idx = 0;
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__is_sort_entry(fmt) ||
|
||||
perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, fmt->name,
|
||||
renderer, "markup",
|
||||
@ -519,20 +534,24 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
|
||||
|
||||
/* construct merged column header since sort keys share single column */
|
||||
buf[0] = '\0';
|
||||
hists__for_each_format(hists ,fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt) &&
|
||||
!perf_hpp__is_dynamic_entry(fmt))
|
||||
continue;
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
first_node = true;
|
||||
list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
|
||||
if (!first_node)
|
||||
strcat(buf, " / ");
|
||||
first_node = false;
|
||||
|
||||
fmt->header(fmt, &hpp, hists_to_evsel(hists));
|
||||
strcat(buf, rtrim(hpp.buf));
|
||||
first_col = true;
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp ,fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (!first_col)
|
||||
strcat(buf, "+");
|
||||
first_col = false;
|
||||
|
||||
fmt->header(fmt, &hpp, hists_to_evsel(hists));
|
||||
strcat(buf, ltrim(rtrim(hpp.buf)));
|
||||
}
|
||||
}
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "../util/util.h"
|
||||
#include "../util/sort.h"
|
||||
#include "../util/evsel.h"
|
||||
#include "../util/evlist.h"
|
||||
|
||||
/* hist period print (hpp) functions */
|
||||
|
||||
@ -715,3 +716,71 @@ void perf_hpp__set_user_width(const char *width_list_str)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
struct perf_hpp_list_node *node = NULL;
|
||||
struct perf_hpp_fmt *fmt_copy;
|
||||
bool found = false;
|
||||
bool skip = perf_hpp__should_skip(fmt, hists);
|
||||
|
||||
list_for_each_entry(node, &hists->hpp_formats, list) {
|
||||
if (node->level == fmt->level) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
node = malloc(sizeof(*node));
|
||||
if (node == NULL)
|
||||
return -1;
|
||||
|
||||
node->skip = skip;
|
||||
node->level = fmt->level;
|
||||
perf_hpp_list__init(&node->hpp);
|
||||
|
||||
hists->nr_hpp_node++;
|
||||
list_add_tail(&node->list, &hists->hpp_formats);
|
||||
}
|
||||
|
||||
fmt_copy = perf_hpp_fmt__dup(fmt);
|
||||
if (fmt_copy == NULL)
|
||||
return -1;
|
||||
|
||||
if (!skip)
|
||||
node->skip = false;
|
||||
|
||||
list_add_tail(&fmt_copy->list, &node->hpp.fields);
|
||||
list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
|
||||
struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct hists *hists;
|
||||
int ret;
|
||||
|
||||
if (!symbol_conf.report_hierarchy)
|
||||
return 0;
|
||||
|
||||
evlist__for_each(evlist, evsel) {
|
||||
hists = evsel__hists(evsel);
|
||||
|
||||
perf_hpp_list__for_each_sort_list(list, fmt) {
|
||||
if (perf_hpp__is_dynamic_entry(fmt) &&
|
||||
!perf_hpp__defined_dynamic_entry(fmt, hists))
|
||||
continue;
|
||||
|
||||
ret = add_hierarchy_fmt(hists, fmt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -412,11 +412,12 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
|
||||
|
||||
static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
|
||||
struct perf_hpp *hpp,
|
||||
int nr_sort_key, struct hists *hists,
|
||||
struct hists *hists,
|
||||
FILE *fp)
|
||||
{
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
char *buf = hpp->buf;
|
||||
size_t size = hpp->size;
|
||||
int ret, printed = 0;
|
||||
@ -428,10 +429,10 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
|
||||
advance_hpp(hpp, ret);
|
||||
|
||||
hists__for_each_format(he->hists, fmt) {
|
||||
if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
/*
|
||||
* If there's no field_sep, we still need
|
||||
* to display initial ' '.
|
||||
@ -451,33 +452,33 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
|
||||
advance_hpp(hpp, ret);
|
||||
}
|
||||
|
||||
if (sep)
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%s", sep);
|
||||
else
|
||||
if (!sep)
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%*s",
|
||||
(nr_sort_key - 1) * HIERARCHY_INDENT + 2, "");
|
||||
(hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
|
||||
advance_hpp(hpp, ret);
|
||||
|
||||
printed += fprintf(fp, "%s", buf);
|
||||
|
||||
hpp->buf = buf;
|
||||
hpp->size = size;
|
||||
perf_hpp_list__for_each_format(he->hpp_list, fmt) {
|
||||
hpp->buf = buf;
|
||||
hpp->size = size;
|
||||
|
||||
/*
|
||||
* No need to call hist_entry__snprintf_alignment() since this
|
||||
* fmt is always the last column in the hierarchy mode.
|
||||
*/
|
||||
fmt = he->fmt;
|
||||
if (perf_hpp__use_color() && fmt->color)
|
||||
fmt->color(fmt, hpp, he);
|
||||
else
|
||||
fmt->entry(fmt, hpp, he);
|
||||
/*
|
||||
* No need to call hist_entry__snprintf_alignment() since this
|
||||
* fmt is always the last column in the hierarchy mode.
|
||||
*/
|
||||
if (perf_hpp__use_color() && fmt->color)
|
||||
fmt->color(fmt, hpp, he);
|
||||
else
|
||||
fmt->entry(fmt, hpp, he);
|
||||
|
||||
/*
|
||||
* dynamic entries are right-aligned but we want left-aligned
|
||||
* in the hierarchy mode
|
||||
*/
|
||||
printed += fprintf(fp, "%s\n", ltrim(buf));
|
||||
/*
|
||||
* dynamic entries are right-aligned but we want left-aligned
|
||||
* in the hierarchy mode
|
||||
*/
|
||||
printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf));
|
||||
}
|
||||
printed += putc('\n', fp);
|
||||
|
||||
if (symbol_conf.use_callchain && he->leaf) {
|
||||
u64 total = hists__total_period(hists);
|
||||
@ -504,12 +505,8 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
if (size == 0 || size > bfsz)
|
||||
size = hpp.size = bfsz;
|
||||
|
||||
if (symbol_conf.report_hierarchy) {
|
||||
int nr_sort = hists->nr_sort_keys;
|
||||
|
||||
return hist_entry__hierarchy_fprintf(he, &hpp, nr_sort,
|
||||
hists, fp);
|
||||
}
|
||||
if (symbol_conf.report_hierarchy)
|
||||
return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
|
||||
|
||||
hist_entry__snprintf(he, &hpp);
|
||||
|
||||
@ -521,92 +518,97 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_hierarchy_indent(const char *sep, int nr_sort,
|
||||
static int print_hierarchy_indent(const char *sep, int indent,
|
||||
const char *line, FILE *fp)
|
||||
{
|
||||
if (sep != NULL || nr_sort < 1)
|
||||
if (sep != NULL || indent < 2)
|
||||
return 0;
|
||||
|
||||
return fprintf(fp, "%-.*s", (nr_sort - 1) * HIERARCHY_INDENT, line);
|
||||
return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line);
|
||||
}
|
||||
|
||||
static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp,
|
||||
const char *sep, FILE *fp)
|
||||
{
|
||||
bool first = true;
|
||||
int nr_sort;
|
||||
bool first_node, first_col;
|
||||
int indent;
|
||||
int depth;
|
||||
unsigned width = 0;
|
||||
unsigned header_width = 0;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *fmt_node;
|
||||
|
||||
nr_sort = hists->nr_sort_keys;
|
||||
indent = hists->nr_hpp_node;
|
||||
|
||||
/* preserve max indent depth for column headers */
|
||||
print_hierarchy_indent(sep, nr_sort, spaces, fp);
|
||||
print_hierarchy_indent(sep, indent, spaces, fp);
|
||||
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
fmt->header(fmt, hpp, hists_to_evsel(hists));
|
||||
fprintf(fp, "%s", hpp->buf);
|
||||
fprintf(fp, "%s%s", hpp->buf, sep ?: " ");
|
||||
}
|
||||
|
||||
/* combine sort headers with ' / ' */
|
||||
first = true;
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt))
|
||||
continue;
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
first_node = true;
|
||||
list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
|
||||
if (!first_node)
|
||||
header_width += fprintf(fp, " / ");
|
||||
else {
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
first = false;
|
||||
first_node = false;
|
||||
|
||||
first_col = true;
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (!first_col)
|
||||
header_width += fprintf(fp, "+");
|
||||
first_col = false;
|
||||
|
||||
fmt->header(fmt, hpp, hists_to_evsel(hists));
|
||||
rtrim(hpp->buf);
|
||||
|
||||
header_width += fprintf(fp, "%s", ltrim(hpp->buf));
|
||||
}
|
||||
|
||||
fmt->header(fmt, hpp, hists_to_evsel(hists));
|
||||
rtrim(hpp->buf);
|
||||
|
||||
header_width += fprintf(fp, "%s", ltrim(hpp->buf));
|
||||
}
|
||||
|
||||
fprintf(fp, "\n# ");
|
||||
|
||||
/* preserve max indent depth for initial dots */
|
||||
print_hierarchy_indent(sep, nr_sort, dots, fp);
|
||||
print_hierarchy_indent(sep, indent, dots, fp);
|
||||
|
||||
first = true;
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
||||
break;
|
||||
/* the first hpp_list_node is for overhead columns */
|
||||
fmt_node = list_first_entry(&hists->hpp_formats,
|
||||
struct perf_hpp_list_node, list);
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
first_col = true;
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (!first_col)
|
||||
fprintf(fp, "%s", sep ?: "..");
|
||||
first_col = false;
|
||||
|
||||
width = fmt->width(fmt, hpp, hists_to_evsel(hists));
|
||||
fprintf(fp, "%.*s", width, dots);
|
||||
}
|
||||
|
||||
depth = 0;
|
||||
hists__for_each_format(hists, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt))
|
||||
continue;
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
|
||||
first_col = true;
|
||||
width = depth * HIERARCHY_INDENT;
|
||||
|
||||
width = fmt->width(fmt, hpp, hists_to_evsel(hists));
|
||||
width += depth * HIERARCHY_INDENT;
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
continue;
|
||||
|
||||
if (!first_col)
|
||||
width++; /* for '+' sign between column header */
|
||||
first_col = false;
|
||||
|
||||
width += fmt->width(fmt, hpp, hists_to_evsel(hists));
|
||||
}
|
||||
|
||||
if (width > header_width)
|
||||
header_width = width;
|
||||
@ -625,6 +627,7 @@ 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 perf_hpp_list_node *fmt_node;
|
||||
struct rb_node *nd;
|
||||
size_t ret = 0;
|
||||
unsigned int width;
|
||||
@ -654,6 +657,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
fprintf(fp, "# ");
|
||||
|
||||
if (symbol_conf.report_hierarchy) {
|
||||
list_for_each_entry(fmt_node, &hists->hpp_formats, list) {
|
||||
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
}
|
||||
nr_rows += print_hierarchy_header(hists, &dummy_hpp, sep, fp);
|
||||
goto print_entries;
|
||||
}
|
||||
@ -738,9 +745,9 @@ print_entries:
|
||||
* display "no entry >= x.xx%" message.
|
||||
*/
|
||||
if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
|
||||
int nr_sort = hists->nr_sort_keys;
|
||||
int depth = hists->nr_hpp_node + h->depth + 1;
|
||||
|
||||
print_hierarchy_indent(sep, nr_sort + h->depth + 1, spaces, fp);
|
||||
print_hierarchy_indent(sep, depth, spaces, fp);
|
||||
fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
|
||||
|
||||
if (max_rows && ++nr_rows >= max_rows)
|
||||
|
@ -1091,18 +1091,25 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he);
|
||||
static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
|
||||
struct rb_root *root,
|
||||
struct hist_entry *he,
|
||||
struct perf_hpp_fmt *fmt)
|
||||
struct perf_hpp_list *hpp_list)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct hist_entry *iter, *new;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int64_t cmp;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct hist_entry, rb_node_in);
|
||||
|
||||
cmp = fmt->collapse(fmt, iter, he);
|
||||
cmp = 0;
|
||||
perf_hpp_list__for_each_sort_list(hpp_list, fmt) {
|
||||
cmp = fmt->collapse(fmt, iter, he);
|
||||
if (cmp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cmp) {
|
||||
he_stat__add_stat(&iter->stat, &he->stat);
|
||||
return iter;
|
||||
@ -1121,24 +1128,26 @@ static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
|
||||
hists__apply_filters(hists, new);
|
||||
hists->nr_entries++;
|
||||
|
||||
/* save related format for output */
|
||||
new->fmt = fmt;
|
||||
/* save related format list for output */
|
||||
new->hpp_list = hpp_list;
|
||||
|
||||
/* some fields are now passed to 'new' */
|
||||
if (perf_hpp__is_trace_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
||||
he->trace_output = NULL;
|
||||
else
|
||||
new->trace_output = NULL;
|
||||
perf_hpp_list__for_each_sort_list(hpp_list, fmt) {
|
||||
if (perf_hpp__is_trace_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
||||
he->trace_output = NULL;
|
||||
else
|
||||
new->trace_output = NULL;
|
||||
|
||||
if (perf_hpp__is_srcline_entry(fmt))
|
||||
he->srcline = NULL;
|
||||
else
|
||||
new->srcline = NULL;
|
||||
if (perf_hpp__is_srcline_entry(fmt))
|
||||
he->srcline = NULL;
|
||||
else
|
||||
new->srcline = NULL;
|
||||
|
||||
if (perf_hpp__is_srcfile_entry(fmt))
|
||||
he->srcfile = NULL;
|
||||
else
|
||||
new->srcfile = NULL;
|
||||
if (perf_hpp__is_srcfile_entry(fmt))
|
||||
he->srcfile = NULL;
|
||||
else
|
||||
new->srcfile = NULL;
|
||||
}
|
||||
|
||||
rb_link_node(&new->rb_node_in, parent, p);
|
||||
rb_insert_color(&new->rb_node_in, root);
|
||||
@ -1149,21 +1158,19 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
|
||||
struct rb_root *root,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list_node *node;
|
||||
struct hist_entry *new_he = NULL;
|
||||
struct hist_entry *parent = NULL;
|
||||
int depth = 0;
|
||||
int ret = 0;
|
||||
|
||||
hists__for_each_sort_list(hists, fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt) &&
|
||||
!perf_hpp__is_dynamic_entry(fmt))
|
||||
continue;
|
||||
if (perf_hpp__should_skip(fmt, hists))
|
||||
list_for_each_entry(node, &hists->hpp_formats, list) {
|
||||
/* skip period (overhead) and elided columns */
|
||||
if (node->level == 0 || node->skip)
|
||||
continue;
|
||||
|
||||
/* insert copy of 'he' for each fmt into the hierarchy */
|
||||
new_he = hierarchy_insert_entry(hists, root, he, fmt);
|
||||
new_he = hierarchy_insert_entry(hists, root, he, &node->hpp);
|
||||
if (new_he == NULL) {
|
||||
ret = -1;
|
||||
break;
|
||||
@ -1358,6 +1365,7 @@ static void hierarchy_insert_output_entry(struct rb_root *root,
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct hist_entry *iter;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
@ -1373,8 +1381,10 @@ static void hierarchy_insert_output_entry(struct rb_root *root,
|
||||
rb_insert_color(&he->rb_node, root);
|
||||
|
||||
/* update column width of dynamic entry */
|
||||
if (perf_hpp__is_dynamic_entry(he->fmt))
|
||||
he->fmt->sort(he->fmt, he, NULL);
|
||||
perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) {
|
||||
if (perf_hpp__is_dynamic_entry(fmt))
|
||||
fmt->sort(fmt, he, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void hists__hierarchy_output_resort(struct hists *hists,
|
||||
@ -2105,6 +2115,7 @@ int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list)
|
||||
pthread_mutex_init(&hists->lock, NULL);
|
||||
hists->socket_filter = -1;
|
||||
hists->hpp_list = hpp_list;
|
||||
INIT_LIST_HEAD(&hists->hpp_formats);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2133,8 +2144,19 @@ static void hists__delete_all_entries(struct hists *hists)
|
||||
static void hists_evsel__exit(struct perf_evsel *evsel)
|
||||
{
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
struct perf_hpp_fmt *fmt, *pos;
|
||||
struct perf_hpp_list_node *node, *tmp;
|
||||
|
||||
hists__delete_all_entries(hists);
|
||||
|
||||
list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) {
|
||||
perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) {
|
||||
list_del(&fmt->list);
|
||||
free(fmt);
|
||||
}
|
||||
list_del(&node->list);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
static int hists_evsel__init(struct perf_evsel *evsel)
|
||||
|
@ -78,7 +78,9 @@ struct hists {
|
||||
u16 col_len[HISTC_NR_COLS];
|
||||
int socket_filter;
|
||||
struct perf_hpp_list *hpp_list;
|
||||
struct list_head hpp_formats;
|
||||
int nr_sort_keys;
|
||||
int nr_hpp_node;
|
||||
};
|
||||
|
||||
struct hist_entry_iter;
|
||||
@ -233,6 +235,7 @@ struct perf_hpp_fmt {
|
||||
int len;
|
||||
int user_len;
|
||||
int idx;
|
||||
int level;
|
||||
};
|
||||
|
||||
struct perf_hpp_list {
|
||||
@ -243,6 +246,13 @@ struct perf_hpp_list {
|
||||
|
||||
extern struct perf_hpp_list perf_hpp_list;
|
||||
|
||||
struct perf_hpp_list_node {
|
||||
struct list_head list;
|
||||
struct perf_hpp_list hpp;
|
||||
int level;
|
||||
bool skip;
|
||||
};
|
||||
|
||||
void perf_hpp_list__column_register(struct perf_hpp_list *list,
|
||||
struct perf_hpp_fmt *format);
|
||||
void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
|
||||
@ -298,6 +308,8 @@ void perf_hpp__cancel_cumulate(void);
|
||||
void perf_hpp__setup_output_field(struct perf_hpp_list *list);
|
||||
void perf_hpp__reset_output_field(struct perf_hpp_list *list);
|
||||
void perf_hpp__append_sort_keys(struct perf_hpp_list *list);
|
||||
int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
|
||||
struct perf_evlist *evlist);
|
||||
|
||||
|
||||
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
|
||||
@ -307,6 +319,8 @@ bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt);
|
||||
bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt);
|
||||
bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt);
|
||||
|
||||
struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt);
|
||||
|
||||
int hist_entry__filter(struct hist_entry *he, int type, const void *arg);
|
||||
|
||||
static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format,
|
||||
|
@ -98,6 +98,21 @@ jit_close(struct jit_buf_desc *jd)
|
||||
jd->in = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
jit_validate_events(struct perf_session *session)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
/*
|
||||
* check that all events use CLOCK_MONOTONIC
|
||||
*/
|
||||
evlist__for_each(session->evlist, evsel) {
|
||||
if (evsel->attr.use_clockid == 0 || evsel->attr.clockid != CLOCK_MONOTONIC)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
jit_open(struct jit_buf_desc *jd, const char *name)
|
||||
{
|
||||
@ -157,6 +172,14 @@ jit_open(struct jit_buf_desc *jd, const char *name)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* validate event is using the correct clockid
|
||||
*/
|
||||
if (jit_validate_events(jd->session)) {
|
||||
pr_err("error, jitted code must be sampled with perf record -k 1\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
bs = header.total_size - sizeof(header);
|
||||
|
||||
if (bs > bsz) {
|
||||
@ -647,7 +670,7 @@ jit_process(struct perf_session *session,
|
||||
* first, detect marker mmap (i.e., the jitdump mmap)
|
||||
*/
|
||||
if (jit_detect(filename, pid))
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
memset(&jd, 0, sizeof(jd));
|
||||
|
||||
@ -665,8 +688,10 @@ jit_process(struct perf_session *session,
|
||||
*nbytes = 0;
|
||||
|
||||
ret = jit_inject(&jd, filename);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
*nbytes = jd.bytes_written;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ do { \
|
||||
INIT_LIST_HEAD(list); \
|
||||
} while (0)
|
||||
|
||||
static inc_group_count(struct list_head *list,
|
||||
static void inc_group_count(struct list_head *list,
|
||||
struct parse_events_evlist *data)
|
||||
{
|
||||
/* Count groups only have more than 1 members */
|
||||
|
@ -240,14 +240,6 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_build_id_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_session *session __maybe_unused)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct ordered_events *oe __maybe_unused)
|
||||
@ -260,23 +252,6 @@ static int process_finished_round(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct ordered_events *oe);
|
||||
|
||||
static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_session *perf_session
|
||||
__maybe_unused)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_session *session __maybe_unused)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skipn(int fd, off_t n)
|
||||
{
|
||||
char buf[4096];
|
||||
@ -303,10 +278,9 @@ static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused,
|
||||
return event->auxtrace.size;
|
||||
}
|
||||
|
||||
static
|
||||
int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_session *session __maybe_unused)
|
||||
static int process_event_op2_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_session *session __maybe_unused)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
@ -410,7 +384,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
|
||||
if (tool->tracing_data == NULL)
|
||||
tool->tracing_data = process_event_synth_tracing_data_stub;
|
||||
if (tool->build_id == NULL)
|
||||
tool->build_id = process_build_id_stub;
|
||||
tool->build_id = process_event_op2_stub;
|
||||
if (tool->finished_round == NULL) {
|
||||
if (tool->ordered_events)
|
||||
tool->finished_round = process_finished_round;
|
||||
@ -418,13 +392,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
|
||||
tool->finished_round = process_finished_round_stub;
|
||||
}
|
||||
if (tool->id_index == NULL)
|
||||
tool->id_index = process_id_index_stub;
|
||||
tool->id_index = process_event_op2_stub;
|
||||
if (tool->auxtrace_info == NULL)
|
||||
tool->auxtrace_info = process_event_auxtrace_info_stub;
|
||||
tool->auxtrace_info = process_event_op2_stub;
|
||||
if (tool->auxtrace == NULL)
|
||||
tool->auxtrace = process_event_auxtrace_stub;
|
||||
if (tool->auxtrace_error == NULL)
|
||||
tool->auxtrace_error = process_event_auxtrace_error_stub;
|
||||
tool->auxtrace_error = process_event_op2_stub;
|
||||
if (tool->thread_map == NULL)
|
||||
tool->thread_map = process_event_thread_map_stub;
|
||||
if (tool->cpu_map == NULL)
|
||||
|
@ -1544,7 +1544,7 @@ static void hse_free(struct perf_hpp_fmt *fmt)
|
||||
}
|
||||
|
||||
static struct hpp_sort_entry *
|
||||
__sort_dimension__alloc_hpp(struct sort_dimension *sd)
|
||||
__sort_dimension__alloc_hpp(struct sort_dimension *sd, int level)
|
||||
{
|
||||
struct hpp_sort_entry *hse;
|
||||
|
||||
@ -1572,6 +1572,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
|
||||
hse->hpp.elide = false;
|
||||
hse->hpp.len = 0;
|
||||
hse->hpp.user_len = 0;
|
||||
hse->hpp.level = level;
|
||||
|
||||
return hse;
|
||||
}
|
||||
@ -1581,7 +1582,8 @@ static void hpp_free(struct perf_hpp_fmt *fmt)
|
||||
free(fmt);
|
||||
}
|
||||
|
||||
static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd)
|
||||
static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd,
|
||||
int level)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
@ -1590,6 +1592,7 @@ static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd)
|
||||
INIT_LIST_HEAD(&fmt->list);
|
||||
INIT_LIST_HEAD(&fmt->sort_list);
|
||||
fmt->free = hpp_free;
|
||||
fmt->level = level;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
@ -1611,9 +1614,9 @@ int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
|
||||
return hse->se->se_filter(he, type, arg);
|
||||
}
|
||||
|
||||
static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
|
||||
static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd, int level)
|
||||
{
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
|
||||
|
||||
if (hse == NULL)
|
||||
return -1;
|
||||
@ -1625,7 +1628,7 @@ static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
|
||||
static int __sort_dimension__add_hpp_output(struct perf_hpp_list *list,
|
||||
struct sort_dimension *sd)
|
||||
{
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
|
||||
struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0);
|
||||
|
||||
if (hse == NULL)
|
||||
return -1;
|
||||
@ -1868,7 +1871,8 @@ static void hde_free(struct perf_hpp_fmt *fmt)
|
||||
}
|
||||
|
||||
static struct hpp_dynamic_entry *
|
||||
__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field)
|
||||
__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field,
|
||||
int level)
|
||||
{
|
||||
struct hpp_dynamic_entry *hde;
|
||||
|
||||
@ -1899,10 +1903,39 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field)
|
||||
hde->hpp.elide = false;
|
||||
hde->hpp.len = 0;
|
||||
hde->hpp.user_len = 0;
|
||||
hde->hpp.level = level;
|
||||
|
||||
return hde;
|
||||
}
|
||||
|
||||
struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt)
|
||||
{
|
||||
struct perf_hpp_fmt *new_fmt = NULL;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt)) {
|
||||
struct hpp_sort_entry *hse, *new_hse;
|
||||
|
||||
hse = container_of(fmt, struct hpp_sort_entry, hpp);
|
||||
new_hse = memdup(hse, sizeof(*hse));
|
||||
if (new_hse)
|
||||
new_fmt = &new_hse->hpp;
|
||||
} else if (perf_hpp__is_dynamic_entry(fmt)) {
|
||||
struct hpp_dynamic_entry *hde, *new_hde;
|
||||
|
||||
hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
|
||||
new_hde = memdup(hde, sizeof(*hde));
|
||||
if (new_hde)
|
||||
new_fmt = &new_hde->hpp;
|
||||
} else {
|
||||
new_fmt = memdup(fmt, sizeof(*fmt));
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&new_fmt->list);
|
||||
INIT_LIST_HEAD(&new_fmt->sort_list);
|
||||
|
||||
return new_fmt;
|
||||
}
|
||||
|
||||
static int parse_field_name(char *str, char **event, char **field, char **opt)
|
||||
{
|
||||
char *event_name, *field_name, *opt_name;
|
||||
@ -1974,11 +2007,11 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam
|
||||
|
||||
static int __dynamic_dimension__add(struct perf_evsel *evsel,
|
||||
struct format_field *field,
|
||||
bool raw_trace)
|
||||
bool raw_trace, int level)
|
||||
{
|
||||
struct hpp_dynamic_entry *hde;
|
||||
|
||||
hde = __alloc_dynamic_entry(evsel, field);
|
||||
hde = __alloc_dynamic_entry(evsel, field, level);
|
||||
if (hde == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1988,14 +2021,14 @@ static int __dynamic_dimension__add(struct perf_evsel *evsel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace)
|
||||
static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level)
|
||||
{
|
||||
int ret;
|
||||
struct format_field *field;
|
||||
|
||||
field = evsel->tp_format->format.fields;
|
||||
while (field) {
|
||||
ret = __dynamic_dimension__add(evsel, field, raw_trace);
|
||||
ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -2004,7 +2037,8 @@ static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace)
|
||||
static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace,
|
||||
int level)
|
||||
{
|
||||
int ret;
|
||||
struct perf_evsel *evsel;
|
||||
@ -2013,7 +2047,7 @@ static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace)
|
||||
if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
continue;
|
||||
|
||||
ret = add_evsel_fields(evsel, raw_trace);
|
||||
ret = add_evsel_fields(evsel, raw_trace, level);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@ -2021,7 +2055,7 @@ static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace)
|
||||
}
|
||||
|
||||
static int add_all_matching_fields(struct perf_evlist *evlist,
|
||||
char *field_name, bool raw_trace)
|
||||
char *field_name, bool raw_trace, int level)
|
||||
{
|
||||
int ret = -ESRCH;
|
||||
struct perf_evsel *evsel;
|
||||
@ -2035,14 +2069,15 @@ static int add_all_matching_fields(struct perf_evlist *evlist,
|
||||
if (field == NULL)
|
||||
continue;
|
||||
|
||||
ret = __dynamic_dimension__add(evsel, field, raw_trace);
|
||||
ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok)
|
||||
static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok,
|
||||
int level)
|
||||
{
|
||||
char *str, *event_name, *field_name, *opt_name;
|
||||
struct perf_evsel *evsel;
|
||||
@ -2072,12 +2107,12 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok)
|
||||
}
|
||||
|
||||
if (!strcmp(field_name, "trace_fields")) {
|
||||
ret = add_all_dynamic_fields(evlist, raw_trace);
|
||||
ret = add_all_dynamic_fields(evlist, raw_trace, level);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (event_name == NULL) {
|
||||
ret = add_all_matching_fields(evlist, field_name, raw_trace);
|
||||
ret = add_all_matching_fields(evlist, field_name, raw_trace, level);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -2095,7 +2130,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok)
|
||||
}
|
||||
|
||||
if (!strcmp(field_name, "*")) {
|
||||
ret = add_evsel_fields(evsel, raw_trace);
|
||||
ret = add_evsel_fields(evsel, raw_trace, level);
|
||||
} else {
|
||||
field = pevent_find_any_field(evsel->tp_format, field_name);
|
||||
if (field == NULL) {
|
||||
@ -2104,7 +2139,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = __dynamic_dimension__add(evsel, field, raw_trace);
|
||||
ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -2112,12 +2147,12 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __sort_dimension__add(struct sort_dimension *sd)
|
||||
static int __sort_dimension__add(struct sort_dimension *sd, int level)
|
||||
{
|
||||
if (sd->taken)
|
||||
return 0;
|
||||
|
||||
if (__sort_dimension__add_hpp_sort(sd) < 0)
|
||||
if (__sort_dimension__add_hpp_sort(sd, level) < 0)
|
||||
return -1;
|
||||
|
||||
if (sd->entry->se_collapse)
|
||||
@ -2128,14 +2163,14 @@ static int __sort_dimension__add(struct sort_dimension *sd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp_dimension__add(struct hpp_dimension *hd)
|
||||
static int __hpp_dimension__add(struct hpp_dimension *hd, int level)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
if (hd->taken)
|
||||
return 0;
|
||||
|
||||
fmt = __hpp_dimension__alloc_hpp(hd);
|
||||
fmt = __hpp_dimension__alloc_hpp(hd, level);
|
||||
if (!fmt)
|
||||
return -1;
|
||||
|
||||
@ -2165,7 +2200,7 @@ static int __hpp_dimension__add_output(struct perf_hpp_list *list,
|
||||
if (hd->taken)
|
||||
return 0;
|
||||
|
||||
fmt = __hpp_dimension__alloc_hpp(hd);
|
||||
fmt = __hpp_dimension__alloc_hpp(hd, 0);
|
||||
if (!fmt)
|
||||
return -1;
|
||||
|
||||
@ -2180,8 +2215,8 @@ int hpp_dimension__add_output(unsigned col)
|
||||
return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]);
|
||||
}
|
||||
|
||||
static int sort_dimension__add(const char *tok,
|
||||
struct perf_evlist *evlist __maybe_unused)
|
||||
static int sort_dimension__add(const char *tok, struct perf_evlist *evlist,
|
||||
int level)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@ -2220,7 +2255,7 @@ static int sort_dimension__add(const char *tok,
|
||||
sort__has_thread = 1;
|
||||
}
|
||||
|
||||
return __sort_dimension__add(sd);
|
||||
return __sort_dimension__add(sd, level);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
|
||||
@ -2229,7 +2264,7 @@ static int sort_dimension__add(const char *tok,
|
||||
if (strncasecmp(tok, hd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
return __hpp_dimension__add(hd);
|
||||
return __hpp_dimension__add(hd, level);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
|
||||
@ -2244,7 +2279,7 @@ static 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);
|
||||
__sort_dimension__add(sd, level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2260,11 +2295,11 @@ static int sort_dimension__add(const char *tok,
|
||||
if (sd->entry == &sort_mem_daddr_sym)
|
||||
sort__has_sym = 1;
|
||||
|
||||
__sort_dimension__add(sd);
|
||||
__sort_dimension__add(sd, level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!add_dynamic_entry(evlist, tok))
|
||||
if (!add_dynamic_entry(evlist, tok, level))
|
||||
return 0;
|
||||
|
||||
return -ESRCH;
|
||||
@ -2274,18 +2309,41 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist)
|
||||
{
|
||||
char *tmp, *tok;
|
||||
int ret = 0;
|
||||
int level = 0;
|
||||
int next_level = 1;
|
||||
bool in_group = false;
|
||||
|
||||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
ret = sort_dimension__add(tok, evlist);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --sort key: `%s'", tok);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --sort key: `%s'", tok);
|
||||
break;
|
||||
do {
|
||||
tok = str;
|
||||
tmp = strpbrk(str, "{}, ");
|
||||
if (tmp) {
|
||||
if (in_group)
|
||||
next_level = level;
|
||||
else
|
||||
next_level = level + 1;
|
||||
|
||||
if (*tmp == '{')
|
||||
in_group = true;
|
||||
else if (*tmp == '}')
|
||||
in_group = false;
|
||||
|
||||
*tmp = '\0';
|
||||
str = tmp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*tok) {
|
||||
ret = sort_dimension__add(tok, evlist, level);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --sort key: `%s'", tok);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --sort key: `%s'", tok);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
level = next_level;
|
||||
} while (tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2667,7 +2725,7 @@ int setup_sorting(struct perf_evlist *evlist)
|
||||
return err;
|
||||
|
||||
if (parent_pattern != default_parent_pattern) {
|
||||
err = sort_dimension__add("parent", evlist);
|
||||
err = sort_dimension__add("parent", evlist, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -2692,6 +2750,10 @@ int setup_sorting(struct perf_evlist *evlist)
|
||||
/* and then copy output fields to sort keys */
|
||||
perf_hpp__append_sort_keys(&perf_hpp_list);
|
||||
|
||||
/* setup hists-specific output fields */
|
||||
if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,7 @@ struct hist_entry {
|
||||
u32 raw_size;
|
||||
void *trace_output;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct perf_hpp_list *hpp_list;
|
||||
struct hist_entry *parent_he;
|
||||
union {
|
||||
/* this is for hierarchical entry structure */
|
||||
|
@ -19,7 +19,7 @@ u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
|
||||
u64 quot, rem;
|
||||
|
||||
quot = cyc >> tc->time_shift;
|
||||
rem = cyc & ((1 << tc->time_shift) - 1);
|
||||
rem = cyc & (((u64)1 << tc->time_shift) - 1);
|
||||
return tc->time_zero + quot * tc->time_mult +
|
||||
((rem * tc->time_mult) >> tc->time_shift);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user