Commit Graph

1594 Commits

Author SHA1 Message Date
Arnaldo Carvalho de Melo c50b6d37e9 pahole: Add infrastructure to have multiple concatenated type_enum
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 671ea22ca1 pahole: Move finding type_enum to a separate function
Since we'll allow for combining enums, get that in a separate function.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo dd3c0e9eb0 dwarves: Move the common initialization of fields for 'struct type'
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo aa7ab3e875 pahole: Allow for more compact enum filters by suppressing common prefix
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 4ece15c37b dwarves: Find common enumerators prefix
Allowing for filters such as 'type==MMAP', equivalent to
'type==PERF_RECORD_MMAP'.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo d28bc97b5c man-pages: Document pretty printing capabilities and provide examples
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo a345691f08 pahole: Make --header without -C to be equivalent to -C header-arg --count=1
I.e.:

  $ pahole -C elf64_hdr --count 1 < /lib/modules/5.8.0-rc6+/build/vmlinux
  {
  	.e_ident = { 127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
  	.e_type = 2,
  	.e_machine = 62,
  	.e_version = 1,
  	.e_entry = 16777216,
  	.e_phoff = 64,
  	.e_shoff = 604653784,
  	.e_flags = 0,
  	.e_ehsize = 64,
  	.e_phentsize = 56,
  	.e_phnum = 5,
  	.e_shentsize = 64,
  	.e_shnum = 80,
  	.e_shstrndx = 79,
  },
  $ pahole --header elf64_hdr < /lib/modules/5.8.0-rc6+/build/vmlinux
  {
  	.e_ident = { 127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
  	.e_type = 2,
  	.e_machine = 62,
  	.e_version = 1,
  	.e_entry = 16777216,
  	.e_phoff = 64,
  	.e_shoff = 604653784,
  	.e_flags = 0,
  	.e_ehsize = 64,
  	.e_phentsize = 56,
  	.e_phnum = 5,
  	.e_shentsize = 64,
  	.e_shnum = 80,
  	.e_shstrndx = 79,
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo eba4ac6b2c pahole: Fallback to pretty printing using types in multiple CUs
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 7c12b234ee dwarves: Introduce cus__find_type_by_name()
To find a type in all CUs.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo c6f3386e33 pahole: Make the type_instance constructor receive the looked up type + its CU
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 30297256e1 pahole: Pass the header type_instance to tag__stdio_fprintf_value()
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo ead8084d3f pahole: Store the CU in the type_instance struct
As it may be different than the CU where the --class_name is found.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 2c886fff0a pahole: Store the CU where type_enum was found
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 526b116bfe pahole: If pretty printing, don't discard CUs, keep them
As we may need them at the end if not all required types are in a single
CU.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo a1b8fddb4d pahole: Show which classes were not processed and why
$ 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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo bc6a3cac50 pahole: Fixup the --class_name parsing wrt class args (type=, sizeof=, etc)
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 363c373405 pahole: Remope pretty printed classes from the prototypes list
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 9f675e7fdb cmake: Use -O0 for debug builds
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 4e5b02beea pahole: Don't stop when not finding the type_enum
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 823739b56f pahole: Convert class_names into a list of struct prototypes
And parse the prototypes earlier.

No change in user visible behaviour.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 0a97d6c143 pahole: Factor out parsing class prototypes
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 80af0fbbf3 dutils: Allow for having a priv area per strlist
We'll use it to parse args to a class, before we find it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 04b6547e73 pahole: Honour --hex_fmt when pretty printing
I.e. by default now we print in decimal, as we do for printing just the
struct definitions, use --hex to ask for hexadecimal formatting, i.e.:

  $ 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
  $

That is, the perf_event_attr for the only event in the perf.data file
(default file processed by perf tools such as 'perf evlist'), now with
pahole:

  $ pahole ~/bin/perf --header=perf_file_header \
		           -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' \
		      --seek_bytes='$header.attrs.offset' \
		      --size_bytes='$header.attrs.size' --count=1 < perf.data  | grep -v '= 0,'
  {
  	.type = PERF_TYPE_HARDWARE,
  	.size = 120,
  	.sample_period = 4000,
  	.sample_freq = 4000,
  	.sample_type = 263,
  	.read_format = 4,
  	.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,
  },
  $

--count=1 was used because what is in that $header.attrs.offset/size
range are instances of:

  $ pahole ~/bin/perf -C perf_file_attr
  struct perf_file_attr {
  	struct perf_event_attr     attr;                 /*     0   120 */
  	/* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */
  	struct perf_file_section   ids;                  /*   120    16 */

  	/* size: 136, cachelines: 3, members: 2 */
  	/* last cacheline: 8 bytes */
  };
  $

We can drop --count=1 if we use the right type, 'struct perf_file_attr':

  $ pahole ~/bin/perf --header=perf_file_header \
		           -C 'perf_file_attr' \
		      --seek_bytes='$header.attrs.offset' \
		      --size_bytes='$header.attrs.size' < perf.data  | grep -v '= 0,'

  {
  	.attr = {
  		.size = 120,
  		.sample_period = 4000,
  		.sample_freq = 4000,
  		.sample_type = 263,
  		.read_format = 4,
  		.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,
  	},
  	.ids = {
  		.offset = 104,
  		.size = 64,
  	},
  },

We lose the ability to use type + type_enum, as we still don't support
specifying fields in fields for those class attributes, i.e. we need to
support:

  -C 'perf_event_attr(sizeof=attr.size,type=attr.type,type_enum=perf_type_id)'

Which is easy enough, we already support that for $header.data.offset,
for instance, will generalise that and reuse with class attributes.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 266c025598 pahole: Support filters without 'filter='
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 6c683ce0e1 pahole: Allow for a 'type' boolean class arg meaning 'type=type'
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 1c68f41bb8 pahole: Allow for a 'sizeof' boolean class arg meaning 'sizeof=size'
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo b4b3fb0a3a pahole: First look for ',' then '=' to allow for boolean args
Before:

  $ 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)' < perf.data
  pahole: invalid arg 'sizeof,type' in 'perf_event_header(sizeof,type=type,type_enum=perf_event_type)' (known args: sizeof=member, type=member, type_enum=enum)
  $

After:

  $ 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)' < perf.data
  pahole: invalid, missing '=' in 'sizeof'
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 446b85f727 pahole: Add support for --size_bytes, accepts header variables
Last magic number elliminated for the printing of 'struct
perf_event_header' variable sized records as found in perf.data files.

Now its possible to state when to stop processing, and do that based on
fields in the header, i.e., for perf.data files we have:

  $ 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 },
  },
  $

So we want to start at header->data.offset and stop processing after
event->data.size bytes, we can do it as naturally as:

  $ 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)' \
           --size_bytes='$header.data.size' ~/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
  pahole: size bytes evaluated from --size_bytes=$header.data.size is 0x588
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.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",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x80,
  	},
  	.id = 0x1e94,
  	.path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x58,
  	},
  	.id = 0xd,
  	.path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope",
  },
  {
  	.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",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x68,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x55ea2a865000,
  	.len = 0x4000,
  	.pgoff = 0x2000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e0526,
  	.ino_generation = 0xc97273c3,
  	.prot = 0x5,
  	.flags = 0x1802,
  	.filename = "/usr/bin/sleep",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x70,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7f53040c7000,
  	.len = 0x20000,
  	.pgoff = 0x1000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e1faf,
  	.ino_generation = 0x83ee2ed3,
  	.prot = 0x5,
  	.flags = 0x1802,
  	.filename = "/usr/lib64/ld-2.29.so",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x60,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7fffd8b69000,
  	.len = 0x2000,
  	.pgoff = 0,
  	.maj = 0,
  	.min = 0,
  	.ino = 0,
  	.ino_generation = 0,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "[vdso]",
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b3544, 0x1 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b4a90, 0x1 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b57c8, 0x6 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b644b, 0x3a },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b7279, 0x266 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b97c7, 0x16cd },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f53040c814b, 0x7e5000007e50, 0x190aac32bc2b8, 0x5f03 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f53040dd5af, 0x7e5000007e50, 0x190aac32c1af2, 0x16141 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x70,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7f5303efe000,
  	.len = 0x14d000,
  	.pgoff = 0x22000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e028a,
  	.ino_generation = 0xd5947787,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "/usr/lib64/libc-2.29.so",
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f53040d7281, 0x7e5000007e50, 0x190aac32ee484, 0x31f6d },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f5304011e55, 0x7e5000007e50, 0x190aac331dd50, 0x34455 },
  },
  {
  	.type = PERF_RECORD_EXIT,
  	.misc = 0,
  	.size = 0x30,
  },
  {
  	.type = 0x44,
  	.misc = 0,
  	.size = 0x8,
  },
  $

Should work with --count, --skip and filtering by type, lets see, lets
show just two PERF_RECORD_CGROUP records, but skip the first:

  $ pahole -F btf \
               --header=perf_file_header \
           --seek_bytes='$header.data.offset' \
                     -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' \
           --size_bytes='$header.data.size' --skip 1 --count 2 ~/bin/perf < perf.data
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x611,
  	.path = "/system.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  $

Well, this works even without -size_bytes, but if we ask for the first
500 records, skipping the first, then --size_bytes will avoid processing
things outside the 'struct perf_event_header' range:

  $ pahole -F btf \
                      --header=perf_file_header \
                  --seek_bytes='$header.data.offset' \
                            -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' \
                  --size_bytes='$header.data.size' --skip 1 --count 500 ~/bin/perf < perf.data
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x611,
  	.path = "/system.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x80,
  	},
  	.id = 0x1e94,
  	.path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x58,
  	},
  	.id = 0xd,
  	.path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope",
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 0f10fa7529 pahole: Move reading the header to outside the --seek_bytes code
So that we can reuse it with the upcoming --size_bytes.

No change in the resulting output.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 9310b04854 pahole: Add support for referencing header variables when pretty printing
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 611f5e8bd7 pahole: Add == class member filtering
Simplest one, wanna see just the PERF_RECORD_CGROUP records?

  $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' < perf.data
  {
  	.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",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x80,
  	},
  	.id = 0x1e94,
  	.path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x58,
  	},
  	.id = 0xd,
  	.path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope",
  },
  Couldn't read record: 25441 bytes
  $

--seek_bytes was found from output of the data specific tool:

  perf record -D

Then we didn't specify a --count because we don't know how many are in
this specific perf.data file, the validations in place ended up
producing the right result, with a warning about an invalid
perf_event_header->size.

In time we'll read this:

  $ 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 },
  },
  $

Like this:

  $ pahole ~/bin/perf header=perf_file_header --seek_bytes='$header.data.offset' --size='$header.data.size' \
	   -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' < perf.data

I.e. strings outside options with a '=' assignment operator will be
considered a pretty printing variable, one that we can subsequently use
in options, expressions, etc.

At some point we'll be able to describe an arbitrary file format and
have a generic type based (BTF, DWARF, CTF, whatever) raw data pretty
printer.

As-is it is works already with any of the -C type fields:

  $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=size==0x30)' --count 3 < perf.data
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x611,
  	.path = "/system.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  {
  	.type = PERF_RECORD_EXIT,
  	.misc = 0,
  	.size = 0x30,
  },
  $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type,filter=misc==0)' --count 3 < perf.data
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = 0x4a,
  	.misc = 0,
  	.size = 0x20,
  },
  $

Shouldn't be difficult to filter based on the demultiplexed types (i.e.
filters like path==/etc/*).

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo f2987224be pahole: Do the 'type_enum' class argument validation as soon as we parse it
No point in deferring this for later.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 02ca176c62 pahole: Do the 'type' class argument validation earlier
As soon as we parse it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 172d743d9c pahole: Do the 'sizeof' class argument validation earlier
As soon as we parse it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 9a30c101d7 pahole: As soon as a attribute is found, check if the type is a struct or class
And stop at that point if it ain't that.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo a38ccf4723 pahole: Allow filter=expression as a class argument for pretty printing
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 3c4ee1d91f pahole: Pretty print bitfields
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 451d66ec02 pahole: Pretty print unions too, cope with unnamed ones
See those unnamed unions?

  $ pahole perf_event_attr
  struct perf_event_attr {
  	__u32                      type;                 /*     0     4 */
  	__u32                      size;                 /*     4     4 */
  	__u64                      config;               /*     8     8 */
  	union {
  		__u64              sample_period;        /*    16     8 */
  		__u64              sample_freq;          /*    16     8 */
  	};                                               /*    16     8 */
  	__u64                      sample_type;          /*    24     8 */
  	__u64                      read_format;          /*    32     8 */
  	__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 */
  	union {
  		__u32              wakeup_events;        /*    48     4 */
  		__u32              wakeup_watermark;     /*    48     4 */
  	};                                               /*    48     4 */
  	__u32                      bp_type;              /*    52     4 */
  	union {
  		__u64              bp_addr;              /*    56     8 */
  		__u64              kprobe_func;          /*    56     8 */
  		__u64              uprobe_path;          /*    56     8 */
  		__u64              config1;              /*    56     8 */
  	};                                               /*    56     8 */
  	/* --- cacheline 1 boundary (64 bytes) --- */
  	union {
  		__u64              bp_len;               /*    64     8 */
  		__u64              kprobe_addr;          /*    64     8 */
  		__u64              probe_offset;         /*    64     8 */
  		__u64              config2;              /*    64     8 */
  	};                                               /*    64     8 */
  	__u64                      branch_sample_type;   /*    72     8 */
  	__u64                      sample_regs_user;     /*    80     8 */
  	__u32                      sample_stack_user;    /*    88     4 */
  	__s32                      clockid;              /*    92     4 */
  	__u64                      sample_regs_intr;     /*    96     8 */
  	__u32                      aux_watermark;        /*   104     4 */
  	__u16                      sample_max_stack;     /*   108     2 */
  	__u16                      __reserved_2;         /*   110     2 */
  	__u32                      aux_sample_size;      /*   112     4 */
  	__u32                      __reserved_3;         /*   116     4 */

  	/* size: 120, cachelines: 2, members: 53 */
  	/* last cacheline: 56 bytes */
  };
  $

So, there is at least one in a perf.data file, lets see where that is:

  $ 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 },
  },
  $

So, at that 0xa8 offset we should find this;

  $ 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
  $

Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one
can access it as attr->sample_freq or attr->sample_period.

Now to the bitfields, all those with value 0x6195b723...

  $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data
  {
  	.type = PERF_TYPE_HARDWARE,
  	.size = 0x78,
  	.config = 0,
  	.sample_period = 0xfa0,
  	.sample_freq = 0xfa0,
  	.sample_type = 0x107,
  	.read_format = 0x4,
  	.disabled = 0x6195b723,
  	.inherit = 0x6195b723,
  	.pinned = 0x6195b723,
  	.exclusive = 0x6195b723,
  	.exclude_user = 0x6195b723,
  	.exclude_kernel = 0x6195b723,
  	.exclude_hv = 0x6195b723,
  	.exclude_idle = 0x6195b723,
  	.mmap = 0x6195b723,
  	.comm = 0x6195b723,
  	.freq = 0x6195b723,
  	.inherit_stat = 0x6195b723,
  	.enable_on_exec = 0x6195b723,
  	.task = 0x6195b723,
  	.watermark = 0x6195b723,
  	.precise_ip = 0x6195b723,
  	.mmap_data = 0x6195b723,
  	.sample_id_all = 0x6195b723,
  	.exclude_host = 0x6195b723,
  	.exclude_guest = 0x6195b723,
  	.exclude_callchain_kernel = 0x6195b723,
  	.exclude_callchain_user = 0x6195b723,
  	.mmap2 = 0x6195b723,
  	.comm_exec = 0x6195b723,
  	.use_clockid = 0x6195b723,
  	.context_switch = 0x6195b723,
  	.write_backward = 0x6195b723,
  	.namespaces = 0x6195b723,
  	.ksymbol = 0x6195b723,
  	.bpf_event = 0x6195b723,
  	.aux_output = 0x6195b723,
  	.cgroup = 0x6195b723,
  	.__reserved_1 = 0x6195b723,
  	.wakeup_events = 0,
  	.wakeup_watermark = 0,
  	.bp_type = 0,
  	.bp_addr = 0,
  	.kprobe_func = 0,
  	.uprobe_path = 0,
  	.config1 = 0,
  	.bp_len = 0,
  	.kprobe_addr = 0,
  	.probe_offset = 0,
  	.config2 = 0,
  	.branch_sample_type = 0,
  	.sample_regs_user = 0,
  	.sample_stack_user = 0,
  	.clockid = 0,
  	.sample_regs_intr = 0,
  	.aux_watermark = 0,
  	.sample_max_stack = 0,
  	.__reserved_2 = 0,
  	.aux_sample_size = 0,
  	.__reserved_3 = 0,
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 48c7704b49 pahole: Check if the type with arguments is present in the current CU
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 472519ac2c pahole: Support nested structs
I.e.

  $ pahole -C perf_record_mmap2 ~/bin/perf
  struct perf_record_mmap2 {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */
  	__u64                      start;                /*    16     8 */
  	__u64                      len;                  /*    24     8 */
  	__u64                      pgoff;                /*    32     8 */
  	__u32                      maj;                  /*    40     4 */
  	__u32                      min;                  /*    44     4 */
  	__u64                      ino;                  /*    48     8 */
  	__u64                      ino_generation;       /*    56     8 */
  	/* --- cacheline 1 boundary (64 bytes) --- */
  	__u32                      prot;                 /*    64     4 */
  	__u32                      flags;                /*    68     4 */
  	char                       filename[4096];       /*    72  4096 */

  	/* size: 4168, cachelines: 66, members: 13 */
  	/* last cacheline: 8 bytes */
  };
  $

And others will have the 'header' field pretty printed indented:

  $ pahole --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 ~/bin/perf < perf.data
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.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",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x30,
  	},
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x80,
  	},
  	.id = 0x1e94,
  	.path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope",
  },
  {
  	.header = {
  		.type = PERF_RECORD_CGROUP,
  		.misc = 0,
  		.size = 0x58,
  	},
  	.id = 0xd,
  	.path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope",
  },
  {
  	.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",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x68,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x55ea2a865000,
  	.len = 0x4000,
  	.pgoff = 0x2000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e0526,
  	.ino_generation = 0xc97273c3,
  	.prot = 0x5,
  	.flags = 0x1802,
  	.filename = "/usr/bin/sleep",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x70,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7f53040c7000,
  	.len = 0x20000,
  	.pgoff = 0x1000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e1faf,
  	.ino_generation = 0x83ee2ed3,
  	.prot = 0x5,
  	.flags = 0x1802,
  	.filename = "/usr/lib64/ld-2.29.so",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x60,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7fffd8b69000,
  	.len = 0x2000,
  	.pgoff = 0,
  	.maj = 0,
  	.min = 0,
  	.ino = 0,
  	.ino_generation = 0,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "[vdso]",
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b3544, 0x1 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b4a90, 0x1 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b57c8, 0x6 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b644b, 0x3a },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b7279, 0x266 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4001,
  		.size = 0x28,
  	},
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b97c7, 0x16cd },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f53040c814b, 0x7e5000007e50, 0x190aac32bc2b8, 0x5f03 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f53040dd5af, 0x7e5000007e50, 0x190aac32c1af2, 0x16141 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP2,
  		.misc = 0x2,
  		.size = 0x70,
  	},
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7f5303efe000,
  	.len = 0x14d000,
  	.pgoff = 0x22000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e028a,
  	.ino_generation = 0xd5947787,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "/usr/lib64/libc-2.29.so",
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f53040d7281, 0x7e5000007e50, 0x190aac32ee484, 0x31f6d },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 0x4002,
  		.size = 0x28,
  	},
  	.array = { 0x7f5304011e55, 0x7e5000007e50, 0x190aac331dd50, 0x34455 },
  },
  {
  	.type = PERF_RECORD_EXIT,
  	.misc = 0,
  	.size = 0x30,
  },
  {
  	.type = 0x44,
  	.misc = 0,
  	.size = 0x8,
  },

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo ae43029363 dwarves_fprintf: Export the 'tabs' variable
Will be used by pahole's pretty printer to handle nested structs.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo ab714acec8 pahole: Support zero sized base type arrays
Like the one in:

  $ pahole -C perf_record_sample ~/bin/perf
  struct perf_record_sample {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      array[];              /*     8     0 */

  	/* size: 8, cachelines: 1, members: 2 */
  	/* last cacheline: 8 bytes */
  };
  $

So now we get those:

  $ perf report -D
  <SNIP>
  0x420 [0x60]: event: 10
  . ... raw event: size 96 bytes
  .  0000:  0a 00 00 00 02 00 60 00 50 7e 00 00 50 7e 00 00  ......`.P~..P~..
  .  0010:  00 90 b6 d8 ff 7f 00 00 00 20 00 00 00 00 00 00  ..<B6><D8><FF>.... ......
  .  0020:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  .  0030:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  .  0040:  05 00 00 00 02 10 00 00 5b 76 64 73 6f 5d 00 00  ........[vdso]..
  .  0050:  50 7e 00 00 50 7e 00 00 e7 c5 2a c3 aa 90 01 00  P~..P~..<E7><C5>*<C3><AA>...

  440538069911015 0x420 [0x60]: PERF_RECORD_MMAP2 32336/32336: [0x7fffd8b69000(0x2000) @ 0 00:00 0 0]: r-xp [vdso]

  0x480 [0x28]: event: 9
  . ... raw event: size 40 bytes
  .  0000:  09 00 00 00 01 40 28 00 27 0b c0 88 ff ff ff ff  .....@(.'.<C0>.<FF><FF><FF><FF>
  .  0010:  50 7e 00 00 50 7e 00 00 44 35 2b c3 aa 90 01 00  P~..P~..D5+<C3><AA>...
  .  0020:  01 00 00 00 00 00 00 00                          ........

  440538069939524 0x480 [0x28]: PERF_RECORD_SAMPLE(IP, 0x4001): 32336/32336: 0xffffffff88c00b27 period: 1 addr: 0
   ... thread: sleep:32336
   ...... dso: <not found>

  0x4a8 [0x28]: event: 9
  . ... raw event: size 40 bytes
  .  0000:  09 00 00 00 01 40 28 00 27 0b c0 88 ff ff ff ff  .....@(.'.<C0>.<FF><FF><FF><FF>
  .  0010:  50 7e 00 00 50 7e 00 00 90 4a 2b c3 aa 90 01 00  P~..P~...J+<C3><AA>...
  .  0020:  01 00 00 00 00 00 00 00                          ........

  440538069944976 0x4a8 [0x28]: PERF_RECORD_SAMPLE(IP, 0x4001): 32336/32336: 0xffffffff88c00b27 period: 1 addr: 0

  $ pahole --seek_bytes=0x420 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 3 ~/bin/perf < perf.data
  {
  	.header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7fffd8b69000,
  	.len = 0x2000,
  	.pgoff = 0,
  	.maj = 0,
  	.min = 0,
  	.ino = 0,
  	.ino_generation = 0,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "[vdso]",
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b3544, 0x1 },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b4a90, 0x1 },
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo ff7815a0f8 pahole: Add missing space before '}' in array__fprintf_base_type_value()
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 5f2502274e pahole: Support zero sized arrays in array__fprintf_base_type_value()
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 3aadfbdd72 pahole: Follow array type typedefs to find real sizeof(entry)
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 7309039aa7 pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record
There is this usecase where a header member selects how to interpret the
bytes after the header as some other type, and when this is coupled with
an enum that have as its entries the names of the structs that decode
thsoe types, we should take advantage of it, for sure.

That is the case with, hopefully, many on-disk formats, and probably
even with things that never touch disks.

It is mostly the case with perf.data files, where we have as the fixed
header part this, and since it is a kernel ABI, lets remember that Linux
kernels comes with BTF for all its types, right? Ok, so lets use the
super concise form:

  $ pahole perf_event_header
  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 */
  };
  $

See that 'type' field? Now it is as interesting as that 'size' one
(remember, that is what says how really sized is a record), so, I know
that in perf land, that type is associated with this enum:

  $ pahole perf_event_type
  enum perf_event_type {
  	PERF_RECORD_MMAP = 1,
  	PERF_RECORD_LOST = 2,
  	PERF_RECORD_COMM = 3,
  	PERF_RECORD_EXIT = 4,
  	PERF_RECORD_THROTTLE = 5,
  	PERF_RECORD_UNTHROTTLE = 6,
  	PERF_RECORD_FORK = 7,
  	PERF_RECORD_READ = 8,
  	PERF_RECORD_SAMPLE = 9,
  	PERF_RECORD_MMAP2 = 10,
  	PERF_RECORD_AUX = 11,
  	PERF_RECORD_ITRACE_START = 12,
  	PERF_RECORD_LOST_SAMPLES = 13,
  	PERF_RECORD_SWITCH = 14,
  	PERF_RECORD_SWITCH_CPU_WIDE = 15,
  	PERF_RECORD_NAMESPACES = 16,
  	PERF_RECORD_KSYMBOL = 17,
  	PERF_RECORD_BPF_EVENT = 18,
  	PERF_RECORD_CGROUP = 19,
  	PERF_RECORD_MAX = 20,
  };
  $

See? Now lets try to see if the perf developers used some sensible
naming for the structs representing the payload associated with those
perf_event_header types, and now we need to use whatever debuginfo is in
the perf tool binary when built with -g, which is DWARF, since the
kernel was not so nice and didn't provide types for those records:

  $ pahole perf_event_type
  enum perf_event_type {
  	PERF_RECORD_MMAP = 1,
  	PERF_RECORD_LOST = 2,
  	PERF_RECORD_COMM = 3,
  	PERF_RECORD_EXIT = 4,
  	PERF_RECORD_THROTTLE = 5,
  	PERF_RECORD_UNTHROTTLE = 6,
  	PERF_RECORD_FORK = 7,
  	PERF_RECORD_READ = 8,
  	PERF_RECORD_SAMPLE = 9,
  	PERF_RECORD_MMAP2 = 10,
  	PERF_RECORD_AUX = 11,
  	PERF_RECORD_ITRACE_START = 12,
  	PERF_RECORD_LOST_SAMPLES = 13,
  	PERF_RECORD_SWITCH = 14,
  	PERF_RECORD_SWITCH_CPU_WIDE = 15,
  	PERF_RECORD_NAMESPACES = 16,
  	PERF_RECORD_KSYMBOL = 17,
  	PERF_RECORD_BPF_EVENT = 18,
  	PERF_RECORD_CGROUP = 19,
  	PERF_RECORD_MAX = 20,
  };
  $ pahole ~/bin/perf -C perf_record_mmap
  struct perf_record_mmap {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */
  	__u64                      start;                /*    16     8 */
  	__u64                      len;                  /*    24     8 */
  	__u64                      pgoff;                /*    32     8 */
  	char                       filename[4096];       /*    40  4096 */

  	/* size: 4136, cachelines: 65, members: 7 */
  	/* last cacheline: 40 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_lost
  struct perf_record_lost {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      id;                   /*     8     8 */
  	__u64                      lost;                 /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* last cacheline: 24 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_comm
  struct perf_record_comm {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */
  	char                       comm[16];             /*    16    16 */

  	/* size: 32, cachelines: 1, members: 4 */
  	/* last cacheline: 32 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_exit
  $ pahole ~/bin/perf -C perf_record_throttle
  struct perf_record_throttle {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      time;                 /*     8     8 */
  	__u64                      id;                   /*    16     8 */
  	__u64                      stream_id;            /*    24     8 */

  	/* size: 32, cachelines: 1, members: 4 */
  	/* last cacheline: 32 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_unthrottle
  $ pahole ~/bin/perf -C perf_record_fork
  struct perf_record_fork {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      ppid;                 /*    12     4 */
  	__u32                      tid;                  /*    16     4 */
  	__u32                      ptid;                 /*    20     4 */
  	__u64                      time;                 /*    24     8 */

  	/* size: 32, cachelines: 1, members: 6 */
  	/* last cacheline: 32 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_exec
  $ pahole ~/bin/perf -C perf_record_read
  struct perf_record_read {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */
  	__u64                      value;                /*    16     8 */
  	__u64                      time_enabled;         /*    24     8 */
  	__u64                      time_running;         /*    32     8 */
  	__u64                      id;                   /*    40     8 */

  	/* size: 48, cachelines: 1, members: 7 */
  	/* last cacheline: 48 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_sample
  struct perf_record_sample {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      array[];              /*     8     0 */

  	/* size: 8, cachelines: 1, members: 2 */
  	/* last cacheline: 8 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_mmap2
  struct perf_record_mmap2 {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */
  	__u64                      start;                /*    16     8 */
  	__u64                      len;                  /*    24     8 */
  	__u64                      pgoff;                /*    32     8 */
  	__u32                      maj;                  /*    40     4 */
  	__u32                      min;                  /*    44     4 */
  	__u64                      ino;                  /*    48     8 */
  	__u64                      ino_generation;       /*    56     8 */
  	/* --- cacheline 1 boundary (64 bytes) --- */
  	__u32                      prot;                 /*    64     4 */
  	__u32                      flags;                /*    68     4 */
  	char                       filename[4096];       /*    72  4096 */

  	/* size: 4168, cachelines: 66, members: 13 */
  	/* last cacheline: 8 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_aux
  struct perf_record_aux {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      aux_offset;           /*     8     8 */
  	__u64                      aux_size;             /*    16     8 */
  	__u64                      flags;                /*    24     8 */

  	/* size: 32, cachelines: 1, members: 4 */
  	/* last cacheline: 32 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_itrace_start
  struct perf_record_itrace_start {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */

  	/* size: 16, cachelines: 1, members: 3 */
  	/* last cacheline: 16 bytes */
  };
  $ pahole ~/bin/perf -C perf_record_lost_samples
  struct perf_record_lost_samples {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      lost;                 /*     8     8 */

  	/* size: 16, cachelines: 1, members: 2 */
  	/* last cacheline: 16 bytes */
  };
  $

And so on. So most can be obtained this way, i.e. casts based on a type
selector present on the common part.

  $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = 0x4a,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00,
  	.id = 0x1,
  	.path = "/",
  },
  {
  	.header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00,
  	.id = 0x611,
  	.path = "/system.slice",
  },
  {
  	.header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00,
  	.id = 0x8,
  	.path = "/machine.slice",
  },
  {
  	.header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00,
  	.id = 0x1e94,
  	.path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope",
  },
  {
  	.header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00,
  	.id = 0xd,
  	.path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope",
  },
  {
  	.header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.comm = "perf",
  },
  {
  	.header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.comm = "sleep",
  },
  {
  	.header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x55ea2a865000,
  	.len = 0x4000,
  	.pgoff = 0x2000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e0526,
  	.ino_generation = 0xc97273c3,
  	.prot = 0x5,
  	.flags = 0x1802,
  	.filename = "/usr/bin/sleep",
  },
  {
  	.header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7f53040c7000,
  	.len = 0x20000,
  	.pgoff = 0x1000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e1faf,
  	.ino_generation = 0x83ee2ed3,
  	.prot = 0x5,
  	.flags = 0x1802,
  	.filename = "/usr/lib64/ld-2.29.so",
  },
  {
  	.header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7fffd8b69000,
  	.len = 0x2000,
  	.pgoff = 0,
  	.maj = 0,
  	.min = 0,
  	.ino = 0,
  	.ino_generation = 0,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "[vdso]",
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00,
  	.pid = 0x7e50,
  	.tid = 0x7e50,
  	.start = 0x7f5303efe000,
  	.len = 0x14d000,
  	.pgoff = 0x22000,
  	.maj = 0xfd,
  	.min = 0,
  	.ino = 0x1e028a,
  	.ino_generation = 0xd5947787,
  	.prot = 0x5,
  	.flags = 0x1002,
  	.filename = "/usr/lib64/libc-2.29.so",
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00,
  	.array = { },
  },
  {
  	.type = PERF_RECORD_EXIT,
  	.misc = 0,
  	.size = 0x30,
  },
  {
  	.type = 0x44,
  	.misc = 0,
  	.size = 0x8,
  },
  $

So now we go and get the whole thing in this command line:

Ok, we lost some stuff along the way, namely we need to do recursive pretty
printing, the .header field is being now defaulted to the primitive hex dumper
that is the last resort when pahole wasn't instructed to dtrt...

Tomorrow, probably :-)

But I'll point this to global kernel variables, that now we know the types,
--expand_pointers is there for just showing the structure, with support for
files, in addition to reading from stdin, we'll be able to get things from
random places, not just sequential, etc.

Addictive :-\

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 42b7a759f3 dutil: Add a strlwr() helper to lowercase a string, returning it
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo a5bb31b86f pahole: Fix --skip for variable sized records
We were not honouring 'sizeof=size' with --skip.

Before:

  $ pahole --seek_bytes=0x130 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 5 < perf.data
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = 0x4a,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x30,
  },
  $ pahole --seek_bytes=0x130 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 2 --skip 3 < perf.data
  {
  	.type = 0xcf8532e,
  	.misc = 0xffffffffffff8c52,
  	.size = 0x1,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },

After:

  $ pahole --seek_bytes=0x130 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 2 --skip 3 < perf.data
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x30,
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo f4df384d77 pahole: Decouple reading ->sizeof_member from printing
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>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 78cdde5cb7 pahole: Introduce 'type_enum' class argument
This, together with the 'type=member' allows for associating an enum to the
'type' member, which initially allows for looking up the value in the 'type'
member in the 'type_enum' to pretty print it, e.g.:

Hey, it is kernel ABI, so lets get it from BTF, way faster, and this way we can
test if all the enumerations, etc are readily available in the kernel BTF file,
and indeed they are :-)

Back to the pretty printing:

  $ pahole perf_event_header
  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 */
  };
  $

Check if that enum is there:

  $ pahole --seek_bytes=0x130 'perf_event_header(sizeof=size,type=type,type_enum=bla)' --count 26 < perf.data
  pahole: the type enum 'bla' wasn't found in '/sys/kernel/btf/vmlinux'
  $

Use the correct enumeration (enum perf_event_type):

  $ pahole --seek_bytes=0x130 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = 0x4a,
  	.misc = 0,
  	.size = 0x20,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x30,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x30,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x80,
  },
  {
  	.type = PERF_RECORD_CGROUP,
  	.misc = 0,
  	.size = 0x58,
  },
  {
  	.type = PERF_RECORD_COMM,
  	.misc = 0,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_COMM,
  	.misc = 0x2000,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_MMAP2,
  	.misc = 0x2,
  	.size = 0x68,
  },
  {
  	.type = PERF_RECORD_MMAP2,
  	.misc = 0x2,
  	.size = 0x70,
  },
  {
  	.type = PERF_RECORD_MMAP2,
  	.misc = 0x2,
  	.size = 0x60,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4001,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4001,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4001,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4001,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4001,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4001,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4002,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4002,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_MMAP2,
  	.misc = 0x2,
  	.size = 0x70,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4002,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_SAMPLE,
  	.misc = 0x4002,
  	.size = 0x28,
  },
  {
  	.type = PERF_RECORD_EXIT,
  	.misc = 0,
  	.size = 0x30,
  },
  {
  	.type = 0x44,
  	.misc = 0,
  	.size = 0x8,
  },
  $

But the coolest part comes next, when we'll lookup the enumerator, say,
PERF_RECORD_MMAP2, to find a type to pretty print what comes after the common
header fields, i.e.:

Ok, kernel BTF doesn't contains 'struct perf_record_mmap2', so back to using
perf's DWARF info :-)

  $ pahole -C perf_record_mmap2 ~/bin/perf
  struct perf_record_mmap2 {
  	struct perf_event_header   header;               /*     0     8 */
  	__u32                      pid;                  /*     8     4 */
  	__u32                      tid;                  /*    12     4 */
  	__u64                      start;                /*    16     8 */
  	__u64                      len;                  /*    24     8 */
  	__u64                      pgoff;                /*    32     8 */
  	__u32                      maj;                  /*    40     4 */
  	__u32                      min;                  /*    44     4 */
  	__u64                      ino;                  /*    48     8 */
  	__u64                      ino_generation;       /*    56     8 */
  	/* --- cacheline 1 boundary (64 bytes) --- */
  	__u32                      prot;                 /*    64     4 */
  	__u32                      flags;                /*    68     4 */
  	char                       filename[4096];       /*    72  4096 */

  	/* size: 4168, cachelines: 66, members: 13 */
  	/* last cacheline: 8 bytes */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00