diff options
-rw-r--r-- | tools/perf/builtin-annotate.c | 80 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 160 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 2 | ||||
-rw-r--r-- | tools/perf/util/header.c | 31 | ||||
-rw-r--r-- | tools/perf/util/header.h | 2 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 6 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 12 | ||||
-rw-r--r-- | tools/perf/util/session.c | 63 | ||||
-rw-r--r-- | tools/perf/util/session.h | 16 | ||||
-rw-r--r-- | tools/perf/util/ui/browsers/hists.c | 32 |
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 | ||
39 | static const char *sym_hist_filter; | 41 | static const char *sym_hist_filter; |
40 | 42 | ||
41 | static int hists__add_entry(struct hists *self, struct addr_location *al) | 43 | static 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 | ||
75 | static int process_sample_event(union perf_event *event, | 93 | static 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 | ||
103 | static void hists__find_annotations(struct hists *self) | 121 | static 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; |
192 | out_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 | } | ||
224 | out_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; | |||
46 | static char callchain_default_opt[] = "fractal,0.5"; | 48 | static char callchain_default_opt[] = "fractal,0.5"; |
47 | static symbol_filter_t annotate_init; | 49 | static symbol_filter_t annotate_init; |
48 | 50 | ||
49 | static 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 | |||
82 | static int perf_session__add_hist_entry(struct perf_session *session, | 51 | static 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); | ||
109 | out: | ||
134 | return err; | 110 | return err; |
135 | } | 111 | } |
136 | 112 | ||
137 | static 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 | ||
163 | static int process_sample_event(union perf_event *event, | 114 | static 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 | ||
195 | static int process_read_event(union perf_event *event, | 138 | static 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 | ||
285 | static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | 225 | static 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) | |||
317 | static int __cmd_report(void) | 256 | static 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 | ||
373 | out_delete: | 311 | out_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 | ||
11 | struct perf_counts_values { | 12 | struct 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 | ||
972 | struct perf_event_attr * | ||
973 | perf_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 | |||
1003 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | 972 | int 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 | ||
86 | u64 perf_header__sample_type(struct perf_header *header); | 86 | u64 perf_header__sample_type(struct perf_header *header); |
87 | bool perf_header__sample_id_all(const struct perf_header *header); | 87 | bool perf_header__sample_id_all(const struct perf_header *header); |
88 | struct perf_event_attr * | ||
89 | perf_header__find_attr(u64 id, struct perf_header *header); | ||
90 | void perf_header__set_feat(struct perf_header *self, int feat); | 88 | void perf_header__set_feat(struct perf_header *self, int feat); |
91 | void perf_header__clear_feat(struct perf_header *self, int feat); | 89 | void perf_header__clear_feat(struct perf_header *self, int feat); |
92 | bool perf_header__has_feat(const struct perf_header *self, int feat); | 90 | bool 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 | ||
44 | struct hists { | 44 | struct 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); | |||
87 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 84 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
88 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | 85 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); |
89 | 86 | ||
87 | struct perf_evlist; | ||
88 | |||
90 | #ifdef NO_NEWT_SUPPORT | 89 | #ifdef NO_NEWT_SUPPORT |
91 | static inline int hists__browse(struct hists *self __used, | 90 | static 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 | ||
98 | static inline int hists__tui_browse_tree(struct rb_root *self __used, | 97 | static 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 | ||
121 | int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx); | 119 | int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help); |
122 | #endif | 120 | #endif |
123 | 121 | ||
124 | unsigned int hists__sort_list_width(struct hists *self); | 122 | unsigned 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 | ||
16 | static 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 | |||
50 | out_delete_evlist: | ||
51 | perf_evlist__delete(session->evlist); | ||
52 | session->evlist = NULL; | ||
53 | return -ENOMEM; | ||
54 | } | ||
55 | |||
14 | static int perf_session__open(struct perf_session *self, bool force) | 56 | static 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 | |||
1187 | size_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); | |||
151 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, | 151 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, |
152 | FILE *fp, bool with_hits); | 152 | FILE *fp, bool with_hits); |
153 | 153 | ||
154 | static inline | 154 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); |
155 | size_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 | ||
160 | static inline int perf_session__parse_sample(struct perf_session *session, | 156 | static 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 | ||
990 | int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx) | 992 | int 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 | } |