diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 557 |
1 files changed, 272 insertions, 285 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c9cdedb58134..c3836b966ccf 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -64,44 +64,6 @@ | |||
64 | #include <linux/unistd.h> | 64 | #include <linux/unistd.h> |
65 | #include <linux/types.h> | 65 | #include <linux/types.h> |
66 | 66 | ||
67 | static struct perf_top top = { | ||
68 | .count_filter = 5, | ||
69 | .delay_secs = 2, | ||
70 | .target_pid = -1, | ||
71 | .target_tid = -1, | ||
72 | .freq = 1000, /* 1 KHz */ | ||
73 | }; | ||
74 | |||
75 | static bool system_wide = false; | ||
76 | |||
77 | static bool use_tui, use_stdio; | ||
78 | |||
79 | static bool sort_has_symbols; | ||
80 | |||
81 | static bool dont_use_callchains; | ||
82 | static char callchain_default_opt[] = "fractal,0.5,callee"; | ||
83 | |||
84 | |||
85 | static int default_interval = 0; | ||
86 | |||
87 | static bool kptr_restrict_warned; | ||
88 | static bool vmlinux_warned; | ||
89 | static bool inherit = false; | ||
90 | static int realtime_prio = 0; | ||
91 | static bool group = false; | ||
92 | static bool sample_id_all_avail = true; | ||
93 | static unsigned int mmap_pages = 128; | ||
94 | |||
95 | static bool dump_symtab = false; | ||
96 | |||
97 | static struct winsize winsize; | ||
98 | |||
99 | static const char *sym_filter = NULL; | ||
100 | static int sym_pcnt_filter = 5; | ||
101 | |||
102 | /* | ||
103 | * Source functions | ||
104 | */ | ||
105 | 67 | ||
106 | void get_term_dimensions(struct winsize *ws) | 68 | void get_term_dimensions(struct winsize *ws) |
107 | { | 69 | { |
@@ -125,21 +87,23 @@ void get_term_dimensions(struct winsize *ws) | |||
125 | ws->ws_col = 80; | 87 | ws->ws_col = 80; |
126 | } | 88 | } |
127 | 89 | ||
128 | static void update_print_entries(struct winsize *ws) | 90 | static void perf_top__update_print_entries(struct perf_top *top) |
129 | { | 91 | { |
130 | top.print_entries = ws->ws_row; | 92 | top->print_entries = top->winsize.ws_row; |
131 | 93 | ||
132 | if (top.print_entries > 9) | 94 | if (top->print_entries > 9) |
133 | top.print_entries -= 9; | 95 | top->print_entries -= 9; |
134 | } | 96 | } |
135 | 97 | ||
136 | static void sig_winch_handler(int sig __used) | 98 | static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *arg) |
137 | { | 99 | { |
138 | get_term_dimensions(&winsize); | 100 | struct perf_top *top = arg; |
139 | update_print_entries(&winsize); | 101 | |
102 | get_term_dimensions(&top->winsize); | ||
103 | perf_top__update_print_entries(top); | ||
140 | } | 104 | } |
141 | 105 | ||
142 | static int parse_source(struct hist_entry *he) | 106 | static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he) |
143 | { | 107 | { |
144 | struct symbol *sym; | 108 | struct symbol *sym; |
145 | struct annotation *notes; | 109 | struct annotation *notes; |
@@ -170,7 +134,7 @@ static int parse_source(struct hist_entry *he) | |||
170 | 134 | ||
171 | pthread_mutex_lock(¬es->lock); | 135 | pthread_mutex_lock(¬es->lock); |
172 | 136 | ||
173 | if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { | 137 | if (symbol__alloc_hist(sym) < 0) { |
174 | pthread_mutex_unlock(¬es->lock); | 138 | pthread_mutex_unlock(¬es->lock); |
175 | pr_err("Not enough memory for annotating '%s' symbol!\n", | 139 | pr_err("Not enough memory for annotating '%s' symbol!\n", |
176 | sym->name); | 140 | sym->name); |
@@ -181,7 +145,7 @@ static int parse_source(struct hist_entry *he) | |||
181 | err = symbol__annotate(sym, map, 0); | 145 | err = symbol__annotate(sym, map, 0); |
182 | if (err == 0) { | 146 | if (err == 0) { |
183 | out_assign: | 147 | out_assign: |
184 | top.sym_filter_entry = he; | 148 | top->sym_filter_entry = he; |
185 | } | 149 | } |
186 | 150 | ||
187 | pthread_mutex_unlock(¬es->lock); | 151 | pthread_mutex_unlock(¬es->lock); |
@@ -194,14 +158,16 @@ static void __zero_source_counters(struct hist_entry *he) | |||
194 | symbol__annotate_zero_histograms(sym); | 158 | symbol__annotate_zero_histograms(sym); |
195 | } | 159 | } |
196 | 160 | ||
197 | static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) | 161 | static void perf_top__record_precise_ip(struct perf_top *top, |
162 | struct hist_entry *he, | ||
163 | int counter, u64 ip) | ||
198 | { | 164 | { |
199 | struct annotation *notes; | 165 | struct annotation *notes; |
200 | struct symbol *sym; | 166 | struct symbol *sym; |
201 | 167 | ||
202 | if (he == NULL || he->ms.sym == NULL || | 168 | if (he == NULL || he->ms.sym == NULL || |
203 | ((top.sym_filter_entry == NULL || | 169 | ((top->sym_filter_entry == NULL || |
204 | top.sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1)) | 170 | top->sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1)) |
205 | return; | 171 | return; |
206 | 172 | ||
207 | sym = he->ms.sym; | 173 | sym = he->ms.sym; |
@@ -210,8 +176,7 @@ static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) | |||
210 | if (pthread_mutex_trylock(¬es->lock)) | 176 | if (pthread_mutex_trylock(¬es->lock)) |
211 | return; | 177 | return; |
212 | 178 | ||
213 | if (notes->src == NULL && | 179 | if (notes->src == NULL && symbol__alloc_hist(sym) < 0) { |
214 | symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { | ||
215 | pthread_mutex_unlock(¬es->lock); | 180 | pthread_mutex_unlock(¬es->lock); |
216 | pr_err("Not enough memory for annotating '%s' symbol!\n", | 181 | pr_err("Not enough memory for annotating '%s' symbol!\n", |
217 | sym->name); | 182 | sym->name); |
@@ -225,8 +190,9 @@ static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) | |||
225 | pthread_mutex_unlock(¬es->lock); | 190 | pthread_mutex_unlock(¬es->lock); |
226 | } | 191 | } |
227 | 192 | ||
228 | static void show_details(struct hist_entry *he) | 193 | static void perf_top__show_details(struct perf_top *top) |
229 | { | 194 | { |
195 | struct hist_entry *he = top->sym_filter_entry; | ||
230 | struct annotation *notes; | 196 | struct annotation *notes; |
231 | struct symbol *symbol; | 197 | struct symbol *symbol; |
232 | int more; | 198 | int more; |
@@ -242,15 +208,15 @@ static void show_details(struct hist_entry *he) | |||
242 | if (notes->src == NULL) | 208 | if (notes->src == NULL) |
243 | goto out_unlock; | 209 | goto out_unlock; |
244 | 210 | ||
245 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); | 211 | printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name); |
246 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 212 | printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); |
247 | 213 | ||
248 | more = symbol__annotate_printf(symbol, he->ms.map, top.sym_evsel->idx, | 214 | more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, |
249 | 0, sym_pcnt_filter, top.print_entries, 4); | 215 | 0, top->sym_pcnt_filter, top->print_entries, 4); |
250 | if (top.zero) | 216 | if (top->zero) |
251 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); | 217 | symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx); |
252 | else | 218 | else |
253 | symbol__annotate_decay_histogram(symbol, top.sym_evsel->idx); | 219 | symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx); |
254 | if (more != 0) | 220 | if (more != 0) |
255 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 221 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
256 | out_unlock: | 222 | out_unlock: |
@@ -259,11 +225,9 @@ out_unlock: | |||
259 | 225 | ||
260 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 226 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
261 | 227 | ||
262 | static struct hist_entry * | 228 | static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, |
263 | perf_session__add_hist_entry(struct perf_session *session, | 229 | struct addr_location *al, |
264 | struct addr_location *al, | 230 | struct perf_sample *sample) |
265 | struct perf_sample *sample, | ||
266 | struct perf_evsel *evsel) | ||
267 | { | 231 | { |
268 | struct hist_entry *he; | 232 | struct hist_entry *he; |
269 | 233 | ||
@@ -271,50 +235,51 @@ static struct hist_entry * | |||
271 | if (he == NULL) | 235 | if (he == NULL) |
272 | return NULL; | 236 | return NULL; |
273 | 237 | ||
274 | session->hists.stats.total_period += sample->period; | 238 | evsel->hists.stats.total_period += sample->period; |
275 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 239 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
276 | return he; | 240 | return he; |
277 | } | 241 | } |
278 | 242 | ||
279 | static void print_sym_table(void) | 243 | static void perf_top__print_sym_table(struct perf_top *top) |
280 | { | 244 | { |
281 | char bf[160]; | 245 | char bf[160]; |
282 | int printed = 0; | 246 | int printed = 0; |
283 | const int win_width = winsize.ws_col - 1; | 247 | const int win_width = top->winsize.ws_col - 1; |
284 | 248 | ||
285 | puts(CONSOLE_CLEAR); | 249 | puts(CONSOLE_CLEAR); |
286 | 250 | ||
287 | perf_top__header_snprintf(&top, bf, sizeof(bf)); | 251 | perf_top__header_snprintf(top, bf, sizeof(bf)); |
288 | printf("%s\n", bf); | 252 | printf("%s\n", bf); |
289 | 253 | ||
290 | perf_top__reset_sample_counters(&top); | 254 | perf_top__reset_sample_counters(top); |
291 | 255 | ||
292 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 256 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
293 | 257 | ||
294 | if (top.sym_evsel->hists.stats.nr_lost_warned != | 258 | if (top->sym_evsel->hists.stats.nr_lost_warned != |
295 | top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { | 259 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { |
296 | top.sym_evsel->hists.stats.nr_lost_warned = | 260 | top->sym_evsel->hists.stats.nr_lost_warned = |
297 | top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | 261 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; |
298 | color_fprintf(stdout, PERF_COLOR_RED, | 262 | color_fprintf(stdout, PERF_COLOR_RED, |
299 | "WARNING: LOST %d chunks, Check IO/CPU overload", | 263 | "WARNING: LOST %d chunks, Check IO/CPU overload", |
300 | top.sym_evsel->hists.stats.nr_lost_warned); | 264 | top->sym_evsel->hists.stats.nr_lost_warned); |
301 | ++printed; | 265 | ++printed; |
302 | } | 266 | } |
303 | 267 | ||
304 | if (top.sym_filter_entry) { | 268 | if (top->sym_filter_entry) { |
305 | show_details(top.sym_filter_entry); | 269 | perf_top__show_details(top); |
306 | return; | 270 | return; |
307 | } | 271 | } |
308 | 272 | ||
309 | hists__collapse_resort_threaded(&top.sym_evsel->hists); | 273 | hists__collapse_resort_threaded(&top->sym_evsel->hists); |
310 | hists__output_resort_threaded(&top.sym_evsel->hists); | 274 | hists__output_resort_threaded(&top->sym_evsel->hists); |
311 | hists__decay_entries_threaded(&top.sym_evsel->hists, | 275 | hists__decay_entries_threaded(&top->sym_evsel->hists, |
312 | top.hide_user_symbols, | 276 | top->hide_user_symbols, |
313 | top.hide_kernel_symbols); | 277 | top->hide_kernel_symbols); |
314 | hists__output_recalc_col_len(&top.sym_evsel->hists, winsize.ws_row - 3); | 278 | hists__output_recalc_col_len(&top->sym_evsel->hists, |
279 | top->winsize.ws_row - 3); | ||
315 | putchar('\n'); | 280 | putchar('\n'); |
316 | hists__fprintf(&top.sym_evsel->hists, NULL, false, false, | 281 | hists__fprintf(&top->sym_evsel->hists, NULL, false, false, |
317 | winsize.ws_row - 4 - printed, win_width, stdout); | 282 | top->winsize.ws_row - 4 - printed, win_width, stdout); |
318 | } | 283 | } |
319 | 284 | ||
320 | static void prompt_integer(int *target, const char *msg) | 285 | static void prompt_integer(int *target, const char *msg) |
@@ -352,17 +317,17 @@ static void prompt_percent(int *target, const char *msg) | |||
352 | *target = tmp; | 317 | *target = tmp; |
353 | } | 318 | } |
354 | 319 | ||
355 | static void prompt_symbol(struct hist_entry **target, const char *msg) | 320 | static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) |
356 | { | 321 | { |
357 | char *buf = malloc(0), *p; | 322 | char *buf = malloc(0), *p; |
358 | struct hist_entry *syme = *target, *n, *found = NULL; | 323 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; |
359 | struct rb_node *next; | 324 | struct rb_node *next; |
360 | size_t dummy = 0; | 325 | size_t dummy = 0; |
361 | 326 | ||
362 | /* zero counters of active symbol */ | 327 | /* zero counters of active symbol */ |
363 | if (syme) { | 328 | if (syme) { |
364 | __zero_source_counters(syme); | 329 | __zero_source_counters(syme); |
365 | *target = NULL; | 330 | top->sym_filter_entry = NULL; |
366 | } | 331 | } |
367 | 332 | ||
368 | fprintf(stdout, "\n%s: ", msg); | 333 | fprintf(stdout, "\n%s: ", msg); |
@@ -373,7 +338,7 @@ static void prompt_symbol(struct hist_entry **target, const char *msg) | |||
373 | if (p) | 338 | if (p) |
374 | *p = 0; | 339 | *p = 0; |
375 | 340 | ||
376 | next = rb_first(&top.sym_evsel->hists.entries); | 341 | next = rb_first(&top->sym_evsel->hists.entries); |
377 | while (next) { | 342 | while (next) { |
378 | n = rb_entry(next, struct hist_entry, rb_node); | 343 | n = rb_entry(next, struct hist_entry, rb_node); |
379 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | 344 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { |
@@ -388,45 +353,45 @@ static void prompt_symbol(struct hist_entry **target, const char *msg) | |||
388 | sleep(1); | 353 | sleep(1); |
389 | return; | 354 | return; |
390 | } else | 355 | } else |
391 | parse_source(found); | 356 | perf_top__parse_source(top, found); |
392 | 357 | ||
393 | out_free: | 358 | out_free: |
394 | free(buf); | 359 | free(buf); |
395 | } | 360 | } |
396 | 361 | ||
397 | static void print_mapped_keys(void) | 362 | static void perf_top__print_mapped_keys(struct perf_top *top) |
398 | { | 363 | { |
399 | char *name = NULL; | 364 | char *name = NULL; |
400 | 365 | ||
401 | if (top.sym_filter_entry) { | 366 | if (top->sym_filter_entry) { |
402 | struct symbol *sym = top.sym_filter_entry->ms.sym; | 367 | struct symbol *sym = top->sym_filter_entry->ms.sym; |
403 | name = sym->name; | 368 | name = sym->name; |
404 | } | 369 | } |
405 | 370 | ||
406 | fprintf(stdout, "\nMapped keys:\n"); | 371 | fprintf(stdout, "\nMapped keys:\n"); |
407 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top.delay_secs); | 372 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top->delay_secs); |
408 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top.print_entries); | 373 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries); |
409 | 374 | ||
410 | if (top.evlist->nr_entries > 1) | 375 | if (top->evlist->nr_entries > 1) |
411 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top.sym_evsel)); | 376 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top->sym_evsel)); |
412 | 377 | ||
413 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top.count_filter); | 378 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter); |
414 | 379 | ||
415 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 380 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", top->sym_pcnt_filter); |
416 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 381 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
417 | fprintf(stdout, "\t[S] stop annotation.\n"); | 382 | fprintf(stdout, "\t[S] stop annotation.\n"); |
418 | 383 | ||
419 | fprintf(stdout, | 384 | fprintf(stdout, |
420 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | 385 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
421 | top.hide_kernel_symbols ? "yes" : "no"); | 386 | top->hide_kernel_symbols ? "yes" : "no"); |
422 | fprintf(stdout, | 387 | fprintf(stdout, |
423 | "\t[U] hide user symbols. \t(%s)\n", | 388 | "\t[U] hide user symbols. \t(%s)\n", |
424 | top.hide_user_symbols ? "yes" : "no"); | 389 | top->hide_user_symbols ? "yes" : "no"); |
425 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top.zero ? 1 : 0); | 390 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top->zero ? 1 : 0); |
426 | fprintf(stdout, "\t[qQ] quit.\n"); | 391 | fprintf(stdout, "\t[qQ] quit.\n"); |
427 | } | 392 | } |
428 | 393 | ||
429 | static int key_mapped(int c) | 394 | static int perf_top__key_mapped(struct perf_top *top, int c) |
430 | { | 395 | { |
431 | switch (c) { | 396 | switch (c) { |
432 | case 'd': | 397 | case 'd': |
@@ -442,7 +407,7 @@ static int key_mapped(int c) | |||
442 | case 'S': | 407 | case 'S': |
443 | return 1; | 408 | return 1; |
444 | case 'E': | 409 | case 'E': |
445 | return top.evlist->nr_entries > 1 ? 1 : 0; | 410 | return top->evlist->nr_entries > 1 ? 1 : 0; |
446 | default: | 411 | default: |
447 | break; | 412 | break; |
448 | } | 413 | } |
@@ -450,13 +415,13 @@ static int key_mapped(int c) | |||
450 | return 0; | 415 | return 0; |
451 | } | 416 | } |
452 | 417 | ||
453 | static void handle_keypress(int c) | 418 | static void perf_top__handle_keypress(struct perf_top *top, int c) |
454 | { | 419 | { |
455 | if (!key_mapped(c)) { | 420 | if (!perf_top__key_mapped(top, c)) { |
456 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 421 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
457 | struct termios tc, save; | 422 | struct termios tc, save; |
458 | 423 | ||
459 | print_mapped_keys(); | 424 | perf_top__print_mapped_keys(top); |
460 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); | 425 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); |
461 | fflush(stdout); | 426 | fflush(stdout); |
462 | 427 | ||
@@ -471,81 +436,86 @@ static void handle_keypress(int c) | |||
471 | c = getc(stdin); | 436 | c = getc(stdin); |
472 | 437 | ||
473 | tcsetattr(0, TCSAFLUSH, &save); | 438 | tcsetattr(0, TCSAFLUSH, &save); |
474 | if (!key_mapped(c)) | 439 | if (!perf_top__key_mapped(top, c)) |
475 | return; | 440 | return; |
476 | } | 441 | } |
477 | 442 | ||
478 | switch (c) { | 443 | switch (c) { |
479 | case 'd': | 444 | case 'd': |
480 | prompt_integer(&top.delay_secs, "Enter display delay"); | 445 | prompt_integer(&top->delay_secs, "Enter display delay"); |
481 | if (top.delay_secs < 1) | 446 | if (top->delay_secs < 1) |
482 | top.delay_secs = 1; | 447 | top->delay_secs = 1; |
483 | break; | 448 | break; |
484 | case 'e': | 449 | case 'e': |
485 | prompt_integer(&top.print_entries, "Enter display entries (lines)"); | 450 | prompt_integer(&top->print_entries, "Enter display entries (lines)"); |
486 | if (top.print_entries == 0) { | 451 | if (top->print_entries == 0) { |
487 | sig_winch_handler(SIGWINCH); | 452 | struct sigaction act = { |
488 | signal(SIGWINCH, sig_winch_handler); | 453 | .sa_sigaction = perf_top__sig_winch, |
454 | .sa_flags = SA_SIGINFO, | ||
455 | }; | ||
456 | perf_top__sig_winch(SIGWINCH, NULL, top); | ||
457 | sigaction(SIGWINCH, &act, NULL); | ||
489 | } else | 458 | } else |
490 | signal(SIGWINCH, SIG_DFL); | 459 | signal(SIGWINCH, SIG_DFL); |
491 | break; | 460 | break; |
492 | case 'E': | 461 | case 'E': |
493 | if (top.evlist->nr_entries > 1) { | 462 | if (top->evlist->nr_entries > 1) { |
494 | /* Select 0 as the default event: */ | 463 | /* Select 0 as the default event: */ |
495 | int counter = 0; | 464 | int counter = 0; |
496 | 465 | ||
497 | fprintf(stderr, "\nAvailable events:"); | 466 | fprintf(stderr, "\nAvailable events:"); |
498 | 467 | ||
499 | list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) | 468 | list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) |
500 | fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel)); | 469 | fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel)); |
501 | 470 | ||
502 | prompt_integer(&counter, "Enter details event counter"); | 471 | prompt_integer(&counter, "Enter details event counter"); |
503 | 472 | ||
504 | if (counter >= top.evlist->nr_entries) { | 473 | if (counter >= top->evlist->nr_entries) { |
505 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 474 | top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node); |
506 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel)); | 475 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel)); |
507 | sleep(1); | 476 | sleep(1); |
508 | break; | 477 | break; |
509 | } | 478 | } |
510 | list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) | 479 | list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) |
511 | if (top.sym_evsel->idx == counter) | 480 | if (top->sym_evsel->idx == counter) |
512 | break; | 481 | break; |
513 | } else | 482 | } else |
514 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 483 | top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node); |
515 | break; | 484 | break; |
516 | case 'f': | 485 | case 'f': |
517 | prompt_integer(&top.count_filter, "Enter display event count filter"); | 486 | prompt_integer(&top->count_filter, "Enter display event count filter"); |
518 | break; | 487 | break; |
519 | case 'F': | 488 | case 'F': |
520 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 489 | prompt_percent(&top->sym_pcnt_filter, |
490 | "Enter details display event filter (percent)"); | ||
521 | break; | 491 | break; |
522 | case 'K': | 492 | case 'K': |
523 | top.hide_kernel_symbols = !top.hide_kernel_symbols; | 493 | top->hide_kernel_symbols = !top->hide_kernel_symbols; |
524 | break; | 494 | break; |
525 | case 'q': | 495 | case 'q': |
526 | case 'Q': | 496 | case 'Q': |
527 | printf("exiting.\n"); | 497 | printf("exiting.\n"); |
528 | if (dump_symtab) | 498 | if (top->dump_symtab) |
529 | perf_session__fprintf_dsos(top.session, stderr); | 499 | perf_session__fprintf_dsos(top->session, stderr); |
530 | exit(0); | 500 | exit(0); |
531 | case 's': | 501 | case 's': |
532 | prompt_symbol(&top.sym_filter_entry, "Enter details symbol"); | 502 | perf_top__prompt_symbol(top, "Enter details symbol"); |
533 | break; | 503 | break; |
534 | case 'S': | 504 | case 'S': |
535 | if (!top.sym_filter_entry) | 505 | if (!top->sym_filter_entry) |
536 | break; | 506 | break; |
537 | else { | 507 | else { |
538 | struct hist_entry *syme = top.sym_filter_entry; | 508 | struct hist_entry *syme = top->sym_filter_entry; |
539 | 509 | ||
540 | top.sym_filter_entry = NULL; | 510 | top->sym_filter_entry = NULL; |
541 | __zero_source_counters(syme); | 511 | __zero_source_counters(syme); |
542 | } | 512 | } |
543 | break; | 513 | break; |
544 | case 'U': | 514 | case 'U': |
545 | top.hide_user_symbols = !top.hide_user_symbols; | 515 | top->hide_user_symbols = !top->hide_user_symbols; |
546 | break; | 516 | break; |
547 | case 'z': | 517 | case 'z': |
548 | top.zero = !top.zero; | 518 | top->zero = !top->zero; |
549 | break; | 519 | break; |
550 | default: | 520 | default: |
551 | break; | 521 | break; |
@@ -563,28 +533,30 @@ static void perf_top__sort_new_samples(void *arg) | |||
563 | hists__collapse_resort_threaded(&t->sym_evsel->hists); | 533 | hists__collapse_resort_threaded(&t->sym_evsel->hists); |
564 | hists__output_resort_threaded(&t->sym_evsel->hists); | 534 | hists__output_resort_threaded(&t->sym_evsel->hists); |
565 | hists__decay_entries_threaded(&t->sym_evsel->hists, | 535 | hists__decay_entries_threaded(&t->sym_evsel->hists, |
566 | top.hide_user_symbols, | 536 | t->hide_user_symbols, |
567 | top.hide_kernel_symbols); | 537 | t->hide_kernel_symbols); |
568 | } | 538 | } |
569 | 539 | ||
570 | static void *display_thread_tui(void *arg __used) | 540 | static void *display_thread_tui(void *arg) |
571 | { | 541 | { |
542 | struct perf_top *top = arg; | ||
572 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; | 543 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
573 | 544 | ||
574 | perf_top__sort_new_samples(&top); | 545 | perf_top__sort_new_samples(top); |
575 | perf_evlist__tui_browse_hists(top.evlist, help, | 546 | perf_evlist__tui_browse_hists(top->evlist, help, |
576 | perf_top__sort_new_samples, | 547 | perf_top__sort_new_samples, |
577 | &top, top.delay_secs); | 548 | top, top->delay_secs); |
578 | 549 | ||
579 | exit_browser(0); | 550 | exit_browser(0); |
580 | exit(0); | 551 | exit(0); |
581 | return NULL; | 552 | return NULL; |
582 | } | 553 | } |
583 | 554 | ||
584 | static void *display_thread(void *arg __used) | 555 | static void *display_thread(void *arg) |
585 | { | 556 | { |
586 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 557 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
587 | struct termios tc, save; | 558 | struct termios tc, save; |
559 | struct perf_top *top = arg; | ||
588 | int delay_msecs, c; | 560 | int delay_msecs, c; |
589 | 561 | ||
590 | tcgetattr(0, &save); | 562 | tcgetattr(0, &save); |
@@ -595,13 +567,13 @@ static void *display_thread(void *arg __used) | |||
595 | 567 | ||
596 | pthread__unblock_sigwinch(); | 568 | pthread__unblock_sigwinch(); |
597 | repeat: | 569 | repeat: |
598 | delay_msecs = top.delay_secs * 1000; | 570 | delay_msecs = top->delay_secs * 1000; |
599 | tcsetattr(0, TCSANOW, &tc); | 571 | tcsetattr(0, TCSANOW, &tc); |
600 | /* trash return*/ | 572 | /* trash return*/ |
601 | getc(stdin); | 573 | getc(stdin); |
602 | 574 | ||
603 | while (1) { | 575 | while (1) { |
604 | print_sym_table(); | 576 | perf_top__print_sym_table(top); |
605 | /* | 577 | /* |
606 | * Either timeout expired or we got an EINTR due to SIGWINCH, | 578 | * Either timeout expired or we got an EINTR due to SIGWINCH, |
607 | * refresh screen in both cases. | 579 | * refresh screen in both cases. |
@@ -621,7 +593,7 @@ process_hotkey: | |||
621 | c = getc(stdin); | 593 | c = getc(stdin); |
622 | tcsetattr(0, TCSAFLUSH, &save); | 594 | tcsetattr(0, TCSAFLUSH, &save); |
623 | 595 | ||
624 | handle_keypress(c); | 596 | perf_top__handle_keypress(top, c); |
625 | goto repeat; | 597 | goto repeat; |
626 | 598 | ||
627 | return NULL; | 599 | return NULL; |
@@ -673,47 +645,17 @@ static int symbol_filter(struct map *map __used, struct symbol *sym) | |||
673 | return 0; | 645 | return 0; |
674 | } | 646 | } |
675 | 647 | ||
676 | static void perf_event__process_sample(const union perf_event *event, | 648 | static void perf_event__process_sample(struct perf_tool *tool, |
649 | const union perf_event *event, | ||
677 | struct perf_evsel *evsel, | 650 | struct perf_evsel *evsel, |
678 | struct perf_sample *sample, | 651 | struct perf_sample *sample, |
679 | struct perf_session *session) | 652 | struct machine *machine) |
680 | { | 653 | { |
654 | struct perf_top *top = container_of(tool, struct perf_top, tool); | ||
681 | struct symbol *parent = NULL; | 655 | struct symbol *parent = NULL; |
682 | u64 ip = event->ip.ip; | 656 | u64 ip = event->ip.ip; |
683 | struct addr_location al; | 657 | struct addr_location al; |
684 | struct machine *machine; | ||
685 | int err; | 658 | int err; |
686 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
687 | |||
688 | ++top.samples; | ||
689 | |||
690 | switch (origin) { | ||
691 | case PERF_RECORD_MISC_USER: | ||
692 | ++top.us_samples; | ||
693 | if (top.hide_user_symbols) | ||
694 | return; | ||
695 | machine = perf_session__find_host_machine(session); | ||
696 | break; | ||
697 | case PERF_RECORD_MISC_KERNEL: | ||
698 | ++top.kernel_samples; | ||
699 | if (top.hide_kernel_symbols) | ||
700 | return; | ||
701 | machine = perf_session__find_host_machine(session); | ||
702 | break; | ||
703 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
704 | ++top.guest_kernel_samples; | ||
705 | machine = perf_session__find_machine(session, event->ip.pid); | ||
706 | break; | ||
707 | case PERF_RECORD_MISC_GUEST_USER: | ||
708 | ++top.guest_us_samples; | ||
709 | /* | ||
710 | * TODO: we don't process guest user from host side | ||
711 | * except simple counting. | ||
712 | */ | ||
713 | return; | ||
714 | default: | ||
715 | return; | ||
716 | } | ||
717 | 659 | ||
718 | if (!machine && perf_guest) { | 660 | if (!machine && perf_guest) { |
719 | pr_err("Can't find guest [%d]'s kernel information\n", | 661 | pr_err("Can't find guest [%d]'s kernel information\n", |
@@ -722,14 +664,14 @@ static void perf_event__process_sample(const union perf_event *event, | |||
722 | } | 664 | } |
723 | 665 | ||
724 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) | 666 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
725 | top.exact_samples++; | 667 | top->exact_samples++; |
726 | 668 | ||
727 | if (perf_event__preprocess_sample(event, session, &al, sample, | 669 | if (perf_event__preprocess_sample(event, machine, &al, sample, |
728 | symbol_filter) < 0 || | 670 | symbol_filter) < 0 || |
729 | al.filtered) | 671 | al.filtered) |
730 | return; | 672 | return; |
731 | 673 | ||
732 | if (!kptr_restrict_warned && | 674 | if (!top->kptr_restrict_warned && |
733 | symbol_conf.kptr_restrict && | 675 | symbol_conf.kptr_restrict && |
734 | al.cpumode == PERF_RECORD_MISC_KERNEL) { | 676 | al.cpumode == PERF_RECORD_MISC_KERNEL) { |
735 | ui__warning( | 677 | ui__warning( |
@@ -740,7 +682,7 @@ static void perf_event__process_sample(const union perf_event *event, | |||
740 | " modules" : ""); | 682 | " modules" : ""); |
741 | if (use_browser <= 0) | 683 | if (use_browser <= 0) |
742 | sleep(5); | 684 | sleep(5); |
743 | kptr_restrict_warned = true; | 685 | top->kptr_restrict_warned = true; |
744 | } | 686 | } |
745 | 687 | ||
746 | if (al.sym == NULL) { | 688 | if (al.sym == NULL) { |
@@ -756,7 +698,7 @@ static void perf_event__process_sample(const union perf_event *event, | |||
756 | * --hide-kernel-symbols, even if the user specifies an | 698 | * --hide-kernel-symbols, even if the user specifies an |
757 | * invalid --vmlinux ;-) | 699 | * invalid --vmlinux ;-) |
758 | */ | 700 | */ |
759 | if (!kptr_restrict_warned && !vmlinux_warned && | 701 | if (!top->kptr_restrict_warned && !top->vmlinux_warned && |
760 | al.map == machine->vmlinux_maps[MAP__FUNCTION] && | 702 | al.map == machine->vmlinux_maps[MAP__FUNCTION] && |
761 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | 703 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
762 | if (symbol_conf.vmlinux_name) { | 704 | if (symbol_conf.vmlinux_name) { |
@@ -769,7 +711,7 @@ static void perf_event__process_sample(const union perf_event *event, | |||
769 | 711 | ||
770 | if (use_browser <= 0) | 712 | if (use_browser <= 0) |
771 | sleep(5); | 713 | sleep(5); |
772 | vmlinux_warned = true; | 714 | top->vmlinux_warned = true; |
773 | } | 715 | } |
774 | } | 716 | } |
775 | 717 | ||
@@ -778,70 +720,109 @@ static void perf_event__process_sample(const union perf_event *event, | |||
778 | 720 | ||
779 | if ((sort__has_parent || symbol_conf.use_callchain) && | 721 | if ((sort__has_parent || symbol_conf.use_callchain) && |
780 | sample->callchain) { | 722 | sample->callchain) { |
781 | err = perf_session__resolve_callchain(session, al.thread, | 723 | err = machine__resolve_callchain(machine, evsel, al.thread, |
782 | sample->callchain, &parent); | 724 | sample->callchain, &parent); |
783 | if (err) | 725 | if (err) |
784 | return; | 726 | return; |
785 | } | 727 | } |
786 | 728 | ||
787 | he = perf_session__add_hist_entry(session, &al, sample, evsel); | 729 | he = perf_evsel__add_hist_entry(evsel, &al, sample); |
788 | if (he == NULL) { | 730 | if (he == NULL) { |
789 | pr_err("Problem incrementing symbol period, skipping event\n"); | 731 | pr_err("Problem incrementing symbol period, skipping event\n"); |
790 | return; | 732 | return; |
791 | } | 733 | } |
792 | 734 | ||
793 | if (symbol_conf.use_callchain) { | 735 | if (symbol_conf.use_callchain) { |
794 | err = callchain_append(he->callchain, &session->callchain_cursor, | 736 | err = callchain_append(he->callchain, &evsel->hists.callchain_cursor, |
795 | sample->period); | 737 | sample->period); |
796 | if (err) | 738 | if (err) |
797 | return; | 739 | return; |
798 | } | 740 | } |
799 | 741 | ||
800 | if (sort_has_symbols) | 742 | if (top->sort_has_symbols) |
801 | record_precise_ip(he, evsel->idx, ip); | 743 | perf_top__record_precise_ip(top, he, evsel->idx, ip); |
802 | } | 744 | } |
803 | 745 | ||
804 | return; | 746 | return; |
805 | } | 747 | } |
806 | 748 | ||
807 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) | 749 | static void perf_top__mmap_read_idx(struct perf_top *top, int idx) |
808 | { | 750 | { |
809 | struct perf_sample sample; | 751 | struct perf_sample sample; |
810 | struct perf_evsel *evsel; | 752 | struct perf_evsel *evsel; |
753 | struct perf_session *session = top->session; | ||
811 | union perf_event *event; | 754 | union perf_event *event; |
755 | struct machine *machine; | ||
756 | u8 origin; | ||
812 | int ret; | 757 | int ret; |
813 | 758 | ||
814 | while ((event = perf_evlist__mmap_read(top.evlist, idx)) != NULL) { | 759 | while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) { |
815 | ret = perf_session__parse_sample(self, event, &sample); | 760 | ret = perf_session__parse_sample(session, event, &sample); |
816 | if (ret) { | 761 | if (ret) { |
817 | pr_err("Can't parse sample, err = %d\n", ret); | 762 | pr_err("Can't parse sample, err = %d\n", ret); |
818 | continue; | 763 | continue; |
819 | } | 764 | } |
820 | 765 | ||
821 | evsel = perf_evlist__id2evsel(self->evlist, sample.id); | 766 | evsel = perf_evlist__id2evsel(session->evlist, sample.id); |
822 | assert(evsel != NULL); | 767 | assert(evsel != NULL); |
823 | 768 | ||
769 | origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
770 | |||
824 | if (event->header.type == PERF_RECORD_SAMPLE) | 771 | if (event->header.type == PERF_RECORD_SAMPLE) |
825 | perf_event__process_sample(event, evsel, &sample, self); | 772 | ++top->samples; |
826 | else if (event->header.type < PERF_RECORD_MAX) { | 773 | |
774 | switch (origin) { | ||
775 | case PERF_RECORD_MISC_USER: | ||
776 | ++top->us_samples; | ||
777 | if (top->hide_user_symbols) | ||
778 | continue; | ||
779 | machine = perf_session__find_host_machine(session); | ||
780 | break; | ||
781 | case PERF_RECORD_MISC_KERNEL: | ||
782 | ++top->kernel_samples; | ||
783 | if (top->hide_kernel_symbols) | ||
784 | continue; | ||
785 | machine = perf_session__find_host_machine(session); | ||
786 | break; | ||
787 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
788 | ++top->guest_kernel_samples; | ||
789 | machine = perf_session__find_machine(session, event->ip.pid); | ||
790 | break; | ||
791 | case PERF_RECORD_MISC_GUEST_USER: | ||
792 | ++top->guest_us_samples; | ||
793 | /* | ||
794 | * TODO: we don't process guest user from host side | ||
795 | * except simple counting. | ||
796 | */ | ||
797 | /* Fall thru */ | ||
798 | default: | ||
799 | continue; | ||
800 | } | ||
801 | |||
802 | |||
803 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
804 | perf_event__process_sample(&top->tool, event, evsel, | ||
805 | &sample, machine); | ||
806 | } else if (event->header.type < PERF_RECORD_MAX) { | ||
827 | hists__inc_nr_events(&evsel->hists, event->header.type); | 807 | hists__inc_nr_events(&evsel->hists, event->header.type); |
828 | perf_event__process(event, &sample, self); | 808 | perf_event__process(&top->tool, event, &sample, machine); |
829 | } else | 809 | } else |
830 | ++self->hists.stats.nr_unknown_events; | 810 | ++session->hists.stats.nr_unknown_events; |
831 | } | 811 | } |
832 | } | 812 | } |
833 | 813 | ||
834 | static void perf_session__mmap_read(struct perf_session *self) | 814 | static void perf_top__mmap_read(struct perf_top *top) |
835 | { | 815 | { |
836 | int i; | 816 | int i; |
837 | 817 | ||
838 | for (i = 0; i < top.evlist->nr_mmaps; i++) | 818 | for (i = 0; i < top->evlist->nr_mmaps; i++) |
839 | perf_session__mmap_read_idx(self, i); | 819 | perf_top__mmap_read_idx(top, i); |
840 | } | 820 | } |
841 | 821 | ||
842 | static void start_counters(struct perf_evlist *evlist) | 822 | static void perf_top__start_counters(struct perf_top *top) |
843 | { | 823 | { |
844 | struct perf_evsel *counter, *first; | 824 | struct perf_evsel *counter, *first; |
825 | struct perf_evlist *evlist = top->evlist; | ||
845 | 826 | ||
846 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 827 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
847 | 828 | ||
@@ -849,15 +830,15 @@ static void start_counters(struct perf_evlist *evlist) | |||
849 | struct perf_event_attr *attr = &counter->attr; | 830 | struct perf_event_attr *attr = &counter->attr; |
850 | struct xyarray *group_fd = NULL; | 831 | struct xyarray *group_fd = NULL; |
851 | 832 | ||
852 | if (group && counter != first) | 833 | if (top->group && counter != first) |
853 | group_fd = first->fd; | 834 | group_fd = first->fd; |
854 | 835 | ||
855 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 836 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
856 | 837 | ||
857 | if (top.freq) { | 838 | if (top->freq) { |
858 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 839 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
859 | attr->freq = 1; | 840 | attr->freq = 1; |
860 | attr->sample_freq = top.freq; | 841 | attr->sample_freq = top->freq; |
861 | } | 842 | } |
862 | 843 | ||
863 | if (evlist->nr_entries > 1) { | 844 | if (evlist->nr_entries > 1) { |
@@ -870,23 +851,23 @@ static void start_counters(struct perf_evlist *evlist) | |||
870 | 851 | ||
871 | attr->mmap = 1; | 852 | attr->mmap = 1; |
872 | attr->comm = 1; | 853 | attr->comm = 1; |
873 | attr->inherit = inherit; | 854 | attr->inherit = top->inherit; |
874 | retry_sample_id: | 855 | retry_sample_id: |
875 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | 856 | attr->sample_id_all = top->sample_id_all_avail ? 1 : 0; |
876 | try_again: | 857 | try_again: |
877 | if (perf_evsel__open(counter, top.evlist->cpus, | 858 | if (perf_evsel__open(counter, top->evlist->cpus, |
878 | top.evlist->threads, group, | 859 | top->evlist->threads, top->group, |
879 | group_fd) < 0) { | 860 | group_fd) < 0) { |
880 | int err = errno; | 861 | int err = errno; |
881 | 862 | ||
882 | if (err == EPERM || err == EACCES) { | 863 | if (err == EPERM || err == EACCES) { |
883 | ui__error_paranoid(); | 864 | ui__error_paranoid(); |
884 | goto out_err; | 865 | goto out_err; |
885 | } else if (err == EINVAL && sample_id_all_avail) { | 866 | } else if (err == EINVAL && top->sample_id_all_avail) { |
886 | /* | 867 | /* |
887 | * Old kernel, no attr->sample_id_type_all field | 868 | * Old kernel, no attr->sample_id_type_all field |
888 | */ | 869 | */ |
889 | sample_id_all_avail = false; | 870 | top->sample_id_all_avail = false; |
890 | goto retry_sample_id; | 871 | goto retry_sample_id; |
891 | } | 872 | } |
892 | /* | 873 | /* |
@@ -920,7 +901,7 @@ try_again: | |||
920 | } | 901 | } |
921 | } | 902 | } |
922 | 903 | ||
923 | if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) { | 904 | if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) { |
924 | ui__warning("Failed to mmap with %d (%s)\n", | 905 | ui__warning("Failed to mmap with %d (%s)\n", |
925 | errno, strerror(errno)); | 906 | errno, strerror(errno)); |
926 | goto out_err; | 907 | goto out_err; |
@@ -933,14 +914,14 @@ out_err: | |||
933 | exit(0); | 914 | exit(0); |
934 | } | 915 | } |
935 | 916 | ||
936 | static int setup_sample_type(void) | 917 | static int perf_top__setup_sample_type(struct perf_top *top) |
937 | { | 918 | { |
938 | if (!sort_has_symbols) { | 919 | if (!top->sort_has_symbols) { |
939 | if (symbol_conf.use_callchain) { | 920 | if (symbol_conf.use_callchain) { |
940 | ui__warning("Selected -g but \"sym\" not present in --sort/-s."); | 921 | ui__warning("Selected -g but \"sym\" not present in --sort/-s."); |
941 | return -EINVAL; | 922 | return -EINVAL; |
942 | } | 923 | } |
943 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | 924 | } else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) { |
944 | if (callchain_register_param(&callchain_param) < 0) { | 925 | if (callchain_register_param(&callchain_param) < 0) { |
945 | ui__warning("Can't register callchain params.\n"); | 926 | ui__warning("Can't register callchain params.\n"); |
946 | return -EINVAL; | 927 | return -EINVAL; |
@@ -950,7 +931,7 @@ static int setup_sample_type(void) | |||
950 | return 0; | 931 | return 0; |
951 | } | 932 | } |
952 | 933 | ||
953 | static int __cmd_top(void) | 934 | static int __cmd_top(struct perf_top *top) |
954 | { | 935 | { |
955 | pthread_t thread; | 936 | pthread_t thread; |
956 | int ret; | 937 | int ret; |
@@ -958,39 +939,40 @@ static int __cmd_top(void) | |||
958 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 939 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
959 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 940 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
960 | */ | 941 | */ |
961 | top.session = perf_session__new(NULL, O_WRONLY, false, false, NULL); | 942 | top->session = perf_session__new(NULL, O_WRONLY, false, false, NULL); |
962 | if (top.session == NULL) | 943 | if (top->session == NULL) |
963 | return -ENOMEM; | 944 | return -ENOMEM; |
964 | 945 | ||
965 | ret = setup_sample_type(); | 946 | ret = perf_top__setup_sample_type(top); |
966 | if (ret) | 947 | if (ret) |
967 | goto out_delete; | 948 | goto out_delete; |
968 | 949 | ||
969 | if (top.target_tid != -1) | 950 | if (top->target_tid != -1) |
970 | perf_event__synthesize_thread_map(top.evlist->threads, | 951 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, |
971 | perf_event__process, top.session); | 952 | perf_event__process, |
953 | &top->session->host_machine); | ||
972 | else | 954 | else |
973 | perf_event__synthesize_threads(perf_event__process, top.session); | 955 | perf_event__synthesize_threads(&top->tool, perf_event__process, |
974 | 956 | &top->session->host_machine); | |
975 | start_counters(top.evlist); | 957 | perf_top__start_counters(top); |
976 | top.session->evlist = top.evlist; | 958 | top->session->evlist = top->evlist; |
977 | perf_session__update_sample_type(top.session); | 959 | perf_session__update_sample_type(top->session); |
978 | 960 | ||
979 | /* Wait for a minimal set of events before starting the snapshot */ | 961 | /* Wait for a minimal set of events before starting the snapshot */ |
980 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 962 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); |
981 | 963 | ||
982 | perf_session__mmap_read(top.session); | 964 | perf_top__mmap_read(top); |
983 | 965 | ||
984 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : | 966 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
985 | display_thread), NULL)) { | 967 | display_thread), top)) { |
986 | printf("Could not create display thread.\n"); | 968 | printf("Could not create display thread.\n"); |
987 | exit(-1); | 969 | exit(-1); |
988 | } | 970 | } |
989 | 971 | ||
990 | if (realtime_prio) { | 972 | if (top->realtime_prio) { |
991 | struct sched_param param; | 973 | struct sched_param param; |
992 | 974 | ||
993 | param.sched_priority = realtime_prio; | 975 | param.sched_priority = top->realtime_prio; |
994 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 976 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
995 | printf("Could not set realtime priority.\n"); | 977 | printf("Could not set realtime priority.\n"); |
996 | exit(-1); | 978 | exit(-1); |
@@ -998,25 +980,25 @@ static int __cmd_top(void) | |||
998 | } | 980 | } |
999 | 981 | ||
1000 | while (1) { | 982 | while (1) { |
1001 | u64 hits = top.samples; | 983 | u64 hits = top->samples; |
1002 | 984 | ||
1003 | perf_session__mmap_read(top.session); | 985 | perf_top__mmap_read(top); |
1004 | 986 | ||
1005 | if (hits == top.samples) | 987 | if (hits == top->samples) |
1006 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 988 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); |
1007 | } | 989 | } |
1008 | 990 | ||
1009 | out_delete: | 991 | out_delete: |
1010 | perf_session__delete(top.session); | 992 | perf_session__delete(top->session); |
1011 | top.session = NULL; | 993 | top->session = NULL; |
1012 | 994 | ||
1013 | return 0; | 995 | return 0; |
1014 | } | 996 | } |
1015 | 997 | ||
1016 | static int | 998 | static int |
1017 | parse_callchain_opt(const struct option *opt __used, const char *arg, | 999 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) |
1018 | int unset) | ||
1019 | { | 1000 | { |
1001 | struct perf_top *top = (struct perf_top *)opt->value; | ||
1020 | char *tok, *tok2; | 1002 | char *tok, *tok2; |
1021 | char *endptr; | 1003 | char *endptr; |
1022 | 1004 | ||
@@ -1024,7 +1006,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
1024 | * --no-call-graph | 1006 | * --no-call-graph |
1025 | */ | 1007 | */ |
1026 | if (unset) { | 1008 | if (unset) { |
1027 | dont_use_callchains = true; | 1009 | top->dont_use_callchains = true; |
1028 | return 0; | 1010 | return 0; |
1029 | } | 1011 | } |
1030 | 1012 | ||
@@ -1052,9 +1034,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
1052 | symbol_conf.use_callchain = false; | 1034 | symbol_conf.use_callchain = false; |
1053 | 1035 | ||
1054 | return 0; | 1036 | return 0; |
1055 | } | 1037 | } else |
1056 | |||
1057 | else | ||
1058 | return -1; | 1038 | return -1; |
1059 | 1039 | ||
1060 | /* get the min percentage */ | 1040 | /* get the min percentage */ |
@@ -1098,17 +1078,32 @@ static const char * const top_usage[] = { | |||
1098 | NULL | 1078 | NULL |
1099 | }; | 1079 | }; |
1100 | 1080 | ||
1101 | static const struct option options[] = { | 1081 | int cmd_top(int argc, const char **argv, const char *prefix __used) |
1082 | { | ||
1083 | struct perf_evsel *pos; | ||
1084 | int status = -ENOMEM; | ||
1085 | struct perf_top top = { | ||
1086 | .count_filter = 5, | ||
1087 | .delay_secs = 2, | ||
1088 | .target_pid = -1, | ||
1089 | .target_tid = -1, | ||
1090 | .freq = 1000, /* 1 KHz */ | ||
1091 | .sample_id_all_avail = true, | ||
1092 | .mmap_pages = 128, | ||
1093 | .sym_pcnt_filter = 5, | ||
1094 | }; | ||
1095 | char callchain_default_opt[] = "fractal,0.5,callee"; | ||
1096 | const struct option options[] = { | ||
1102 | OPT_CALLBACK('e', "event", &top.evlist, "event", | 1097 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
1103 | "event selector. use 'perf list' to list available events", | 1098 | "event selector. use 'perf list' to list available events", |
1104 | parse_events_option), | 1099 | parse_events_option), |
1105 | OPT_INTEGER('c', "count", &default_interval, | 1100 | OPT_INTEGER('c', "count", &top.default_interval, |
1106 | "event period to sample"), | 1101 | "event period to sample"), |
1107 | OPT_INTEGER('p', "pid", &top.target_pid, | 1102 | OPT_INTEGER('p', "pid", &top.target_pid, |
1108 | "profile events on existing process id"), | 1103 | "profile events on existing process id"), |
1109 | OPT_INTEGER('t', "tid", &top.target_tid, | 1104 | OPT_INTEGER('t', "tid", &top.target_tid, |
1110 | "profile events on existing thread id"), | 1105 | "profile events on existing thread id"), |
1111 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1106 | OPT_BOOLEAN('a', "all-cpus", &top.system_wide, |
1112 | "system-wide collection from all CPUs"), | 1107 | "system-wide collection from all CPUs"), |
1113 | OPT_STRING('C', "cpu", &top.cpu_list, "cpu", | 1108 | OPT_STRING('C', "cpu", &top.cpu_list, "cpu", |
1114 | "list of cpus to monitor"), | 1109 | "list of cpus to monitor"), |
@@ -1116,20 +1111,20 @@ static const struct option options[] = { | |||
1116 | "file", "vmlinux pathname"), | 1111 | "file", "vmlinux pathname"), |
1117 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, | 1112 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, |
1118 | "hide kernel symbols"), | 1113 | "hide kernel symbols"), |
1119 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), | 1114 | OPT_UINTEGER('m', "mmap-pages", &top.mmap_pages, "number of mmap data pages"), |
1120 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1115 | OPT_INTEGER('r', "realtime", &top.realtime_prio, |
1121 | "collect data with this RT SCHED_FIFO priority"), | 1116 | "collect data with this RT SCHED_FIFO priority"), |
1122 | OPT_INTEGER('d', "delay", &top.delay_secs, | 1117 | OPT_INTEGER('d', "delay", &top.delay_secs, |
1123 | "number of seconds to delay between refreshes"), | 1118 | "number of seconds to delay between refreshes"), |
1124 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, | 1119 | OPT_BOOLEAN('D', "dump-symtab", &top.dump_symtab, |
1125 | "dump the symbol table used for profiling"), | 1120 | "dump the symbol table used for profiling"), |
1126 | OPT_INTEGER('f', "count-filter", &top.count_filter, | 1121 | OPT_INTEGER('f', "count-filter", &top.count_filter, |
1127 | "only display functions with more events than this"), | 1122 | "only display functions with more events than this"), |
1128 | OPT_BOOLEAN('g', "group", &group, | 1123 | OPT_BOOLEAN('g', "group", &top.group, |
1129 | "put the counters into a counter group"), | 1124 | "put the counters into a counter group"), |
1130 | OPT_BOOLEAN('i', "inherit", &inherit, | 1125 | OPT_BOOLEAN('i', "inherit", &top.inherit, |
1131 | "child tasks inherit counters"), | 1126 | "child tasks inherit counters"), |
1132 | OPT_STRING(0, "sym-annotate", &sym_filter, "symbol name", | 1127 | OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name", |
1133 | "symbol to annotate"), | 1128 | "symbol to annotate"), |
1134 | OPT_BOOLEAN('z', "zero", &top.zero, | 1129 | OPT_BOOLEAN('z', "zero", &top.zero, |
1135 | "zero history across updates"), | 1130 | "zero history across updates"), |
@@ -1139,15 +1134,15 @@ static const struct option options[] = { | |||
1139 | "display this many functions"), | 1134 | "display this many functions"), |
1140 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, | 1135 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, |
1141 | "hide user symbols"), | 1136 | "hide user symbols"), |
1142 | OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), | 1137 | OPT_BOOLEAN(0, "tui", &top.use_tui, "Use the TUI interface"), |
1143 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), | 1138 | OPT_BOOLEAN(0, "stdio", &top.use_stdio, "Use the stdio interface"), |
1144 | OPT_INCR('v', "verbose", &verbose, | 1139 | OPT_INCR('v', "verbose", &verbose, |
1145 | "be more verbose (show counter open errors, etc)"), | 1140 | "be more verbose (show counter open errors, etc)"), |
1146 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1141 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1147 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1142 | "sort by key(s): pid, comm, dso, symbol, parent"), |
1148 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1143 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1149 | "Show a column with the number of samples"), | 1144 | "Show a column with the number of samples"), |
1150 | OPT_CALLBACK_DEFAULT('G', "call-graph", NULL, "output_type,min_percent, call_order", | 1145 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top, "output_type,min_percent, call_order", |
1151 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | 1146 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " |
1152 | "Default: fractal,0.5,callee", &parse_callchain_opt, | 1147 | "Default: fractal,0.5,callee", &parse_callchain_opt, |
1153 | callchain_default_opt), | 1148 | callchain_default_opt), |
@@ -1166,12 +1161,7 @@ static const struct option options[] = { | |||
1166 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1161 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
1167 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1162 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
1168 | OPT_END() | 1163 | OPT_END() |
1169 | }; | 1164 | }; |
1170 | |||
1171 | int cmd_top(int argc, const char **argv, const char *prefix __used) | ||
1172 | { | ||
1173 | struct perf_evsel *pos; | ||
1174 | int status = -ENOMEM; | ||
1175 | 1165 | ||
1176 | top.evlist = perf_evlist__new(NULL, NULL); | 1166 | top.evlist = perf_evlist__new(NULL, NULL); |
1177 | if (top.evlist == NULL) | 1167 | if (top.evlist == NULL) |
@@ -1188,9 +1178,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1188 | 1178 | ||
1189 | setup_sorting(top_usage, options); | 1179 | setup_sorting(top_usage, options); |
1190 | 1180 | ||
1191 | if (use_stdio) | 1181 | if (top.use_stdio) |
1192 | use_browser = 0; | 1182 | use_browser = 0; |
1193 | else if (use_tui) | 1183 | else if (top.use_tui) |
1194 | use_browser = 1; | 1184 | use_browser = 1; |
1195 | 1185 | ||
1196 | setup_browser(false); | 1186 | setup_browser(false); |
@@ -1215,38 +1205,31 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1215 | return -ENOMEM; | 1205 | return -ENOMEM; |
1216 | } | 1206 | } |
1217 | 1207 | ||
1208 | symbol_conf.nr_events = top.evlist->nr_entries; | ||
1209 | |||
1218 | if (top.delay_secs < 1) | 1210 | if (top.delay_secs < 1) |
1219 | top.delay_secs = 1; | 1211 | top.delay_secs = 1; |
1220 | 1212 | ||
1221 | /* | 1213 | /* |
1222 | * User specified count overrides default frequency. | 1214 | * User specified count overrides default frequency. |
1223 | */ | 1215 | */ |
1224 | if (default_interval) | 1216 | if (top.default_interval) |
1225 | top.freq = 0; | 1217 | top.freq = 0; |
1226 | else if (top.freq) { | 1218 | else if (top.freq) { |
1227 | default_interval = top.freq; | 1219 | top.default_interval = top.freq; |
1228 | } else { | 1220 | } else { |
1229 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 1221 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
1230 | exit(EXIT_FAILURE); | 1222 | exit(EXIT_FAILURE); |
1231 | } | 1223 | } |
1232 | 1224 | ||
1233 | list_for_each_entry(pos, &top.evlist->entries, node) { | 1225 | list_for_each_entry(pos, &top.evlist->entries, node) { |
1234 | if (perf_evsel__alloc_fd(pos, top.evlist->cpus->nr, | ||
1235 | top.evlist->threads->nr) < 0) | ||
1236 | goto out_free_fd; | ||
1237 | /* | 1226 | /* |
1238 | * Fill in the ones not specifically initialized via -c: | 1227 | * Fill in the ones not specifically initialized via -c: |
1239 | */ | 1228 | */ |
1240 | if (pos->attr.sample_period) | 1229 | if (!pos->attr.sample_period) |
1241 | continue; | 1230 | pos->attr.sample_period = top.default_interval; |
1242 | |||
1243 | pos->attr.sample_period = default_interval; | ||
1244 | } | 1231 | } |
1245 | 1232 | ||
1246 | if (perf_evlist__alloc_pollfd(top.evlist) < 0 || | ||
1247 | perf_evlist__alloc_mmap(top.evlist) < 0) | ||
1248 | goto out_free_fd; | ||
1249 | |||
1250 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 1233 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
1251 | 1234 | ||
1252 | symbol_conf.priv_size = sizeof(struct annotation); | 1235 | symbol_conf.priv_size = sizeof(struct annotation); |
@@ -1263,16 +1246,20 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1263 | * Avoid annotation data structures overhead when symbols aren't on the | 1246 | * Avoid annotation data structures overhead when symbols aren't on the |
1264 | * sort list. | 1247 | * sort list. |
1265 | */ | 1248 | */ |
1266 | sort_has_symbols = sort_sym.list.next != NULL; | 1249 | top.sort_has_symbols = sort_sym.list.next != NULL; |
1267 | 1250 | ||
1268 | get_term_dimensions(&winsize); | 1251 | get_term_dimensions(&top.winsize); |
1269 | if (top.print_entries == 0) { | 1252 | if (top.print_entries == 0) { |
1270 | update_print_entries(&winsize); | 1253 | struct sigaction act = { |
1271 | signal(SIGWINCH, sig_winch_handler); | 1254 | .sa_sigaction = perf_top__sig_winch, |
1255 | .sa_flags = SA_SIGINFO, | ||
1256 | }; | ||
1257 | perf_top__update_print_entries(&top); | ||
1258 | sigaction(SIGWINCH, &act, NULL); | ||
1272 | } | 1259 | } |
1273 | 1260 | ||
1274 | status = __cmd_top(); | 1261 | status = __cmd_top(&top); |
1275 | out_free_fd: | 1262 | |
1276 | perf_evlist__delete(top.evlist); | 1263 | perf_evlist__delete(top.evlist); |
1277 | 1264 | ||
1278 | return status; | 1265 | return status; |