diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-02-06 11:54:44 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-02-06 13:08:50 -0500 |
commit | 36532461a0f60bb36c5470a0326f7394f19db23c (patch) | |
tree | 3cf2108006fca07f0093eda19cde4438c8b52d80 /tools/perf/builtin-top.c | |
parent | f1e2701de02cff6d988b1dd49960620d5720cb89 (diff) |
perf top: Ditch private annotation code, share perf annotate's
Next step: Live TUI annotation in perf top, just press enter on a symbol
line.
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/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 181 |
1 files changed, 31 insertions, 150 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); |