Root/
1 | /* |
2 | * builtin-top.c |
3 | * |
4 | * Builtin top command: Display a continuously updated profile of |
5 | * any workload, CPU or specific PID. |
6 | * |
7 | * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com> |
8 | * |
9 | * Improvements and fixes by: |
10 | * |
11 | * Arjan van de Ven <arjan@linux.intel.com> |
12 | * Yanmin Zhang <yanmin.zhang@intel.com> |
13 | * Wu Fengguang <fengguang.wu@intel.com> |
14 | * Mike Galbraith <efault@gmx.de> |
15 | * Paul Mackerras <paulus@samba.org> |
16 | * |
17 | * Released under the GPL v2. (and only v2, not any later version) |
18 | */ |
19 | #include "builtin.h" |
20 | |
21 | #include "perf.h" |
22 | |
23 | #include "util/color.h" |
24 | #include "util/session.h" |
25 | #include "util/symbol.h" |
26 | #include "util/thread.h" |
27 | #include "util/util.h" |
28 | #include <linux/rbtree.h> |
29 | #include "util/parse-options.h" |
30 | #include "util/parse-events.h" |
31 | #include "util/cpumap.h" |
32 | |
33 | #include "util/debug.h" |
34 | |
35 | #include <assert.h> |
36 | #include <fcntl.h> |
37 | |
38 | #include <stdio.h> |
39 | #include <termios.h> |
40 | #include <unistd.h> |
41 | |
42 | #include <errno.h> |
43 | #include <time.h> |
44 | #include <sched.h> |
45 | #include <pthread.h> |
46 | |
47 | #include <sys/syscall.h> |
48 | #include <sys/ioctl.h> |
49 | #include <sys/poll.h> |
50 | #include <sys/prctl.h> |
51 | #include <sys/wait.h> |
52 | #include <sys/uio.h> |
53 | #include <sys/mman.h> |
54 | |
55 | #include <linux/unistd.h> |
56 | #include <linux/types.h> |
57 | |
58 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; |
59 | |
60 | static int system_wide = 0; |
61 | |
62 | static int default_interval = 0; |
63 | |
64 | static int count_filter = 5; |
65 | static int print_entries; |
66 | |
67 | static int target_pid = -1; |
68 | static int inherit = 0; |
69 | static int profile_cpu = -1; |
70 | static int nr_cpus = 0; |
71 | static unsigned int realtime_prio = 0; |
72 | static int group = 0; |
73 | static unsigned int page_size; |
74 | static unsigned int mmap_pages = 16; |
75 | static int freq = 1000; /* 1 KHz */ |
76 | |
77 | static int delay_secs = 2; |
78 | static int zero = 0; |
79 | static int dump_symtab = 0; |
80 | |
81 | static bool hide_kernel_symbols = false; |
82 | static bool hide_user_symbols = false; |
83 | static struct winsize winsize; |
84 | |
85 | /* |
86 | * Source |
87 | */ |
88 | |
89 | struct source_line { |
90 | u64 eip; |
91 | unsigned long count[MAX_COUNTERS]; |
92 | char *line; |
93 | struct source_line *next; |
94 | }; |
95 | |
96 | static char *sym_filter = NULL; |
97 | struct sym_entry *sym_filter_entry = NULL; |
98 | struct sym_entry *sym_filter_entry_sched = NULL; |
99 | static int sym_pcnt_filter = 5; |
100 | static int sym_counter = 0; |
101 | static int display_weighted = -1; |
102 | |
103 | /* |
104 | * Symbols |
105 | */ |
106 | |
107 | struct sym_entry_source { |
108 | struct source_line *source; |
109 | struct source_line *lines; |
110 | struct source_line **lines_tail; |
111 | pthread_mutex_t lock; |
112 | }; |
113 | |
114 | struct sym_entry { |
115 | struct rb_node rb_node; |
116 | struct list_head node; |
117 | unsigned long snap_count; |
118 | double weight; |
119 | int skip; |
120 | u16 name_len; |
121 | u8 origin; |
122 | struct map *map; |
123 | struct sym_entry_source *src; |
124 | unsigned long count[0]; |
125 | }; |
126 | |
127 | /* |
128 | * Source functions |
129 | */ |
130 | |
131 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) |
132 | { |
133 | return ((void *)self) + symbol_conf.priv_size; |
134 | } |
135 | |
136 | static void get_term_dimensions(struct winsize *ws) |
137 | { |
138 | char *s = getenv("LINES"); |
139 | |
140 | if (s != NULL) { |
141 | ws->ws_row = atoi(s); |
142 | s = getenv("COLUMNS"); |
143 | if (s != NULL) { |
144 | ws->ws_col = atoi(s); |
145 | if (ws->ws_row && ws->ws_col) |
146 | return; |
147 | } |
148 | } |
149 | #ifdef TIOCGWINSZ |
150 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && |
151 | ws->ws_row && ws->ws_col) |
152 | return; |
153 | #endif |
154 | ws->ws_row = 25; |
155 | ws->ws_col = 80; |
156 | } |
157 | |
158 | static void update_print_entries(struct winsize *ws) |
159 | { |
160 | print_entries = ws->ws_row; |
161 | |
162 | if (print_entries > 9) |
163 | print_entries -= 9; |
164 | } |
165 | |
166 | static void sig_winch_handler(int sig __used) |
167 | { |
168 | get_term_dimensions(&winsize); |
169 | update_print_entries(&winsize); |
170 | } |
171 | |
172 | static void parse_source(struct sym_entry *syme) |
173 | { |
174 | struct symbol *sym; |
175 | struct sym_entry_source *source; |
176 | struct map *map; |
177 | FILE *file; |
178 | char command[PATH_MAX*2]; |
179 | const char *path; |
180 | u64 len; |
181 | |
182 | if (!syme) |
183 | return; |
184 | |
185 | if (syme->src == NULL) { |
186 | syme->src = zalloc(sizeof(*source)); |
187 | if (syme->src == NULL) |
188 | return; |
189 | pthread_mutex_init(&syme->src->lock, NULL); |
190 | } |
191 | |
192 | source = syme->src; |
193 | |
194 | if (source->lines) { |
195 | pthread_mutex_lock(&source->lock); |
196 | goto out_assign; |
197 | } |
198 | |
199 | sym = sym_entry__symbol(syme); |
200 | map = syme->map; |
201 | path = map->dso->long_name; |
202 | |
203 | len = sym->end - sym->start; |
204 | |
205 | sprintf(command, |
206 | "objdump --start-address=%#0*Lx --stop-address=%#0*Lx -dS %s", |
207 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), |
208 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); |
209 | |
210 | file = popen(command, "r"); |
211 | if (!file) |
212 | return; |
213 | |
214 | pthread_mutex_lock(&source->lock); |
215 | source->lines_tail = &source->lines; |
216 | while (!feof(file)) { |
217 | struct source_line *src; |
218 | size_t dummy = 0; |
219 | char *c, *sep; |
220 | |
221 | src = malloc(sizeof(struct source_line)); |
222 | assert(src != NULL); |
223 | memset(src, 0, sizeof(struct source_line)); |
224 | |
225 | if (getline(&src->line, &dummy, file) < 0) |
226 | break; |
227 | if (!src->line) |
228 | break; |
229 | |
230 | c = strchr(src->line, '\n'); |
231 | if (c) |
232 | *c = 0; |
233 | |
234 | src->next = NULL; |
235 | *source->lines_tail = src; |
236 | source->lines_tail = &src->next; |
237 | |
238 | src->eip = strtoull(src->line, &sep, 16); |
239 | if (*sep == ':') |
240 | src->eip = map__objdump_2ip(map, src->eip); |
241 | else /* this line has no ip info (e.g. source line) */ |
242 | src->eip = 0; |
243 | } |
244 | pclose(file); |
245 | out_assign: |
246 | sym_filter_entry = syme; |
247 | pthread_mutex_unlock(&source->lock); |
248 | } |
249 | |
250 | static void __zero_source_counters(struct sym_entry *syme) |
251 | { |
252 | int i; |
253 | struct source_line *line; |
254 | |
255 | line = syme->src->lines; |
256 | while (line) { |
257 | for (i = 0; i < nr_counters; i++) |
258 | line->count[i] = 0; |
259 | line = line->next; |
260 | } |
261 | } |
262 | |
263 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) |
264 | { |
265 | struct source_line *line; |
266 | |
267 | if (syme != sym_filter_entry) |
268 | return; |
269 | |
270 | if (pthread_mutex_trylock(&syme->src->lock)) |
271 | return; |
272 | |
273 | if (syme->src == NULL || syme->src->source == NULL) |
274 | goto out_unlock; |
275 | |
276 | for (line = syme->src->lines; line; line = line->next) { |
277 | /* skip lines without IP info */ |
278 | if (line->eip == 0) |
279 | continue; |
280 | if (line->eip == ip) { |
281 | line->count[counter]++; |
282 | break; |
283 | } |
284 | if (line->eip > ip) |
285 | break; |
286 | } |
287 | out_unlock: |
288 | pthread_mutex_unlock(&syme->src->lock); |
289 | } |
290 | |
291 | #define PATTERN_LEN (BITS_PER_LONG / 4 + 2) |
292 | |
293 | static void lookup_sym_source(struct sym_entry *syme) |
294 | { |
295 | struct symbol *symbol = sym_entry__symbol(syme); |
296 | struct source_line *line; |
297 | char pattern[PATTERN_LEN + 1]; |
298 | |
299 | sprintf(pattern, "%0*Lx <", BITS_PER_LONG / 4, |
300 | map__rip_2objdump(syme->map, symbol->start)); |
301 | |
302 | pthread_mutex_lock(&syme->src->lock); |
303 | for (line = syme->src->lines; line; line = line->next) { |
304 | if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { |
305 | syme->src->source = line; |
306 | break; |
307 | } |
308 | } |
309 | pthread_mutex_unlock(&syme->src->lock); |
310 | } |
311 | |
312 | static void show_lines(struct source_line *queue, int count, int total) |
313 | { |
314 | int i; |
315 | struct source_line *line; |
316 | |
317 | line = queue; |
318 | for (i = 0; i < count; i++) { |
319 | float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; |
320 | |
321 | printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); |
322 | line = line->next; |
323 | } |
324 | } |
325 | |
326 | #define TRACE_COUNT 3 |
327 | |
328 | static void show_details(struct sym_entry *syme) |
329 | { |
330 | struct symbol *symbol; |
331 | struct source_line *line; |
332 | struct source_line *line_queue = NULL; |
333 | int displayed = 0; |
334 | int line_queue_count = 0, total = 0, more = 0; |
335 | |
336 | if (!syme) |
337 | return; |
338 | |
339 | if (!syme->src->source) |
340 | lookup_sym_source(syme); |
341 | |
342 | if (!syme->src->source) |
343 | return; |
344 | |
345 | symbol = sym_entry__symbol(syme); |
346 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); |
347 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
348 | |
349 | pthread_mutex_lock(&syme->src->lock); |
350 | line = syme->src->source; |
351 | while (line) { |
352 | total += line->count[sym_counter]; |
353 | line = line->next; |
354 | } |
355 | |
356 | line = syme->src->source; |
357 | while (line) { |
358 | float pcnt = 0.0; |
359 | |
360 | if (!line_queue_count) |
361 | line_queue = line; |
362 | line_queue_count++; |
363 | |
364 | if (line->count[sym_counter]) |
365 | pcnt = 100.0 * line->count[sym_counter] / (float)total; |
366 | if (pcnt >= (float)sym_pcnt_filter) { |
367 | if (displayed <= print_entries) |
368 | show_lines(line_queue, line_queue_count, total); |
369 | else more++; |
370 | displayed += line_queue_count; |
371 | line_queue_count = 0; |
372 | line_queue = NULL; |
373 | } else if (line_queue_count > TRACE_COUNT) { |
374 | line_queue = line_queue->next; |
375 | line_queue_count--; |
376 | } |
377 | |
378 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; |
379 | line = line->next; |
380 | } |
381 | pthread_mutex_unlock(&syme->src->lock); |
382 | if (more) |
383 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
384 | } |
385 | |
386 | /* |
387 | * Symbols will be added here in event__process_sample and will get out |
388 | * after decayed. |
389 | */ |
390 | static LIST_HEAD(active_symbols); |
391 | static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER; |
392 | |
393 | /* |
394 | * Ordering weight: count-1 * count-2 * ... / count-n |
395 | */ |
396 | static double sym_weight(const struct sym_entry *sym) |
397 | { |
398 | double weight = sym->snap_count; |
399 | int counter; |
400 | |
401 | if (!display_weighted) |
402 | return weight; |
403 | |
404 | for (counter = 1; counter < nr_counters-1; counter++) |
405 | weight *= sym->count[counter]; |
406 | |
407 | weight /= (sym->count[counter] + 1); |
408 | |
409 | return weight; |
410 | } |
411 | |
412 | static long samples; |
413 | static long userspace_samples; |
414 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
415 | |
416 | static void __list_insert_active_sym(struct sym_entry *syme) |
417 | { |
418 | list_add(&syme->node, &active_symbols); |
419 | } |
420 | |
421 | static void list_remove_active_sym(struct sym_entry *syme) |
422 | { |
423 | pthread_mutex_lock(&active_symbols_lock); |
424 | list_del_init(&syme->node); |
425 | pthread_mutex_unlock(&active_symbols_lock); |
426 | } |
427 | |
428 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) |
429 | { |
430 | struct rb_node **p = &tree->rb_node; |
431 | struct rb_node *parent = NULL; |
432 | struct sym_entry *iter; |
433 | |
434 | while (*p != NULL) { |
435 | parent = *p; |
436 | iter = rb_entry(parent, struct sym_entry, rb_node); |
437 | |
438 | if (se->weight > iter->weight) |
439 | p = &(*p)->rb_left; |
440 | else |
441 | p = &(*p)->rb_right; |
442 | } |
443 | |
444 | rb_link_node(&se->rb_node, parent, p); |
445 | rb_insert_color(&se->rb_node, tree); |
446 | } |
447 | |
448 | static void print_sym_table(void) |
449 | { |
450 | int printed = 0, j; |
451 | int counter, snap = !display_weighted ? sym_counter : 0; |
452 | float samples_per_sec = samples/delay_secs; |
453 | float ksamples_per_sec = (samples-userspace_samples)/delay_secs; |
454 | float sum_ksamples = 0.0; |
455 | struct sym_entry *syme, *n; |
456 | struct rb_root tmp = RB_ROOT; |
457 | struct rb_node *nd; |
458 | int sym_width = 0, dso_width = 0, dso_short_width = 0; |
459 | const int win_width = winsize.ws_col - 1; |
460 | |
461 | samples = userspace_samples = 0; |
462 | |
463 | /* Sort the active symbols */ |
464 | pthread_mutex_lock(&active_symbols_lock); |
465 | syme = list_entry(active_symbols.next, struct sym_entry, node); |
466 | pthread_mutex_unlock(&active_symbols_lock); |
467 | |
468 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
469 | syme->snap_count = syme->count[snap]; |
470 | if (syme->snap_count != 0) { |
471 | |
472 | if ((hide_user_symbols && |
473 | syme->origin == PERF_RECORD_MISC_USER) || |
474 | (hide_kernel_symbols && |
475 | syme->origin == PERF_RECORD_MISC_KERNEL)) { |
476 | list_remove_active_sym(syme); |
477 | continue; |
478 | } |
479 | syme->weight = sym_weight(syme); |
480 | rb_insert_active_sym(&tmp, syme); |
481 | sum_ksamples += syme->snap_count; |
482 | |
483 | for (j = 0; j < nr_counters; j++) |
484 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; |
485 | } else |
486 | list_remove_active_sym(syme); |
487 | } |
488 | |
489 | puts(CONSOLE_CLEAR); |
490 | |
491 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
492 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", |
493 | samples_per_sec, |
494 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); |
495 | |
496 | if (nr_counters == 1 || !display_weighted) { |
497 | printf("%Ld", (u64)attrs[0].sample_period); |
498 | if (freq) |
499 | printf("Hz "); |
500 | else |
501 | printf(" "); |
502 | } |
503 | |
504 | if (!display_weighted) |
505 | printf("%s", event_name(sym_counter)); |
506 | else for (counter = 0; counter < nr_counters; counter++) { |
507 | if (counter) |
508 | printf("/"); |
509 | |
510 | printf("%s", event_name(counter)); |
511 | } |
512 | |
513 | printf( "], "); |
514 | |
515 | if (target_pid != -1) |
516 | printf(" (target_pid: %d", target_pid); |
517 | else |
518 | printf(" (all"); |
519 | |
520 | if (profile_cpu != -1) |
521 | printf(", cpu: %d)\n", profile_cpu); |
522 | else { |
523 | if (target_pid != -1) |
524 | printf(")\n"); |
525 | else |
526 | printf(", %d CPUs)\n", nr_cpus); |
527 | } |
528 | |
529 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
530 | |
531 | if (sym_filter_entry) { |
532 | show_details(sym_filter_entry); |
533 | return; |
534 | } |
535 | |
536 | /* |
537 | * Find the longest symbol name that will be displayed |
538 | */ |
539 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
540 | syme = rb_entry(nd, struct sym_entry, rb_node); |
541 | if (++printed > print_entries || |
542 | (int)syme->snap_count < count_filter) |
543 | continue; |
544 | |
545 | if (syme->map->dso->long_name_len > dso_width) |
546 | dso_width = syme->map->dso->long_name_len; |
547 | |
548 | if (syme->map->dso->short_name_len > dso_short_width) |
549 | dso_short_width = syme->map->dso->short_name_len; |
550 | |
551 | if (syme->name_len > sym_width) |
552 | sym_width = syme->name_len; |
553 | } |
554 | |
555 | printed = 0; |
556 | |
557 | if (sym_width + dso_width > winsize.ws_col - 29) { |
558 | dso_width = dso_short_width; |
559 | if (sym_width + dso_width > winsize.ws_col - 29) |
560 | sym_width = winsize.ws_col - dso_width - 29; |
561 | } |
562 | putchar('\n'); |
563 | if (nr_counters == 1) |
564 | printf(" samples pcnt"); |
565 | else |
566 | printf(" weight samples pcnt"); |
567 | |
568 | if (verbose) |
569 | printf(" RIP "); |
570 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
571 | printf(" %s _______ _____", |
572 | nr_counters == 1 ? " " : "______"); |
573 | if (verbose) |
574 | printf(" ________________"); |
575 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
576 | printf(" %-*.*s", dso_width, dso_width, graph_line); |
577 | puts("\n"); |
578 | |
579 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
580 | struct symbol *sym; |
581 | double pcnt; |
582 | |
583 | syme = rb_entry(nd, struct sym_entry, rb_node); |
584 | sym = sym_entry__symbol(syme); |
585 | |
586 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
587 | continue; |
588 | |
589 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
590 | sum_ksamples)); |
591 | |
592 | if (nr_counters == 1 || !display_weighted) |
593 | printf("%20.2f ", syme->weight); |
594 | else |
595 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
596 | |
597 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
598 | if (verbose) |
599 | printf(" %016llx", sym->start); |
600 | printf(" %-*.*s", sym_width, sym_width, sym->name); |
601 | printf(" %-*.*s\n", dso_width, dso_width, |
602 | dso_width >= syme->map->dso->long_name_len ? |
603 | syme->map->dso->long_name : |
604 | syme->map->dso->short_name); |
605 | } |
606 | } |
607 | |
608 | static void prompt_integer(int *target, const char *msg) |
609 | { |
610 | char *buf = malloc(0), *p; |
611 | size_t dummy = 0; |
612 | int tmp; |
613 | |
614 | fprintf(stdout, "\n%s: ", msg); |
615 | if (getline(&buf, &dummy, stdin) < 0) |
616 | return; |
617 | |
618 | p = strchr(buf, '\n'); |
619 | if (p) |
620 | *p = 0; |
621 | |
622 | p = buf; |
623 | while(*p) { |
624 | if (!isdigit(*p)) |
625 | goto out_free; |
626 | p++; |
627 | } |
628 | tmp = strtoul(buf, NULL, 10); |
629 | *target = tmp; |
630 | out_free: |
631 | free(buf); |
632 | } |
633 | |
634 | static void prompt_percent(int *target, const char *msg) |
635 | { |
636 | int tmp = 0; |
637 | |
638 | prompt_integer(&tmp, msg); |
639 | if (tmp >= 0 && tmp <= 100) |
640 | *target = tmp; |
641 | } |
642 | |
643 | static void prompt_symbol(struct sym_entry **target, const char *msg) |
644 | { |
645 | char *buf = malloc(0), *p; |
646 | struct sym_entry *syme = *target, *n, *found = NULL; |
647 | size_t dummy = 0; |
648 | |
649 | /* zero counters of active symbol */ |
650 | if (syme) { |
651 | pthread_mutex_lock(&syme->src->lock); |
652 | __zero_source_counters(syme); |
653 | *target = NULL; |
654 | pthread_mutex_unlock(&syme->src->lock); |
655 | } |
656 | |
657 | fprintf(stdout, "\n%s: ", msg); |
658 | if (getline(&buf, &dummy, stdin) < 0) |
659 | goto out_free; |
660 | |
661 | p = strchr(buf, '\n'); |
662 | if (p) |
663 | *p = 0; |
664 | |
665 | pthread_mutex_lock(&active_symbols_lock); |
666 | syme = list_entry(active_symbols.next, struct sym_entry, node); |
667 | pthread_mutex_unlock(&active_symbols_lock); |
668 | |
669 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
670 | struct symbol *sym = sym_entry__symbol(syme); |
671 | |
672 | if (!strcmp(buf, sym->name)) { |
673 | found = syme; |
674 | break; |
675 | } |
676 | } |
677 | |
678 | if (!found) { |
679 | fprintf(stderr, "Sorry, %s is not active.\n", buf); |
680 | sleep(1); |
681 | return; |
682 | } else |
683 | parse_source(found); |
684 | |
685 | out_free: |
686 | free(buf); |
687 | } |
688 | |
689 | static void print_mapped_keys(void) |
690 | { |
691 | char *name = NULL; |
692 | |
693 | if (sym_filter_entry) { |
694 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); |
695 | name = sym->name; |
696 | } |
697 | |
698 | fprintf(stdout, "\nMapped keys:\n"); |
699 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); |
700 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); |
701 | |
702 | if (nr_counters > 1) |
703 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter)); |
704 | |
705 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
706 | |
707 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
708 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
709 | fprintf(stdout, "\t[S] stop annotation.\n"); |
710 | |
711 | if (nr_counters > 1) |
712 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
713 | |
714 | fprintf(stdout, |
715 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
716 | hide_kernel_symbols ? "yes" : "no"); |
717 | fprintf(stdout, |
718 | "\t[U] hide user symbols. \t(%s)\n", |
719 | hide_user_symbols ? "yes" : "no"); |
720 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); |
721 | fprintf(stdout, "\t[qQ] quit.\n"); |
722 | } |
723 | |
724 | static int key_mapped(int c) |
725 | { |
726 | switch (c) { |
727 | case 'd': |
728 | case 'e': |
729 | case 'f': |
730 | case 'z': |
731 | case 'q': |
732 | case 'Q': |
733 | case 'K': |
734 | case 'U': |
735 | case 'F': |
736 | case 's': |
737 | case 'S': |
738 | return 1; |
739 | case 'E': |
740 | case 'w': |
741 | return nr_counters > 1 ? 1 : 0; |
742 | default: |
743 | break; |
744 | } |
745 | |
746 | return 0; |
747 | } |
748 | |
749 | static void handle_keypress(int c) |
750 | { |
751 | if (!key_mapped(c)) { |
752 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
753 | struct termios tc, save; |
754 | |
755 | print_mapped_keys(); |
756 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); |
757 | fflush(stdout); |
758 | |
759 | tcgetattr(0, &save); |
760 | tc = save; |
761 | tc.c_lflag &= ~(ICANON | ECHO); |
762 | tc.c_cc[VMIN] = 0; |
763 | tc.c_cc[VTIME] = 0; |
764 | tcsetattr(0, TCSANOW, &tc); |
765 | |
766 | poll(&stdin_poll, 1, -1); |
767 | c = getc(stdin); |
768 | |
769 | tcsetattr(0, TCSAFLUSH, &save); |
770 | if (!key_mapped(c)) |
771 | return; |
772 | } |
773 | |
774 | switch (c) { |
775 | case 'd': |
776 | prompt_integer(&delay_secs, "Enter display delay"); |
777 | if (delay_secs < 1) |
778 | delay_secs = 1; |
779 | break; |
780 | case 'e': |
781 | prompt_integer(&print_entries, "Enter display entries (lines)"); |
782 | if (print_entries == 0) { |
783 | sig_winch_handler(SIGWINCH); |
784 | signal(SIGWINCH, sig_winch_handler); |
785 | } else |
786 | signal(SIGWINCH, SIG_DFL); |
787 | break; |
788 | case 'E': |
789 | if (nr_counters > 1) { |
790 | int i; |
791 | |
792 | fprintf(stderr, "\nAvailable events:"); |
793 | for (i = 0; i < nr_counters; i++) |
794 | fprintf(stderr, "\n\t%d %s", i, event_name(i)); |
795 | |
796 | prompt_integer(&sym_counter, "Enter details event counter"); |
797 | |
798 | if (sym_counter >= nr_counters) { |
799 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0)); |
800 | sym_counter = 0; |
801 | sleep(1); |
802 | } |
803 | } else sym_counter = 0; |
804 | break; |
805 | case 'f': |
806 | prompt_integer(&count_filter, "Enter display event count filter"); |
807 | break; |
808 | case 'F': |
809 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
810 | break; |
811 | case 'K': |
812 | hide_kernel_symbols = !hide_kernel_symbols; |
813 | break; |
814 | case 'q': |
815 | case 'Q': |
816 | printf("exiting.\n"); |
817 | if (dump_symtab) |
818 | dsos__fprintf(stderr); |
819 | exit(0); |
820 | case 's': |
821 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); |
822 | break; |
823 | case 'S': |
824 | if (!sym_filter_entry) |
825 | break; |
826 | else { |
827 | struct sym_entry *syme = sym_filter_entry; |
828 | |
829 | pthread_mutex_lock(&syme->src->lock); |
830 | sym_filter_entry = NULL; |
831 | __zero_source_counters(syme); |
832 | pthread_mutex_unlock(&syme->src->lock); |
833 | } |
834 | break; |
835 | case 'U': |
836 | hide_user_symbols = !hide_user_symbols; |
837 | break; |
838 | case 'w': |
839 | display_weighted = ~display_weighted; |
840 | break; |
841 | case 'z': |
842 | zero = ~zero; |
843 | break; |
844 | default: |
845 | break; |
846 | } |
847 | } |
848 | |
849 | static void *display_thread(void *arg __used) |
850 | { |
851 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
852 | struct termios tc, save; |
853 | int delay_msecs, c; |
854 | |
855 | tcgetattr(0, &save); |
856 | tc = save; |
857 | tc.c_lflag &= ~(ICANON | ECHO); |
858 | tc.c_cc[VMIN] = 0; |
859 | tc.c_cc[VTIME] = 0; |
860 | |
861 | repeat: |
862 | delay_msecs = delay_secs * 1000; |
863 | tcsetattr(0, TCSANOW, &tc); |
864 | /* trash return*/ |
865 | getc(stdin); |
866 | |
867 | do { |
868 | print_sym_table(); |
869 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); |
870 | |
871 | c = getc(stdin); |
872 | tcsetattr(0, TCSAFLUSH, &save); |
873 | |
874 | handle_keypress(c); |
875 | goto repeat; |
876 | |
877 | return NULL; |
878 | } |
879 | |
880 | /* Tag samples to be skipped. */ |
881 | static const char *skip_symbols[] = { |
882 | "default_idle", |
883 | "cpu_idle", |
884 | "enter_idle", |
885 | "exit_idle", |
886 | "mwait_idle", |
887 | "mwait_idle_with_hints", |
888 | "poll_idle", |
889 | "ppc64_runlatch_off", |
890 | "pseries_dedicated_idle_sleep", |
891 | NULL |
892 | }; |
893 | |
894 | static int symbol_filter(struct map *map, struct symbol *sym) |
895 | { |
896 | struct sym_entry *syme; |
897 | const char *name = sym->name; |
898 | int i; |
899 | |
900 | /* |
901 | * ppc64 uses function descriptors and appends a '.' to the |
902 | * start of every instruction address. Remove it. |
903 | */ |
904 | if (name[0] == '.') |
905 | name++; |
906 | |
907 | if (!strcmp(name, "_text") || |
908 | !strcmp(name, "_etext") || |
909 | !strcmp(name, "_sinittext") || |
910 | !strncmp("init_module", name, 11) || |
911 | !strncmp("cleanup_module", name, 14) || |
912 | strstr(name, "_text_start") || |
913 | strstr(name, "_text_end")) |
914 | return 1; |
915 | |
916 | syme = symbol__priv(sym); |
917 | syme->map = map; |
918 | syme->src = NULL; |
919 | |
920 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { |
921 | /* schedule initial sym_filter_entry setup */ |
922 | sym_filter_entry_sched = syme; |
923 | sym_filter = NULL; |
924 | } |
925 | |
926 | for (i = 0; skip_symbols[i]; i++) { |
927 | if (!strcmp(skip_symbols[i], name)) { |
928 | syme->skip = 1; |
929 | break; |
930 | } |
931 | } |
932 | |
933 | if (!syme->skip) |
934 | syme->name_len = strlen(sym->name); |
935 | |
936 | return 0; |
937 | } |
938 | |
939 | static void event__process_sample(const event_t *self, |
940 | struct perf_session *session, int counter) |
941 | { |
942 | u64 ip = self->ip.ip; |
943 | struct sym_entry *syme; |
944 | struct addr_location al; |
945 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
946 | |
947 | ++samples; |
948 | |
949 | switch (origin) { |
950 | case PERF_RECORD_MISC_USER: |
951 | ++userspace_samples; |
952 | if (hide_user_symbols) |
953 | return; |
954 | break; |
955 | case PERF_RECORD_MISC_KERNEL: |
956 | if (hide_kernel_symbols) |
957 | return; |
958 | break; |
959 | default: |
960 | return; |
961 | } |
962 | |
963 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
964 | al.filtered) |
965 | return; |
966 | |
967 | if (al.sym == NULL) { |
968 | /* |
969 | * As we do lazy loading of symtabs we only will know if the |
970 | * specified vmlinux file is invalid when we actually have a |
971 | * hit in kernel space and then try to load it. So if we get |
972 | * here and there are _no_ symbols in the DSO backing the |
973 | * kernel map, bail out. |
974 | * |
975 | * We may never get here, for instance, if we use -K/ |
976 | * --hide-kernel-symbols, even if the user specifies an |
977 | * invalid --vmlinux ;-) |
978 | */ |
979 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && |
980 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
981 | pr_err("The %s file can't be used\n", |
982 | symbol_conf.vmlinux_name); |
983 | exit(1); |
984 | } |
985 | |
986 | return; |
987 | } |
988 | |
989 | /* let's see, whether we need to install initial sym_filter_entry */ |
990 | if (sym_filter_entry_sched) { |
991 | sym_filter_entry = sym_filter_entry_sched; |
992 | sym_filter_entry_sched = NULL; |
993 | parse_source(sym_filter_entry); |
994 | } |
995 | |
996 | syme = symbol__priv(al.sym); |
997 | if (!syme->skip) { |
998 | syme->count[counter]++; |
999 | syme->origin = origin; |
1000 | record_precise_ip(syme, counter, ip); |
1001 | pthread_mutex_lock(&active_symbols_lock); |
1002 | if (list_empty(&syme->node) || !syme->node.next) |
1003 | __list_insert_active_sym(syme); |
1004 | pthread_mutex_unlock(&active_symbols_lock); |
1005 | } |
1006 | } |
1007 | |
1008 | static int event__process(event_t *event, struct perf_session *session) |
1009 | { |
1010 | switch (event->header.type) { |
1011 | case PERF_RECORD_COMM: |
1012 | event__process_comm(event, session); |
1013 | break; |
1014 | case PERF_RECORD_MMAP: |
1015 | event__process_mmap(event, session); |
1016 | break; |
1017 | case PERF_RECORD_FORK: |
1018 | case PERF_RECORD_EXIT: |
1019 | event__process_task(event, session); |
1020 | break; |
1021 | default: |
1022 | break; |
1023 | } |
1024 | |
1025 | return 0; |
1026 | } |
1027 | |
1028 | struct mmap_data { |
1029 | int counter; |
1030 | void *base; |
1031 | int mask; |
1032 | unsigned int prev; |
1033 | }; |
1034 | |
1035 | static unsigned int mmap_read_head(struct mmap_data *md) |
1036 | { |
1037 | struct perf_event_mmap_page *pc = md->base; |
1038 | int head; |
1039 | |
1040 | head = pc->data_head; |
1041 | rmb(); |
1042 | |
1043 | return head; |
1044 | } |
1045 | |
1046 | static void perf_session__mmap_read_counter(struct perf_session *self, |
1047 | struct mmap_data *md) |
1048 | { |
1049 | unsigned int head = mmap_read_head(md); |
1050 | unsigned int old = md->prev; |
1051 | unsigned char *data = md->base + page_size; |
1052 | int diff; |
1053 | |
1054 | /* |
1055 | * If we're further behind than half the buffer, there's a chance |
1056 | * the writer will bite our tail and mess up the samples under us. |
1057 | * |
1058 | * If we somehow ended up ahead of the head, we got messed up. |
1059 | * |
1060 | * In either case, truncate and restart at head. |
1061 | */ |
1062 | diff = head - old; |
1063 | if (diff > md->mask / 2 || diff < 0) { |
1064 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
1065 | |
1066 | /* |
1067 | * head points to a known good entry, start there. |
1068 | */ |
1069 | old = head; |
1070 | } |
1071 | |
1072 | for (; old != head;) { |
1073 | event_t *event = (event_t *)&data[old & md->mask]; |
1074 | |
1075 | event_t event_copy; |
1076 | |
1077 | size_t size = event->header.size; |
1078 | |
1079 | /* |
1080 | * Event straddles the mmap boundary -- header should always |
1081 | * be inside due to u64 alignment of output. |
1082 | */ |
1083 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
1084 | unsigned int offset = old; |
1085 | unsigned int len = min(sizeof(*event), size), cpy; |
1086 | void *dst = &event_copy; |
1087 | |
1088 | do { |
1089 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
1090 | memcpy(dst, &data[offset & md->mask], cpy); |
1091 | offset += cpy; |
1092 | dst += cpy; |
1093 | len -= cpy; |
1094 | } while (len); |
1095 | |
1096 | event = &event_copy; |
1097 | } |
1098 | |
1099 | if (event->header.type == PERF_RECORD_SAMPLE) |
1100 | event__process_sample(event, self, md->counter); |
1101 | else |
1102 | event__process(event, self); |
1103 | old += size; |
1104 | } |
1105 | |
1106 | md->prev = old; |
1107 | } |
1108 | |
1109 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
1110 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
1111 | |
1112 | static void perf_session__mmap_read(struct perf_session *self) |
1113 | { |
1114 | int i, counter; |
1115 | |
1116 | for (i = 0; i < nr_cpus; i++) { |
1117 | for (counter = 0; counter < nr_counters; counter++) |
1118 | perf_session__mmap_read_counter(self, &mmap_array[i][counter]); |
1119 | } |
1120 | } |
1121 | |
1122 | int nr_poll; |
1123 | int group_fd; |
1124 | |
1125 | static void start_counter(int i, int counter) |
1126 | { |
1127 | struct perf_event_attr *attr; |
1128 | int cpu; |
1129 | |
1130 | cpu = profile_cpu; |
1131 | if (target_pid == -1 && profile_cpu == -1) |
1132 | cpu = cpumap[i]; |
1133 | |
1134 | attr = attrs + counter; |
1135 | |
1136 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1137 | |
1138 | if (freq) { |
1139 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
1140 | attr->freq = 1; |
1141 | attr->sample_freq = freq; |
1142 | } |
1143 | |
1144 | attr->inherit = (cpu < 0) && inherit; |
1145 | attr->mmap = 1; |
1146 | |
1147 | try_again: |
1148 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); |
1149 | |
1150 | if (fd[i][counter] < 0) { |
1151 | int err = errno; |
1152 | |
1153 | if (err == EPERM || err == EACCES) |
1154 | die("No permission - are you root?\n"); |
1155 | /* |
1156 | * If it's cycles then fall back to hrtimer |
1157 | * based cpu-clock-tick sw counter, which |
1158 | * is always available even if no PMU support: |
1159 | */ |
1160 | if (attr->type == PERF_TYPE_HARDWARE |
1161 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { |
1162 | |
1163 | if (verbose) |
1164 | warning(" ... trying to fall back to cpu-clock-ticks\n"); |
1165 | |
1166 | attr->type = PERF_TYPE_SOFTWARE; |
1167 | attr->config = PERF_COUNT_SW_CPU_CLOCK; |
1168 | goto try_again; |
1169 | } |
1170 | printf("\n"); |
1171 | error("perfcounter syscall returned with %d (%s)\n", |
1172 | fd[i][counter], strerror(err)); |
1173 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
1174 | exit(-1); |
1175 | } |
1176 | assert(fd[i][counter] >= 0); |
1177 | fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); |
1178 | |
1179 | /* |
1180 | * First counter acts as the group leader: |
1181 | */ |
1182 | if (group && group_fd == -1) |
1183 | group_fd = fd[i][counter]; |
1184 | |
1185 | event_array[nr_poll].fd = fd[i][counter]; |
1186 | event_array[nr_poll].events = POLLIN; |
1187 | nr_poll++; |
1188 | |
1189 | mmap_array[i][counter].counter = counter; |
1190 | mmap_array[i][counter].prev = 0; |
1191 | mmap_array[i][counter].mask = mmap_pages*page_size - 1; |
1192 | mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, |
1193 | PROT_READ, MAP_SHARED, fd[i][counter], 0); |
1194 | if (mmap_array[i][counter].base == MAP_FAILED) |
1195 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
1196 | } |
1197 | |
1198 | static int __cmd_top(void) |
1199 | { |
1200 | pthread_t thread; |
1201 | int i, counter; |
1202 | int ret; |
1203 | /* |
1204 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
1205 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
1206 | */ |
1207 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); |
1208 | if (session == NULL) |
1209 | return -ENOMEM; |
1210 | |
1211 | if (target_pid != -1) |
1212 | event__synthesize_thread(target_pid, event__process, session); |
1213 | else |
1214 | event__synthesize_threads(event__process, session); |
1215 | |
1216 | for (i = 0; i < nr_cpus; i++) { |
1217 | group_fd = -1; |
1218 | for (counter = 0; counter < nr_counters; counter++) |
1219 | start_counter(i, counter); |
1220 | } |
1221 | |
1222 | /* Wait for a minimal set of events before starting the snapshot */ |
1223 | poll(event_array, nr_poll, 100); |
1224 | |
1225 | perf_session__mmap_read(session); |
1226 | |
1227 | if (pthread_create(&thread, NULL, display_thread, NULL)) { |
1228 | printf("Could not create display thread.\n"); |
1229 | exit(-1); |
1230 | } |
1231 | |
1232 | if (realtime_prio) { |
1233 | struct sched_param param; |
1234 | |
1235 | param.sched_priority = realtime_prio; |
1236 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
1237 | printf("Could not set realtime priority.\n"); |
1238 | exit(-1); |
1239 | } |
1240 | } |
1241 | |
1242 | while (1) { |
1243 | int hits = samples; |
1244 | |
1245 | perf_session__mmap_read(session); |
1246 | |
1247 | if (hits == samples) |
1248 | ret = poll(event_array, nr_poll, 100); |
1249 | } |
1250 | |
1251 | return 0; |
1252 | } |
1253 | |
1254 | static const char * const top_usage[] = { |
1255 | "perf top [<options>]", |
1256 | NULL |
1257 | }; |
1258 | |
1259 | static const struct option options[] = { |
1260 | OPT_CALLBACK('e', "event", NULL, "event", |
1261 | "event selector. use 'perf list' to list available events", |
1262 | parse_events), |
1263 | OPT_INTEGER('c', "count", &default_interval, |
1264 | "event period to sample"), |
1265 | OPT_INTEGER('p', "pid", &target_pid, |
1266 | "profile events on existing pid"), |
1267 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1268 | "system-wide collection from all CPUs"), |
1269 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1270 | "CPU to profile on"), |
1271 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1272 | "file", "vmlinux pathname"), |
1273 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, |
1274 | "hide kernel symbols"), |
1275 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
1276 | "number of mmap data pages"), |
1277 | OPT_INTEGER('r', "realtime", &realtime_prio, |
1278 | "collect data with this RT SCHED_FIFO priority"), |
1279 | OPT_INTEGER('d', "delay", &delay_secs, |
1280 | "number of seconds to delay between refreshes"), |
1281 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, |
1282 | "dump the symbol table used for profiling"), |
1283 | OPT_INTEGER('f', "count-filter", &count_filter, |
1284 | "only display functions with more events than this"), |
1285 | OPT_BOOLEAN('g', "group", &group, |
1286 | "put the counters into a counter group"), |
1287 | OPT_BOOLEAN('i', "inherit", &inherit, |
1288 | "child tasks inherit counters"), |
1289 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
1290 | "symbol to annotate"), |
1291 | OPT_BOOLEAN('z', "zero", &zero, |
1292 | "zero history across updates"), |
1293 | OPT_INTEGER('F', "freq", &freq, |
1294 | "profile at this frequency"), |
1295 | OPT_INTEGER('E', "entries", &print_entries, |
1296 | "display this many functions"), |
1297 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, |
1298 | "hide user symbols"), |
1299 | OPT_BOOLEAN('v', "verbose", &verbose, |
1300 | "be more verbose (show counter open errors, etc)"), |
1301 | OPT_END() |
1302 | }; |
1303 | |
1304 | int cmd_top(int argc, const char **argv, const char *prefix __used) |
1305 | { |
1306 | int counter; |
1307 | |
1308 | page_size = sysconf(_SC_PAGE_SIZE); |
1309 | |
1310 | argc = parse_options(argc, argv, options, top_usage, 0); |
1311 | if (argc) |
1312 | usage_with_options(top_usage, options); |
1313 | |
1314 | /* CPU and PID are mutually exclusive */ |
1315 | if (target_pid != -1 && profile_cpu != -1) { |
1316 | printf("WARNING: PID switch overriding CPU\n"); |
1317 | sleep(1); |
1318 | profile_cpu = -1; |
1319 | } |
1320 | |
1321 | if (!nr_counters) |
1322 | nr_counters = 1; |
1323 | |
1324 | symbol_conf.priv_size = (sizeof(struct sym_entry) + |
1325 | (nr_counters + 1) * sizeof(unsigned long)); |
1326 | |
1327 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1328 | if (symbol__init() < 0) |
1329 | return -1; |
1330 | |
1331 | if (delay_secs < 1) |
1332 | delay_secs = 1; |
1333 | |
1334 | /* |
1335 | * User specified count overrides default frequency. |
1336 | */ |
1337 | if (default_interval) |
1338 | freq = 0; |
1339 | else if (freq) { |
1340 | default_interval = freq; |
1341 | } else { |
1342 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
1343 | exit(EXIT_FAILURE); |
1344 | } |
1345 | |
1346 | /* |
1347 | * Fill in the ones not specifically initialized via -c: |
1348 | */ |
1349 | for (counter = 0; counter < nr_counters; counter++) { |
1350 | if (attrs[counter].sample_period) |
1351 | continue; |
1352 | |
1353 | attrs[counter].sample_period = default_interval; |
1354 | } |
1355 | |
1356 | if (target_pid != -1 || profile_cpu != -1) |
1357 | nr_cpus = 1; |
1358 | else |
1359 | nr_cpus = read_cpu_map(); |
1360 | |
1361 | get_term_dimensions(&winsize); |
1362 | if (print_entries == 0) { |
1363 | update_print_entries(&winsize); |
1364 | signal(SIGWINCH, sig_winch_handler); |
1365 | } |
1366 | |
1367 | return __cmd_top(); |
1368 | } |
1369 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9