mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-23 20:53:53 +08:00
perf/core improvements and fixes:
New features: - Allow running 'perf test' entries in the same process, not forking to test each testcase, useful for debugging (Jiri Olsa) - Show number of samples in the stdio annotate header (Peter Zijlstra) Documentation: - Add documentation for perf.data on disk format (Andi Kleen) Build fixes: - Fix 'perf trace' build on old systems wrt missing SCHED_RESET_ON_FORK and eventfd.h (Arnaldo Carvalho de Melo) Infrastructure: - Utility function to fetch arch from evsel/evlist (Ravi Bangoria) Trivial: - Fix spelling mistake: "missmatch" -> "mismatch" in libbpf (Colin Ian King) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXdZFuAAoJENZQFvNTUqpALH8P/jUEFEVEnkUPjITbQgT6mfaH 3eoF2aM9XzyeJ4RwcSEWqjegF4nkwv10FBVEKjeIvvs8ajEO+SnyChL0/zpGoMHJ DWXeicQYFTbQy23d4p2ecU2gOH6QqtlzJ9d/UodwCWjM05FdROUa8Mz0WwyvIOiy xTZ81DdV/f0E7axI06Rel0yeQIirc8uUG+Wt4wYZhnrXP3tANLG/1bHjLgeAEYsB 2KPMz9lt2Wbzc/tng6fExWJzG7gct/yAuWV1kJxs7I1bIZekxAYGG+7ZDP+o5jh9 2bU021pajJh8KdwcabJJ37t1A3/LZOSjCz4Qc1PPHqBJFZMTOtKMUWdW2Pvzp7XB qF+7cvePG1JuapSin4QeqBG38ECQVbcg5dFIHPtLPWmoNmMYdjak2ypaWloeYDOT O68bbTaR+MLDdfSRtr5mHplAZL6m3I7nWerwtAWwy/LdFUkQrwfhdO23m2debwPH WuFmPz3HZqBAdSaTNl1F1Rqe/IL7MsPCgSZy/o3piC59sNhXyc55GaxMfF83yu95 PBz0ngZmW3/otYZW2EQC0BwkT1ef5ewqtmn3HAEBNnzqMHKTHkg+n3Ode0H0t2q4 rCXYIM0PAvQw8YSICt4HeapmW7unu2DcsFecApafpdjZhRmgIu40bwZ0eHBPK3bu HhK58+DFEuYmMOzsRoIQ =MDzG -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-20160630' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: New features: - Allow running 'perf test' entries in the same process, not forking to test each testcase, useful for debugging (Jiri Olsa) - Show number of samples in the stdio annotate header (Peter Zijlstra) Documentation changes: - Add documentation for perf.data on disk format (Andi Kleen) Build fixes: - Fix 'perf trace' build on old systems wrt missing SCHED_RESET_ON_FORK and eventfd.h (Arnaldo Carvalho de Melo) Infrastructure changes: - Utility function to fetch arch from evsel/evlist (Ravi Bangoria) Trivial changes: - Fix spelling mistake: "missmatch" -> "mismatch" in libbpf (Colin Ian King) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
dc29bb47a3
@ -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",
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
442
tools/perf/Documentation/perf.data-file-format.txt
Normal file
442
tools/perf/Documentation/perf.data-file-format.txt
Normal file
@ -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.
|
@ -14,6 +14,8 @@
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "symbol.h"
|
||||
|
||||
static bool dont_fork;
|
||||
|
||||
struct test __weak arch_tests[] = {
|
||||
{
|
||||
.func = NULL,
|
||||
@ -211,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,
|
||||
},
|
||||
@ -247,7 +253,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 +263,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 +438,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 };
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -1,13 +1,20 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#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);
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#ifndef EFD_SEMAPHORE
|
||||
#define EFD_SEMAPHORE 1
|
||||
#endif
|
||||
|
@ -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)
|
||||
|
@ -1522,13 +1522,14 @@ 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;
|
||||
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 +1541,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",
|
||||
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);
|
||||
|
||||
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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user