summaryrefslogtreecommitdiff
path: root/tools/perf
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/Makefile16
-rw-r--r--tools/perf/builtin-record.c63
-rw-r--r--tools/perf/builtin-report.c29
-rw-r--r--tools/perf/builtin-stat.c98
-rw-r--r--tools/perf/builtin-test.c43
-rw-r--r--tools/perf/builtin-top.c54
-rw-r--r--tools/perf/perf.h8
-rw-r--r--tools/perf/ui/browser.c105
-rw-r--r--tools/perf/ui/browser.h5
-rw-r--r--tools/perf/ui/browsers/annotate.c508
-rw-r--r--tools/perf/ui/browsers/hists.c4
-rw-r--r--tools/perf/ui/gtk/browser.c31
-rw-r--r--tools/perf/ui/gtk/setup.c12
-rw-r--r--tools/perf/ui/setup.c168
-rw-r--r--tools/perf/ui/tui/setup.c140
-rw-r--r--tools/perf/util/annotate.c582
-rw-r--r--tools/perf/util/annotate.h67
-rw-r--r--tools/perf/util/cache.h24
-rw-r--r--tools/perf/util/debug.c1
-rw-r--r--tools/perf/util/evlist.c16
-rw-r--r--tools/perf/util/evlist.h4
-rw-r--r--tools/perf/util/evsel.c11
-rw-r--r--tools/perf/util/header.c19
-rw-r--r--tools/perf/util/hist.c2
-rw-r--r--tools/perf/util/parse-events.c45
-rw-r--r--tools/perf/util/parse-events.h23
-rw-r--r--tools/perf/util/parse-events.l2
-rw-r--r--tools/perf/util/parse-events.y16
-rw-r--r--tools/perf/util/pmu.c70
-rw-r--r--tools/perf/util/session.c30
-rw-r--r--tools/perf/util/symbol.c13
-rw-r--r--tools/perf/util/symbol.h5
-rw-r--r--tools/perf/util/target.c142
-rw-r--r--tools/perf/util/target.h65
-rw-r--r--tools/perf/util/top.c19
-rw-r--r--tools/perf/util/top.h6
-rw-r--r--tools/perf/util/usage.c38
-rw-r--r--tools/perf/util/util.c10
-rw-r--r--tools/perf/util/util.h5
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(&notes->src->source, self);
+ next = disasm__get_next_ip_line(&notes->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(&notes->lock);
list_for_each_entry(pos, &notes->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(&notes->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, &notes->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, &notes->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, &notes->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, &notes->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 = &notes->src->source,
browser.b.width += 18; /* Percentage */
ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
list_for_each_entry_safe(pos, n, &notes->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(&notes->src->source, oline);
+ next = disasm__get_next_ip_line(&notes->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, &notes->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(&notes->src->source, objdump_line);
+ disasm__add(&notes->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