~jpsamaroo/pahole

Add musl build support
4cfd420f — Arnaldo Carvalho de Melo 2 years ago
README: Add instructions to do a cross build

This was on a ubuntu:18.04 system, using their cross build packages and
elfutils and zlib cross built from sources.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
9e495f68 — Arnaldo Carvalho de Melo 2 years ago
dwarf_loader: Move vaddr to conditional where it is used

To avoid build failures in architectures where HAVE_DWFL_MODULE_BUILD_ID
isn't defined.

Noticed while cross building for s390x.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
69fce762 — Arnaldo Carvalho de Melo 2 years ago
pahole: Use "%s" in a snprintf call

To address this clang 11 build error:

[ 86%] Building C object CMakeFiles/pahole.dir/pahole.c.o
/home/acme/git/pahole/pahole.c:1626:33: error: format string is not a string literal (potentially insecure) [-Werror,-Wformat-security]
                        snprintf(name, sizeof(name), enumerator__name(enumerator, cu_enumerator));
                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/acme/git/pahole/pahole.c:1626:33: note: treat the string as an argument to avoid this
                        snprintf(name, sizeof(name), enumerator__name(enumerator, cu_enumerator));
                                                     ^
                                                     "%s",
1 error generated.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
22f93766 — Arnaldo Carvalho de Melo 2 years ago
pahole: Support multiple types for pretty printing

For now one has to specify them in the order they appear in the file,
i.e. for perf.data files where we have:

  $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data
  {
  	.magic = 0x32454c4946524550,
  	.size = 0x68,
  	.attr_size = 0x88,
  	.attrs = {
  		.offset = 0x168,
  		.size = 0x220,
  	},
  	.data = {
  		.offset = 0x388,
  		.size = 0x306698,
  	},
  	.event_types = {
  		.offset = 0,
  		.size = 0,
  	},
  	.adds_features = { 0x16717ffc, 0, 0, 0 },
  },
  $

We need to ask for pretty printing the attrs then the data sections, as:

  $ pahole ~/bin/perf --header=perf_file_header \
     -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data

Notice that both types have the range= setting where in the header it
should find the instances of its respective types, the result for this
perf.data file:

  $ perf evlist
  instructions
  cycles
  cache-misses
  dummy:HG
  $

Those events have these attributes, which we'll match in the pahole
output for the header 'attrs' range:

  $ perf evlist -v
  instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1
  cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1
  cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1
  dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1
  $

To make it more compact lets remove zeroed fields using grep:

  $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,'
  {
  	.attr = {
  		.size = 120,
  		.config = 1,
  		.sample_period = 4000,
  		.sample_freq = 4000,
  		.sample_type = 455,
  		.read_format = 4,
  		.disabled = 1,
  		.inherit = 1,
  		.freq = 1,
  		.sample_id_all = 1,
  		.exclude_guest = 1,
  	},
  	.ids = {
  		.offset = 104,
  		.size = 64,
  	},
  },
  {
  	.attr = {
  		.size = 120,
  		.sample_period = 4000,
  		.sample_freq = 4000,
  		.sample_type = 455,
  		.read_format = 4,
  		.disabled = 1,
  		.inherit = 1,
  		.freq = 1,
  		.sample_id_all = 1,
  		.exclude_guest = 1,
  	},
  	.ids = {
  		.offset = 168,
  		.size = 64,
  	},
  },
  {
  	.attr = {
  		.size = 120,
  		.config = 3,
  		.sample_period = 4000,
  		.sample_freq = 4000,
  		.sample_type = 455,
  		.read_format = 4,
  		.disabled = 1,
  		.inherit = 1,
  		.freq = 1,
  		.sample_id_all = 1,
  		.exclude_guest = 1,
  	},
  	.ids = {
  		.offset = 232,
  		.size = 64,
  	},
  },
  {
  	.attr = {
  		.type = 1,
  		.size = 120,
  		.config = 9,
  		.sample_period = 4000,
  		.sample_freq = 4000,
  		.sample_type = 455,
  		.read_format = 4,
  		.inherit = 1,
  		.mmap = 1,
  		.comm = 1,
  		.freq = 1,
  		.task = 1,
  		.sample_id_all = 1,
  		.mmap2 = 1,
  		.comm_exec = 1,
  		.ksymbol = 1,
  		.bpf_event = 1,
  	},
  	.ids = {
  		.offset = 296,
  		.size = 64,
  	},
  },
  {
  	.header = {
  		.type = PERF_RECORD_TIME_CONV,
  		.size = 32,
  	},
  	.time_shift = 31,
  	.time_mult = 1016798081,
  	.time_zero = 670877213069232,
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP,
  		.misc = 1,
  		.size = 96,
  	},
  	.pid = -1,
  	.start = -1929379840,
  	.len = 14683553,
  	.pgoff = -1929379840,
  	.filename = "[kernel.kallsyms]_text",
  },
  {
  	.header = {
  		.type = PERF_RECORD_MMAP,
  		.misc = 1,
  		.size = 136,
  	},
  	.pid = -1,
  	.start = -1072852992,
  	.len = 139264,
  	.filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz",
  },
<SNIP>
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 1,
  		.size = 56,
  	},
  	.array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 },
  },
  {
  	.header = {
  		.type = PERF_RECORD_SAMPLE,
  		.misc = 1,
  		.size = 56,
  	},
  	.array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 },
  },
  {
  	.type = PERF_RECORD_FINISHED_ROUND,
  	.size = 8,
  },
  $

Validation is done all around:

  $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,'
  pahole: --header_type=paerf_file_header not found
  $

  $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,'
  pahole: type 'perf_file_atatr' not found
  $

  $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,'
  pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs
  $
  $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,'
  pahole: type 'perf_event_hader' not found
  $
  $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null
  pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata
  $
  $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null
  pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type
  pahole: type 'perf_event_header' not found or attributes not validated
  $

The algorithm to find the types was improved to not fallback at the end,
but instead go on saving types that were found, which increases the
possibility of resolving all of them, at the end we just need to check
if all that is needed was found, printing relevant messages when this
isn't the case.

More to do, like pretty printing flags such as sample_type, etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
78f2177d — Arnaldo Carvalho de Melo 2 years ago
pahole: Print the evaluated range= per class

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
5c32b9d5 — Arnaldo Carvalho de Melo 2 years ago
pahole: Count the total number of bytes read from stdin

Another prep patch to allow for having multiple pretty printing types,
now we need to resolve all the types needed for all the classes to then
allow for multiple types.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
e3e5a462 — Arnaldo Carvalho de Melo 2 years ago
pahole: Make sure the header is read only once

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
208bcd98 — Arnaldo Carvalho de Melo 2 years ago
pahole: Introduce 'range=member' as a class argument for pretty printing

I.e. this:

  pahole ~/bin/perf --header=perf_file_header \
		    -C 'perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data

Is equivalent to:

  pahole ~/bin/perf --header=perf_file_header --range=data \
		    -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data

This is prep work for pretty printing multiple types of records, doing
it just fot that per-type range.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
b9e40631 — Arnaldo Carvalho de Melo 2 years ago
pahole: Cache the type_enum lookups into struct enumerator

I.e. when we get the type= field value, look it up in the type_enum=,
get the struct enumerator and if it is the first time that we're looking
up the string representation of that enumerator, do it and then cache
the results, so that next time we reuse it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
fda1825f — Arnaldo Carvalho de Melo 2 years ago
dwarves: Introduce tag_cu_node, so that we can have the leaner tag_cu

With just tag + cu pointers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
47d4dd4c — Arnaldo Carvalho de Melo 2 years ago
pahole: Optimize --header processing by keeping the first successfull instance

Since we store both the tag and the cu when we find it, defer deleting
it till we're completely sure we don't need it anymore.

Sometimes this is the only reason for us to fallback to looking at all
the cus to get all the needed types, so keeping it may make the DWARF
"stealer" to be able to, after finding the header type in one CU, find
the other needed types in another and we end up not having to process
all the CUs in a DWARF based session, speeding up the process.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
fdfc64ec — Arnaldo Carvalho de Melo 2 years ago
pahole: Introduce --range

For a data structure like:

  $ pahole --hex ~/bin/perf --header perf_file_header < 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 },
  },
  $

These are now equivalent:

  $ 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_user_event_type)' --count 1 --hex < perf.data
  {
  	.header = {
  		.type = PERF_RECORD_TIME_CONV,
  		.misc = 0,
  		.size = 0x20,
  	},
  	.time_shift = 0x1f,
  	.time_mult = 0x3c9b3031,
  	.time_zero = 0x18c520cf8532e,
  },
  $ pahole ~/bin/perf --header=perf_file_header --range=data -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --count 1 --hex < perf.data
  {
  	.header = {
  		.type = PERF_RECORD_TIME_CONV,
  		.misc = 0,
  		.size = 0x20,
  	},
  	.time_shift = 0x1f,
  	.time_mult = 0x3c9b3031,
  	.time_zero = 0x18c520cf8532e,
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
f63d3677 — Arnaldo Carvalho de Melo 2 years ago
pahole: Support multiple enums in type_enum=

See the pahole man page:

Some of the records are not found in 'type_enum=perf_event_type' so some of the
records don't get converted to a type that fully shows its contents. For perf
we know that those are in another enumeration, 'enum perf_user_event_type', so,
for these  cases,  we can create a 'virtual enum', i.e. the sum of two enums
and then get all those entries decoded and properly casted, first few records
with just 'enum perf_event_type':

  $ 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)' --count 4 < perf.data
  {
       .type = 79,
       .misc = 0,
       .size = 32,
  },
  {
       .type = 73,
       .misc = 0,
       .size = 40,
  },
  {
       .type = 74,
       .misc = 0,
       .size = 32,
  },
  {
       .header = {
            .type = PERF_RECORD_CGROUP,
            .misc = 0,
            .size = 40,
       },
       .id = 1,
       .path = "/",
  },
  $

Now with both enumerations, i.e. with 'type_enum=perf_event_type+perf_user_event_type':

  $ 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_user_event_type)' --count 5 < perf.data
  {
       .header = {
            .type = PERF_RECORD_TIME_CONV,
            .misc = 0,
            .size = 32,
       },
       .time_shift = 31,
       .time_mult = 1016803377,
       .time_zero = 435759009518382,
  },
  {
       .header = {
            .type = PERF_RECORD_THREAD_MAP,
            .misc = 0,
            .size = 40,
       },
       .nr = 1,
       .entries = 0x50 0x7e 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00,
  },
  {
       .header = {
            .type = PERF_RECORD_CPU_MAP,
            .misc = 0,
            .size = 32,
       },
       .data = {
            .type = 1,
            .data = "",
       },
  },
  {
       .header = {
            .type = PERF_RECORD_CGROUP,
            .misc = 0,
            .size = 40,
       },
       .id = 1,
       .path = "/",
  },
  {
       .header = {
            .type = PERF_RECORD_CGROUP,
            .misc = 0,
            .size = 48,
       },
       .id = 1553,
       .path = "/system.slice",
  },
  $

And since the fun never ends, this needs to be properly supported (arrays of
structs):

  $ pahole ~/bin/perf -C perf_record_thread_map_entry
  struct perf_record_thread_map_entry {
  	__u64                      pid;                  /*     0     8 */
  	char                       comm[16];             /*     8    16 */

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

that 'nr' field:

  $ pahole ~/bin/perf -C perf_record_thread_map
  struct perf_record_thread_map {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      nr;                   /*     8     8 */
  	struct perf_record_thread_map_entry entries[];   /*    16     0 */

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

So probably we need something like a file with types and its pretty printing
details, with one for 'struct perf_record_thread_map':

perf_record_thread_map(entries.nr_entries=$nr)

Meaning: the perf_record_thread_map 'entries' has a number of
sizeof(typeof entries) that is defined in the perf_record_thread_map
'nr' member.  Everything starting with a '$' needs to be evaluated, like
the '$header.*' we already have, since there is no 'namespace selector'
($header. in the header case) this means: the current record.

So, when pretty printing a record that has such type (perf_record_thread_map)
this has to be taken into account, and validated by the containing 'struct
perf_event_header->size' field.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
c50b6d37 — Arnaldo Carvalho de Melo 2 years ago
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>
671ea22c — Arnaldo Carvalho de Melo 2 years ago
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>
dd3c0e9e — Arnaldo Carvalho de Melo 2 years ago
dwarves: Move the common initialization of fields for 'struct type'

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
aa7ab3e8 — Arnaldo Carvalho de Melo 2 years ago
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>
4ece15c3 — Arnaldo Carvalho de Melo 2 years ago
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>
d28bc97b — Arnaldo Carvalho de Melo 2 years ago
man-pages: Document pretty printing capabilities and provide examples

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Next