From d4897e1935552663030fe7681a53eccc58d6aebd Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 24 Jun 2016 13:41:25 -0700 Subject: [PATCH 01/11] perf tools: Add documentation for perf.data on disk format Add some documentation for the on disk format of perf.data. This is not documenting the actual perf events -- which are documented in perf_event.h -- but just the additional headers that perf record adds around them when writing the data to disk. Signed-off-by: Andi Kleen Cc: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1466800885-12974-1-git-send-email-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- .../Documentation/perf.data-file-format.txt | 442 ++++++++++++++++++ 1 file changed, 442 insertions(+) create mode 100644 tools/perf/Documentation/perf.data-file-format.txt diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt new file mode 100644 index 000000000000..fdc99fe6bbc3 --- /dev/null +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -0,0 +1,442 @@ +perf.data format + +Uptodate as of v4.7 + +This document describes the on-disk perf.data format, generated by perf record +or perf inject and consumed by the other perf tools. + +On a high level perf.data contains the events generated by the PMUs, plus metadata. + +All fields are in native-endian of the machine that generated the perf.data. + +When perf is writing to a pipe it uses a special version of the file +format that does not rely on seeking to adjust data offsets. This +format is not described here. The pipe version can be converted to +normal perf.data with perf inject. + +The file starts with a perf_header: + +struct perf_header { + char magic[8]; /* PERFILE2 */ + uint64_t size; /* size of the header */ + uint64_t attr_size; /* size of an attribute in attrs */ + struct perf_file_section attrs; + struct perf_file_section data; + struct perf_file_section event_types; + uint64_t flags; + uint64_t flags1[3]; +}; + +The magic number identifies the perf file and the version. Current perf versions +use PERFILE2. Old perf versions generated a version 1 format (PERFFILE). Version 1 +is not described here. The magic number also identifies the endian. When the +magic value is 64bit byte swapped compared the file is in non-native +endian. + +A perf_file_section contains a pointer to another section of the perf file. +The header contains three such pointers: for attributes, data and event types. + +struct perf_file_section { + uint64_t offset; /* offset from start of file */ + uint64_t size; /* size of the section */ +}; + +Flags section: + +The header is followed by different optional headers, described by the bits set +in flags. Only headers for which the bit is set are included. Each header +consists of a perf_file_section located after the initial header. +The respective perf_file_section points to the data of the additional +header and defines its size. + +Some headers consist of strings, which are defined like this: + +struct perf_header_string { + uint32_t len; + char string[len]; /* zero terminated */ +}; + +Some headers consist of a sequence of strings, which start with a + +struct perf_header_string_list { + uint32_t nr; + struct perf_header_string strings[nr]; /* variable length records */ +}; + +The bits are the flags bits in a 256 bit bitmap starting with +flags. These define the valid bits: + + HEADER_RESERVED = 0, /* always cleared */ + HEADER_FIRST_FEATURE = 1, + HEADER_TRACING_DATA = 1, + +Describe me. + + HEADER_BUILD_ID = 2, + +The header consists of an sequence of build_id_event. The size of each record +is defined by header.size (see perf_event.h). Each event defines a ELF build id +for a executable file name for a pid. An ELF build id is a unique identifier +assigned by the linker to an executable. + +struct build_id_event { + struct perf_event_header header; + pid_t pid; + uint8_t build_id[24]; + char filename[header.size - offsetof(struct build_id_event, filename)]; +}; + + HEADER_HOSTNAME = 3, + +A perf_header_string with the hostname where the data was collected +(uname -n) + + HEADER_OSRELEASE = 4, + +A perf_header_string with the os release where the data was collected +(uname -r) + + HEADER_VERSION = 5, + +A perf_header_string with the perf user tool version where the +data was collected. This is the same as the version of the source tree +the perf tool was built from. + + HEADER_ARCH = 6, + +A perf_header_string with the CPU architecture (uname -m) + + HEADER_NRCPUS = 7, + +A structure defining the number of CPUs. + +struct nr_cpus { + uint32_t nr_cpus_online; + uint32_t nr_cpus_available; /* CPUs not yet onlined */ +}; + + HEADER_CPUDESC = 8, + +A perf_header_string with description of the CPU. On x86 this is the model name +in /proc/cpuinfo + + HEADER_CPUID = 9, + +A perf_header_string with the exact CPU type. On x86 this is +vendor,family,model,stepping. For example: GenuineIntel,6,69,1 + + HEADER_TOTAL_MEM = 10, + +An uint64_t with the total memory in bytes. + + HEADER_CMDLINE = 11, + +A perf_header_string with the perf command line used to collect the data. + + HEADER_EVENT_DESC = 12, + +Another description of the perf_event_attrs, more detailed than header.attrs +including IDs and names. See perf_event.h or the man page for a description +of a struct perf_event_attr. + +struct { + uint32_t nr; /* number of events */ + uint32_t attr_size; /* size of each perf_event_attr */ + struct { + struct perf_event_attr attr; /* size of attr_size */ + uint32_t nr_ids; + struct perf_header_string event_string; + uint64_t ids[nr_ids]; + } events[nr]; /* Variable length records */ +}; + + HEADER_CPU_TOPOLOGY = 13, + +String lists defining the core and CPU threads topology. + +struct { + struct perf_header_string_list cores; /* Variable length */ + struct perf_header_string_list threads; /* Variable length */ +}; + +Example: + sibling cores : 0-3 + sibling threads : 0-1 + sibling threads : 2-3 + + HEADER_NUMA_TOPOLOGY = 14, + + A list of NUMA node descriptions + +struct { + uint32_t nr; + struct { + uint32_t nodenr; + uint64_t mem_total; + uint64_t mem_free; + struct perf_header_string cpus; + } nodes[nr]; /* Variable length records */ +}; + + HEADER_BRANCH_STACK = 15, + +Not implemented in perf. + + HEADER_PMU_MAPPINGS = 16, + + A list of PMU structures, defining the different PMUs supported by perf. + +struct { + uint32_t nr; + struct pmu { + uint32_t pmu_type; + struct perf_header_string pmu_name; + } [nr]; /* Variable length records */ +}; + + HEADER_GROUP_DESC = 17, + + Description of counter groups ({...} in perf syntax) + +struct { + uint32_t nr; + struct { + struct perf_header_string string; + uint32_t leader_idx; + uint32_t nr_members; + } [nr]; /* Variable length records */ +}; + + HEADER_AUXTRACE = 18, + +Define additional auxtrace areas in the perf.data. auxtrace is used to store +undecoded hardware tracing information, such as Intel Processor Trace data. + +/** + * struct auxtrace_index_entry - indexes a AUX area tracing event within a + * perf.data file. + * @file_offset: offset within the perf.data file + * @sz: size of the event + */ +struct auxtrace_index_entry { + u64 file_offset; + u64 sz; +}; + +#define PERF_AUXTRACE_INDEX_ENTRY_COUNT 256 + +/** + * struct auxtrace_index - index of AUX area tracing events within a perf.data + * file. + * @list: linking a number of arrays of entries + * @nr: number of entries + * @entries: array of entries + */ +struct auxtrace_index { + struct list_head list; + size_t nr; + struct auxtrace_index_entry entries[PERF_AUXTRACE_INDEX_ENTRY_COUNT]; +}; + + other bits are reserved and should ignored for now + HEADER_FEAT_BITS = 256, + +Attributes + +This is an array of perf_event_attrs, each attr_size bytes long, which defines +each event collected. See perf_event.h or the man page for a detailed +description. + +Data + +This section is the bulk of the file. It consist of a stream of perf_events +describing events. This matches the format generated by the kernel. +See perf_event.h or the manpage for a detailed description. + +Some notes on parsing: + +Ordering + +The events are not necessarily in time stamp order, as they can be +collected in parallel on different CPUs. If the events should be +processed in time order they need to be sorted first. It is possible +to only do a partial sort using the FINISHED_ROUND event header (see +below). perf record guarantees that there is no reordering over a +FINISHED_ROUND. + +ID vs IDENTIFIER + +When the event stream contains multiple events each event is identified +by an ID. This can be either through the PERF_SAMPLE_ID or the +PERF_SAMPLE_IDENTIFIER header. The PERF_SAMPLE_IDENTIFIER header is +at a fixed offset from the event header, which allows reliable +parsing of the header. Relying on ID may be ambigious. +IDENTIFIER is only supported by newer Linux kernels. + +Perf record specific events: + +In addition to the kernel generated event types perf record adds its +own event types (in addition it also synthesizes some kernel events, +for example MMAP events) + + PERF_RECORD_USER_TYPE_START = 64, + PERF_RECORD_HEADER_ATTR = 64, + +struct attr_event { + struct perf_event_header header; + struct perf_event_attr attr; + uint64_t id[]; +}; + + PERF_RECORD_HEADER_EVENT_TYPE = 65, /* depreceated */ + +#define MAX_EVENT_NAME 64 + +struct perf_trace_event_type { + uint64_t event_id; + char name[MAX_EVENT_NAME]; +}; + +struct event_type_event { + struct perf_event_header header; + struct perf_trace_event_type event_type; +}; + + + PERF_RECORD_HEADER_TRACING_DATA = 66, + +Describe me + +struct tracing_data_event { + struct perf_event_header header; + uint32_t size; +}; + + PERF_RECORD_HEADER_BUILD_ID = 67, + +Define a ELF build ID for a referenced executable. + + struct build_id_event; /* See above */ + + PERF_RECORD_FINISHED_ROUND = 68, + +No event reordering over this header. No payload. + + PERF_RECORD_ID_INDEX = 69, + +Map event ids to CPUs and TIDs. + +struct id_index_entry { + uint64_t id; + uint64_t idx; + uint64_t cpu; + uint64_t tid; +}; + +struct id_index_event { + struct perf_event_header header; + uint64_t nr; + struct id_index_entry entries[nr]; +}; + + PERF_RECORD_AUXTRACE_INFO = 70, + +Auxtrace type specific information. Describe me + +struct auxtrace_info_event { + struct perf_event_header header; + uint32_t type; + uint32_t reserved__; /* For alignment */ + uint64_t priv[]; +}; + + PERF_RECORD_AUXTRACE = 71, + +Defines auxtrace data. Followed by the actual data. The contents of +the auxtrace data is dependent on the event and the CPU. For example +for Intel Processor Trace it contains Processor Trace data generated +by the CPU. + +struct auxtrace_event { + struct perf_event_header header; + uint64_t size; + uint64_t offset; + uint64_t reference; + uint32_t idx; + uint32_t tid; + uint32_t cpu; + uint32_t reserved__; /* For alignment */ +}; + +struct aux_event { + struct perf_event_header header; + uint64_t aux_offset; + uint64_t aux_size; + uint64_t flags; +}; + + PERF_RECORD_AUXTRACE_ERROR = 72, + +Describes an error in hardware tracing + +enum auxtrace_error_type { + PERF_AUXTRACE_ERROR_ITRACE = 1, + PERF_AUXTRACE_ERROR_MAX +}; + +#define MAX_AUXTRACE_ERROR_MSG 64 + +struct auxtrace_error_event { + struct perf_event_header header; + uint32_t type; + uint32_t code; + uint32_t cpu; + uint32_t pid; + uint32_t tid; + uint32_t reserved__; /* For alignment */ + uint64_t ip; + char msg[MAX_AUXTRACE_ERROR_MSG]; +}; + +Event types + +Define the event attributes with their IDs. + +An array bound by the perf_file_section size. + + struct { + struct perf_event_attr attr; /* Size defined by header.attr_size */ + struct perf_file_section ids; + } + +ids points to a array of uint64_t defining the ids for event attr attr. + +References: + +include/uapi/linux/perf_event.h + +This is the canonical description of the kernel generated perf_events +and the perf_event_attrs. + +perf_events manpage + +A manpage describing perf_event and perf_event_attr is here: +http://web.eece.maine.edu/~vweaver/projects/perf_events/programming.html +This tends to be slightly behind the kernel include, but has better +descriptions. An (typically older) version of the man page may be +included with the standard Linux man pages, available with "man +perf_events" + +pmu-tools + +https://github.com/andikleen/pmu-tools/tree/master/parser + +A definition of the perf.data format in python "construct" format is available +in pmu-tools parser. This allows to read perf.data from python and dump it. + +quipper + +The quipper C++ parser is available at +https://chromium.googlesource.com/chromiumos/platform/chromiumos-wide-profiling/ +Unfortunately this parser tends to be many versions behind and may not be able +to parse data files generated by recent perf. From de8a63bd5076761fad4e236c93350fdf297708be Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Jun 2016 13:23:37 +0100 Subject: [PATCH 02/11] tools lib bpf: Fix spelling mistake: "missmatch" -> "mismatch" Trivial fix to spelling mistake Signed-off-by: Colin King Acked-by: Wang Nan Cc: Alexei Starovoitov Cc: He Kuang Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1467116617-8318-1-git-send-email-colin.king@canonical.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/libbpf.c | 2 +- tools/lib/bpf/libbpf.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 462e526a4465..a7cb40abe634 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -71,7 +71,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf", [ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid", [ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost", - [ERRCODE_OFFSET(ENDIAN)] = "Endian missmatch", + [ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch", [ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf", [ERRCODE_OFFSET(RELOC)] = "Relocation failed", [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading", diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 722f46b2d553..148df3640ba0 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -19,7 +19,7 @@ enum libbpf_errno { LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START, LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */ LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */ - LIBBPF_ERRNO__ENDIAN, /* Endian missmatch */ + LIBBPF_ERRNO__ENDIAN, /* Endian mismatch */ LIBBPF_ERRNO__INTERNAL, /* Internal error in libbpf */ LIBBPF_ERRNO__RELOC, /* Relocation failed */ LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */ From f4e47f9f7b0bcbb1069b93bd719a1d34fb37d933 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 30 Jun 2016 11:44:19 +0530 Subject: [PATCH 03/11] perf evsel: Utility function to fetch arch Add Utility function to fetch arch using evsel. (evsel->env->arch) Signed-off-by: Ravi Bangoria Cc: Ananth N Mavinakayanahalli Cc: Anton Blanchard Cc: Daniel Axtens Cc: David Laight Cc: Michael Ellerman Cc: Naveen N. Rao Link: http://lkml.kernel.org/r/1467267262-4589-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 7 +++++++ tools/perf/util/evsel.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1d8f2bbd38a7..0fea724e735c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2422,3 +2422,10 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, err, strerror_r(err, sbuf, sizeof(sbuf)), perf_evsel__name(evsel)); } + +char *perf_evsel__env_arch(struct perf_evsel *evsel) +{ + if (evsel && evsel->evlist && evsel->evlist->env) + return evsel->evlist->env->arch; + return NULL; +} diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 828ddd1c8947..86fed7a2932b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -435,4 +435,6 @@ typedef int (*attr__fprintf_f)(FILE *, const char *, const char *, void *); int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, attr__fprintf_f attr__fprintf, void *priv); +char *perf_evsel__env_arch(struct perf_evsel *evsel); + #endif /* __PERF_EVSEL_H */ From 53dd9b5f95dda95bcadda1b4680be42dfe1f9e5e Mon Sep 17 00:00:00 2001 From: "Peter Zijlstra (Intel)" Date: Thu, 30 Jun 2016 09:17:26 -0300 Subject: [PATCH 04/11] perf annotate: Simplify header dotted line sizing No need to use strlen, etc to figure that out, just use the return from printf(), it will tell how wide the following line needs to be. Signed-off-by: Peter Zijlstra (Intel) Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20160630082955.GA30921@twins.programming.kicks-ass.net [ split from a larger patch ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index c385fecb9d32..78e5d6f3ff61 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1528,7 +1528,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int more = 0; u64 len; int width = 8; - int namelen, evsel_name_len, graph_dotted_len; + int graph_dotted_len; filename = strdup(dso->long_name); if (!filename) @@ -1540,17 +1540,14 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, d_filename = basename(filename); len = symbol__size(sym); - namelen = strlen(d_filename); - evsel_name_len = strlen(evsel_name); if (perf_evsel__is_group_event(evsel)) width *= evsel->nr_members; - printf(" %-*.*s| Source code & Disassembly of %s for %s\n", + graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s\n", width, width, "Percent", d_filename, evsel_name); - graph_dotted_len = width + namelen + evsel_name_len; - printf("-%-*.*s-----------------------------------------\n", + printf("%-*.*s----\n", graph_dotted_len, graph_dotted_len, graph_dotted_line); if (verbose) From 135cce1bf12bd30d7d66360022f9dac6ea3a07cd Mon Sep 17 00:00:00 2001 From: "Peter Zijlstra (Intel)" Date: Thu, 30 Jun 2016 10:29:55 +0200 Subject: [PATCH 05/11] perf annotate: Add number of samples to the header Staring at annotations of large functions is useless if there's only a few samples in them. Report the number of samples in the header to make this easier to determine. Committer note: The change amounts to: - Percent | Source code & Disassembly of perf-vdso.so for cycles:u ------------------------------------------------------------------ + Percent | Source code & Disassembly of perf-vdso.so for cycles:u (3278 samples) +-------------------------------------------------------------------------------- Signed-off-by: Peter Zijlstra (Intel) Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20160630082955.GA30921@twins.programming.kicks-ass.net [ split from a larger patch ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 78e5d6f3ff61..e9825fe825fd 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1522,6 +1522,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, const char *d_filename; const char *evsel_name = perf_evsel__name(evsel); struct annotation *notes = symbol__annotation(sym); + struct sym_hist *h = annotation__histogram(notes, evsel->idx); struct disasm_line *pos, *queue = NULL; u64 start = map__rip_2objdump(map, sym->start); int printed = 2, queue_len = 0; @@ -1544,8 +1545,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, if (perf_evsel__is_group_event(evsel)) width *= evsel->nr_members; - graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s\n", - width, width, "Percent", d_filename, evsel_name); + graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s (%" PRIu64 " samples)\n", + width, width, "Percent", d_filename, evsel_name, h->sum); printf("%-*.*s----\n", graph_dotted_len, graph_dotted_len, graph_dotted_line); From a41af25b3c317331743dc53defaa3f2e03d255cb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 30 Jun 2016 11:57:33 -0300 Subject: [PATCH 06/11] perf trace beauty sched_policy: Define SCHED_RESET_ON_FORK for older systems RHEL5 for instance doesn't have this one, help it. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-3adewnii78zi110eovfciopy@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/sched_policy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/trace/beauty/sched_policy.c b/tools/perf/trace/beauty/sched_policy.c index c205bc608b3c..34775295b9b3 100644 --- a/tools/perf/trace/beauty/sched_policy.c +++ b/tools/perf/trace/beauty/sched_policy.c @@ -9,6 +9,9 @@ #ifndef SCHED_DEADLINE #define SCHED_DEADLINE 6 #endif +#ifndef SCHED_RESET_ON_FORK +#define SCHED_RESET_ON_FORK 0x40000000 +#endif static size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size, struct syscall_arg *arg) From 3be28870c05ad09dfff4dbefe71b02aba5dba569 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 30 Jun 2016 12:00:58 -0300 Subject: [PATCH 07/11] perf trace beauty eventfd: No need to include eventfd.h Old systems such as RHEL5 lack this file, and what we need is already under ifdefs, so just ditch this #include. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-dzbjfllw6znuoy37skwnwa4r@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/eventfd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/perf/trace/beauty/eventfd.c b/tools/perf/trace/beauty/eventfd.c index d64f4a9128a1..b08f21eb6f4d 100644 --- a/tools/perf/trace/beauty/eventfd.c +++ b/tools/perf/trace/beauty/eventfd.c @@ -1,5 +1,3 @@ -#include - #ifndef EFD_SEMAPHORE #define EFD_SEMAPHORE 1 #endif From f3069249e9e6b0ce303c3547dfa2960ee2e95b61 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 28 Jun 2016 13:29:02 +0200 Subject: [PATCH 08/11] perf tools: Allow to reset open files counter I hit a bug when running test suite without forking each test (-F option): $ perf test -F dso 8: Test dso data read : Ok 9: Test dso data cache : FAILED! 10: Test dso data reopen : FAILED! The reason the session file limit is set just once for perf process so we need to reset it for each test, otherwise wrong limit is taken into account. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Nilay Vaish Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1467113345-12669-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/dso-data.c | 6 ++++++ tools/perf/util/dso.c | 22 ++++++++++++++++------ tools/perf/util/dso.h | 2 ++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 8cf0d9e189a8..13725e09ba22 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -251,6 +251,9 @@ int test__dso_data_cache(int subtest __maybe_unused) long nr_end, nr = open_files_cnt(); int dso_cnt, limit, i, fd; + /* Rest the internal dso open counter limit. */ + reset_fd_limit(); + memset(&machine, 0, sizeof(machine)); /* set as system limit */ @@ -312,6 +315,9 @@ int test__dso_data_reopen(int subtest __maybe_unused) #define dso_1 (dsos[1]) #define dso_2 (dsos[2]) + /* Rest the internal dso open counter limit. */ + reset_fd_limit(); + memset(&machine, 0, sizeof(machine)); /* diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 5d286f5d7906..e1de6cc4863e 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -442,17 +442,27 @@ static rlim_t get_fd_limit(void) return limit; } +static rlim_t fd_limit; + +/* + * Used only by tests/dso-data.c to reset the environment + * for tests. I dont expect we should change this during + * standard runtime. + */ +void reset_fd_limit(void) +{ + fd_limit = 0; +} + static bool may_cache_fd(void) { - static rlim_t limit; + if (!fd_limit) + fd_limit = get_fd_limit(); - if (!limit) - limit = get_fd_limit(); - - if (limit == RLIM_INFINITY) + if (fd_limit == RLIM_INFINITY) return true; - return limit > (rlim_t) dso__data_open_cnt; + return fd_limit > (rlim_t) dso__data_open_cnt; } /* diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 76d79d070e21..a571f24895ca 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -360,4 +360,6 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine); int dso__strerror_load(struct dso *dso, char *buf, size_t buflen); +void reset_fd_limit(void); + #endif /* __PERF_DSO */ From 8fbc38aaaf542433ef195e5fae704c56d1c071e6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 28 Jun 2016 13:29:03 +0200 Subject: [PATCH 09/11] perf tests: Fix thread map test for -F option I hit a bug when running test suite without forking each test (-F option): $ perf test -Fv ... 34: Test thread map : --- start --- FAILED tests/thread-map.c:24 wrong comm ---- end ---- Test thread map: FAILED! The reason was the process name wasn't 'perf' as expected by the test, because other tests set the name as well. Setting it explicitly now. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Nilay Vaish Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1467113345-12669-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/thread-map.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index fccde848fe9c..cee2a2cdc933 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -1,13 +1,20 @@ #include #include +#include #include "tests.h" #include "thread_map.h" #include "debug.h" +#define NAME (const char *) "perf" +#define NAMEUL (unsigned long) NAME + int test__thread_map(int subtest __maybe_unused) { struct thread_map *map; + TEST_ASSERT_VAL("failed to set process name", + !prctl(PR_SET_NAME, NAMEUL, 0, 0, 0)); + /* test map on current pid */ map = thread_map__new_by_pid(getpid()); TEST_ASSERT_VAL("failed to alloc map", map); @@ -19,7 +26,7 @@ int test__thread_map(int subtest __maybe_unused) thread_map__pid(map, 0) == getpid()); TEST_ASSERT_VAL("wrong comm", thread_map__comm(map, 0) && - !strcmp(thread_map__comm(map, 0), "perf")); + !strcmp(thread_map__comm(map, 0), NAME)); TEST_ASSERT_VAL("wrong refcnt", atomic_read(&map->refcnt) == 1); thread_map__put(map); @@ -51,7 +58,7 @@ static int process_event(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong nr", map->nr == 1); TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid()); - TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf")); + TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, NAME)); threads = thread_map__new_event(&event->thread_map); TEST_ASSERT_VAL("failed to alloc map", threads); @@ -61,7 +68,7 @@ static int process_event(struct perf_tool *tool __maybe_unused, thread_map__pid(threads, 0) == getpid()); TEST_ASSERT_VAL("wrong comm", thread_map__comm(threads, 0) && - !strcmp(thread_map__comm(threads, 0), "perf")); + !strcmp(thread_map__comm(threads, 0), NAME)); TEST_ASSERT_VAL("wrong refcnt", atomic_read(&threads->refcnt) == 1); thread_map__put(threads); @@ -72,6 +79,9 @@ int test__thread_map_synthesize(int subtest __maybe_unused) { struct thread_map *threads; + TEST_ASSERT_VAL("failed to set process name", + !prctl(PR_SET_NAME, NAMEUL, 0, 0, 0)); + /* test map on current pid */ threads = thread_map__new_by_pid(getpid()); TEST_ASSERT_VAL("failed to alloc map", threads); From 7fa9b8fba0b55edd1ff5b8ea696ec75fc5f6194c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 28 Jun 2016 13:29:01 +0200 Subject: [PATCH 10/11] perf test: Add -F/--dont-fork option Adding -F/--dont-fork option to bypass forking for each test. It's useful for debugging test. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Nilay Vaish Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1467113345-12669-1-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-test.txt | 4 ++ tools/perf/tests/builtin-test.c | 53 ++++++++++++++++---------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 31a5c3ea7f74..b329c65d7f40 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt @@ -30,3 +30,7 @@ OPTIONS -v:: --verbose:: Be more verbose. + +-F:: +--dont-fork:: + Do not fork child for each test, run all tests within single process. diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 0e95c20ecf6e..5781c1640eae 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -14,6 +14,8 @@ #include #include "symbol.h" +static bool dont_fork; + struct test __weak arch_tests[] = { { .func = NULL, @@ -247,7 +249,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const char static int run_test(struct test *test, int subtest) { - int status, err = -1, child = fork(); + int status, err = -1, child = dont_fork ? 0 : fork(); char sbuf[STRERR_BUFSIZE]; if (child < 0) { @@ -257,34 +259,41 @@ static int run_test(struct test *test, int subtest) } if (!child) { - pr_debug("test child forked, pid %d\n", getpid()); - if (!verbose) { - int nullfd = open("/dev/null", O_WRONLY); - if (nullfd >= 0) { - close(STDERR_FILENO); - close(STDOUT_FILENO); + if (!dont_fork) { + pr_debug("test child forked, pid %d\n", getpid()); - dup2(nullfd, STDOUT_FILENO); - dup2(STDOUT_FILENO, STDERR_FILENO); - close(nullfd); + if (!verbose) { + int nullfd = open("/dev/null", O_WRONLY); + + if (nullfd >= 0) { + close(STDERR_FILENO); + close(STDOUT_FILENO); + + dup2(nullfd, STDOUT_FILENO); + dup2(STDOUT_FILENO, STDERR_FILENO); + close(nullfd); + } + } else { + signal(SIGSEGV, sighandler_dump_stack); + signal(SIGFPE, sighandler_dump_stack); } - } else { - signal(SIGSEGV, sighandler_dump_stack); - signal(SIGFPE, sighandler_dump_stack); } err = test->func(subtest); - exit(err); + if (!dont_fork) + exit(err); } - wait(&status); + if (!dont_fork) { + wait(&status); - if (WIFEXITED(status)) { - err = (signed char)WEXITSTATUS(status); - pr_debug("test child finished with %d\n", err); - } else if (WIFSIGNALED(status)) { - err = -1; - pr_debug("test child interrupted\n"); + if (WIFEXITED(status)) { + err = (signed char)WEXITSTATUS(status); + pr_debug("test child finished with %d\n", err); + } else if (WIFSIGNALED(status)) { + err = -1; + pr_debug("test child interrupted\n"); + } } return err; @@ -425,6 +434,8 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING('s', "skip", &skip, "tests", "tests to skip"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('F', "dont-fork", &dont_fork, + "Do not fork for testcase"), OPT_END() }; const char * const test_subcommands[] = { "list", NULL }; From a24020e6b7cf6eb8b75d8bca6b89870b1cee6ba7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 28 Jun 2016 13:29:04 +0200 Subject: [PATCH 11/11] perf tools: Change cpu_map__fprintf output Display cpu map in standard list form. (perf report -D output on perf stat data). before: 0x590 [0x18]: PERF_RECORD_CPU_MAP nr: 4 cpus: 0, 1, 2, 3 after: 0x590 [0x18]: PERF_RECORD_CPU_MAP: 0-3 Adding automated testcase. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1467113345-12669-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 4 +++ tools/perf/tests/cpumap.c | 24 +++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/cpumap.c | 54 +++++++++++++++++++++++++++++---- tools/perf/util/cpumap.h | 1 + tools/perf/util/event.c | 2 +- 6 files changed, 79 insertions(+), 7 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 5781c1640eae..07c14e9f6546 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -213,6 +213,10 @@ static struct test generic_tests[] = { .desc = "Test backward reading from ring buffer", .func = test__backward_ring_buffer, }, + { + .desc = "Test cpu map print", + .func = test__cpu_map_print, + }, { .func = NULL, }, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 4cb6418a8ffc..c9ec5f83e42c 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -86,3 +86,27 @@ int test__cpu_map_synthesize(int subtest __maybe_unused) cpu_map__put(cpus); return 0; } + +static int cpu_map_print(const char *str) +{ + struct cpu_map *map = cpu_map__new(str); + char buf[100]; + + if (!map) + return -1; + + cpu_map__snprint(map, buf, sizeof(buf)); + return !strcmp(buf, str); +} + +int test__cpu_map_print(int subtest __maybe_unused) +{ + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1")); + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1,5")); + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1,3,5,7,9,11,13,15,17,19,21-40")); + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("2-5")); + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1,3-6,8-10,24,35-37")); + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1,3-6,8-10,24,35-37")); + TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1-10,12-20,22-30,32-40")); + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index c57e72c826d2..52f969570c97 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -87,6 +87,7 @@ int test__synthesize_stat_round(int subtest); int test__event_update(int subtest); int test__event_times(int subtest); int test__backward_ring_buffer(int subtest); +int test__cpu_map_print(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 02d801670f30..15f83acac1b8 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -236,13 +236,12 @@ struct cpu_map *cpu_map__new_data(struct cpu_map_data *data) size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) { - int i; - size_t printed = fprintf(fp, "%d cpu%s: ", - map->nr, map->nr > 1 ? "s" : ""); - for (i = 0; i < map->nr; ++i) - printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]); +#define BUFSIZE 1024 + char buf[BUFSIZE]; - return printed + fprintf(fp, "\n"); + cpu_map__snprint(map, buf, sizeof(buf)); + return fprintf(fp, "%s\n", buf); +#undef BUFSIZE } struct cpu_map *cpu_map__dummy_new(void) @@ -599,3 +598,46 @@ bool cpu_map__has(struct cpu_map *cpus, int cpu) return false; } + +size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size) +{ + int i, cpu, start = -1; + bool first = true; + size_t ret = 0; + +#define COMMA first ? "" : "," + + for (i = 0; i < map->nr + 1; i++) { + bool last = i == map->nr; + + cpu = last ? INT_MAX : map->map[i]; + + if (start == -1) { + start = i; + if (last) { + ret += snprintf(buf + ret, size - ret, + "%s%d", COMMA, + map->map[i]); + } + } else if (((i - start) != (cpu - map->map[start])) || last) { + int end = i - 1; + + if (start == end) { + ret += snprintf(buf + ret, size - ret, + "%s%d", COMMA, + map->map[start]); + } else { + ret += snprintf(buf + ret, size - ret, + "%s%d-%d", COMMA, + map->map[start], map->map[end]); + } + first = false; + start = i; + } + } + +#undef COMMA + + pr_debug("cpumask list: %s\n", buf); + return ret; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 1a0a35073ce1..206dc550354a 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -19,6 +19,7 @@ struct cpu_map *cpu_map__empty_new(int nr); struct cpu_map *cpu_map__dummy_new(void); struct cpu_map *cpu_map__new_data(struct cpu_map_data *data); struct cpu_map *cpu_map__read(FILE *file); +size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size); size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); int cpu_map__get_socket_id(int cpu); int cpu_map__get_socket(struct cpu_map *map, int idx, void *data); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9b141f12329e..e20438b784be 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1092,7 +1092,7 @@ size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp) struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data); size_t ret; - ret = fprintf(fp, " nr: "); + ret = fprintf(fp, ": "); if (cpus) ret += cpu_map__fprintf(cpus, fp);