Commit Graph

326 Commits

Author SHA1 Message Date
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 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 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 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 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 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
Arnaldo Carvalho de Melo 163c330d31 dwarves: Introduce cu__find_enumeration_by_name()
We'll use it in an upcoming feature, to pretty print fields that albeit
not declared as an enum, have values coming from one.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 20051b7157 pahole: Add the 'type' modifier to make a struct member be used to find a cast type
This is the first step:

  $ pahole -V -C 'perf_event_header(sizeof=size,type=type)' ~/bin/perf
  pahole: sizeof_operator for 'perf_event_header' is 'size'
  pahole: type member for 'perf_event_header' is '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 */
  };
  $

Next step is to add another modifier that will point to an enum that
maps the value in perf_event_header->type to a string that can then be
used to lookup the type to be used to pretty print what is after
'sizeof(struct perf_event_header)', i.e. things like:

  $ pahole -EC perf_record_mmap ~/bin/perf
  struct perf_record_mmap {
  	struct perf_event_header {
  		/* typedef __u32 */ unsigned int       type;     /*     0     4 */
  		/* typedef __u16 */ short unsigned int misc;     /*     4     2 */
  		/* typedef __u16 */ short unsigned int size;     /*     6     2 */
  	} header; /*     0     8 */
  	/* typedef __u32 */ unsigned int               pid;      /*     8     4 */
  	/* typedef __u32 */ unsigned int               tid;      /*    12     4 */
  	/* typedef __u64 */ long long unsigned int     start;    /*    16     8 */
  	/* typedef __u64 */ long long unsigned int     len;      /*    24     8 */
  	/* typedef __u64 */ long long unsigned int     pgoff;    /*    32     8 */
  	char                       filename[4096];               /*    40  4096 */

  	/* size: 4136, cachelines: 65, members: 7 */
  	/* last cacheline: 40 bytes */
  };
  $

The various validations performed:

  $ pahole -V -C 'perf_event_header(sizeof=size,typ=type)' ~/bin/perf
  pahole: sizeof_operator for 'perf_event_header' is 'size'
  pahole: invalid arg 'typ' in 'perf_event_header(sizeof=size,typ=type)' (known args: sizeof=member, type=member)
  $
  $ pahole -V -C 'perf_event_header(sizeof=size,type=bla)' ~/bin/perf
  pahole: sizeof_operator for 'perf_event_header' is 'size'
  pahole: type member for 'perf_event_header' is 'bla'
  pahole: the type member 'bla' wasn't found in the 'perf_event_header' type
  $
  $ pahole -V -C 'perf_event_header(sizeof=size,type=type)' ~/bin/perf
  pahole: sizeof_operator for 'perf_event_header' is 'size'
  pahole: type member for 'perf_event_header' is '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 */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo e0773683fa dwarves: Allow setting a struct/class member as the source of sizeof()
This one just allows that to be set, i.e.:

  $ pahole -C perf_event_header ~/bin/perf
  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 */
  };
  $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes=0x348 --count 1 ~/bin/perf < perf.data
  {
  	.type = 0xa,
  	.misc = 0x2,
  	.size = 0x68,
  },
  $

But:

  $ pahole -C 'perf_event_header(sizeof=bla)' --seek_bytes=0x348 --count 1 ~/bin/perf < perf.data
  pahole: the sizeof member 'bla' not found in the 'perf_event_header' type
  $

And:

  $ pahole -C 'perf_event_header(size=misc)' --seek_bytes=0x348 --count 1 ~/bin/perf < perf.data
  pahole: invalid arg 'size' in 'perf_event_header(size=misc)' (known args: sizeof=member)
  $

The next cset will implement sizeof(type) with that modifier, using the
stdin bytes to obtain the size (0x68) in the above case, and then we'll
be able to print a sequence of variable-sized records correctly.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo fe28422144 pahole: Introduce --seek_bytes
Works with stdio, will work with files where we'll use plain lseek and
allow for pretty printing trailer structs.

E.g.:

  $ objcopy -O binary --only-section=__versions drivers/scsi/sg.ko versions
  $ pahole -C modversion_info drivers/scsi/sg.ko
  struct modversion_info {
          long unsigned int          crc;                  /*     0     8 */
          char                       name[56];             /*     8    56 */

          /* size: 64, cachelines: 1, members: 2 */
  };
  $ pahole --count 2 -C modversion_info drivers/scsi/sg.ko < versions
  {
  	.crc = 0x8dabd84,
  	.name = "module_layout",
  },
  {
  	.crc = 0x45e4617b,
  	.name = "no_llseek",
  },
  $ pahole --skip 1 --count 1 -C modversion_info drivers/scsi/sg.ko < versions
  {
  	.crc = 0x45e4617b,
  	.name = "no_llseek",
  },

Then the equivalent, skipping sizeof(modversion_info) explicitely:

  $ pahole --seek_bytes 64 --count 1 -C modversion_info drivers/scsi/sg.ko < versions
  {
  	.crc = 0x45e4617b,
  	.name = "no_llseek",
  },
  $

Using a perf.data file generated by 'perf record':

  $ perf report -D | head -18
  # 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�.R...

  0 0x130 [0x20]: PERF_RECORD_TIME_CONV: unhandled!

  0x150 [0x28]: event: 73
  .
  . ... raw event: size 40 bytes
  .  0000:  49 00 00 00 00 00 28 00 01 00 00 00 00 00 00 00  I.....(.........
  .  0010:  50 7e 00 00 00 00 00 00 00 00 00 00 00 00 00 00  P~..............
  .  0020:  00 00 00 00 00 00 00 00                          ........

  $ pahole --seek_bytes 0x130 --count 1 -C perf_event_header < perf.data
  {
  	.type = 0x4f,
  	.misc = 0,
  	.size = 0x20,
  },
  $ printf "0x%x\n" 79
  0x4f
  $ pahole --seek_bytes 0x150 --count 1 -C perf_event_header < perf.data
  {
  	.type = 0x49,
  	.misc = 0,
  	.size = 0x28,
  },
  $ printf "0x%x\n" 73
  0x49
  $

Now to use more complex types, again using perf.data files.

  # perf record -a sleep 1
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 3.834 MB perf.data (31853 samples) ]
  # perf report -D | grep -m1 -B20 PERF_RECORD_BPF
  0x6aa0 [0x58]: event: 17
  .
  . ... raw event: size 88 bytes
  .  0020:  5f 37 62 65 34 39 65 33 39 33 34 61 31 32 35 62  _7be49e3934a125b
  .  0030:  61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  a...............
  .  0040:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  .  0050:  00 00 00 00 00 00 00 00                          ........

  0 0 0x6aa0 [0x58]: PERF_RECORD_KSYMBOL addr ffffffffc03e0e90 len 203 type 1 flags 0x0 name bpf_prog_7be49e3934a125ba

  0x6af8 [0x38]: event: 18
  .
  . ... raw event: size 56 bytes
  .  0000:  12 00 00 00 00 00 38 00 01 00 00 00 11 00 00 00  ......8.........
  .  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                          ........

  0 0 0x6af8 [0x38]: PERF_RECORD_BPF_EVENT type 1, flags 0, id 17
  Binary file (standard input) matches
  # pahole -C perf_record_bpf_event ~/bin/perf
  struct perf_record_bpf_event {
  	struct perf_event_header   header;               /*     0     8 */
  	__u16                      type;                 /*     8     2 */
  	__u16                      flags;                /*    10     2 */
  	__u32                      id;                   /*    12     4 */
  	__u8                       tag[8];               /*    16     8 */

  	/* size: 24, cachelines: 1, members: 5 */
  	/* last cacheline: 24 bytes */
  };
  # pahole -C perf_record_bpf_event --seek_bytes 0x6af8 --count 1 ~/bin/perf < perf.data
  {
  	.header = 0x12 0x00 0x00 0x00 0x00 0x00 0x38 0x00,
  	.type = 0x1,
  	.flags = 0,
  	.id = 0x11,
  	.tag = { 0, 0, 0, 0, 0, 0, 0, 0},
  },
  # printf "0x%x\n" 18
  0x12
  # pahole -C perf_record_ksymbol ~/bin/perf
  struct perf_record_ksymbol {
  	struct perf_event_header   header;               /*     0     8 */
  	__u64                      addr;                 /*     8     8 */
  	__u32                      len;                  /*    16     4 */
  	__u16                      ksym_type;            /*    20     2 */
  	__u16                      flags;                /*    22     2 */
  	char                       name[256];            /*    24   256 */

  	/* size: 280, cachelines: 5, members: 6 */
  	/* last cacheline: 24 bytes */
  };
  # pahole -C perf_record_ksymbol --seek_bytes 0x6aa0 --count 1 ~/bin/perf < perf.data
  {
  	.header = 0x11 0x00 0x00 0x00 0x00 0x00 0x58 0x00,
  	.addr = 0xffffffffc03e0e90,
  	.len = 0xcb,
  	.ksym_type = 0x1,
  	.flags = 0,
  	.name = "bpf_prog_7be49e3934a125ba",
  },
  # printf "0x%x\n" 17
  0x11
  #

Need to recursively pretty print substructs, but all seems to work with
the simple hexdump.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-01 09:25:57 -03:00
Arnaldo Carvalho de Melo 44af7c02b5 pahole: Implement --skip, just like dd
$ objcopy -O binary --only-section=__versions drivers/scsi/sg.ko versions
  $ pahole -C modversion_info drivers/scsi/sg.ko
  struct modversion_info {
  	long unsigned int          crc;                  /*     0     8 */
  	char                       name[56];             /*     8    56 */

  	/* size: 64, cachelines: 1, members: 2 */
  };
  $ pahole --count 3 -C modversion_info drivers/scsi/sg.ko < versions
  {
  	.crc = 0x8dabd84,
  	.name = "module_layout",
  },
  {
  	.crc = 0x45e4617b,
  	.name = "no_llseek",
  },
  {
  	.crc = 0xa23fae8c,
  	.name = "param_ops_int",
  },
  $ pahole --skip 1 --count 2 -C modversion_info drivers/scsi/sg.ko < versions
  {
  	.crc = 0x45e4617b,
  	.name = "no_llseek",
  },
  {
  	.crc = 0xa23fae8c,
  	.name = "param_ops_int",
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-01 08:36:28 -03:00
Arnaldo Carvalho de Melo 5631fdcea1 pahole: Introduce --count, just like dd's
$ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions
  $ pahole --count 2 alt_instr  < .altinstructions
  {
  	.instr_offset = 0xffffffffffe0e690,
  	.repl_offset = 0x4873,
  	.cpuid = 0x70,
  	.instrlen = 0x5,
  	.replacementlen = 0x5,
  	.padlen = 0,
  },
  {
  	.instr_offset = 0xffffffffffe0e683,
  	.repl_offset = 0x486b,
  	.cpuid = 0x129,
  	.instrlen = 0x5,
  	.replacementlen = 0x5,
  	.padlen = 0,
  },
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-01 08:18:42 -03:00
Arnaldo Carvalho de Melo 1b2cdda38c dwarves: Introduce tag__is_array()
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-27 09:40:51 -03:00
Arnaldo Carvalho de Melo cc65946e30 dwarves: Adopt tag__is_base_type() from ctrace.c
We'll need it in pahole when pretty printing raw data as structs, etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:14:33 -03:00
Arnaldo Carvalho de Melo ded5d36f9c dwarves: Introduce cu__find_type_by_name
To look at all the 'struct type' descendants, like enums, typedefs,
structs, unions, etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-01-15 13:45:47 -03:00
Arnaldo Carvalho de Melo 66e640508e dwarves: Make function__for_each_parameter receive 'cu' arg
Needed to find the right 'struct ftype' to iterate function arguments
on. This is due to how BTF works with this and how we implemented it, at
some point this can get improved to avoid the need for checking if it is
BTF, doing it in a more format abstracted way, but for now, abstract
this away in the for_each_parameter helpers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-01-09 14:16:52 -03:00
Arnaldo Carvalho de Melo ccf3eebfcd btf_loader: Add support for BTF_KIND_FUNC
Some changes to the fprintf routines were needed, as BTF has as the
function type just a BTF_KIND_FUNC_PROTO, while DWARF has as the type
for a function its return value type. With a function->btf flag this was
overcome and all the other goodies in pfunct are present, for instance:

  $ pahole -JV examples/tcp.o | grep -w FUNC | head
  [4068] FUNC tcp_init type_id=4067
  [4070] FUNC tcp_abort type_id=4069
  [4072] FUNC tcp_done type_id=4071
  [4074] FUNC tcp_md5_hash_key type_id=4073
  [4076] FUNC tcp_md5_hash_skb_data type_id=4075
  [4078] FUNC tcp_get_md5sig_pool type_id=4077
  [4080] FUNC tcp_alloc_md5sig_pool type_id=4079
  [4082] FUNC compat_tcp_getsockopt type_id=4081
  [4084] FUNC tcp_getsockopt type_id=4083
  [4086] FUNC tcp_get_timestamping_opt_stats type_id=4085
  $

  $ pfunct -F btf examples/tcp.o  | head
  memset
  memcpy
  tcp_enter_memory_pressure
  tcp_leave_memory_pressure
  tcp_init_sock
  tcp_init_transfer
  tcp_poll
  tcp_ioctl
  tcp_splice_read
  sk_stream_alloc_skb
  $

  $ pfunct --prototype -F btf examples/tcp.o | head
  void * memset(void * p, int c, __kernel_size_t size);
  void * memcpy(void * p, const void  * q, __kernel_size_t size);
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  __poll_t tcp_poll(struct file * file, struct socket * sock, poll_table * wait);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  ssize_t tcp_splice_read(struct socket * sock, loff_t * ppos, struct pipe_inode_info * pipe, size_t len, unsigned int flags);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  $

Now to ask just for the 'struct sock' 'methods', i.e. functions that
have as one of its arguments a pointer to the given 'class' name:

  $ pfunct --class sock -F btf examples/tcp.o | head
  tcp_abort
  tcp_done
  compat_tcp_getsockopt
  tcp_getsockopt
  tcp_get_info
  compat_tcp_setsockopt
  tcp_setsockopt
  tcp_disconnect
  tcp_write_queue_purge
  tcp_close
  $

Then ask for the prototypes, which requires -V, should have that fixed:

  $ pfunct -V --prototypes --class sock -F btf examples/tcp.o | head
  int tcp_abort(struct sock * sk, int err);
  void tcp_done(struct sock * sk);
  int compat_tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen);
  int tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen);
  void tcp_get_info(struct sock * sk, struct tcp_info * info);
  int compat_tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  int tcp_disconnect(struct sock * sk, int flags);
  void tcp_write_queue_purge(struct sock * sk);
  void tcp_close(struct sock * sk, long int timeout);
  $

Don't like prototypes with parm names, got you covered:

  $ pfunct --no_parm_names -V --prototypes --class sock -F btf examples/tcp.o | head
  int tcp_abort(struct sock *, int);
  void tcp_done(struct sock *);
  int compat_tcp_getsockopt(struct sock *, int, int, char *, int *);
  int tcp_getsockopt(struct sock *, int, int, char *, int *);
  void tcp_get_info(struct sock *, struct tcp_info *);
  int compat_tcp_setsockopt(struct sock *, int, int, char *, unsigned int);
  int tcp_setsockopt(struct sock *, int, int, char *, unsigned int);
  int tcp_disconnect(struct sock *, int);
  void tcp_write_queue_purge(struct sock *);
  void tcp_close(struct sock *, long int);
  $

Don't like long options and want just one function?

  $ pfunct -f tcp_setsockopt -F btf examples/tcp.o
  int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  $

Want to generate compileable code for all of those functions, full with
the necessary types, etc?

  $ pfunct -F btf --compile examples/tcp.o  > a.c
  $ gcc -c -o a.o a.c
  $ pfunct -F dwarf --prototypes --class sock a.o | head
  pfunct: a.o: No debugging information found
  $ gcc -g -c -o a.o a.c
  $ pfunct -V -F dwarf --prototypes --class sock a.o | head
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size);
  $

Now lets go full circle and encode BTF for this a.o generated from
source code generated from the original BTF info in that examples/tcp.o
file:

  $ pahole -JV a.o | tail
  [465] FUNC_PROTO (anon) return=35 args=(392 hp, 393 skb, 5 header_len)
  [466] FUNC tcp_md5_hash_skb_data type_id=465
  [467] FUNC_PROTO (anon) return=35 args=(392 hp, 394 key)
  [468] FUNC tcp_md5_hash_key type_id=467
  [469] FUNC_PROTO (anon) return=0 args=(49 sk)
  [470] FUNC tcp_done type_id=469
  [471] FUNC_PROTO (anon) return=35 args=(49 sk, 35 err)
  [472] FUNC tcp_abort type_id=471
  [473] FUNC_PROTO (anon) return=0 args=(void)
  [474] FUNC tcp_init type_id=473
  $

  $ pfunct -F btf -V --prototypes --class=sock a.o | head
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size);
  $

Curious about the code generated by 'pfunct -F btf --compile examples/tcp.o?

  http://vger.kernel.org/~acme/pahole/pfunct-F-BTF--compile-examples-tcp.o.txt

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-05 12:04:23 -03:00
Andrii Nakryiko 3c5f2a224a btf_encoder: Preserve and encode exported functions as BTF_KIND_FUNC
Add encoding of DWARF's DW_TAG_subprogram_type into BTF's BTF_KIND_FUNC
(plus corresponding BTF_KIND_FUNC_PROTO). Only exported functions are
converted for now. This allows to capture all the exported kernel
functions, same subset that's exposed through /proc/kallsyms.

Committer testing:

Before:

  $ readelf -SW vmlinux  | grep BTF
    [78] .BTF              PROGBITS        0000000000000000 26a27da9 1e5543 00      0   0  1
  $

After:

  $ pahole -J vmlinux
  $ readelf -SW vmlinux  | grep BTF
    [78] .BTF              PROGBITS        0000000000000000 26a27da9 2d5f47 00      0   0  1
  $

  >>> 0x2d5f47 - 0x1e5543
  985604

The kernel has a lot of functions! :-)

  $ pahole -VJ vmlinux > /tmp/pahole-btf-encoding-verbose-output.txt

  $ grep -w FUNC /tmp/pahole-btf-encoding-verbose-output.txt | wc -l
  22871
  [acme@quaco pahole]$ grep -w FUNC /tmp/pahole-btf-encoding-verbose-output.txt | tail
  [4511543] FUNC copy_from_user_nmi type_id=4511542
  [4512934] FUNC memcpy_page_flushcache type_id=4512933
  [4512936] FUNC __memcpy_flushcache type_id=4512935
  [4512938] FUNC __copy_user_flushcache type_id=4512937
  [4512940] FUNC arch_wb_cache_pmem type_id=4512939
  [4512942] FUNC mcsafe_handle_tail type_id=4512941
  [4512944] FUNC copy_user_handle_tail type_id=4512943
  [4512946] FUNC clear_user type_id=4512945
  [4512948] FUNC __clear_user type_id=4512947
  [4512950] FUNC memcpy type_id=4512949
  $ grep -w FUNC_PROTO /tmp/pahole-btf-encoding-verbose-output.txt | tail
  [4512902] FUNC_PROTO (anon) return=4511725 args=(4512097 (anon), 4511544 (anon))
  [4512933] FUNC_PROTO (anon) return=0 args=(4511598 to, 4511725 page, 4511610 offset, 4511610 len)
  [4512935] FUNC_PROTO (anon) return=0 args=(4511638 _dst, 4511759 _src, 4511610 size)
  [4512937] FUNC_PROTO (anon) return=4511585 args=(4511638 dst, 4511759 src, 4511552 size)
  [4512939] FUNC_PROTO (anon) return=0 args=(4511638 addr, 4511610 size)
  [4512941] FUNC_PROTO (anon) return=4511544 args=(4511598 to, 4511598 from, 4511552 len)
  [4512943] FUNC_PROTO (anon) return=4511544 args=(4511598 to, 4511598 from, 4511552 len)
  [4512945] FUNC_PROTO (anon) return=4511544 args=(4511638 to, 4511544 n)
  [4512947] FUNC_PROTO (anon) return=4511544 args=(4511638 addr, 4511544 size)
  [4512949] FUNC_PROTO (anon) return=4511638 args=(4511638 p, 4511759 q, 4511591 size)
  $ grep -w FUNC_PROTO /tmp/pahole-btf-encoding-verbose-output.txt |grep 4511542
  [4511542] FUNC_PROTO (anon) return=4510159 args=(4510254 to, 4510374 from, 4510159 n)
  $

With a little change to pdwtags to see DW_TAG_subroutine_type, which is
what BTF's KIND_FUNC_PROTO maps to, we see some of those last
prototypes:

[acme@quaco pahole]$ pdwtags -F btf vmlinux  | grep '()(' | tail
void ()(struct insn * insn); /* size: 45404744 */
int ()(struct insn * insn); /* size: 4 */
void ()(struct insn * insn, const void  * kaddr, int buf_len, int x86_64); /* size: 45405032 */
long unsigned int ()(const char  * purpose); /* size: 8 */
void ()(char * to, struct page * page, size_t offset, size_t len); /* size: 45405864 */
void ()(void * _dst, const void  * _src, size_t size); /* size: 45406200 */
long int ()(void * dst, const void  * src, unsigned int size); /* size: 8 */
long unsigned int ()(char * to, char * from, unsigned int len); /* size: 8 */
long unsigned int ()(void * to, long unsigned int n); /* size: 8 */
long unsigned int ()(void * addr, long unsigned int size); /* size: 8 */
[acme@quaco pahole]$

I.e.:

  [4512941] FUNC_PROTO (anon) return=4511544 args=(4511598 to, 4511598 from, 4511552 len)

gets decoded by pdwtags as:

  long unsigned int ()(char * to, char * from, unsigned int len); /* size: 8 */

  $ grep '\[\(4511544\|4511598\|4511550\|4511552\)\]' /tmp/pahole-btf-encoding-verbose-output.txt
  [4511544] INT long unsigned int size=8 bit_offset=0 nr_bits=64 encoding=(none)
  [4511550] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [4511552] INT unsigned int size=4 bit_offset=0 nr_bits=32 encoding=(none)
  [4511598] PTR (anon) type_id=4511550
  $

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Cc: kernel-team@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-05 09:27:58 -03:00
Arnaldo Carvalho de Melo 0fb727166a pfunct: Strip inlines in the code generated for --compile
If we have:

inline void process_adjtimex_modes(const struct __kernel_timex  * txc, s32 * time_tai)
{
}

And any other struct receiving as a parameter pointers to 'struct
__kerne_timex', then the source file with the above inline, since it
doesn't have any inline expansion, i.e. 'pfunct --compile' generates
just empty function bodies, the types won't be included in the resulting
.o.

Since the original file has the expansions, type types will be there and
thus we will not be able to compare those types, so ask for any 'inline'
to be stripped, so that we keep those types and 'fullcircle' can do its
work.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 15:00:44 -03:00
Arnaldo Carvalho de Melo 2bcb01fc2f fprintf: Handle single zero sized array member structs
/home/acme/git/build/v5.1-rc4+/fs/proc/kcore.o
  /tmp/fullcircle.Vnd2oz.c:788:29: error: flexible array member in a struct with no named members
    char                       x[];                  /*     0     0 */

Original:

include/linux/mmzone.h, line 109:

  /*
   * zone->lock and the zone lru_lock are two of the hottest locks in the kernel.
   * So add a wild amount of padding here to ensure that they fall into separate
   * cachelines.  There are very few zone structures in the machine, so space
   * consumption is not a concern here.
   */
  #if defined(CONFIG_SMP)
  struct zone_padding {
          char x[0];
  } ____cacheline_internodealigned_in_smp;
  #define ZONE_PADDING(name)      struct zone_padding name;
  #else
  #define ZONE_PADDING(name)
  #endif

B0rken:

  struct zone_padding {
          char                       x[];                 /*     0     0 */

          /* size: 0, cachelines: 0, members: 1 */
  } __attribute__((__aligned__(64)));

Fixed:

  struct zone_padding {
          char                       x[0];                 /*     0     0 */

          /* size: 0, cachelines: 0, members: 1 */
  } __attribute__((__aligned__(64)));

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 17:20:11 -03:00
Arnaldo Carvalho de Melo 0987266cd9 fprintf: Deal with zero sized arrays in the midle of a union
gcc dislikes invalid C:

  /home/acme/git/build/v5.1-rc4+/fs/ceph/export.o
  /tmp/fullcircle.CvAfpM.c:591:22: error: flexible array member in union
     __u32              raw[];                /*     0     0 */
                        ^~~
Before:

  $ pahole -C fid /home/acme/git/build/v5.1-rc4+/fs/ceph/export.o
  struct fid {
  	union {
  		struct {
  			u32        ino;                  /*     0     4 */
  			u32        gen;                  /*     4     4 */
  			u32        parent_ino;           /*     8     4 */
  			u32        parent_gen;           /*    12     4 */
  		} i32;                                   /*     0    16 */
  		struct {
  			u32        block;                /*     0     4 */
  			u16        partref;              /*     4     2 */
  			u16        parent_partref;       /*     6     2 */
  			u32        generation;           /*     8     4 */
  			u32        parent_block;         /*    12     4 */
  			u32        parent_generation;    /*    16     4 */
  		} udf;                                   /*     0    20 */
  		__u32              raw[];                /*     0     0 */
  	};                                               /*     0    20 */

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

After:

  $ pahole -C fid /home/acme/git/build/v5.1-rc4+/fs/ceph/export.o
  struct fid {
  	union {
  		struct {
  			u32        ino;                  /*     0     4 */
  			u32        gen;                  /*     4     4 */
  			u32        parent_ino;           /*     8     4 */
  			u32        parent_gen;           /*    12     4 */
  		} i32;                                   /*     0    16 */
  		struct {
  			u32        block;                /*     0     4 */
  			u16        partref;              /*     4     2 */
  			u16        parent_partref;       /*     6     2 */
  			u32        generation;           /*     8     4 */
  			u32        parent_block;         /*    12     4 */
  			u32        parent_generation;    /*    16     4 */
  		} udf;                                   /*     0    20 */
  		__u32              raw[0];               /*     0     0 */
  	};                                               /*     0    20 */

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

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 17:09:03 -03:00
Arnaldo Carvalho de Melo 1101337a74 fprintf: Deal with zero sized arrays in the middle of a struct
Consider:

  struct ipc64_perm {
          __kernel_key_t          key;
          __kernel_uid32_t        uid;
          __kernel_gid32_t        gid;
          __kernel_uid32_t        cuid;
          __kernel_gid32_t        cgid;
          __kernel_mode_t         mode;
                                  /* pad if mode_t is u16: */
          unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
          unsigned short          seq;
          unsigned short          __pad2;
          __kernel_ulong_t        __unused1;
          __kernel_ulong_t        __unused2;
  };

That is a roundabout way of using __attribute__(__aligned__(4)), but
should work nonetheless.

We were not putting the [0] in that zero sized array which ended up
making gcc complain with:

  $ gcc -g -c shm.c
  shm.c:199:29: error: flexible array member not at end of struct
    unsigned char              __pad1[];            /*    24     0 */
                               ^~~~~~
  $

Now this works, i.e. generates compilable source code out of the
type tags, be it from BTF or from DWARF, i.e. this is all from the
internal representation of such types, agnostic wrt the original type
format.

So, the full circle:

  $ pahole -C ipc64_perm /home/acme/git/build/v5.1-rc4+/ipc/shm.o
  struct ipc64_perm {
  	__kernel_key_t             key;                  /*     0     4 */
  	__kernel_uid32_t           uid;                  /*     4     4 */
  	__kernel_gid32_t           gid;                  /*     8     4 */
  	__kernel_uid32_t           cuid;                 /*    12     4 */
  	__kernel_gid32_t           cgid;                 /*    16     4 */
  	__kernel_mode_t            mode;                 /*    20     4 */
  	unsigned char              __pad1[0];            /*    24     0 */
  	short unsigned int         seq;                  /*    24     2 */
  	short unsigned int         __pad2;               /*    26     2 */

  	/* XXX 4 bytes hole, try to pack */

  	__kernel_ulong_t           __unused1;            /*    32     8 */
  	__kernel_ulong_t           __unused2;            /*    40     8 */

  	/* size: 48, cachelines: 1, members: 11 */
  	/* sum members: 44, holes: 1, sum holes: 4 */
  	/* last cacheline: 48 bytes */
  };
  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/ipc/shm.o > shm.c
  $ gcc -g -c shm.c
  $ pahole -C ipc64_perm shm.o
  struct ipc64_perm {
  	__kernel_key_t             key;                  /*     0     4 */
  	__kernel_uid32_t           uid;                  /*     4     4 */
  	__kernel_gid32_t           gid;                  /*     8     4 */
  	__kernel_uid32_t           cuid;                 /*    12     4 */
  	__kernel_gid32_t           cgid;                 /*    16     4 */
  	__kernel_mode_t            mode;                 /*    20     4 */
  	unsigned char              __pad1[0];            /*    24     0 */
  	short unsigned int         seq;                  /*    24     2 */
  	short unsigned int         __pad2;               /*    26     2 */

  	/* XXX 4 bytes hole, try to pack */

  	__kernel_ulong_t           __unused1;            /*    32     8 */
  	__kernel_ulong_t           __unused2;            /*    40     8 */

  	/* size: 48, cachelines: 1, members: 11 */
  	/* sum members: 44, holes: 1, sum holes: 4 */
  	/* last cacheline: 48 bytes */
  };
  $

And for a chuckle, the original source code with a bit of history about
struct layout worries:

include/uapi/asm-generic/ipcbuf.h:

  /*
   * The generic ipc64_perm structure:
   * Note extra padding because this structure is passed back and forth
   * between kernel and user space.
   *
   * ipc64_perm was originally meant to be architecture specific, but
   * everyone just ended up making identical copies without specific
   * optimizations, so we may just as well all use the same one.
   *
   * Pad space is left for:
   * - 32-bit mode_t on architectures that only had 16 bit
   * - 32-bit seq
   * - 2 miscellaneous 32-bit values
   */

  struct ipc64_perm {
          __kernel_key_t          key;
          __kernel_uid32_t        uid;
          __kernel_gid32_t        gid;
          __kernel_uid32_t        cuid;
          __kernel_gid32_t        cgid;
          __kernel_mode_t         mode;
                                  /* pad if mode_t is u16: */
          unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
          unsigned short          seq;
          unsigned short          __pad2;
          __kernel_ulong_t        __unused1;
          __kernel_ulong_t        __unused2;
  };

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 16:54:15 -03:00
Arnaldo Carvalho de Melo c8fc6f5a7a core: Use unnatural alignment of struct embedded in another to infer __packed__
Since we don't have something like DW_AT_alignment for
__attribute__((__packed__)), we need to use whatever hints that are
there in the alignments to figure out if a naturally packed struct has
the __attribute__((packed)) in the original sources, because that is
needed to waiver its natural alignment requisites.

For instance,

  /* Used at: btrfs.c */
  /* <1e7b> /home/acme/git/pahole/btrfs.c:199 */
  struct btrfs_block_group_cache {
          struct btrfs_key   key;                          /*     0    17 */
          struct btrfs_block_group_item item;              /*    17    24 */

          /* XXX 7 bytes hole, try to pack */

          struct btrfs_fs_info *     fs_info;              /*    48     8 */
          struct inode *             inode;                /*    56     8 */

In the original source code, btrfs_block_group_item is marked
__packed__, and being so, even seemingly unnecessarily, makes it, when
embedded in another struct, like the above, forfeit its natural
alingment, that would be 8 bytes, and instead appear right at the 17th
byte offset...

  struct btrfs_block_group_item {
          __le64                     used;                 /*     0     8 */
          __le64                     chunk_objectid;       /*     8     8 */
          __le64                     flags;                /*    16     8 */

          /* size: 24, cachelines: 1, members: 3 */
          /* last cacheline: 24 bytes */
  } __attribute__((__packed__));

So we need to, seeing its use at a unnatural offset, go backwards to the
btrfs_block_group_item pahole internal data structure, 'struct type' and
mark is_packed field as 'true', despite it not looking like a packed
struct.

Same thing with:

  struct ieee80211_mcs_info {
          u8                         rx_mask[10];          /*     0    10 */
          __le16                     rx_highest;           /*    10     2 */
          u8                         tx_params;            /*    12     1 */
          u8                         reserved[3];          /*    13     3 */

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

That is naturally aligned and as 16 bytes, a power of two, then when it appears at the end of:

  $ pahole -IC ieee80211_sta_ht_cap vht.o
  /* Used at: vht.c */
  /* <31ea> /home/acme/git/pahole/vht.c:1769 */
  struct ieee80211_sta_ht_cap {
  	u16                        cap;                  /*     0     2 */
  	bool                       ht_supported;         /*     2     1 */
  	u8                         ampdu_factor;         /*     3     1 */
  	u8                         ampdu_density;        /*     4     1 */

  	/* XXX 1 byte hole, try to pack */

  	struct ieee80211_mcs_info mcs;                   /*     6    16 */

  	/* size: 22, cachelines: 1, members: 5 */
  	/* sum members: 21, holes: 1, sum holes: 1 */
  	/* last cacheline: 22 bytes */
  };
  $

We get that one byte hole if ieee80211_mcs_info isn't marked __packed__, as soon as we mark it:

  $ pahole -IC ieee80211_sta_ht_cap vht.o
  /* Used at: vht.c */
  /* <31ea> /home/acme/git/pahole/vht.c:1769 */
  struct ieee80211_sta_ht_cap {
  	u16                        cap;                  /*     0     2 */
  	bool                       ht_supported;         /*     2     1 */
  	u8                         ampdu_factor;         /*     3     1 */
  	u8                         ampdu_density;        /*     4     1 */
  	struct ieee80211_mcs_info mcs;                   /*     5    16 */

  	/* size: 22, cachelines: 1, members: 5 */
  	/* padding: 1 */
  	/* last cacheline: 22 bytes */
  };
  [acme@quaco pahole]$

It works, so __packed__ in this case just says: trow away the natural
alignment, make it 1 in whatever container structs.

So, before emitting the types for some struct, we go back looking at
each of its members and checking for such unnatural offsets, marking the
types as __packed__. Now:

  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/net/mac80211/vht.o | grep "^struct ieee80211_mcs_info" -A8
  struct ieee80211_mcs_info {
  	u8                         rx_mask[10];          /*     0    10 */
  	__le16                     rx_highest;           /*    10     2 */
  	u8                         tx_params;            /*    12     1 */
  	u8                         reserved[3];          /*    13     3 */

  	/* size: 16, cachelines: 1, members: 4 */
  	/* last cacheline: 16 bytes */
  } __attribute__((__packed__));
  $

  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/btrfs/free-space-tree.o | grep "^struct btrfs_block_group_item" -A7
  struct btrfs_block_group_item {
  	__le64                     used;                 /*     0     8 */
  	__le64                     chunk_objectid;       /*     8     8 */
  	__le64                     flags;                /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* last cacheline: 24 bytes */
  } __attribute__((__packed__));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 15:51:16 -03:00
Arnaldo Carvalho de Melo 9a4d719304 fprintf: Allow suppressing the inferred __attribute__((__packed__))
We use things like DW_AT_alignment, so not all of those attributes are
inferred by formats like BTF that lack that info, allow suppressing the
output and make btfdiff ask for both DWARF and BTF output to have this
suppressed.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 15:01:53 -03:00
Arnaldo Carvalho de Melo ec935ee422 fprintf: Allow suppressing the output of force paddings at the end of structs
Things like 'struct timex' in the linux kernel led to this output:

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 14:45:29 -03:00
Arnaldo Carvalho de Melo 49c27bdd66 core: Allow the loaders to advertise features they have
For instance, DWARF has DW_AT_alignment, and some output features
require that, so let loaders advertise such things, next patch will use
this info.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 14:45:27 -03:00
Arnaldo Carvalho de Melo f78633cfb9 core: Infer __packed__ for union struct members
I.e. check that all the structs that are embedded in a union have their natural
alignment satisfied by the size of the array they are contained in,

Before this change we ended up not marking union struct members that didn't had
natural alignment violations as __packed__ even tho they had to be to be in a
struct that didn't satisfied their natural alignment requirements, which would
violate them when said union was in an array, i.e. the second entry would have
the non __packed__ union struct member in a bad position.

E.g. Before:

  $ pahole -C ceph_osd_op /home/acme/git/build/v5.1-rc4+/net/ceph/osd_client.o
  struct ceph_osd_op {
          __le16                     op;                   /*     0     2 */
          __le32                     flags;                /*     2     4 */
          union {
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     truncate_size;        /*    22     8 */
                          __le32     truncate_seq;         /*    30     4 */
                  } __attribute__((__packed__)) extent;    /*     6    28 */
                  struct {
                          __le32     name_len;             /*     6     4 */
                          __le32     value_len;            /*    10     4 */
                          __u8       cmp_op;               /*    14     1 */
                          __u8       cmp_mode;             /*    15     1 */
                  } __attribute__((__packed__)) xattr;     /*     6    10 */
                  struct {
                          __u8       class_len;            /*     6     1 */
                          __u8       method_len;           /*     7     1 */
                          __u8       argc;                 /*     8     1 */
                          __le32     indata_len;           /*     9     4 */
                  } __attribute__((__packed__)) cls;       /*     6     7 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     count;                /*    14     8 */
                  } pgls;                                  /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                  } snap;                                  /*     6     8 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     ver;                  /*    14     8 */
                          __u8       op;                   /*    22     1 */
                          __le32     gen;                  /*    23     4 */
                  } __attribute__((__packed__)) watch;     /*     6    21 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                  } notify;                                /*     6     8 */
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     src_offset;           /*    22     8 */
                  } clonerange;                            /*     6    24 */
                  struct {
                          __le64     expected_object_size; /*     6     8 */
                          __le64     expected_write_size;  /*    14     8 */
                  } alloc_hint;                            /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                          __le64     src_version;          /*    14     8 */
                          __u8       flags;                /*    22     1 */
                          __le32     src_fadvise_flags;    /*    23     4 */
                  } __attribute__((__packed__)) copy_from; /*     6    21 */
          };                                               /*     6    28 */
          __le32                     payload_len;          /*    34     4 */

          /* size: 38, cachelines: 1, members: 4 */
          /* last cacheline: 38 bytes */
  } __attribute__((__packed__));

After:

  $ pahole -C ceph_osd_op /home/acme/git/build/v5.1-rc4+/net/ceph/osd_client.o
  struct ceph_osd_op {
          __le16                     op;                   /*     0     2 */
          __le32                     flags;                /*     2     4 */
          union {
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     truncate_size;        /*    22     8 */
                          __le32     truncate_seq;         /*    30     4 */
                  } __attribute__((__packed__)) extent;    /*     6    28 */
                  struct {
                          __le32     name_len;             /*     6     4 */
                          __le32     value_len;            /*    10     4 */
                          __u8       cmp_op;               /*    14     1 */
                          __u8       cmp_mode;             /*    15     1 */
                  } __attribute__((__packed__)) xattr;     /*     6    10 */
                  struct {
                          __u8       class_len;            /*     6     1 */
                          __u8       method_len;           /*     7     1 */
                          __u8       argc;                 /*     8     1 */
                          __le32     indata_len;           /*     9     4 */
                  } __attribute__((__packed__)) cls;       /*     6     7 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     count;                /*    14     8 */
                  } pgls;                                  /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                  } snap;                                  /*     6     8 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     ver;                  /*    14     8 */
                          __u8       op;                   /*    22     1 */
                          __le32     gen;                  /*    23     4 */
                  } __attribute__((__packed__)) watch;     /*     6    21 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                  } notify;                                /*     6     8 */
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     src_offset;           /*    22     8 */
                  } clonerange;                            /*     6    24 */
                  struct {
                          __le64     expected_object_size; /*     6     8 */
                          __le64     expected_write_size;  /*    14     8 */
                  } alloc_hint;                            /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                          __le64     src_version;          /*    14     8 */
                          __u8       flags;                /*    22     1 */
                          __le32     src_fadvise_flags;    /*    23     4 */
                  } __attribute__((__packed__)) copy_from; /*     6    21 */
          };                                               /*     6    28 */
          __le32                     payload_len;          /*    34     4 */

          /* size: 38, cachelines: 1, members: 4 */
          /* last cacheline: 38 bytes */
  } __attribute__((__packed__));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-12 17:08:41 -03:00
Arnaldo Carvalho de Melo 75c52de9c6 core: Move packed_attribute_inferred from 'class' to 'type' class
Since we need to infer the attributes of union members too, so move
there.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-12 12:39:50 -03:00
Arnaldo Carvalho de Melo dc3d441961 core: Improve the natural alignment calculation
We need to take more than just arrays into account when figuring out the
natural alignment of struct members, looking recursively at types till
we get to basic types and pointers.

Before this patch the 'new' struct field in the 'v' union was considered
__packed__, when in fact it is not, as the natural alignment for the
'state_id' typedef is 4, so it can start at offset 36 (or 4 considering
just its container struct), see below:

  $ pahole -IC nfsd4_lock /home/acme/git/build/v5.1-rc4+/fs/nfsd/nfs4xdr.o
  /* Used at: /home/acme/git/linux/fs/nfsd/nfs4xdr.c */
  /* <1717a> /home/acme/git/linux/fs/nfsd/xdr4.h:156 */
  struct nfsd4_lock {
          u32                        lk_type;              /*     0     4 */
          u32                        lk_reclaim;           /*     4     4 */
          u64                        lk_offset;            /*     8     8 */
          u64                        lk_length;            /*    16     8 */
          u32                        lk_is_new;            /*    24     4 */

          /* XXX 4 bytes hole, try to pack */

          union {
                  struct {
                          u32        open_seqid;           /*    32     4 */
                          stateid_t  open_stateid;         /*    36    16 */
                          u32        lock_seqid;           /*    52     4 */
                          clientid_t clientid;             /*    56     8 */
                          /* --- cacheline 1 boundary (64 bytes) --- */
                          struct xdr_netobj owner;         /*    64    16 */
                  } __attribute__((__packed__)) new;       /*    32    48 */
                  struct {
                          stateid_t  lock_stateid;         /*    32    16 */
                          u32        lock_seqid;           /*    48     4 */
                  } __attribute__((__packed__)) old;       /*    32    20 */
          } v;                                             /*    32    48 */
          /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */
          union {
                  struct {
                          stateid_t  stateid;              /*    80    16 */
                  } ok;                                    /*    80    16 */
                  struct nfsd4_lock_denied denied;         /*    80    48 */
          } u;                                             /*    80    48 */

          /* size: 128, cachelines: 2, members: 7 */
          /* sum members: 124, holes: 1, sum holes: 4 */
  };
  $

Asking for -rEIC, i.e. relative offsets, expand types we can see that
stateid_t opaque type:

                struct {
                        /* typedef u32 -> __u32 */ unsigned int open_seqid;              /*     0     4 */
                        /* typedef stateid_t */ struct {
                                /* typedef u32 -> __u32 */ unsigned int si_generation;   /*     0     4 */
                                /* typedef stateid_opaque_t */ struct {
                                        /* typedef clientid_t */ struct {
                                                /* typedef u32 -> __u32 */ unsigned int   cl_boot; /*     0     4 */
                                                /* typedef u32 -> __u32 */ unsigned int   cl_id; /*     4     4 */
                                        } so_clid; /*     0     8 */
                                        /* typedef u32 -> __u32 */ unsigned int so_id;   /*     8     4 */
                                } si_opaque; /*     4    12 */
                        } open_stateid; /*     4    16 */

With the algorithm implemented in this patch we get it correctly as not
packed:

  $ pahole -IC nfsd4_lock /home/acme/git/build/v5.1-rc4+/fs/nfsd/nfs4xdr.o
  /* Used at: /home/acme/git/linux/fs/nfsd/nfs4xdr.c */
  /* <1717a> /home/acme/git/linux/fs/nfsd/xdr4.h:156 */
  struct nfsd4_lock {
          u32                        lk_type;              /*     0     4 */
          u32                        lk_reclaim;           /*     4     4 */
          u64                        lk_offset;            /*     8     8 */
          u64                        lk_length;            /*    16     8 */
          u32                        lk_is_new;            /*    24     4 */

          /* XXX 4 bytes hole, try to pack */

          union {
                  struct {
                          u32        open_seqid;           /*    32     4 */
                          stateid_t  open_stateid;         /*    36    16 */
                          u32        lock_seqid;           /*    52     4 */
                          clientid_t clientid;             /*    56     8 */
                          /* --- cacheline 1 boundary (64 bytes) --- */
                          struct xdr_netobj owner;         /*    64    16 */
                  } new;                                   /*    32    48 */
                  struct {
                          stateid_t  lock_stateid;         /*    32    16 */
                          u32        lock_seqid;           /*    48     4 */
                  } old;                                   /*    32    20 */
          } v;                                             /*    32    48 */
          /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */
          union {
                  struct {
                          stateid_t  stateid;              /*    80    16 */
                  } ok;                                    /*    80    16 */
                  struct nfsd4_lock_denied denied;         /*    80    48 */
          } u;                                             /*    80    48 */

          /* size: 128, cachelines: 2, members: 7 */
          /* sum members: 124, holes: 1, sum holes: 4 */
  };

Fixes: f2641ce169 ("core: Take arrays into account when inferring if a struct is packed")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-11 16:43:59 -03:00
Arnaldo Carvalho de Melo 3247a777dc core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.

This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.

  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	int                        refcnt;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	int                        refcnt;               /*    16     4 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* padding: 4 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	int                        refcnt;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	int                        refcnt;               /*    16     4 */

  	/* size: 20, cachelines: 1, members: 3 */
  	/* last cacheline: 20 bytes */
  } __attribute__((__packed__));
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	int                        refcnt;
  	const char  *              uptr;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	int                        refcnt;               /*     8     4 */

  	/* XXX 4 bytes hole, try to pack */

  	const char  *              uptr;                 /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* sum members: 20, holes: 1, sum holes: 4 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	int                        refcnt;
  	const char  *              uptr;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	int                        refcnt;               /*     8     4 */
  	const char  *              uptr;                 /*    12     8 */

  	/* size: 20, cachelines: 1, members: 3 */
  	/* last cacheline: 20 bytes */
  } __attribute__((__packed__));
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	unsigned char              refcnt;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	unsigned char              refcnt;               /*    16     1 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* padding: 7 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	unsigned char              refcnt;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	unsigned char              refcnt;               /*    16     1 */

  	/* size: 17, cachelines: 1, members: 3 */
  	/* last cacheline: 17 bytes */
  } __attribute__((__packed__));
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	unsigned char              refcnt;
  	const char  *              uptr;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	unsigned char              refcnt;               /*     8     1 */

  	/* XXX 7 bytes hole, try to pack */

  	const char  *              uptr;                 /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* sum members: 17, holes: 1, sum holes: 7 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	unsigned char              refcnt;
  	const char  *              uptr;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	unsigned char              refcnt;               /*     8     1 */
  	const char  *              uptr;                 /*     9     8 */

  	/* size: 17, cachelines: 1, members: 3 */
  	/* last cacheline: 17 bytes */
  } __attribute__((__packed__));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 17:41:59 -03:00
Arnaldo Carvalho de Melo 1c9c1d6bbd dwarf_loader: Store DW_AT_alignment if available in DW_TAG_{structure,union,class}_type
That is not just for DW_TAG_class_member :-)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 16:57:52 -03:00
Arnaldo Carvalho de Melo 15a754f224 core: Add nr_entries member to 'struct cus'
Will be used when considering comparing multiple CU entries in a struct
cus to the sole compile unit in a second file, like when comparing the
types in a multi-CU DWARF file like vmlinux against the combined,
deduplicated entries in that vmlinux .BTF section.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-08 11:54:06 -03:00
Arnaldo Carvalho de Melo 881aabd6fc reorganize: Introduce class__for_each_member_from_safe()
Reducing boilerplate, keeping consistent with the other member traversal
helpers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:16:16 -03:00
Arnaldo Carvalho de Melo 1b2e3389f3 reorganize: Introduce class__for_each_member_reverse()
Reducing boilerplate, keeping consistent with the other member traversal
helpers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:11:42 -03:00
Arnaldo Carvalho de Melo 10fef2916d reorganize: Introduce class__for_each_member_continue()
To get a lot of boilerplate behind a nice helper.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:07:30 -03:00
Arnaldo Carvalho de Melo e7a56ee8cc reorganize: Introduce class__for_each_member_from()
To get a lot of boilerplate behind a nice helper.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:05:28 -03:00
Arnaldo Carvalho de Melo 9a79bb6ced tag: Introduce tag__is_pointer_to()
To shorten the check if a tag is a pointer to a particular type.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 15:28:55 -03:00
Arnaldo Carvalho de Melo 45ad545944 tag: Introduce tag__is_pointer()
For the usual idiom to ask if a tag is a pointer, removing a bit of
DWARFism and shortening the operation.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 15:21:55 -03:00
Arnaldo Carvalho de Melo 6cd6a6bd87 dwarves_fprintf: Allow suppressing the __attribute__((__aligned__(N))
So that we can use it in things like btfdiff.

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Arnaldo Carvalho de Melo c002873c44 dwarves_fprintf: Move invariant printing of ; to outside if block
Will facilitate printing something just before the ;

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Andrii Nakryiko 78c110a7ea dwarves: Revert semantics of member bit/byte hole
pahole --reorganize heavily depends on member's bit_hole and hole fields
to denote bit/byte holes *after* member. Previous commit "dwarves: use
bit sizes and bit/byte hole info in __class__fprintf" changed its
meaning to bit/byte hole *before* member to accomodate possible bit/byte
holes at the beginning of a struct. This change broke reorganization
algorithm, though, which is quite involved and isn't trivially
modifiable to accomodate new semantics.

This patch reverts the meaning of bit_hole and hole, but also introduces
per class pre_bit_hole/pre_hole to record initial bit/byte hole of a
struct. This allows to fix reorg code more easily and still handle
initial holes cases, if at the expense of being not as elegant.

Committer testing:

  $ time pahole -F btf --packable vmlinux | sort -nr -k4 | head
  bts_ctx     12288 8192 4096
  swsusp_info  4096  432 3664
  vc_data       792  496  296
  pci_dev      2488 2320  168
  rcu_state    3392 3240  152
  ptr_ring      192   40  152
  xdp_sock      960  840  120
  zone         1664 1552  112
  rcu_data      576  472  104
  rcu_node      576  480   96

  real	0m0.038s
  user	0m0.029s
  sys	0m0.017s
  $

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 10:09:33 -03:00
Andrii Nakryiko 5104d1bef3 loaders: Record CU's endianness in dwarf/btf/ctf loaders
This patch records for each CU whether it's in little-endian or
big-endian data format. This flag will be used in subsequent commits to
adjust bit offsets where necessary, to make them uniform across
endianness. This patch doesn't have any effect on pahole's output.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Andrii Nakryiko 55c96aaed8 loaders: Strip away volatile/const/restrict when fixing bitfields
btf_loader and ctf_loader didn't remove const/volatile/restrict, so
bitfields using modifiers were not adjusted properly.

This patch abstracts logic of stripping aways typedefs and access
modifiers into tag__strip_typedefs_and_modifiers, which handles any
interleaving of typedefs and modifiers. dwarf_loader was adapter to
reuse this function as well, instead of custom goto loop.

REPRO:

  $ cat vc_map.c
  typedef unsigned int u32;
  typedef volatile u32 vu32;
  typedef vu32 vu32_t;

  typedef struct vc_map {
          volatile unsigned int tx: 1;
          vu32_t rx: 1;
          void *x1, *x2;
  } vc_map;

  int main() {
          struct vc_map s;
          return 0;
  }

BEFORE:

  $ ~/pahole/build/pahole -F btf vc_map
  struct vc_map {
          volatile unsigned int      tx:1;                 /*     0: 0  4 */
          vu32_t                     rx:1;                 /*     0: 0  4 */

          /* XXX 30 bits hole, try to pack */
          /* XXX 4 bytes hole, try to pack */

          void *                     x1;                   /*     8     8 */
          void *                     x2;                   /*    16     8 */

          /* size: 24, cachelines: 1, members: 4 */
          /* sum members: 20, holes: 1, sum holes: 4 */
          /* bit holes: 1, sum bit holes: 30 bits */
          /* last cacheline: 24 bytes */
  };

AFTER:

  $ ~/pahole/build/pahole -F btf vc_map
  struct vc_map {
          volatile unsigned int      tx:1;                 /*     0:31  4 */
          vu32_t                     rx:1;                 /*     0:30  4 */

          /* XXX 30 bits hole, try to pack */
          /* XXX 4 bytes hole, try to pack */

          void *                     x1;                   /*     8     8 */
          void *                     x2;                   /*    16     8 */

          /* size: 24, cachelines: 1, members: 4 */
          /* sum members: 20, holes: 1, sum holes: 4 */
          /* bit holes: 1, sum bit holes: 30 bits */
          /* last cacheline: 24 bytes */
  };

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Arnaldo Carvalho de Melo bb8350acf5 dwarves: Switch type_id_t from uint16_t to uint32_t
With BTF dedup we end up with a CU with all the types, and with
something like an allmodconfig vmlinux image it ends up with more than
65535 types, so use uint32_t for the type IDs to accomodate that.

Reported-by: Andrii Nakryiko <andriin@fb.com>
Tested-by:Andrii Nakryiko <andrii.nakryiko@gmail.com>
Link: https://lore.kernel.org/bpf/CAEf4Bzb0SpvXdDKMMnUof==kp4Y0AP54bKFjeCzX_AsmDm7k7g@mail.gmail.com/
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-11 11:44:57 -03:00
Arnaldo Carvalho de Melo 5375d06faf dwarves: Introduce type_id_t for use with the type IDs
This is just a prep patch, marking uint16_t IDs as type_id_t, that
points to uint16_t, so no change in the resulting code.

Cc: Andrii Nakryiko <andriin@fb.com>
Tested-by:Andrii Nakryiko <andrii.nakryiko@gmail.com>
Link: https://lore.kernel.org/bpf/CAEf4Bzb0SpvXdDKMMnUof==kp4Y0AP54bKFjeCzX_AsmDm7k7g@mail.gmail.com/
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-11 11:44:53 -03:00
Arnaldo Carvalho de Melo c9b2ef034f dwarf: Add cu__add_tag_with_id() to stop using id == -1 to allocate id
the CTF and BTF loaders come already with the id to use, while the DWARF
loader comes with a Dwarf_Off that needs to be converted into the
ptr_table index.

So keep the cu__add_tag(cu, tag, &id) method to ask ask for the index to
be allocated in the ptr_table and the result to come back via the 'id'
parameter, now a uint32_t and introduce a cu__add_tag_with_id(cu, tag, id)
method to indicate that the 'uint32_t id' is the one to use.

With this we can use a uint32_t for the id both on 32-bit and 64-bit
arches.

Reported-by: Andrii Nakryiko <andriin@fb.com>
Tested-by:Andrii Nakryiko <andrii.nakryiko@gmail.com>
Link: https://lore.kernel.org/bpf/CAEf4Bzb0SpvXdDKMMnUof==kp4Y0AP54bKFjeCzX_AsmDm7k7g@mail.gmail.com/
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-11 11:44:45 -03:00
Domenico Andreoli e714d2eaa1 Adopt SPDX-License-Identifier
Signed-off-by: Domenico Andreoli <domenico.andreoli@linux.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-18 15:41:48 -03:00
Yonghong Song b0cf845e02 dwarves: Change type of bitfield_offset from uint8_t to int8_t
The dwarves class_member field bitfield_offset represents the dwarf tag
DW_AT_bit_offset. For dwarf2, this field can be negative for little
endian for bitfields in packed data structures which cross type
boundary.

  -bash-4.4$ cat bitfield.c
  struct packed {
    char x1: 1;
    char x2: 3;
    char x3: 3;
    int y1: 7;
    int y2: 20;
  } __attribute__((packed));
  struct packed g;
  -bash-4.4$ gcc -O2 -c -g bitfield.c
  -bash-4.4$ pahole -JV bitfield.o
  File bitfield.o:
  [1] STRUCT packed kind_flag=1 size=5 vlen=5
        x1 type_id=2 bitfield_size=1 bits_offset=0
        x2 type_id=2 bitfield_size=3 bits_offset=1
        x3 type_id=2 bitfield_size=3 bits_offset=4
        y1 type_id=3 bitfield_size=7 bits_offset=7
        y2 type_id=3 bitfield_size=255 bits_offset=16776974
  [2] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [3] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  -bash-4.4$

The above large negative bits_offset and bitfield_size=255 results from
negative bitfield_offset which is interpreted as positive value in btf
encoding.

With this fix, the pahole works properly for BTF:
  -bash-4.4$ pahole -JV bitfield.o
  File bitfield.o:
  [1] STRUCT packed kind_flag=1 size=5 vlen=5
        x1 type_id=2 bitfield_size=1 bits_offset=0
        x2 type_id=2 bitfield_size=3 bits_offset=1
        x3 type_id=2 bitfield_size=3 bits_offset=4
        y1 type_id=3 bitfield_size=7 bits_offset=7
        y2 type_id=3 bitfield_size=20 bits_offset=14
  [2] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [3] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  -bash-4.4$

Note that change bitfield_offset from uint8_t to int8_t is safe as the
maximum int type we support is __int128 and maximum bitfield_offset is
127.

Signed-off-by: Yonghong Song <yhs@fb.com>
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: dwarves@vger.kernel.org
Link: https://www.spinics.net/lists/dwarves/msg00199.html
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-14 10:35:04 -03:00
Arnaldo Carvalho de Melo 18f5910f96 dwarves: Add type to tag helper
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Yonghong Song 1176661409 btf: Fix kind_flag usage in btf_loader
Commit 2a82d593be ("btf: Add kind_flag support for btf_loader") added
kind_flag supported in btf_loader to get correct bitfield_size and
bit_offset for struct/union members.  The commit unnecessarily stored
the bit in the structure type which is not used later.

So let us remove the kind_flag from the struct type and any other
changes whose purpose is to set this bit.

Signed-off-by: Yonghong Song <yhs@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Fixes: 2a82d593be ("btf: Add kind_flag support for btf_loader")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Arnaldo Carvalho de Melo b24718fe27 dwarves: Fix documentation for class_memer->bitfield_size
Just a cut'n'past thinko.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Yonghong Song 2a82d593be btf: Add kind_flag support for btf_loader
For struct/union members, the struct/union type info kind_flag is needed
to calculate correct bitfield_size and bit_offset.

  if (kind_flag) {
    bitfield_size = BTF_MEMBER_BITFIELD_SIZE(member->offset);
    bit_offset = BTF_MEMBER_BIT_OFFSET(member->offset);
  } else {
    bitfield_size = 0;
    bit_offset = member->offset;
  }

Note that bitfield_size and bit_offset will not depend on the member
type. The member type will help calculate correct bitfield_offset,
byte_size, byte_offset, bit_size.

For example, with the fix, we will be able to display
bit offset and bitfield size properly.

  -bash-4.4$ cat t.c
  struct t {
    int a:2;
    int b:3;
    int c:2;
  } g;
  -bash-4.4$ gcc -c -O2 -g t.c
  -bash-4.4$ pahole -JV t.o
  File t.o:
  [1] STRUCT t kind_flag=1 size=4 vlen=3
          a type_id=2 bitfield_size=2 bits_offset=0
          b type_id=2 bitfield_size=3 bits_offset=2
          c type_id=2 bitfield_size=2 bits_offset=5
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  -bash-4.4$ pahole -F btf t.o
  struct t {
          int                        a:2;                  /*     0: 0  4 */
          int                        b:3;                  /*     0: 2  4 */
          int                        c:2;                  /*     0: 5  4 */

          /* size: 4, cachelines: 1, members: 3 */
          /* bit_padding: 25 bits */
          /* last cacheline: 4 bytes */
  };

Note that the above offset showing is different from the below dwarf as
BTF bitfield_offset is always the offset from the start of structure,
kindly like big endian encoding. This may need adjustment to be
conforming to the dwarf dump format.

  -bash-4.4$ pahole -F dwarf t.o
  struct t {
          int                        a:2;                  /*     0:30  4 */
          int                        b:3;                  /*     0:27  4 */
          int                        c:2;                  /*     0:25  4 */

          /* size: 4, cachelines: 1, members: 3 */
          /* bit_padding: 25 bits */
          /* last cacheline: 4 bytes */
  };

Signed-off-by: Yonghong Song <yhs@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-21 09:51:12 -03:00
Yonghong Song 8630ce4042 btf: fix struct/union/fwd types with kind_flag
This patch fixed two issues with BTF. One is related to struct/union
bitfield encoding and the other is related to forward type.

Issue #1 and solution:
======================

Current btf encoding of bitfield follows what pahole generates.
For each bitfield, pahole will duplicate the type chain and
put the bitfield size at the final int or enum type.
Since the BTF enum type cannot encode bit size,
commit b18354f64c ("btf: Generate correct struct bitfield
member types") workarounds the issue by generating
an int type whenever the enum bit size is not 32.

The above workaround is not ideal as we lost original type
in BTF. Another undesiable fact is the type duplication
as the pahole duplicates the type chain.

To fix this issue, this patch implemented a compatible
change for BTF struct type encoding:
  . the bit 31 of type->info, previously reserved,
    now is used to indicate whether bitfield_size is
    encoded in btf_member or not.
  . if bit 31 of struct_type->info is set,
    btf_member->offset will encode like:
      bit 0 - 23: bit offset
      bit 24 - 31: bitfield size
    if bit 31 is not set, the old behavior is preserved:
      bit 0 - 31: bit offset

So if the struct contains a bit field, the maximum bit offset
will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
bitfield size will be 255 which is enough for today as maximum
bitfield in compiler can be 128 where int128 type is supported.

A new global, no_bitfield_type_recode, is introduced and which
will be set to true if BTF encoding is enabled. This global
will prevent pahole duplicating the bitfield types to avoid
type duplication in BTF.

Issue #2 and solution:
======================

Current forward type in BTF does not specify whether the original
type is struct or union. This will not work for type pretty print
and BTF-to-header-file conversion as struct/union must be specified.

To fix this issue, similar to issue #1, type->info bit 31
is used. If the bit is set, it is union type. Otherwise, it is
a struct type.

Examples:
=========

  -bash-4.4$ cat t.c
  struct s;
  union u;
  typedef int ___int;
  enum A { A1, A2, A3 };
  struct t {
	  int a[5];
	  ___int b:4;
	  volatile enum A c:4;
	  struct s *p1;
	  union u *p2;
  } g;
  -bash-4.4$ gcc -c -O2 -g t.c

Without this patch:

  $ pahole -JV t.o
  [1] TYPEDEF ___int type_id=2
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [3] ENUM A size=4 vlen=3
        A1 val=0
        A2 val=1
        A3 val=2
  [4] STRUCT t size=40 vlen=5
        a type_id=5 bits_offset=0
        b type_id=13 bits_offset=160
        c type_id=15 bits_offset=164
        p1 type_id=9 bits_offset=192
        p2 type_id=11 bits_offset=256
  [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
  [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
  [7] VOLATILE (anon) type_id=3
  [8] FWD s type_id=0
  [9] PTR (anon) type_id=8
  [10] FWD u type_id=0
  [11] PTR (anon) type_id=10
  [12] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
  [13] TYPEDEF ___int type_id=12
  [14] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
  [15] VOLATILE (anon) type_id=14

With this patch:

  $ pahole -JV t.o
  File t.o:
  [1] TYPEDEF ___int type_id=2
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  [3] ENUM A size=4 vlen=3
        A1 val=0
        A2 val=1
        A3 val=2
  [4] STRUCT t kind_flag=1 size=40 vlen=5
        a type_id=5 bitfield_size=0 bits_offset=0
        b type_id=1 bitfield_size=4 bits_offset=160
        c type_id=7 bitfield_size=4 bits_offset=164
        p1 type_id=9 bitfield_size=0 bits_offset=192
        p2 type_id=11 bitfield_size=0 bits_offset=256
  [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
  [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
  [7] VOLATILE (anon) type_id=3
  [8] FWD s struct
  [9] PTR (anon) type_id=8
  [10] FWD u union
  [11] PTR (anon) type_id=10

The fix removed the type duplication, preserved the enum type for the
bitfield, and have correct struct/union information for the forward
type.

Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-20 11:27:20 -03:00
Arnaldo Carvalho de Melo da632a3686 dwarves: Introduce {cu,cus}__find_struct_or_union_by_name() methods
Since tools like 'pahole' now shows unions in addition to structs.

Cc: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-03 13:13:51 -03:00
Arnaldo Carvalho de Melo 31664d60ad pahole: Show tagged enums as well when no class is specified
The pahole tool initial goal was to show struct holes, which can't
happen with a first level of a tagged union (a union with a name),
so those only were displayed when part of a higher level struct.

Show the first level tagged enums as well, as this is useful when just
wanting to see its members.

E.g.:

  $ pahole ../build/v4.20-rc5/net/core/sock.o | grep ^union
  union fpregs_state {
  union irq_stack_union {
  union sigval {
  union __sifields {
  union thread_union {
  union kernfs_node_id {
  union flowi_uli {
  union ethtool_flow_union {
  union key_payload {
  union bpf_attr {
  union tcp_md5_addr {
  $

Suggested-by: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-03 12:51:30 -03:00
Arnaldo Carvalho de Melo bfdea37668 dwarves_fprintf: Print the scope of variables
E.g.:

$ pfunct -iVT ../build/v4.18.0+/kernel/sched/core.o  -f tg_cfs_schedulable_down
int tg_cfs_schedulable_down(struct task_group * tg, void * data);
{ /* low_pc=0x10340 */
	struct cfs_schedulable_data * d; /* scope: optimized */       //  6681
	struct cfs_bandwidth * cfs_b; /* scope: optimized */          //  6682
	s64 quota; /* scope: optimized */                             //  6683
	s64 parent_quota; /* scope: register */                       //  6683
	{
		struct cfs_bandwidth * parent_b; /* scope: optimized */ //  6688
		{
			bool branch; /* scope: optimized */           //  6698
			arch_static_branch(struct static_key * key,
						bool branch); /* size=30, low_pc=0x103a3 */ //  6698
		} /* lexblock size=0 */
		{
			s64 __UNIQUE_ID___x272; /* scope: optimized *///  6699
			s64 __UNIQUE_ID___y273; /* scope: optimized *///  6699
		} /* lexblock size=0 */
		normalize_cfs_quota(struct task_group * tg,
					struct cfs_schedulable_data * d); /* size=94, low_pc=0x10355 */ //  6690
	} /* lexblock size=0 */
}/* size: 240, variables: 4 */

This is part of an investigation on how to lookup the struct types associated
to registers in instructions with offsets from registers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-09-26 16:45:25 -03:00
Arnaldo Carvalho de Melo 465110ec99 dwarves: Add the DWARF location to struct variable
This is DWARF specific, we don't have in CTF, AFAIK, info about where a
variable is put, i.e. in a register? in the stack? etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-09-26 16:45:25 -03:00
Arnaldo Carvalho de Melo c65f2cf436 dwarves: Rename variable->location to ->scope
We'll use location in the DWARF sense, i.e. location lists, etc, i.e.
where is this variable? In a register? The stack? etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-09-26 16:45:25 -03:00
Arnaldo Carvalho de Melo 103e89bb25 dwarves_fprintf: Find holes on structs embedded in other structs
Take 'struct task_struct' in the Linux kernel, these fields:

        /* --- cacheline 2 boundary (128 bytes) --- */
        struct sched_entity        se;                   /*   128   448 */

        /* XXX last struct has 24 bytes of padding */

        /* --- cacheline 9 boundary (576 bytes) --- */
        struct sched_rt_entity     rt;                   /*   576    48 */

The sched_entity struct has 24 bytes of padding, and that info would
only appear when printing 'struct task_struct' if class__find_holes()
had previously been run on 'struct sched_entity' which wasn't always the
case, make sure that happens.

This results in this extra stat being printed for 'struct task_struct':

	/* paddings: 4, sum paddings: 38 */

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-30 16:18:11 -03:00
Arnaldo Carvalho de Melo ab97c07a7e dwarves_fprintf: Fixup cacheline boundary printing on expanded structs
A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes:

  [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30
  --- /tmp/before.c	2016-06-29 17:00:38.082647281 -0300
  +++ /tmp/a.c	2016-06-29 17:03:36.913124779 -0300
  @@ -43,8 +43,8 @@
 			struct list_head * prev;                                         /*   176     8 */
 		} group_node; /*   168    16 */
 		unsigned int       on_rq;                                                /*   184     4 */
  +		/* --- cacheline 3 boundary (192 bytes) --- */
 		/* typedef u64 */ long long unsigned int exec_start;                     /*   192     8 */
  -		/* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */
 		/* typedef u64 */ long long unsigned int sum_exec_runtime;               /*   200     8 */
 		/* typedef u64 */ long long unsigned int vruntime;                       /*   208     8 */
 		/* typedef u64 */ long long unsigned int prev_sum_exec_runtime;          /*   216     8 */
  @@ -53,40 +53,40 @@
 			/* typedef u64 */ long long unsigned int wait_start;             /*   232     8 */
 			/* typedef u64 */ long long unsigned int wait_max;               /*   240     8 */
 			/* typedef u64 */ long long unsigned int wait_count;             /*   248     8 */
  +			/* --- cacheline 4 boundary (256 bytes) --- */
 			/* typedef u64 */ long long unsigned int wait_sum;               /*   256     8 */
 			/* typedef u64 */ long long unsigned int iowait_count;           /*   264     8 */
 			/* typedef u64 */ long long unsigned int iowait_sum;             /*   272     8 */
 			/* typedef u64 */ long long unsigned int sleep_start;            /*   280     8 */
 			/* typedef u64 */ long long unsigned int sleep_max;              /*   288     8 */
  -			/* --- cacheline 1 boundary (64 bytes) --- */
 			/* typedef s64 */ long long int sum_sleep_runtime;               /*   296     8 */
 			/* typedef u64 */ long long unsigned int block_start;            /*   304     8 */
 			/* typedef u64 */ long long unsigned int block_max;              /*   312     8 */
  +			/* --- cacheline 5 boundary (320 bytes) --- */
 			/* typedef u64 */ long long unsigned int exec_max;               /*   320     8 */
 			/* typedef u64 */ long long unsigned int slice_max;              /*   328     8 */
 			/* typedef u64 */ long long unsigned int nr_migrations_cold;     /*   336     8 */
  [acme@jouet linux]$

I.e. the boundary detection was being reset at each expanded struct, do the math globally,
using the member offset, that was already done globally and correctly.

Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 17:27:51 -03:00
Arnaldo Carvalho de Melo 046ad67af3 dwarves_fprintf: Shorten class__fprintf() sig
That conf_fprintf can be elided as it is always NULL for the root call,
i.e. only when expanding types is that it will be called recursively.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 16:19:20 -03:00
Arnaldo Carvalho de Melo 45618c7ec1 dwarf_loader: Initial support for DW_TAG_unspecified_type
Still need to check what to fprintf for this, but at least have it in
the type lists so that we can find it.

Reported-by: Christophe Fergeau <cfergeau@redhat.com>
Cc: Dodji Seketeli <dodji@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-17 10:05:34 -03:00
Arnaldo Carvalho de Melo 0fbb39291d dwarf_loader: Add support for DW_TAG_restrict_type
I.e. supporting the 'restrict' keyword, emitted by recent compilers:

  [acme@jouet pahole]$ pfunct -P ~/bin/perf |& grep -w restrict
  inline int vprintf(const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline size_t fread(void * restrict  __ptr, size_t __size, size_t __n, FILE * restrict  __stream);
  inline int vfprintf(FILE * restrict  __stream, const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline int vasprintf(char * * restrict  __ptr, const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline char * realpath(const char  * restrict  __name, char * restrict  __resolved);
  inline ssize_t readlink(const char  * restrict  __path, char * restrict  __buf, size_t __len);
  inline char * strcat(char * restrict  __dest, const char  * restrict  __src);
  inline char * fgets(char * restrict  __s, int __n, FILE * restrict  __stream);
  inline int snprintf(char * restrict  __s, size_t __n, const char  * restrict  __fmt, ...);
  inline int sprintf(char * restrict  __s, const char  * restrict  __fmt, ...);
  inline char * strcpy(char * restrict  __dest, const char  * restrict  __src);
  inline int asprintf(char * * restrict  __ptr, const char  * restrict  __fmt, ...);
  inline char * strncpy(char * restrict  __dest, const char  * restrict  __src, size_t __len);
  inline int fprintf(FILE * restrict  __stream, const char  * restrict  __fmt, ...);
  inline int vsnprintf(char * restrict  __s, size_t __n, const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline int printf(const char  * restrict  __fmt, ...);
  [acme@jouet pahole]$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-06 15:02:17 -03:00
Arnaldo Carvalho de Melo 9df42c6826 dwarves: Initial support for rvalue_reference_type
Need to read more on http://www.artima.com/cppsource/rvalue.html, but
handling it mostly like DW_TAG_typedef so that at least references to it
are resolved, we can get its byte size, etc.

FIXME: look at the vtable parameters, some are resolving to "(null)".

Reported-by: Benjamin Kosnik <bkoz@redhat.com>
Reported-by: Mark Wieelard <mjw@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=962571
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-03-15 16:37:48 -03:00
Arnaldo Carvalho de Melo 10515a7c4d dwarves: Introduce cus__fprintf_load_files_err()
Out of code in pdwtags and pahole, will be used in the other tools.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-03-15 14:25:58 -03:00
Arnaldo Carvalho de Melo fd3838ae9a dwarves: Stop using 'self'
As Thomas Gleixner wisely pointed out, using 'self' is stoopid, it
doesn't convey useful information, so use sensible names

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2014-01-06 16:46:50 -03:00
Arnaldo Carvalho de Melo 8c6378fd88 dwarves: Support static class data members
Fixes the following BFA:

[acme@sandy pahole]$ pahole brainfart.o
class ios_base {
	enum _Ios_Openmodeconst    in;                   /*     0     4 */
	typedef enum _Ios_Fmtflags fmtflags;

	/* size: 1, cachelines: 1, members: 1 */
	/* padding: 65533 */
	/* last cacheline: 1 bytes */

	/* BRAIN FART ALERT! 1 != 4 + 0(holes), diff = -3 */

};

That now produces:

[acme@sandy pahole]$ build/pahole brainfart.o
class ios_base {
	static enum _Ios_Openmodeconst    in = 8;        /*     0     0 */
	typedef enum _Ios_Fmtflags fmtflags;

	/* size: 1, cachelines: 0, members: 0, static members: 1 */
	/* last cacheline: 1 bytes */
};
[acme@sandy pahole]$

Reported-by: Nicolas <nikos42@gmail.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-08-20 14:42:17 -03:00
Tom Tromey 2938a70e1e Add support for .debug_types sections.
.debug_types is a new DWARF 4 feature that adds some simple
compression to DWARF.  The basic idea is that identical type
definitions are merged by the linker and put into a new .debug_types
section.

This introduces 'dwarf_off_ref', which is like Dwarf_Off, but carries
a flag indicating if the DIE came from .debug_types.  This allows
future uses to find the DIE properly.

Type units are read a little differently from ordinary CUs.  All type
units are read at once, then types are recoded for them.  (I think
something like this is needed more generally, to support inter-CU
references, but I have not tried that.)

The type unit is also kept around longer, because any other CU may
refer to it.  This necessitated a change to load_steal_kind to replace
the notion of a "stolen" CU with a request by the "stealer" for the
caller to delete the CU when appropriate.

I need elfutils patch 581c3f60e2b1fc7ddaf4260bb5a9cb59f8e3f0d0
to make this work properly; without it I see crashes.

You can make test cases by compiling with '-gdwarf-4 -fdebug-types-section'.
2012-03-22 12:51:20 -06:00
Tom Tromey 5ef26a053d Remove unused field from debug_fmt_ops
Nothing seemed to use the 'tag__orig_type' method in debug_fmt_ops.
It was simpler to remove it than to try to fix it for the next patch.
2012-03-22 12:43:59 -06:00
Arnaldo Carvalho de Melo 25cd635806 dwarves: base_type->float_type holds only 12 possible values
So make it a :4 u8 to combine with the previous bitfield. This field is
also seldom used so no expected perf hit.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-12-05 19:07:51 -02:00
Arnaldo Carvalho de Melo c6d27e325c dwarves: Enlarge vtable_entry
Since we have two bytes after it in a hole, use them for vtable_entry,
hell knows if there isn't any crazy code with that many vtable
entries...

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-12-05 18:35:10 -02:00
Arnaldo Carvalho de Melo 800fd44d6b dwarves: Make two bitfield entries be bool
As we had 14 bits hole, so use them as bools, each taking one byte
but generating better/shorter code.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-12-05 18:31:16 -02:00
Arnaldo Carvalho de Melo 6476d24d73 pahole: Introduce --hex to print offsets and sizes in hexadecimal
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-10-20 13:59:12 -02:00
Arnaldo Carvalho de Melo 9720415091 dwarf: Detect type loops
[acme@doppio pahole]$ pahole -F ctf /media/tb/debuginfo/usr/lib/debug/usr/bin/greycstoration4integration.debug > /tmp/bla
<ERROR(tag__size:837): detected type loop: type=572, tag=const_type>
<ERROR(tag__size:837): detected type loop: type=572, tag=const_type>
[acme@doppio pahole]$

These type loops are problems in the CTF encoding, that should be fixed, but
should not cause the core code to segfault on an infinite recursion.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-09-14 17:07:02 -03:00
Arnaldo Carvalho de Melo 406944b404 dwarves: Add more __name() routines and remove s()
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-09-12 18:11:19 -03:00
Arnaldo Carvalho de Melo 7e5b39f944 dwarves_fprintf: Make tag__id_not_found_(f|sn)printf print __LINE__
Helps debugging.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-09-11 15:10:43 -03:00
Arnaldo Carvalho de Melo fc1269af2f pahole: Introduce --classes_as_structs
That asks dwarf_fprintf to always use "struct" in places where it would
use "class", because CTF doesn't have the "class" concept, so for
'regtest diffctf' sake, we use this.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-24 17:22:43 -03:00
Arnaldo Carvalho de Melo d9b4badca2 ctf: Handle dwfl_module_getsymtab errors
That can happen, for instance, when the symtabs are NOBITS. When that
happened we ended up in an infinite loop. Call it earlier and check the
result.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-23 11:47:03 -03:00
Arnaldo Carvalho de Melo ca6dc1446c dwarf_loader: Follow const types too in class_member__cache_byte_size
In the same fashion as DW_TAG_volatile_type, as we need to get to the
DW_TAG_base_type at the end of the chain.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-20 19:48:14 -03:00
Arnaldo Carvalho de Melo bd361e461e dwarf_loader/dwarves_fprintf: Support "using" pointing to data members
If it is C++ add DW_TAG_member entries to cu->tags_table and at
imported_declaration__fprintf fallback to cu__tag() if cu__function()
fails.

The right thing tho, long term, is to have a class for
"DW_TAG_imported_declaration" to register to what kind of tag this
points, if for DW_TAG_subprogram or to DW_TAG_member, the info is in the
DWARF DW_AT_import attribute, but so far we're not decoding it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-20 18:33:52 -03:00
Arnaldo Carvalho de Melo 3de722e9ab dwarves: Delete list entries in reverse order
It doesn't matter when using a traditional malloc/free allocator, but
with obstacks we need to do it in reverse order.

For the usual case where we successfully process an object this doesn't
matter, as when we started using obstacks we don't traverse all the tags
calling their destructors anymore, we just free the whole obstack in one
go.

Noticed when processing object files built from non-supported languages
such as FORTRAN and Pascal, where there are some DWARF tags that are not
supported, which makes the object file load to be prematurely aborted
and that calls destructors for things like classes and functions that in
turn free space for their parameter/member lists, which now have to be
done in reverse order.

We could just stop calling the destructors and then destroying the whole
obstack, but I think that partially processed files are a nice feature,
so keep the interface in a way that both obstacks and traditinal malloc
alocators can be used.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-19 10:32:49 -03:00
Arnaldo Carvalho de Melo 19bbecf668 dwarves: Pass the cu to destructors to free memory on the obstack
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-18 18:21:20 -03:00
Arnaldo Carvalho de Melo d46525e7ce dwarves: reorganize struct namespace
Oh well, we need to use these tools on these tools from time to time ;-)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-07-08 16:53:22 -03:00
Arnaldo Carvalho de Melo 932a884ed9 dwarves: Use an obstack for all the tags
We got it down from:

15.733210256  seconds time elapsed   ( +-   0.197% )

as reported in the previous changeset to:

[acme@doppio pahole]$ perf stat -r 5 pahole object_samples/zweinberg\@mozilla.com/libgklayout.so > /dev/null

 Performance counter stats for 'pahole object_samples/zweinberg@mozilla.com/libgklayout.so' (5 runs):

   12293.726462  task-clock-msecs         #      0.969 CPUs    ( +-   0.189% )
           2663  context-switches         #      0.000 M/sec   ( +-  18.994% )
             40  CPU-migrations           #      0.000 M/sec   ( +-  34.146% )
         127003  page-faults              #      0.010 M/sec   ( +-   0.000% )
    24417854522  cycles                   #   1986.204 M/sec   ( +-   0.174% )
    29007002413  instructions             #      1.188 IPC     ( +-   0.009% )
      297872959  cache-references         #     24.230 M/sec   ( +-   0.529% )
       21440854  cache-misses             #      1.744 M/sec   ( +-   0.321% )

   12.680530119  seconds time elapsed   ( +-   1.042% )

[acme@doppio pahole]$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-07-08 16:37:37 -03:00
Arnaldo Carvalho de Melo 9c0cb4939c dwarves: Allow avoiding loading addr information
As, for instance, pahole doesn't need it at all.

Down from:

[acme@doppio pahole]$ perf stat -r 5 pahole object_samples/zweinberg\@mozilla.com/libgklayout.so > /dev/null

 Performance counter stats for 'pahole object_samples/zweinberg@mozilla.com/libgklayout.so' (5 runs):

   17233.989563  task-clock-msecs         #      0.994 CPUs    ( +-   0.076% )
           1880  context-switches         #      0.000 M/sec   ( +-   0.159% )
              0  CPU-migrations           #      0.000 M/sec   ( +-   0.000% )
          26248  page-faults              #      0.002 M/sec   ( +-   0.000% )
    34244461105  cycles                   #   1987.030 M/sec   ( +-   0.078% )
    34510583834  instructions             #      1.008 IPC     ( +-   0.001% )
      445937867  cache-references         #     25.875 M/sec   ( +-   0.160% )
       56898165  cache-misses             #      3.302 M/sec   ( +-   0.074% )

   17.335292038  seconds time elapsed   ( +-   0.076% )

[acme@doppio pahole]$

To:

[acme@doppio pahole]$ perf stat -r 5 pahole object_samples/zweinberg\@mozilla.com/libgklayout.so > /dev/null

 Performance counter stats for 'pahole object_samples/zweinberg@mozilla.com/libgklayout.so' (5 runs):

   16511.627334  task-clock-msecs         #      0.992 CPUs    ( +-   0.208% )
           1922  context-switches         #      0.000 M/sec   ( +-   3.068% )
              0  CPU-migrations           #      0.000 M/sec   ( +-   0.000% )
          25570  page-faults              #      0.002 M/sec   ( +-   0.000% )
    32807624343  cycles                   #   1986.941 M/sec   ( +-   0.208% )
    32711598374  instructions             #      0.997 IPC     ( +-   0.001% )
      436345377  cache-references         #     26.427 M/sec   ( +-   0.178% )
       54044997  cache-misses             #      3.273 M/sec   ( +-   0.685% )

   16.652951166  seconds time elapsed   ( +-   0.304% )

[acme@doppio pahole]$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-07-06 13:44:57 -03:00
Arnaldo Carvalho de Melo 5ad2e36d32 dwarves: overhaul cu->language handling
Adding an enum so that CTF doesn't have to include dwarf.h and
setting the language to LANG_C in the CTF loader.

Next csets will handle C++ in a different way, because we may need
to find class sizes in a different CU since ancestors sometimes
are only forward declared...

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-17 15:32:35 -03:00
Arnaldo Carvalho de Melo 063bad0a80 dwarves: Add missing return type to function__addr
dwarves.h:695: warning: return type defaults to ‘int’

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-17 13:09:54 -03:00
Arnaldo Carvalho de Melo 4b796de4aa dwarves: export ftype__fprintf_parms
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-08 14:23:46 -03:00
Arnaldo Carvalho de Melo e39c0b0998 dwarves: Introduce function__addr method
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-08 14:20:44 -03:00
Arnaldo Carvalho de Melo 2791a55e95 dwarves: Add label__name method
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-08 14:19:23 -03:00
Arnaldo Carvalho de Melo 570dd1ea55 dwarves: constify filename parm of cus__load_file
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-08 14:17:23 -03:00
Arnaldo Carvalho de Melo 7c6603189e dwarves: Make all the tags that have an IP to be derived from ip_tag
Next we'll add a new kind of tag, DW_TAG_perf_counter, that will come
from perf.data generated by 'perf report'.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-04 17:30:06 -03:00
Arnaldo Carvalho de Melo e429f8efbb dwarves: Add an rbtree for the functions in a cu
That is used by cus__find_function_by_addr & cu__func_function_by_addr.

First user is pfunct --addr, but this is really for pfunct --annotate, that
will process a perf.data file generated by 'perf report', load the debugging
info and regenerate the functions (pfunct -TVi like) that had hits, using
libdisasm to show the assembly code, etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-04 14:56:44 -03:00
Arnaldo Carvalho de Melo dd8c983b40 dwarves: addresses are now uint64_t, no more Dwarf_ types in the core
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-03 15:40:38 -03:00
Arnaldo Carvalho de Melo 0b9d022fb1 dwarf_loader: Move the specification Dwarf_Off from the core
This field is resolved now entirely by the dwarf loader, no sense in having it
in the core.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-03 15:33:57 -03:00
Arnaldo Carvalho de Melo e2b7d1a5f5 dwarves: types are uint16_t now
Remove one more DWARF specific detail.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-06-03 14:56:53 -03:00
Arnaldo Carvalho de Melo 8cdd311538 dwarf_loader: Load JAVA interfaces as a struct/class
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-04-25 01:53:59 -03:00
Arnaldo Carvalho de Melo 29e67fce58 dwarf_loader: Add containing_type to dwarf_tag
Sharing the same space with abstract_origin, so that we can remove the last
Dwarf_Off in dwarf_fprintf.c.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-04-19 14:04:59 -03:00
Arnaldo Carvalho de Melo f84bf73d54 dwarves: Move the fprintf code to a new source file.
$ wc -l dwarves.c dwarves_fprintf.c
 1468 dwarves.c
 1554 dwarves_fprintf.c
 3022 total
$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-04-19 13:48:51 -03:00