aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2011-03-05 19:40:06 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2011-03-06 11:13:40 -0500
commite248de331a452f8771eda6ed4bb30d92c82df28b (patch)
tree7ef04743a7bf7a1da354a3b82536ef32504823d9 /tools
parent3d3b5e95997208067c963923db90ed1517565d14 (diff)
perf tools: Improve support for sessions with multiple events
By creating an perf_evlist out of the attributes in the perf.data file header, so that we can use evlists and evsels when reading recorded sessions in addition to when we record sessions. More work is needed to allow tools to allow the user to select which events are wanted when browsing sessions, be it just one or a subset of them, aggregated or showed at the same time but with different indications on the UI to allow seeing workloads thru different views at the same time. But the overall goal/trend is to more uniformly use evsels and evlists. Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <new-submission> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/builtin-annotate.c80
-rw-r--r--tools/perf/builtin-report.c160
-rw-r--r--tools/perf/util/evsel.h2
-rw-r--r--tools/perf/util/header.c31
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/hist.c6
-rw-r--r--tools/perf/util/hist.h12
-rw-r--r--tools/perf/util/session.c63
-rw-r--r--tools/perf/util/session.h16
-rw-r--r--tools/perf/util/ui/browsers/hists.c32
10 files changed, 208 insertions, 196 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 427182953fd7..695de4b5ae63 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -19,6 +19,8 @@
19#include "perf.h" 19#include "perf.h"
20#include "util/debug.h" 20#include "util/debug.h"
21 21
22#include "util/evlist.h"
23#include "util/evsel.h"
22#include "util/annotate.h" 24#include "util/annotate.h"
23#include "util/event.h" 25#include "util/event.h"
24#include "util/parse-options.h" 26#include "util/parse-options.h"
@@ -38,9 +40,13 @@ static bool print_line;
38 40
39static const char *sym_hist_filter; 41static const char *sym_hist_filter;
40 42
41static int hists__add_entry(struct hists *self, struct addr_location *al) 43static int perf_evlist__add_sample(struct perf_evlist *evlist,
44 struct perf_sample *sample,
45 struct addr_location *al)
42{ 46{
47 struct perf_evsel *evsel;
43 struct hist_entry *he; 48 struct hist_entry *he;
49 int ret;
44 50
45 if (sym_hist_filter != NULL && 51 if (sym_hist_filter != NULL &&
46 (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { 52 (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
@@ -53,23 +59,35 @@ static int hists__add_entry(struct hists *self, struct addr_location *al)
53 return 0; 59 return 0;
54 } 60 }
55 61
56 he = __hists__add_entry(self, al, NULL, 1); 62 evsel = perf_evlist__id2evsel(evlist, sample->id);
63 if (evsel == NULL) {
64 /*
65 * FIXME: Propagate this back, but at least we're in a builtin,
66 * where exit() is allowed. ;-)
67 */
68 ui__warning("Invalid %s file, contains samples with id not in "
69 "its header!\n", input_name);
70 exit_browser(0);
71 exit(1);
72 }
73
74 he = __hists__add_entry(&evsel->hists, al, NULL, 1);
57 if (he == NULL) 75 if (he == NULL)
58 return -ENOMEM; 76 return -ENOMEM;
59 77
78 ret = 0;
60 if (he->ms.sym != NULL) { 79 if (he->ms.sym != NULL) {
61 /*
62 * All aggregated on the first sym_hist.
63 */
64 struct annotation *notes = symbol__annotation(he->ms.sym); 80 struct annotation *notes = symbol__annotation(he->ms.sym);
65 if (notes->src == NULL && 81 if (notes->src == NULL &&
66 symbol__alloc_hist(he->ms.sym, 1) < 0) 82 symbol__alloc_hist(he->ms.sym, evlist->nr_entries) < 0)
67 return -ENOMEM; 83 return -ENOMEM;
68 84
69 return hist_entry__inc_addr_samples(he, 0, al->addr); 85 ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
70 } 86 }
71 87
72 return 0; 88 evsel->hists.stats.total_period += sample->period;
89 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
90 return ret;
73} 91}
74 92
75static int process_sample_event(union perf_event *event, 93static int process_sample_event(union perf_event *event,
@@ -85,7 +103,7 @@ static int process_sample_event(union perf_event *event,
85 return -1; 103 return -1;
86 } 104 }
87 105
88 if (!al.filtered && hists__add_entry(&session->hists, &al)) { 106 if (!al.filtered && perf_evlist__add_sample(session->evlist, sample, &al)) {
89 pr_warning("problem incrementing symbol count, " 107 pr_warning("problem incrementing symbol count, "
90 "skipping event\n"); 108 "skipping event\n");
91 return -1; 109 return -1;
@@ -100,7 +118,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx)
100 print_line, full_paths, 0, 0); 118 print_line, full_paths, 0, 0);
101} 119}
102 120
103static void hists__find_annotations(struct hists *self) 121static void hists__find_annotations(struct hists *self, int evidx)
104{ 122{
105 struct rb_node *nd = rb_first(&self->entries), *next; 123 struct rb_node *nd = rb_first(&self->entries), *next;
106 int key = KEY_RIGHT; 124 int key = KEY_RIGHT;
@@ -123,8 +141,7 @@ find_next:
123 } 141 }
124 142
125 if (use_browser > 0) { 143 if (use_browser > 0) {
126 /* For now all is aggregated on the first */ 144 key = hist_entry__tui_annotate(he, evidx);
127 key = hist_entry__tui_annotate(he, 0);
128 switch (key) { 145 switch (key) {
129 case KEY_RIGHT: 146 case KEY_RIGHT:
130 next = rb_next(nd); 147 next = rb_next(nd);
@@ -139,8 +156,7 @@ find_next:
139 if (next != NULL) 156 if (next != NULL)
140 nd = next; 157 nd = next;
141 } else { 158 } else {
142 /* For now all is aggregated on the first */ 159 hist_entry__tty_annotate(he, evidx);
143 hist_entry__tty_annotate(he, 0);
144 nd = rb_next(nd); 160 nd = rb_next(nd);
145 /* 161 /*
146 * Since we have a hist_entry per IP for the same 162 * Since we have a hist_entry per IP for the same
@@ -166,6 +182,8 @@ static int __cmd_annotate(void)
166{ 182{
167 int ret; 183 int ret;
168 struct perf_session *session; 184 struct perf_session *session;
185 struct perf_evsel *pos;
186 u64 total_nr_samples;
169 187
170 session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); 188 session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
171 if (session == NULL) 189 if (session == NULL)
@@ -186,12 +204,36 @@ static int __cmd_annotate(void)
186 if (verbose > 2) 204 if (verbose > 2)
187 perf_session__fprintf_dsos(session, stdout); 205 perf_session__fprintf_dsos(session, stdout);
188 206
189 hists__collapse_resort(&session->hists); 207 total_nr_samples = 0;
190 hists__output_resort(&session->hists); 208 list_for_each_entry(pos, &session->evlist->entries, node) {
191 hists__find_annotations(&session->hists); 209 struct hists *hists = &pos->hists;
192out_delete: 210 u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
193 perf_session__delete(session); 211
212 if (nr_samples > 0) {
213 total_nr_samples += nr_samples;
214 hists__collapse_resort(hists);
215 hists__output_resort(hists);
216 hists__find_annotations(hists, pos->idx);
217 }
218 }
194 219
220 if (total_nr_samples == 0) {
221 ui__warning("The %s file has no samples!\n", input_name);
222 goto out_delete;
223 }
224out_delete:
225 /*
226 * Speed up the exit process, for large files this can
227 * take quite a while.
228 *
229 * XXX Enable this when using valgrind or if we ever
230 * librarize this command.
231 *
232 * Also experiment with obstacks to see how much speed
233 * up we'll get here.
234 *
235 * perf_session__delete(session);
236 */
195 return ret; 237 return ret;
196} 238}
197 239
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index dddcc7ea2bec..1c399eae5f7b 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -21,6 +21,8 @@
21 21
22#include "perf.h" 22#include "perf.h"
23#include "util/debug.h" 23#include "util/debug.h"
24#include "util/evlist.h"
25#include "util/evsel.h"
24#include "util/header.h" 26#include "util/header.h"
25#include "util/session.h" 27#include "util/session.h"
26 28
@@ -46,39 +48,6 @@ static const char *pretty_printing_style = default_pretty_printing_style;
46static char callchain_default_opt[] = "fractal,0.5"; 48static char callchain_default_opt[] = "fractal,0.5";
47static symbol_filter_t annotate_init; 49static symbol_filter_t annotate_init;
48 50
49static struct hists *perf_session__hists_findnew(struct perf_session *self,
50 u64 event_stream, u32 type,
51 u64 config)
52{
53 struct rb_node **p = &self->hists_tree.rb_node;
54 struct rb_node *parent = NULL;
55 struct hists *iter, *new;
56
57 while (*p != NULL) {
58 parent = *p;
59 iter = rb_entry(parent, struct hists, rb_node);
60 if (iter->config == config)
61 return iter;
62
63
64 if (config > iter->config)
65 p = &(*p)->rb_right;
66 else
67 p = &(*p)->rb_left;
68 }
69
70 new = malloc(sizeof(struct hists));
71 if (new == NULL)
72 return NULL;
73 memset(new, 0, sizeof(struct hists));
74 new->event_stream = event_stream;
75 new->config = config;
76 new->type = type;
77 rb_link_node(&new->rb_node, parent, p);
78 rb_insert_color(&new->rb_node, &self->hists_tree);
79 return new;
80}
81
82static int perf_session__add_hist_entry(struct perf_session *session, 51static int perf_session__add_hist_entry(struct perf_session *session,
83 struct addr_location *al, 52 struct addr_location *al,
84 struct perf_sample *sample) 53 struct perf_sample *sample)
@@ -86,8 +55,7 @@ static int perf_session__add_hist_entry(struct perf_session *session,
86 struct symbol *parent = NULL; 55 struct symbol *parent = NULL;
87 int err = 0; 56 int err = 0;
88 struct hist_entry *he; 57 struct hist_entry *he;
89 struct hists *hists; 58 struct perf_evsel *evsel;
90 struct perf_event_attr *attr;
91 59
92 if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { 60 if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
93 err = perf_session__resolve_callchain(session, al->thread, 61 err = perf_session__resolve_callchain(session, al->thread,
@@ -96,15 +64,19 @@ static int perf_session__add_hist_entry(struct perf_session *session,
96 return err; 64 return err;
97 } 65 }
98 66
99 attr = perf_header__find_attr(sample->id, &session->header); 67 evsel = perf_evlist__id2evsel(session->evlist, sample->id);
100 if (attr) 68 if (evsel == NULL) {
101 hists = perf_session__hists_findnew(session, sample->id, attr->type, attr->config); 69 /*
102 else 70 * FIXME: Propagate this back, but at least we're in a builtin,
103 hists = perf_session__hists_findnew(session, sample->id, 0, 0); 71 * where exit() is allowed. ;-)
104 if (hists == NULL) 72 */
105 return -ENOMEM; 73 ui__warning("Invalid %s file, contains samples with id not in "
74 "its header!\n", input_name);
75 exit_browser(0);
76 exit(1);
77 }
106 78
107 he = __hists__add_entry(hists, al, parent, sample->period); 79 he = __hists__add_entry(&evsel->hists, al, parent, sample->period);
108 if (he == NULL) 80 if (he == NULL)
109 return -ENOMEM; 81 return -ENOMEM;
110 82
@@ -120,52 +92,30 @@ static int perf_session__add_hist_entry(struct perf_session *session,
120 * code will not use it. 92 * code will not use it.
121 */ 93 */
122 if (al->sym != NULL && use_browser > 0) { 94 if (al->sym != NULL && use_browser > 0) {
123 /*
124 * All aggregated on the first sym_hist.
125 */
126 struct annotation *notes = symbol__annotation(he->ms.sym); 95 struct annotation *notes = symbol__annotation(he->ms.sym);
96
97 assert(evsel != NULL);
98
99 err = -ENOMEM;
127 if (notes->src == NULL && 100 if (notes->src == NULL &&
128 symbol__alloc_hist(he->ms.sym, 1) < 0) 101 symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0)
129 err = -ENOMEM; 102 goto out;
130 else 103
131 err = hist_entry__inc_addr_samples(he, 0, al->addr); 104 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
132 } 105 }
133 106
107 evsel->hists.stats.total_period += sample->period;
108 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
109out:
134 return err; 110 return err;
135} 111}
136 112
137static int add_event_total(struct perf_session *session,
138 struct perf_sample *sample,
139 struct perf_event_attr *attr)
140{
141 struct hists *hists;
142
143 if (attr)
144 hists = perf_session__hists_findnew(session, sample->id,
145 attr->type, attr->config);
146 else
147 hists = perf_session__hists_findnew(session, sample->id, 0, 0);
148
149 if (!hists)
150 return -ENOMEM;
151
152 hists->stats.total_period += sample->period;
153 /*
154 * FIXME: add_event_total should be moved from here to
155 * perf_session__process_event so that the proper hist is passed to
156 * the event_op methods.
157 */
158 hists__inc_nr_events(hists, PERF_RECORD_SAMPLE);
159 session->hists.stats.total_period += sample->period;
160 return 0;
161}
162 113
163static int process_sample_event(union perf_event *event, 114static int process_sample_event(union perf_event *event,
164 struct perf_sample *sample, 115 struct perf_sample *sample,
165 struct perf_session *session) 116 struct perf_session *session)
166{ 117{
167 struct addr_location al; 118 struct addr_location al;
168 struct perf_event_attr *attr;
169 119
170 if (perf_event__preprocess_sample(event, session, &al, sample, 120 if (perf_event__preprocess_sample(event, session, &al, sample,
171 annotate_init) < 0) { 121 annotate_init) < 0) {
@@ -182,27 +132,17 @@ static int process_sample_event(union perf_event *event,
182 return -1; 132 return -1;
183 } 133 }
184 134
185 attr = perf_header__find_attr(sample->id, &session->header);
186
187 if (add_event_total(session, sample, attr)) {
188 pr_debug("problem adding event period\n");
189 return -1;
190 }
191
192 return 0; 135 return 0;
193} 136}
194 137
195static int process_read_event(union perf_event *event, 138static int process_read_event(union perf_event *event,
196 struct perf_sample *sample __used, 139 struct perf_sample *sample __used,
197 struct perf_session *session __used) 140 struct perf_session *session)
198{ 141{
199 struct perf_event_attr *attr; 142 struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist,
200 143 event->read.id);
201 attr = perf_header__find_attr(event->read.id, &session->header);
202
203 if (show_threads) { 144 if (show_threads) {
204 const char *name = attr ? __event_name(attr->type, attr->config) 145 const char *name = evsel ? event_name(evsel) : "unknown";
205 : "unknown";
206 perf_read_values_add_value(&show_threads_values, 146 perf_read_values_add_value(&show_threads_values,
207 event->read.pid, event->read.tid, 147 event->read.pid, event->read.tid,
208 event->read.id, 148 event->read.id,
@@ -211,7 +151,7 @@ static int process_read_event(union perf_event *event,
211 } 151 }
212 152
213 dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, 153 dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
214 attr ? __event_name(attr->type, attr->config) : "FAIL", 154 evsel ? event_name(evsel) : "FAIL",
215 event->read.value); 155 event->read.value);
216 156
217 return 0; 157 return 0;
@@ -282,21 +222,20 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
282 return ret + fprintf(fp, "\n#\n"); 222 return ret + fprintf(fp, "\n#\n");
283} 223}
284 224
285static int hists__tty_browse_tree(struct rb_root *tree, const char *help) 225static int hists__tty_browse_tree(struct perf_evlist *evlist, const char *help)
286{ 226{
287 struct rb_node *next = rb_first(tree); 227 struct perf_evsel *pos;
288 228
289 while (next) { 229 list_for_each_entry(pos, &evlist->entries, node) {
290 struct hists *hists = rb_entry(next, struct hists, rb_node); 230 struct hists *hists = &pos->hists;
291 const char *evname = NULL; 231 const char *evname = NULL;
292 232
293 if (rb_first(&hists->entries) != rb_last(&hists->entries)) 233 if (rb_first(&hists->entries) != rb_last(&hists->entries))
294 evname = __event_name(hists->type, hists->config); 234 evname = event_name(pos);
295 235
296 hists__fprintf_nr_sample_events(hists, evname, stdout); 236 hists__fprintf_nr_sample_events(hists, evname, stdout);
297 hists__fprintf(hists, NULL, false, stdout); 237 hists__fprintf(hists, NULL, false, stdout);
298 fprintf(stdout, "\n\n"); 238 fprintf(stdout, "\n\n");
299 next = rb_next(&hists->rb_node);
300 } 239 }
301 240
302 if (sort_order == default_sort_order && 241 if (sort_order == default_sort_order &&
@@ -317,8 +256,9 @@ static int hists__tty_browse_tree(struct rb_root *tree, const char *help)
317static int __cmd_report(void) 256static int __cmd_report(void)
318{ 257{
319 int ret = -EINVAL; 258 int ret = -EINVAL;
259 u64 nr_samples;
320 struct perf_session *session; 260 struct perf_session *session;
321 struct rb_node *next; 261 struct perf_evsel *pos;
322 const char *help = "For a higher level overview, try: perf report --sort comm,dso"; 262 const char *help = "For a higher level overview, try: perf report --sort comm,dso";
323 263
324 signal(SIGINT, sig_handler); 264 signal(SIGINT, sig_handler);
@@ -349,26 +289,24 @@ static int __cmd_report(void)
349 if (verbose > 2) 289 if (verbose > 2)
350 perf_session__fprintf_dsos(session, stdout); 290 perf_session__fprintf_dsos(session, stdout);
351 291
352 next = rb_first(&session->hists_tree); 292 nr_samples = 0;
353 293 list_for_each_entry(pos, &session->evlist->entries, node) {
354 if (next == NULL) { 294 struct hists *hists = &pos->hists;
355 ui__warning("The %s file has no samples!\n", input_name);
356 goto out_delete;
357 }
358
359 while (next) {
360 struct hists *hists;
361 295
362 hists = rb_entry(next, struct hists, rb_node);
363 hists__collapse_resort(hists); 296 hists__collapse_resort(hists);
364 hists__output_resort(hists); 297 hists__output_resort(hists);
365 next = rb_next(&hists->rb_node); 298 nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
299 }
300
301 if (nr_samples == 0) {
302 ui__warning("The %s file has no samples!\n", input_name);
303 goto out_delete;
366 } 304 }
367 305
368 if (use_browser > 0) 306 if (use_browser > 0)
369 hists__tui_browse_tree(&session->hists_tree, help, 0); 307 hists__tui_browse_tree(session->evlist, help);
370 else 308 else
371 hists__tty_browse_tree(&session->hists_tree, help); 309 hists__tty_browse_tree(session->evlist, help);
372 310
373out_delete: 311out_delete:
374 /* 312 /*
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index f6fc8f651a25..281b60e5fc7b 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -7,6 +7,7 @@
7#include "types.h" 7#include "types.h"
8#include "xyarray.h" 8#include "xyarray.h"
9#include "cgroup.h" 9#include "cgroup.h"
10#include "hist.h"
10 11
11struct perf_counts_values { 12struct perf_counts_values {
12 union { 13 union {
@@ -51,6 +52,7 @@ struct perf_evsel {
51 struct xyarray *id; 52 struct xyarray *id;
52 struct perf_counts *counts; 53 struct perf_counts *counts;
53 int idx; 54 int idx;
55 struct hists hists;
54 char *name; 56 char *name;
55 void *priv; 57 void *priv;
56 struct cgroup_sel *cgrp; 58 struct cgroup_sel *cgrp;
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 72c124dc5781..108b0db7bbef 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -969,37 +969,6 @@ bool perf_header__sample_id_all(const struct perf_header *header)
969 return value; 969 return value;
970} 970}
971 971
972struct perf_event_attr *
973perf_header__find_attr(u64 id, struct perf_header *header)
974{
975 int i;
976
977 /*
978 * We set id to -1 if the data file doesn't contain sample
979 * ids. This can happen when the data file contains one type
980 * of event and in that case, the header can still store the
981 * event attribute information. Check for this and avoid
982 * walking through the entire list of ids which may be large.
983 */
984 if (id == -1ULL) {
985 if (header->attrs > 0)
986 return &header->attr[0]->attr;
987 return NULL;
988 }
989
990 for (i = 0; i < header->attrs; i++) {
991 struct perf_header_attr *attr = header->attr[i];
992 int j;
993
994 for (j = 0; j < attr->ids; j++) {
995 if (attr->id[j] == id)
996 return &attr->attr;
997 }
998 }
999
1000 return NULL;
1001}
1002
1003int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, 972int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
1004 perf_event__handler_t process, 973 perf_event__handler_t process,
1005 struct perf_session *session) 974 struct perf_session *session)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index f042cebcec1e..2fab13348aab 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -85,8 +85,6 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
85 85
86u64 perf_header__sample_type(struct perf_header *header); 86u64 perf_header__sample_type(struct perf_header *header);
87bool perf_header__sample_id_all(const struct perf_header *header); 87bool perf_header__sample_id_all(const struct perf_header *header);
88struct perf_event_attr *
89perf_header__find_attr(u64 id, struct perf_header *header);
90void perf_header__set_feat(struct perf_header *self, int feat); 88void perf_header__set_feat(struct perf_header *self, int feat);
91void perf_header__clear_feat(struct perf_header *self, int feat); 89void perf_header__clear_feat(struct perf_header *self, int feat);
92bool perf_header__has_feat(const struct perf_header *self, int feat); 90bool perf_header__has_feat(const struct perf_header *self, int feat);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index f7ad6bdbc667..627a02e03c57 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -984,8 +984,12 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
984 size_t ret = 0; 984 size_t ret = 0;
985 985
986 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 986 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
987 const char *name = perf_event__name(i); 987 const char *name;
988 988
989 if (self->stats.nr_events[i] == 0)
990 continue;
991
992 name = perf_event__name(i);
989 if (!strcmp(name, "UNKNOWN")) 993 if (!strcmp(name, "UNKNOWN"))
990 continue; 994 continue;
991 995
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 37c79089de09..0d38b435827b 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -42,13 +42,10 @@ enum hist_column {
42}; 42};
43 43
44struct hists { 44struct hists {
45 struct rb_node rb_node;
46 struct rb_root entries; 45 struct rb_root entries;
47 u64 nr_entries; 46 u64 nr_entries;
48 struct events_stats stats; 47 struct events_stats stats;
49 u64 config;
50 u64 event_stream; 48 u64 event_stream;
51 u32 type;
52 u16 col_len[HISTC_NR_COLS]; 49 u16 col_len[HISTC_NR_COLS];
53 /* Best would be to reuse the session callchain cursor */ 50 /* Best would be to reuse the session callchain cursor */
54 struct callchain_cursor callchain_cursor; 51 struct callchain_cursor callchain_cursor;
@@ -87,6 +84,8 @@ u16 hists__col_len(struct hists *self, enum hist_column col);
87void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); 84void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
88bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); 85bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
89 86
87struct perf_evlist;
88
90#ifdef NO_NEWT_SUPPORT 89#ifdef NO_NEWT_SUPPORT
91static inline int hists__browse(struct hists *self __used, 90static inline int hists__browse(struct hists *self __used,
92 const char *helpline __used, 91 const char *helpline __used,
@@ -95,9 +94,8 @@ static inline int hists__browse(struct hists *self __used,
95 return 0; 94 return 0;
96} 95}
97 96
98static inline int hists__tui_browse_tree(struct rb_root *self __used, 97static inline int hists__tui_browse_tree(struct perf_evlist *evlist __used,
99 const char *help __used, 98 const char *help __used)
100 int evidx __used)
101{ 99{
102 return 0; 100 return 0;
103} 101}
@@ -118,7 +116,7 @@ int hist_entry__tui_annotate(struct hist_entry *self, int evidx);
118#define KEY_LEFT NEWT_KEY_LEFT 116#define KEY_LEFT NEWT_KEY_LEFT
119#define KEY_RIGHT NEWT_KEY_RIGHT 117#define KEY_RIGHT NEWT_KEY_RIGHT
120 118
121int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx); 119int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help);
122#endif 120#endif
123 121
124unsigned int hists__sort_list_width(struct hists *self); 122unsigned int hists__sort_list_width(struct hists *self);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index a3a871f7bda3..0d414199889d 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -7,10 +7,52 @@
7#include <sys/types.h> 7#include <sys/types.h>
8#include <sys/mman.h> 8#include <sys/mman.h>
9 9
10#include "evlist.h"
11#include "evsel.h"
10#include "session.h" 12#include "session.h"
11#include "sort.h" 13#include "sort.h"
12#include "util.h" 14#include "util.h"
13 15
16static int perf_session__read_evlist(struct perf_session *session)
17{
18 int i, j;
19
20 session->evlist = perf_evlist__new(NULL, NULL);
21 if (session->evlist == NULL)
22 return -ENOMEM;
23
24 for (i = 0; i < session->header.attrs; ++i) {
25 struct perf_header_attr *hattr = session->header.attr[i];
26 struct perf_evsel *evsel = perf_evsel__new(&hattr->attr, i);
27
28 if (evsel == NULL)
29 goto out_delete_evlist;
30 /*
31 * Do it before so that if perf_evsel__alloc_id fails, this
32 * entry gets purged too at perf_evlist__delete().
33 */
34 perf_evlist__add(session->evlist, evsel);
35 /*
36 * We don't have the cpu and thread maps on the header, so
37 * for allocating the perf_sample_id table we fake 1 cpu and
38 * hattr->ids threads.
39 */
40 if (perf_evsel__alloc_id(evsel, 1, hattr->ids))
41 goto out_delete_evlist;
42
43 for (j = 0; j < hattr->ids; ++j)
44 perf_evlist__id_hash(session->evlist, evsel, 0, j,
45 hattr->id[j]);
46 }
47
48 return 0;
49
50out_delete_evlist:
51 perf_evlist__delete(session->evlist);
52 session->evlist = NULL;
53 return -ENOMEM;
54}
55
14static int perf_session__open(struct perf_session *self, bool force) 56static int perf_session__open(struct perf_session *self, bool force)
15{ 57{
16 struct stat input_stat; 58 struct stat input_stat;
@@ -56,6 +98,11 @@ static int perf_session__open(struct perf_session *self, bool force)
56 goto out_close; 98 goto out_close;
57 } 99 }
58 100
101 if (perf_session__read_evlist(self) < 0) {
102 pr_err("Not enough memory to read the event selector list\n");
103 goto out_close;
104 }
105
59 self->size = input_stat.st_size; 106 self->size = input_stat.st_size;
60 return 0; 107 return 0;
61 108
@@ -141,7 +188,6 @@ struct perf_session *perf_session__new(const char *filename, int mode,
141 memcpy(self->filename, filename, len); 188 memcpy(self->filename, filename, len);
142 self->threads = RB_ROOT; 189 self->threads = RB_ROOT;
143 INIT_LIST_HEAD(&self->dead_threads); 190 INIT_LIST_HEAD(&self->dead_threads);
144 self->hists_tree = RB_ROOT;
145 self->last_match = NULL; 191 self->last_match = NULL;
146 /* 192 /*
147 * On 64bit we can mmap the data file in one go. No need for tiny mmap 193 * On 64bit we can mmap the data file in one go. No need for tiny mmap
@@ -1137,3 +1183,18 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
1137 size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); 1183 size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
1138 return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); 1184 return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
1139} 1185}
1186
1187size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
1188{
1189 struct perf_evsel *pos;
1190 size_t ret = fprintf(fp, "Aggregated stats:\n");
1191
1192 ret += hists__fprintf_nr_events(&session->hists, fp);
1193
1194 list_for_each_entry(pos, &session->evlist->entries, node) {
1195 ret += fprintf(fp, "%s stats:\n", event_name(pos));
1196 ret += hists__fprintf_nr_events(&pos->hists, fp);
1197 }
1198
1199 return ret;
1200}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 977b3a1b14aa..05dd7bcb9453 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -34,12 +34,12 @@ struct perf_session {
34 struct thread *last_match; 34 struct thread *last_match;
35 struct machine host_machine; 35 struct machine host_machine;
36 struct rb_root machines; 36 struct rb_root machines;
37 struct rb_root hists_tree; 37 struct perf_evlist *evlist;
38 /* 38 /*
39 * FIXME: should point to the first entry in hists_tree and 39 * FIXME: Need to split this up further, we need global
40 * be a hists instance. Right now its only 'report' 40 * stats + per event stats. 'perf diff' also needs
41 * that is using ->hists_tree while all the rest use 41 * to properly support multiple events in a single
42 * ->hists. 42 * perf.data file.
43 */ 43 */
44 struct hists hists; 44 struct hists hists;
45 u64 sample_type; 45 u64 sample_type;
@@ -151,11 +151,7 @@ size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
151size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, 151size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
152 FILE *fp, bool with_hits); 152 FILE *fp, bool with_hits);
153 153
154static inline 154size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
155size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
156{
157 return hists__fprintf_nr_events(&self->hists, fp);
158}
159 155
160static inline int perf_session__parse_sample(struct perf_session *session, 156static inline int perf_session__parse_sample(struct perf_session *session,
161 const union perf_event *event, 157 const union perf_event *event,
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index c98e6f81d285..f3af4fe5cdc4 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -7,6 +7,8 @@
7#include <newt.h> 7#include <newt.h>
8#include <linux/rbtree.h> 8#include <linux/rbtree.h>
9 9
10#include "../../evsel.h"
11#include "../../evlist.h"
10#include "../../hist.h" 12#include "../../hist.h"
11#include "../../pstack.h" 13#include "../../pstack.h"
12#include "../../sort.h" 14#include "../../sort.h"
@@ -987,31 +989,33 @@ out:
987 return key; 989 return key;
988} 990}
989 991
990int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx) 992int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help)
991{ 993{
992 struct rb_node *first = rb_first(self), *nd = first, *next; 994 struct perf_evsel *pos;
993 int key = 0;
994 995
995 while (nd) { 996 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
996 struct hists *hists = rb_entry(nd, struct hists, rb_node); 997 while (pos) {
997 const char *ev_name = __event_name(hists->type, hists->config); 998 struct hists *hists = &pos->hists;
999 const char *ev_name = event_name(pos);
1000 int key = hists__browse(hists, help, ev_name, pos->idx);
998 1001
999 key = hists__browse(hists, help, ev_name, evidx);
1000 switch (key) { 1002 switch (key) {
1001 case NEWT_KEY_TAB: 1003 case NEWT_KEY_TAB:
1002 next = rb_next(nd); 1004 if (pos->node.next == &evlist->entries)
1003 if (next) 1005 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1004 nd = next; 1006 else
1007 pos = list_entry(pos->node.next, struct perf_evsel, node);
1005 break; 1008 break;
1006 case NEWT_KEY_UNTAB: 1009 case NEWT_KEY_UNTAB:
1007 if (nd == first) 1010 if (pos->node.prev == &evlist->entries)
1008 continue; 1011 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1009 nd = rb_prev(nd); 1012 else
1013 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1010 break; 1014 break;
1011 default: 1015 default:
1012 return key; 1016 return key;
1013 } 1017 }
1014 } 1018 }
1015 1019
1016 return key; 1020 return 0;
1017} 1021}