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:
@@ -71,7 +71,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
|
|||||||
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
|
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
|
||||||
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
|
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
|
||||||
[ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
|
[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(INTERNAL)] = "Internal error in libbpf",
|
||||||
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
|
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
|
||||||
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
|
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ enum libbpf_errno {
|
|||||||
LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
|
LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
|
||||||
LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */
|
LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */
|
||||||
LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */
|
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__INTERNAL, /* Internal error in libbpf */
|
||||||
LIBBPF_ERRNO__RELOC, /* Relocation failed */
|
LIBBPF_ERRNO__RELOC, /* Relocation failed */
|
||||||
LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */
|
LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */
|
||||||
|
|||||||
@@ -30,3 +30,7 @@ OPTIONS
|
|||||||
-v::
|
-v::
|
||||||
--verbose::
|
--verbose::
|
||||||
Be more 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 <subcmd/parse-options.h>
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
|
|
||||||
|
static bool dont_fork;
|
||||||
|
|
||||||
struct test __weak arch_tests[] = {
|
struct test __weak arch_tests[] = {
|
||||||
{
|
{
|
||||||
.func = NULL,
|
.func = NULL,
|
||||||
@@ -211,6 +213,10 @@ static struct test generic_tests[] = {
|
|||||||
.desc = "Test backward reading from ring buffer",
|
.desc = "Test backward reading from ring buffer",
|
||||||
.func = test__backward_ring_buffer,
|
.func = test__backward_ring_buffer,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.desc = "Test cpu map print",
|
||||||
|
.func = test__cpu_map_print,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.func = NULL,
|
.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)
|
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];
|
char sbuf[STRERR_BUFSIZE];
|
||||||
|
|
||||||
if (child < 0) {
|
if (child < 0) {
|
||||||
@@ -257,34 +263,41 @@ static int run_test(struct test *test, int subtest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!child) {
|
if (!child) {
|
||||||
pr_debug("test child forked, pid %d\n", getpid());
|
if (!dont_fork) {
|
||||||
if (!verbose) {
|
pr_debug("test child forked, pid %d\n", getpid());
|
||||||
int nullfd = open("/dev/null", O_WRONLY);
|
|
||||||
if (nullfd >= 0) {
|
|
||||||
close(STDERR_FILENO);
|
|
||||||
close(STDOUT_FILENO);
|
|
||||||
|
|
||||||
dup2(nullfd, STDOUT_FILENO);
|
if (!verbose) {
|
||||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
int nullfd = open("/dev/null", O_WRONLY);
|
||||||
close(nullfd);
|
|
||||||
|
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);
|
err = test->func(subtest);
|
||||||
exit(err);
|
if (!dont_fork)
|
||||||
|
exit(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
wait(&status);
|
if (!dont_fork) {
|
||||||
|
wait(&status);
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(status)) {
|
||||||
err = (signed char)WEXITSTATUS(status);
|
err = (signed char)WEXITSTATUS(status);
|
||||||
pr_debug("test child finished with %d\n", err);
|
pr_debug("test child finished with %d\n", err);
|
||||||
} else if (WIFSIGNALED(status)) {
|
} else if (WIFSIGNALED(status)) {
|
||||||
err = -1;
|
err = -1;
|
||||||
pr_debug("test child interrupted\n");
|
pr_debug("test child interrupted\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
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_STRING('s', "skip", &skip, "tests", "tests to skip"),
|
||||||
OPT_INCR('v', "verbose", &verbose,
|
OPT_INCR('v', "verbose", &verbose,
|
||||||
"be more verbose (show symbol address, etc)"),
|
"be more verbose (show symbol address, etc)"),
|
||||||
|
OPT_BOOLEAN('F', "dont-fork", &dont_fork,
|
||||||
|
"Do not fork for testcase"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
const char * const test_subcommands[] = { "list", NULL };
|
const char * const test_subcommands[] = { "list", NULL };
|
||||||
|
|||||||
@@ -86,3 +86,27 @@ int test__cpu_map_synthesize(int subtest __maybe_unused)
|
|||||||
cpu_map__put(cpus);
|
cpu_map__put(cpus);
|
||||||
return 0;
|
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();
|
long nr_end, nr = open_files_cnt();
|
||||||
int dso_cnt, limit, i, fd;
|
int dso_cnt, limit, i, fd;
|
||||||
|
|
||||||
|
/* Rest the internal dso open counter limit. */
|
||||||
|
reset_fd_limit();
|
||||||
|
|
||||||
memset(&machine, 0, sizeof(machine));
|
memset(&machine, 0, sizeof(machine));
|
||||||
|
|
||||||
/* set as system limit */
|
/* set as system limit */
|
||||||
@@ -312,6 +315,9 @@ int test__dso_data_reopen(int subtest __maybe_unused)
|
|||||||
#define dso_1 (dsos[1])
|
#define dso_1 (dsos[1])
|
||||||
#define dso_2 (dsos[2])
|
#define dso_2 (dsos[2])
|
||||||
|
|
||||||
|
/* Rest the internal dso open counter limit. */
|
||||||
|
reset_fd_limit();
|
||||||
|
|
||||||
memset(&machine, 0, sizeof(machine));
|
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_update(int subtest);
|
||||||
int test__event_times(int subtest);
|
int test__event_times(int subtest);
|
||||||
int test__backward_ring_buffer(int subtest);
|
int test__backward_ring_buffer(int subtest);
|
||||||
|
int test__cpu_map_print(int subtest);
|
||||||
|
|
||||||
#if defined(__arm__) || defined(__aarch64__)
|
#if defined(__arm__) || defined(__aarch64__)
|
||||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
#include "thread_map.h"
|
#include "thread_map.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
#define NAME (const char *) "perf"
|
||||||
|
#define NAMEUL (unsigned long) NAME
|
||||||
|
|
||||||
int test__thread_map(int subtest __maybe_unused)
|
int test__thread_map(int subtest __maybe_unused)
|
||||||
{
|
{
|
||||||
struct thread_map *map;
|
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 */
|
/* test map on current pid */
|
||||||
map = thread_map__new_by_pid(getpid());
|
map = thread_map__new_by_pid(getpid());
|
||||||
TEST_ASSERT_VAL("failed to alloc map", map);
|
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());
|
thread_map__pid(map, 0) == getpid());
|
||||||
TEST_ASSERT_VAL("wrong comm",
|
TEST_ASSERT_VAL("wrong comm",
|
||||||
thread_map__comm(map, 0) &&
|
thread_map__comm(map, 0) &&
|
||||||
!strcmp(thread_map__comm(map, 0), "perf"));
|
!strcmp(thread_map__comm(map, 0), NAME));
|
||||||
TEST_ASSERT_VAL("wrong refcnt",
|
TEST_ASSERT_VAL("wrong refcnt",
|
||||||
atomic_read(&map->refcnt) == 1);
|
atomic_read(&map->refcnt) == 1);
|
||||||
thread_map__put(map);
|
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 nr", map->nr == 1);
|
||||||
TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid());
|
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);
|
threads = thread_map__new_event(&event->thread_map);
|
||||||
TEST_ASSERT_VAL("failed to alloc map", threads);
|
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());
|
thread_map__pid(threads, 0) == getpid());
|
||||||
TEST_ASSERT_VAL("wrong comm",
|
TEST_ASSERT_VAL("wrong comm",
|
||||||
thread_map__comm(threads, 0) &&
|
thread_map__comm(threads, 0) &&
|
||||||
!strcmp(thread_map__comm(threads, 0), "perf"));
|
!strcmp(thread_map__comm(threads, 0), NAME));
|
||||||
TEST_ASSERT_VAL("wrong refcnt",
|
TEST_ASSERT_VAL("wrong refcnt",
|
||||||
atomic_read(&threads->refcnt) == 1);
|
atomic_read(&threads->refcnt) == 1);
|
||||||
thread_map__put(threads);
|
thread_map__put(threads);
|
||||||
@@ -72,6 +79,9 @@ int test__thread_map_synthesize(int subtest __maybe_unused)
|
|||||||
{
|
{
|
||||||
struct thread_map *threads;
|
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 */
|
/* test map on current pid */
|
||||||
threads = thread_map__new_by_pid(getpid());
|
threads = thread_map__new_by_pid(getpid());
|
||||||
TEST_ASSERT_VAL("failed to alloc map", threads);
|
TEST_ASSERT_VAL("failed to alloc map", threads);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#include <sys/eventfd.h>
|
|
||||||
|
|
||||||
#ifndef EFD_SEMAPHORE
|
#ifndef EFD_SEMAPHORE
|
||||||
#define EFD_SEMAPHORE 1
|
#define EFD_SEMAPHORE 1
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#ifndef SCHED_DEADLINE
|
#ifndef SCHED_DEADLINE
|
||||||
#define SCHED_DEADLINE 6
|
#define SCHED_DEADLINE 6
|
||||||
#endif
|
#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,
|
static size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size,
|
||||||
struct syscall_arg *arg)
|
struct syscall_arg *arg)
|
||||||
|
|||||||
@@ -1522,13 +1522,14 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
|||||||
const char *d_filename;
|
const char *d_filename;
|
||||||
const char *evsel_name = perf_evsel__name(evsel);
|
const char *evsel_name = perf_evsel__name(evsel);
|
||||||
struct annotation *notes = symbol__annotation(sym);
|
struct annotation *notes = symbol__annotation(sym);
|
||||||
|
struct sym_hist *h = annotation__histogram(notes, evsel->idx);
|
||||||
struct disasm_line *pos, *queue = NULL;
|
struct disasm_line *pos, *queue = NULL;
|
||||||
u64 start = map__rip_2objdump(map, sym->start);
|
u64 start = map__rip_2objdump(map, sym->start);
|
||||||
int printed = 2, queue_len = 0;
|
int printed = 2, queue_len = 0;
|
||||||
int more = 0;
|
int more = 0;
|
||||||
u64 len;
|
u64 len;
|
||||||
int width = 8;
|
int width = 8;
|
||||||
int namelen, evsel_name_len, graph_dotted_len;
|
int graph_dotted_len;
|
||||||
|
|
||||||
filename = strdup(dso->long_name);
|
filename = strdup(dso->long_name);
|
||||||
if (!filename)
|
if (!filename)
|
||||||
@@ -1540,17 +1541,14 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
|||||||
d_filename = basename(filename);
|
d_filename = basename(filename);
|
||||||
|
|
||||||
len = symbol__size(sym);
|
len = symbol__size(sym);
|
||||||
namelen = strlen(d_filename);
|
|
||||||
evsel_name_len = strlen(evsel_name);
|
|
||||||
|
|
||||||
if (perf_evsel__is_group_event(evsel))
|
if (perf_evsel__is_group_event(evsel))
|
||||||
width *= evsel->nr_members;
|
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 (%" PRIu64 " samples)\n",
|
||||||
width, width, "Percent", d_filename, evsel_name);
|
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);
|
graph_dotted_len, graph_dotted_len, graph_dotted_line);
|
||||||
|
|
||||||
if (verbose)
|
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)
|
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
|
||||||
{
|
{
|
||||||
int i;
|
#define BUFSIZE 1024
|
||||||
size_t printed = fprintf(fp, "%d cpu%s: ",
|
char buf[BUFSIZE];
|
||||||
map->nr, map->nr > 1 ? "s" : "");
|
|
||||||
for (i = 0; i < map->nr; ++i)
|
|
||||||
printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]);
|
|
||||||
|
|
||||||
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)
|
struct cpu_map *cpu_map__dummy_new(void)
|
||||||
@@ -599,3 +598,46 @@ bool cpu_map__has(struct cpu_map *cpus, int cpu)
|
|||||||
|
|
||||||
return false;
|
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__dummy_new(void);
|
||||||
struct cpu_map *cpu_map__new_data(struct cpu_map_data *data);
|
struct cpu_map *cpu_map__new_data(struct cpu_map_data *data);
|
||||||
struct cpu_map *cpu_map__read(FILE *file);
|
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);
|
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
||||||
int cpu_map__get_socket_id(int cpu);
|
int cpu_map__get_socket_id(int cpu);
|
||||||
int cpu_map__get_socket(struct cpu_map *map, int idx, void *data);
|
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;
|
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 bool may_cache_fd(void)
|
||||||
{
|
{
|
||||||
static rlim_t limit;
|
if (!fd_limit)
|
||||||
|
fd_limit = get_fd_limit();
|
||||||
|
|
||||||
if (!limit)
|
if (fd_limit == RLIM_INFINITY)
|
||||||
limit = get_fd_limit();
|
|
||||||
|
|
||||||
if (limit == RLIM_INFINITY)
|
|
||||||
return true;
|
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);
|
int dso__strerror_load(struct dso *dso, char *buf, size_t buflen);
|
||||||
|
|
||||||
|
void reset_fd_limit(void);
|
||||||
|
|
||||||
#endif /* __PERF_DSO */
|
#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);
|
struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data);
|
||||||
size_t ret;
|
size_t ret;
|
||||||
|
|
||||||
ret = fprintf(fp, " nr: ");
|
ret = fprintf(fp, ": ");
|
||||||
|
|
||||||
if (cpus)
|
if (cpus)
|
||||||
ret += cpu_map__fprintf(cpus, fp);
|
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)),
|
err, strerror_r(err, sbuf, sizeof(sbuf)),
|
||||||
perf_evsel__name(evsel));
|
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,
|
int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
|
||||||
attr__fprintf_f attr__fprintf, void *priv);
|
attr__fprintf_f attr__fprintf, void *priv);
|
||||||
|
|
||||||
|
char *perf_evsel__env_arch(struct perf_evsel *evsel);
|
||||||
|
|
||||||
#endif /* __PERF_EVSEL_H */
|
#endif /* __PERF_EVSEL_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user