Root/tools/perf/builtin-top.c

1/*
2 * builtin-top.c
3 *
4 * Builtin top command: Display a continuously updated profile of
5 * any workload, CPU or specific PID.
6 *
7 * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
8 * 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
9 *
10 * Improvements and fixes by:
11 *
12 * Arjan van de Ven <arjan@linux.intel.com>
13 * Yanmin Zhang <yanmin.zhang@intel.com>
14 * Wu Fengguang <fengguang.wu@intel.com>
15 * Mike Galbraith <efault@gmx.de>
16 * Paul Mackerras <paulus@samba.org>
17 *
18 * Released under the GPL v2. (and only v2, not any later version)
19 */
20#include "builtin.h"
21
22#include "perf.h"
23
24#include "util/annotate.h"
25#include "util/cache.h"
26#include "util/color.h"
27#include "util/evlist.h"
28#include "util/evsel.h"
29#include "util/session.h"
30#include "util/symbol.h"
31#include "util/thread.h"
32#include "util/thread_map.h"
33#include "util/top.h"
34#include "util/util.h"
35#include <linux/rbtree.h>
36#include "util/parse-options.h"
37#include "util/parse-events.h"
38#include "util/cpumap.h"
39#include "util/xyarray.h"
40#include "util/sort.h"
41
42#include "util/debug.h"
43
44#include <assert.h>
45#include <elf.h>
46#include <fcntl.h>
47
48#include <stdio.h>
49#include <termios.h>
50#include <unistd.h>
51#include <inttypes.h>
52
53#include <errno.h>
54#include <time.h>
55#include <sched.h>
56
57#include <sys/syscall.h>
58#include <sys/ioctl.h>
59#include <sys/poll.h>
60#include <sys/prctl.h>
61#include <sys/wait.h>
62#include <sys/uio.h>
63#include <sys/utsname.h>
64#include <sys/mman.h>
65
66#include <linux/unistd.h>
67#include <linux/types.h>
68
69void get_term_dimensions(struct winsize *ws)
70{
71    char *s = getenv("LINES");
72
73    if (s != NULL) {
74        ws->ws_row = atoi(s);
75        s = getenv("COLUMNS");
76        if (s != NULL) {
77            ws->ws_col = atoi(s);
78            if (ws->ws_row && ws->ws_col)
79                return;
80        }
81    }
82#ifdef TIOCGWINSZ
83    if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
84        ws->ws_row && ws->ws_col)
85        return;
86#endif
87    ws->ws_row = 25;
88    ws->ws_col = 80;
89}
90
91static void perf_top__update_print_entries(struct perf_top *top)
92{
93    if (top->print_entries > 9)
94        top->print_entries -= 9;
95}
96
97static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *arg)
98{
99    struct perf_top *top = arg;
100
101    get_term_dimensions(&top->winsize);
102    if (!top->print_entries
103        || (top->print_entries+4) > top->winsize.ws_row) {
104        top->print_entries = top->winsize.ws_row;
105    } else {
106        top->print_entries += 4;
107        top->winsize.ws_row = top->print_entries;
108    }
109    perf_top__update_print_entries(top);
110}
111
112static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
113{
114    struct symbol *sym;
115    struct annotation *notes;
116    struct map *map;
117    int err = -1;
118
119    if (!he || !he->ms.sym)
120        return -1;
121
122    sym = he->ms.sym;
123    map = he->ms.map;
124
125    /*
126     * We can't annotate with just /proc/kallsyms
127     */
128    if (map->dso->symtab_type == SYMTAB__KALLSYMS) {
129        pr_err("Can't annotate %s: No vmlinux file was found in the "
130               "path\n", sym->name);
131        sleep(1);
132        return -1;
133    }
134
135    notes = symbol__annotation(sym);
136    if (notes->src != NULL) {
137        pthread_mutex_lock(&notes->lock);
138        goto out_assign;
139    }
140
141    pthread_mutex_lock(&notes->lock);
142
143    if (symbol__alloc_hist(sym) < 0) {
144        pthread_mutex_unlock(&notes->lock);
145        pr_err("Not enough memory for annotating '%s' symbol!\n",
146               sym->name);
147        sleep(1);
148        return err;
149    }
150
151    err = symbol__annotate(sym, map, 0);
152    if (err == 0) {
153out_assign:
154        top->sym_filter_entry = he;
155    }
156
157    pthread_mutex_unlock(&notes->lock);
158    return err;
159}
160
161static void __zero_source_counters(struct hist_entry *he)
162{
163    struct symbol *sym = he->ms.sym;
164    symbol__annotate_zero_histograms(sym);
165}
166
167static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
168{
169    struct utsname uts;
170    int err = uname(&uts);
171
172    ui__warning("Out of bounds address found:\n\n"
173            "Addr: %" PRIx64 "\n"
174            "DSO: %s %c\n"
175            "Map: %" PRIx64 "-%" PRIx64 "\n"
176            "Symbol: %" PRIx64 "-%" PRIx64 " %c %s\n"
177            "Arch: %s\n"
178            "Kernel: %s\n"
179            "Tools: %s\n\n"
180            "Not all samples will be on the annotation output.\n\n"
181            "Please report to linux-kernel@vger.kernel.org\n",
182            ip, map->dso->long_name, dso__symtab_origin(map->dso),
183            map->start, map->end, sym->start, sym->end,
184            sym->binding == STB_GLOBAL ? 'g' :
185            sym->binding == STB_LOCAL ? 'l' : 'w', sym->name,
186            err ? "[unknown]" : uts.machine,
187            err ? "[unknown]" : uts.release, perf_version_string);
188    if (use_browser <= 0)
189        sleep(5);
190    
191    map->erange_warned = true;
192}
193
194static void perf_top__record_precise_ip(struct perf_top *top,
195                    struct hist_entry *he,
196                    int counter, u64 ip)
197{
198    struct annotation *notes;
199    struct symbol *sym;
200    int err;
201
202    if (he == NULL || he->ms.sym == NULL ||
203        ((top->sym_filter_entry == NULL ||
204          top->sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1))
205        return;
206
207    sym = he->ms.sym;
208    notes = symbol__annotation(sym);
209
210    if (pthread_mutex_trylock(&notes->lock))
211        return;
212
213    if (notes->src == NULL && symbol__alloc_hist(sym) < 0) {
214        pthread_mutex_unlock(&notes->lock);
215        pr_err("Not enough memory for annotating '%s' symbol!\n",
216               sym->name);
217        sleep(1);
218        return;
219    }
220
221    ip = he->ms.map->map_ip(he->ms.map, ip);
222    err = symbol__inc_addr_samples(sym, he->ms.map, counter, ip);
223
224    pthread_mutex_unlock(&notes->lock);
225
226    if (err == -ERANGE && !he->ms.map->erange_warned)
227        ui__warn_map_erange(he->ms.map, sym, ip);
228}
229
230static void perf_top__show_details(struct perf_top *top)
231{
232    struct hist_entry *he = top->sym_filter_entry;
233    struct annotation *notes;
234    struct symbol *symbol;
235    int more;
236
237    if (!he)
238        return;
239
240    symbol = he->ms.sym;
241    notes = symbol__annotation(symbol);
242
243    pthread_mutex_lock(&notes->lock);
244
245    if (notes->src == NULL)
246        goto out_unlock;
247
248    printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name);
249    printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
250
251    more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx,
252                       0, top->sym_pcnt_filter, top->print_entries, 4);
253    if (top->zero)
254        symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
255    else
256        symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx);
257    if (more != 0)
258        printf("%d lines not displayed, maybe increase display entries [e]\n", more);
259out_unlock:
260    pthread_mutex_unlock(&notes->lock);
261}
262
263static const char CONSOLE_CLEAR[] = "";
264
265static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
266                             struct addr_location *al,
267                             struct perf_sample *sample)
268{
269    struct hist_entry *he;
270
271    he = __hists__add_entry(&evsel->hists, al, NULL, sample->period);
272    if (he == NULL)
273        return NULL;
274
275    hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
276    return he;
277}
278
279static void perf_top__print_sym_table(struct perf_top *top)
280{
281    char bf[160];
282    int printed = 0;
283    const int win_width = top->winsize.ws_col - 1;
284
285    puts(CONSOLE_CLEAR);
286
287    perf_top__header_snprintf(top, bf, sizeof(bf));
288    printf("%s\n", bf);
289
290    perf_top__reset_sample_counters(top);
291
292    printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
293
294    if (top->sym_evsel->hists.stats.nr_lost_warned !=
295        top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) {
296        top->sym_evsel->hists.stats.nr_lost_warned =
297            top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST];
298        color_fprintf(stdout, PERF_COLOR_RED,
299                  "WARNING: LOST %d chunks, Check IO/CPU overload",
300                  top->sym_evsel->hists.stats.nr_lost_warned);
301        ++printed;
302    }
303
304    if (top->sym_filter_entry) {
305        perf_top__show_details(top);
306        return;
307    }
308
309    hists__collapse_resort_threaded(&top->sym_evsel->hists);
310    hists__output_resort_threaded(&top->sym_evsel->hists);
311    hists__decay_entries_threaded(&top->sym_evsel->hists,
312                      top->hide_user_symbols,
313                      top->hide_kernel_symbols);
314    hists__output_recalc_col_len(&top->sym_evsel->hists,
315                     top->winsize.ws_row - 3);
316    putchar('\n');
317    hists__fprintf(&top->sym_evsel->hists, NULL, false, false,
318               top->winsize.ws_row - 4 - printed, win_width, stdout);
319}
320
321static void prompt_integer(int *target, const char *msg)
322{
323    char *buf = malloc(0), *p;
324    size_t dummy = 0;
325    int tmp;
326
327    fprintf(stdout, "\n%s: ", msg);
328    if (getline(&buf, &dummy, stdin) < 0)
329        return;
330
331    p = strchr(buf, '\n');
332    if (p)
333        *p = 0;
334
335    p = buf;
336    while(*p) {
337        if (!isdigit(*p))
338            goto out_free;
339        p++;
340    }
341    tmp = strtoul(buf, NULL, 10);
342    *target = tmp;
343out_free:
344    free(buf);
345}
346
347static void prompt_percent(int *target, const char *msg)
348{
349    int tmp = 0;
350
351    prompt_integer(&tmp, msg);
352    if (tmp >= 0 && tmp <= 100)
353        *target = tmp;
354}
355
356static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
357{
358    char *buf = malloc(0), *p;
359    struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL;
360    struct rb_node *next;
361    size_t dummy = 0;
362
363    /* zero counters of active symbol */
364    if (syme) {
365        __zero_source_counters(syme);
366        top->sym_filter_entry = NULL;
367    }
368
369    fprintf(stdout, "\n%s: ", msg);
370    if (getline(&buf, &dummy, stdin) < 0)
371        goto out_free;
372
373    p = strchr(buf, '\n');
374    if (p)
375        *p = 0;
376
377    next = rb_first(&top->sym_evsel->hists.entries);
378    while (next) {
379        n = rb_entry(next, struct hist_entry, rb_node);
380        if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) {
381            found = n;
382            break;
383        }
384        next = rb_next(&n->rb_node);
385    }
386
387    if (!found) {
388        fprintf(stderr, "Sorry, %s is not active.\n", buf);
389        sleep(1);
390    } else
391        perf_top__parse_source(top, found);
392
393out_free:
394    free(buf);
395}
396
397static void perf_top__print_mapped_keys(struct perf_top *top)
398{
399    char *name = NULL;
400
401    if (top->sym_filter_entry) {
402        struct symbol *sym = top->sym_filter_entry->ms.sym;
403        name = sym->name;
404    }
405
406    fprintf(stdout, "\nMapped keys:\n");
407    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);
409
410    if (top->evlist->nr_entries > 1)
411        fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top->sym_evsel));
412
413    fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter);
414
415    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");
417    fprintf(stdout, "\t[S] stop annotation.\n");
418
419    fprintf(stdout,
420        "\t[K] hide kernel_symbols symbols. \t(%s)\n",
421        top->hide_kernel_symbols ? "yes" : "no");
422    fprintf(stdout,
423        "\t[U] hide user symbols. \t(%s)\n",
424        top->hide_user_symbols ? "yes" : "no");
425    fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top->zero ? 1 : 0);
426    fprintf(stdout, "\t[qQ] quit.\n");
427}
428
429static int perf_top__key_mapped(struct perf_top *top, int c)
430{
431    switch (c) {
432        case 'd':
433        case 'e':
434        case 'f':
435        case 'z':
436        case 'q':
437        case 'Q':
438        case 'K':
439        case 'U':
440        case 'F':
441        case 's':
442        case 'S':
443            return 1;
444        case 'E':
445            return top->evlist->nr_entries > 1 ? 1 : 0;
446        default:
447            break;
448    }
449
450    return 0;
451}
452
453static void perf_top__handle_keypress(struct perf_top *top, int c)
454{
455    if (!perf_top__key_mapped(top, c)) {
456        struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
457        struct termios tc, save;
458
459        perf_top__print_mapped_keys(top);
460        fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
461        fflush(stdout);
462
463        tcgetattr(0, &save);
464        tc = save;
465        tc.c_lflag &= ~(ICANON | ECHO);
466        tc.c_cc[VMIN] = 0;
467        tc.c_cc[VTIME] = 0;
468        tcsetattr(0, TCSANOW, &tc);
469
470        poll(&stdin_poll, 1, -1);
471        c = getc(stdin);
472
473        tcsetattr(0, TCSAFLUSH, &save);
474        if (!perf_top__key_mapped(top, c))
475            return;
476    }
477
478    switch (c) {
479        case 'd':
480            prompt_integer(&top->delay_secs, "Enter display delay");
481            if (top->delay_secs < 1)
482                top->delay_secs = 1;
483            break;
484        case 'e':
485            prompt_integer(&top->print_entries, "Enter display entries (lines)");
486            if (top->print_entries == 0) {
487                struct sigaction act = {
488                    .sa_sigaction = perf_top__sig_winch,
489                    .sa_flags = SA_SIGINFO,
490                };
491                perf_top__sig_winch(SIGWINCH, NULL, top);
492                sigaction(SIGWINCH, &act, NULL);
493            } else {
494                perf_top__sig_winch(SIGWINCH, NULL, top);
495                signal(SIGWINCH, SIG_DFL);
496            }
497            break;
498        case 'E':
499            if (top->evlist->nr_entries > 1) {
500                /* Select 0 as the default event: */
501                int counter = 0;
502
503                fprintf(stderr, "\nAvailable events:");
504
505                list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
506                    fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel));
507
508                prompt_integer(&counter, "Enter details event counter");
509
510                if (counter >= top->evlist->nr_entries) {
511                    top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
512                    fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel));
513                    sleep(1);
514                    break;
515                }
516                list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
517                    if (top->sym_evsel->idx == counter)
518                        break;
519            } else
520                top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
521            break;
522        case 'f':
523            prompt_integer(&top->count_filter, "Enter display event count filter");
524            break;
525        case 'F':
526            prompt_percent(&top->sym_pcnt_filter,
527                       "Enter details display event filter (percent)");
528            break;
529        case 'K':
530            top->hide_kernel_symbols = !top->hide_kernel_symbols;
531            break;
532        case 'q':
533        case 'Q':
534            printf("exiting.\n");
535            if (top->dump_symtab)
536                perf_session__fprintf_dsos(top->session, stderr);
537            exit(0);
538        case 's':
539            perf_top__prompt_symbol(top, "Enter details symbol");
540            break;
541        case 'S':
542            if (!top->sym_filter_entry)
543                break;
544            else {
545                struct hist_entry *syme = top->sym_filter_entry;
546
547                top->sym_filter_entry = NULL;
548                __zero_source_counters(syme);
549            }
550            break;
551        case 'U':
552            top->hide_user_symbols = !top->hide_user_symbols;
553            break;
554        case 'z':
555            top->zero = !top->zero;
556            break;
557        default:
558            break;
559    }
560}
561
562static void perf_top__sort_new_samples(void *arg)
563{
564    struct perf_top *t = arg;
565    perf_top__reset_sample_counters(t);
566
567    if (t->evlist->selected != NULL)
568        t->sym_evsel = t->evlist->selected;
569
570    hists__collapse_resort_threaded(&t->sym_evsel->hists);
571    hists__output_resort_threaded(&t->sym_evsel->hists);
572    hists__decay_entries_threaded(&t->sym_evsel->hists,
573                      t->hide_user_symbols,
574                      t->hide_kernel_symbols);
575}
576
577static void *display_thread_tui(void *arg)
578{
579    struct perf_evsel *pos;
580    struct perf_top *top = arg;
581    const char *help = "For a higher level overview, try: perf top --sort comm,dso";
582
583    perf_top__sort_new_samples(top);
584
585    /*
586     * Initialize the uid_filter_str, in the future the TUI will allow
587     * Zooming in/out UIDs. For now juse use whatever the user passed
588     * via --uid.
589     */
590    list_for_each_entry(pos, &top->evlist->entries, node)
591        pos->hists.uid_filter_str = top->target.uid_str;
592
593    perf_evlist__tui_browse_hists(top->evlist, help,
594                      perf_top__sort_new_samples,
595                      top, top->delay_secs);
596
597    exit_browser(0);
598    exit(0);
599    return NULL;
600}
601
602static void *display_thread(void *arg)
603{
604    struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
605    struct termios tc, save;
606    struct perf_top *top = arg;
607    int delay_msecs, c;
608
609    tcgetattr(0, &save);
610    tc = save;
611    tc.c_lflag &= ~(ICANON | ECHO);
612    tc.c_cc[VMIN] = 0;
613    tc.c_cc[VTIME] = 0;
614
615    pthread__unblock_sigwinch();
616repeat:
617    delay_msecs = top->delay_secs * 1000;
618    tcsetattr(0, TCSANOW, &tc);
619    /* trash return*/
620    getc(stdin);
621
622    while (1) {
623        perf_top__print_sym_table(top);
624        /*
625         * Either timeout expired or we got an EINTR due to SIGWINCH,
626         * refresh screen in both cases.
627         */
628        switch (poll(&stdin_poll, 1, delay_msecs)) {
629        case 0:
630            continue;
631        case -1:
632            if (errno == EINTR)
633                continue;
634            /* Fall trhu */
635        default:
636            goto process_hotkey;
637        }
638    }
639process_hotkey:
640    c = getc(stdin);
641    tcsetattr(0, TCSAFLUSH, &save);
642
643    perf_top__handle_keypress(top, c);
644    goto repeat;
645
646    return NULL;
647}
648
649/* Tag samples to be skipped. */
650static const char *skip_symbols[] = {
651    "intel_idle",
652    "default_idle",
653    "native_safe_halt",
654    "cpu_idle",
655    "enter_idle",
656    "exit_idle",
657    "mwait_idle",
658    "mwait_idle_with_hints",
659    "poll_idle",
660    "ppc64_runlatch_off",
661    "pseries_dedicated_idle_sleep",
662    NULL
663};
664
665static int symbol_filter(struct map *map __used, struct symbol *sym)
666{
667    const char *name = sym->name;
668    int i;
669
670    /*
671     * ppc64 uses function descriptors and appends a '.' to the
672     * start of every instruction address. Remove it.
673     */
674    if (name[0] == '.')
675        name++;
676
677    if (!strcmp(name, "_text") ||
678        !strcmp(name, "_etext") ||
679        !strcmp(name, "_sinittext") ||
680        !strncmp("init_module", name, 11) ||
681        !strncmp("cleanup_module", name, 14) ||
682        strstr(name, "_text_start") ||
683        strstr(name, "_text_end"))
684        return 1;
685
686    for (i = 0; skip_symbols[i]; i++) {
687        if (!strcmp(skip_symbols[i], name)) {
688            sym->ignore = true;
689            break;
690        }
691    }
692
693    return 0;
694}
695
696static void perf_event__process_sample(struct perf_tool *tool,
697                       const union perf_event *event,
698                       struct perf_evsel *evsel,
699                       struct perf_sample *sample,
700                       struct machine *machine)
701{
702    struct perf_top *top = container_of(tool, struct perf_top, tool);
703    struct symbol *parent = NULL;
704    u64 ip = event->ip.ip;
705    struct addr_location al;
706    int err;
707
708    if (!machine && perf_guest) {
709        pr_err("Can't find guest [%d]'s kernel information\n",
710            event->ip.pid);
711        return;
712    }
713
714    if (!machine) {
715        pr_err("%u unprocessable samples recorded.",
716               top->session->hists.stats.nr_unprocessable_samples++);
717        return;
718    }
719
720    if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
721        top->exact_samples++;
722
723    if (perf_event__preprocess_sample(event, machine, &al, sample,
724                      symbol_filter) < 0 ||
725        al.filtered)
726        return;
727
728    if (!top->kptr_restrict_warned &&
729        symbol_conf.kptr_restrict &&
730        al.cpumode == PERF_RECORD_MISC_KERNEL) {
731        ui__warning(
732"Kernel address maps (/proc/{kallsyms,modules}) are restricted.\n\n"
733"Check /proc/sys/kernel/kptr_restrict.\n\n"
734"Kernel%s samples will not be resolved.\n",
735              !RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION]) ?
736              " modules" : "");
737        if (use_browser <= 0)
738            sleep(5);
739        top->kptr_restrict_warned = true;
740    }
741
742    if (al.sym == NULL) {
743        const char *msg = "Kernel samples will not be resolved.\n";
744        /*
745         * As we do lazy loading of symtabs we only will know if the
746         * specified vmlinux file is invalid when we actually have a
747         * hit in kernel space and then try to load it. So if we get
748         * here and there are _no_ symbols in the DSO backing the
749         * kernel map, bail out.
750         *
751         * We may never get here, for instance, if we use -K/
752         * --hide-kernel-symbols, even if the user specifies an
753         * invalid --vmlinux ;-)
754         */
755        if (!top->kptr_restrict_warned && !top->vmlinux_warned &&
756            al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
757            RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
758            if (symbol_conf.vmlinux_name) {
759                ui__warning("The %s file can't be used.\n%s",
760                        symbol_conf.vmlinux_name, msg);
761            } else {
762                ui__warning("A vmlinux file was not found.\n%s",
763                        msg);
764            }
765
766            if (use_browser <= 0)
767                sleep(5);
768            top->vmlinux_warned = true;
769        }
770    }
771
772    if (al.sym == NULL || !al.sym->ignore) {
773        struct hist_entry *he;
774
775        if ((sort__has_parent || symbol_conf.use_callchain) &&
776            sample->callchain) {
777            err = machine__resolve_callchain(machine, evsel, al.thread,
778                             sample->callchain, &parent);
779            if (err)
780                return;
781        }
782
783        he = perf_evsel__add_hist_entry(evsel, &al, sample);
784        if (he == NULL) {
785            pr_err("Problem incrementing symbol period, skipping event\n");
786            return;
787        }
788
789        if (symbol_conf.use_callchain) {
790            err = callchain_append(he->callchain, &callchain_cursor,
791                           sample->period);
792            if (err)
793                return;
794        }
795
796        if (top->sort_has_symbols)
797            perf_top__record_precise_ip(top, he, evsel->idx, ip);
798    }
799
800    return;
801}
802
803static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
804{
805    struct perf_sample sample;
806    struct perf_evsel *evsel;
807    struct perf_session *session = top->session;
808    union perf_event *event;
809    struct machine *machine;
810    u8 origin;
811    int ret;
812
813    while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) {
814        ret = perf_session__parse_sample(session, event, &sample);
815        if (ret) {
816            pr_err("Can't parse sample, err = %d\n", ret);
817            continue;
818        }
819
820        evsel = perf_evlist__id2evsel(session->evlist, sample.id);
821        assert(evsel != NULL);
822
823        origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
824
825        if (event->header.type == PERF_RECORD_SAMPLE)
826            ++top->samples;
827
828        switch (origin) {
829        case PERF_RECORD_MISC_USER:
830            ++top->us_samples;
831            if (top->hide_user_symbols)
832                continue;
833            machine = perf_session__find_host_machine(session);
834            break;
835        case PERF_RECORD_MISC_KERNEL:
836            ++top->kernel_samples;
837            if (top->hide_kernel_symbols)
838                continue;
839            machine = perf_session__find_host_machine(session);
840            break;
841        case PERF_RECORD_MISC_GUEST_KERNEL:
842            ++top->guest_kernel_samples;
843            machine = perf_session__find_machine(session, event->ip.pid);
844            break;
845        case PERF_RECORD_MISC_GUEST_USER:
846            ++top->guest_us_samples;
847            /*
848             * TODO: we don't process guest user from host side
849             * except simple counting.
850             */
851            /* Fall thru */
852        default:
853            continue;
854        }
855
856
857        if (event->header.type == PERF_RECORD_SAMPLE) {
858            perf_event__process_sample(&top->tool, event, evsel,
859                           &sample, machine);
860        } else if (event->header.type < PERF_RECORD_MAX) {
861            hists__inc_nr_events(&evsel->hists, event->header.type);
862            perf_event__process(&top->tool, event, &sample, machine);
863        } else
864            ++session->hists.stats.nr_unknown_events;
865    }
866}
867
868static void perf_top__mmap_read(struct perf_top *top)
869{
870    int i;
871
872    for (i = 0; i < top->evlist->nr_mmaps; i++)
873        perf_top__mmap_read_idx(top, i);
874}
875
876static void perf_top__start_counters(struct perf_top *top)
877{
878    struct perf_evsel *counter, *first;
879    struct perf_evlist *evlist = top->evlist;
880
881    first = list_entry(evlist->entries.next, struct perf_evsel, node);
882
883    list_for_each_entry(counter, &evlist->entries, node) {
884        struct perf_event_attr *attr = &counter->attr;
885        struct xyarray *group_fd = NULL;
886
887        if (top->group && counter != first)
888            group_fd = first->fd;
889
890        attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
891
892        if (top->freq) {
893            attr->sample_type |= PERF_SAMPLE_PERIOD;
894            attr->freq = 1;
895            attr->sample_freq = top->freq;
896        }
897
898        if (evlist->nr_entries > 1) {
899            attr->sample_type |= PERF_SAMPLE_ID;
900            attr->read_format |= PERF_FORMAT_ID;
901        }
902
903        if (perf_target__has_cpu(&top->target))
904            attr->sample_type |= PERF_SAMPLE_CPU;
905
906        if (symbol_conf.use_callchain)
907            attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
908
909        attr->mmap = 1;
910        attr->comm = 1;
911        attr->inherit = top->inherit;
912fallback_missing_features:
913        if (top->exclude_guest_missing)
914            attr->exclude_guest = attr->exclude_host = 0;
915retry_sample_id:
916        attr->sample_id_all = top->sample_id_all_missing ? 0 : 1;
917try_again:
918        if (perf_evsel__open(counter, top->evlist->cpus,
919                     top->evlist->threads, top->group,
920                     group_fd) < 0) {
921            int err = errno;
922
923            if (err == EPERM || err == EACCES) {
924                ui__error_paranoid();
925                goto out_err;
926            } else if (err == EINVAL) {
927                if (!top->exclude_guest_missing &&
928                    (attr->exclude_guest || attr->exclude_host)) {
929                    pr_debug("Old kernel, cannot exclude "
930                         "guest or host samples.\n");
931                    top->exclude_guest_missing = true;
932                    goto fallback_missing_features;
933                } else if (!top->sample_id_all_missing) {
934                    /*
935                     * Old kernel, no attr->sample_id_type_all field
936                     */
937                    top->sample_id_all_missing = true;
938                    goto retry_sample_id;
939                }
940            }
941            /*
942             * If it's cycles then fall back to hrtimer
943             * based cpu-clock-tick sw counter, which
944             * is always available even if no PMU support:
945             */
946            if (attr->type == PERF_TYPE_HARDWARE &&
947                attr->config == PERF_COUNT_HW_CPU_CYCLES) {
948                if (verbose)
949                    ui__warning("Cycles event not supported,\n"
950                            "trying to fall back to cpu-clock-ticks\n");
951
952                attr->type = PERF_TYPE_SOFTWARE;
953                attr->config = PERF_COUNT_SW_CPU_CLOCK;
954                if (counter->name) {
955                    free(counter->name);
956                    counter->name = NULL;
957                }
958                goto try_again;
959            }
960
961            if (err == ENOENT) {
962                ui__error("The %s event is not supported.\n",
963                        event_name(counter));
964                goto out_err;
965            } else if (err == EMFILE) {
966                ui__error("Too many events are opened.\n"
967                        "Try again after reducing the number of events\n");
968                goto out_err;
969            }
970
971            ui__error("The sys_perf_event_open() syscall "
972                    "returned with %d (%s). /bin/dmesg "
973                    "may provide additional information.\n"
974                    "No CONFIG_PERF_EVENTS=y kernel support "
975                    "configured?\n", err, strerror(err));
976            goto out_err;
977        }
978    }
979
980    if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) {
981        ui__error("Failed to mmap with %d (%s)\n",
982                errno, strerror(errno));
983        goto out_err;
984    }
985
986    return;
987
988out_err:
989    exit_browser(0);
990    exit(0);
991}
992
993static int perf_top__setup_sample_type(struct perf_top *top)
994{
995    if (!top->sort_has_symbols) {
996        if (symbol_conf.use_callchain) {
997            ui__error("Selected -g but \"sym\" not present in --sort/-s.");
998            return -EINVAL;
999        }
1000    } else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) {
1001        if (callchain_register_param(&callchain_param) < 0) {
1002            ui__error("Can't register callchain params.\n");
1003            return -EINVAL;
1004        }
1005    }
1006
1007    return 0;
1008}
1009
1010static int __cmd_top(struct perf_top *top)
1011{
1012    pthread_t thread;
1013    int ret;
1014    /*
1015     * FIXME: perf_session__new should allow passing a O_MMAP, so that all this
1016     * mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
1017     */
1018    top->session = perf_session__new(NULL, O_WRONLY, false, false, NULL);
1019    if (top->session == NULL)
1020        return -ENOMEM;
1021
1022    ret = perf_top__setup_sample_type(top);
1023    if (ret)
1024        goto out_delete;
1025
1026    if (perf_target__has_task(&top->target))
1027        perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
1028                          perf_event__process,
1029                          &top->session->host_machine);
1030    else
1031        perf_event__synthesize_threads(&top->tool, perf_event__process,
1032                           &top->session->host_machine);
1033    perf_top__start_counters(top);
1034    top->session->evlist = top->evlist;
1035    perf_session__update_sample_type(top->session);
1036
1037    /* Wait for a minimal set of events before starting the snapshot */
1038    poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
1039
1040    perf_top__mmap_read(top);
1041
1042    if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :
1043                                display_thread), top)) {
1044        ui__error("Could not create display thread.\n");
1045        exit(-1);
1046    }
1047
1048    if (top->realtime_prio) {
1049        struct sched_param param;
1050
1051        param.sched_priority = top->realtime_prio;
1052        if (sched_setscheduler(0, SCHED_FIFO, &param)) {
1053            ui__error("Could not set realtime priority.\n");
1054            exit(-1);
1055        }
1056    }
1057
1058    while (1) {
1059        u64 hits = top->samples;
1060
1061        perf_top__mmap_read(top);
1062
1063        if (hits == top->samples)
1064            ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
1065    }
1066
1067out_delete:
1068    perf_session__delete(top->session);
1069    top->session = NULL;
1070
1071    return 0;
1072}
1073
1074static int
1075parse_callchain_opt(const struct option *opt, const char *arg, int unset)
1076{
1077    struct perf_top *top = (struct perf_top *)opt->value;
1078    char *tok, *tok2;
1079    char *endptr;
1080
1081    /*
1082     * --no-call-graph
1083     */
1084    if (unset) {
1085        top->dont_use_callchains = true;
1086        return 0;
1087    }
1088
1089    symbol_conf.use_callchain = true;
1090
1091    if (!arg)
1092        return 0;
1093
1094    tok = strtok((char *)arg, ",");
1095    if (!tok)
1096        return -1;
1097
1098    /* get the output mode */
1099    if (!strncmp(tok, "graph", strlen(arg)))
1100        callchain_param.mode = CHAIN_GRAPH_ABS;
1101
1102    else if (!strncmp(tok, "flat", strlen(arg)))
1103        callchain_param.mode = CHAIN_FLAT;
1104
1105    else if (!strncmp(tok, "fractal", strlen(arg)))
1106        callchain_param.mode = CHAIN_GRAPH_REL;
1107
1108    else if (!strncmp(tok, "none", strlen(arg))) {
1109        callchain_param.mode = CHAIN_NONE;
1110        symbol_conf.use_callchain = false;
1111
1112        return 0;
1113    } else
1114        return -1;
1115
1116    /* get the min percentage */
1117    tok = strtok(NULL, ",");
1118    if (!tok)
1119        goto setup;
1120
1121    callchain_param.min_percent = strtod(tok, &endptr);
1122    if (tok == endptr)
1123        return -1;
1124
1125    /* get the print limit */
1126    tok2 = strtok(NULL, ",");
1127    if (!tok2)
1128        goto setup;
1129
1130    if (tok2[0] != 'c') {
1131        callchain_param.print_limit = strtod(tok2, &endptr);
1132        tok2 = strtok(NULL, ",");
1133        if (!tok2)
1134            goto setup;
1135    }
1136
1137    /* get the call chain order */
1138    if (!strcmp(tok2, "caller"))
1139        callchain_param.order = ORDER_CALLER;
1140    else if (!strcmp(tok2, "callee"))
1141        callchain_param.order = ORDER_CALLEE;
1142    else
1143        return -1;
1144setup:
1145    if (callchain_register_param(&callchain_param) < 0) {
1146        fprintf(stderr, "Can't register callchain params\n");
1147        return -1;
1148    }
1149    return 0;
1150}
1151
1152static const char * const top_usage[] = {
1153    "perf top [<options>]",
1154    NULL
1155};
1156
1157int cmd_top(int argc, const char **argv, const char *prefix __used)
1158{
1159    struct perf_evsel *pos;
1160    int status;
1161    char errbuf[BUFSIZ];
1162    struct perf_top top = {
1163        .count_filter = 5,
1164        .delay_secs = 2,
1165        .freq = 4000, /* 4 KHz */
1166        .mmap_pages = 128,
1167        .sym_pcnt_filter = 5,
1168        .target = {
1169            .uses_mmap = true,
1170        },
1171    };
1172    char callchain_default_opt[] = "fractal,0.5,callee";
1173    const struct option options[] = {
1174    OPT_CALLBACK('e', "event", &top.evlist, "event",
1175             "event selector. use 'perf list' to list available events",
1176             parse_events_option),
1177    OPT_INTEGER('c', "count", &top.default_interval,
1178            "event period to sample"),
1179    OPT_STRING('p', "pid", &top.target.pid, "pid",
1180            "profile events on existing process id"),
1181    OPT_STRING('t', "tid", &top.target.tid, "tid",
1182            "profile events on existing thread id"),
1183    OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide,
1184                "system-wide collection from all CPUs"),
1185    OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu",
1186            "list of cpus to monitor"),
1187    OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
1188           "file", "vmlinux pathname"),
1189    OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols,
1190            "hide kernel symbols"),
1191    OPT_UINTEGER('m', "mmap-pages", &top.mmap_pages, "number of mmap data pages"),
1192    OPT_INTEGER('r', "realtime", &top.realtime_prio,
1193            "collect data with this RT SCHED_FIFO priority"),
1194    OPT_INTEGER('d', "delay", &top.delay_secs,
1195            "number of seconds to delay between refreshes"),
1196    OPT_BOOLEAN('D', "dump-symtab", &top.dump_symtab,
1197                "dump the symbol table used for profiling"),
1198    OPT_INTEGER('f', "count-filter", &top.count_filter,
1199            "only display functions with more events than this"),
1200    OPT_BOOLEAN('g', "group", &top.group,
1201                "put the counters into a counter group"),
1202    OPT_BOOLEAN('i', "inherit", &top.inherit,
1203            "child tasks inherit counters"),
1204    OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name",
1205            "symbol to annotate"),
1206    OPT_BOOLEAN('z', "zero", &top.zero,
1207            "zero history across updates"),
1208    OPT_INTEGER('F', "freq", &top.freq,
1209            "profile at this frequency"),
1210    OPT_INTEGER('E', "entries", &top.print_entries,
1211            "display this many functions"),
1212    OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols,
1213            "hide user symbols"),
1214    OPT_BOOLEAN(0, "tui", &top.use_tui, "Use the TUI interface"),
1215    OPT_BOOLEAN(0, "stdio", &top.use_stdio, "Use the stdio interface"),
1216    OPT_INCR('v', "verbose", &verbose,
1217            "be more verbose (show counter open errors, etc)"),
1218    OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1219           "sort by key(s): pid, comm, dso, symbol, parent"),
1220    OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
1221            "Show a column with the number of samples"),
1222    OPT_CALLBACK_DEFAULT('G', "call-graph", &top, "output_type,min_percent, call_order",
1223             "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. "
1224             "Default: fractal,0.5,callee", &parse_callchain_opt,
1225             callchain_default_opt),
1226    OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
1227            "Show a column with the sum of periods"),
1228    OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
1229           "only consider symbols in these dsos"),
1230    OPT_STRING(0, "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
1231           "only consider symbols in these comms"),
1232    OPT_STRING(0, "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
1233           "only consider these symbols"),
1234    OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
1235            "Interleave source code with assembly code (default)"),
1236    OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
1237            "Display raw encoding of assembly instructions (default)"),
1238    OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
1239           "Specify disassembler style (e.g. -M intel for intel syntax)"),
1240    OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"),
1241    OPT_END()
1242    };
1243
1244    top.evlist = perf_evlist__new(NULL, NULL);
1245    if (top.evlist == NULL)
1246        return -ENOMEM;
1247
1248    symbol_conf.exclude_other = false;
1249
1250    argc = parse_options(argc, argv, options, top_usage, 0);
1251    if (argc)
1252        usage_with_options(top_usage, options);
1253
1254    if (sort_order == default_sort_order)
1255        sort_order = "dso,symbol";
1256
1257    setup_sorting(top_usage, options);
1258
1259    if (top.use_stdio)
1260        use_browser = 0;
1261    else if (top.use_tui)
1262        use_browser = 1;
1263
1264    setup_browser(false);
1265
1266    status = perf_target__validate(&top.target);
1267    if (status) {
1268        perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
1269        ui__warning("%s", errbuf);
1270    }
1271
1272    status = perf_target__parse_uid(&top.target);
1273    if (status) {
1274        int saved_errno = errno;
1275
1276        perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
1277        ui__error("%s", errbuf);
1278
1279        status = -saved_errno;
1280        goto out_delete_evlist;
1281    }
1282
1283    if (perf_target__none(&top.target))
1284        top.target.system_wide = true;
1285
1286    if (perf_evlist__create_maps(top.evlist, &top.target) < 0)
1287        usage_with_options(top_usage, options);
1288
1289    if (!top.evlist->nr_entries &&
1290        perf_evlist__add_default(top.evlist) < 0) {
1291        ui__error("Not enough memory for event selector list\n");
1292        return -ENOMEM;
1293    }
1294
1295    symbol_conf.nr_events = top.evlist->nr_entries;
1296
1297    if (top.delay_secs < 1)
1298        top.delay_secs = 1;
1299
1300    /*
1301     * User specified count overrides default frequency.
1302     */
1303    if (top.default_interval)
1304        top.freq = 0;
1305    else if (top.freq) {
1306        top.default_interval = top.freq;
1307    } else {
1308        ui__error("frequency and count are zero, aborting\n");
1309        exit(EXIT_FAILURE);
1310    }
1311
1312    list_for_each_entry(pos, &top.evlist->entries, node) {
1313        /*
1314         * Fill in the ones not specifically initialized via -c:
1315         */
1316        if (!pos->attr.sample_period)
1317            pos->attr.sample_period = top.default_interval;
1318    }
1319
1320    top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
1321
1322    symbol_conf.priv_size = sizeof(struct annotation);
1323
1324    symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
1325    if (symbol__init() < 0)
1326        return -1;
1327
1328    sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
1329    sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
1330    sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
1331
1332    /*
1333     * Avoid annotation data structures overhead when symbols aren't on the
1334     * sort list.
1335     */
1336    top.sort_has_symbols = sort_sym.list.next != NULL;
1337
1338    get_term_dimensions(&top.winsize);
1339    if (top.print_entries == 0) {
1340        struct sigaction act = {
1341            .sa_sigaction = perf_top__sig_winch,
1342            .sa_flags = SA_SIGINFO,
1343        };
1344        perf_top__update_print_entries(&top);
1345        sigaction(SIGWINCH, &act, NULL);
1346    }
1347
1348    status = __cmd_top(&top);
1349
1350out_delete_evlist:
1351    perf_evlist__delete(top.evlist);
1352
1353    return status;
1354}
1355

Archive Download this file



interactive