diff options
Diffstat (limited to 'tools/perf')
-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"); |