diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 503 |
1 files changed, 304 insertions, 199 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e23bc74e734f..e0a374d0e43a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -22,6 +22,7 @@ | |||
22 | 22 | ||
23 | #include "util/symbol.h" | 23 | #include "util/symbol.h" |
24 | #include "util/color.h" | 24 | #include "util/color.h" |
25 | #include "util/thread.h" | ||
25 | #include "util/util.h" | 26 | #include "util/util.h" |
26 | #include <linux/rbtree.h> | 27 | #include <linux/rbtree.h> |
27 | #include "util/parse-options.h" | 28 | #include "util/parse-options.h" |
@@ -54,26 +55,31 @@ | |||
54 | 55 | ||
55 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | 56 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; |
56 | 57 | ||
57 | static int system_wide = 0; | 58 | static int system_wide = 0; |
58 | 59 | ||
59 | static int default_interval = 100000; | 60 | static int default_interval = 0; |
60 | 61 | ||
61 | static int count_filter = 5; | 62 | static int count_filter = 5; |
62 | static int print_entries = 15; | 63 | static int print_entries; |
63 | 64 | ||
64 | static int target_pid = -1; | 65 | static int target_pid = -1; |
65 | static int inherit = 0; | 66 | static int inherit = 0; |
66 | static int profile_cpu = -1; | 67 | static int profile_cpu = -1; |
67 | static int nr_cpus = 0; | 68 | static int nr_cpus = 0; |
68 | static unsigned int realtime_prio = 0; | 69 | static unsigned int realtime_prio = 0; |
69 | static int group = 0; | 70 | static int group = 0; |
70 | static unsigned int page_size; | 71 | static unsigned int page_size; |
71 | static unsigned int mmap_pages = 16; | 72 | static unsigned int mmap_pages = 16; |
72 | static int freq = 0; | 73 | static int freq = 1000; /* 1 KHz */ |
73 | 74 | ||
74 | static int delay_secs = 2; | 75 | static int delay_secs = 2; |
75 | static int zero; | 76 | static int zero = 0; |
76 | static int dump_symtab; | 77 | static int dump_symtab = 0; |
78 | |||
79 | static bool hide_kernel_symbols = false; | ||
80 | static bool hide_user_symbols = false; | ||
81 | static struct winsize winsize; | ||
82 | struct symbol_conf symbol_conf; | ||
77 | 83 | ||
78 | /* | 84 | /* |
79 | * Source | 85 | * Source |
@@ -86,83 +92,126 @@ struct source_line { | |||
86 | struct source_line *next; | 92 | struct source_line *next; |
87 | }; | 93 | }; |
88 | 94 | ||
89 | static char *sym_filter = NULL; | 95 | static char *sym_filter = NULL; |
90 | struct sym_entry *sym_filter_entry = NULL; | 96 | struct sym_entry *sym_filter_entry = NULL; |
91 | static int sym_pcnt_filter = 5; | 97 | static int sym_pcnt_filter = 5; |
92 | static int sym_counter = 0; | 98 | static int sym_counter = 0; |
93 | static int display_weighted = -1; | 99 | static int display_weighted = -1; |
94 | 100 | ||
95 | /* | 101 | /* |
96 | * Symbols | 102 | * Symbols |
97 | */ | 103 | */ |
98 | 104 | ||
99 | static u64 min_ip; | 105 | struct sym_entry_source { |
100 | static u64 max_ip = -1ll; | 106 | struct source_line *source; |
107 | struct source_line *lines; | ||
108 | struct source_line **lines_tail; | ||
109 | pthread_mutex_t lock; | ||
110 | }; | ||
101 | 111 | ||
102 | struct sym_entry { | 112 | struct sym_entry { |
103 | struct rb_node rb_node; | 113 | struct rb_node rb_node; |
104 | struct list_head node; | 114 | struct list_head node; |
105 | unsigned long count[MAX_COUNTERS]; | ||
106 | unsigned long snap_count; | 115 | unsigned long snap_count; |
107 | double weight; | 116 | double weight; |
108 | int skip; | 117 | int skip; |
109 | struct source_line *source; | 118 | u16 name_len; |
110 | struct source_line *lines; | 119 | u8 origin; |
111 | struct source_line **lines_tail; | 120 | struct map *map; |
112 | pthread_mutex_t source_lock; | 121 | struct sym_entry_source *src; |
122 | unsigned long count[0]; | ||
113 | }; | 123 | }; |
114 | 124 | ||
115 | /* | 125 | /* |
116 | * Source functions | 126 | * Source functions |
117 | */ | 127 | */ |
118 | 128 | ||
129 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
130 | { | ||
131 | return ((void *)self) + symbol_conf.priv_size; | ||
132 | } | ||
133 | |||
134 | static void get_term_dimensions(struct winsize *ws) | ||
135 | { | ||
136 | char *s = getenv("LINES"); | ||
137 | |||
138 | if (s != NULL) { | ||
139 | ws->ws_row = atoi(s); | ||
140 | s = getenv("COLUMNS"); | ||
141 | if (s != NULL) { | ||
142 | ws->ws_col = atoi(s); | ||
143 | if (ws->ws_row && ws->ws_col) | ||
144 | return; | ||
145 | } | ||
146 | } | ||
147 | #ifdef TIOCGWINSZ | ||
148 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
149 | ws->ws_row && ws->ws_col) | ||
150 | return; | ||
151 | #endif | ||
152 | ws->ws_row = 25; | ||
153 | ws->ws_col = 80; | ||
154 | } | ||
155 | |||
156 | static void update_print_entries(struct winsize *ws) | ||
157 | { | ||
158 | print_entries = ws->ws_row; | ||
159 | |||
160 | if (print_entries > 9) | ||
161 | print_entries -= 9; | ||
162 | } | ||
163 | |||
164 | static void sig_winch_handler(int sig __used) | ||
165 | { | ||
166 | get_term_dimensions(&winsize); | ||
167 | update_print_entries(&winsize); | ||
168 | } | ||
169 | |||
119 | static void parse_source(struct sym_entry *syme) | 170 | static void parse_source(struct sym_entry *syme) |
120 | { | 171 | { |
121 | struct symbol *sym; | 172 | struct symbol *sym; |
122 | struct module *module; | 173 | struct sym_entry_source *source; |
123 | struct section *section = NULL; | 174 | struct map *map; |
124 | FILE *file; | 175 | FILE *file; |
125 | char command[PATH_MAX*2]; | 176 | char command[PATH_MAX*2]; |
126 | const char *path = vmlinux_name; | 177 | const char *path; |
127 | u64 start, end, len; | 178 | u64 len; |
128 | 179 | ||
129 | if (!syme) | 180 | if (!syme) |
130 | return; | 181 | return; |
131 | 182 | ||
132 | if (syme->lines) { | 183 | if (syme->src == NULL) { |
133 | pthread_mutex_lock(&syme->source_lock); | 184 | syme->src = zalloc(sizeof(*source)); |
134 | goto out_assign; | 185 | if (syme->src == NULL) |
186 | return; | ||
187 | pthread_mutex_init(&syme->src->lock, NULL); | ||
135 | } | 188 | } |
136 | 189 | ||
137 | sym = (struct symbol *)(syme + 1); | 190 | source = syme->src; |
138 | module = sym->module; | ||
139 | |||
140 | if (module) | ||
141 | path = module->path; | ||
142 | if (!path) | ||
143 | return; | ||
144 | |||
145 | start = sym->obj_start; | ||
146 | if (!start) | ||
147 | start = sym->start; | ||
148 | 191 | ||
149 | if (module) { | 192 | if (source->lines) { |
150 | section = module->sections->find_section(module->sections, ".text"); | 193 | pthread_mutex_lock(&source->lock); |
151 | if (section) | 194 | goto out_assign; |
152 | start -= section->vma; | ||
153 | } | 195 | } |
154 | 196 | ||
155 | end = start + sym->end - sym->start + 1; | 197 | sym = sym_entry__symbol(syme); |
198 | map = syme->map; | ||
199 | path = map->dso->long_name; | ||
200 | |||
156 | len = sym->end - sym->start; | 201 | len = sym->end - sym->start; |
157 | 202 | ||
158 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path); | 203 | sprintf(command, |
204 | "objdump --start-address=0x%016Lx " | ||
205 | "--stop-address=0x%016Lx -dS %s", | ||
206 | map->unmap_ip(map, sym->start), | ||
207 | map->unmap_ip(map, sym->end), path); | ||
159 | 208 | ||
160 | file = popen(command, "r"); | 209 | file = popen(command, "r"); |
161 | if (!file) | 210 | if (!file) |
162 | return; | 211 | return; |
163 | 212 | ||
164 | pthread_mutex_lock(&syme->source_lock); | 213 | pthread_mutex_lock(&source->lock); |
165 | syme->lines_tail = &syme->lines; | 214 | source->lines_tail = &source->lines; |
166 | while (!feof(file)) { | 215 | while (!feof(file)) { |
167 | struct source_line *src; | 216 | struct source_line *src; |
168 | size_t dummy = 0; | 217 | size_t dummy = 0; |
@@ -182,24 +231,22 @@ static void parse_source(struct sym_entry *syme) | |||
182 | *c = 0; | 231 | *c = 0; |
183 | 232 | ||
184 | src->next = NULL; | 233 | src->next = NULL; |
185 | *syme->lines_tail = src; | 234 | *source->lines_tail = src; |
186 | syme->lines_tail = &src->next; | 235 | source->lines_tail = &src->next; |
187 | 236 | ||
188 | if (strlen(src->line)>8 && src->line[8] == ':') { | 237 | if (strlen(src->line)>8 && src->line[8] == ':') { |
189 | src->eip = strtoull(src->line, NULL, 16); | 238 | src->eip = strtoull(src->line, NULL, 16); |
190 | if (section) | 239 | src->eip = map->unmap_ip(map, src->eip); |
191 | src->eip += section->vma; | ||
192 | } | 240 | } |
193 | if (strlen(src->line)>8 && src->line[16] == ':') { | 241 | if (strlen(src->line)>8 && src->line[16] == ':') { |
194 | src->eip = strtoull(src->line, NULL, 16); | 242 | src->eip = strtoull(src->line, NULL, 16); |
195 | if (section) | 243 | src->eip = map->unmap_ip(map, src->eip); |
196 | src->eip += section->vma; | ||
197 | } | 244 | } |
198 | } | 245 | } |
199 | pclose(file); | 246 | pclose(file); |
200 | out_assign: | 247 | out_assign: |
201 | sym_filter_entry = syme; | 248 | sym_filter_entry = syme; |
202 | pthread_mutex_unlock(&syme->source_lock); | 249 | pthread_mutex_unlock(&source->lock); |
203 | } | 250 | } |
204 | 251 | ||
205 | static void __zero_source_counters(struct sym_entry *syme) | 252 | static void __zero_source_counters(struct sym_entry *syme) |
@@ -207,7 +254,7 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
207 | int i; | 254 | int i; |
208 | struct source_line *line; | 255 | struct source_line *line; |
209 | 256 | ||
210 | line = syme->lines; | 257 | line = syme->src->lines; |
211 | while (line) { | 258 | while (line) { |
212 | for (i = 0; i < nr_counters; i++) | 259 | for (i = 0; i < nr_counters; i++) |
213 | line->count[i] = 0; | 260 | line->count[i] = 0; |
@@ -222,13 +269,13 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
222 | if (syme != sym_filter_entry) | 269 | if (syme != sym_filter_entry) |
223 | return; | 270 | return; |
224 | 271 | ||
225 | if (pthread_mutex_trylock(&syme->source_lock)) | 272 | if (pthread_mutex_trylock(&syme->src->lock)) |
226 | return; | 273 | return; |
227 | 274 | ||
228 | if (!syme->source) | 275 | if (syme->src == NULL || syme->src->source == NULL) |
229 | goto out_unlock; | 276 | goto out_unlock; |
230 | 277 | ||
231 | for (line = syme->lines; line; line = line->next) { | 278 | for (line = syme->src->lines; line; line = line->next) { |
232 | if (line->eip == ip) { | 279 | if (line->eip == ip) { |
233 | line->count[counter]++; | 280 | line->count[counter]++; |
234 | break; | 281 | break; |
@@ -237,32 +284,25 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
237 | break; | 284 | break; |
238 | } | 285 | } |
239 | out_unlock: | 286 | out_unlock: |
240 | pthread_mutex_unlock(&syme->source_lock); | 287 | pthread_mutex_unlock(&syme->src->lock); |
241 | } | 288 | } |
242 | 289 | ||
243 | static void lookup_sym_source(struct sym_entry *syme) | 290 | static void lookup_sym_source(struct sym_entry *syme) |
244 | { | 291 | { |
245 | struct symbol *symbol = (struct symbol *)(syme + 1); | 292 | struct symbol *symbol = sym_entry__symbol(syme); |
246 | struct source_line *line; | 293 | struct source_line *line; |
247 | char pattern[PATH_MAX]; | 294 | char pattern[PATH_MAX]; |
248 | char *idx; | ||
249 | 295 | ||
250 | sprintf(pattern, "<%s>:", symbol->name); | 296 | sprintf(pattern, "<%s>:", symbol->name); |
251 | 297 | ||
252 | if (symbol->module) { | 298 | pthread_mutex_lock(&syme->src->lock); |
253 | idx = strstr(pattern, "\t"); | 299 | for (line = syme->src->lines; line; line = line->next) { |
254 | if (idx) | ||
255 | *idx = 0; | ||
256 | } | ||
257 | |||
258 | pthread_mutex_lock(&syme->source_lock); | ||
259 | for (line = syme->lines; line; line = line->next) { | ||
260 | if (strstr(line->line, pattern)) { | 300 | if (strstr(line->line, pattern)) { |
261 | syme->source = line; | 301 | syme->src->source = line; |
262 | break; | 302 | break; |
263 | } | 303 | } |
264 | } | 304 | } |
265 | pthread_mutex_unlock(&syme->source_lock); | 305 | pthread_mutex_unlock(&syme->src->lock); |
266 | } | 306 | } |
267 | 307 | ||
268 | static void show_lines(struct source_line *queue, int count, int total) | 308 | static void show_lines(struct source_line *queue, int count, int total) |
@@ -292,24 +332,24 @@ static void show_details(struct sym_entry *syme) | |||
292 | if (!syme) | 332 | if (!syme) |
293 | return; | 333 | return; |
294 | 334 | ||
295 | if (!syme->source) | 335 | if (!syme->src->source) |
296 | lookup_sym_source(syme); | 336 | lookup_sym_source(syme); |
297 | 337 | ||
298 | if (!syme->source) | 338 | if (!syme->src->source) |
299 | return; | 339 | return; |
300 | 340 | ||
301 | symbol = (struct symbol *)(syme + 1); | 341 | symbol = sym_entry__symbol(syme); |
302 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); | 342 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); |
303 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 343 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
304 | 344 | ||
305 | pthread_mutex_lock(&syme->source_lock); | 345 | pthread_mutex_lock(&syme->src->lock); |
306 | line = syme->source; | 346 | line = syme->src->source; |
307 | while (line) { | 347 | while (line) { |
308 | total += line->count[sym_counter]; | 348 | total += line->count[sym_counter]; |
309 | line = line->next; | 349 | line = line->next; |
310 | } | 350 | } |
311 | 351 | ||
312 | line = syme->source; | 352 | line = syme->src->source; |
313 | while (line) { | 353 | while (line) { |
314 | float pcnt = 0.0; | 354 | float pcnt = 0.0; |
315 | 355 | ||
@@ -334,13 +374,13 @@ static void show_details(struct sym_entry *syme) | |||
334 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | 374 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; |
335 | line = line->next; | 375 | line = line->next; |
336 | } | 376 | } |
337 | pthread_mutex_unlock(&syme->source_lock); | 377 | pthread_mutex_unlock(&syme->src->lock); |
338 | if (more) | 378 | if (more) |
339 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 379 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
340 | } | 380 | } |
341 | 381 | ||
342 | /* | 382 | /* |
343 | * Symbols will be added here in record_ip and will get out | 383 | * Symbols will be added here in event__process_sample and will get out |
344 | * after decayed. | 384 | * after decayed. |
345 | */ | 385 | */ |
346 | static LIST_HEAD(active_symbols); | 386 | static LIST_HEAD(active_symbols); |
@@ -411,6 +451,8 @@ static void print_sym_table(void) | |||
411 | struct sym_entry *syme, *n; | 451 | struct sym_entry *syme, *n; |
412 | struct rb_root tmp = RB_ROOT; | 452 | struct rb_root tmp = RB_ROOT; |
413 | struct rb_node *nd; | 453 | struct rb_node *nd; |
454 | int sym_width = 0, dso_width = 0, max_dso_width; | ||
455 | const int win_width = winsize.ws_col - 1; | ||
414 | 456 | ||
415 | samples = userspace_samples = 0; | 457 | samples = userspace_samples = 0; |
416 | 458 | ||
@@ -422,6 +464,14 @@ static void print_sym_table(void) | |||
422 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 464 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
423 | syme->snap_count = syme->count[snap]; | 465 | syme->snap_count = syme->count[snap]; |
424 | if (syme->snap_count != 0) { | 466 | if (syme->snap_count != 0) { |
467 | |||
468 | if ((hide_user_symbols && | ||
469 | syme->origin == PERF_RECORD_MISC_USER) || | ||
470 | (hide_kernel_symbols && | ||
471 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
472 | list_remove_active_sym(syme); | ||
473 | continue; | ||
474 | } | ||
425 | syme->weight = sym_weight(syme); | 475 | syme->weight = sym_weight(syme); |
426 | rb_insert_active_sym(&tmp, syme); | 476 | rb_insert_active_sym(&tmp, syme); |
427 | sum_ksamples += syme->snap_count; | 477 | sum_ksamples += syme->snap_count; |
@@ -434,8 +484,7 @@ static void print_sym_table(void) | |||
434 | 484 | ||
435 | puts(CONSOLE_CLEAR); | 485 | puts(CONSOLE_CLEAR); |
436 | 486 | ||
437 | printf( | 487 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
438 | "------------------------------------------------------------------------------\n"); | ||
439 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", | 488 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", |
440 | samples_per_sec, | 489 | samples_per_sec, |
441 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); | 490 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); |
@@ -473,33 +522,57 @@ static void print_sym_table(void) | |||
473 | printf(", %d CPUs)\n", nr_cpus); | 522 | printf(", %d CPUs)\n", nr_cpus); |
474 | } | 523 | } |
475 | 524 | ||
476 | printf("------------------------------------------------------------------------------\n\n"); | 525 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
477 | 526 | ||
478 | if (sym_filter_entry) { | 527 | if (sym_filter_entry) { |
479 | show_details(sym_filter_entry); | 528 | show_details(sym_filter_entry); |
480 | return; | 529 | return; |
481 | } | 530 | } |
482 | 531 | ||
532 | /* | ||
533 | * Find the longest symbol name that will be displayed | ||
534 | */ | ||
535 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
536 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
537 | if (++printed > print_entries || | ||
538 | (int)syme->snap_count < count_filter) | ||
539 | continue; | ||
540 | |||
541 | if (syme->map->dso->long_name_len > dso_width) | ||
542 | dso_width = syme->map->dso->long_name_len; | ||
543 | |||
544 | if (syme->name_len > sym_width) | ||
545 | sym_width = syme->name_len; | ||
546 | } | ||
547 | |||
548 | printed = 0; | ||
549 | |||
550 | max_dso_width = winsize.ws_col - sym_width - 29; | ||
551 | if (dso_width > max_dso_width) | ||
552 | dso_width = max_dso_width; | ||
553 | putchar('\n'); | ||
483 | if (nr_counters == 1) | 554 | if (nr_counters == 1) |
484 | printf(" samples pcnt"); | 555 | printf(" samples pcnt"); |
485 | else | 556 | else |
486 | printf(" weight samples pcnt"); | 557 | printf(" weight samples pcnt"); |
487 | 558 | ||
488 | if (verbose) | 559 | if (verbose) |
489 | printf(" RIP "); | 560 | printf(" RIP "); |
490 | printf(" kernel function\n"); | 561 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
491 | printf(" %s _______ _____", | 562 | printf(" %s _______ _____", |
492 | nr_counters == 1 ? " " : "______"); | 563 | nr_counters == 1 ? " " : "______"); |
493 | if (verbose) | 564 | if (verbose) |
494 | printf(" ________________"); | 565 | printf(" ________________"); |
495 | printf(" _______________\n\n"); | 566 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
567 | printf(" %-*.*s", dso_width, dso_width, graph_line); | ||
568 | puts("\n"); | ||
496 | 569 | ||
497 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | 570 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
498 | struct symbol *sym; | 571 | struct symbol *sym; |
499 | double pcnt; | 572 | double pcnt; |
500 | 573 | ||
501 | syme = rb_entry(nd, struct sym_entry, rb_node); | 574 | syme = rb_entry(nd, struct sym_entry, rb_node); |
502 | sym = (struct symbol *)(syme + 1); | 575 | sym = sym_entry__symbol(syme); |
503 | 576 | ||
504 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 577 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
505 | continue; | 578 | continue; |
@@ -508,17 +581,18 @@ static void print_sym_table(void) | |||
508 | sum_ksamples)); | 581 | sum_ksamples)); |
509 | 582 | ||
510 | if (nr_counters == 1 || !display_weighted) | 583 | if (nr_counters == 1 || !display_weighted) |
511 | printf("%20.2f - ", syme->weight); | 584 | printf("%20.2f ", syme->weight); |
512 | else | 585 | else |
513 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 586 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
514 | 587 | ||
515 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | 588 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
516 | if (verbose) | 589 | if (verbose) |
517 | printf(" - %016llx", sym->start); | 590 | printf(" %016llx", sym->start); |
518 | printf(" : %s", sym->name); | 591 | printf(" %-*.*s", sym_width, sym_width, sym->name); |
519 | if (sym->module) | 592 | printf(" %-*.*s\n", dso_width, dso_width, |
520 | printf("\t[%s]", sym->module->name); | 593 | dso_width >= syme->map->dso->long_name_len ? |
521 | printf("\n"); | 594 | syme->map->dso->long_name : |
595 | syme->map->dso->short_name); | ||
522 | } | 596 | } |
523 | } | 597 | } |
524 | 598 | ||
@@ -565,10 +639,10 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
565 | 639 | ||
566 | /* zero counters of active symbol */ | 640 | /* zero counters of active symbol */ |
567 | if (syme) { | 641 | if (syme) { |
568 | pthread_mutex_lock(&syme->source_lock); | 642 | pthread_mutex_lock(&syme->src->lock); |
569 | __zero_source_counters(syme); | 643 | __zero_source_counters(syme); |
570 | *target = NULL; | 644 | *target = NULL; |
571 | pthread_mutex_unlock(&syme->source_lock); | 645 | pthread_mutex_unlock(&syme->src->lock); |
572 | } | 646 | } |
573 | 647 | ||
574 | fprintf(stdout, "\n%s: ", msg); | 648 | fprintf(stdout, "\n%s: ", msg); |
@@ -584,7 +658,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
584 | pthread_mutex_unlock(&active_symbols_lock); | 658 | pthread_mutex_unlock(&active_symbols_lock); |
585 | 659 | ||
586 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 660 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
587 | struct symbol *sym = (struct symbol *)(syme + 1); | 661 | struct symbol *sym = sym_entry__symbol(syme); |
588 | 662 | ||
589 | if (!strcmp(buf, sym->name)) { | 663 | if (!strcmp(buf, sym->name)) { |
590 | found = syme; | 664 | found = syme; |
@@ -608,7 +682,7 @@ static void print_mapped_keys(void) | |||
608 | char *name = NULL; | 682 | char *name = NULL; |
609 | 683 | ||
610 | if (sym_filter_entry) { | 684 | if (sym_filter_entry) { |
611 | struct symbol *sym = (struct symbol *)(sym_filter_entry+1); | 685 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); |
612 | name = sym->name; | 686 | name = sym->name; |
613 | } | 687 | } |
614 | 688 | ||
@@ -621,7 +695,7 @@ static void print_mapped_keys(void) | |||
621 | 695 | ||
622 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 696 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
623 | 697 | ||
624 | if (vmlinux_name) { | 698 | if (symbol_conf.vmlinux_name) { |
625 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 699 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
626 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 700 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
627 | fprintf(stdout, "\t[S] stop annotation.\n"); | 701 | fprintf(stdout, "\t[S] stop annotation.\n"); |
@@ -630,6 +704,12 @@ static void print_mapped_keys(void) | |||
630 | if (nr_counters > 1) | 704 | if (nr_counters > 1) |
631 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 705 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
632 | 706 | ||
707 | fprintf(stdout, | ||
708 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | ||
709 | hide_kernel_symbols ? "yes" : "no"); | ||
710 | fprintf(stdout, | ||
711 | "\t[U] hide user symbols. \t(%s)\n", | ||
712 | hide_user_symbols ? "yes" : "no"); | ||
633 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 713 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); |
634 | fprintf(stdout, "\t[qQ] quit.\n"); | 714 | fprintf(stdout, "\t[qQ] quit.\n"); |
635 | } | 715 | } |
@@ -643,6 +723,8 @@ static int key_mapped(int c) | |||
643 | case 'z': | 723 | case 'z': |
644 | case 'q': | 724 | case 'q': |
645 | case 'Q': | 725 | case 'Q': |
726 | case 'K': | ||
727 | case 'U': | ||
646 | return 1; | 728 | return 1; |
647 | case 'E': | 729 | case 'E': |
648 | case 'w': | 730 | case 'w': |
@@ -650,7 +732,7 @@ static int key_mapped(int c) | |||
650 | case 'F': | 732 | case 'F': |
651 | case 's': | 733 | case 's': |
652 | case 'S': | 734 | case 'S': |
653 | return vmlinux_name ? 1 : 0; | 735 | return symbol_conf.vmlinux_name ? 1 : 0; |
654 | default: | 736 | default: |
655 | break; | 737 | break; |
656 | } | 738 | } |
@@ -691,6 +773,11 @@ static void handle_keypress(int c) | |||
691 | break; | 773 | break; |
692 | case 'e': | 774 | case 'e': |
693 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 775 | prompt_integer(&print_entries, "Enter display entries (lines)"); |
776 | if (print_entries == 0) { | ||
777 | sig_winch_handler(SIGWINCH); | ||
778 | signal(SIGWINCH, sig_winch_handler); | ||
779 | } else | ||
780 | signal(SIGWINCH, SIG_DFL); | ||
694 | break; | 781 | break; |
695 | case 'E': | 782 | case 'E': |
696 | if (nr_counters > 1) { | 783 | if (nr_counters > 1) { |
@@ -715,9 +802,14 @@ static void handle_keypress(int c) | |||
715 | case 'F': | 802 | case 'F': |
716 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 803 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
717 | break; | 804 | break; |
805 | case 'K': | ||
806 | hide_kernel_symbols = !hide_kernel_symbols; | ||
807 | break; | ||
718 | case 'q': | 808 | case 'q': |
719 | case 'Q': | 809 | case 'Q': |
720 | printf("exiting.\n"); | 810 | printf("exiting.\n"); |
811 | if (dump_symtab) | ||
812 | dsos__fprintf(stderr); | ||
721 | exit(0); | 813 | exit(0); |
722 | case 's': | 814 | case 's': |
723 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | 815 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); |
@@ -728,12 +820,15 @@ static void handle_keypress(int c) | |||
728 | else { | 820 | else { |
729 | struct sym_entry *syme = sym_filter_entry; | 821 | struct sym_entry *syme = sym_filter_entry; |
730 | 822 | ||
731 | pthread_mutex_lock(&syme->source_lock); | 823 | pthread_mutex_lock(&syme->src->lock); |
732 | sym_filter_entry = NULL; | 824 | sym_filter_entry = NULL; |
733 | __zero_source_counters(syme); | 825 | __zero_source_counters(syme); |
734 | pthread_mutex_unlock(&syme->source_lock); | 826 | pthread_mutex_unlock(&syme->src->lock); |
735 | } | 827 | } |
736 | break; | 828 | break; |
829 | case 'U': | ||
830 | hide_user_symbols = !hide_user_symbols; | ||
831 | break; | ||
737 | case 'w': | 832 | case 'w': |
738 | display_weighted = ~display_weighted; | 833 | display_weighted = ~display_weighted; |
739 | break; | 834 | break; |
@@ -790,7 +885,7 @@ static const char *skip_symbols[] = { | |||
790 | NULL | 885 | NULL |
791 | }; | 886 | }; |
792 | 887 | ||
793 | static int symbol_filter(struct dso *self, struct symbol *sym) | 888 | static int symbol_filter(struct map *map, struct symbol *sym) |
794 | { | 889 | { |
795 | struct sym_entry *syme; | 890 | struct sym_entry *syme; |
796 | const char *name = sym->name; | 891 | const char *name = sym->name; |
@@ -812,8 +907,9 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
812 | strstr(name, "_text_end")) | 907 | strstr(name, "_text_end")) |
813 | return 1; | 908 | return 1; |
814 | 909 | ||
815 | syme = dso__sym_priv(self, sym); | 910 | syme = symbol__priv(sym); |
816 | pthread_mutex_init(&syme->source_lock, NULL); | 911 | syme->map = map; |
912 | syme->src = NULL; | ||
817 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | 913 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) |
818 | sym_filter_entry = syme; | 914 | sym_filter_entry = syme; |
819 | 915 | ||
@@ -824,75 +920,65 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
824 | } | 920 | } |
825 | } | 921 | } |
826 | 922 | ||
827 | return 0; | 923 | if (!syme->skip) |
828 | } | 924 | syme->name_len = strlen(sym->name); |
829 | |||
830 | static int parse_symbols(void) | ||
831 | { | ||
832 | struct rb_node *node; | ||
833 | struct symbol *sym; | ||
834 | int use_modules = vmlinux_name ? 1 : 0; | ||
835 | |||
836 | kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); | ||
837 | if (kernel_dso == NULL) | ||
838 | return -1; | ||
839 | |||
840 | if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0) | ||
841 | goto out_delete_dso; | ||
842 | |||
843 | node = rb_first(&kernel_dso->syms); | ||
844 | sym = rb_entry(node, struct symbol, rb_node); | ||
845 | min_ip = sym->start; | ||
846 | |||
847 | node = rb_last(&kernel_dso->syms); | ||
848 | sym = rb_entry(node, struct symbol, rb_node); | ||
849 | max_ip = sym->end; | ||
850 | |||
851 | if (dump_symtab) | ||
852 | dso__fprintf(kernel_dso, stderr); | ||
853 | 925 | ||
854 | return 0; | 926 | return 0; |
855 | |||
856 | out_delete_dso: | ||
857 | dso__delete(kernel_dso); | ||
858 | kernel_dso = NULL; | ||
859 | return -1; | ||
860 | } | 927 | } |
861 | 928 | ||
862 | /* | 929 | static void event__process_sample(const event_t *self, int counter) |
863 | * Binary search in the histogram table and record the hit: | ||
864 | */ | ||
865 | static void record_ip(u64 ip, int counter) | ||
866 | { | 930 | { |
867 | struct symbol *sym = dso__find_symbol(kernel_dso, ip); | 931 | u64 ip = self->ip.ip; |
868 | 932 | struct sym_entry *syme; | |
869 | if (sym != NULL) { | 933 | struct addr_location al; |
870 | struct sym_entry *syme = dso__sym_priv(kernel_dso, sym); | 934 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
871 | 935 | ||
872 | if (!syme->skip) { | 936 | switch (origin) { |
873 | syme->count[counter]++; | 937 | case PERF_RECORD_MISC_USER: |
874 | record_precise_ip(syme, counter, ip); | 938 | if (hide_user_symbols) |
875 | pthread_mutex_lock(&active_symbols_lock); | ||
876 | if (list_empty(&syme->node) || !syme->node.next) | ||
877 | __list_insert_active_sym(syme); | ||
878 | pthread_mutex_unlock(&active_symbols_lock); | ||
879 | return; | 939 | return; |
880 | } | 940 | break; |
941 | case PERF_RECORD_MISC_KERNEL: | ||
942 | if (hide_kernel_symbols) | ||
943 | return; | ||
944 | break; | ||
945 | default: | ||
946 | return; | ||
881 | } | 947 | } |
882 | 948 | ||
883 | samples--; | 949 | if (event__preprocess_sample(self, &al, symbol_filter) < 0 || |
950 | al.sym == NULL) | ||
951 | return; | ||
952 | |||
953 | syme = symbol__priv(al.sym); | ||
954 | if (!syme->skip) { | ||
955 | syme->count[counter]++; | ||
956 | syme->origin = origin; | ||
957 | record_precise_ip(syme, counter, ip); | ||
958 | pthread_mutex_lock(&active_symbols_lock); | ||
959 | if (list_empty(&syme->node) || !syme->node.next) | ||
960 | __list_insert_active_sym(syme); | ||
961 | pthread_mutex_unlock(&active_symbols_lock); | ||
962 | if (origin == PERF_RECORD_MISC_USER) | ||
963 | ++userspace_samples; | ||
964 | ++samples; | ||
965 | } | ||
884 | } | 966 | } |
885 | 967 | ||
886 | static void process_event(u64 ip, int counter, int user) | 968 | static int event__process(event_t *event) |
887 | { | 969 | { |
888 | samples++; | 970 | switch (event->header.type) { |
889 | 971 | case PERF_RECORD_COMM: | |
890 | if (user) { | 972 | event__process_comm(event); |
891 | userspace_samples++; | 973 | break; |
892 | return; | 974 | case PERF_RECORD_MMAP: |
975 | event__process_mmap(event); | ||
976 | break; | ||
977 | default: | ||
978 | break; | ||
893 | } | 979 | } |
894 | 980 | ||
895 | record_ip(ip, counter); | 981 | return 0; |
896 | } | 982 | } |
897 | 983 | ||
898 | struct mmap_data { | 984 | struct mmap_data { |
@@ -913,8 +999,6 @@ static unsigned int mmap_read_head(struct mmap_data *md) | |||
913 | return head; | 999 | return head; |
914 | } | 1000 | } |
915 | 1001 | ||
916 | struct timeval last_read, this_read; | ||
917 | |||
918 | static void mmap_read_counter(struct mmap_data *md) | 1002 | static void mmap_read_counter(struct mmap_data *md) |
919 | { | 1003 | { |
920 | unsigned int head = mmap_read_head(md); | 1004 | unsigned int head = mmap_read_head(md); |
@@ -922,8 +1006,6 @@ static void mmap_read_counter(struct mmap_data *md) | |||
922 | unsigned char *data = md->base + page_size; | 1006 | unsigned char *data = md->base + page_size; |
923 | int diff; | 1007 | int diff; |
924 | 1008 | ||
925 | gettimeofday(&this_read, NULL); | ||
926 | |||
927 | /* | 1009 | /* |
928 | * If we're further behind than half the buffer, there's a chance | 1010 | * If we're further behind than half the buffer, there's a chance |
929 | * the writer will bite our tail and mess up the samples under us. | 1011 | * the writer will bite our tail and mess up the samples under us. |
@@ -934,14 +1016,7 @@ static void mmap_read_counter(struct mmap_data *md) | |||
934 | */ | 1016 | */ |
935 | diff = head - old; | 1017 | diff = head - old; |
936 | if (diff > md->mask / 2 || diff < 0) { | 1018 | if (diff > md->mask / 2 || diff < 0) { |
937 | struct timeval iv; | 1019 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
938 | unsigned long msecs; | ||
939 | |||
940 | timersub(&this_read, &last_read, &iv); | ||
941 | msecs = iv.tv_sec*1000 + iv.tv_usec/1000; | ||
942 | |||
943 | fprintf(stderr, "WARNING: failed to keep up with mmap data." | ||
944 | " Last read %lu msecs ago.\n", msecs); | ||
945 | 1020 | ||
946 | /* | 1021 | /* |
947 | * head points to a known good entry, start there. | 1022 | * head points to a known good entry, start there. |
@@ -949,8 +1024,6 @@ static void mmap_read_counter(struct mmap_data *md) | |||
949 | old = head; | 1024 | old = head; |
950 | } | 1025 | } |
951 | 1026 | ||
952 | last_read = this_read; | ||
953 | |||
954 | for (; old != head;) { | 1027 | for (; old != head;) { |
955 | event_t *event = (event_t *)&data[old & md->mask]; | 1028 | event_t *event = (event_t *)&data[old & md->mask]; |
956 | 1029 | ||
@@ -978,13 +1051,11 @@ static void mmap_read_counter(struct mmap_data *md) | |||
978 | event = &event_copy; | 1051 | event = &event_copy; |
979 | } | 1052 | } |
980 | 1053 | ||
1054 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
1055 | event__process_sample(event, md->counter); | ||
1056 | else | ||
1057 | event__process(event); | ||
981 | old += size; | 1058 | old += size; |
982 | |||
983 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
984 | int user = | ||
985 | (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER; | ||
986 | process_event(event->ip.ip, md->counter, user); | ||
987 | } | ||
988 | } | 1059 | } |
989 | 1060 | ||
990 | md->prev = old; | 1061 | md->prev = old; |
@@ -1018,8 +1089,15 @@ static void start_counter(int i, int counter) | |||
1018 | attr = attrs + counter; | 1089 | attr = attrs + counter; |
1019 | 1090 | ||
1020 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1091 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1021 | attr->freq = freq; | 1092 | |
1093 | if (freq) { | ||
1094 | attr->sample_type |= PERF_SAMPLE_PERIOD; | ||
1095 | attr->freq = 1; | ||
1096 | attr->sample_freq = freq; | ||
1097 | } | ||
1098 | |||
1022 | attr->inherit = (cpu < 0) && inherit; | 1099 | attr->inherit = (cpu < 0) && inherit; |
1100 | attr->mmap = 1; | ||
1023 | 1101 | ||
1024 | try_again: | 1102 | try_again: |
1025 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); | 1103 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); |
@@ -1078,6 +1156,11 @@ static int __cmd_top(void) | |||
1078 | int i, counter; | 1156 | int i, counter; |
1079 | int ret; | 1157 | int ret; |
1080 | 1158 | ||
1159 | if (target_pid != -1) | ||
1160 | event__synthesize_thread(target_pid, event__process); | ||
1161 | else | ||
1162 | event__synthesize_threads(event__process); | ||
1163 | |||
1081 | for (i = 0; i < nr_cpus; i++) { | 1164 | for (i = 0; i < nr_cpus; i++) { |
1082 | group_fd = -1; | 1165 | group_fd = -1; |
1083 | for (counter = 0; counter < nr_counters; counter++) | 1166 | for (counter = 0; counter < nr_counters; counter++) |
@@ -1133,7 +1216,10 @@ static const struct option options[] = { | |||
1133 | "system-wide collection from all CPUs"), | 1216 | "system-wide collection from all CPUs"), |
1134 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1217 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1135 | "CPU to profile on"), | 1218 | "CPU to profile on"), |
1136 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 1219 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1220 | "file", "vmlinux pathname"), | ||
1221 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | ||
1222 | "hide kernel symbols"), | ||
1137 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 1223 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
1138 | "number of mmap data pages"), | 1224 | "number of mmap data pages"), |
1139 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1225 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -1156,6 +1242,8 @@ static const struct option options[] = { | |||
1156 | "profile at this frequency"), | 1242 | "profile at this frequency"), |
1157 | OPT_INTEGER('E', "entries", &print_entries, | 1243 | OPT_INTEGER('E', "entries", &print_entries, |
1158 | "display this many functions"), | 1244 | "display this many functions"), |
1245 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | ||
1246 | "hide user symbols"), | ||
1159 | OPT_BOOLEAN('v', "verbose", &verbose, | 1247 | OPT_BOOLEAN('v', "verbose", &verbose, |
1160 | "be more verbose (show counter open errors, etc)"), | 1248 | "be more verbose (show counter open errors, etc)"), |
1161 | OPT_END() | 1249 | OPT_END() |
@@ -1165,19 +1253,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1165 | { | 1253 | { |
1166 | int counter; | 1254 | int counter; |
1167 | 1255 | ||
1168 | symbol__init(); | ||
1169 | |||
1170 | page_size = sysconf(_SC_PAGE_SIZE); | 1256 | page_size = sysconf(_SC_PAGE_SIZE); |
1171 | 1257 | ||
1172 | argc = parse_options(argc, argv, options, top_usage, 0); | 1258 | argc = parse_options(argc, argv, options, top_usage, 0); |
1173 | if (argc) | 1259 | if (argc) |
1174 | usage_with_options(top_usage, options); | 1260 | usage_with_options(top_usage, options); |
1175 | 1261 | ||
1176 | if (freq) { | ||
1177 | default_interval = freq; | ||
1178 | freq = 1; | ||
1179 | } | ||
1180 | |||
1181 | /* CPU and PID are mutually exclusive */ | 1262 | /* CPU and PID are mutually exclusive */ |
1182 | if (target_pid != -1 && profile_cpu != -1) { | 1263 | if (target_pid != -1 && profile_cpu != -1) { |
1183 | printf("WARNING: PID switch overriding CPU\n"); | 1264 | printf("WARNING: PID switch overriding CPU\n"); |
@@ -1188,13 +1269,31 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1188 | if (!nr_counters) | 1269 | if (!nr_counters) |
1189 | nr_counters = 1; | 1270 | nr_counters = 1; |
1190 | 1271 | ||
1272 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | ||
1273 | (nr_counters + 1) * sizeof(unsigned long)); | ||
1274 | if (symbol_conf.vmlinux_name == NULL) | ||
1275 | symbol_conf.try_vmlinux_path = true; | ||
1276 | if (symbol__init(&symbol_conf) < 0) | ||
1277 | return -1; | ||
1278 | |||
1191 | if (delay_secs < 1) | 1279 | if (delay_secs < 1) |
1192 | delay_secs = 1; | 1280 | delay_secs = 1; |
1193 | 1281 | ||
1194 | parse_symbols(); | ||
1195 | parse_source(sym_filter_entry); | 1282 | parse_source(sym_filter_entry); |
1196 | 1283 | ||
1197 | /* | 1284 | /* |
1285 | * User specified count overrides default frequency. | ||
1286 | */ | ||
1287 | if (default_interval) | ||
1288 | freq = 0; | ||
1289 | else if (freq) { | ||
1290 | default_interval = freq; | ||
1291 | } else { | ||
1292 | fprintf(stderr, "frequency and count are zero, aborting\n"); | ||
1293 | exit(EXIT_FAILURE); | ||
1294 | } | ||
1295 | |||
1296 | /* | ||
1198 | * Fill in the ones not specifically initialized via -c: | 1297 | * Fill in the ones not specifically initialized via -c: |
1199 | */ | 1298 | */ |
1200 | for (counter = 0; counter < nr_counters; counter++) { | 1299 | for (counter = 0; counter < nr_counters; counter++) { |
@@ -1211,5 +1310,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1211 | if (target_pid != -1 || profile_cpu != -1) | 1310 | if (target_pid != -1 || profile_cpu != -1) |
1212 | nr_cpus = 1; | 1311 | nr_cpus = 1; |
1213 | 1312 | ||
1313 | get_term_dimensions(&winsize); | ||
1314 | if (print_entries == 0) { | ||
1315 | update_print_entries(&winsize); | ||
1316 | signal(SIGWINCH, sig_winch_handler); | ||
1317 | } | ||
1318 | |||
1214 | return __cmd_top(); | 1319 | return __cmd_top(); |
1215 | } | 1320 | } |