Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/tools/perf/builtin-top.c
10820 views
1
/*
2
* builtin-top.c
3
*
4
* Builtin top command: Display a continuously updated profile of
5
* any workload, CPU or specific PID.
6
*
7
* Copyright (C) 2008, Red Hat Inc, Ingo Molnar <[email protected]>
8
*
9
* Improvements and fixes by:
10
*
11
* Arjan van de Ven <[email protected]>
12
* Yanmin Zhang <[email protected]>
13
* Wu Fengguang <[email protected]>
14
* Mike Galbraith <[email protected]>
15
* Paul Mackerras <[email protected]>
16
*
17
* Released under the GPL v2. (and only v2, not any later version)
18
*/
19
#include "builtin.h"
20
21
#include "perf.h"
22
23
#include "util/annotate.h"
24
#include "util/cache.h"
25
#include "util/color.h"
26
#include "util/evlist.h"
27
#include "util/evsel.h"
28
#include "util/session.h"
29
#include "util/symbol.h"
30
#include "util/thread.h"
31
#include "util/thread_map.h"
32
#include "util/top.h"
33
#include "util/util.h"
34
#include <linux/rbtree.h>
35
#include "util/parse-options.h"
36
#include "util/parse-events.h"
37
#include "util/cpumap.h"
38
#include "util/xyarray.h"
39
40
#include "util/debug.h"
41
42
#include <assert.h>
43
#include <fcntl.h>
44
45
#include <stdio.h>
46
#include <termios.h>
47
#include <unistd.h>
48
#include <inttypes.h>
49
50
#include <errno.h>
51
#include <time.h>
52
#include <sched.h>
53
54
#include <sys/syscall.h>
55
#include <sys/ioctl.h>
56
#include <sys/poll.h>
57
#include <sys/prctl.h>
58
#include <sys/wait.h>
59
#include <sys/uio.h>
60
#include <sys/mman.h>
61
62
#include <linux/unistd.h>
63
#include <linux/types.h>
64
65
static struct perf_top top = {
66
.count_filter = 5,
67
.delay_secs = 2,
68
.display_weighted = -1,
69
.target_pid = -1,
70
.target_tid = -1,
71
.active_symbols = LIST_HEAD_INIT(top.active_symbols),
72
.active_symbols_lock = PTHREAD_MUTEX_INITIALIZER,
73
.active_symbols_cond = PTHREAD_COND_INITIALIZER,
74
.freq = 1000, /* 1 KHz */
75
};
76
77
static bool system_wide = false;
78
79
static bool use_tui, use_stdio;
80
81
static int default_interval = 0;
82
83
static bool kptr_restrict_warned;
84
static bool vmlinux_warned;
85
static bool inherit = false;
86
static int realtime_prio = 0;
87
static bool group = false;
88
static unsigned int page_size;
89
static unsigned int mmap_pages = 128;
90
91
static bool dump_symtab = false;
92
93
static struct winsize winsize;
94
95
static const char *sym_filter = NULL;
96
struct sym_entry *sym_filter_entry_sched = NULL;
97
static int sym_pcnt_filter = 5;
98
99
/*
100
* Source functions
101
*/
102
103
void get_term_dimensions(struct winsize *ws)
104
{
105
char *s = getenv("LINES");
106
107
if (s != NULL) {
108
ws->ws_row = atoi(s);
109
s = getenv("COLUMNS");
110
if (s != NULL) {
111
ws->ws_col = atoi(s);
112
if (ws->ws_row && ws->ws_col)
113
return;
114
}
115
}
116
#ifdef TIOCGWINSZ
117
if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
118
ws->ws_row && ws->ws_col)
119
return;
120
#endif
121
ws->ws_row = 25;
122
ws->ws_col = 80;
123
}
124
125
static void update_print_entries(struct winsize *ws)
126
{
127
top.print_entries = ws->ws_row;
128
129
if (top.print_entries > 9)
130
top.print_entries -= 9;
131
}
132
133
static void sig_winch_handler(int sig __used)
134
{
135
get_term_dimensions(&winsize);
136
update_print_entries(&winsize);
137
}
138
139
static int parse_source(struct sym_entry *syme)
140
{
141
struct symbol *sym;
142
struct annotation *notes;
143
struct map *map;
144
int err = -1;
145
146
if (!syme)
147
return -1;
148
149
sym = sym_entry__symbol(syme);
150
map = syme->map;
151
152
/*
153
* We can't annotate with just /proc/kallsyms
154
*/
155
if (map->dso->symtab_type == SYMTAB__KALLSYMS) {
156
pr_err("Can't annotate %s: No vmlinux file was found in the "
157
"path\n", sym->name);
158
sleep(1);
159
return -1;
160
}
161
162
notes = symbol__annotation(sym);
163
if (notes->src != NULL) {
164
pthread_mutex_lock(&notes->lock);
165
goto out_assign;
166
}
167
168
pthread_mutex_lock(&notes->lock);
169
170
if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) {
171
pthread_mutex_unlock(&notes->lock);
172
pr_err("Not enough memory for annotating '%s' symbol!\n",
173
sym->name);
174
sleep(1);
175
return err;
176
}
177
178
err = symbol__annotate(sym, syme->map, 0);
179
if (err == 0) {
180
out_assign:
181
top.sym_filter_entry = syme;
182
}
183
184
pthread_mutex_unlock(&notes->lock);
185
return err;
186
}
187
188
static void __zero_source_counters(struct sym_entry *syme)
189
{
190
struct symbol *sym = sym_entry__symbol(syme);
191
symbol__annotate_zero_histograms(sym);
192
}
193
194
static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
195
{
196
struct annotation *notes;
197
struct symbol *sym;
198
199
if (syme != top.sym_filter_entry)
200
return;
201
202
sym = sym_entry__symbol(syme);
203
notes = symbol__annotation(sym);
204
205
if (pthread_mutex_trylock(&notes->lock))
206
return;
207
208
ip = syme->map->map_ip(syme->map, ip);
209
symbol__inc_addr_samples(sym, syme->map, counter, ip);
210
211
pthread_mutex_unlock(&notes->lock);
212
}
213
214
static void show_details(struct sym_entry *syme)
215
{
216
struct annotation *notes;
217
struct symbol *symbol;
218
int more;
219
220
if (!syme)
221
return;
222
223
symbol = sym_entry__symbol(syme);
224
notes = symbol__annotation(symbol);
225
226
pthread_mutex_lock(&notes->lock);
227
228
if (notes->src == NULL)
229
goto out_unlock;
230
231
printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name);
232
printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
233
234
more = symbol__annotate_printf(symbol, syme->map, top.sym_evsel->idx,
235
0, sym_pcnt_filter, top.print_entries, 4);
236
if (top.zero)
237
symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx);
238
else
239
symbol__annotate_decay_histogram(symbol, top.sym_evsel->idx);
240
if (more != 0)
241
printf("%d lines not displayed, maybe increase display entries [e]\n", more);
242
out_unlock:
243
pthread_mutex_unlock(&notes->lock);
244
}
245
246
static const char CONSOLE_CLEAR[] = "";
247
248
static void __list_insert_active_sym(struct sym_entry *syme)
249
{
250
list_add(&syme->node, &top.active_symbols);
251
}
252
253
static void print_sym_table(struct perf_session *session)
254
{
255
char bf[160];
256
int printed = 0;
257
struct rb_node *nd;
258
struct sym_entry *syme;
259
struct rb_root tmp = RB_ROOT;
260
const int win_width = winsize.ws_col - 1;
261
int sym_width, dso_width, dso_short_width;
262
float sum_ksamples = perf_top__decay_samples(&top, &tmp);
263
264
puts(CONSOLE_CLEAR);
265
266
perf_top__header_snprintf(&top, bf, sizeof(bf));
267
printf("%s\n", bf);
268
269
perf_top__reset_sample_counters(&top);
270
271
printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
272
273
if (session->hists.stats.total_lost != 0) {
274
color_fprintf(stdout, PERF_COLOR_RED, "WARNING:");
275
printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n",
276
session->hists.stats.total_lost);
277
}
278
279
if (top.sym_filter_entry) {
280
show_details(top.sym_filter_entry);
281
return;
282
}
283
284
perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width,
285
&sym_width);
286
287
if (sym_width + dso_width > winsize.ws_col - 29) {
288
dso_width = dso_short_width;
289
if (sym_width + dso_width > winsize.ws_col - 29)
290
sym_width = winsize.ws_col - dso_width - 29;
291
}
292
putchar('\n');
293
if (top.evlist->nr_entries == 1)
294
printf(" samples pcnt");
295
else
296
printf(" weight samples pcnt");
297
298
if (verbose)
299
printf(" RIP ");
300
printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
301
printf(" %s _______ _____",
302
top.evlist->nr_entries == 1 ? " " : "______");
303
if (verbose)
304
printf(" ________________");
305
printf(" %-*.*s", sym_width, sym_width, graph_line);
306
printf(" %-*.*s", dso_width, dso_width, graph_line);
307
puts("\n");
308
309
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
310
struct symbol *sym;
311
double pcnt;
312
313
syme = rb_entry(nd, struct sym_entry, rb_node);
314
sym = sym_entry__symbol(syme);
315
if (++printed > top.print_entries ||
316
(int)syme->snap_count < top.count_filter)
317
continue;
318
319
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
320
sum_ksamples));
321
322
if (top.evlist->nr_entries == 1 || !top.display_weighted)
323
printf("%20.2f ", syme->weight);
324
else
325
printf("%9.1f %10ld ", syme->weight, syme->snap_count);
326
327
percent_color_fprintf(stdout, "%4.1f%%", pcnt);
328
if (verbose)
329
printf(" %016" PRIx64, sym->start);
330
printf(" %-*.*s", sym_width, sym_width, sym->name);
331
printf(" %-*.*s\n", dso_width, dso_width,
332
dso_width >= syme->map->dso->long_name_len ?
333
syme->map->dso->long_name :
334
syme->map->dso->short_name);
335
}
336
}
337
338
static void prompt_integer(int *target, const char *msg)
339
{
340
char *buf = malloc(0), *p;
341
size_t dummy = 0;
342
int tmp;
343
344
fprintf(stdout, "\n%s: ", msg);
345
if (getline(&buf, &dummy, stdin) < 0)
346
return;
347
348
p = strchr(buf, '\n');
349
if (p)
350
*p = 0;
351
352
p = buf;
353
while(*p) {
354
if (!isdigit(*p))
355
goto out_free;
356
p++;
357
}
358
tmp = strtoul(buf, NULL, 10);
359
*target = tmp;
360
out_free:
361
free(buf);
362
}
363
364
static void prompt_percent(int *target, const char *msg)
365
{
366
int tmp = 0;
367
368
prompt_integer(&tmp, msg);
369
if (tmp >= 0 && tmp <= 100)
370
*target = tmp;
371
}
372
373
static void prompt_symbol(struct sym_entry **target, const char *msg)
374
{
375
char *buf = malloc(0), *p;
376
struct sym_entry *syme = *target, *n, *found = NULL;
377
size_t dummy = 0;
378
379
/* zero counters of active symbol */
380
if (syme) {
381
__zero_source_counters(syme);
382
*target = NULL;
383
}
384
385
fprintf(stdout, "\n%s: ", msg);
386
if (getline(&buf, &dummy, stdin) < 0)
387
goto out_free;
388
389
p = strchr(buf, '\n');
390
if (p)
391
*p = 0;
392
393
pthread_mutex_lock(&top.active_symbols_lock);
394
syme = list_entry(top.active_symbols.next, struct sym_entry, node);
395
pthread_mutex_unlock(&top.active_symbols_lock);
396
397
list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) {
398
struct symbol *sym = sym_entry__symbol(syme);
399
400
if (!strcmp(buf, sym->name)) {
401
found = syme;
402
break;
403
}
404
}
405
406
if (!found) {
407
fprintf(stderr, "Sorry, %s is not active.\n", buf);
408
sleep(1);
409
return;
410
} else
411
parse_source(found);
412
413
out_free:
414
free(buf);
415
}
416
417
static void print_mapped_keys(void)
418
{
419
char *name = NULL;
420
421
if (top.sym_filter_entry) {
422
struct symbol *sym = sym_entry__symbol(top.sym_filter_entry);
423
name = sym->name;
424
}
425
426
fprintf(stdout, "\nMapped keys:\n");
427
fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top.delay_secs);
428
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top.print_entries);
429
430
if (top.evlist->nr_entries > 1)
431
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top.sym_evsel));
432
433
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top.count_filter);
434
435
fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
436
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
437
fprintf(stdout, "\t[S] stop annotation.\n");
438
439
if (top.evlist->nr_entries > 1)
440
fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0);
441
442
fprintf(stdout,
443
"\t[K] hide kernel_symbols symbols. \t(%s)\n",
444
top.hide_kernel_symbols ? "yes" : "no");
445
fprintf(stdout,
446
"\t[U] hide user symbols. \t(%s)\n",
447
top.hide_user_symbols ? "yes" : "no");
448
fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top.zero ? 1 : 0);
449
fprintf(stdout, "\t[qQ] quit.\n");
450
}
451
452
static int key_mapped(int c)
453
{
454
switch (c) {
455
case 'd':
456
case 'e':
457
case 'f':
458
case 'z':
459
case 'q':
460
case 'Q':
461
case 'K':
462
case 'U':
463
case 'F':
464
case 's':
465
case 'S':
466
return 1;
467
case 'E':
468
case 'w':
469
return top.evlist->nr_entries > 1 ? 1 : 0;
470
default:
471
break;
472
}
473
474
return 0;
475
}
476
477
static void handle_keypress(struct perf_session *session, int c)
478
{
479
if (!key_mapped(c)) {
480
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
481
struct termios tc, save;
482
483
print_mapped_keys();
484
fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
485
fflush(stdout);
486
487
tcgetattr(0, &save);
488
tc = save;
489
tc.c_lflag &= ~(ICANON | ECHO);
490
tc.c_cc[VMIN] = 0;
491
tc.c_cc[VTIME] = 0;
492
tcsetattr(0, TCSANOW, &tc);
493
494
poll(&stdin_poll, 1, -1);
495
c = getc(stdin);
496
497
tcsetattr(0, TCSAFLUSH, &save);
498
if (!key_mapped(c))
499
return;
500
}
501
502
switch (c) {
503
case 'd':
504
prompt_integer(&top.delay_secs, "Enter display delay");
505
if (top.delay_secs < 1)
506
top.delay_secs = 1;
507
break;
508
case 'e':
509
prompt_integer(&top.print_entries, "Enter display entries (lines)");
510
if (top.print_entries == 0) {
511
sig_winch_handler(SIGWINCH);
512
signal(SIGWINCH, sig_winch_handler);
513
} else
514
signal(SIGWINCH, SIG_DFL);
515
break;
516
case 'E':
517
if (top.evlist->nr_entries > 1) {
518
/* Select 0 as the default event: */
519
int counter = 0;
520
521
fprintf(stderr, "\nAvailable events:");
522
523
list_for_each_entry(top.sym_evsel, &top.evlist->entries, node)
524
fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel));
525
526
prompt_integer(&counter, "Enter details event counter");
527
528
if (counter >= top.evlist->nr_entries) {
529
top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
530
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel));
531
sleep(1);
532
break;
533
}
534
list_for_each_entry(top.sym_evsel, &top.evlist->entries, node)
535
if (top.sym_evsel->idx == counter)
536
break;
537
} else
538
top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
539
break;
540
case 'f':
541
prompt_integer(&top.count_filter, "Enter display event count filter");
542
break;
543
case 'F':
544
prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
545
break;
546
case 'K':
547
top.hide_kernel_symbols = !top.hide_kernel_symbols;
548
break;
549
case 'q':
550
case 'Q':
551
printf("exiting.\n");
552
if (dump_symtab)
553
perf_session__fprintf_dsos(session, stderr);
554
exit(0);
555
case 's':
556
prompt_symbol(&top.sym_filter_entry, "Enter details symbol");
557
break;
558
case 'S':
559
if (!top.sym_filter_entry)
560
break;
561
else {
562
struct sym_entry *syme = top.sym_filter_entry;
563
564
top.sym_filter_entry = NULL;
565
__zero_source_counters(syme);
566
}
567
break;
568
case 'U':
569
top.hide_user_symbols = !top.hide_user_symbols;
570
break;
571
case 'w':
572
top.display_weighted = ~top.display_weighted;
573
break;
574
case 'z':
575
top.zero = !top.zero;
576
break;
577
default:
578
break;
579
}
580
}
581
582
static void *display_thread_tui(void *arg __used)
583
{
584
int err = 0;
585
pthread_mutex_lock(&top.active_symbols_lock);
586
while (list_empty(&top.active_symbols)) {
587
err = pthread_cond_wait(&top.active_symbols_cond,
588
&top.active_symbols_lock);
589
if (err)
590
break;
591
}
592
pthread_mutex_unlock(&top.active_symbols_lock);
593
if (!err)
594
perf_top__tui_browser(&top);
595
exit_browser(0);
596
exit(0);
597
return NULL;
598
}
599
600
static void *display_thread(void *arg __used)
601
{
602
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
603
struct termios tc, save;
604
int delay_msecs, c;
605
struct perf_session *session = (struct perf_session *) arg;
606
607
tcgetattr(0, &save);
608
tc = save;
609
tc.c_lflag &= ~(ICANON | ECHO);
610
tc.c_cc[VMIN] = 0;
611
tc.c_cc[VTIME] = 0;
612
613
repeat:
614
delay_msecs = top.delay_secs * 1000;
615
tcsetattr(0, TCSANOW, &tc);
616
/* trash return*/
617
getc(stdin);
618
619
do {
620
print_sym_table(session);
621
} while (!poll(&stdin_poll, 1, delay_msecs) == 1);
622
623
c = getc(stdin);
624
tcsetattr(0, TCSAFLUSH, &save);
625
626
handle_keypress(session, c);
627
goto repeat;
628
629
return NULL;
630
}
631
632
/* Tag samples to be skipped. */
633
static const char *skip_symbols[] = {
634
"default_idle",
635
"native_safe_halt",
636
"cpu_idle",
637
"enter_idle",
638
"exit_idle",
639
"mwait_idle",
640
"mwait_idle_with_hints",
641
"poll_idle",
642
"ppc64_runlatch_off",
643
"pseries_dedicated_idle_sleep",
644
NULL
645
};
646
647
static int symbol_filter(struct map *map, struct symbol *sym)
648
{
649
struct sym_entry *syme;
650
const char *name = sym->name;
651
int i;
652
653
/*
654
* ppc64 uses function descriptors and appends a '.' to the
655
* start of every instruction address. Remove it.
656
*/
657
if (name[0] == '.')
658
name++;
659
660
if (!strcmp(name, "_text") ||
661
!strcmp(name, "_etext") ||
662
!strcmp(name, "_sinittext") ||
663
!strncmp("init_module", name, 11) ||
664
!strncmp("cleanup_module", name, 14) ||
665
strstr(name, "_text_start") ||
666
strstr(name, "_text_end"))
667
return 1;
668
669
syme = symbol__priv(sym);
670
syme->map = map;
671
symbol__annotate_init(map, sym);
672
673
if (!top.sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) {
674
/* schedule initial sym_filter_entry setup */
675
sym_filter_entry_sched = syme;
676
sym_filter = NULL;
677
}
678
679
for (i = 0; skip_symbols[i]; i++) {
680
if (!strcmp(skip_symbols[i], name)) {
681
sym->ignore = true;
682
break;
683
}
684
}
685
686
return 0;
687
}
688
689
static void perf_event__process_sample(const union perf_event *event,
690
struct perf_sample *sample,
691
struct perf_session *session)
692
{
693
u64 ip = event->ip.ip;
694
struct sym_entry *syme;
695
struct addr_location al;
696
struct machine *machine;
697
u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
698
699
++top.samples;
700
701
switch (origin) {
702
case PERF_RECORD_MISC_USER:
703
++top.us_samples;
704
if (top.hide_user_symbols)
705
return;
706
machine = perf_session__find_host_machine(session);
707
break;
708
case PERF_RECORD_MISC_KERNEL:
709
++top.kernel_samples;
710
if (top.hide_kernel_symbols)
711
return;
712
machine = perf_session__find_host_machine(session);
713
break;
714
case PERF_RECORD_MISC_GUEST_KERNEL:
715
++top.guest_kernel_samples;
716
machine = perf_session__find_machine(session, event->ip.pid);
717
break;
718
case PERF_RECORD_MISC_GUEST_USER:
719
++top.guest_us_samples;
720
/*
721
* TODO: we don't process guest user from host side
722
* except simple counting.
723
*/
724
return;
725
default:
726
return;
727
}
728
729
if (!machine && perf_guest) {
730
pr_err("Can't find guest [%d]'s kernel information\n",
731
event->ip.pid);
732
return;
733
}
734
735
if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
736
top.exact_samples++;
737
738
if (perf_event__preprocess_sample(event, session, &al, sample,
739
symbol_filter) < 0 ||
740
al.filtered)
741
return;
742
743
if (!kptr_restrict_warned &&
744
symbol_conf.kptr_restrict &&
745
al.cpumode == PERF_RECORD_MISC_KERNEL) {
746
ui__warning(
747
"Kernel address maps (/proc/{kallsyms,modules}) are restricted.\n\n"
748
"Check /proc/sys/kernel/kptr_restrict.\n\n"
749
"Kernel%s samples will not be resolved.\n",
750
!RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION]) ?
751
" modules" : "");
752
if (use_browser <= 0)
753
sleep(5);
754
kptr_restrict_warned = true;
755
}
756
757
if (al.sym == NULL) {
758
const char *msg = "Kernel samples will not be resolved.\n";
759
/*
760
* As we do lazy loading of symtabs we only will know if the
761
* specified vmlinux file is invalid when we actually have a
762
* hit in kernel space and then try to load it. So if we get
763
* here and there are _no_ symbols in the DSO backing the
764
* kernel map, bail out.
765
*
766
* We may never get here, for instance, if we use -K/
767
* --hide-kernel-symbols, even if the user specifies an
768
* invalid --vmlinux ;-)
769
*/
770
if (!kptr_restrict_warned && !vmlinux_warned &&
771
al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
772
RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
773
if (symbol_conf.vmlinux_name) {
774
ui__warning("The %s file can't be used.\n%s",
775
symbol_conf.vmlinux_name, msg);
776
} else {
777
ui__warning("A vmlinux file was not found.\n%s",
778
msg);
779
}
780
781
if (use_browser <= 0)
782
sleep(5);
783
vmlinux_warned = true;
784
}
785
786
return;
787
}
788
789
/* let's see, whether we need to install initial sym_filter_entry */
790
if (sym_filter_entry_sched) {
791
top.sym_filter_entry = sym_filter_entry_sched;
792
sym_filter_entry_sched = NULL;
793
if (parse_source(top.sym_filter_entry) < 0) {
794
struct symbol *sym = sym_entry__symbol(top.sym_filter_entry);
795
796
pr_err("Can't annotate %s", sym->name);
797
if (top.sym_filter_entry->map->dso->symtab_type == SYMTAB__KALLSYMS) {
798
pr_err(": No vmlinux file was found in the path:\n");
799
machine__fprintf_vmlinux_path(machine, stderr);
800
} else
801
pr_err(".\n");
802
exit(1);
803
}
804
}
805
806
syme = symbol__priv(al.sym);
807
if (!al.sym->ignore) {
808
struct perf_evsel *evsel;
809
810
evsel = perf_evlist__id2evsel(top.evlist, sample->id);
811
assert(evsel != NULL);
812
syme->count[evsel->idx]++;
813
record_precise_ip(syme, evsel->idx, ip);
814
pthread_mutex_lock(&top.active_symbols_lock);
815
if (list_empty(&syme->node) || !syme->node.next) {
816
static bool first = true;
817
__list_insert_active_sym(syme);
818
if (first) {
819
pthread_cond_broadcast(&top.active_symbols_cond);
820
first = false;
821
}
822
}
823
pthread_mutex_unlock(&top.active_symbols_lock);
824
}
825
}
826
827
static void perf_session__mmap_read_idx(struct perf_session *self, int idx)
828
{
829
struct perf_sample sample;
830
union perf_event *event;
831
int ret;
832
833
while ((event = perf_evlist__mmap_read(top.evlist, idx)) != NULL) {
834
ret = perf_session__parse_sample(self, event, &sample);
835
if (ret) {
836
pr_err("Can't parse sample, err = %d\n", ret);
837
continue;
838
}
839
840
if (event->header.type == PERF_RECORD_SAMPLE)
841
perf_event__process_sample(event, &sample, self);
842
else
843
perf_event__process(event, &sample, self);
844
}
845
}
846
847
static void perf_session__mmap_read(struct perf_session *self)
848
{
849
int i;
850
851
for (i = 0; i < top.evlist->nr_mmaps; i++)
852
perf_session__mmap_read_idx(self, i);
853
}
854
855
static void start_counters(struct perf_evlist *evlist)
856
{
857
struct perf_evsel *counter;
858
859
list_for_each_entry(counter, &evlist->entries, node) {
860
struct perf_event_attr *attr = &counter->attr;
861
862
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
863
864
if (top.freq) {
865
attr->sample_type |= PERF_SAMPLE_PERIOD;
866
attr->freq = 1;
867
attr->sample_freq = top.freq;
868
}
869
870
if (evlist->nr_entries > 1) {
871
attr->sample_type |= PERF_SAMPLE_ID;
872
attr->read_format |= PERF_FORMAT_ID;
873
}
874
875
attr->mmap = 1;
876
attr->inherit = inherit;
877
try_again:
878
if (perf_evsel__open(counter, top.evlist->cpus,
879
top.evlist->threads, group) < 0) {
880
int err = errno;
881
882
if (err == EPERM || err == EACCES) {
883
ui__warning_paranoid();
884
goto out_err;
885
}
886
/*
887
* If it's cycles then fall back to hrtimer
888
* based cpu-clock-tick sw counter, which
889
* is always available even if no PMU support:
890
*/
891
if (attr->type == PERF_TYPE_HARDWARE &&
892
attr->config == PERF_COUNT_HW_CPU_CYCLES) {
893
if (verbose)
894
ui__warning("Cycles event not supported,\n"
895
"trying to fall back to cpu-clock-ticks\n");
896
897
attr->type = PERF_TYPE_SOFTWARE;
898
attr->config = PERF_COUNT_SW_CPU_CLOCK;
899
goto try_again;
900
}
901
902
if (err == ENOENT) {
903
ui__warning("The %s event is not supported.\n",
904
event_name(counter));
905
goto out_err;
906
}
907
908
ui__warning("The sys_perf_event_open() syscall "
909
"returned with %d (%s). /bin/dmesg "
910
"may provide additional information.\n"
911
"No CONFIG_PERF_EVENTS=y kernel support "
912
"configured?\n", err, strerror(err));
913
goto out_err;
914
}
915
}
916
917
if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) {
918
ui__warning("Failed to mmap with %d (%s)\n",
919
errno, strerror(errno));
920
goto out_err;
921
}
922
923
return;
924
925
out_err:
926
exit_browser(0);
927
exit(0);
928
}
929
930
static int __cmd_top(void)
931
{
932
pthread_t thread;
933
int ret __used;
934
/*
935
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
936
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
937
*/
938
struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL);
939
if (session == NULL)
940
return -ENOMEM;
941
942
if (top.target_tid != -1)
943
perf_event__synthesize_thread_map(top.evlist->threads,
944
perf_event__process, session);
945
else
946
perf_event__synthesize_threads(perf_event__process, session);
947
948
start_counters(top.evlist);
949
session->evlist = top.evlist;
950
perf_session__update_sample_type(session);
951
952
/* Wait for a minimal set of events before starting the snapshot */
953
poll(top.evlist->pollfd, top.evlist->nr_fds, 100);
954
955
perf_session__mmap_read(session);
956
957
if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :
958
display_thread), session)) {
959
printf("Could not create display thread.\n");
960
exit(-1);
961
}
962
963
if (realtime_prio) {
964
struct sched_param param;
965
966
param.sched_priority = realtime_prio;
967
if (sched_setscheduler(0, SCHED_FIFO, &param)) {
968
printf("Could not set realtime priority.\n");
969
exit(-1);
970
}
971
}
972
973
while (1) {
974
u64 hits = top.samples;
975
976
perf_session__mmap_read(session);
977
978
if (hits == top.samples)
979
ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100);
980
}
981
982
return 0;
983
}
984
985
static const char * const top_usage[] = {
986
"perf top [<options>]",
987
NULL
988
};
989
990
static const struct option options[] = {
991
OPT_CALLBACK('e', "event", &top.evlist, "event",
992
"event selector. use 'perf list' to list available events",
993
parse_events),
994
OPT_INTEGER('c', "count", &default_interval,
995
"event period to sample"),
996
OPT_INTEGER('p', "pid", &top.target_pid,
997
"profile events on existing process id"),
998
OPT_INTEGER('t', "tid", &top.target_tid,
999
"profile events on existing thread id"),
1000
OPT_BOOLEAN('a', "all-cpus", &system_wide,
1001
"system-wide collection from all CPUs"),
1002
OPT_STRING('C', "cpu", &top.cpu_list, "cpu",
1003
"list of cpus to monitor"),
1004
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
1005
"file", "vmlinux pathname"),
1006
OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols,
1007
"hide kernel symbols"),
1008
OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
1009
OPT_INTEGER('r', "realtime", &realtime_prio,
1010
"collect data with this RT SCHED_FIFO priority"),
1011
OPT_INTEGER('d', "delay", &top.delay_secs,
1012
"number of seconds to delay between refreshes"),
1013
OPT_BOOLEAN('D', "dump-symtab", &dump_symtab,
1014
"dump the symbol table used for profiling"),
1015
OPT_INTEGER('f', "count-filter", &top.count_filter,
1016
"only display functions with more events than this"),
1017
OPT_BOOLEAN('g', "group", &group,
1018
"put the counters into a counter group"),
1019
OPT_BOOLEAN('i', "inherit", &inherit,
1020
"child tasks inherit counters"),
1021
OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
1022
"symbol to annotate"),
1023
OPT_BOOLEAN('z', "zero", &top.zero,
1024
"zero history across updates"),
1025
OPT_INTEGER('F', "freq", &top.freq,
1026
"profile at this frequency"),
1027
OPT_INTEGER('E', "entries", &top.print_entries,
1028
"display this many functions"),
1029
OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols,
1030
"hide user symbols"),
1031
OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
1032
OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
1033
OPT_INCR('v', "verbose", &verbose,
1034
"be more verbose (show counter open errors, etc)"),
1035
OPT_END()
1036
};
1037
1038
int cmd_top(int argc, const char **argv, const char *prefix __used)
1039
{
1040
struct perf_evsel *pos;
1041
int status = -ENOMEM;
1042
1043
top.evlist = perf_evlist__new(NULL, NULL);
1044
if (top.evlist == NULL)
1045
return -ENOMEM;
1046
1047
page_size = sysconf(_SC_PAGE_SIZE);
1048
1049
argc = parse_options(argc, argv, options, top_usage, 0);
1050
if (argc)
1051
usage_with_options(top_usage, options);
1052
1053
/*
1054
* XXX For now start disabled, only using TUI if explicitely asked for.
1055
* Change that when handle_keys equivalent gets written, live annotation
1056
* done, etc.
1057
*/
1058
use_browser = 0;
1059
1060
if (use_stdio)
1061
use_browser = 0;
1062
else if (use_tui)
1063
use_browser = 1;
1064
1065
setup_browser(false);
1066
1067
/* CPU and PID are mutually exclusive */
1068
if (top.target_tid > 0 && top.cpu_list) {
1069
printf("WARNING: PID switch overriding CPU\n");
1070
sleep(1);
1071
top.cpu_list = NULL;
1072
}
1073
1074
if (top.target_pid != -1)
1075
top.target_tid = top.target_pid;
1076
1077
if (perf_evlist__create_maps(top.evlist, top.target_pid,
1078
top.target_tid, top.cpu_list) < 0)
1079
usage_with_options(top_usage, options);
1080
1081
if (!top.evlist->nr_entries &&
1082
perf_evlist__add_default(top.evlist) < 0) {
1083
pr_err("Not enough memory for event selector list\n");
1084
return -ENOMEM;
1085
}
1086
1087
if (top.delay_secs < 1)
1088
top.delay_secs = 1;
1089
1090
/*
1091
* User specified count overrides default frequency.
1092
*/
1093
if (default_interval)
1094
top.freq = 0;
1095
else if (top.freq) {
1096
default_interval = top.freq;
1097
} else {
1098
fprintf(stderr, "frequency and count are zero, aborting\n");
1099
exit(EXIT_FAILURE);
1100
}
1101
1102
list_for_each_entry(pos, &top.evlist->entries, node) {
1103
if (perf_evsel__alloc_fd(pos, top.evlist->cpus->nr,
1104
top.evlist->threads->nr) < 0)
1105
goto out_free_fd;
1106
/*
1107
* Fill in the ones not specifically initialized via -c:
1108
*/
1109
if (pos->attr.sample_period)
1110
continue;
1111
1112
pos->attr.sample_period = default_interval;
1113
}
1114
1115
if (perf_evlist__alloc_pollfd(top.evlist) < 0 ||
1116
perf_evlist__alloc_mmap(top.evlist) < 0)
1117
goto out_free_fd;
1118
1119
top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
1120
1121
symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) +
1122
(top.evlist->nr_entries + 1) * sizeof(unsigned long));
1123
1124
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
1125
if (symbol__init() < 0)
1126
return -1;
1127
1128
get_term_dimensions(&winsize);
1129
if (top.print_entries == 0) {
1130
update_print_entries(&winsize);
1131
signal(SIGWINCH, sig_winch_handler);
1132
}
1133
1134
status = __cmd_top();
1135
out_free_fd:
1136
perf_evlist__delete(top.evlist);
1137
1138
return status;
1139
}
1140
1141