Commit 6d4818c5 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Arnaldo Carvalho de Melo

perf tools: Fix display of first level of callchains

The callchain stdio mode display was written using a sorted by symbol
report. In this mode we have only one callchain root per hist so we
forgot to handle cases where we have multiple callchain root, as in per
dso sorting for example.

Fix this by handling these roots like any other branch, with the hist as
the parent.

Before:

     1.97%  libpthread-2.12.1.so
            |
            --- __libc_write
                create_worker
                bench_sched_messaging
                cmd_bench
                run_builtin
                main
                __libc_start_main

            |
            --- __libc_read
                create_worker
                bench_sched_messaging
                cmd_bench
                run_builtin
                main
                __libc_start_main

After:

     1.97%  libpthread-2.12.1.so
            |
            |--36.97%-- __libc_write
            |          create_worker
            |          bench_sched_messaging
            |          cmd_bench
            |          run_builtin
            |          main
            |          __libc_start_main
            |
            |--31.47%-- __libc_read
            |          create_worker
            |          bench_sched_messaging
            |          cmd_bench
            |          run_builtin
            |          main
            |          __libc_start_main
           ...

Single roots keep their entry without percentage because they have
the same overhead than the hist they refer to. ie: 100% in fractal
mode and the percentage of the hist in graph mode:

     0.00%  [k] reschedule_interrupt
            |
            --- default_idle
                amd_e400_idle
                cpu_idle
                start_secondary
Reported-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1332526010-15400-1-git-send-email-fweisbec@gmail.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b01c3a00
...@@ -607,7 +607,7 @@ static void init_rem_hits(void) ...@@ -607,7 +607,7 @@ static void init_rem_hits(void)
rem_hits.ms.sym = rem_sq_bracket; rem_hits.ms.sym = rem_sq_bracket;
} }
static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
u64 total_samples, int depth, u64 total_samples, int depth,
int depth_mask, int left_margin) int depth_mask, int left_margin)
{ {
...@@ -615,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ...@@ -615,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
struct callchain_node *child; struct callchain_node *child;
struct callchain_list *chain; struct callchain_list *chain;
int new_depth_mask = depth_mask; int new_depth_mask = depth_mask;
u64 new_total;
u64 remaining; u64 remaining;
size_t ret = 0; size_t ret = 0;
int i; int i;
uint entries_printed = 0; uint entries_printed = 0;
if (callchain_param.mode == CHAIN_GRAPH_REL) remaining = total_samples;
new_total = self->children_hit;
else
new_total = total_samples;
remaining = new_total;
node = rb_first(&self->rb_root); node = rb_first(root);
while (node) { while (node) {
u64 new_total;
u64 cumul; u64 cumul;
child = rb_entry(node, struct callchain_node, rb_node); child = rb_entry(node, struct callchain_node, rb_node);
...@@ -657,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ...@@ -657,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
list_for_each_entry(chain, &child->val, list) { list_for_each_entry(chain, &child->val, list) {
ret += ipchain__fprintf_graph(fp, chain, depth, ret += ipchain__fprintf_graph(fp, chain, depth,
new_depth_mask, i++, new_depth_mask, i++,
new_total, total_samples,
cumul, cumul,
left_margin); left_margin);
} }
ret += __callchain__fprintf_graph(fp, child, new_total,
if (callchain_param.mode == CHAIN_GRAPH_REL)
new_total = child->children_hit;
else
new_total = total_samples;
ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
depth + 1, depth + 1,
new_depth_mask | (1 << depth), new_depth_mask | (1 << depth),
left_margin); left_margin);
...@@ -671,40 +672,52 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ...@@ -671,40 +672,52 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
} }
if (callchain_param.mode == CHAIN_GRAPH_REL && if (callchain_param.mode == CHAIN_GRAPH_REL &&
remaining && remaining != new_total) { remaining && remaining != total_samples) {
if (!rem_sq_bracket) if (!rem_sq_bracket)
return ret; return ret;
new_depth_mask &= ~(1 << (depth - 1)); new_depth_mask &= ~(1 << (depth - 1));
ret += ipchain__fprintf_graph(fp, &rem_hits, depth, ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
new_depth_mask, 0, new_total, new_depth_mask, 0, total_samples,
remaining, left_margin); remaining, left_margin);
} }
return ret; return ret;
} }
static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
u64 total_samples, int left_margin) u64 total_samples, int left_margin)
{ {
struct callchain_node *cnode;
struct callchain_list *chain; struct callchain_list *chain;
u32 entries_printed = 0;
bool printed = false; bool printed = false;
struct rb_node *node;
int i = 0; int i = 0;
int ret = 0; int ret;
u32 entries_printed = 0;
list_for_each_entry(chain, &self->val, list) { /*
* If have one single callchain root, don't bother printing
* its percentage (100 % in fractal mode and the same percentage
* than the hist in graph mode). This also avoid one level of column.
*/
node = rb_first(root);
if (node && !rb_next(node)) {
cnode = rb_entry(node, struct callchain_node, rb_node);
list_for_each_entry(chain, &cnode->val, list) {
/*
* If we sort by symbol, the first entry is the same than
* the symbol. No need to print it otherwise it appears as
* displayed twice.
*/
if (!i++ && sort__first_dimension == SORT_SYM) if (!i++ && sort__first_dimension == SORT_SYM)
continue; continue;
if (!printed) { if (!printed) {
ret += callchain__fprintf_left_margin(fp, left_margin); ret += callchain__fprintf_left_margin(fp, left_margin);
ret += fprintf(fp, "|\n"); ret += fprintf(fp, "|\n");
ret += callchain__fprintf_left_margin(fp, left_margin); ret += callchain__fprintf_left_margin(fp, left_margin);
ret += fprintf(fp, "---"); ret += fprintf(fp, "---");
left_margin += 3; left_margin += 3;
printed = true; printed = true;
} else } else
...@@ -718,13 +731,15 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ...@@ -718,13 +731,15 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
if (++entries_printed == callchain_param.print_limit) if (++entries_printed == callchain_param.print_limit)
break; break;
} }
root = &cnode->rb_root;
}
ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); return __callchain__fprintf_graph(fp, root, total_samples,
1, 1, left_margin);
return ret;
} }
static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, static size_t __callchain__fprintf_flat(FILE *fp,
struct callchain_node *self,
u64 total_samples) u64 total_samples)
{ {
struct callchain_list *chain; struct callchain_list *chain;
...@@ -733,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, ...@@ -733,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
if (!self) if (!self)
return 0; return 0;
ret += callchain__fprintf_flat(fp, self->parent, total_samples); ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
list_for_each_entry(chain, &self->val, list) { list_for_each_entry(chain, &self->val, list) {
...@@ -749,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, ...@@ -749,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
return ret; return ret;
} }
static size_t hist_entry_callchain__fprintf(struct hist_entry *he, static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
u64 total_samples, int left_margin, u64 total_samples)
FILE *fp)
{ {
struct rb_node *rb_node;
struct callchain_node *chain;
size_t ret = 0; size_t ret = 0;
u32 entries_printed = 0; u32 entries_printed = 0;
struct rb_node *rb_node;
struct callchain_node *chain;
rb_node = rb_first(&he->sorted_chain); rb_node = rb_first(self);
while (rb_node) { while (rb_node) {
double percent; double percent;
chain = rb_entry(rb_node, struct callchain_node, rb_node); chain = rb_entry(rb_node, struct callchain_node, rb_node);
percent = chain->hit * 100.0 / total_samples; percent = chain->hit * 100.0 / total_samples;
switch (callchain_param.mode) {
case CHAIN_FLAT: ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
ret += percent_color_fprintf(fp, " %6.2f%%\n", ret += __callchain__fprintf_flat(fp, chain, total_samples);
percent);
ret += callchain__fprintf_flat(fp, chain, total_samples);
break;
case CHAIN_GRAPH_ABS: /* Falldown */
case CHAIN_GRAPH_REL:
ret += callchain__fprintf_graph(fp, chain, total_samples,
left_margin);
case CHAIN_NONE:
default:
break;
}
ret += fprintf(fp, "\n"); ret += fprintf(fp, "\n");
if (++entries_printed == callchain_param.print_limit) if (++entries_printed == callchain_param.print_limit)
break; break;
rb_node = rb_next(rb_node); rb_node = rb_next(rb_node);
} }
return ret; return ret;
} }
static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
u64 total_samples, int left_margin,
FILE *fp)
{
switch (callchain_param.mode) {
case CHAIN_GRAPH_REL:
return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
left_margin);
break;
case CHAIN_GRAPH_ABS:
return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
left_margin);
break;
case CHAIN_FLAT:
return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
break;
case CHAIN_NONE:
break;
default:
pr_err("Bad callchain mode\n");
}
return 0;
}
void hists__output_recalc_col_len(struct hists *hists, int max_rows) void hists__output_recalc_col_len(struct hists *hists, int max_rows)
{ {
struct rb_node *next = rb_first(&hists->entries); struct rb_node *next = rb_first(&hists->entries);
......
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