diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
| -rw-r--r-- | tools/perf/builtin-report.c | 115 |
1 files changed, 100 insertions, 15 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 294b4cf105f2..f815de25d0fc 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -45,29 +45,71 @@ static char *pretty_printing_style = default_pretty_printing_style; | |||
| 45 | 45 | ||
| 46 | static char callchain_default_opt[] = "fractal,0.5"; | 46 | static char callchain_default_opt[] = "fractal,0.5"; |
| 47 | 47 | ||
| 48 | static struct event_stat_id *get_stats(struct perf_session *self, | ||
| 49 | u64 event_stream, u32 type, u64 config) | ||
| 50 | { | ||
| 51 | struct rb_node **p = &self->stats_by_id.rb_node; | ||
| 52 | struct rb_node *parent = NULL; | ||
| 53 | struct event_stat_id *iter, *new; | ||
| 54 | |||
| 55 | while (*p != NULL) { | ||
| 56 | parent = *p; | ||
| 57 | iter = rb_entry(parent, struct event_stat_id, rb_node); | ||
| 58 | if (iter->config == config) | ||
| 59 | return iter; | ||
| 60 | |||
| 61 | |||
| 62 | if (config > iter->config) | ||
| 63 | p = &(*p)->rb_right; | ||
| 64 | else | ||
| 65 | p = &(*p)->rb_left; | ||
| 66 | } | ||
| 67 | |||
| 68 | new = malloc(sizeof(struct event_stat_id)); | ||
| 69 | if (new == NULL) | ||
| 70 | return NULL; | ||
| 71 | memset(new, 0, sizeof(struct event_stat_id)); | ||
| 72 | new->event_stream = event_stream; | ||
| 73 | new->config = config; | ||
| 74 | new->type = type; | ||
| 75 | rb_link_node(&new->rb_node, parent, p); | ||
| 76 | rb_insert_color(&new->rb_node, &self->stats_by_id); | ||
| 77 | return new; | ||
| 78 | } | ||
| 79 | |||
| 48 | static int perf_session__add_hist_entry(struct perf_session *self, | 80 | static int perf_session__add_hist_entry(struct perf_session *self, |
| 49 | struct addr_location *al, | 81 | struct addr_location *al, |
| 50 | struct ip_callchain *chain, u64 count) | 82 | struct sample_data *data) |
| 51 | { | 83 | { |
| 52 | struct symbol **syms = NULL, *parent = NULL; | 84 | struct symbol **syms = NULL, *parent = NULL; |
| 53 | bool hit; | 85 | bool hit; |
| 54 | struct hist_entry *he; | 86 | struct hist_entry *he; |
| 87 | struct event_stat_id *stats; | ||
| 88 | struct perf_event_attr *attr; | ||
| 55 | 89 | ||
| 56 | if ((sort__has_parent || symbol_conf.use_callchain) && chain) | 90 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) |
| 57 | syms = perf_session__resolve_callchain(self, al->thread, | 91 | syms = perf_session__resolve_callchain(self, al->thread, |
| 58 | chain, &parent); | 92 | data->callchain, &parent); |
| 59 | he = __perf_session__add_hist_entry(&self->hists, al, parent, | 93 | |
| 60 | count, &hit); | 94 | attr = perf_header__find_attr(data->id, &self->header); |
| 95 | if (attr) | ||
| 96 | stats = get_stats(self, data->id, attr->type, attr->config); | ||
| 97 | else | ||
| 98 | stats = get_stats(self, data->id, 0, 0); | ||
| 99 | if (stats == NULL) | ||
| 100 | return -ENOMEM; | ||
| 101 | he = __perf_session__add_hist_entry(&stats->hists, al, parent, | ||
| 102 | data->period, &hit); | ||
| 61 | if (he == NULL) | 103 | if (he == NULL) |
| 62 | return -ENOMEM; | 104 | return -ENOMEM; |
| 63 | 105 | ||
| 64 | if (hit) | 106 | if (hit) |
| 65 | he->count += count; | 107 | he->count += data->period; |
| 66 | 108 | ||
| 67 | if (symbol_conf.use_callchain) { | 109 | if (symbol_conf.use_callchain) { |
| 68 | if (!hit) | 110 | if (!hit) |
| 69 | callchain_init(&he->callchain); | 111 | callchain_init(&he->callchain); |
| 70 | append_chain(&he->callchain, chain, syms); | 112 | append_chain(&he->callchain, data->callchain, syms); |
| 71 | free(syms); | 113 | free(syms); |
| 72 | } | 114 | } |
| 73 | 115 | ||
| @@ -87,10 +129,30 @@ static int validate_chain(struct ip_callchain *chain, event_t *event) | |||
| 87 | return 0; | 129 | return 0; |
| 88 | } | 130 | } |
| 89 | 131 | ||
| 132 | static int add_event_total(struct perf_session *session, | ||
| 133 | struct sample_data *data, | ||
| 134 | struct perf_event_attr *attr) | ||
| 135 | { | ||
| 136 | struct event_stat_id *stats; | ||
| 137 | |||
| 138 | if (attr) | ||
| 139 | stats = get_stats(session, data->id, attr->type, attr->config); | ||
| 140 | else | ||
| 141 | stats = get_stats(session, data->id, 0, 0); | ||
| 142 | |||
| 143 | if (!stats) | ||
| 144 | return -ENOMEM; | ||
| 145 | |||
| 146 | stats->stats.total += data->period; | ||
| 147 | session->events_stats.total += data->period; | ||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 90 | static int process_sample_event(event_t *event, struct perf_session *session) | 151 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 91 | { | 152 | { |
| 92 | struct sample_data data = { .period = 1, }; | 153 | struct sample_data data = { .period = 1, }; |
| 93 | struct addr_location al; | 154 | struct addr_location al; |
| 155 | struct perf_event_attr *attr; | ||
| 94 | 156 | ||
| 95 | event__parse_sample(event, session->sample_type, &data); | 157 | event__parse_sample(event, session->sample_type, &data); |
| 96 | 158 | ||
| @@ -124,12 +186,18 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
| 124 | if (al.filtered || (hide_unresolved && al.sym == NULL)) | 186 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
| 125 | return 0; | 187 | return 0; |
| 126 | 188 | ||
| 127 | if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { | 189 | if (perf_session__add_hist_entry(session, &al, &data)) { |
| 128 | pr_debug("problem incrementing symbol count, skipping event\n"); | 190 | pr_debug("problem incrementing symbol count, skipping event\n"); |
| 129 | return -1; | 191 | return -1; |
| 130 | } | 192 | } |
| 131 | 193 | ||
| 132 | session->events_stats.total += data.period; | 194 | attr = perf_header__find_attr(data.id, &session->header); |
| 195 | |||
| 196 | if (add_event_total(session, &data, attr)) { | ||
| 197 | pr_debug("problem adding event count\n"); | ||
| 198 | return -1; | ||
| 199 | } | ||
| 200 | |||
| 133 | return 0; | 201 | return 0; |
| 134 | } | 202 | } |
| 135 | 203 | ||
| @@ -198,6 +266,7 @@ static int __cmd_report(void) | |||
| 198 | { | 266 | { |
| 199 | int ret = -EINVAL; | 267 | int ret = -EINVAL; |
| 200 | struct perf_session *session; | 268 | struct perf_session *session; |
| 269 | struct rb_node *next; | ||
| 201 | 270 | ||
| 202 | session = perf_session__new(input_name, O_RDONLY, force); | 271 | session = perf_session__new(input_name, O_RDONLY, force); |
| 203 | if (session == NULL) | 272 | if (session == NULL) |
| @@ -225,12 +294,28 @@ static int __cmd_report(void) | |||
| 225 | if (verbose > 2) | 294 | if (verbose > 2) |
| 226 | dsos__fprintf(stdout); | 295 | dsos__fprintf(stdout); |
| 227 | 296 | ||
| 228 | perf_session__collapse_resort(&session->hists); | 297 | next = rb_first(&session->stats_by_id); |
| 229 | perf_session__output_resort(&session->hists, | 298 | while (next) { |
| 230 | session->events_stats.total); | 299 | struct event_stat_id *stats; |
| 231 | fprintf(stdout, "# Samples: %Ld\n#\n", session->events_stats.total); | 300 | |
| 232 | perf_session__fprintf_hists(&session->hists, NULL, false, stdout, | 301 | stats = rb_entry(next, struct event_stat_id, rb_node); |
| 233 | session->events_stats.total); | 302 | perf_session__collapse_resort(&stats->hists); |
| 303 | perf_session__output_resort(&stats->hists, stats->stats.total); | ||
| 304 | if (rb_first(&session->stats_by_id) == | ||
| 305 | rb_last(&session->stats_by_id)) | ||
| 306 | fprintf(stdout, "# Samples: %Ld\n#\n", | ||
| 307 | stats->stats.total); | ||
| 308 | else | ||
| 309 | fprintf(stdout, "# Samples: %Ld %s\n#\n", | ||
| 310 | stats->stats.total, | ||
| 311 | __event_name(stats->type, stats->config)); | ||
| 312 | |||
| 313 | perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, | ||
| 314 | stats->stats.total); | ||
| 315 | fprintf(stdout, "\n\n"); | ||
| 316 | next = rb_next(&stats->rb_node); | ||
| 317 | } | ||
| 318 | |||
| 234 | if (sort_order == default_sort_order && | 319 | if (sort_order == default_sort_order && |
| 235 | parent_pattern == default_parent_pattern) | 320 | parent_pattern == default_parent_pattern) |
| 236 | fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); | 321 | fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); |
