|
@@ -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 = new_total;
|
|
|
+ remaining = total_samples;
|
|
|
|
|
|
- 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;
|
|
|
+ u32 entries_printed = 0;
|
|
|
bool printed = false;
|
|
|
+ struct rb_node *node;
|
|
|
int i = 0;
|
|
|
- int ret = 0;
|
|
|
- u32 entries_printed = 0;
|
|
|
-
|
|
|
- list_for_each_entry(chain, &self->val, list) {
|
|
|
- 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);
|
|
|
+ int ret;
|
|
|
|
|
|
- if (chain->ms.sym)
|
|
|
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
|
|
|
- else
|
|
|
- ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
|
|
|
+ /*
|
|
|
+ * 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 (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);
|