From 73323f541fe5f55a3b8a5c3d565bfc1efd64abf6 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 2 Feb 2012 13:54:44 +0100 Subject: [PATCH 01/18] perf tools: fix endianness detection in perf.data The current version of perf detects whether or not the perf.data file is written in a different endianness using the attr_size field in the header of the file. This field represents sizeof(struct perf_event_attr) as known to perf record. If the sizes do not match, then perf tries the byte-swapped version. If they match, then the tool assumes a different endianness. The issue with the approach is that it assumes the size of perf_event_attr always has to match between perf record and perf report. However, the kernel perf_event ABI is extensible. New fields can be added to struct perf_event_attr. Consequently, it is not possible to use attr_size to detect endianness. This patch takes another approach by using the magic number written at the beginning of the perf.data file to detect endianness. The magic number is an eight-byte signature. It's primary purpose is to identify (signature) a perf.data file. But it could also be used to encode the endianness. The patch introduces a new value for this signature. The key difference is that the signature is written differently in the file depending on the endianness. Thus, by comparing the signature from the file with the tool's own signature it is possible to detect endianness. The new signature is "PERFILE2". Backward compatiblity with existing perf.data file is ensured. Tested-by: David Ahern Acked-by: David Ahern Cc: Andi Kleen Cc: Anshuman Khandual Cc: Arun Sharma Cc: David Ahern Cc: Ingo Molnar Cc: Lin Ming Cc: Peter Zijlstra Cc: Roberto Agostino Vitillo Cc: Robert Richter Cc: Vince Weaver Link: http://lkml.kernel.org/r/1328187288-24395-15-git-send-email-eranian@google.com Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 77 +++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index ecd7f4dd7eea01..6f4187de4b728a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -63,9 +63,20 @@ char *perf_header__find_event(u64 id) return NULL; } -static const char *__perf_magic = "PERFFILE"; +/* + * magic2 = "PERFILE2" + * must be a numerical value to let the endianness + * determine the memory layout. That way we are able + * to detect endianness when reading the perf.data file + * back. + * + * we check for legacy (PERFFILE) format. + */ +static const char *__perf_magic1 = "PERFFILE"; +static const u64 __perf_magic2 = 0x32454c4946524550ULL; +static const u64 __perf_magic2_sw = 0x50455246494c4532ULL; -#define PERF_MAGIC (*(u64 *)__perf_magic) +#define PERF_MAGIC __perf_magic2 struct perf_file_attr { struct perf_event_attr attr; @@ -1620,24 +1631,59 @@ int perf_header__process_sections(struct perf_header *header, int fd, return err; } +static int check_magic_endian(u64 *magic, struct perf_file_header *header, + struct perf_header *ph) +{ + int ret; + + /* check for legacy format */ + ret = memcmp(magic, __perf_magic1, sizeof(*magic)); + if (ret == 0) { + pr_debug("legacy perf.data format\n"); + if (!header) + return -1; + + if (header->attr_size != sizeof(struct perf_file_attr)) { + u64 attr_size = bswap_64(header->attr_size); + + if (attr_size != sizeof(struct perf_file_attr)) + return -1; + + ph->needs_swap = true; + } + return 0; + } + + /* check magic number with same endianness */ + if (*magic == __perf_magic2) + return 0; + + /* check magic number but opposite endianness */ + if (*magic != __perf_magic2_sw) + return -1; + + ph->needs_swap = true; + + return 0; +} + int perf_file_header__read(struct perf_file_header *header, struct perf_header *ph, int fd) { + int ret; + lseek(fd, 0, SEEK_SET); - if (readn(fd, header, sizeof(*header)) <= 0 || - memcmp(&header->magic, __perf_magic, sizeof(header->magic))) + ret = readn(fd, header, sizeof(*header)); + if (ret <= 0) return -1; - if (header->attr_size != sizeof(struct perf_file_attr)) { - u64 attr_size = bswap_64(header->attr_size); - - if (attr_size != sizeof(struct perf_file_attr)) - return -1; + if (check_magic_endian(&header->magic, header, ph) < 0) + return -1; + if (ph->needs_swap) { mem_bswap_64(header, offsetof(struct perf_file_header, - adds_features)); - ph->needs_swap = true; + adds_features)); } if (header->size != sizeof(*header)) { @@ -1873,8 +1919,13 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, struct perf_header *ph, int fd, bool repipe) { - if (readn(fd, header, sizeof(*header)) <= 0 || - memcmp(&header->magic, __perf_magic, sizeof(header->magic))) + int ret; + + ret = readn(fd, header, sizeof(*header)); + if (ret <= 0) + return -1; + + if (check_magic_endian(&header->magic, NULL, ph) < 0) return -1; if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) From d3665498955779e56453501a16f4ad084f798802 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 6 Feb 2012 15:27:52 -0700 Subject: [PATCH 02/18] perf record: No build id option fails A recent refactoring of perf-record introduced the following: perf record -a -B Couldn't generating buildids. Use --no-buildid to profile anyway. sleep: Terminated I believe the triple negative was meant to be only a double negative. :-) While I'm there, fixed the grammar on the error message. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1328567272-13190-1-git-send-email-dsahern@gmail.com Signed-off-by: David Ahern Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f8d9a545dd6ec0..d6d1c6cdcfe659 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -494,9 +494,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) return err; } - if (!!rec->no_buildid + if (!rec->no_buildid && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) { - pr_err("Couldn't generating buildids. " + pr_err("Couldn't generate buildids. " "Use --no-buildid to profile anyway.\n"); return -1; } From 6a5c13aff49ac9b3fea38d5f84b436718cb2780d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 12 Feb 2012 19:45:24 +0900 Subject: [PATCH 03/18] perf tools: Fix build dependency of perf python extension The perf python extention (perf.so) file lacks its dependencies in the Makefile so that it cannot be refreshed if one of source files it depends is changed. Fix it by putting them in a separate file and processing it in both of Makefile and setup.py. Reported-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1329043524-12470-1-git-send-email-namhyung@gmail.com Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 5 ++++- tools/perf/util/python-ext-sources | 17 +++++++++++++++++ tools/perf/util/setup.py | 8 ++++---- 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 tools/perf/util/python-ext-sources diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 64df5de12ca83c..8359fa140d2d8c 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -183,7 +183,10 @@ SCRIPT_SH += perf-archive.sh grep-libs = $(filter -l%,$(1)) strip-libs = $(filter-out -l%,$(1)) -$(OUTPUT)python/perf.so: $(PYRF_OBJS) +PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) +PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py + +$(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ --quiet build_ext; \ mkdir -p $(OUTPUT)python && \ diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources new file mode 100644 index 00000000000000..ff606f482a7cd6 --- /dev/null +++ b/tools/perf/util/python-ext-sources @@ -0,0 +1,17 @@ +# +# List of files needed by perf python extention +# +# Each source file must be placed on its own line so that it can be +# processed by Makefile and util/setup.py accordingly. +# + +util/python.c +util/ctype.c +util/evlist.c +util/evsel.c +util/cpumap.c +util/thread_map.c +util/util.c +util/xyarray.c +util/cgroup.c +util/debugfs.c diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 36d4c561957504..d0f9f29cf181ad 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -24,11 +24,11 @@ def finalize_options(self): build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') +ext_sources = [f.strip() for f in file('util/python-ext-sources') + if len(f.strip()) > 0 and f[0] != '#'] + perf = Extension('perf', - sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', - 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', - 'util/util.c', 'util/xyarray.c', 'util/cgroup.c', - 'util/debugfs.c'], + sources = ext_sources, include_dirs = ['util/include'], extra_compile_args = cflags, ) From eca1c3e3f937307331fd1fd5ee5205e57f2131ca Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 13 Feb 2012 07:57:37 -0700 Subject: [PATCH 04/18] perf tools: Fix out of tree compiles For latest tip/perf/core tree Compiles are failing on: GEN common-cmds.h make: *** No rule to make target `../../arch/x86/lib/memset_64.S', needed by `builtin-annotate.o'. Stop. Resolve by adding memset.* to the tar file. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1329145057-26302-1-git-send-email-dsahern@gmail.com Signed-off-by: David Ahern Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/MANIFEST | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 1078c5fadd5bfc..5476bc0a1eac87 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -9,6 +9,7 @@ lib/rbtree.c include/linux/swab.h arch/*/include/asm/unistd*.h arch/*/lib/memcpy*.S +arch/*/lib/memset*.S include/linux/poison.h include/linux/magic.h include/linux/hw_breakpoint.h From b52956c961be3a04182ae7b776623531601e0fb7 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 8 Feb 2012 09:32:52 -0700 Subject: [PATCH 05/18] perf tools: Allow multiple threads or processes in record, stat, top Allow a user to collect events for multiple threads or processes using a comma separated list. e.g., collect data on a VM and its vhost thread: perf top -p 21483,21485 perf stat -p 21483,21485 -ddd perf record -p 21483,21485 or monitoring vcpu threads perf top -t 21488,21489 perf stat -t 21488,21489 -ddd perf record -t 21488,21489 Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1328718772-16688-1-git-send-email-dsahern@gmail.com Signed-off-by: David Ahern Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 4 +- tools/perf/Documentation/perf-stat.txt | 4 +- tools/perf/Documentation/perf-top.txt | 4 +- tools/perf/builtin-record.c | 10 +- tools/perf/builtin-stat.c | 31 +++--- tools/perf/builtin-test.c | 2 - tools/perf/builtin-top.c | 12 +-- tools/perf/perf.h | 4 +- tools/perf/util/evlist.c | 10 +- tools/perf/util/evlist.h | 4 +- tools/perf/util/evsel.c | 2 +- tools/perf/util/python-ext-sources | 2 + tools/perf/util/thread_map.c | 128 +++++++++++++++++++++++ tools/perf/util/thread_map.h | 4 + tools/perf/util/top.c | 10 +- tools/perf/util/top.h | 2 +- tools/perf/util/usage.c | 6 +- tools/perf/util/util.h | 2 +- 18 files changed, 185 insertions(+), 56 deletions(-) diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index ff9a66e0d4e4be..a5766b4b012562 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -52,11 +52,11 @@ OPTIONS -p:: --pid=:: - Record events on existing process ID. + Record events on existing process ID (comma separated list). -t:: --tid=:: - Record events on existing thread ID. + Record events on existing thread ID (comma separated list). -u:: --uid=:: diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 8966b9ab20144a..2fa173b5197061 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -35,11 +35,11 @@ OPTIONS child tasks do not inherit counters -p:: --pid=:: - stat events on existing process id + stat events on existing process id (comma separated list) -t:: --tid=:: - stat events on existing thread id + stat events on existing thread id (comma separated list) -a:: diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index ab1454ed450f96..4a5680cb242ebe 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -72,11 +72,11 @@ Default is to monitor all CPUS. -p :: --pid=:: - Profile events on existing Process ID. + Profile events on existing Process ID (comma separated list). -t :: --tid=:: - Profile events on existing thread ID. + Profile events on existing thread ID (comma separated list). -u:: --uid=:: diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d6d1c6cdcfe659..08ed24b66ffe4b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -645,8 +645,6 @@ static const char * const record_usage[] = { */ static struct perf_record record = { .opts = { - .target_pid = -1, - .target_tid = -1, .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, @@ -670,9 +668,9 @@ const struct option record_options[] = { parse_events_option), OPT_CALLBACK(0, "filter", &record.evlist, "filter", "event filter", parse_filter), - OPT_INTEGER('p', "pid", &record.opts.target_pid, + OPT_STRING('p', "pid", &record.opts.target_pid, "pid", "record events on existing process id"), - OPT_INTEGER('t', "tid", &record.opts.target_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"), @@ -739,7 +737,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 == -1 && rec->opts.target_tid == -1 && + if (!argc && !rec->opts.target_pid && !rec->opts.target_tid && !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str) usage_with_options(record_usage, record_options); @@ -785,7 +783,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1) goto out_free_fd; - if (rec->opts.target_pid != -1) + if (rec->opts.target_pid) rec->opts.target_tid = rec->opts.target_pid; if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d14b37ad7638b1..ea40e4e8b2271f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -182,8 +182,8 @@ static int run_count = 1; static bool no_inherit = false; static bool scale = true; static bool no_aggr = false; -static pid_t target_pid = -1; -static pid_t target_tid = -1; +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; @@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, if (system_wide) return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, group, group_fd); - if (target_pid == -1 && target_tid == -1) { + if (!target_pid && !target_tid) { attr->disabled = 1; attr->enable_on_exec = 1; } @@ -446,7 +446,7 @@ static int run_perf_stat(int argc __used, const char **argv) exit(-1); } - if (target_tid == -1 && target_pid == -1 && !system_wide) + if (!target_tid && !target_pid && !system_wide) evsel_list->threads->map[0] = child_pid; /* @@ -968,14 +968,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 == -1 && target_tid == -1) { + if (!target_pid && !target_tid) { fprintf(output, "\'%s", argv[0]); for (i = 1; i < argc; i++) fprintf(output, " %s", argv[i]); - } else if (target_pid != -1) - fprintf(output, "process id \'%d", target_pid); + } else if (target_pid) + fprintf(output, "process id \'%s", target_pid); else - fprintf(output, "thread id \'%d", target_tid); + fprintf(output, "thread id \'%s", target_tid); fprintf(output, "\'"); if (run_count > 1) @@ -1049,10 +1049,10 @@ static const struct option options[] = { "event filter", parse_filter), OPT_BOOLEAN('i', "no-inherit", &no_inherit, "child tasks do not inherit counters"), - OPT_INTEGER('p', "pid", &target_pid, - "stat events on existing process id"), - OPT_INTEGER('t', "tid", &target_tid, - "stat events on existing thread id"), + OPT_STRING('p', "pid", &target_pid, "pid", + "stat events on existing process id"), + OPT_STRING('t', "tid", &target_tid, "tid", + "stat events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('g', "group", &group, @@ -1190,7 +1190,7 @@ 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 == -1 && target_tid == -1) + if (!argc && !target_pid && !target_tid) usage_with_options(stat_usage, options); if (run_count <= 0) usage_with_options(stat_usage, options); @@ -1206,10 +1206,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) if (add_default_attributes()) goto out; - if (target_pid != -1) + if (target_pid) target_tid = target_pid; - evsel_list->threads = thread_map__new(target_pid, target_tid, UINT_MAX); + 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); diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 70c4eb2bdf7236..0f151952a77010 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -1010,8 +1010,6 @@ static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp, static int test__PERF_RECORD(void) { struct perf_record_opts opts = { - .target_pid = -1, - .target_tid = -1, .no_delay = true, .freq = 10, .mmap_pages = 256, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d869b214ada273..94d55cb2048c38 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -965,7 +965,7 @@ static int __cmd_top(struct perf_top *top) if (ret) goto out_delete; - if (top->target_tid != -1 || top->uid != UINT_MAX) + if (top->target_tid || top->uid != UINT_MAX) perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, perf_event__process, &top->session->host_machine); @@ -1103,8 +1103,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) struct perf_top top = { .count_filter = 5, .delay_secs = 2, - .target_pid = -1, - .target_tid = -1, .uid = UINT_MAX, .freq = 1000, /* 1 KHz */ .sample_id_all_avail = true, @@ -1118,9 +1116,9 @@ 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_INTEGER('p', "pid", &top.target_pid, + OPT_STRING('p', "pid", &top.target_pid, "pid", "profile events on existing process id"), - OPT_INTEGER('t', "tid", &top.target_tid, + OPT_STRING('t', "tid", &top.target_tid, "tid", "profile events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &top.system_wide, "system-wide collection from all CPUs"), @@ -1210,13 +1208,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) goto out_delete_evlist; /* CPU and PID are mutually exclusive */ - if (top.target_tid > 0 && top.cpu_list) { + if (top.target_tid && top.cpu_list) { printf("WARNING: PID switch overriding CPU\n"); sleep(1); top.cpu_list = NULL; } - if (top.target_pid != -1) + if (top.target_pid) top.target_tid = top.target_pid; if (perf_evlist__create_maps(top.evlist, top.target_pid, diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 92af1688bae45a..deb17dba4a5b42 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -186,8 +186,8 @@ extern const char perf_version_string[]; void pthread__unblock_sigwinch(void); struct perf_record_opts { - pid_t target_pid; - pid_t target_tid; + const char *target_pid; + const char *target_tid; uid_t uid; bool call_graph; bool group; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index a57a8cfc5d90af..5c61dc57d7c785 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -593,15 +593,15 @@ 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, pid_t target_pid, - pid_t target_tid, uid_t uid, const char *cpu_list) +int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, + const char *target_tid, uid_t uid, const char *cpu_list) { - evlist->threads = thread_map__new(target_pid, target_tid, uid); + evlist->threads = thread_map__new_str(target_pid, target_tid, uid); if (evlist->threads == NULL) return -1; - if (uid != UINT_MAX || (cpu_list == NULL && target_tid != -1)) + if (uid != UINT_MAX || (cpu_list == NULL && target_tid)) evlist->cpus = cpu_map__dummy_new(); else evlist->cpus = cpu_map__new(cpu_list); @@ -820,7 +820,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, exit(-1); } - if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1) + if (!opts->system_wide && !opts->target_tid && !opts->target_pid) 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 1b4282be8fe746..21f1c9e57f1390 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, pid_t target_pid, - pid_t tid, uid_t uid, const char *cpu_list); +int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, + const char *tid, uid_t uid, const char *cpu_list); 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 9a11f9edac12d0..f910f50136d014 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -130,7 +130,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) attr->mmap = track; attr->comm = track; - if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) { + if (!opts->target_pid && !opts->target_tid && !opts->system_wide) { attr->disabled = 1; attr->enable_on_exec = 1; } diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index ff606f482a7cd6..2884e67ee625d6 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -15,3 +15,5 @@ util/util.c util/xyarray.c util/cgroup.c util/debugfs.c +util/strlist.c +../../lib/rbtree.c diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 3d4b6c5931b924..e15983cf077d80 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -6,6 +6,8 @@ #include #include #include +#include "strlist.h" +#include #include "thread_map.h" /* Skip "." and ".." directories */ @@ -152,6 +154,132 @@ struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) return thread_map__new_by_tid(tid); } +static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) +{ + struct thread_map *threads = NULL, *nt; + char name[256]; + int items, total_tasks = 0; + struct dirent **namelist = NULL; + int i, j = 0; + pid_t pid, prev_pid = INT_MAX; + char *end_ptr; + struct str_node *pos; + struct strlist *slist = strlist__new(false, pid_str); + + if (!slist) + return NULL; + + strlist__for_each(pos, slist) { + pid = strtol(pos->s, &end_ptr, 10); + + if (pid == INT_MIN || pid == INT_MAX || + (*end_ptr != '\0' && *end_ptr != ',')) + goto out_free_threads; + + if (pid == prev_pid) + continue; + + sprintf(name, "/proc/%d/task", pid); + items = scandir(name, &namelist, filter, NULL); + if (items <= 0) + goto out_free_threads; + + total_tasks += items; + nt = realloc(threads, (sizeof(*threads) + + sizeof(pid_t) * total_tasks)); + if (nt == NULL) + goto out_free_threads; + + threads = nt; + + if (threads) { + for (i = 0; i < items; i++) + threads->map[j++] = atoi(namelist[i]->d_name); + threads->nr = total_tasks; + } + + for (i = 0; i < items; i++) + free(namelist[i]); + free(namelist); + + if (!threads) + break; + } + +out: + strlist__delete(slist); + return threads; + +out_free_threads: + free(threads); + threads = NULL; + goto out; +} + +static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) +{ + struct thread_map *threads = NULL, *nt; + int ntasks = 0; + pid_t tid, prev_tid = INT_MAX; + char *end_ptr; + struct str_node *pos; + struct strlist *slist; + + /* perf-stat expects threads to be generated even if tid not given */ + if (!tid_str) { + threads = malloc(sizeof(*threads) + sizeof(pid_t)); + if (threads != NULL) { + threads->map[1] = -1; + threads->nr = 1; + } + return threads; + } + + slist = strlist__new(false, tid_str); + if (!slist) + return NULL; + + strlist__for_each(pos, slist) { + tid = strtol(pos->s, &end_ptr, 10); + + if (tid == INT_MIN || tid == INT_MAX || + (*end_ptr != '\0' && *end_ptr != ',')) + goto out_free_threads; + + if (tid == prev_tid) + continue; + + ntasks++; + nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks); + + if (nt == NULL) + goto out_free_threads; + + threads = nt; + threads->map[ntasks - 1] = tid; + threads->nr = ntasks; + } +out: + return threads; + +out_free_threads: + free(threads); + threads = NULL; + goto out; +} + +struct thread_map *thread_map__new_str(const char *pid, const char *tid, + uid_t uid) +{ + if (pid) + return thread_map__new_by_pid_str(pid); + + if (!tid && uid != UINT_MAX) + return thread_map__new_by_uid(uid); + + return thread_map__new_by_tid_str(tid); +} + void thread_map__delete(struct thread_map *threads) { free(threads); diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index c75ddbaba0055d..7da80f14418bc1 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -13,6 +13,10 @@ struct thread_map *thread_map__new_by_pid(pid_t pid); struct thread_map *thread_map__new_by_tid(pid_t tid); struct thread_map *thread_map__new_by_uid(uid_t uid); struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); + +struct thread_map *thread_map__new_str(const char *pid, + const char *tid, uid_t uid); + void thread_map__delete(struct thread_map *threads); size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index e4370ca271933d..09fe579ccafbc9 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -69,11 +69,11 @@ 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 != -1) - ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d", + if (top->target_pid) + ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", top->target_pid); - else if (top->target_tid != -1) - ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", + else if (top->target_tid) + ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", top->target_tid); else if (top->uid_str != NULL) ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", @@ -85,7 +85,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); else { - if (top->target_tid != -1) + 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 def3e53e0fe0d2..49eb8481f19fd5 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -23,7 +23,7 @@ struct perf_top { u64 guest_us_samples, guest_kernel_samples; int print_entries, count_filter, delay_secs; int freq; - pid_t target_pid, target_tid; + const char *target_pid, *target_tid; uid_t uid; bool hide_kernel_symbols, hide_user_symbols, zero; bool system_wide; diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index d0c013934f30ad..52bb07c6442a5b 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c @@ -83,7 +83,7 @@ void warning(const char *warn, ...) va_end(params); } -uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid) +uid_t parse_target_uid(const char *str, const char *tid, const char *pid) { struct passwd pwd, *result; char buf[1024]; @@ -91,8 +91,8 @@ uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid) if (str == NULL) return UINT_MAX; - /* CPU and PID are mutually exclusive */ - if (tid > 0 || pid > 0) { + /* UID and PID are mutually exclusive */ + if (tid || pid) { ui__warning("PID/TID switch overriding UID\n"); sleep(1); return UINT_MAX; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 232d17ef3e60eb..7917b09430bd83 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -245,7 +245,7 @@ struct perf_event_attr; void event_attr_init(struct perf_event_attr *attr); -uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid); +uid_t parse_target_uid(const char *str, const char *tid, const char *pid); #define _STR(x) #x #define STR(x) _STR(x) From 0c095715b388d19d7a0b7e8eaceeceb018f5b3d1 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 10 Feb 2012 18:05:04 +0100 Subject: [PATCH 06/18] perf top: Don't process samples with no valid machine object The perf sample processing code relies on a valid machine object. Make sure that this path is only entered when such a object exists. A counter for samples where no machine object exits is also introduced to give the user a message about these samples. Reported-by: David Ahern Reported-by: Jason Wang Cc: David Ahern Cc: Ingo Molnar Cc: Jason Wang Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1328893505-4115-2-git-send-email-joerg.roedel@amd.com Signed-off-by: Joerg Roedel Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 6 ++++++ tools/perf/util/hist.h | 1 + tools/perf/util/session.c | 10 ++++++++++ 3 files changed, 17 insertions(+) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 94d55cb2048c38..5a88c0dadf1554 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -677,6 +677,12 @@ static void perf_event__process_sample(struct perf_tool *tool, return; } + if (!machine) { + pr_err("%u unprocessable samples recorded.", + top->session->hists.stats.nr_unprocessable_samples++); + return; + } + if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) top->exact_samples++; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 0d486135d10fde..48e5acd1e862f7 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -32,6 +32,7 @@ struct events_stats { u32 nr_unknown_events; u32 nr_invalid_chains; u32 nr_unknown_id; + u32 nr_unprocessable_samples; }; enum hist_column { diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 552c1c50eea19d..9f833cf9c6a9b9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -796,6 +796,10 @@ static int perf_session_deliver_event(struct perf_session *session, ++session->hists.stats.nr_unknown_id; return -1; } + if (machine == NULL) { + ++session->hists.stats.nr_unprocessable_samples; + return -1; + } return tool->sample(tool, event, sample, evsel, machine); case PERF_RECORD_MMAP: return tool->mmap(tool, event, sample, machine); @@ -964,6 +968,12 @@ static void perf_session__warn_about_errors(const struct perf_session *session, session->hists.stats.nr_invalid_chains, session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); } + + if (session->hists.stats.nr_unprocessable_samples != 0) { + ui__warning("%u unprocessable samples recorded.\n" + "Do you have a KVM guest running and not using 'perf kvm'?\n", + session->hists.stats.nr_unprocessable_samples); + } } #define session_done() (*(volatile int *)(&session_done)) From c4a7dca92bbb9881a5d678720f1d0c2153499749 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 10 Feb 2012 18:05:05 +0100 Subject: [PATCH 07/18] perf tools: Change perf_guest default back to false Setting perf_guest to true by default makes no sense because the perf subcommands can not setup guest symbol information and thus not process and guest samples. The only exception is perf-kvm which changes the perf_guest value on its own. So change the default for perf_guest back to false. Cc: David Ahern Cc: Ingo Molnar Cc: Jason Wang Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1328893505-4115-3-git-send-email-joerg.roedel@amd.com Signed-off-by: Joerg Roedel Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 813141047fc22f..fb25d132921882 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -6,7 +6,7 @@ * XXX We need to find a better place for these things... */ bool perf_host = true; -bool perf_guest = true; +bool perf_guest = false; void event_attr_init(struct perf_event_attr *attr) { From 2cd13b0f7dd3a5df68ff4cd82af2e1f7bc2a43ab Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 10 Feb 2012 10:10:15 +0900 Subject: [PATCH 08/18] perf tools: Implement islower/isupper macro into util.h The util.h header provides various ctype macros but lacks those two. Add them. Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1328836217-9118-1-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7917b09430bd83..0f99f394d8e089 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -199,6 +199,8 @@ static inline int has_extension(const char *filename, const char *ext) #undef isalpha #undef isprint #undef isalnum +#undef islower +#undef isupper #undef tolower #undef toupper @@ -219,6 +221,8 @@ extern unsigned char sane_ctype[256]; #define isalpha(x) sane_istest(x,GIT_ALPHA) #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) #define isprint(x) sane_istest(x,GIT_PRINT) +#define islower(x) (sane_istest(x,GIT_ALPHA) && sane_istest(x,0x20)) +#define isupper(x) (sane_istest(x,GIT_ALPHA) && !sane_istest(x,0x20)) #define tolower(x) sane_case((unsigned char)(x), 0x20) #define toupper(x) sane_case((unsigned char)(x), 0) From 3bd2b8d1095d41254d3bdc3d62622c7fd5e143b8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 10 Feb 2012 10:10:16 +0900 Subject: [PATCH 09/18] perf tools: ctype.c only wants util.h The implementation of sane ctype macros only depends on symbols in util.h not cache.h. Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1328836217-9118-2-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ctype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index 35073621e5de9a..aada3ac5e891f8 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c @@ -3,7 +3,7 @@ * * No surprises, and works with signed and unsigned chars. */ -#include "cache.h" +#include "util.h" enum { S = GIT_SPACE, From e334c726ca774d346cb7c4db487e80953a202b58 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 10 Feb 2012 10:10:17 +0900 Subject: [PATCH 10/18] perf tools: Get rid of ctype.h in symbol.c The ctype.h in symbol.c was needed because of isupper(). However we now have it in util.h, it can be changed to use our implementation. Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1328836217-9118-3-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fc6e12fe4b44f1..5dd83c3e2c0c4d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include "build-id.h" +#include "util.h" #include "debug.h" #include "symbol.h" #include "strlist.h" From 2837609fefbfe8f1248bfce09d9f0b44d5eb713d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 27 Jan 2012 15:34:21 +0100 Subject: [PATCH 11/18] perf tools: Remove unused functions from debugfs object Following debugfs object functions are not referenced within the code: int debugfs_valid_entry(const char *path); int debugfs_umount(void); int debugfs_write(const char *entry, const char *value); int debugfs_read(const char *entry, char *buffer, size_t size); void debugfs_force_cleanup(void); int debugfs_make_path(const char *element, char *buffer, int size); Removing them. Cc: Corey Ashford Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1327674868-10486-3-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debugfs.c | 141 -------------------------------------- tools/perf/util/debugfs.h | 6 -- 2 files changed, 147 deletions(-) diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index ffc35e748e89e1..dd8b19319c03d9 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c @@ -15,32 +15,6 @@ static const char *debugfs_known_mountpoints[] = { 0, }; -/* use this to force a umount */ -void debugfs_force_cleanup(void) -{ - debugfs_find_mountpoint(); - debugfs_premounted = 0; - debugfs_umount(); -} - -/* construct a full path to a debugfs element */ -int debugfs_make_path(const char *element, char *buffer, int size) -{ - int len; - - if (strlen(debugfs_mountpoint) == 0) { - buffer[0] = '\0'; - return -1; - } - - len = strlen(debugfs_mountpoint) + strlen(element) + 1; - if (len >= size) - return len+1; - - snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element); - return 0; -} - static int debugfs_found; /* find the path to the mounted debugfs */ @@ -97,17 +71,6 @@ int debugfs_valid_mountpoint(const char *debugfs) return 0; } - -int debugfs_valid_entry(const char *path) -{ - struct stat st; - - if (stat(path, &st)) - return -errno; - - return 0; -} - static void debugfs_set_tracing_events_path(const char *mountpoint) { snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", @@ -149,107 +112,3 @@ void debugfs_set_path(const char *mountpoint) snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); debugfs_set_tracing_events_path(mountpoint); } - -/* umount the debugfs */ - -int debugfs_umount(void) -{ - char umountcmd[128]; - int ret; - - /* if it was already mounted, leave it */ - if (debugfs_premounted) - return 0; - - /* make sure it's a valid mount point */ - ret = debugfs_valid_mountpoint(debugfs_mountpoint); - if (ret) - return ret; - - snprintf(umountcmd, sizeof(umountcmd), - "/bin/umount %s", debugfs_mountpoint); - return system(umountcmd); -} - -int debugfs_write(const char *entry, const char *value) -{ - char path[PATH_MAX + 1]; - int ret, count; - int fd; - - /* construct the path */ - snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); - - /* verify that it exists */ - ret = debugfs_valid_entry(path); - if (ret) - return ret; - - /* get how many chars we're going to write */ - count = strlen(value); - - /* open the debugfs entry */ - fd = open(path, O_RDWR); - if (fd < 0) - return -errno; - - while (count > 0) { - /* write it */ - ret = write(fd, value, count); - if (ret <= 0) { - if (ret == EAGAIN) - continue; - close(fd); - return -errno; - } - count -= ret; - } - - /* close it */ - close(fd); - - /* return success */ - return 0; -} - -/* - * read a debugfs entry - * returns the number of chars read or a negative errno - */ -int debugfs_read(const char *entry, char *buffer, size_t size) -{ - char path[PATH_MAX + 1]; - int ret; - int fd; - - /* construct the path */ - snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); - - /* verify that it exists */ - ret = debugfs_valid_entry(path); - if (ret) - return ret; - - /* open the debugfs entry */ - fd = open(path, O_RDONLY); - if (fd < 0) - return -errno; - - do { - /* read it */ - ret = read(fd, buffer, size); - if (ret == 0) { - close(fd); - return EOF; - } - } while (ret < 0 && errno == EAGAIN); - - /* close it */ - close(fd); - - /* make *sure* there's a null character at the end */ - buffer[ret] = '\0'; - - /* return the number of chars read */ - return ret; -} diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h index 4a878f735eb013..68f3e87ec57f3b 100644 --- a/tools/perf/util/debugfs.h +++ b/tools/perf/util/debugfs.h @@ -3,14 +3,8 @@ const char *debugfs_find_mountpoint(void); int debugfs_valid_mountpoint(const char *debugfs); -int debugfs_valid_entry(const char *path); char *debugfs_mount(const char *mountpoint); -int debugfs_umount(void); void debugfs_set_path(const char *mountpoint); -int debugfs_write(const char *entry, const char *value); -int debugfs_read(const char *entry, char *buffer, size_t size); -void debugfs_force_cleanup(void); -int debugfs_make_path(const char *element, char *buffer, int size); extern char debugfs_mountpoint[]; extern char tracing_events_path[]; From e90fda0635401225ca7c2343bea2f6d279347d10 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 27 Jan 2012 15:34:22 +0100 Subject: [PATCH 12/18] perf tools: Add sysfs mountpoint interface Adding sysfs object to provide sysfs mount information in the same way as debugfs object does. The object provides following function: sysfs_find_mountpoint which returns the sysfs mount mount. Cc: Corey Ashford Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1327674868-10486-4-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 ++ tools/perf/util/sysfs.c | 60 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/sysfs.h | 6 +++++ 3 files changed, 68 insertions(+) create mode 100644 tools/perf/util/sysfs.c create mode 100644 tools/perf/util/sysfs.h diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 8359fa140d2d8c..e011b5060f922b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -259,6 +259,7 @@ LIB_H += util/callchain.h LIB_H += util/build-id.h LIB_H += util/debug.h LIB_H += util/debugfs.h +LIB_H += util/sysfs.h LIB_H += util/event.h LIB_H += util/evsel.h LIB_H += util/evlist.h @@ -305,6 +306,7 @@ LIB_OBJS += $(OUTPUT)util/build-id.o LIB_OBJS += $(OUTPUT)util/config.o LIB_OBJS += $(OUTPUT)util/ctype.o LIB_OBJS += $(OUTPUT)util/debugfs.o +LIB_OBJS += $(OUTPUT)util/sysfs.o LIB_OBJS += $(OUTPUT)util/environment.o LIB_OBJS += $(OUTPUT)util/event.o LIB_OBJS += $(OUTPUT)util/evlist.o diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c new file mode 100644 index 00000000000000..48c6902e749f7d --- /dev/null +++ b/tools/perf/util/sysfs.c @@ -0,0 +1,60 @@ + +#include "util.h" +#include "sysfs.h" + +static const char * const sysfs_known_mountpoints[] = { + "/sys", + 0, +}; + +static int sysfs_found; +char sysfs_mountpoint[PATH_MAX]; + +static int sysfs_valid_mountpoint(const char *sysfs) +{ + struct statfs st_fs; + + if (statfs(sysfs, &st_fs) < 0) + return -ENOENT; + else if (st_fs.f_type != (long) SYSFS_MAGIC) + return -ENOENT; + + return 0; +} + +const char *sysfs_find_mountpoint(void) +{ + const char * const *ptr; + char type[100]; + FILE *fp; + + if (sysfs_found) + return (const char *) sysfs_mountpoint; + + ptr = sysfs_known_mountpoints; + while (*ptr) { + if (sysfs_valid_mountpoint(*ptr) == 0) { + sysfs_found = 1; + strcpy(sysfs_mountpoint, *ptr); + return sysfs_mountpoint; + } + ptr++; + } + + /* give up and parse /proc/mounts */ + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + while (!sysfs_found && + fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", + sysfs_mountpoint, type) == 2) { + + if (strcmp(type, "sysfs") == 0) + sysfs_found = 1; + } + + fclose(fp); + + return sysfs_found ? sysfs_mountpoint : NULL; +} diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h new file mode 100644 index 00000000000000..a813b720393852 --- /dev/null +++ b/tools/perf/util/sysfs.h @@ -0,0 +1,6 @@ +#ifndef __SYSFS_H__ +#define __SYSFS_H__ + +const char *sysfs_find_mountpoint(void); + +#endif /* __DEBUGFS_H__ */ From 850f8127fa3666737881aecb3b16e8ede85e58f4 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 27 Jan 2012 15:34:23 +0100 Subject: [PATCH 13/18] perf tools: Add bitmap_or function into bitmap object Adding implementation os bitmap_or function to the bitmap object. It is stolen from the kernel lib/bitmap.o object. It is used in upcomming patches. Cc: Corey Ashford Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1327674868-10486-5-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bitmap.c | 10 ++++++++++ tools/perf/util/include/linux/bitmap.h | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c index 5e230acae1e95a..0a1adc1111fd2d 100644 --- a/tools/perf/util/bitmap.c +++ b/tools/perf/util/bitmap.c @@ -19,3 +19,13 @@ int __bitmap_weight(const unsigned long *bitmap, int bits) return w; } + +void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + + for (k = 0; k < nr; k++) + dst[k] = bitmap1[k] | bitmap2[k]; +} diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index eda4416efa0a98..bb162e40c76cf6 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -5,6 +5,8 @@ #include int __bitmap_weight(const unsigned long *bitmap, int bits); +void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); #define BITMAP_LAST_WORD_MASK(nbits) \ ( \ @@ -32,4 +34,13 @@ static inline int bitmap_weight(const unsigned long *src, int nbits) return __bitmap_weight(src, nbits); } +static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = *src1 | *src2; + else + __bitmap_or(dst, src1, src2, nbits); +} + #endif /* _PERF_BITOPS_H */ From 08d95bd256277f914e525aeb292da52f15173e7d Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 10 Feb 2012 15:41:55 +0100 Subject: [PATCH 14/18] perf tools: Moving code in header.c Needed for later changes. No modified functionality. Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1328884916-5901-1-git-send-email-robert.richter@amd.com Signed-off-by: Robert Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 300 +++++++++++++++++++-------------------- 1 file changed, 150 insertions(+), 150 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6f4187de4b728a..5bb2d752ab8f49 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1316,6 +1316,156 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) free(str); } +static int __event_process_build_id(struct build_id_event *bev, + char *filename, + struct perf_session *session) +{ + int err = -1; + struct list_head *head; + struct machine *machine; + u16 misc; + struct dso *dso; + enum dso_kernel_type dso_type; + + machine = perf_session__findnew_machine(session, bev->pid); + if (!machine) + goto out; + + misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + switch (misc) { + case PERF_RECORD_MISC_KERNEL: + dso_type = DSO_TYPE_KERNEL; + head = &machine->kernel_dsos; + break; + case PERF_RECORD_MISC_GUEST_KERNEL: + dso_type = DSO_TYPE_GUEST_KERNEL; + head = &machine->kernel_dsos; + break; + case PERF_RECORD_MISC_USER: + case PERF_RECORD_MISC_GUEST_USER: + dso_type = DSO_TYPE_USER; + head = &machine->user_dsos; + break; + default: + goto out; + } + + dso = __dsos__findnew(head, filename); + if (dso != NULL) { + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + + dso__set_build_id(dso, &bev->build_id); + + if (filename[0] == '[') + dso->kernel = dso_type; + + build_id__sprintf(dso->build_id, sizeof(dso->build_id), + sbuild_id); + pr_debug("build id event received for %s: %s\n", + dso->long_name, sbuild_id); + } + + err = 0; +out: + return err; +} + +static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, + int input, u64 offset, u64 size) +{ + struct perf_session *session = container_of(header, struct perf_session, header); + struct { + struct perf_event_header header; + u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; + char filename[0]; + } old_bev; + struct build_id_event bev; + char filename[PATH_MAX]; + u64 limit = offset + size; + + while (offset < limit) { + ssize_t len; + + if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) + return -1; + + if (header->needs_swap) + perf_event_header__bswap(&old_bev.header); + + len = old_bev.header.size - sizeof(old_bev); + if (read(input, filename, len) != len) + return -1; + + bev.header = old_bev.header; + + /* + * As the pid is the missing value, we need to fill + * it properly. The header.misc value give us nice hint. + */ + bev.pid = HOST_KERNEL_ID; + if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || + bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) + bev.pid = DEFAULT_GUEST_KERNEL_ID; + + memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); + __event_process_build_id(&bev, filename, session); + + offset += bev.header.size; + } + + return 0; +} + +static int perf_header__read_build_ids(struct perf_header *header, + int input, u64 offset, u64 size) +{ + struct perf_session *session = container_of(header, struct perf_session, header); + struct build_id_event bev; + char filename[PATH_MAX]; + u64 limit = offset + size, orig_offset = offset; + int err = -1; + + while (offset < limit) { + ssize_t len; + + if (read(input, &bev, sizeof(bev)) != sizeof(bev)) + goto out; + + if (header->needs_swap) + perf_event_header__bswap(&bev.header); + + len = bev.header.size - sizeof(bev); + if (read(input, filename, len) != len) + goto out; + /* + * The a1645ce1 changeset: + * + * "perf: 'perf kvm' tool for monitoring guest performance from host" + * + * Added a field to struct build_id_event that broke the file + * format. + * + * Since the kernel build-id is the first entry, process the + * table using the old format if the well known + * '[kernel.kallsyms]' string for the kernel build-id has the + * first 4 characters chopped off (where the pid_t sits). + */ + if (memcmp(filename, "nel.kallsyms]", 13) == 0) { + if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) + return -1; + return perf_header__read_build_ids_abi_quirk(header, input, offset, size); + } + + __event_process_build_id(&bev, filename, session); + + offset += bev.header.size; + } + err = 0; +out: + return err; +} + struct feature_ops { int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); void (*print)(struct perf_header *h, int fd, FILE *fp); @@ -1735,156 +1885,6 @@ int perf_file_header__read(struct perf_file_header *header, return 0; } -static int __event_process_build_id(struct build_id_event *bev, - char *filename, - struct perf_session *session) -{ - int err = -1; - struct list_head *head; - struct machine *machine; - u16 misc; - struct dso *dso; - enum dso_kernel_type dso_type; - - machine = perf_session__findnew_machine(session, bev->pid); - if (!machine) - goto out; - - misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - - switch (misc) { - case PERF_RECORD_MISC_KERNEL: - dso_type = DSO_TYPE_KERNEL; - head = &machine->kernel_dsos; - break; - case PERF_RECORD_MISC_GUEST_KERNEL: - dso_type = DSO_TYPE_GUEST_KERNEL; - head = &machine->kernel_dsos; - break; - case PERF_RECORD_MISC_USER: - case PERF_RECORD_MISC_GUEST_USER: - dso_type = DSO_TYPE_USER; - head = &machine->user_dsos; - break; - default: - goto out; - } - - dso = __dsos__findnew(head, filename); - if (dso != NULL) { - char sbuild_id[BUILD_ID_SIZE * 2 + 1]; - - dso__set_build_id(dso, &bev->build_id); - - if (filename[0] == '[') - dso->kernel = dso_type; - - build_id__sprintf(dso->build_id, sizeof(dso->build_id), - sbuild_id); - pr_debug("build id event received for %s: %s\n", - dso->long_name, sbuild_id); - } - - err = 0; -out: - return err; -} - -static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, - int input, u64 offset, u64 size) -{ - struct perf_session *session = container_of(header, struct perf_session, header); - struct { - struct perf_event_header header; - u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; - char filename[0]; - } old_bev; - struct build_id_event bev; - char filename[PATH_MAX]; - u64 limit = offset + size; - - while (offset < limit) { - ssize_t len; - - if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) - return -1; - - if (header->needs_swap) - perf_event_header__bswap(&old_bev.header); - - len = old_bev.header.size - sizeof(old_bev); - if (read(input, filename, len) != len) - return -1; - - bev.header = old_bev.header; - - /* - * As the pid is the missing value, we need to fill - * it properly. The header.misc value give us nice hint. - */ - bev.pid = HOST_KERNEL_ID; - if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || - bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) - bev.pid = DEFAULT_GUEST_KERNEL_ID; - - memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); - __event_process_build_id(&bev, filename, session); - - offset += bev.header.size; - } - - return 0; -} - -static int perf_header__read_build_ids(struct perf_header *header, - int input, u64 offset, u64 size) -{ - struct perf_session *session = container_of(header, struct perf_session, header); - struct build_id_event bev; - char filename[PATH_MAX]; - u64 limit = offset + size, orig_offset = offset; - int err = -1; - - while (offset < limit) { - ssize_t len; - - if (read(input, &bev, sizeof(bev)) != sizeof(bev)) - goto out; - - if (header->needs_swap) - perf_event_header__bswap(&bev.header); - - len = bev.header.size - sizeof(bev); - if (read(input, filename, len) != len) - goto out; - /* - * The a1645ce1 changeset: - * - * "perf: 'perf kvm' tool for monitoring guest performance from host" - * - * Added a field to struct build_id_event that broke the file - * format. - * - * Since the kernel build-id is the first entry, process the - * table using the old format if the well known - * '[kernel.kallsyms]' string for the kernel build-id has the - * first 4 characters chopped off (where the pid_t sits). - */ - if (memcmp(filename, "nel.kallsyms]", 13) == 0) { - if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) - return -1; - return perf_header__read_build_ids_abi_quirk(header, input, offset, size); - } - - __event_process_build_id(&bev, filename, session); - - offset += bev.header.size; - } - err = 0; -out: - return err; -} - static int perf_file_section__process(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data __used) From f1c67db7e351bf9fd328e368ba045cbeca11d513 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 10 Feb 2012 15:41:56 +0100 Subject: [PATCH 15/18] perf tools: Factor out feature op to process header sections There is individual code for each feature to process header sections. Adding a function pointer .process to struct feature_ops for keeping the implementation in separate functions. Code to process header sections is now a generic function. Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1328884916-5901-2-git-send-email-robert.richter@amd.com Signed-off-by: Robert Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 44 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 5bb2d752ab8f49..9f867d96c6a5e5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1466,25 +1466,48 @@ static int perf_header__read_build_ids(struct perf_header *header, return err; } +static int process_trace_info(struct perf_file_section *section __unused, + struct perf_header *ph __unused, + int feat __unused, int fd) +{ + trace_report(fd, false); + return 0; +} + +static int process_build_id(struct perf_file_section *section, + struct perf_header *ph, + int feat __unused, int fd) +{ + if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) + pr_debug("Failed to read buildids, continuing...\n"); + return 0; +} + struct feature_ops { int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); void (*print)(struct perf_header *h, int fd, FILE *fp); + int (*process)(struct perf_file_section *section, + struct perf_header *h, int feat, int fd); const char *name; bool full_only; }; #define FEAT_OPA(n, func) \ [n] = { .name = #n, .write = write_##func, .print = print_##func } +#define FEAT_OPP(n, func) \ + [n] = { .name = #n, .write = write_##func, .print = print_##func, \ + .process = process_##func } #define FEAT_OPF(n, func) \ - [n] = { .name = #n, .write = write_##func, .print = print_##func, .full_only = true } + [n] = { .name = #n, .write = write_##func, .print = print_##func, \ + .full_only = true } /* feature_ops not implemented: */ #define print_trace_info NULL #define print_build_id NULL static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { - FEAT_OPA(HEADER_TRACE_INFO, trace_info), - FEAT_OPA(HEADER_BUILD_ID, build_id), + FEAT_OPP(HEADER_TRACE_INFO, trace_info), + FEAT_OPP(HEADER_BUILD_ID, build_id), FEAT_OPA(HEADER_HOSTNAME, hostname), FEAT_OPA(HEADER_OSRELEASE, osrelease), FEAT_OPA(HEADER_VERSION, version), @@ -1900,19 +1923,10 @@ static int perf_file_section__process(struct perf_file_section *section, return 0; } - switch (feat) { - case HEADER_TRACE_INFO: - trace_report(fd, false); - break; - case HEADER_BUILD_ID: - if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) - pr_debug("Failed to read buildids, continuing...\n"); - break; - default: - break; - } + if (!feat_ops[feat].process) + return 0; - return 0; + return feat_ops[feat].process(section, ph, feat, fd); } static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, From 7e1ccd3804281fc0755eb726b654469c40a96d89 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 9 Feb 2012 16:12:38 +0100 Subject: [PATCH 16/18] perf tools: cleanup initialization of attr->size The perf_event_attr size needs to be initialized in all cases because it captures the ABI version. This patch moves the initialization of the field from the perf_event_open() syscall stub to its proper location in the event_attr_init(). Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120209151238.GA10272@quad Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/perf.h | 1 - tools/perf/util/util.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/perf.h b/tools/perf/perf.h index deb17dba4a5b42..03a045618148ec 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -167,7 +167,6 @@ sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { - attr->size = sizeof(*attr); return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index fb25d132921882..8109a907841e63 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -14,6 +14,8 @@ void event_attr_init(struct perf_event_attr *attr) attr->exclude_host = 1; if (!perf_guest) attr->exclude_guest = 1; + /* to capture ABI version */ + attr->size = sizeof(*attr); } int mkdir_p(char *path, mode_t mode) From 0c9781280fb672ca09c997df3f14ba506bbdb977 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 14 Feb 2012 14:05:30 -0200 Subject: [PATCH 17/18] perf tools: Handle kernels that don't support attr.exclude_{guest,host} Just fall back to resetting those fields, if set, warning the user that that feature is not available. If guest samples appear they will just be discarded because no struct machine will be found and thus the event will be accounted as not handled and dropped, see 0c09571. Reported-by: Namhyung Kim Tested-by: Joerg Roedel Cc: David Ahern Cc: Frederic Weisbecker Cc: Joerg Roedel Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-vuwxig36mzprl5n7nzvnxxsh@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 29 ++++++++++++++++++++--------- tools/perf/builtin-top.c | 23 +++++++++++++++++------ tools/perf/perf.h | 1 + tools/perf/util/top.h | 1 + 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 08ed24b66ffe4b..d6c10e8ff6b6d5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -205,6 +205,9 @@ static void perf_record__open(struct perf_record *rec) if (opts->group && pos != first) group_fd = first->fd; +fallback_missing_features: + if (opts->exclude_guest_missing) + attr->exclude_guest = attr->exclude_host = 0; retry_sample_id: attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; try_again: @@ -218,15 +221,23 @@ static void perf_record__open(struct perf_record *rec) } else if (err == ENODEV && opts->cpu_list) { die("No such device - did you specify" " an out-of-range profile CPU?\n"); - } else if (err == EINVAL && opts->sample_id_all_avail) { - /* - * Old kernel, no attr->sample_id_type_all field - */ - opts->sample_id_all_avail = false; - if (!opts->sample_time && !opts->raw_samples && !time_needed) - attr->sample_type &= ~PERF_SAMPLE_TIME; - - goto retry_sample_id; + } else if (err == EINVAL) { + if (!opts->exclude_guest_missing && + (attr->exclude_guest || attr->exclude_host)) { + pr_debug("Old kernel, cannot exclude " + "guest or host samples.\n"); + opts->exclude_guest_missing = true; + goto fallback_missing_features; + } else if (opts->sample_id_all_avail) { + /* + * Old kernel, no attr->sample_id_type_all field + */ + opts->sample_id_all_avail = false; + if (!opts->sample_time && !opts->raw_samples && !time_needed) + attr->sample_type &= ~PERF_SAMPLE_TIME; + + goto retry_sample_id; + } } /* diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5a88c0dadf1554..02e11ff5a72906 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -872,6 +872,9 @@ static void perf_top__start_counters(struct perf_top *top) attr->mmap = 1; attr->comm = 1; attr->inherit = top->inherit; +fallback_missing_features: + if (top->exclude_guest_missing) + attr->exclude_guest = attr->exclude_host = 0; retry_sample_id: attr->sample_id_all = top->sample_id_all_avail ? 1 : 0; try_again: @@ -883,12 +886,20 @@ static void perf_top__start_counters(struct perf_top *top) if (err == EPERM || err == EACCES) { ui__error_paranoid(); goto out_err; - } else if (err == EINVAL && top->sample_id_all_avail) { - /* - * Old kernel, no attr->sample_id_type_all field - */ - top->sample_id_all_avail = false; - goto retry_sample_id; + } else if (err == EINVAL) { + if (!top->exclude_guest_missing && + (attr->exclude_guest || attr->exclude_host)) { + pr_debug("Old kernel, cannot exclude " + "guest or host samples.\n"); + top->exclude_guest_missing = true; + goto fallback_missing_features; + } else if (top->sample_id_all_avail) { + /* + * Old kernel, no attr->sample_id_type_all field + */ + top->sample_id_all_avail = false; + goto retry_sample_id; + } } /* * If it's cycles then fall back to hrtimer diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 03a045618148ec..8b9c43635f4dfa 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -199,6 +199,7 @@ struct perf_record_opts { bool sample_address; bool sample_time; bool sample_id_all_avail; + bool exclude_guest_missing; bool system_wide; bool period; unsigned int freq; diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 49eb8481f19fd5..7dea8912aa9722 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -35,6 +35,7 @@ struct perf_top { bool inherit; bool group; bool sample_id_all_avail; + bool exclude_guest_missing; bool dump_symtab; const char *cpu_list; struct hist_entry *sym_filter_entry; From 808e122630d45a7f036d25582474d70548a87e2c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 14 Feb 2012 14:18:57 -0200 Subject: [PATCH 18/18] perf tools: Invert the sample_id_all logic Instead of requiring that users of perf_record_opts set .sample_id_all_avail to true, just invert the logic, using .sample_id_all_missing, that doesn't need to be explicitely initialized since gcc will zero members ommitted in a struct initialization. Just like the newly introduced .exclude_{guest,host} feature test. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ab772uzk78cwybihf0vt7kxw@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 7 +++---- tools/perf/builtin-test.c | 1 - tools/perf/builtin-top.c | 7 +++---- tools/perf/perf.h | 2 +- tools/perf/util/evsel.c | 4 ++-- tools/perf/util/top.h | 2 +- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d6c10e8ff6b6d5..75d230fef20262 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -209,7 +209,7 @@ static void perf_record__open(struct perf_record *rec) if (opts->exclude_guest_missing) attr->exclude_guest = attr->exclude_host = 0; retry_sample_id: - attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; + attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; try_again: if (perf_evsel__open(pos, evlist->cpus, evlist->threads, opts->group, group_fd) < 0) { @@ -228,11 +228,11 @@ static void perf_record__open(struct perf_record *rec) "guest or host samples.\n"); opts->exclude_guest_missing = true; goto fallback_missing_features; - } else if (opts->sample_id_all_avail) { + } else if (!opts->sample_id_all_missing) { /* * Old kernel, no attr->sample_id_type_all field */ - opts->sample_id_all_avail = false; + opts->sample_id_all_missing = true; if (!opts->sample_time && !opts->raw_samples && !time_needed) attr->sample_type &= ~PERF_SAMPLE_TIME; @@ -660,7 +660,6 @@ static struct perf_record record = { .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, .freq = 1000, - .sample_id_all_avail = true, }, .write_mode = WRITE_FORCE, .file_new = true, diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 0f151952a77010..3e087ce8daa63f 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -1013,7 +1013,6 @@ static int test__PERF_RECORD(void) .no_delay = true, .freq = 10, .mmap_pages = 256, - .sample_id_all_avail = true, }; cpu_set_t *cpu_mask = NULL; size_t cpu_mask_size = 0; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 02e11ff5a72906..e3c63aef8efc6a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -876,7 +876,7 @@ static void perf_top__start_counters(struct perf_top *top) if (top->exclude_guest_missing) attr->exclude_guest = attr->exclude_host = 0; retry_sample_id: - attr->sample_id_all = top->sample_id_all_avail ? 1 : 0; + attr->sample_id_all = top->sample_id_all_missing ? 0 : 1; try_again: if (perf_evsel__open(counter, top->evlist->cpus, top->evlist->threads, top->group, @@ -893,11 +893,11 @@ static void perf_top__start_counters(struct perf_top *top) "guest or host samples.\n"); top->exclude_guest_missing = true; goto fallback_missing_features; - } else if (top->sample_id_all_avail) { + } else if (!top->sample_id_all_missing) { /* * Old kernel, no attr->sample_id_type_all field */ - top->sample_id_all_avail = false; + top->sample_id_all_missing = true; goto retry_sample_id; } } @@ -1122,7 +1122,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) .delay_secs = 2, .uid = UINT_MAX, .freq = 1000, /* 1 KHz */ - .sample_id_all_avail = true, .mmap_pages = 128, .sym_pcnt_filter = 5, }; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 8b9c43635f4dfa..f0227e93665d07 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -198,7 +198,7 @@ struct perf_record_opts { bool raw_samples; bool sample_address; bool sample_time; - bool sample_id_all_avail; + bool sample_id_all_missing; bool exclude_guest_missing; bool system_wide; bool period; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f910f50136d014..302d49a9f9855e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -68,7 +68,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->sample_id_all = opts->sample_id_all_avail ? 1 : 0; + attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; attr->inherit = !opts->no_inherit; attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | @@ -111,7 +111,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) if (opts->period) attr->sample_type |= PERF_SAMPLE_PERIOD; - if (opts->sample_id_all_avail && + if (!opts->sample_id_all_missing && (opts->sample_time || opts->system_wide || !opts->no_inherit || opts->cpu_list)) attr->sample_type |= PERF_SAMPLE_TIME; diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 7dea8912aa9722..ce61cb2d1acf61 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -34,7 +34,7 @@ struct perf_top { bool vmlinux_warned; bool inherit; bool group; - bool sample_id_all_avail; + bool sample_id_all_missing; bool exclude_guest_missing; bool dump_symtab; const char *cpu_list;