As sometimes we have multiple enums to represent some struct type, like
with perf_event_attr->type, that has 'enum perf_event_type' in Linux's
UAPI and 'enum perf_user_event_type' for purely userspace types, like
the ones synthesized for Intel PT, like PERF_RECORD_AUXTRACE, etc.
This patch just transforms type->type_enum into a list, the support for
multiple types comes next.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e. these are all equivalent:
filter=type==PERF_RECORD_MMAP
type==PERF_RECORD_MMAP
filter=type==MMAP
type==MMAP
Update docs about it.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
For DWARF we may end up with a set of arguments that reference multiple
types and those may not be all present in any of the CUs when using
DWARF, i.e. for instance this wasn't possible with DWARF:
$ pahole ~/bin/perf --header=perf_file_header \
--seek_bytes='$header.data.offset' -C
'perf_event_header(sizeof,type,type_enum=perf_event_type)'
--size_bytes='$header.data.size' < perf.data
As these types are present in a single CU in the DWARF sections of ~/bin/perf:
'struct perf_file_header'
'struct perf_event_header'
'enum perf_event_type'
This works with BTF as we have everything in just one place, one 'CU':
Running the above command line with -F btf and with -F dwarf produces
the same output.
$ pahole -F dwarf ~/bin/perf --header=perf_file_header --seek_bytes='$header.data.offset' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type)' --size_bytes='$header.data.size' < perf.data > dwarf
$
$ pahole -F btf ~/bin/perf --header=perf_file_header --seek_bytes='$header.data.offset' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type)' --size_bytes='$header.data.size' < perf.data > btf
$
$ diff -u btf dwarf
$
$ tail btf
{
.type = PERF_RECORD_EXIT,
.misc = 0,
.size = 48,
},
{
.type = 68,
.misc = 0,
.size = 8,
},
$ wc btf
246 642 3863 btf
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Instead of receiving the type name to then lookup in that CU.
This is to use it with the type and cu returned from
cus__find_struct_by_name().
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
So that we can look it up in a different CU in the fallback for when we
don't find all the required types in a single CU.
And this avoids doing the search for the type twice.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
For now its all in the same CU, but if not all types needed are found in
a single CU, we'll do a fallback lookup in all of them, so we need to
pair tags with the CUs where they were found.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type), ' < perf.data
These types were not found all in the same CU (compile unit/object file/debugging info unit):
-C perf_event_header --header=perf_file_header enum_type=perf_event_type
$
this works with BTF, not with DWARF, for the reason stated on the above
output.
This also improves the previous behaviour, as previously this would
print a blank line:
$ pahole bla
Not found: bla
$
When obtaining the type info from a DWARF file.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We were going past the end of the argument when the last one had (),
i.e.
-C 'perf_event_header(sizeof,type,type_enum=perf_event_type)'
We were going after the ending '\0' because when looking for the ',' we
first found the inner ones, as we looked at the first ) from the
current class, we needed to use as the separator the ')' location + one,
but when we have just one a() style class we would put the '\0' not on
top of a ',' but on the already existing one, then increment the
separator to after the '\0', oops, fix it.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
So that at the end we have in there only the classes we couldn't find
all the types required in its args (type_enum, --header, etc).
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This just means we couldn't find any CU (object file, debugging unit)
that had all the types asked for, i.e. instead of:
$ pahole -V ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type)' < perf.data
pahole: the type enum 'perf_event_type' wasn't found in 'util/header.c'
$
We'll just have the previous behaviour of continuing to look at the
following CUs, all of them, trying to find one that has, in this case,
the definitions for 'struct perf_file_header', 'struct
perf_event_header' and 'enum perf_event_type', and since none has, in
this ~/bin/perf binary (that has only DWARF info) not print anything.
The next cset will add a nicer comment, pointing out the unprocessed
types, which will be better than printing nothing.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
So that we can do it earlier, when processing the -C/--class_name, to
avoid doing it for every CU and to reuse it later when handling DWARF
and not all types are found in a single CU.
No change in user visible behaviour.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e. instead of requiring:
filter=left==right
Check if the first char after '=' is '=' and if so, consider that a
filter, allowing for the more compact:
left==right
Form, e.g.:
$ pahole -F btf -V ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,type==PERF_RECORD_COMM)' < perf.data
pahole: sizeof_operator for 'perf_event_header' is 'size'
pahole: type member for 'perf_event_header' is 'type'
pahole: type enum for 'perf_event_header' is 'perf_event_type'
pahole: filter for 'perf_event_header' is 'type==PERF_RECORD_COMM'
pahole: seek bytes evaluated from --seek_bytes=$header.data.offset is 0x130
pahole: size bytes evaluated from --size_bytes=$header.data.size is 0x588
{
.header = {
.type = PERF_RECORD_COMM,
.misc = 0,
.size = 0x28,
},
.pid = 0x7e50,
.tid = 0x7e50,
.comm = "perf",
},
{
.header = {
.type = PERF_RECORD_COMM,
.misc = 0x2000,
.size = 0x28,
},
.pid = 0x7e50,
.tid = 0x7e50,
.comm = "sleep",
},
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
To make this more compact, for the common case where record has as its
record type a 'type' member, allow for 'type' to be a boolean instead of
requiring the more verbose/redundant 'type=type':
$ perf report --header-only | grep cmdline
# cmdline : /home/acme/bin/perf record sleep 1
$ pahole -F btf -V ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,filter=type==PERF_RECORD_COMM)' < perf.data
pahole: sizeof_operator for 'perf_event_header' is 'size'
pahole: type member for 'perf_event_header' is 'type'
pahole: type enum for 'perf_event_header' is 'perf_event_type'
pahole: filter for 'perf_event_header' is 'type==PERF_RECORD_COMM'
pahole: seek bytes evaluated from --seek_bytes=$header.data.offset is 0x130
pahole: size bytes evaluated from --size_bytes=$header.data.size is 0x588
{
.header = {
.type = PERF_RECORD_COMM,
.misc = 0,
.size = 0x28,
},
.pid = 0x7e50,
.tid = 0x7e50,
.comm = "perf",
},
{
.header = {
.type = PERF_RECORD_COMM,
.misc = 0x2000,
.size = 0x28,
},
.pid = 0x7e50,
.tid = 0x7e50,
.comm = "sleep",
},
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
To make this more compact, for the common case where a variable sized
record has as the size of the record a 'size' member, allow for 'sizeof'
to be a boolean instead of requiring the more verbose/redundant
'sizeof=size':
$ perf report --header-only | grep cmdline
# cmdline : /home/acme/bin/perf record sleep 1
$ pahole -F btf -V ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type=type,type_enum=perf_event_type,filter=type==PERF_RECORD_COMM)' < perf.data
pahole: sizeof_operator for 'perf_event_header' is 'size'
pahole: type member for 'perf_event_header' is 'type'
pahole: type enum for 'perf_event_header' is 'perf_event_type'
pahole: filter for 'perf_event_header' is 'type==PERF_RECORD_COMM'
pahole: seek bytes evaluated from --seek_bytes=$header.data.offset is 0x130
pahole: size bytes evaluated from --size_bytes=$header.data.size is 0x588
{
.header = {
.type = PERF_RECORD_COMM,
.misc = 0,
.size = 0x28,
},
.pid = 0x7e50,
.tid = 0x7e50,
.comm = "perf",
},
{
.header = {
.type = PERF_RECORD_COMM,
.misc = 0x2000,
.size = 0x28,
},
.pid = 0x7e50,
.tid = 0x7e50,
.comm = "sleep",
},
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
To know from where to start reading some variable sized type we need
some information from a domain specific tool such as 'perf report -D'
for perf.data files, i.e.:
$ perf report -D -i perf.data
# To display the perf.data header info, please use --header/--header-only options.
#
0x130 [0x20]: event: 79
.
. ... raw event: size 32 bytes
. 0000: 4f 00 00 00 00 00 20 00 1f 00 00 00 00 00 00 00 O..... .........
. 0010: 31 30 9b 3c 00 00 00 00 2e 53 f8 0c 52 8c 01 00 10.<.....S<F8>.R...
0 0x130 [0x20]: PERF_RECORD_TIME_CONV: unhandled!
So we see that 0x130 is where the first PERF_RECORD_* event is located,
and we can use that with --seek_bytes and with some extra info we can
decode variable sized records:
$ pahole --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --skip 2 --count 3 ~/bin/perf < perf.data
{
.type = 0x4a,
.misc = 0,
.size = 0x20,
},
{
.header = {
.type = PERF_RECORD_CGROUP,
.misc = 0,
.size = 0x28,
},
.id = 0x1,
.path = "/",
},
{
.header = {
.type = PERF_RECORD_CGROUP,
.misc = 0,
.size = 0x30,
},
.id = 0x611,
.path = "/system.slice",
},
$
But if we decode the perf.data file header:
$ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data
{
.magic = 0x32454c4946524550,
.size = 0x68,
.attr_size = 0x88,
.attrs = {
.offset = 0xa8,
.size = 0x88,
},
.data = {
.offset = 0x130,
.size = 0x588,
},
.event_types = {
.offset = 0,
.size = 0,
},
.adds_features = { 0x16717ffc, 0, 0, 0 },
},
$
We see that that 0x130 offset is at field perf_file_header->data.offset,
so lets automate this so that we can try to extract that value and then
use it with --seek_bytes:
$ pahole --header=perf_file_header --seek_bytes='$header.data.offset' -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --skip 2 --count 3 ~/bin/perf < perf.data
pahole: the type enum 'perf_event_type' wasn't found in 'util/header.c'
$
The problem here is that pahole tries to avoid processing all the CUs
(compile units) in a binary, as, so far, it only needed to process one
main type at a time, i.e. the ones in -C/--class.
Now we need multiple types, in the above example we need:
struct perf_event_header
struct pref_file_header
enum perf_event_type
And in this case, the perf binary doesn't have any object/CU that has
all these three types.
To see if the code works we can resort to using BTF, that combines all
types into just one "CU", deduplicating them in the process, so:
$ pahole --btf_encode ~/bin/perf
And now it works:
$ pahole -V -F btf --header=perf_file_header --seek_bytes='$header.data.offset' -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --skip 2 --count 3 ~/bin/perf < perf.data
pahole: sizeof_operator for 'perf_event_header' is 'size'
pahole: type member for 'perf_event_header' is 'type'
pahole: type enum for 'perf_event_header' is 'perf_event_type'
pahole: seek bytes evaluated from --seek_bytes=$header.data.offset is 0x130
{
.type = 0x4a,
.misc = 0,
.size = 0x20,
},
{
.header = {
.type = PERF_RECORD_CGROUP,
.misc = 0,
.size = 0x28,
},
.id = 0x1,
.path = "/",
},
{
.header = {
.type = PERF_RECORD_CGROUP,
.misc = 0,
.size = 0x30,
},
.id = 0x611,
.path = "/system.slice",
},
$
In the next csets a fallback approach will allow for this to work even
with DWARF, when we'll notice that stdin wasn't consumed and thus we
need to search for the types needed with cus__find_struct_by_name() &
friends.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e.:
$ pahole -V ~/bin/perf --count 1 --seek_bytes 0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=type==PERF_RECORD_MMAP2)' < perf.data
pahole: sizeof_operator for 'perf_event_header' is 'size'
pahole: type member for 'perf_event_header' is 'type'
pahole: type enum for 'perf_event_header' is 'perf_event_type'
pahole: filter for 'perf_event_header' is 'type==PERF_RECORD_MMAP2'
{
.type = 0x4f,
.misc = 0,
.size = 0x20,
},
$
Now we need add the simplest possible parser for that expression, one
that will start just supporting the '==' operator and that expects the
first operand to be a struct field and the second a integer value, then
a string that will be searched for in the possible type_enum=foo arg if
the first operand is the type=foo specified type.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
So for such a bitfieldy struct:
$ pahole perf_event_attr | grep :
__u64 disabled:1; /* 40: 0 8 */
__u64 inherit:1; /* 40: 1 8 */
__u64 pinned:1; /* 40: 2 8 */
__u64 exclusive:1; /* 40: 3 8 */
__u64 exclude_user:1; /* 40: 4 8 */
__u64 exclude_kernel:1; /* 40: 5 8 */
__u64 exclude_hv:1; /* 40: 6 8 */
__u64 exclude_idle:1; /* 40: 7 8 */
__u64 mmap:1; /* 40: 8 8 */
__u64 comm:1; /* 40: 9 8 */
__u64 freq:1; /* 40:10 8 */
__u64 inherit_stat:1; /* 40:11 8 */
__u64 enable_on_exec:1; /* 40:12 8 */
__u64 task:1; /* 40:13 8 */
__u64 watermark:1; /* 40:14 8 */
__u64 precise_ip:2; /* 40:15 8 */
__u64 mmap_data:1; /* 40:17 8 */
__u64 sample_id_all:1; /* 40:18 8 */
__u64 exclude_host:1; /* 40:19 8 */
__u64 exclude_guest:1; /* 40:20 8 */
__u64 exclude_callchain_kernel:1; /* 40:21 8 */
__u64 exclude_callchain_user:1; /* 40:22 8 */
__u64 mmap2:1; /* 40:23 8 */
__u64 comm_exec:1; /* 40:24 8 */
__u64 use_clockid:1; /* 40:25 8 */
__u64 context_switch:1; /* 40:26 8 */
__u64 write_backward:1; /* 40:27 8 */
__u64 namespaces:1; /* 40:28 8 */
__u64 ksymbol:1; /* 40:29 8 */
__u64 bpf_event:1; /* 40:30 8 */
__u64 aux_output:1; /* 40:31 8 */
__u64 cgroup:1; /* 40:32 8 */
__u64 __reserved_1:31; /* 40:33 8 */
/* size: 120, cachelines: 2, members: 53 */
/* last cacheline: 56 bytes */
$
Using a domain specific tool to show which of those bitfield entries
have non-zero value for a given perf.data file (the default input file
for perf):
$ perf evlist -v
cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1
$
We can see that we now manage to decode them, at least on x86_64 using a
x86_64 perf.data file:
$ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data | grep -v ' = 0,'
{
.type = PERF_TYPE_HARDWARE,
.size = 0x78,
.sample_period = 0xfa0,
.sample_freq = 0xfa0,
.sample_type = 0x107,
.read_format = 0x4,
.disabled = 0x1,
.inherit = 0x1,
.exclude_kernel = 0x1,
.mmap = 0x1,
.comm = 0x1,
.freq = 0x1,
.enable_on_exec = 0x1,
.task = 0x1,
.precise_ip = 0x3,
.sample_id_all = 0x1,
.exclude_guest = 0x1,
.mmap2 = 0x1,
.comm_exec = 0x1,
.ksymbol = 0x1,
.bpf_event = 0x1,
},
$
And this reminds me that defaulting to eliding fields with a zero value
is valuable...
Also, this was in a rush, probably processing files from a different
arch will produce bad results as bitfields are tricky, so probably we'll
need more features to state how bitfields should be processed, etc.
The plan is to have a way to create variables on the command line, where
we'll read a header, give it a name, keep its contents accessible, then
in subsequent entries in the command line, refer to them and, say, set
the endianness from fields in the header, refer to offsets in header
members to state what type to use to pretty print a range of the
provided file or stdin, etc.
A rabbit hole...
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
In multi CU (Compile Unit) objects, namely DWARF ones, we go on
processing all of them looking for the types of interest (the ones in
-C), but before we check if the type is in that CU, we first parse its
arguments, when present.
Do that lookup sooner, right after we get hold of the type name, to
avoid needlessly parsing its arguments, now when we run;
$ pahole -V ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26
pahole: sizeof_operator for 'perf_event_header' is 'size'
pahole: type member for 'perf_event_header' is 'type'
pahole: type enum for 'perf_event_header' is 'perf_event_type'
struct perf_event_header {
__u32 type; /* 0 4 */
__u16 misc; /* 4 2 */
__u16 size; /* 6 2 */
/* size: 8, cachelines: 1, members: 3 */
/* last cacheline: 8 bytes */
};
$
We'll see those verbose messages just once, in the first CU that has the
definitions for the types of interest.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We still need to support it in the array__fprintf_base_type_value()
caller, in the next patch.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We follow typedefs to decide when to call
array__fprintf_base_type_value(), but then we were not following when
looking for the sizeof of its entries, fix it.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We need to know it right after reading the fixed part, so that we can
fix the --skip handling.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>