diff options
-rw-r--r-- | tools/perf/builtin-top.c | 181 | ||||
-rw-r--r-- | tools/perf/util/annotate.c | 76 | ||||
-rw-r--r-- | tools/perf/util/annotate.h | 11 | ||||
-rw-r--r-- | tools/perf/util/top.h | 11 |
4 files changed, 106 insertions, 173 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 154e088588bc..716118a3b3e4 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -20,6 +20,7 @@ | |||
20 | 20 | ||
21 | #include "perf.h" | 21 | #include "perf.h" |
22 | 22 | ||
23 | #include "util/annotate.h" | ||
23 | #include "util/cache.h" | 24 | #include "util/cache.h" |
24 | #include "util/color.h" | 25 | #include "util/color.h" |
25 | #include "util/evlist.h" | 26 | #include "util/evlist.h" |
@@ -140,10 +141,7 @@ static int parse_source(struct sym_entry *syme) | |||
140 | struct symbol *sym; | 141 | struct symbol *sym; |
141 | struct sym_entry_source *source; | 142 | struct sym_entry_source *source; |
142 | struct map *map; | 143 | struct map *map; |
143 | FILE *file; | 144 | int err = -1; |
144 | char command[PATH_MAX*2]; | ||
145 | const char *path; | ||
146 | u64 len; | ||
147 | 145 | ||
148 | if (!syme) | 146 | if (!syme) |
149 | return -1; | 147 | return -1; |
@@ -162,197 +160,80 @@ static int parse_source(struct sym_entry *syme) | |||
162 | if (syme->src == NULL) | 160 | if (syme->src == NULL) |
163 | return -1; | 161 | return -1; |
164 | pthread_mutex_init(&syme->src->lock, NULL); | 162 | pthread_mutex_init(&syme->src->lock, NULL); |
163 | INIT_LIST_HEAD(&syme->src->head); | ||
165 | } | 164 | } |
166 | 165 | ||
167 | source = syme->src; | 166 | source = syme->src; |
168 | 167 | ||
169 | if (source->lines) { | 168 | if (symbol__annotation(sym)->histograms != NULL) { |
170 | pthread_mutex_lock(&source->lock); | 169 | pthread_mutex_lock(&source->lock); |
171 | goto out_assign; | 170 | goto out_assign; |
172 | } | 171 | } |
173 | path = map->dso->long_name; | ||
174 | |||
175 | len = sym->end - sym->start; | ||
176 | |||
177 | sprintf(command, | ||
178 | "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", | ||
179 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), | ||
180 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); | ||
181 | |||
182 | file = popen(command, "r"); | ||
183 | if (!file) | ||
184 | return -1; | ||
185 | 172 | ||
186 | pthread_mutex_lock(&source->lock); | 173 | pthread_mutex_lock(&source->lock); |
187 | source->lines_tail = &source->lines; | ||
188 | while (!feof(file)) { | ||
189 | struct source_line *src; | ||
190 | size_t dummy = 0; | ||
191 | char *c, *sep; | ||
192 | |||
193 | src = malloc(sizeof(struct source_line)); | ||
194 | assert(src != NULL); | ||
195 | memset(src, 0, sizeof(struct source_line)); | ||
196 | |||
197 | if (getline(&src->line, &dummy, file) < 0) | ||
198 | break; | ||
199 | if (!src->line) | ||
200 | break; | ||
201 | |||
202 | c = strchr(src->line, '\n'); | ||
203 | if (c) | ||
204 | *c = 0; | ||
205 | 174 | ||
206 | src->next = NULL; | 175 | if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { |
207 | *source->lines_tail = src; | 176 | pr_err("Not enough memory for annotating '%s' symbol!\n", |
208 | source->lines_tail = &src->next; | 177 | sym->name); |
209 | 178 | goto out_unlock; | |
210 | src->eip = strtoull(src->line, &sep, 16); | ||
211 | if (*sep == ':') | ||
212 | src->eip = map__objdump_2ip(map, src->eip); | ||
213 | else /* this line has no ip info (e.g. source line) */ | ||
214 | src->eip = 0; | ||
215 | } | 179 | } |
216 | pclose(file); | 180 | |
181 | err = symbol__annotate(sym, syme->map, &source->head, 0); | ||
182 | if (err == 0) { | ||
217 | out_assign: | 183 | out_assign: |
218 | sym_filter_entry = syme; | 184 | sym_filter_entry = syme; |
185 | } | ||
186 | out_unlock: | ||
219 | pthread_mutex_unlock(&source->lock); | 187 | pthread_mutex_unlock(&source->lock); |
220 | return 0; | 188 | return err; |
221 | } | 189 | } |
222 | 190 | ||
223 | static void __zero_source_counters(struct sym_entry *syme) | 191 | static void __zero_source_counters(struct sym_entry *syme) |
224 | { | 192 | { |
225 | int i; | 193 | struct symbol *sym = sym_entry__symbol(syme); |
226 | struct source_line *line; | 194 | symbol__annotate_zero_histograms(sym); |
227 | |||
228 | line = syme->src->lines; | ||
229 | while (line) { | ||
230 | for (i = 0; i < top.evlist->nr_entries; i++) | ||
231 | line->count[i] = 0; | ||
232 | line = line->next; | ||
233 | } | ||
234 | } | 195 | } |
235 | 196 | ||
236 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | 197 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) |
237 | { | 198 | { |
238 | struct source_line *line; | ||
239 | |||
240 | if (syme != sym_filter_entry) | 199 | if (syme != sym_filter_entry) |
241 | return; | 200 | return; |
242 | 201 | ||
243 | if (pthread_mutex_trylock(&syme->src->lock)) | 202 | if (pthread_mutex_trylock(&syme->src->lock)) |
244 | return; | 203 | return; |
245 | 204 | ||
246 | if (syme->src == NULL || syme->src->source == NULL) | 205 | ip = syme->map->map_ip(syme->map, ip); |
247 | goto out_unlock; | 206 | symbol__inc_addr_samples(sym_entry__symbol(syme), syme->map, counter, ip); |
248 | |||
249 | for (line = syme->src->lines; line; line = line->next) { | ||
250 | /* skip lines without IP info */ | ||
251 | if (line->eip == 0) | ||
252 | continue; | ||
253 | if (line->eip == ip) { | ||
254 | line->count[counter]++; | ||
255 | break; | ||
256 | } | ||
257 | if (line->eip > ip) | ||
258 | break; | ||
259 | } | ||
260 | out_unlock: | ||
261 | pthread_mutex_unlock(&syme->src->lock); | ||
262 | } | ||
263 | |||
264 | #define PATTERN_LEN (BITS_PER_LONG / 4 + 2) | ||
265 | 207 | ||
266 | static void lookup_sym_source(struct sym_entry *syme) | ||
267 | { | ||
268 | struct symbol *symbol = sym_entry__symbol(syme); | ||
269 | struct source_line *line; | ||
270 | char pattern[PATTERN_LEN + 1]; | ||
271 | |||
272 | sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4, | ||
273 | map__rip_2objdump(syme->map, symbol->start)); | ||
274 | |||
275 | pthread_mutex_lock(&syme->src->lock); | ||
276 | for (line = syme->src->lines; line; line = line->next) { | ||
277 | if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { | ||
278 | syme->src->source = line; | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | pthread_mutex_unlock(&syme->src->lock); | 208 | pthread_mutex_unlock(&syme->src->lock); |
283 | } | 209 | } |
284 | 210 | ||
285 | static void show_lines(struct source_line *queue, int count, int total) | ||
286 | { | ||
287 | int i; | ||
288 | struct source_line *line; | ||
289 | |||
290 | line = queue; | ||
291 | for (i = 0; i < count; i++) { | ||
292 | float pcnt = 100.0*(float)line->count[top.sym_counter]/(float)total; | ||
293 | |||
294 | printf("%8li %4.1f%%\t%s\n", line->count[top.sym_counter], pcnt, line->line); | ||
295 | line = line->next; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | #define TRACE_COUNT 3 | ||
300 | |||
301 | static void show_details(struct sym_entry *syme) | 211 | static void show_details(struct sym_entry *syme) |
302 | { | 212 | { |
303 | struct symbol *symbol; | 213 | struct symbol *symbol; |
304 | struct source_line *line; | 214 | int more; |
305 | struct source_line *line_queue = NULL; | ||
306 | int displayed = 0; | ||
307 | int line_queue_count = 0, total = 0, more = 0; | ||
308 | 215 | ||
309 | if (!syme) | 216 | if (!syme) |
310 | return; | 217 | return; |
311 | 218 | ||
312 | if (!syme->src->source) | 219 | symbol = sym_entry__symbol(syme); |
313 | lookup_sym_source(syme); | 220 | if (!syme->src || symbol__annotation(symbol)->histograms == NULL) |
314 | |||
315 | if (!syme->src->source) | ||
316 | return; | 221 | return; |
317 | 222 | ||
318 | symbol = sym_entry__symbol(syme); | ||
319 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); | 223 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); |
320 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 224 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
321 | 225 | ||
322 | pthread_mutex_lock(&syme->src->lock); | 226 | pthread_mutex_lock(&syme->src->lock); |
323 | line = syme->src->source; | 227 | more = symbol__annotate_printf(symbol, syme->map, &syme->src->head, |
324 | while (line) { | 228 | top.sym_evsel->idx, 0, sym_pcnt_filter, |
325 | total += line->count[top.sym_counter]; | 229 | top.print_entries); |
326 | line = line->next; | 230 | if (top.zero) |
327 | } | 231 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); |
328 | 232 | else | |
329 | line = syme->src->source; | 233 | symbol__annotate_decay_histogram(symbol, &syme->src->head, |
330 | while (line) { | 234 | top.sym_evsel->idx); |
331 | float pcnt = 0.0; | ||
332 | |||
333 | if (!line_queue_count) | ||
334 | line_queue = line; | ||
335 | line_queue_count++; | ||
336 | |||
337 | if (line->count[top.sym_counter]) | ||
338 | pcnt = 100.0 * line->count[top.sym_counter] / (float)total; | ||
339 | if (pcnt >= (float)sym_pcnt_filter) { | ||
340 | if (displayed <= top.print_entries) | ||
341 | show_lines(line_queue, line_queue_count, total); | ||
342 | else more++; | ||
343 | displayed += line_queue_count; | ||
344 | line_queue_count = 0; | ||
345 | line_queue = NULL; | ||
346 | } else if (line_queue_count > TRACE_COUNT) { | ||
347 | line_queue = line_queue->next; | ||
348 | line_queue_count--; | ||
349 | } | ||
350 | |||
351 | line->count[top.sym_counter] = top.zero ? 0 : line->count[top.sym_counter] * 7 / 8; | ||
352 | line = line->next; | ||
353 | } | ||
354 | pthread_mutex_unlock(&syme->src->lock); | 235 | pthread_mutex_unlock(&syme->src->lock); |
355 | if (more) | 236 | if (more != 0) |
356 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 237 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
357 | } | 238 | } |
358 | 239 | ||
@@ -1172,7 +1053,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1172 | 1053 | ||
1173 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 1054 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
1174 | 1055 | ||
1175 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1056 | symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + |
1176 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); | 1057 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); |
1177 | 1058 | ||
1178 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1059 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 10cdbad76058..297337649c21 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -22,9 +22,19 @@ int symbol__alloc_hist(struct symbol *sym, int nevents) | |||
22 | notes->sizeof_sym_hist = (sizeof(*notes->histograms) + | 22 | notes->sizeof_sym_hist = (sizeof(*notes->histograms) + |
23 | (sym->end - sym->start) * sizeof(u64)); | 23 | (sym->end - sym->start) * sizeof(u64)); |
24 | notes->histograms = calloc(nevents, notes->sizeof_sym_hist); | 24 | notes->histograms = calloc(nevents, notes->sizeof_sym_hist); |
25 | notes->nr_histograms = nevents; | ||
25 | return notes->histograms == NULL ? -1 : 0; | 26 | return notes->histograms == NULL ? -1 : 0; |
26 | } | 27 | } |
27 | 28 | ||
29 | void symbol__annotate_zero_histograms(struct symbol *sym) | ||
30 | { | ||
31 | struct annotation *notes = symbol__annotation(sym); | ||
32 | |||
33 | if (notes->histograms != NULL) | ||
34 | memset(notes->histograms, 0, | ||
35 | notes->nr_histograms * notes->sizeof_sym_hist); | ||
36 | } | ||
37 | |||
28 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | 38 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
29 | int evidx, u64 addr) | 39 | int evidx, u64 addr) |
30 | { | 40 | { |
@@ -85,9 +95,10 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | |||
85 | return NULL; | 95 | return NULL; |
86 | } | 96 | } |
87 | 97 | ||
88 | static void objdump_line__print(struct objdump_line *oline, | 98 | static int objdump_line__print(struct objdump_line *oline, |
89 | struct list_head *head, struct symbol *sym, | 99 | struct list_head *head, struct symbol *sym, |
90 | int evidx, u64 len, int min_pcnt) | 100 | int evidx, u64 len, int min_pcnt, |
101 | int printed, int max_lines) | ||
91 | { | 102 | { |
92 | static const char *prev_line; | 103 | static const char *prev_line; |
93 | static const char *prev_color; | 104 | static const char *prev_color; |
@@ -119,7 +130,10 @@ static void objdump_line__print(struct objdump_line *oline, | |||
119 | percent = 100.0 * hits / h->sum; | 130 | percent = 100.0 * hits / h->sum; |
120 | 131 | ||
121 | if (percent < min_pcnt) | 132 | if (percent < min_pcnt) |
122 | return; | 133 | return -1; |
134 | |||
135 | if (printed >= max_lines) | ||
136 | return 1; | ||
123 | 137 | ||
124 | color = get_percent_color(percent); | 138 | color = get_percent_color(percent); |
125 | 139 | ||
@@ -140,12 +154,16 @@ static void objdump_line__print(struct objdump_line *oline, | |||
140 | color_fprintf(stdout, color, " %7.2f", percent); | 154 | color_fprintf(stdout, color, " %7.2f", percent); |
141 | printf(" : "); | 155 | printf(" : "); |
142 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); | 156 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); |
143 | } else { | 157 | } else if (printed >= max_lines) |
158 | return 1; | ||
159 | else { | ||
144 | if (!*oline->line) | 160 | if (!*oline->line) |
145 | printf(" :\n"); | 161 | printf(" :\n"); |
146 | else | 162 | else |
147 | printf(" : %s\n", oline->line); | 163 | printf(" : %s\n", oline->line); |
148 | } | 164 | } |
165 | |||
166 | return 0; | ||
149 | } | 167 | } |
150 | 168 | ||
151 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, | 169 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, |
@@ -421,14 +439,15 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) | |||
421 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | 439 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); |
422 | } | 440 | } |
423 | 441 | ||
424 | void symbol__annotate_printf(struct symbol *sym, struct map *map, | 442 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
425 | struct list_head *head, int evidx, bool full_paths, | 443 | struct list_head *head, int evidx, bool full_paths, |
426 | int min_pcnt, int max_lines) | 444 | int min_pcnt, int max_lines) |
427 | { | 445 | { |
428 | struct dso *dso = map->dso; | 446 | struct dso *dso = map->dso; |
429 | const char *filename = dso->long_name, *d_filename; | 447 | const char *filename = dso->long_name, *d_filename; |
430 | struct objdump_line *pos; | 448 | struct objdump_line *pos; |
431 | int printed = 2; | 449 | int printed = 2; |
450 | int more = 0; | ||
432 | u64 len; | 451 | u64 len; |
433 | 452 | ||
434 | if (full_paths) | 453 | if (full_paths) |
@@ -445,10 +464,47 @@ void symbol__annotate_printf(struct symbol *sym, struct map *map, | |||
445 | symbol__annotate_hits(sym, evidx); | 464 | symbol__annotate_hits(sym, evidx); |
446 | 465 | ||
447 | list_for_each_entry(pos, head, node) { | 466 | list_for_each_entry(pos, head, node) { |
448 | objdump_line__print(pos, head, sym, evidx, len, min_pcnt); | 467 | switch (objdump_line__print(pos, head, sym, evidx, len, min_pcnt, |
449 | if (max_lines && ++printed >= max_lines) | 468 | printed, max_lines)) { |
469 | case 0: | ||
470 | ++printed; | ||
471 | break; | ||
472 | case 1: | ||
473 | /* filtered by max_lines */ | ||
474 | ++more; | ||
450 | break; | 475 | break; |
476 | case -1: | ||
477 | default: | ||
478 | /* filtered by min_pcnt */ | ||
479 | break; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | return more; | ||
484 | } | ||
451 | 485 | ||
486 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) | ||
487 | { | ||
488 | struct annotation *notes = symbol__annotation(sym); | ||
489 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
490 | |||
491 | memset(h, 0, notes->sizeof_sym_hist); | ||
492 | } | ||
493 | |||
494 | void symbol__annotate_decay_histogram(struct symbol *sym, | ||
495 | struct list_head *head, int evidx) | ||
496 | { | ||
497 | struct annotation *notes = symbol__annotation(sym); | ||
498 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
499 | struct objdump_line *pos; | ||
500 | |||
501 | h->sum = 0; | ||
502 | |||
503 | list_for_each_entry(pos, head, node) { | ||
504 | if (pos->offset != -1) { | ||
505 | h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8; | ||
506 | h->sum += h->addr[pos->offset]; | ||
507 | } | ||
452 | } | 508 | } |
453 | } | 509 | } |
454 | 510 | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 53dd92de2615..b1253aadf340 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -42,6 +42,7 @@ struct source_line { | |||
42 | struct annotation { | 42 | struct annotation { |
43 | struct source_line *src_line; | 43 | struct source_line *src_line; |
44 | struct sym_hist *histograms; | 44 | struct sym_hist *histograms; |
45 | int nr_histograms; | ||
45 | int sizeof_sym_hist; | 46 | int sizeof_sym_hist; |
46 | }; | 47 | }; |
47 | 48 | ||
@@ -64,12 +65,16 @@ static inline struct annotation *symbol__annotation(struct symbol *sym) | |||
64 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | 65 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
65 | int evidx, u64 addr); | 66 | int evidx, u64 addr); |
66 | int symbol__alloc_hist(struct symbol *sym, int nevents); | 67 | int symbol__alloc_hist(struct symbol *sym, int nevents); |
68 | void symbol__annotate_zero_histograms(struct symbol *sym); | ||
67 | 69 | ||
68 | int symbol__annotate(struct symbol *sym, struct map *map, | 70 | int symbol__annotate(struct symbol *sym, struct map *map, |
69 | struct list_head *head, size_t privsize); | 71 | struct list_head *head, size_t privsize); |
70 | void symbol__annotate_printf(struct symbol *sym, struct map *map, | 72 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
71 | struct list_head *head, int evidx, bool full_paths, | 73 | struct list_head *head, int evidx, bool full_paths, |
72 | int min_pcnt, int max_lines); | 74 | int min_pcnt, int max_lines); |
75 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); | ||
76 | void symbol__annotate_decay_histogram(struct symbol *sym, | ||
77 | struct list_head *head, int evidx); | ||
73 | void objdump_line_list__purge(struct list_head *head); | 78 | void objdump_line_list__purge(struct list_head *head); |
74 | 79 | ||
75 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | 80 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 500950818d2f..fe44afb69985 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -11,17 +11,8 @@ | |||
11 | struct perf_evlist; | 11 | struct perf_evlist; |
12 | struct perf_evsel; | 12 | struct perf_evsel; |
13 | 13 | ||
14 | struct source_line { | ||
15 | u64 eip; | ||
16 | unsigned long count[MAX_COUNTERS]; /* FIXME */ | ||
17 | char *line; | ||
18 | struct source_line *next; | ||
19 | }; | ||
20 | |||
21 | struct sym_entry_source { | 14 | struct sym_entry_source { |
22 | struct source_line *source; | 15 | struct list_head head; |
23 | struct source_line *lines; | ||
24 | struct source_line **lines_tail; | ||
25 | pthread_mutex_t lock; | 16 | pthread_mutex_t lock; |
26 | }; | 17 | }; |
27 | 18 | ||