diff options
Diffstat (limited to 'tools/perf/builtin-annotate.c')
-rw-r--r-- | tools/perf/builtin-annotate.c | 80 |
1 files changed, 61 insertions, 19 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 | ||