diff options
Diffstat (limited to 'tools/perf')
39 files changed, 1846 insertions, 653 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index af0e5d64a9f7..fa37cd53e9b9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -212,8 +212,8 @@ endif export PERL_PATH -FLEX = $(CROSS_COMPILE)flex -BISON= $(CROSS_COMPILE)bison +FLEX = flex +BISON= bison $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c @@ -312,6 +312,7 @@ LIB_H += util/top.h LIB_H += $(ARCH_INCLUDE) LIB_H += util/cgroup.h LIB_H += $(EVENT_PARSE_DIR)event-parse.h +LIB_H += util/target.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o @@ -373,6 +374,7 @@ LIB_OBJS += $(OUTPUT)util/util.o LIB_OBJS += $(OUTPUT)util/xyarray.o LIB_OBJS += $(OUTPUT)util/cpumap.o LIB_OBJS += $(OUTPUT)util/cgroup.o +LIB_OBJS += $(OUTPUT)util/target.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o @@ -493,6 +495,7 @@ else LIB_OBJS += $(OUTPUT)ui/helpline.o LIB_OBJS += $(OUTPUT)ui/progress.o LIB_OBJS += $(OUTPUT)ui/util.o + LIB_OBJS += $(OUTPUT)ui/tui/setup.o LIB_H += ui/browser.h LIB_H += ui/browsers/map.h LIB_H += ui/helpline.h @@ -515,6 +518,11 @@ else BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0) EXTLIBS += $(shell pkg-config --libs gtk+-2.0) LIB_OBJS += $(OUTPUT)ui/gtk/browser.o + LIB_OBJS += $(OUTPUT)ui/gtk/setup.o + # Make sure that it'd be included only once. + ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),) + LIB_OBJS += $(OUTPUT)ui/setup.o + endif endif endif @@ -741,10 +749,10 @@ $(OUTPUT)perf.o perf.spec \ # over the general rule for .o $(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -w $< $(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -w $< $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 10b1f1f25ed7..8a3dfac161e2 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -44,7 +44,6 @@ struct perf_record { struct perf_evlist *evlist; struct perf_session *session; const char *progname; - const char *uid_str; int output; unsigned int page_size; int realtime_prio; @@ -218,7 +217,7 @@ try_again: if (err == EPERM || err == EACCES) { ui__error_paranoid(); exit(EXIT_FAILURE); - } else if (err == ENODEV && opts->cpu_list) { + } else if (err == ENODEV && opts->target.cpu_list) { die("No such device - did you specify" " an out-of-range profile CPU?\n"); } else if (err == EINVAL) { @@ -243,9 +242,13 @@ try_again: /* * If it's cycles then fall back to hrtimer * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: + * is always available even if no PMU support. + * + * PPC returns ENXIO until 2.6.37 (behavior changed + * with commit b0a873e). */ - if (err == ENOENT && attr->type == PERF_TYPE_HARDWARE + if ((err == ENOENT || err == ENXIO) + && attr->type == PERF_TYPE_HARDWARE && attr->config == PERF_COUNT_HW_CPU_CYCLES) { if (verbose) @@ -253,6 +256,10 @@ try_again: "trying to fall back to cpu-clock-ticks\n"); attr->type = PERF_TYPE_SOFTWARE; attr->config = PERF_COUNT_SW_CPU_CLOCK; + if (pos->name) { + free(pos->name); + pos->name = NULL; + } goto try_again; } @@ -578,7 +585,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) perf_session__process_machines(session, tool, perf_event__synthesize_guest_os); - if (!opts->system_wide) + if (!opts->target.system_wide) perf_event__synthesize_thread_map(tool, evsel_list->threads, process_synthesized_event, machine); @@ -747,6 +754,9 @@ static struct perf_record record = { .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, .freq = 1000, + .target = { + .uses_mmap = true, + }, }, .write_mode = WRITE_FORCE, .file_new = true, @@ -765,9 +775,9 @@ const struct option record_options[] = { parse_events_option), OPT_CALLBACK(0, "filter", &record.evlist, "filter", "event filter", parse_filter), - OPT_STRING('p', "pid", &record.opts.target_pid, "pid", + OPT_STRING('p', "pid", &record.opts.target.pid, "pid", "record events on existing process id"), - OPT_STRING('t', "tid", &record.opts.target_tid, "tid", + OPT_STRING('t', "tid", &record.opts.target.tid, "tid", "record events on existing thread id"), OPT_INTEGER('r', "realtime", &record.realtime_prio, "collect data with this RT SCHED_FIFO priority"), @@ -775,11 +785,11 @@ const struct option record_options[] = { "collect data without buffering"), OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples, "collect raw sample records from all opened counters"), - OPT_BOOLEAN('a', "all-cpus", &record.opts.system_wide, + OPT_BOOLEAN('a', "all-cpus", &record.opts.target.system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('A', "append", &record.append_file, "append to the output file to do incremental profiling"), - OPT_STRING('C', "cpu", &record.opts.cpu_list, "cpu", + OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_BOOLEAN('f', "force", &record.force, "overwrite existing data file (deprecated)"), @@ -813,7 +823,8 @@ const struct option record_options[] = { OPT_CALLBACK('G', "cgroup", &record.evlist, "name", "monitor event in cgroup name only", parse_cgroups), - OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), + OPT_STRING('u', "uid", &record.opts.target.uid_str, "user", + "user to profile"), OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, "branch any", "sample any taken branches", @@ -831,6 +842,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) struct perf_evsel *pos; struct perf_evlist *evsel_list; struct perf_record *rec = &record; + char errbuf[BUFSIZ]; perf_header__set_cmdline(argc, argv); @@ -842,8 +854,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && !rec->opts.target_pid && !rec->opts.target_tid && - !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str) + if (!argc && perf_target__none(&rec->opts.target)) usage_with_options(record_usage, record_options); if (rec->force && rec->append_file) { @@ -856,7 +867,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) rec->write_mode = WRITE_FORCE; } - if (nr_cgroups && !rec->opts.system_wide) { + if (nr_cgroups && !rec->opts.target.system_wide) { fprintf(stderr, "cgroup monitoring only available in" " system-wide mode\n"); usage_with_options(record_usage, record_options); @@ -883,17 +894,25 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) goto out_symbol_exit; } - rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid, - rec->opts.target_pid); - if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1) - goto out_free_fd; + err = perf_target__validate(&rec->opts.target); + if (err) { + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + } + + err = perf_target__parse_uid(&rec->opts.target); + if (err) { + int saved_errno = errno; - if (rec->opts.target_pid) - rec->opts.target_tid = rec->opts.target_pid; + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + + err = -saved_errno; + goto out_free_fd; + } - if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, - rec->opts.target_tid, rec->opts.uid, - rec->opts.cpu_list) < 0) + err = -ENOMEM; + if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); list_for_each_entry(pos, &evsel_list->entries, node) { diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index cec2b8cee80c..d58e41445d0d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -304,7 +304,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, if (evname != NULL) ret += fprintf(fp, " of event '%s'", evname); - ret += fprintf(fp, "\n# Event count (approx.): %lu", nr_events); + ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); return ret + fprintf(fp, "\n#\n"); } @@ -377,16 +377,23 @@ static int __cmd_report(struct perf_report *rep) (kernel_map->dso->hit && (kernel_kmap->ref_reloc_sym == NULL || kernel_kmap->ref_reloc_sym->addr == 0))) { - const struct dso *kdso = kernel_map->dso; + const char *desc = + "As no suitable kallsyms nor vmlinux was found, kernel samples\n" + "can't be resolved."; + + if (kernel_map) { + const struct dso *kdso = kernel_map->dso; + if (!RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION])) { + desc = "If some relocation was applied (e.g. " + "kexec) symbols may be misresolved."; + } + } ui__warning( "Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n" "Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n" "Samples in kernel modules can't be resolved as well.\n\n", - RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION]) ? -"As no suitable kallsyms nor vmlinux was found, kernel samples\n" -"can't be resolved." : -"If some relocation was applied (e.g. kexec) symbols may be misresolved."); + desc); } if (dump_trace) { @@ -676,14 +683,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) } - if (strcmp(report.input_name, "-") != 0) { - if (report.use_gtk) - perf_gtk_setup_browser(argc, argv, true); - else - setup_browser(true); - } else { + if (strcmp(report.input_name, "-") != 0) + setup_browser(true); + else use_browser = 0; - } /* * Only in the newt browser we are doing integrated annotation, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index dde9e17c018b..62ae30d34fa6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -175,22 +175,21 @@ static struct perf_event_attr very_very_detailed_attrs[] = { static struct perf_evlist *evsel_list; -static bool system_wide = false; -static int run_idx = 0; +static struct perf_target target = { + .uid = UINT_MAX, +}; +static int run_idx = 0; static int run_count = 1; static bool no_inherit = false; static bool scale = true; static bool no_aggr = false; -static const char *target_pid; -static const char *target_tid; static pid_t child_pid = -1; static bool null_run = false; static int detailed_run = 0; static bool sync_run = false; static bool big_num = true; static int big_num_opt = -1; -static const char *cpu_list; static const char *csv_sep = NULL; static bool csv_output = false; static bool group = false; @@ -283,6 +282,8 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, { struct perf_event_attr *attr = &evsel->attr; struct xyarray *group_fd = NULL; + bool exclude_guest_missing = false; + int ret; if (group && evsel != first) group_fd = first->fd; @@ -293,16 +294,39 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, attr->inherit = !no_inherit; - if (system_wide) - return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, - group, group_fd); - if (!target_pid && !target_tid && (!group || evsel == first)) { +retry: + if (exclude_guest_missing) + evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; + + if (perf_target__has_cpu(&target)) { + ret = perf_evsel__open_per_cpu(evsel, evsel_list->cpus, + group, group_fd); + if (ret) + goto check_ret; + return 0; + } + + if (!perf_target__has_task(&target) && (!group || evsel == first)) { attr->disabled = 1; attr->enable_on_exec = 1; } - return perf_evsel__open_per_thread(evsel, evsel_list->threads, - group, group_fd); + ret = perf_evsel__open_per_thread(evsel, evsel_list->threads, + group, group_fd); + if (!ret) + return 0; + /* fall through */ +check_ret: + if (ret && errno == EINVAL) { + if (!exclude_guest_missing && + (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { + pr_debug("Old kernel, cannot exclude " + "guest or host samples.\n"); + exclude_guest_missing = true; + goto retry; + } + } + return ret; } /* @@ -446,7 +470,7 @@ static int run_perf_stat(int argc __used, const char **argv) exit(-1); } - if (!target_tid && !target_pid && !system_wide) + if (perf_target__none(&target)) evsel_list->threads->map[0] = child_pid; /* @@ -463,8 +487,13 @@ static int run_perf_stat(int argc __used, const char **argv) list_for_each_entry(counter, &evsel_list->entries, node) { if (create_perf_stat_counter(counter, first) < 0) { + /* + * PPC returns ENXIO for HW counters until 2.6.37 + * (behavior changed with commit b0a873e). + */ if (errno == EINVAL || errno == ENOSYS || - errno == ENOENT || errno == EOPNOTSUPP) { + errno == ENOENT || errno == EOPNOTSUPP || + errno == ENXIO) { if (verbose) ui__warning("%s event is not supported by the kernel.\n", event_name(counter)); @@ -476,7 +505,7 @@ static int run_perf_stat(int argc __used, const char **argv) error("You may not have permission to collect %sstats.\n" "\t Consider tweaking" " /proc/sys/kernel/perf_event_paranoid or running as root.", - system_wide ? "system-wide " : ""); + target.system_wide ? "system-wide " : ""); } else { error("open_counter returned with %d (%s). " "/bin/dmesg may provide additional information.\n", @@ -968,14 +997,14 @@ static void print_stat(int argc, const char **argv) if (!csv_output) { fprintf(output, "\n"); fprintf(output, " Performance counter stats for "); - if (!target_pid && !target_tid) { + if (!perf_target__has_task(&target)) { fprintf(output, "\'%s", argv[0]); for (i = 1; i < argc; i++) fprintf(output, " %s", argv[i]); - } else if (target_pid) - fprintf(output, "process id \'%s", target_pid); + } else if (target.pid) + fprintf(output, "process id \'%s", target.pid); else - fprintf(output, "thread id \'%s", target_tid); + fprintf(output, "thread id \'%s", target.tid); fprintf(output, "\'"); if (run_count > 1) @@ -1049,11 +1078,11 @@ static const struct option options[] = { "event filter", parse_filter), OPT_BOOLEAN('i', "no-inherit", &no_inherit, "child tasks do not inherit counters"), - OPT_STRING('p', "pid", &target_pid, "pid", + OPT_STRING('p', "pid", &target.pid, "pid", "stat events on existing process id"), - OPT_STRING('t', "tid", &target_tid, "tid", + OPT_STRING('t', "tid", &target.tid, "tid", "stat events on existing thread id"), - OPT_BOOLEAN('a', "all-cpus", &system_wide, + OPT_BOOLEAN('a', "all-cpus", &target.system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('g', "group", &group, "put the counters into a counter group"), @@ -1072,7 +1101,7 @@ static const struct option options[] = { OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, "print large numbers with thousands\' separators", stat__set_big_num), - OPT_STRING('C', "cpu", &cpu_list, "cpu", + OPT_STRING('C', "cpu", &target.cpu_list, "cpu", "list of cpus to monitor in system-wide"), OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"), @@ -1190,13 +1219,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) } else if (big_num_opt == 0) /* User passed --no-big-num */ big_num = false; - if (!argc && !target_pid && !target_tid) + if (!argc && !perf_target__has_task(&target)) usage_with_options(stat_usage, options); if (run_count <= 0) usage_with_options(stat_usage, options); /* no_aggr, cgroup are for system-wide only */ - if ((no_aggr || nr_cgroups) && !system_wide) { + if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) { fprintf(stderr, "both cgroup and no-aggregation " "modes only available in system-wide mode\n"); @@ -1206,23 +1235,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) if (add_default_attributes()) goto out; - if (target_pid) - target_tid = target_pid; + perf_target__validate(&target); - evsel_list->threads = thread_map__new_str(target_pid, - target_tid, UINT_MAX); - if (evsel_list->threads == NULL) { - pr_err("Problems finding threads of monitor\n"); - usage_with_options(stat_usage, options); - } - - if (system_wide) - evsel_list->cpus = cpu_map__new(cpu_list); - else - evsel_list->cpus = cpu_map__dummy_new(); + if (perf_evlist__create_maps(evsel_list, &target) < 0) { + if (perf_target__has_task(&target)) + pr_err("Problems finding threads of monitor\n"); + if (perf_target__has_cpu(&target)) + perror("failed to parse CPUs map"); - if (evsel_list->cpus == NULL) { - perror("failed to parse CPUs map"); usage_with_options(stat_usage, options); return -1; } diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 1c5b9801ac61..6c47376e29d8 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -851,6 +851,28 @@ static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) return test__checkevent_symbolic_name(evlist); } +static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); + + return test__checkevent_symbolic_name(evlist); +} + +static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); + + return test__checkevent_symbolic_name(evlist); +} + static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) { struct perf_evsel *evsel = list_entry(evlist->entries.next, @@ -1091,6 +1113,14 @@ static struct test__event_st { .name = "r1,syscalls:sys_enter_open:k,1:1:hp", .check = test__checkevent_list, }, + { + .name = "instructions:G", + .check = test__checkevent_exclude_host_modifier, + }, + { + .name = "instructions:H", + .check = test__checkevent_exclude_guest_modifier, + }, }; #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) @@ -1165,6 +1195,10 @@ realloc: static int test__PERF_RECORD(void) { struct perf_record_opts opts = { + .target = { + .uid = UINT_MAX, + .uses_mmap = true, + }, .no_delay = true, .freq = 10, .mmap_pages = 256, @@ -1207,8 +1241,7 @@ static int test__PERF_RECORD(void) * perf_evlist__prepare_workload we'll fill in the only thread * we're monitoring, the one forked there. */ - err = perf_evlist__create_maps(evlist, opts.target_pid, - opts.target_tid, UINT_MAX, opts.cpu_list); + err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); goto out_delete_evlist; @@ -1549,8 +1582,6 @@ static int __test__rdpmc(void) sa.sa_sigaction = segfault_handler; sigaction(SIGSEGV, &sa, NULL); - fprintf(stderr, "\n\n"); - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); if (fd < 0) { die("Error: sys_perf_event_open() syscall returned " @@ -1575,7 +1606,7 @@ static int __test__rdpmc(void) loops *= 10; delta = now - stamp; - fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta); + pr_debug("%14d: %14Lu\n", n, (long long)delta); delta_sum += delta; } @@ -1583,7 +1614,7 @@ static int __test__rdpmc(void) munmap(addr, page_size); close(fd); - fprintf(stderr, " "); + pr_debug(" "); if (!delta_sum) return -1; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 8ef59f8262bb..3e981a710c4d 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -588,7 +588,7 @@ static void *display_thread_tui(void *arg) * via --uid. */ list_for_each_entry(pos, &top->evlist->entries, node) - pos->hists.uid_filter_str = top->uid_str; + pos->hists.uid_filter_str = top->target.uid_str; perf_evlist__tui_browse_hists(top->evlist, help, perf_top__sort_new_samples, @@ -948,6 +948,10 @@ try_again: attr->type = PERF_TYPE_SOFTWARE; attr->config = PERF_COUNT_SW_CPU_CLOCK; + if (counter->name) { + free(counter->name); + counter->name = strdup(event_name(counter)); + } goto try_again; } @@ -1016,7 +1020,7 @@ static int __cmd_top(struct perf_top *top) if (ret) goto out_delete; - if (top->target_tid || top->uid != UINT_MAX) + if (perf_target__has_task(&top->target)) perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, perf_event__process, &top->session->host_machine); @@ -1150,14 +1154,17 @@ static const char * const top_usage[] = { int cmd_top(int argc, const char **argv, const char *prefix __used) { struct perf_evsel *pos; - int status = -ENOMEM; + int status; + char errbuf[BUFSIZ]; struct perf_top top = { .count_filter = 5, .delay_secs = 2, - .uid = UINT_MAX, .freq = 1000, /* 1 KHz */ .mmap_pages = 128, .sym_pcnt_filter = 5, + .target = { + .uses_mmap = true, + }, }; char callchain_default_opt[] = "fractal,0.5,callee"; const struct option options[] = { @@ -1166,13 +1173,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) parse_events_option), OPT_INTEGER('c', "count", &top.default_interval, "event period to sample"), - OPT_STRING('p', "pid", &top.target_pid, "pid", + OPT_STRING('p', "pid", &top.target.pid, "pid", "profile events on existing process id"), - OPT_STRING('t', "tid", &top.target_tid, "tid", + OPT_STRING('t', "tid", &top.target.tid, "tid", "profile events on existing thread id"), - OPT_BOOLEAN('a', "all-cpus", &top.system_wide, + OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide, "system-wide collection from all CPUs"), - OPT_STRING('C', "cpu", &top.cpu_list, "cpu", + OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), @@ -1227,7 +1234,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) "Display raw encoding of assembly instructions (default)"), OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", "Specify disassembler style (e.g. -M intel for intel syntax)"), - OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"), + OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"), OPT_END() }; @@ -1253,22 +1260,27 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) setup_browser(false); - top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid); - if (top.uid_str != NULL && top.uid == UINT_MAX - 1) - goto out_delete_evlist; + status = perf_target__validate(&top.target); + if (status) { + perf_target__strerror(&top.target, status, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + } - /* CPU and PID are mutually exclusive */ - if (top.target_tid && top.cpu_list) { - printf("WARNING: PID switch overriding CPU\n"); - sleep(1); - top.cpu_list = NULL; + status = perf_target__parse_uid(&top.target); + if (status) { + int saved_errno = errno; + + perf_target__strerror(&top.target, status, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + + status = -saved_errno; + goto out_delete_evlist; } - if (top.target_pid) - top.target_tid = top.target_pid; + if (perf_target__none(&top.target)) + top.target.system_wide = true; - if (perf_evlist__create_maps(top.evlist, top.target_pid, - top.target_tid, top.uid, top.cpu_list) < 0) + if (perf_evlist__create_maps(top.evlist, &top.target) < 0) usage_with_options(top_usage, options); if (!top.evlist->nr_entries && diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 89e3355ab173..14f1034f14f9 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -207,10 +207,10 @@ extern const char perf_version_string[]; void pthread__unblock_sigwinch(void); +#include "util/target.h" + struct perf_record_opts { - const char *target_pid; - const char *target_tid; - uid_t uid; + struct perf_target target; bool call_graph; bool group; bool inherit_stat; @@ -223,7 +223,6 @@ struct perf_record_opts { bool sample_time; bool sample_id_all_missing; bool exclude_guest_missing; - bool system_wide; bool period; unsigned int freq; unsigned int mmap_pages; @@ -231,7 +230,6 @@ struct perf_record_opts { int branch_stack; u64 default_interval; u64 user_interval; - const char *cpu_list; }; #endif diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index a1b140cf75ac..cde4d0f0ddb9 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -593,6 +593,111 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser) return row; } +void __ui_browser__vline(struct ui_browser *browser, unsigned int column, + u16 start, u16 end) +{ + SLsmg_set_char_set(1); + ui_browser__gotorc(browser, start, column); + SLsmg_draw_vline(end - start + 1); + SLsmg_set_char_set(0); +} + +void ui_browser__write_graph(struct ui_browser *browser __used, int graph) +{ + SLsmg_set_char_set(1); + SLsmg_write_char(graph); + SLsmg_set_char_set(0); +} + +static void __ui_browser__line_arrow_up(struct ui_browser *browser, + unsigned int column, + u64 start, u64 end) +{ + unsigned int row, end_row; + + SLsmg_set_char_set(1); + + if (start < browser->top_idx + browser->height) { + row = start - browser->top_idx; + ui_browser__gotorc(browser, row, column); + SLsmg_write_char(SLSMG_LLCORN_CHAR); + ui_browser__gotorc(browser, row, column + 1); + SLsmg_draw_hline(2); + + if (row-- == 0) + goto out; + } else + row = browser->height - 1; + + if (end > browser->top_idx) + end_row = end - browser->top_idx; + else + end_row = 0; + + ui_browser__gotorc(browser, end_row, column); + SLsmg_draw_vline(row - end_row + 1); + + ui_browser__gotorc(browser, end_row, column); + if (end >= browser->top_idx) { + SLsmg_write_char(SLSMG_ULCORN_CHAR); + ui_browser__gotorc(browser, end_row, column + 1); + SLsmg_write_char(SLSMG_HLINE_CHAR); + ui_browser__gotorc(browser, end_row, column + 2); + SLsmg_write_char(SLSMG_RARROW_CHAR); + } +out: + SLsmg_set_char_set(0); +} + +static void __ui_browser__line_arrow_down(struct ui_browser *browser, + unsigned int column, + u64 start, u64 end) +{ + unsigned int row, end_row; + + SLsmg_set_char_set(1); + + if (start >= browser->top_idx) { + row = start - browser->top_idx; + ui_browser__gotorc(browser, row, column); + SLsmg_write_char(SLSMG_ULCORN_CHAR); + ui_browser__gotorc(browser, row, column + 1); + SLsmg_draw_hline(2); + + if (row++ == 0) + goto out; + } else + row = 0; + + if (end >= browser->top_idx + browser->height) + end_row = browser->height - 1; + else + end_row = end - browser->top_idx;; + + ui_browser__gotorc(browser, row, column); + SLsmg_draw_vline(end_row - row + 1); + + ui_browser__gotorc(browser, end_row, column); + if (end < browser->top_idx + browser->height) { + SLsmg_write_char(SLSMG_LLCORN_CHAR); + ui_browser__gotorc(browser, end_row, column + 1); + SLsmg_write_char(SLSMG_HLINE_CHAR); + ui_browser__gotorc(browser, end_row, column + 2); + SLsmg_write_char(SLSMG_RARROW_CHAR); + } +out: + SLsmg_set_char_set(0); +} + +void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, + u64 start, u64 end) +{ + if (start > end) + __ui_browser__line_arrow_up(browser, column, start, end); + else + __ui_browser__line_arrow_down(browser, column, start, end); +} + void ui_browser__init(void) { int i = 0; diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 2550277db9f9..dd96d8229902 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -37,6 +37,9 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); void ui_browser__reset_index(struct ui_browser *self); void ui_browser__gotorc(struct ui_browser *self, int y, int x); +void ui_browser__write_graph(struct ui_browser *browser, int graph); +void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, + u64 start, u64 end); void __ui_browser__show_title(struct ui_browser *browser, const char *title); void ui_browser__show_title(struct ui_browser *browser, const char *title); int ui_browser__show(struct ui_browser *self, const char *title, @@ -46,6 +49,8 @@ int ui_browser__refresh(struct ui_browser *self); int ui_browser__run(struct ui_browser *browser, int delay_secs); void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); void ui_browser__handle_resize(struct ui_browser *browser); +void __ui_browser__vline(struct ui_browser *browser, unsigned int column, + u16 start, u16 end); int ui_browser__warning(struct ui_browser *browser, int timeout, const char *format, ...); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 4db5186472b5..6e0ef79be169 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -11,114 +11,229 @@ #include <pthread.h> #include <newt.h> +struct browser_disasm_line { + struct rb_node rb_node; + double percent; + u32 idx; + int idx_asm; + int jump_sources; +}; + struct annotate_browser { struct ui_browser b; struct rb_root entries; struct rb_node *curr_hot; - struct objdump_line *selection; + struct disasm_line *selection; + struct disasm_line **offsets; u64 start; int nr_asm_entries; int nr_entries; + int max_jump_sources; + int nr_jumps; bool hide_src_code; bool use_offset; + bool jump_arrows; + bool show_nr_jumps; bool searching_backwards; + u8 addr_width; + u8 jumps_width; + u8 target_width; + u8 min_addr_width; + u8 max_addr_width; char search_bf[128]; }; -struct objdump_line_rb_node { - struct rb_node rb_node; - double percent; - u32 idx; - int idx_asm; -}; - -static inline -struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) +static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl) { - return (struct objdump_line_rb_node *)(self + 1); + return (struct browser_disasm_line *)(dl + 1); } -static bool objdump_line__filter(struct ui_browser *browser, void *entry) +static bool disasm_line__filter(struct ui_browser *browser, void *entry) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); if (ab->hide_src_code) { - struct objdump_line *ol = list_entry(entry, struct objdump_line, node); - return ol->offset == -1; + struct disasm_line *dl = list_entry(entry, struct disasm_line, node); + return dl->offset == -1; } return false; } +static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, + int nr, bool current) +{ + if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) + return HE_COLORSET_SELECTED; + if (nr == browser->max_jump_sources) + return HE_COLORSET_TOP; + if (nr > 1) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, + int nr, bool current) +{ + int color = annotate_browser__jumps_percent_color(browser, nr, current); + return ui_browser__set_color(&browser->b, color); +} + static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { struct annotate_browser *ab = container_of(self, struct annotate_browser, b); - struct objdump_line *ol = list_entry(entry, struct objdump_line, node); + struct disasm_line *dl = list_entry(entry, struct disasm_line, node); + struct browser_disasm_line *bdl = disasm_line__browser(dl); bool current_entry = ui_browser__is_current_entry(self, row); bool change_color = (!ab->hide_src_code && (!current_entry || (self->use_navkeypressed && !self->navkeypressed))); - int width = self->width; + int width = self->width, printed; + char bf[256]; - if (ol->offset != -1) { - struct objdump_line_rb_node *olrb = objdump_line__rb(ol); - ui_browser__set_percent_color(self, olrb->percent, current_entry); - slsmg_printf(" %7.2f ", olrb->percent); + if (dl->offset != -1 && bdl->percent != 0.0) { + ui_browser__set_percent_color(self, bdl->percent, current_entry); + slsmg_printf("%6.2f ", bdl->percent); } else { ui_browser__set_percent_color(self, 0, current_entry); - slsmg_write_nstring(" ", 9); + slsmg_write_nstring(" ", 7); } - SLsmg_write_char(':'); - slsmg_write_nstring(" ", 8); + SLsmg_write_char(' '); /* The scroll bar isn't being used */ if (!self->navkeypressed) width += 1; - if (ol->offset != -1 && change_color) - ui_browser__set_color(self, HE_COLORSET_CODE); - - if (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else if (ol->offset == -1) - slsmg_write_nstring(ol->line, width - 18); - else { - char bf[64]; - u64 addr = ol->offset; - int printed, color = -1; + if (!*dl->line) + slsmg_write_nstring(" ", width - 7); + else if (dl->offset == -1) { + printed = scnprintf(bf, sizeof(bf), "%*s ", + ab->addr_width, " "); + slsmg_write_nstring(bf, printed); + slsmg_write_nstring(dl->line, width - printed - 6); + } else { + u64 addr = dl->offset; + int color = -1; if (!ab->use_offset) addr += ab->start; - printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr); + if (!ab->use_offset) { + printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); + } else { + if (bdl->jump_sources) { + if (ab->show_nr_jumps) { + int prev; + printed = scnprintf(bf, sizeof(bf), "%*d ", + ab->jumps_width, + bdl->jump_sources); + prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources, + current_entry); + slsmg_write_nstring(bf, printed); + ui_browser__set_color(self, prev); + } + + printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", + ab->target_width, addr); + } else { + printed = scnprintf(bf, sizeof(bf), "%*s ", + ab->addr_width, " "); + } + } + if (change_color) color = ui_browser__set_color(self, HE_COLORSET_ADDR); slsmg_write_nstring(bf, printed); if (change_color) ui_browser__set_color(self, color); - slsmg_write_nstring(ol->line, width - 18 - printed); + if (dl->ins && dl->ins->ops->scnprintf) { + if (ins__is_jump(dl->ins)) { + bool fwd = dl->ops.target.offset > (u64)dl->offset; + + ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR : + SLSMG_UARROW_CHAR); + SLsmg_write_char(' '); + } else if (ins__is_call(dl->ins)) { + ui_browser__write_graph(self, SLSMG_RARROW_CHAR); + SLsmg_write_char(' '); + } else { + slsmg_write_nstring(" ", 2); + } + } else { + if (strcmp(dl->name, "retq")) { + slsmg_write_nstring(" ", 2); + } else { + ui_browser__write_graph(self, SLSMG_LARROW_CHAR); + SLsmg_write_char(' '); + } + } + + disasm_line__scnprintf(dl, bf, sizeof(bf), !ab->use_offset); + slsmg_write_nstring(bf, width - 10 - printed); } if (current_entry) - ab->selection = ol; + ab->selection = dl; +} + +static void annotate_browser__draw_current_jump(struct ui_browser *browser) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct disasm_line *cursor = ab->selection, *target; + struct browser_disasm_line *btarget, *bcursor; + unsigned int from, to; + + if (!cursor->ins || !ins__is_jump(cursor->ins) || + !disasm_line__has_offset(cursor)) + return; + + target = ab->offsets[cursor->ops.target.offset]; + if (!target) + return; + + bcursor = disasm_line__browser(cursor); + btarget = disasm_line__browser(target); + + if (ab->hide_src_code) { + from = bcursor->idx_asm; + to = btarget->idx_asm; + } else { + from = (u64)bcursor->idx; + to = (u64)btarget->idx; + } + + ui_browser__set_color(browser, HE_COLORSET_CODE); + __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to); +} + +static unsigned int annotate_browser__refresh(struct ui_browser *browser) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + int ret = ui_browser__list_head_refresh(browser); + + if (ab->jump_arrows) + annotate_browser__draw_current_jump(browser); + + ui_browser__set_color(browser, HE_COLORSET_NORMAL); + __ui_browser__vline(browser, 7, 0, browser->height - 1); + return ret; } -static double objdump_line__calc_percent(struct objdump_line *self, - struct symbol *sym, int evidx) +static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) { double percent = 0.0; - if (self->offset != -1) { + if (dl->offset != -1) { int len = sym->end - sym->start; unsigned int hits = 0; struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = self->offset; - struct objdump_line *next; + s64 offset = dl->offset; + struct disasm_line *next; - next = objdump__get_next_ip_line(¬es->src->source, self); + next = disasm__get_next_ip_line(¬es->src->source, dl); while (offset < (s64)len && (next == NULL || offset < next->offset)) { if (src_line) { @@ -139,27 +254,26 @@ static double objdump_line__calc_percent(struct objdump_line *self, return percent; } -static void objdump__insert_line(struct rb_root *self, - struct objdump_line_rb_node *line) +static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) { - struct rb_node **p = &self->rb_node; + struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; - struct objdump_line_rb_node *l; + struct browser_disasm_line *l; while (*p != NULL) { parent = *p; - l = rb_entry(parent, struct objdump_line_rb_node, rb_node); - if (line->percent < l->percent) + l = rb_entry(parent, struct browser_disasm_line, rb_node); + if (bdl->percent < l->percent) p = &(*p)->rb_left; else p = &(*p)->rb_right; } - rb_link_node(&line->rb_node, parent, p); - rb_insert_color(&line->rb_node, self); + rb_link_node(&bdl->rb_node, parent, p); + rb_insert_color(&bdl->rb_node, root); } static void annotate_browser__set_top(struct annotate_browser *self, - struct objdump_line *pos, u32 idx) + struct disasm_line *pos, u32 idx) { unsigned back; @@ -168,9 +282,9 @@ static void annotate_browser__set_top(struct annotate_browser *self, self->b.top_idx = self->b.index = idx; while (self->b.top_idx != 0 && back != 0) { - pos = list_entry(pos->node.prev, struct objdump_line, node); + pos = list_entry(pos->node.prev, struct disasm_line, node); - if (objdump_line__filter(&self->b, &pos->node)) + if (disasm_line__filter(&self->b, &pos->node)) continue; --self->b.top_idx; @@ -184,12 +298,12 @@ static void annotate_browser__set_top(struct annotate_browser *self, static void annotate_browser__set_rb_top(struct annotate_browser *browser, struct rb_node *nd) { - struct objdump_line_rb_node *rbpos; - struct objdump_line *pos; + struct browser_disasm_line *bpos; + struct disasm_line *pos; - rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); - pos = ((struct objdump_line *)rbpos) - 1; - annotate_browser__set_top(browser, pos, rbpos->idx); + bpos = rb_entry(nd, struct browser_disasm_line, rb_node); + pos = ((struct disasm_line *)bpos) - 1; + annotate_browser__set_top(browser, pos, bpos->idx); browser->curr_hot = nd; } @@ -199,20 +313,20 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos; + struct disasm_line *pos; browser->entries = RB_ROOT; pthread_mutex_lock(¬es->lock); list_for_each_entry(pos, ¬es->src->source, node) { - struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); - rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); - if (rbpos->percent < 0.01) { - RB_CLEAR_NODE(&rbpos->rb_node); + struct browser_disasm_line *bpos = disasm_line__browser(pos); + bpos->percent = disasm_line__calc_percent(pos, sym, evidx); + if (bpos->percent < 0.01) { + RB_CLEAR_NODE(&bpos->rb_node); continue; } - objdump__insert_line(&browser->entries, rbpos); + disasm_rb_tree__insert(&browser->entries, bpos); } pthread_mutex_unlock(¬es->lock); @@ -221,38 +335,38 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, static bool annotate_browser__toggle_source(struct annotate_browser *browser) { - struct objdump_line *ol; - struct objdump_line_rb_node *olrb; + struct disasm_line *dl; + struct browser_disasm_line *bdl; off_t offset = browser->b.index - browser->b.top_idx; browser->b.seek(&browser->b, offset, SEEK_CUR); - ol = list_entry(browser->b.top, struct objdump_line, node); - olrb = objdump_line__rb(ol); + dl = list_entry(browser->b.top, struct disasm_line, node); + bdl = disasm_line__browser(dl); if (browser->hide_src_code) { - if (olrb->idx_asm < offset) - offset = olrb->idx; + if (bdl->idx_asm < offset) + offset = bdl->idx; browser->b.nr_entries = browser->nr_entries; browser->hide_src_code = false; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = olrb->idx - offset; - browser->b.index = olrb->idx; + browser->b.top_idx = bdl->idx - offset; + browser->b.index = bdl->idx; } else { - if (olrb->idx_asm < 0) { + if (bdl->idx_asm < 0) { ui_helpline__puts("Only available for assembly lines."); browser->b.seek(&browser->b, -offset, SEEK_CUR); return false; } - if (olrb->idx_asm < offset) - offset = olrb->idx_asm; + if (bdl->idx_asm < offset) + offset = bdl->idx_asm; browser->b.nr_entries = browser->nr_asm_entries; browser->hide_src_code = true; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = olrb->idx_asm - offset; - browser->b.index = olrb->idx_asm; + browser->b.top_idx = bdl->idx_asm - offset; + browser->b.index = bdl->idx_asm; } return true; @@ -263,23 +377,16 @@ static bool annotate_browser__callq(struct annotate_browser *browser, void *arg, int delay_secs) { struct map_symbol *ms = browser->b.priv; + struct disasm_line *dl = browser->selection; struct symbol *sym = ms->sym; struct annotation *notes; struct symbol *target; - char *s = strstr(browser->selection->line, "callq "); u64 ip; - if (s == NULL) + if (!ins__is_call(dl->ins)) return false; - s = strchr(s, ' '); - if (s++ == NULL) { - ui_helpline__puts("Invallid callq instruction."); - return true; - } - - ip = strtoull(s, NULL, 16); - ip = ms->map->map_ip(ms->map, ip); + ip = ms->map->map_ip(ms->map, dl->ops.target.addr); target = map__find_symbol(ms->map, ip, NULL); if (target == NULL) { ui_helpline__puts("The called function was not found."); @@ -302,20 +409,20 @@ static bool annotate_browser__callq(struct annotate_browser *browser, return true; } -static struct objdump_line * - annotate_browser__find_offset(struct annotate_browser *browser, - s64 offset, s64 *idx) +static +struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, + s64 offset, s64 *idx) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos; + struct disasm_line *pos; *idx = 0; list_for_each_entry(pos, ¬es->src->source, node) { if (pos->offset == offset) return pos; - if (!objdump_line__filter(&browser->b, &pos->node)) + if (!disasm_line__filter(&browser->b, &pos->node)) ++*idx; } @@ -324,51 +431,35 @@ static struct objdump_line * static bool annotate_browser__jump(struct annotate_browser *browser) { - const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL }; - struct objdump_line *line; - s64 idx, offset; - char *s = NULL; - int i = 0; - - while (jumps[i]) { - s = strstr(browser->selection->line, jumps[i++]); - if (s) - break; - } + struct disasm_line *dl = browser->selection; + s64 idx; - if (s == NULL) + if (!ins__is_jump(dl->ins)) return false; - s = strchr(s, '+'); - if (s++ == NULL) { - ui_helpline__puts("Invallid jump instruction."); - return true; - } - - offset = strtoll(s, NULL, 16); - line = annotate_browser__find_offset(browser, offset, &idx); - if (line == NULL) { + dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); + if (dl == NULL) { ui_helpline__puts("Invallid jump offset"); return true; } - annotate_browser__set_top(browser, line, idx); + annotate_browser__set_top(browser, dl, idx); return true; } -static struct objdump_line * - annotate_browser__find_string(struct annotate_browser *browser, - char *s, s64 *idx) +static +struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser, + char *s, s64 *idx) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos = browser->selection; + struct disasm_line *pos = browser->selection; *idx = browser->b.index; list_for_each_entry_continue(pos, ¬es->src->source, node) { - if (objdump_line__filter(&browser->b, &pos->node)) + if (disasm_line__filter(&browser->b, &pos->node)) continue; ++*idx; @@ -382,32 +473,32 @@ static struct objdump_line * static bool __annotate_browser__search(struct annotate_browser *browser) { - struct objdump_line *line; + struct disasm_line *dl; s64 idx; - line = annotate_browser__find_string(browser, browser->search_bf, &idx); - if (line == NULL) { + dl = annotate_browser__find_string(browser, browser->search_bf, &idx); + if (dl == NULL) { ui_helpline__puts("String not found!"); return false; } - annotate_browser__set_top(browser, line, idx); + annotate_browser__set_top(browser, dl, idx); browser->searching_backwards = false; return true; } -static struct objdump_line * - annotate_browser__find_string_reverse(struct annotate_browser *browser, - char *s, s64 *idx) +static +struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, + char *s, s64 *idx) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos = browser->selection; + struct disasm_line *pos = browser->selection; *idx = browser->b.index; list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) { - if (objdump_line__filter(&browser->b, &pos->node)) + if (disasm_line__filter(&browser->b, &pos->node)) continue; --*idx; @@ -421,16 +512,16 @@ static struct objdump_line * static bool __annotate_browser__search_reverse(struct annotate_browser *browser) { - struct objdump_line *line; + struct disasm_line *dl; s64 idx; - line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); - if (line == NULL) { + dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); + if (dl == NULL) { ui_helpline__puts("String not found!"); return false; } - annotate_browser__set_top(browser, line, idx); + annotate_browser__set_top(browser, dl, idx); browser->searching_backwards = true; return true; } @@ -490,10 +581,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, struct rb_node *nd = NULL; struct map_symbol *ms = self->b.priv; struct symbol *sym = ms->sym; - const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, " - "H: Go to hottest line, ->/ENTER: Line action, " - "O: Toggle offset view, " - "S: Toggle source code view"; + const char *help = "Press 'h' for help on key bindings"; int key; if (ui_browser__show(&self->b, sym->name, help) < 0) @@ -546,19 +634,47 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, else nd = self->curr_hot; break; - case 'H': + case K_F1: case 'h': + ui_browser__help_window(&self->b, + "UP/DOWN/PGUP\n" + "PGDN/SPACE Navigate\n" + "q/ESC/CTRL+C Exit\n\n" + "-> Go to target\n" + "<- Exit\n" + "h Cycle thru hottest instructions\n" + "j Toggle showing jump to target arrows\n" + "J Toggle showing number of jump sources on targets\n" + "n Search next string\n" + "o Toggle disassembler output/simplified view\n" + "s Toggle source code view\n" + "/ Search string\n" + "? Search previous string\n"); + continue; + case 'H': nd = self->curr_hot; break; - case 'S': case 's': if (annotate_browser__toggle_source(self)) ui_helpline__puts(help); continue; - case 'O': case 'o': self->use_offset = !self->use_offset; + if (self->use_offset) + self->target_width = self->min_addr_width; + else + self->target_width = self->max_addr_width; +update_addr_width: + self->addr_width = self->target_width; + if (self->show_nr_jumps) + self->addr_width += self->jumps_width + 1; + continue; + case 'j': + self->jump_arrows = !self->jump_arrows; continue; + case 'J': + self->show_nr_jumps = !self->show_nr_jumps; + goto update_addr_width; case '/': if (annotate_browser__search(self, delay_secs)) { show_help: @@ -581,9 +697,15 @@ show_help: ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); else if (self->selection->offset == -1) ui_helpline__puts("Actions are only available for assembly lines."); - else if (!(annotate_browser__jump(self) || - annotate_browser__callq(self, evidx, timer, arg, delay_secs))) - ui_helpline__puts("Actions are only available for the 'callq' and jump instructions."); + else if (!self->selection->ins) { + if (strcmp(self->selection->name, "retq")) + goto show_sup_ins; + goto out; + } else if (!(annotate_browser__jump(self) || + annotate_browser__callq(self, evidx, timer, arg, delay_secs))) { +show_sup_ins: + ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); + } continue; case K_LEFT: case K_ESC: @@ -609,27 +731,76 @@ int hist_entry__tui_annotate(struct hist_entry *he, int evidx, timer, arg, delay_secs); } +static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, + size_t size) +{ + u64 offset; + + for (offset = 0; offset < size; ++offset) { + struct disasm_line *dl = browser->offsets[offset], *dlt; + struct browser_disasm_line *bdlt; + + if (!dl || !dl->ins || !ins__is_jump(dl->ins) || + !disasm_line__has_offset(dl)) + continue; + + if (dl->ops.target.offset >= size) { + ui__error("jump to after symbol!\n" + "size: %zx, jump target: %" PRIx64, + size, dl->ops.target.offset); + continue; + } + + dlt = browser->offsets[dl->ops.target.offset]; + /* + * FIXME: Oops, no jump target? Buggy disassembler? Or do we + * have to adjust to the previous offset? + */ + if (dlt == NULL) + continue; + + bdlt = disasm_line__browser(dlt); + if (++bdlt->jump_sources > browser->max_jump_sources) + browser->max_jump_sources = bdlt->jump_sources; + + ++browser->nr_jumps; + } + +} + +static inline int width_jumps(int n) +{ + if (n >= 100) + return 5; + if (n / 10) + return 2; + return 1; +} + int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, void(*timer)(void *arg), void *arg, int delay_secs) { - struct objdump_line *pos, *n; + struct disasm_line *pos, *n; struct annotation *notes; + const size_t size = symbol__size(sym); struct map_symbol ms = { .map = map, .sym = sym, }; struct annotate_browser browser = { .b = { - .refresh = ui_browser__list_head_refresh, + .refresh = annotate_browser__refresh, .seek = ui_browser__list_head_seek, .write = annotate_browser__write, - .filter = objdump_line__filter, + .filter = disasm_line__filter, .priv = &ms, .use_navkeypressed = true, }, + .use_offset = true, + .jump_arrows = true, }; - int ret; + int ret = -1; if (sym == NULL) return -1; @@ -637,37 +808,60 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, if (map->dso->annotate_warned) return -1; - if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { - ui__error("%s", ui_helpline__last_msg); + browser.offsets = zalloc(size * sizeof(struct disasm_line *)); + if (browser.offsets == NULL) { + ui__error("Not enough memory!"); return -1; } + if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { + ui__error("%s", ui_helpline__last_msg); + goto out_free_offsets; + } + ui_helpline__push("Press <- or ESC to exit"); notes = symbol__annotation(sym); browser.start = map__rip_2objdump(map, sym->start); list_for_each_entry(pos, ¬es->src->source, node) { - struct objdump_line_rb_node *rbpos; + struct browser_disasm_line *bpos; size_t line_len = strlen(pos->line); if (browser.b.width < line_len) browser.b.width = line_len; - rbpos = objdump_line__rb(pos); - rbpos->idx = browser.nr_entries++; - if (pos->offset != -1) - rbpos->idx_asm = browser.nr_asm_entries++; - else - rbpos->idx_asm = -1; + bpos = disasm_line__browser(pos); + bpos->idx = browser.nr_entries++; + if (pos->offset != -1) { + bpos->idx_asm = browser.nr_asm_entries++; + /* + * FIXME: short term bandaid to cope with assembly + * routines that comes with labels in the same column + * as the address in objdump, sigh. + * + * E.g. copy_user_generic_unrolled + */ + if (pos->offset < (s64)size) + browser.offsets[pos->offset] = pos; + } else + bpos->idx_asm = -1; } + annotate_browser__mark_jump_targets(&browser, size); + + browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); + browser.max_addr_width = hex_width(sym->end); + browser.jumps_width = width_jumps(browser.max_jump_sources); browser.b.nr_entries = browser.nr_entries; browser.b.entries = ¬es->src->source, browser.b.width += 18; /* Percentage */ ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); list_for_each_entry_safe(pos, n, ¬es->src->source, node) { list_del(&pos->node); - objdump_line__free(pos); + disasm_line__free(pos); } + +out_free_offsets: + free(browser.offsets); return ret; } diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 466827e91b87..a372a4b02635 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -941,7 +941,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, goto zoom_dso; case 't': goto zoom_thread; - case 's': + case '/': if (ui_browser__input_window("Symbol to show", "Please enter the name of symbol you want to see", buf, "ENTER: OK, ESC: Cancel", @@ -969,7 +969,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "E Expand all callchains\n" "d Zoom into current DSO\n" "t Zoom into current Thread\n" - "s Filter symbol by name"); + "/ Filter symbol by name"); continue; case K_ENTER: case K_RIGHT: diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 258352a2356c..0656c381a89c 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -9,24 +9,13 @@ #define MAX_COLUMNS 32 -void perf_gtk_setup_browser(int argc, const char *argv[], - bool fallback_to_pager __used) -{ - gtk_init(&argc, (char ***)&argv); -} - -void perf_gtk_exit_browser(bool wait_for_ok __used) -{ - gtk_main_quit(); -} - -static void perf_gtk_signal(int sig) +static void perf_gtk__signal(int sig) { psignal(sig, "perf"); gtk_main_quit(); } -static void perf_gtk_resize_window(GtkWidget *window) +static void perf_gtk__resize_window(GtkWidget *window) { GdkRectangle rect; GdkScreen *screen; @@ -46,7 +35,7 @@ static void perf_gtk_resize_window(GtkWidget *window) gtk_window_resize(GTK_WINDOW(window), width, height); } -static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists) +static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) { GType col_types[MAX_COLUMNS]; GtkCellRenderer *renderer; @@ -142,11 +131,11 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, GtkWidget *notebook; GtkWidget *window; - signal(SIGSEGV, perf_gtk_signal); - signal(SIGFPE, perf_gtk_signal); - signal(SIGINT, perf_gtk_signal); - signal(SIGQUIT, perf_gtk_signal); - signal(SIGTERM, perf_gtk_signal); + signal(SIGSEGV, perf_gtk__signal); + signal(SIGFPE, perf_gtk__signal); + signal(SIGINT, perf_gtk__signal); + signal(SIGQUIT, perf_gtk__signal); + signal(SIGTERM, perf_gtk__signal); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -168,7 +157,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - perf_gtk_show_hists(scrolled_window, hists); + perf_gtk__show_hists(scrolled_window, hists); tab_label = gtk_label_new(evname); @@ -179,7 +168,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, gtk_widget_show_all(window); - perf_gtk_resize_window(window); + perf_gtk__resize_window(window); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c new file mode 100644 index 000000000000..829529957766 --- /dev/null +++ b/tools/perf/ui/gtk/setup.c @@ -0,0 +1,12 @@ +#include "gtk.h" +#include "../../util/cache.h" + +int perf_gtk__init(void) +{ + return gtk_init_check(NULL, NULL) ? 0 : -1; +} + +void perf_gtk__exit(bool wait_for_ok __used) +{ + gtk_main_quit(); +} diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index 85a69faa09aa..9f5f888f73e3 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -1,155 +1,45 @@ -#include <newt.h> -#include <signal.h> -#include <stdbool.h> - #include "../cache.h" #include "../debug.h" -#include "browser.h" -#include "helpline.h" -#include "ui.h" -#include "util.h" -#include "libslang.h" -#include "keysyms.h" - -pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; - -static volatile int ui__need_resize; - -void ui__refresh_dimensions(bool force) -{ - if (force || ui__need_resize) { - ui__need_resize = 0; - pthread_mutex_lock(&ui__lock); - SLtt_get_screen_size(); - SLsmg_reinit_smg(); - pthread_mutex_unlock(&ui__lock); - } -} - -static void ui__sigwinch(int sig __used) -{ - ui__need_resize = 1; -} - -static void ui__setup_sigwinch(void) -{ - static bool done; - if (done) - return; - - done = true; - pthread__unblock_sigwinch(); - signal(SIGWINCH, ui__sigwinch); -} - -int ui__getch(int delay_secs) -{ - struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; - fd_set read_set; - int err, key; - - ui__setup_sigwinch(); - - FD_ZERO(&read_set); - FD_SET(0, &read_set); - - if (delay_secs) { - timeout.tv_sec = delay_secs; - timeout.tv_usec = 0; - } - - err = select(1, &read_set, NULL, NULL, ptimeout); - - if (err == 0) - return K_TIMER; - - if (err == -1) { - if (errno == EINTR) - return K_RESIZE; - return K_ERROR; - } - - key = SLang_getkey(); - if (key != K_ESC) - return key; - - FD_ZERO(&read_set); - FD_SET(0, &read_set); - timeout.tv_sec = 0; - timeout.tv_usec = 20; - err = select(1, &read_set, NULL, NULL, &timeout); - if (err == 0) - return K_ESC; - - SLang_ungetkey(key); - return SLkp_getkey(); -} - -static void newt_suspend(void *d __used) -{ - newtSuspend(); - raise(SIGTSTP); - newtResume(); -} - -static int ui__init(void) -{ - int err = SLkp_init(); - - if (err < 0) - goto out; - - SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); -out: - return err; -} - -static void ui__exit(void) -{ - SLtt_set_cursor_visibility(1); - SLsmg_refresh(); - SLsmg_reset_smg(); - SLang_reset_tty(); -} - -static void ui__signal(int sig) -{ - ui__exit(); - psignal(sig, "perf"); - exit(0); -} void setup_browser(bool fallback_to_pager) { - if (!isatty(1) || !use_browser || dump_trace) { + if (!isatty(1) || dump_trace) use_browser = 0; + + /* default to TUI */ + if (use_browser < 0) + use_browser = 1; + + switch (use_browser) { + case 2: + if (perf_gtk__init() == 0) + break; + /* fall through */ + case 1: + use_browser = 1; + if (ui__init() == 0) + break; + /* fall through */ + default: if (fallback_to_pager) setup_pager(); - return; + break; } - - use_browser = 1; - newtInit(); - ui__init(); - newtSetSuspendCallback(newt_suspend, NULL); - ui_helpline__init(); - ui_browser__init(); - - signal(SIGSEGV, ui__signal); - signal(SIGFPE, ui__signal); - signal(SIGINT, ui__signal); - signal(SIGQUIT, ui__signal); - signal(SIGTERM, ui__signal); } void exit_browser(bool wait_for_ok) { - if (use_browser > 0) { - if (wait_for_ok) - ui__question_window("Fatal Error", - ui_helpline__last_msg, - "Press any key...", 0); - ui__exit(); + switch (use_browser) { + case 2: + perf_gtk__exit(wait_for_ok); + break; + + case 1: + ui__exit(wait_for_ok); + break; + + default: + break; } } diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c new file mode 100644 index 000000000000..d33e943ac434 --- /dev/null +++ b/tools/perf/ui/tui/setup.c @@ -0,0 +1,140 @@ +#include <newt.h> +#include <signal.h> +#include <stdbool.h> + +#include "../../util/cache.h" +#include "../../util/debug.h" +#include "../browser.h" +#include "../helpline.h" +#include "../ui.h" +#include "../util.h" +#include "../libslang.h" +#include "../keysyms.h" + +pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; + +static volatile int ui__need_resize; + +void ui__refresh_dimensions(bool force) +{ + if (force || ui__need_resize) { + ui__need_resize = 0; + pthread_mutex_lock(&ui__lock); + SLtt_get_screen_size(); + SLsmg_reinit_smg(); + pthread_mutex_unlock(&ui__lock); + } +} + +static void ui__sigwinch(int sig __used) +{ + ui__need_resize = 1; +} + +static void ui__setup_sigwinch(void) +{ + static bool done; + + if (done) + return; + + done = true; + pthread__unblock_sigwinch(); + signal(SIGWINCH, ui__sigwinch); +} + +int ui__getch(int delay_secs) +{ + struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; + fd_set read_set; + int err, key; + + ui__setup_sigwinch(); + + FD_ZERO(&read_set); + FD_SET(0, &read_set); + + if (delay_secs) { + timeout.tv_sec = delay_secs; + timeout.tv_usec = 0; + } + + err = select(1, &read_set, NULL, NULL, ptimeout); + + if (err == 0) + return K_TIMER; + + if (err == -1) { + if (errno == EINTR) + return K_RESIZE; + return K_ERROR; + } + + key = SLang_getkey(); + if (key != K_ESC) + return key; + + FD_ZERO(&read_set); + FD_SET(0, &read_set); + timeout.tv_sec = 0; + timeout.tv_usec = 20; + err = select(1, &read_set, NULL, NULL, &timeout); + if (err == 0) + return K_ESC; + + SLang_ungetkey(key); + return SLkp_getkey(); +} + +static void newt_suspend(void *d __used) +{ + newtSuspend(); + raise(SIGTSTP); + newtResume(); +} + +static void ui__signal(int sig) +{ + ui__exit(false); + psignal(sig, "perf"); + exit(0); +} + +int ui__init(void) +{ + int err; + + newtInit(); + err = SLkp_init(); + if (err < 0) { + pr_err("TUI initialization failed.\n"); + goto out; + } + + SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); + + newtSetSuspendCallback(newt_suspend, NULL); + ui_helpline__init(); + ui_browser__init(); + + signal(SIGSEGV, ui__signal); + signal(SIGFPE, ui__signal); + signal(SIGINT, ui__signal); + signal(SIGQUIT, ui__signal); + signal(SIGTERM, ui__signal); +out: + return err; +} + +void ui__exit(bool wait_for_ok) +{ + if (wait_for_ok) + ui__question_window("Fatal Error", + ui_helpline__last_msg, + "Press any key...", 0); + + SLtt_set_cursor_visibility(1); + SLsmg_refresh(); + SLsmg_reset_smg(); + SLang_reset_tty(); +} diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 1e7fd52bd29d..8069dfb5ba77 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -18,6 +18,403 @@ const char *disassembler_style; +static struct ins *ins__find(const char *name); +static int disasm_line__parse(char *line, char **namep, char **rawp); + +static void ins__delete(struct ins_operands *ops) +{ + free(ops->source.raw); + free(ops->source.name); + free(ops->target.raw); + free(ops->target.name); +} + +static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw); +} + +int ins__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + if (ins->ops->scnprintf) + return ins->ops->scnprintf(ins, bf, size, ops); + + return ins__raw_scnprintf(ins, bf, size, ops); +} + +static int call__parse(struct ins_operands *ops) +{ + char *endptr, *tok, *name; + + ops->target.addr = strtoull(ops->raw, &endptr, 16); + + name = strchr(endptr, '<'); + if (name == NULL) + goto indirect_call; + + name++; + + tok = strchr(name, '>'); + if (tok == NULL) + return -1; + + *tok = '\0'; + ops->target.name = strdup(name); + *tok = '>'; + + return ops->target.name == NULL ? -1 : 0; + +indirect_call: + tok = strchr(endptr, '('); + if (tok != NULL) { + ops->target.addr = 0; + return 0; + } + + tok = strchr(endptr, '*'); + if (tok == NULL) + return -1; + + ops->target.addr = strtoull(tok + 1, NULL, 16); + return 0; +} + +static int call__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + if (ops->target.name) + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); + + if (ops->target.addr == 0) + return ins__raw_scnprintf(ins, bf, size, ops); + + return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); +} + +static struct ins_ops call_ops = { + .parse = call__parse, + .scnprintf = call__scnprintf, +}; + +bool ins__is_call(const struct ins *ins) +{ + return ins->ops == &call_ops; +} + +static int jump__parse(struct ins_operands *ops) +{ + const char *s = strchr(ops->raw, '+'); + + ops->target.addr = strtoll(ops->raw, NULL, 16); + + if (s++ != NULL) + ops->target.offset = strtoll(s, NULL, 16); + else + ops->target.offset = UINT64_MAX; + + return 0; +} + +static int jump__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); +} + +static struct ins_ops jump_ops = { + .parse = jump__parse, + .scnprintf = jump__scnprintf, +}; + +bool ins__is_jump(const struct ins *ins) +{ + return ins->ops == &jump_ops; +} + +static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) +{ + char *endptr, *name, *t; + + if (strstr(raw, "(%rip)") == NULL) + return 0; + + *addrp = strtoull(comment, &endptr, 16); + name = strchr(endptr, '<'); + if (name == NULL) + return -1; + + name++; + + t = strchr(name, '>'); + if (t == NULL) + return 0; + + *t = '\0'; + *namep = strdup(name); + *t = '>'; + + return 0; +} + +static int lock__parse(struct ins_operands *ops) +{ + char *name; + + ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); + if (ops->locked.ops == NULL) + return 0; + + if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) + goto out_free_ops; + + ops->locked.ins = ins__find(name); + if (ops->locked.ins == NULL) + goto out_free_ops; + + if (!ops->locked.ins->ops) + return 0; + + if (ops->locked.ins->ops->parse) + ops->locked.ins->ops->parse(ops->locked.ops); + + return 0; + +out_free_ops: + free(ops->locked.ops); + ops->locked.ops = NULL; + return 0; +} + +static int lock__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + int printed; + + if (ops->locked.ins == NULL) + return ins__raw_scnprintf(ins, bf, size, ops); + + printed = scnprintf(bf, size, "%-6.6s ", ins->name); + return printed + ins__scnprintf(ops->locked.ins, bf + printed, + size - printed, ops->locked.ops); +} + +static void lock__delete(struct ins_operands *ops) +{ + free(ops->locked.ops); + free(ops->target.raw); + free(ops->target.name); +} + +static struct ins_ops lock_ops = { + .free = lock__delete, + .parse = lock__parse, + .scnprintf = lock__scnprintf, +}; + +static int mov__parse(struct ins_operands *ops) +{ + char *s = strchr(ops->raw, ','), *target, *comment, prev; + + if (s == NULL) + return -1; + + *s = '\0'; + ops->source.raw = strdup(ops->raw); + *s = ','; + + if (ops->source.raw == NULL) + return -1; + + target = ++s; + + while (s[0] != '\0' && !isspace(s[0])) + ++s; + prev = *s; + *s = '\0'; + + ops->target.raw = strdup(target); + *s = prev; + + if (ops->target.raw == NULL) + goto out_free_source; + + comment = strchr(s, '#'); + if (comment == NULL) + return 0; + + while (comment[0] != '\0' && isspace(comment[0])) + ++comment; + + comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name); + comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); + + return 0; + +out_free_source: + free(ops->source.raw); + ops->source.raw = NULL; + return -1; +} + +static int mov__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %s,%s", ins->name, + ops->source.name ?: ops->source.raw, + ops->target.name ?: ops->target.raw); +} + +static struct ins_ops mov_ops = { + .parse = mov__parse, + .scnprintf = mov__scnprintf, +}; + +static int dec__parse(struct ins_operands *ops) +{ + char *target, *comment, *s, prev; + + target = s = ops->raw; + + while (s[0] != '\0' && !isspace(s[0])) + ++s; + prev = *s; + *s = '\0'; + + ops->target.raw = strdup(target); + *s = prev; + + if (ops->target.raw == NULL) + return -1; + + comment = strchr(s, '#'); + if (comment == NULL) + return 0; + + while (comment[0] != '\0' && isspace(comment[0])) + ++comment; + + comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); + + return 0; +} + +static int dec__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %s", ins->name, + ops->target.name ?: ops->target.raw); +} + +static struct ins_ops dec_ops = { + .parse = dec__parse, + .scnprintf = dec__scnprintf, +}; + +static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size, + struct ins_operands *ops __used) +{ + return scnprintf(bf, size, "%-6.6s", "nop"); +} + +static struct ins_ops nop_ops = { + .scnprintf = nop__scnprintf, +}; + +/* + * Must be sorted by name! + */ +static struct ins instructions[] = { + { .name = "add", .ops = &mov_ops, }, + { .name = "addl", .ops = &mov_ops, }, + { .name = "addq", .ops = &mov_ops, }, + { .name = "addw", .ops = &mov_ops, }, + { .name = "and", .ops = &mov_ops, }, + { .name = "bts", .ops = &mov_ops, }, + { .name = "call", .ops = &call_ops, }, + { .name = "callq", .ops = &call_ops, }, + { .name = "cmp", .ops = &mov_ops, }, + { .name = "cmpb", .ops = &mov_ops, }, + { .name = "cmpl", .ops = &mov_ops, }, + { .name = "cmpq", .ops = &mov_ops, }, + { .name = "cmpw", .ops = &mov_ops, }, + { .name = "cmpxch", .ops = &mov_ops, }, + { .name = "dec", .ops = &dec_ops, }, + { .name = "decl", .ops = &dec_ops, }, + { .name = "imul", .ops = &mov_ops, }, + { .name = "inc", .ops = &dec_ops, }, + { .name = "incl", .ops = &dec_ops, }, + { .name = "ja", .ops = &jump_ops, }, + { .name = "jae", .ops = &jump_ops, }, + { .name = "jb", .ops = &jump_ops, }, + { .name = "jbe", .ops = &jump_ops, }, + { .name = "jc", .ops = &jump_ops, }, + { .name = "jcxz", .ops = &jump_ops, }, + { .name = "je", .ops = &jump_ops, }, + { .name = "jecxz", .ops = &jump_ops, }, + { .name = "jg", .ops = &jump_ops, }, + { .name = "jge", .ops = &jump_ops, }, + { .name = "jl", .ops = &jump_ops, }, + { .name = "jle", .ops = &jump_ops, }, + { .name = "jmp", .ops = &jump_ops, }, + { .name = "jmpq", .ops = &jump_ops, }, + { .name = "jna", .ops = &jump_ops, }, + { .name = "jnae", .ops = &jump_ops, }, + { .name = "jnb", .ops = &jump_ops, }, + { .name = "jnbe", .ops = &jump_ops, }, + { .name = "jnc", .ops = &jump_ops, }, + { .name = "jne", .ops = &jump_ops, }, + { .name = "jng", .ops = &jump_ops, }, + { .name = "jnge", .ops = &jump_ops, }, + { .name = "jnl", .ops = &jump_ops, }, + { .name = "jnle", .ops = &jump_ops, }, + { .name = "jno", .ops = &jump_ops, }, + { .name = "jnp", .ops = &jump_ops, }, + { .name = "jns", .ops = &jump_ops, }, + { .name = "jnz", .ops = &jump_ops, }, + { .name = "jo", .ops = &jump_ops, }, + { .name = "jp", .ops = &jump_ops, }, + { .name = "jpe", .ops = &jump_ops, }, + { .name = "jpo", .ops = &jump_ops, }, + { .name = "jrcxz", .ops = &jump_ops, }, + { .name = "js", .ops = &jump_ops, }, + { .name = "jz", .ops = &jump_ops, }, + { .name = "lea", .ops = &mov_ops, }, + { .name = "lock", .ops = &lock_ops, }, + { .name = "mov", .ops = &mov_ops, }, + { .name = "movb", .ops = &mov_ops, }, + { .name = "movdqa",.ops = &mov_ops, }, + { .name = "movl", .ops = &mov_ops, }, + { .name = "movq", .ops = &mov_ops, }, + { .name = "movslq", .ops = &mov_ops, }, + { .name = "movzbl", .ops = &mov_ops, }, + { .name = "movzwl", .ops = &mov_ops, }, + { .name = "nop", .ops = &nop_ops, }, + { .name = "nopl", .ops = &nop_ops, }, + { .name = "nopw", .ops = &nop_ops, }, + { .name = "or", .ops = &mov_ops, }, + { .name = "orl", .ops = &mov_ops, }, + { .name = "test", .ops = &mov_ops, }, + { .name = "testb", .ops = &mov_ops, }, + { .name = "testl", .ops = &mov_ops, }, + { .name = "xadd", .ops = &mov_ops, }, +}; + +static int ins__cmp(const void *name, const void *insp) +{ + const struct ins *ins = insp; + + return strcmp(name, ins->name); +} + +static struct ins *ins__find(const char *name) +{ + const int nmemb = ARRAY_SIZE(instructions); + + return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); +} + int symbol__annotate_init(struct map *map __used, struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); @@ -28,7 +425,7 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym) int symbol__alloc_hist(struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); - const size_t size = sym->end - sym->start + 1; + const size_t size = symbol__size(sym); size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); @@ -78,36 +475,110 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, return 0; } -static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) +static void disasm_line__init_ins(struct disasm_line *dl) { - struct objdump_line *self = malloc(sizeof(*self) + privsize); + dl->ins = ins__find(dl->name); - if (self != NULL) { - self->offset = offset; - self->line = strdup(line); - if (self->line == NULL) + if (dl->ins == NULL) + return; + + if (!dl->ins->ops) + return; + + if (dl->ins->ops->parse) + dl->ins->ops->parse(&dl->ops); +} + +static int disasm_line__parse(char *line, char **namep, char **rawp) +{ + char *name = line, tmp; + + while (isspace(name[0])) + ++name; + + if (name[0] == '\0') + return -1; + + *rawp = name + 1; + + while ((*rawp)[0] != '\0' && !isspace((*rawp)[0])) + ++*rawp; + + tmp = (*rawp)[0]; + (*rawp)[0] = '\0'; + *namep = strdup(name); + + if (*namep == NULL) + goto out_free_name; + + (*rawp)[0] = tmp; + + if ((*rawp)[0] != '\0') { + (*rawp)++; + while (isspace((*rawp)[0])) + ++(*rawp); + } + + return 0; + +out_free_name: + free(*namep); + *namep = NULL; + return -1; +} + +static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) +{ + struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); + + if (dl != NULL) { + dl->offset = offset; + dl->line = strdup(line); + if (dl->line == NULL) goto out_delete; + + if (offset != -1) { + if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) + goto out_free_line; + + disasm_line__init_ins(dl); + } } - return self; + return dl; + +out_free_line: + free(dl->line); out_delete: - free(self); + free(dl); return NULL; } -void objdump_line__free(struct objdump_line *self) +void disasm_line__free(struct disasm_line *dl) +{ + free(dl->line); + free(dl->name); + if (dl->ins && dl->ins->ops->free) + dl->ins->ops->free(&dl->ops); + else + ins__delete(&dl->ops); + free(dl); +} + +int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) { - free(self->line); - free(self); + if (raw || !dl->ins) + return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw); + + return ins__scnprintf(dl->ins, bf, size, &dl->ops); } -static void objdump__add_line(struct list_head *head, struct objdump_line *line) +static void disasm__add(struct list_head *head, struct disasm_line *line) { list_add_tail(&line->node, head); } -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) +struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) @@ -116,15 +587,14 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head, return NULL; } -static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, - u64 start, int evidx, u64 len, int min_pcnt, - int printed, int max_lines, - struct objdump_line *queue) +static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, + int evidx, u64 len, int min_pcnt, int printed, + int max_lines, struct disasm_line *queue) { static const char *prev_line; static const char *prev_color; - if (oline->offset != -1) { + if (dl->offset != -1) { const char *path = NULL; unsigned int hits = 0; double percent = 0.0; @@ -132,11 +602,11 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = oline->offset; + s64 offset = dl->offset; const u64 addr = start + offset; - struct objdump_line *next; + struct disasm_line *next; - next = objdump__get_next_ip_line(¬es->src->source, oline); + next = disasm__get_next_ip_line(¬es->src->source, dl); while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -161,9 +631,9 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, if (queue != NULL) { list_for_each_entry_from(queue, ¬es->src->source, node) { - if (queue == oline) + if (queue == dl) break; - objdump_line__print(queue, sym, start, evidx, len, + disasm_line__print(queue, sym, start, evidx, len, 0, 0, 1, NULL); } } @@ -187,17 +657,17 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, color_fprintf(stdout, color, " %7.2f", percent); printf(" : "); color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); - color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); + color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); } else if (max_lines && printed >= max_lines) return 1; else { if (queue) return -1; - if (!*oline->line) + if (!*dl->line) printf(" :\n"); else - printf(" : %s\n", oline->line); + printf(" : %s\n", dl->line); } return 0; @@ -207,7 +677,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, size_t privsize) { struct annotation *notes = symbol__annotation(sym); - struct objdump_line *objdump_line; + struct disasm_line *dl; char *line = NULL, *parsed_line, *tmp, *tmp2, *c; size_t line_len; s64 line_ip, offset = -1; @@ -258,13 +728,13 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, parsed_line = tmp2 + 1; } - objdump_line = objdump_line__new(offset, parsed_line, privsize); + dl = disasm_line__new(offset, parsed_line, privsize); free(line); - if (objdump_line == NULL) + if (dl == NULL) return -1; - objdump__add_line(¬es->src->source, objdump_line); + disasm__add(¬es->src->source, dl); return 0; } @@ -487,7 +957,7 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - u64 len = sym->end - sym->start, offset; + u64 len = symbol__size(sym), offset; for (offset = 0; offset < len; ++offset) if (h->addr[offset] != 0) @@ -503,7 +973,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, struct dso *dso = map->dso; const char *filename = dso->long_name, *d_filename; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos, *queue = NULL; + struct disasm_line *pos, *queue = NULL; u64 start = map__rip_2objdump(map, sym->start); int printed = 2, queue_len = 0; int more = 0; @@ -514,7 +984,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, else d_filename = basename(filename); - len = sym->end - sym->start; + len = symbol__size(sym); printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); @@ -528,7 +998,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, queue_len = 0; } - switch (objdump_line__print(pos, sym, start, evidx, len, + switch (disasm_line__print(pos, sym, start, evidx, len, min_pcnt, printed, max_lines, queue)) { case 0: @@ -574,7 +1044,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - int len = sym->end - sym->start, offset; + int len = symbol__size(sym), offset; h->sum = 0; for (offset = 0; offset < len; ++offset) { @@ -583,16 +1053,44 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) } } -void objdump_line_list__purge(struct list_head *head) +void disasm__purge(struct list_head *head) { - struct objdump_line *pos, *n; + struct disasm_line *pos, *n; list_for_each_entry_safe(pos, n, head, node) { list_del(&pos->node); - objdump_line__free(pos); + disasm_line__free(pos); } } +static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) +{ + size_t printed; + + if (dl->offset == -1) + return fprintf(fp, "%s\n", dl->line); + + printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); + + if (dl->ops.raw[0] != '\0') { + printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", + dl->ops.raw); + } + + return printed + fprintf(fp, "\n"); +} + +size_t disasm__fprintf(struct list_head *head, FILE *fp) +{ + struct disasm_line *pos; + size_t printed = 0; + + list_for_each_entry(pos, head, node) + printed += disasm_line__fprintf(pos, fp); + + return printed; +} + int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, bool print_lines, bool full_paths, int min_pcnt, int max_lines) @@ -605,7 +1103,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, if (symbol__annotate(sym, map, 0) < 0) return -1; - len = sym->end - sym->start; + len = symbol__size(sym); if (print_lines) { symbol__get_source_line(sym, map, evidx, &source_line, @@ -618,7 +1116,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, if (print_lines) symbol__free_source_line(sym, len); - objdump_line_list__purge(&symbol__annotation(sym)->src->source); + disasm__purge(&symbol__annotation(sym)->src->source); return 0; } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index efa5dc82bfae..78a5692dd718 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -2,20 +2,69 @@ #define __PERF_ANNOTATE_H #include <stdbool.h> +#include <stdint.h> #include "types.h" #include "symbol.h" #include <linux/list.h> #include <linux/rbtree.h> -struct objdump_line { - struct list_head node; - s64 offset; - char *line; +struct ins; + +struct ins_operands { + char *raw; + struct { + char *raw; + char *name; + u64 addr; + u64 offset; + } target; + union { + struct { + char *raw; + char *name; + u64 addr; + } source; + struct { + struct ins *ins; + struct ins_operands *ops; + } locked; + }; }; -void objdump_line__free(struct objdump_line *self); -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos); +struct ins_ops { + void (*free)(struct ins_operands *ops); + int (*parse)(struct ins_operands *ops); + int (*scnprintf)(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops); +}; + +struct ins { + const char *name; + struct ins_ops *ops; +}; + +bool ins__is_jump(const struct ins *ins); +bool ins__is_call(const struct ins *ins); +int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); + +struct disasm_line { + struct list_head node; + s64 offset; + char *line; + char *name; + struct ins *ins; + struct ins_operands ops; +}; + +static inline bool disasm_line__has_offset(const struct disasm_line *dl) +{ + return dl->ops.target.offset != UINT64_MAX; +} + +void disasm_line__free(struct disasm_line *dl); +struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); +int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); +size_t disasm__fprintf(struct list_head *head, FILE *fp); struct sym_hist { u64 sum; @@ -32,7 +81,7 @@ struct source_line { * * @histogram: Array of addr hit histograms per event being monitored * @lines: If 'print_lines' is specified, per source code line percentages - * @source: source parsed from objdump -dS + * @source: source parsed from a disassembler like objdump -dS * * lines is allocated, percentages calculated and all sorted by percentage * when the annotation is about to be presented, so the percentages are for @@ -82,7 +131,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, int context); void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); -void objdump_line_list__purge(struct list_head *head); +void disasm__purge(struct list_head *head); int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, bool print_lines, bool full_paths, int min_pcnt, diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 8dd224df3e54..cff18c617d13 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -33,7 +33,7 @@ extern int pager_use_color; extern int use_browser; -#ifdef NO_NEWT_SUPPORT +#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) static inline void setup_browser(bool fallback_to_pager) { if (fallback_to_pager) @@ -43,19 +43,29 @@ static inline void exit_browser(bool wait_for_ok __used) {} #else void setup_browser(bool fallback_to_pager); void exit_browser(bool wait_for_ok); + +#ifdef NO_NEWT_SUPPORT +static inline int ui__init(void) +{ + return -1; +} +static inline void ui__exit(bool wait_for_ok __used) {} +#else +int ui__init(void); +void ui__exit(bool wait_for_ok); #endif #ifdef NO_GTK2_SUPPORT -static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager) +static inline int perf_gtk__init(void) { - if (fallback_to_pager) - setup_pager(); + return -1; } -static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {} +static inline void perf_gtk__exit(bool wait_for_ok __used) {} #else -void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager); -void perf_gtk_exit_browser(bool wait_for_ok); +int perf_gtk__init(void); +void perf_gtk__exit(bool wait_for_ok); #endif +#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 26817daa2961..efb1fce259a4 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -11,6 +11,7 @@ #include "event.h" #include "debug.h" #include "util.h" +#include "target.h" int verbose; bool dump_trace = false, quiet = false; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 1986d8051bd1..4ac5f5ae4ce9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -11,6 +11,7 @@ #include <poll.h> #include "cpumap.h" #include "thread_map.h" +#include "target.h" #include "evlist.h" #include "evsel.h" #include <unistd.h> @@ -599,18 +600,21 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, return perf_evlist__mmap_per_cpu(evlist, prot, mask); } -int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, - const char *target_tid, uid_t uid, const char *cpu_list) +int perf_evlist__create_maps(struct perf_evlist *evlist, + struct perf_target *target) { - evlist->threads = thread_map__new_str(target_pid, target_tid, uid); + evlist->threads = thread_map__new_str(target->pid, target->tid, + target->uid); if (evlist->threads == NULL) return -1; - if (uid != UINT_MAX || (cpu_list == NULL && target_tid)) + if (perf_target__has_task(target)) + evlist->cpus = cpu_map__dummy_new(); + else if (!perf_target__has_cpu(target) && !target->uses_mmap) evlist->cpus = cpu_map__dummy_new(); else - evlist->cpus = cpu_map__new(cpu_list); + evlist->cpus = cpu_map__new(target->cpu_list); if (evlist->cpus == NULL) goto out_delete_threads; @@ -827,7 +831,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, exit(-1); } - if (!opts->system_wide && !opts->target_tid && !opts->target_pid) + if (perf_target__none(&opts->target)) evlist->threads->map[0] = evlist->workload.pid; close(child_ready_pipe[1]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 21f1c9e57f13..58abb63ac13a 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, evlist->threads = threads; } -int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, - const char *tid, uid_t uid, const char *cpu_list); +int perf_evlist__create_maps(struct perf_evlist *evlist, + struct perf_target *target); void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__set_filters(struct perf_evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8c13dbcb84b9..f4f427ce4d64 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -14,6 +14,7 @@ #include "util.h" #include "cpumap.h" #include "thread_map.h" +#include "target.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) @@ -69,6 +70,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, struct perf_event_attr *attr = &evsel->attr; int track = !evsel->idx; /* only the first counter needs these */ + attr->disabled = 1; attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; attr->inherit = !opts->no_inherit; attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -106,15 +108,15 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, if (opts->call_graph) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; - if (opts->system_wide) + if (opts->target.system_wide) attr->sample_type |= PERF_SAMPLE_CPU; if (opts->period) attr->sample_type |= PERF_SAMPLE_PERIOD; if (!opts->sample_id_all_missing && - (opts->sample_time || opts->system_wide || - !opts->no_inherit || opts->cpu_list)) + (opts->sample_time || !opts->no_inherit || + perf_target__has_cpu(&opts->target))) attr->sample_type |= PERF_SAMPLE_TIME; if (opts->raw_samples) { @@ -135,9 +137,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, attr->mmap = track; attr->comm = track; - if (!opts->target_pid && !opts->target_tid && !opts->system_wide && + if (perf_target__none(&opts->target) && (!opts->group || evsel == first)) { - attr->disabled = 1; attr->enable_on_exec = 1; } } diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4c7c2d73251f..538598012139 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -31,21 +31,16 @@ static const char **header_argv; int perf_header__push_event(u64 id, const char *name) { + struct perf_trace_event_type *nevents; + if (strlen(name) > MAX_EVENT_NAME) pr_warning("Event %s will be truncated\n", name); - if (!events) { - events = malloc(sizeof(struct perf_trace_event_type)); - if (events == NULL) - return -ENOMEM; - } else { - struct perf_trace_event_type *nevents; + nevents = realloc(events, (event_count + 1) * sizeof(*events)); + if (nevents == NULL) + return -ENOMEM; + events = nevents; - nevents = realloc(events, (event_count + 1) * sizeof(*events)); - if (nevents == NULL) - return -ENOMEM; - events = nevents; - } memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); events[event_count].event_id = id; strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); @@ -296,7 +291,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, if (mkdir_p(filename, 0755)) goto out_free; - snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); + snprintf(filename + len, size - len, "/%s", sbuild_id); if (access(filename, F_OK)) { if (is_kallsyms) { diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9f6d630d5316..1293b5ebea4d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -599,7 +599,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, if (chain->ms.sym) ret += fprintf(fp, "%s\n", chain->ms.sym->name); else - ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); + ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip); return ret; } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5b3a0ef4e232..c7fc18a33d54 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -593,17 +593,27 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, static int config_term(struct perf_event_attr *attr, struct parse_events__term *term) { - switch (term->type) { +#define CHECK_TYPE_VAL(type) \ +do { \ + if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val) \ + return -EINVAL; \ +} while (0) + + switch (term->type_term) { case PARSE_EVENTS__TERM_TYPE_CONFIG: + CHECK_TYPE_VAL(NUM); attr->config = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_CONFIG1: + CHECK_TYPE_VAL(NUM); attr->config1 = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_CONFIG2: + CHECK_TYPE_VAL(NUM); attr->config2 = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: + CHECK_TYPE_VAL(NUM); attr->sample_period = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: @@ -615,7 +625,9 @@ static int config_term(struct perf_event_attr *attr, default: return -EINVAL; } + return 0; +#undef CHECK_TYPE_VAL } static int config_attr(struct perf_event_attr *attr, @@ -1015,11 +1027,12 @@ void print_events(const char *event_glob) int parse_events__is_hardcoded_term(struct parse_events__term *term) { - return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX; + return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; } -int parse_events__new_term(struct parse_events__term **_term, int type, - char *config, char *str, long num) +static int new_term(struct parse_events__term **_term, int type_val, + int type_term, char *config, + char *str, long num) { struct parse_events__term *term; @@ -1028,15 +1041,11 @@ int parse_events__new_term(struct parse_events__term **_term, int type, return -ENOMEM; INIT_LIST_HEAD(&term->list); - term->type = type; + term->type_val = type_val; + term->type_term = type_term; term->config = config; - switch (type) { - case PARSE_EVENTS__TERM_TYPE_CONFIG: - case PARSE_EVENTS__TERM_TYPE_CONFIG1: - case PARSE_EVENTS__TERM_TYPE_CONFIG2: - case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: - case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + switch (type_val) { case PARSE_EVENTS__TERM_TYPE_NUM: term->val.num = num; break; @@ -1051,6 +1060,20 @@ int parse_events__new_term(struct parse_events__term **_term, int type, return 0; } +int parse_events__term_num(struct parse_events__term **term, + int type_term, char *config, long num) +{ + return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, + config, NULL, num); +} + +int parse_events__term_str(struct parse_events__term **term, + int type_term, char *config, char *str) +{ + return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, + config, str, 0); +} + void parse_events__free_terms(struct list_head *terms) { struct parse_events__term *term, *h; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index ca069f893381..3fddd610d350 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -4,7 +4,9 @@ * Parse symbolic events/counts passed in as options: */ +#include <stdbool.h> #include "../../../include/linux/perf_event.h" +#include "types.h" struct list_head; struct perf_evsel; @@ -34,16 +36,17 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); #define EVENTS_HELP_MAX (128*1024) enum { + PARSE_EVENTS__TERM_TYPE_NUM, + PARSE_EVENTS__TERM_TYPE_STR, +}; + +enum { + PARSE_EVENTS__TERM_TYPE_USER, PARSE_EVENTS__TERM_TYPE_CONFIG, PARSE_EVENTS__TERM_TYPE_CONFIG1, PARSE_EVENTS__TERM_TYPE_CONFIG2, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, - PARSE_EVENTS__TERM_TYPE_NUM, - PARSE_EVENTS__TERM_TYPE_STR, - - PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX = - PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, }; struct parse_events__term { @@ -52,14 +55,16 @@ struct parse_events__term { char *str; long num; } val; - int type; - + int type_val; + int type_term; struct list_head list; }; int parse_events__is_hardcoded_term(struct parse_events__term *term); -int parse_events__new_term(struct parse_events__term **term, int type, - char *config, char *str, long num); +int parse_events__term_num(struct parse_events__term **_term, + int type_term, char *config, long num); +int parse_events__term_str(struct parse_events__term **_term, + int type_term, char *config, char *str); void parse_events__free_terms(struct list_head *terms); int parse_events_modifier(struct list_head *list __used, char *str __used); int parse_events_add_tracepoint(struct list_head *list, int *idx, diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 05d766e3ecb5..1fcf1bbc5458 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -54,7 +54,7 @@ num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+ name [a-zA-Z_*?][a-zA-Z0-9_*?]* -modifier_event [ukhp]{1,5} +modifier_event [ukhpGH]{1,8} modifier_bp [rwx] %% diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index d9637da7333c..936913ea0ab6 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -176,8 +176,8 @@ PE_NAME '=' PE_NAME { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR, - $1, $3, 0)); + ABORT_ON(parse_events__term_str(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3)); $$ = term; } | @@ -185,8 +185,8 @@ PE_NAME '=' PE_VALUE { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, - $1, NULL, $3)); + ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3)); $$ = term; } | @@ -194,8 +194,8 @@ PE_NAME { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, - $1, NULL, 1)); + ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, 1)); $$ = term; } | @@ -203,7 +203,7 @@ PE_TERM '=' PE_VALUE { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3)); + ABORT_ON(parse_events__term_num(&term, $1, NULL, $3)); $$ = term; } | @@ -211,7 +211,7 @@ PE_TERM { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1)); + ABORT_ON(parse_events__term_num(&term, $1, NULL, 1)); $$ = term; } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index cb08a118e811..8ee219b7285b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -225,7 +225,7 @@ static int pmu_config_term(struct list_head *formats, if (parse_events__is_hardcoded_term(term)) return 0; - if (term->type != PARSE_EVENTS__TERM_TYPE_NUM) + if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) return -EINVAL; format = pmu_find_format(formats, term->config); @@ -246,6 +246,11 @@ static int pmu_config_term(struct list_head *formats, return -EINVAL; } + /* + * XXX If we ever decide to go with string values for + * non-hardcoded terms, here's the place to translate + * them into value. + */ *vp |= pmu_format_value(format->bits, term->val.num); return 0; } @@ -324,49 +329,58 @@ static struct test_format { /* Simulated users input. */ static struct parse_events__term test_terms[] = { { - .config = (char *) "krava01", - .val.num = 15, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava01", + .val.num = 15, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava02", - .val.num = 170, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava02", + .val.num = 170, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava03", - .val.num = 1, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava03", + .val.num = 1, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava11", - .val.num = 27, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava11", + .val.num = 27, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava12", - .val.num = 1, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava12", + .val.num = 1, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava13", - .val.num = 2, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava13", + .val.num = 2, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava21", - .val.num = 119, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava21", + .val.num = 119, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava22", - .val.num = 11, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava22", + .val.num = 11, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava23", - .val.num = 2, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava23", + .val.num = 2, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, }; #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1efd3bee6336..4dcc8f3190cf 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1108,16 +1108,10 @@ more: } if ((skip = perf_session__process_event(self, &event, tool, head)) < 0) { - dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", - head, event.header.size, event.header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - if (unlikely(head & 7)) - head &= ~7ULL; - - size = 8; + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", + head, event.header.size, event.header.type); + err = -EINVAL; + goto out_err; } head += size; @@ -1226,17 +1220,11 @@ more: if (size == 0 || perf_session__process_event(session, event, tool, file_pos) < 0) { - dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", - file_offset + head, event->header.size, - event->header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - if (unlikely(head & 7)) - head &= ~7ULL; - - size = 8; + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", + file_offset + head, event->header.size, + event->header.type); + err = -EINVAL; + goto out_err; } head += size; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index c0a028c3ebaf..ab9867b2b433 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -977,8 +977,9 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, * And always look at the original dso, not at debuginfo packages, that * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). */ -static int dso__synthesize_plt_symbols(struct dso *dso, struct map *map, - symbol_filter_t filter) +static int +dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map, + symbol_filter_t filter) { uint32_t nr_rel_entries, idx; GElf_Sym sym; @@ -993,10 +994,7 @@ static int dso__synthesize_plt_symbols(struct dso *dso, struct map *map, char sympltname[1024]; Elf *elf; int nr = 0, symidx, fd, err = 0; - char name[PATH_MAX]; - snprintf(name, sizeof(name), "%s%s", - symbol_conf.symfs, dso->long_name); fd = open(name, O_RDONLY); if (fd < 0) goto out; @@ -1703,8 +1701,9 @@ restart: continue; if (ret > 0) { - int nr_plt = dso__synthesize_plt_symbols(dso, map, - filter); + int nr_plt; + + nr_plt = dso__synthesize_plt_symbols(dso, name, map, filter); if (nr_plt > 0) ret += nr_plt; break; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ac49ef208a5f..1f003884f1ab 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -65,6 +65,11 @@ struct symbol { void symbol__delete(struct symbol *sym); +static inline size_t symbol__size(const struct symbol *sym) +{ + return sym->end - sym->start + 1; +} + struct strlist; struct symbol_conf { diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c new file mode 100644 index 000000000000..1064d5b148ad --- /dev/null +++ b/tools/perf/util/target.c @@ -0,0 +1,142 @@ +/* + * Helper functions for handling target threads/cpus + * + * Copyright (C) 2012, LG Electronics, Namhyung Kim <namhyung.kim@lge.com> + * + * Released under the GPL v2. + */ + +#include "target.h" +#include "debug.h" + +#include <pwd.h> +#include <string.h> + + +enum perf_target_errno perf_target__validate(struct perf_target *target) +{ + enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; + + if (target->pid) + target->tid = target->pid; + + /* CPU and PID are mutually exclusive */ + if (target->tid && target->cpu_list) { + target->cpu_list = NULL; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; + } + + /* UID and PID are mutually exclusive */ + if (target->tid && target->uid_str) { + target->uid_str = NULL; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; + } + + /* UID and CPU are mutually exclusive */ + if (target->uid_str && target->cpu_list) { + target->cpu_list = NULL; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; + } + + /* PID and SYSTEM are mutually exclusive */ + if (target->tid && target->system_wide) { + target->system_wide = false; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; + } + + /* UID and SYSTEM are mutually exclusive */ + if (target->uid_str && target->system_wide) { + target->system_wide = false; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; + } + + return ret; +} + +enum perf_target_errno perf_target__parse_uid(struct perf_target *target) +{ + struct passwd pwd, *result; + char buf[1024]; + const char *str = target->uid_str; + + target->uid = UINT_MAX; + if (str == NULL) + return PERF_ERRNO_TARGET__SUCCESS; + + /* Try user name first */ + getpwnam_r(str, &pwd, buf, sizeof(buf), &result); + + if (result == NULL) { + /* + * The user name not found. Maybe it's a UID number. + */ + char *endptr; + int uid = strtol(str, &endptr, 10); + + if (*endptr != '\0') + return PERF_ERRNO_TARGET__INVALID_UID; + + getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); + + if (result == NULL) + return PERF_ERRNO_TARGET__USER_NOT_FOUND; + } + + target->uid = result->pw_uid; + return PERF_ERRNO_TARGET__SUCCESS; +} + +/* + * This must have a same ordering as the enum perf_target_errno. + */ +static const char *perf_target__error_str[] = { + "PID/TID switch overriding CPU", + "PID/TID switch overriding UID", + "UID switch overriding CPU", + "PID/TID switch overriding SYSTEM", + "UID switch overriding SYSTEM", + "Invalid User: %s", + "Problems obtaining information for user %s", +}; + +int perf_target__strerror(struct perf_target *target, int errnum, + char *buf, size_t buflen) +{ + int idx; + const char *msg; + + if (errnum >= 0) { + strerror_r(errnum, buf, buflen); + return 0; + } + + if (errnum < __PERF_ERRNO_TARGET__START || + errnum >= __PERF_ERRNO_TARGET__END) + return -1; + + idx = errnum - __PERF_ERRNO_TARGET__START; + msg = perf_target__error_str[idx]; + + switch (errnum) { + case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU + ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: + snprintf(buf, buflen, "%s", msg); + break; + + case PERF_ERRNO_TARGET__INVALID_UID: + case PERF_ERRNO_TARGET__USER_NOT_FOUND: + snprintf(buf, buflen, msg, target->uid_str); + break; + + default: + /* cannot reach here */ + break; + } + + return 0; +} diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h new file mode 100644 index 000000000000..a4be8575fda5 --- /dev/null +++ b/tools/perf/util/target.h @@ -0,0 +1,65 @@ +#ifndef _PERF_TARGET_H +#define _PERF_TARGET_H + +#include <stdbool.h> +#include <sys/types.h> + +struct perf_target { + const char *pid; + const char *tid; + const char *cpu_list; + const char *uid_str; + uid_t uid; + bool system_wide; + bool uses_mmap; +}; + +enum perf_target_errno { + PERF_ERRNO_TARGET__SUCCESS = 0, + + /* + * Choose an arbitrary negative big number not to clash with standard + * errno since SUS requires the errno has distinct positive values. + * See 'Issue 6' in the link below. + * + * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html + */ + __PERF_ERRNO_TARGET__START = -10000, + + + /* for perf_target__validate() */ + PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START, + PERF_ERRNO_TARGET__PID_OVERRIDE_UID, + PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, + PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, + PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, + + /* for perf_target__parse_uid() */ + PERF_ERRNO_TARGET__INVALID_UID, + PERF_ERRNO_TARGET__USER_NOT_FOUND, + + __PERF_ERRNO_TARGET__END, +}; + +enum perf_target_errno perf_target__validate(struct perf_target *target); +enum perf_target_errno perf_target__parse_uid(struct perf_target *target); + +int perf_target__strerror(struct perf_target *target, int errnum, char *buf, + size_t buflen); + +static inline bool perf_target__has_task(struct perf_target *target) +{ + return target->tid || target->pid || target->uid_str; +} + +static inline bool perf_target__has_cpu(struct perf_target *target) +{ + return target->system_wide || target->cpu_list; +} + +static inline bool perf_target__none(struct perf_target *target) +{ + return !perf_target__has_task(target) && !perf_target__has_cpu(target); +} + +#endif /* _PERF_TARGET_H */ diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 09fe579ccafb..abe0e8e95068 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -69,23 +69,24 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) ret += SNPRINTF(bf + ret, size - ret, "], "); - if (top->target_pid) + if (top->target.pid) ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", - top->target_pid); - else if (top->target_tid) + top->target.pid); + else if (top->target.tid) ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", - top->target_tid); - else if (top->uid_str != NULL) + top->target.tid); + else if (top->target.uid_str != NULL) ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", - top->uid_str); + top->target.uid_str); else ret += SNPRINTF(bf + ret, size - ret, " (all"); - if (top->cpu_list) + if (top->target.cpu_list) ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", - top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); + top->evlist->cpus->nr > 1 ? "s" : "", + top->target.cpu_list); else { - if (top->target_tid) + if (top->target.tid) ret += SNPRINTF(bf + ret, size - ret, ")"); else ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index ce61cb2d1acf..33347ca89ee4 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -13,6 +13,7 @@ struct perf_session; struct perf_top { struct perf_tool tool; struct perf_evlist *evlist; + struct perf_target target; /* * Symbols will be added here in perf_event__process_sample and will * get out after decayed. @@ -23,10 +24,7 @@ struct perf_top { u64 guest_us_samples, guest_kernel_samples; int print_entries, count_filter, delay_secs; int freq; - const char *target_pid, *target_tid; - uid_t uid; bool hide_kernel_symbols, hide_user_symbols, zero; - bool system_wide; bool use_tui, use_stdio; bool sort_has_symbols; bool dont_use_callchains; @@ -37,7 +35,6 @@ struct perf_top { bool sample_id_all_missing; bool exclude_guest_missing; bool dump_symtab; - const char *cpu_list; struct hist_entry *sym_filter_entry; struct perf_evsel *sym_evsel; struct perf_session *session; @@ -47,7 +44,6 @@ struct perf_top { int realtime_prio; int sym_pcnt_filter; const char *sym_filter; - const char *uid_str; }; size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index 52bb07c6442a..4007aca8e0ca 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c @@ -82,41 +82,3 @@ void warning(const char *warn, ...) warn_routine(warn, params); va_end(params); } - -uid_t parse_target_uid(const char *str, const char *tid, const char *pid) -{ - struct passwd pwd, *result; - char buf[1024]; - - if (str == NULL) - return UINT_MAX; - - /* UID and PID are mutually exclusive */ - if (tid || pid) { - ui__warning("PID/TID switch overriding UID\n"); - sleep(1); - return UINT_MAX; - } - - getpwnam_r(str, &pwd, buf, sizeof(buf), &result); - - if (result == NULL) { - char *endptr; - int uid = strtol(str, &endptr, 10); - - if (*endptr != '\0') { - ui__error("Invalid user %s\n", str); - return UINT_MAX - 1; - } - - getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); - - if (result == NULL) { - ui__error("Problems obtaining information for user %s\n", - str); - return UINT_MAX - 1; - } - } - - return result->pw_uid; -} diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8109a907841e..d03599fbe78b 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -148,3 +148,13 @@ int readn(int fd, void *buf, size_t n) return buf - buf_start; } + +size_t hex_width(u64 v) +{ + size_t n = 1; + + while ((v >>= 4)) + ++n; + + return n; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0f99f394d8e0..2daaedb83d84 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -74,7 +74,6 @@ #include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> -#include <pwd.h> #include <inttypes.h> #include "../../../include/linux/magic.h" #include "types.h" @@ -249,8 +248,6 @@ struct perf_event_attr; void event_attr_init(struct perf_event_attr *attr); -uid_t parse_target_uid(const char *str, const char *tid, const char *pid); - #define _STR(x) #x #define STR(x) _STR(x) @@ -265,4 +262,6 @@ bool is_power_of_2(unsigned long n) return (n != 0 && ((n & (n - 1)) == 0)); } +size_t hex_width(u64 v); + #endif |