diff options
Diffstat (limited to 'tools/perf/ui/stdio/hist.c')
| -rw-r--r-- | tools/perf/ui/stdio/hist.c | 155 |
1 files changed, 124 insertions, 31 deletions
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 74c4ae1f0a05..8c4c8925df2c 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -1,21 +1,33 @@ // SPDX-License-Identifier: GPL-2.0 +#include <limits.h> #include <stdio.h> +#include <stdlib.h> #include <linux/string.h> -#include "../../util/util.h" +#include "../../util/callchain.h" +#include "../../util/debug.h" +#include "../../util/event.h" #include "../../util/hist.h" +#include "../../util/map.h" +#include "../../util/maps.h" +#include "../../util/symbol.h" #include "../../util/sort.h" #include "../../util/evsel.h" #include "../../util/srcline.h" #include "../../util/string2.h" #include "../../util/thread.h" -#include "../../util/sane_ctype.h" +#include "../../util/block-info.h" +#include <linux/ctype.h> +#include <linux/zalloc.h> static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) { int i; int ret = fprintf(fp, " "); + if (left_margin > USHRT_MAX) + left_margin = USHRT_MAX; + for (i = 0; i < left_margin; i++) ret += fprintf(fp, " "); @@ -512,7 +524,7 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he, * dynamic entries are right-aligned but we want left-aligned * in the hierarchy mode */ - printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf)); + printed += fprintf(fp, "%s%s", sep ?: " ", skip_spaces(buf)); } printed += putc('\n', fp); @@ -527,6 +539,49 @@ out: return printed; } +static int hist_entry__block_fprintf(struct hist_entry *he, + char *bf, size_t size, + FILE *fp) +{ + struct block_hist *bh = container_of(he, struct block_hist, he); + int ret = 0; + + for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) { + struct perf_hpp hpp = { + .buf = bf, + .size = size, + .skip = false, + }; + + bh->block_idx = i; + hist_entry__snprintf(he, &hpp); + + if (!hpp.skip) + ret += fprintf(fp, "%s\n", bf); + } + + return ret; +} + +static int hist_entry__individual_block_fprintf(struct hist_entry *he, + char *bf, size_t size, + FILE *fp) +{ + int ret = 0; + + struct perf_hpp hpp = { + .buf = bf, + .size = size, + .skip = false, + }; + + hist_entry__snprintf(he, &hpp); + if (!hpp.skip) + ret += fprintf(fp, "%s\n", bf); + + return ret; +} + static int hist_entry__fprintf(struct hist_entry *he, size_t size, char *bf, size_t bfsz, FILE *fp, bool ignore_callchains) @@ -546,6 +601,12 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, if (symbol_conf.report_hierarchy) return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp); + if (symbol_conf.report_block) + return hist_entry__block_fprintf(he, bf, size, fp); + + if (symbol_conf.report_individual_block) + return hist_entry__individual_block_fprintf(he, bf, size, fp); + hist_entry__snprintf(he, &hpp); ret = fprintf(fp, "%s\n", bf); @@ -562,10 +623,14 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, static int print_hierarchy_indent(const char *sep, int indent, const char *line, FILE *fp) { + int width; + if (sep != NULL || indent < 2) return 0; - return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line); + width = (indent - 2) * HIERARCHY_INDENT; + + return fprintf(fp, "%-*.*s", width, width, line); } static int hists__fprintf_hierarchy_headers(struct hists *hists, @@ -578,45 +643,58 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists, unsigned header_width = 0; struct perf_hpp_fmt *fmt; struct perf_hpp_list_node *fmt_node; + struct perf_hpp_list *hpp_list = hists->hpp_list; const char *sep = symbol_conf.field_sep; indent = hists->nr_hpp_node; - /* preserve max indent depth for column headers */ - print_hierarchy_indent(sep, indent, spaces, fp); - /* 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, 0, NULL); - fprintf(fp, "%s%s", hpp->buf, sep ?: " "); - } + for (int line = 0; line < hpp_list->nr_header_lines; line++) { + /* first # is displayed one level up */ + if (line) + fprintf(fp, "# "); - /* combine sort headers with ' / ' */ - first_node = true; - list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { - if (!first_node) - header_width += fprintf(fp, " / "); - first_node = false; + /* preserve max indent depth for column headers */ + print_hierarchy_indent(sep, indent, " ", fp); - 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, line, NULL); + fprintf(fp, "%s%s", hpp->buf, sep ?: " "); + } - if (!first_col) - header_width += fprintf(fp, "+"); - first_col = false; + if (line < hpp_list->nr_header_lines - 1) + goto next_line; + + /* combine sort headers with ' / ' */ + first_node = true; + list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { + if (!first_node) + header_width += fprintf(fp, " / "); + first_node = false; - fmt->header(fmt, hpp, hists, 0, NULL); + first_col = true; + perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { + if (perf_hpp__should_skip(fmt, hists)) + continue; - header_width += fprintf(fp, "%s", trim(hpp->buf)); + if (!first_col) + header_width += fprintf(fp, "+"); + first_col = false; + + fmt->header(fmt, hpp, hists, line, NULL); + + header_width += fprintf(fp, "%s", strim(hpp->buf)); + } } + +next_line: + fprintf(fp, "\n"); } - fprintf(fp, "\n# "); + fprintf(fp, "# "); /* preserve max indent depth for initial dots */ print_hierarchy_indent(sep, indent, dots, fp); @@ -788,14 +866,19 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, indent = hists__overhead_width(hists) + 4; - for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) { + for (nd = rb_first_cached(&hists->entries); nd; + nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); float percent; if (h->filtered) continue; - percent = hist_entry__get_percent_limit(h); + if (symbol_conf.report_individual_block) + percent = block_info__total_cycles_percent(h); + else + percent = hist_entry__get_percent_limit(h); + if (percent < min_pcnt) continue; @@ -811,7 +894,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) { int depth = hists->nr_hpp_node + h->depth + 1; - print_hierarchy_indent(sep, depth, spaces, fp); + print_hierarchy_indent(sep, depth, " ", fp); fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt); if (max_rows && ++nr_rows >= max_rows) @@ -819,7 +902,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, } if (h->ms.map == NULL && verbose > 1) { - map_groups__fprintf(h->thread->mg, fp); + maps__fprintf(thread__maps(h->thread), fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } @@ -835,6 +918,7 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp) { int i; size_t ret = 0; + u32 total = stats->nr_events[0]; for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { const char *name; @@ -842,8 +926,17 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp) name = perf_event__name(i); if (!strcmp(name, "UNKNOWN")) continue; + if (symbol_conf.skip_empty && !stats->nr_events[i]) + continue; - ret += fprintf(fp, "%16s events: %10d\n", name, stats->nr_events[i]); + if (i && total) { + ret += fprintf(fp, "%20s events: %10d (%4.1f%%)\n", + name, stats->nr_events[i], + 100.0 * stats->nr_events[i] / total); + } else { + ret += fprintf(fp, "%20s events: %10d\n", + name, stats->nr_events[i]); + } } return ret; |
