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: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Frederic 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.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Родитель
b01c3a0010
Коммит
6d4818c524
|
@ -607,7 +607,7 @@ static void init_rem_hits(void)
|
|||
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,
|
||||
int depth_mask, int left_margin)
|
||||
{
|
||||
|
@ -615,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
|
|||
struct callchain_node *child;
|
||||
struct callchain_list *chain;
|
||||
int new_depth_mask = depth_mask;
|
||||
u64 new_total;
|
||||
u64 remaining;
|
||||
size_t ret = 0;
|
||||
int i;
|
||||
uint entries_printed = 0;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = self->children_hit;
|
||||
else
|
||||
new_total = total_samples;
|
||||
remaining = total_samples;
|
||||
|
||||
remaining = new_total;
|
||||
|
||||
node = rb_first(&self->rb_root);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
u64 new_total;
|
||||
u64 cumul;
|
||||
|
||||
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,
|
|||
list_for_each_entry(chain, &child->val, list) {
|
||||
ret += ipchain__fprintf_graph(fp, chain, depth,
|
||||
new_depth_mask, i++,
|
||||
new_total,
|
||||
total_samples,
|
||||
cumul,
|
||||
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,
|
||||
new_depth_mask | (1 << depth),
|
||||
left_margin);
|
||||
|
@ -671,61 +672,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
|
|||
}
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL &&
|
||||
remaining && remaining != new_total) {
|
||||
remaining && remaining != total_samples) {
|
||||
|
||||
if (!rem_sq_bracket)
|
||||
return ret;
|
||||
|
||||
new_depth_mask &= ~(1 << (depth - 1));
|
||||
|
||||
ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
|
||||
new_depth_mask, 0, new_total,
|
||||
new_depth_mask, 0, total_samples,
|
||||
remaining, left_margin);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct callchain_node *cnode;
|
||||
struct callchain_list *chain;
|
||||
bool printed = false;
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
u32 entries_printed = 0;
|
||||
bool printed = false;
|
||||
struct rb_node *node;
|
||||
int i = 0;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(chain, &self->val, list) {
|
||||
if (!i++ && sort__first_dimension == SORT_SYM)
|
||||
continue;
|
||||
/*
|
||||
* 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)
|
||||
continue;
|
||||
if (!printed) {
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
ret += fprintf(fp, "|\n");
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
ret += fprintf(fp, "---");
|
||||
left_margin += 3;
|
||||
printed = true;
|
||||
} else
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
|
||||
if (!printed) {
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
ret += fprintf(fp, "|\n");
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
ret += fprintf(fp, "---");
|
||||
if (chain->ms.sym)
|
||||
ret += fprintf(fp, " %s\n", chain->ms.sym->name);
|
||||
else
|
||||
ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
|
||||
|
||||
left_margin += 3;
|
||||
printed = true;
|
||||
} else
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
|
||||
if (chain->ms.sym)
|
||||
ret += fprintf(fp, " %s\n", chain->ms.sym->name);
|
||||
else
|
||||
ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
|
||||
|
||||
if (++entries_printed == callchain_param.print_limit)
|
||||
break;
|
||||
if (++entries_printed == callchain_param.print_limit)
|
||||
break;
|
||||
}
|
||||
root = &cnode->rb_root;
|
||||
}
|
||||
|
||||
ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
|
||||
|
||||
return ret;
|
||||
return __callchain__fprintf_graph(fp, root, total_samples,
|
||||
1, 1, left_margin);
|
||||
}
|
||||
|
||||
static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
|
||||
u64 total_samples)
|
||||
static size_t __callchain__fprintf_flat(FILE *fp,
|
||||
struct callchain_node *self,
|
||||
u64 total_samples)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
size_t ret = 0;
|
||||
|
@ -733,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
|
|||
if (!self)
|
||||
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) {
|
||||
|
@ -749,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
|
||||
u64 total_samples, int left_margin,
|
||||
FILE *fp)
|
||||
static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
|
||||
u64 total_samples)
|
||||
{
|
||||
struct rb_node *rb_node;
|
||||
struct callchain_node *chain;
|
||||
size_t ret = 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) {
|
||||
double percent;
|
||||
|
||||
chain = rb_entry(rb_node, struct callchain_node, rb_node);
|
||||
percent = chain->hit * 100.0 / total_samples;
|
||||
switch (callchain_param.mode) {
|
||||
case CHAIN_FLAT:
|
||||
ret += percent_color_fprintf(fp, " %6.2f%%\n",
|
||||
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 = percent_color_fprintf(fp, " %6.2f%%\n", percent);
|
||||
ret += __callchain__fprintf_flat(fp, chain, total_samples);
|
||||
ret += fprintf(fp, "\n");
|
||||
if (++entries_printed == callchain_param.print_limit)
|
||||
break;
|
||||
|
||||
rb_node = rb_next(rb_node);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct rb_node *next = rb_first(&hists->entries);
|
||||
|
|
Загрузка…
Ссылка в новой задаче