|
@@ -23,6 +23,7 @@ struct hist_browser {
|
|
|
struct hists *hists;
|
|
|
struct hist_entry *he_selection;
|
|
|
struct map_symbol *selection;
|
|
|
+ int print_seq;
|
|
|
bool has_symbols;
|
|
|
};
|
|
|
|
|
@@ -800,6 +801,196 @@ do_offset:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
|
|
|
+ struct callchain_node *chain_node,
|
|
|
+ u64 total, int level,
|
|
|
+ FILE *fp)
|
|
|
+{
|
|
|
+ struct rb_node *node;
|
|
|
+ int offset = level * LEVEL_OFFSET_STEP;
|
|
|
+ u64 new_total, remaining;
|
|
|
+ int printed = 0;
|
|
|
+
|
|
|
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
|
|
|
+ new_total = chain_node->children_hit;
|
|
|
+ else
|
|
|
+ new_total = total;
|
|
|
+
|
|
|
+ remaining = new_total;
|
|
|
+ node = rb_first(&chain_node->rb_root);
|
|
|
+ while (node) {
|
|
|
+ struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
|
|
+ struct rb_node *next = rb_next(node);
|
|
|
+ u64 cumul = callchain_cumul_hits(child);
|
|
|
+ struct callchain_list *chain;
|
|
|
+ char folded_sign = ' ';
|
|
|
+ int first = true;
|
|
|
+ int extra_offset = 0;
|
|
|
+
|
|
|
+ remaining -= cumul;
|
|
|
+
|
|
|
+ list_for_each_entry(chain, &child->val, list) {
|
|
|
+ char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
|
|
|
+ const char *str;
|
|
|
+ bool was_first = first;
|
|
|
+
|
|
|
+ if (first)
|
|
|
+ first = false;
|
|
|
+ else
|
|
|
+ extra_offset = LEVEL_OFFSET_STEP;
|
|
|
+
|
|
|
+ folded_sign = callchain_list__folded(chain);
|
|
|
+
|
|
|
+ alloc_str = NULL;
|
|
|
+ str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
|
|
|
+ if (was_first) {
|
|
|
+ double percent = cumul * 100.0 / new_total;
|
|
|
+
|
|
|
+ if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
|
|
|
+ str = "Not enough memory!";
|
|
|
+ else
|
|
|
+ str = alloc_str;
|
|
|
+ }
|
|
|
+
|
|
|
+ printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
|
|
|
+ free(alloc_str);
|
|
|
+ if (folded_sign == '+')
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (folded_sign == '-') {
|
|
|
+ const int new_level = level + (extra_offset ? 2 : 1);
|
|
|
+ printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
|
|
|
+ new_level, fp);
|
|
|
+ }
|
|
|
+
|
|
|
+ node = next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return printed;
|
|
|
+}
|
|
|
+
|
|
|
+static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
|
|
|
+ struct callchain_node *node,
|
|
|
+ int level, FILE *fp)
|
|
|
+{
|
|
|
+ struct callchain_list *chain;
|
|
|
+ int offset = level * LEVEL_OFFSET_STEP;
|
|
|
+ char folded_sign = ' ';
|
|
|
+ int printed = 0;
|
|
|
+
|
|
|
+ list_for_each_entry(chain, &node->val, list) {
|
|
|
+ char ipstr[BITS_PER_LONG / 4 + 1], *s;
|
|
|
+
|
|
|
+ folded_sign = callchain_list__folded(chain);
|
|
|
+ s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
|
|
|
+ printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (folded_sign == '-')
|
|
|
+ printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
|
|
|
+ browser->hists->stats.total_period,
|
|
|
+ level + 1, fp);
|
|
|
+ return printed;
|
|
|
+}
|
|
|
+
|
|
|
+static int hist_browser__fprintf_callchain(struct hist_browser *browser,
|
|
|
+ struct rb_root *chain, int level, FILE *fp)
|
|
|
+{
|
|
|
+ struct rb_node *nd;
|
|
|
+ int printed = 0;
|
|
|
+
|
|
|
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
|
|
|
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
|
|
|
+
|
|
|
+ printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
|
|
|
+ }
|
|
|
+
|
|
|
+ return printed;
|
|
|
+}
|
|
|
+
|
|
|
+static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
|
|
+ struct hist_entry *he, FILE *fp)
|
|
|
+{
|
|
|
+ char s[8192];
|
|
|
+ double percent;
|
|
|
+ int printed = 0;
|
|
|
+ char folded_sign = ' ';
|
|
|
+
|
|
|
+ if (symbol_conf.use_callchain)
|
|
|
+ folded_sign = hist_entry__folded(he);
|
|
|
+
|
|
|
+ hist_entry__snprintf(he, s, sizeof(s), browser->hists);
|
|
|
+ percent = (he->period * 100.0) / browser->hists->stats.total_period;
|
|
|
+
|
|
|
+ if (symbol_conf.use_callchain)
|
|
|
+ printed += fprintf(fp, "%c ", folded_sign);
|
|
|
+
|
|
|
+ printed += fprintf(fp, " %5.2f%%", percent);
|
|
|
+
|
|
|
+ if (symbol_conf.show_nr_samples)
|
|
|
+ printed += fprintf(fp, " %11u", he->nr_events);
|
|
|
+
|
|
|
+ if (symbol_conf.show_total_period)
|
|
|
+ printed += fprintf(fp, " %12" PRIu64, he->period);
|
|
|
+
|
|
|
+ printed += fprintf(fp, "%s\n", rtrim(s));
|
|
|
+
|
|
|
+ if (folded_sign == '-')
|
|
|
+ printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
|
|
|
+
|
|
|
+ return printed;
|
|
|
+}
|
|
|
+
|
|
|
+static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
|
|
|
+{
|
|
|
+ struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
|
|
|
+ int printed = 0;
|
|
|
+
|
|
|
+ while (nd) {
|
|
|
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
+
|
|
|
+ printed += hist_browser__fprintf_entry(browser, h, fp);
|
|
|
+ nd = hists__filter_entries(rb_next(nd));
|
|
|
+ }
|
|
|
+
|
|
|
+ return printed;
|
|
|
+}
|
|
|
+
|
|
|
+static int hist_browser__dump(struct hist_browser *browser)
|
|
|
+{
|
|
|
+ char filename[64];
|
|
|
+ FILE *fp;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
|
|
|
+ if (access(filename, F_OK))
|
|
|
+ break;
|
|
|
+ /*
|
|
|
+ * XXX: Just an arbitrary lazy upper limit
|
|
|
+ */
|
|
|
+ if (++browser->print_seq == 8192) {
|
|
|
+ ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fp = fopen(filename, "w");
|
|
|
+ if (fp == NULL) {
|
|
|
+ char bf[64];
|
|
|
+ strerror_r(errno, bf, sizeof(bf));
|
|
|
+ ui_helpline__fpush("Couldn't write to %s: %s", filename, bf);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ++browser->print_seq;
|
|
|
+ hist_browser__fprintf(browser, fp);
|
|
|
+ fclose(fp);
|
|
|
+ ui_helpline__fpush("%s written!", filename);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct hist_browser *hist_browser__new(struct hists *hists)
|
|
|
{
|
|
|
struct hist_browser *browser = zalloc(sizeof(*browser));
|
|
@@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|
|
browser->selection->map->dso->annotate_warned)
|
|
|
continue;
|
|
|
goto do_annotate;
|
|
|
+ case 'P':
|
|
|
+ hist_browser__dump(browser);
|
|
|
+ continue;
|
|
|
case 'd':
|
|
|
goto zoom_dso;
|
|
|
case 't':
|
|
@@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|
|
"E Expand all callchains\n"
|
|
|
"d Zoom into current DSO\n"
|
|
|
"t Zoom into current Thread\n"
|
|
|
+ "P Print histograms to perf.hist.N\n"
|
|
|
"/ Filter symbol by name");
|
|
|
continue;
|
|
|
case K_ENTER:
|