Commit b9461ba8 authored by Ingo Molnar's avatar Ingo Molnar

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 00966852 58ecd33b
......@@ -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 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
}
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 (!first_col) {
ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
if (advance_hpp_check(&dummy_hpp, ret))
break;
}
first_col = false;
if (start != dummy_hpp.buf)
memmove(dummy_hpp.buf, start, ret + 1);
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
dummy_hpp.buf[ret] = '\0';
rtrim(dummy_hpp.buf);
if (advance_hpp_check(&dummy_hpp, ret))
break;
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;
if (fmt->color)
ret = fmt->color(fmt, hpp, he);
else
ret = fmt->entry(fmt, hpp, he);
gtk_tree_store_set(store, &iter, col_idx, rtrim(hpp->buf), -1);
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;
first_col = true;
perf_hpp_list__for_each_format(&fmt_node->hpp ,fmt) {
if (perf_hpp__should_skip(fmt, hists))
continue;
fmt->header(fmt, &hpp, hists_to_evsel(hists));
strcat(buf, rtrim(hpp.buf));
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;
/*
* 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);
/*
* dynamic entries are right-aligned but we want left-aligned
* in the hierarchy mode
*/
printed += fprintf(fp, "%s\n", ltrim(buf));
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.
*/
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%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;
fmt->header(fmt, hpp, hists_to_evsel(hists));
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)
header_width += fprintf(fp, "+");
first_col = false;
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 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
* 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 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok)
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;
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;
}
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;
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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment