407693e2ca
291 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
Arnaldo Carvalho de Melo
|
b8255beb12 |
core: Allow passing the base_btf object via 'struct conf_load'
So that we can get rid of that global base_btf and use the right way to pass load configuration to the format loaders. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
505a1f5615 |
dwarves: Stop using obstacks
When the CTF and later the BTF loaders were implemented they didn't use obstacks, and then over time some functions, like type__delete(), class__delete(), enumeration__delete() were shared, which can lead to crashes by corrupting the obstack by not following its requirements or to leaks, to avoid such corruption, stop using it. There is a penalty, but I think its not worth the complexity to keep using it. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
b91b19840b |
dwarf_loader: Support DW_AT_data_bit_offset
This appeared in DWARF4 but is supported only in gcc's -gdwarf-5, support it in a way that makes the output be the same for both cases: $ gcc -gdwarf-4 -c examples/dwarf5/bf.c $ pahole bf.o struct pea { long int a:1; /* 0: 0 8 */ long int b:1; /* 0: 1 8 */ long int c:1; /* 0: 2 8 */ /* XXX 29 bits hole, try to pack */ /* Bitfield combined with next fields */ int after_bitfield; /* 4 4 */ /* size: 8, cachelines: 1, members: 4 */ /* sum members: 4 */ /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ /* last cacheline: 8 bytes */ }; $ gcc -gdwarf-5 -c examples/dwarf5/bf.c $ pahole bf.o struct pea { long int a:1; /* 0: 0 8 */ long int b:1; /* 0: 1 8 */ long int c:1; /* 0: 2 8 */ /* XXX 29 bits hole, try to pack */ /* Bitfield combined with next fields */ int after_bitfield; /* 4 4 */ /* size: 8, cachelines: 1, members: 4 */ /* sum members: 4 */ /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ /* last cacheline: 8 bytes */ }; $ Now with an integer before the bitfield: $ cat examples/dwarf5/bf.c struct pea { int before_bitfield; long a:1, b:1, c:1; int after_bitfield; } p; $ gcc -gdwarf-4 -c examples/dwarf5/bf.c $ pahole bf.o struct pea { int before_bitfield; /* 0 4 */ /* Bitfield combined with previous fields */ long int a:1; /* 0:32 8 */ long int b:1; /* 0:33 8 */ long int c:1; /* 0:34 8 */ /* XXX 29 bits hole, try to pack */ int after_bitfield; /* 8 4 */ /* size: 16, cachelines: 1, members: 5 */ /* sum members: 8 */ /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ /* padding: 4 */ /* last cacheline: 16 bytes */ }; $ gcc -gdwarf-5 -c examples/dwarf5/bf.c $ pahole bf.o struct pea { int before_bitfield; /* 0 4 */ /* Bitfield combined with previous fields */ long int a:1; /* 0:32 8 */ long int b:1; /* 0:33 8 */ long int c:1; /* 0:34 8 */ /* XXX 29 bits hole, try to pack */ int after_bitfield; /* 8 4 */ /* size: 16, cachelines: 1, members: 5 */ /* sum members: 8 */ /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ /* padding: 4 */ /* last cacheline: 16 bytes */ }; $ And an array of long integers at the start, before the combination of an integer with a long integer bitfield: $ cat examples/dwarf5/bf.c struct pea { long array[3]; int before_bitfield; long a:1, b:1, c:1; int after_bitfield; } p; $ gcc -gdwarf-4 -c examples/dwarf5/bf.c $ pahole bf.o struct pea { long int array[3]; /* 0 24 */ int before_bitfield; /* 24 4 */ /* Bitfield combined with previous fields */ long int a:1; /* 24:32 8 */ long int b:1; /* 24:33 8 */ long int c:1; /* 24:34 8 */ /* XXX 29 bits hole, try to pack */ int after_bitfield; /* 32 4 */ /* size: 40, cachelines: 1, members: 6 */ /* sum members: 32 */ /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ /* padding: 4 */ /* last cacheline: 40 bytes */ }; $ gcc -gdwarf-5 -c examples/dwarf5/bf.c $ pahole bf.o struct pea { long int array[3]; /* 0 24 */ int before_bitfield; /* 24 4 */ /* Bitfield combined with previous fields */ long int a:1; /* 24:32 8 */ long int b:1; /* 24:33 8 */ long int c:1; /* 24:34 8 */ /* XXX 29 bits hole, try to pack */ int after_bitfield; /* 32 4 */ /* size: 40, cachelines: 1, members: 6 */ /* sum members: 32 */ /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ /* padding: 4 */ /* last cacheline: 40 bytes */ }; $ Reported-by: Mark Wielaard <mark@klomp.org> Tested-by: "Daniel P. Berrangé" <berrange@redhat.com> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1919965 Link: https://lore.kernel.org/dwarves/20210128121122.GA775562@kernel.org/ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
89cf28228a |
fprintf: Add enumeration__max_entry_name_len()
Cache the results, will be used when pretty printing enumerations, to align the entries. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
bc1afd4585 |
pahole: Introduce --numeric_version for use in scripts and Makefiles
In Makefiles we want to do purely numeric comparisions, such as in the Linux kernel Makefiles and scripts, that have things like this at the moment: $ grep PAHOLE */*.sh scripts/link-vmlinux.sh: if ! [ -x "$(command -v ${PAHOLE})" ]; then scripts/link-vmlinux.sh: echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" scripts/link-vmlinux.sh: pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/') scripts/link-vmlinux.sh: echo >&2 "BTF: ${1}: pahole version $(${PAHOLE} --version) is too old, need at least v1.16" scripts/link-vmlinux.sh: LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} $ So just provide: $ pahole --numeric_version 118 $ While keeping the --version output for older Makefiles and scripts. Cc: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
75f3520fed |
strings: Rename strings.h to avoid clashing with /usr/include/strings.h
This was detected with: In file included from /home/acme/git/pahole/strings.h:9, from /usr/include/string.h:432, from /home/acme/git/pahole/lib/bpf/src/libbpf_common.h:12, from /home/acme/git/pahole/lib/bpf/src/libbpf.h:20, from /home/acme/git/pahole/lib/bpf/src/ringbuf.c:20: /home/acme/git/pahole/lib/bpf/src/btf.h:33:11: error: expected ‘;’ before ‘void’ 33 | LIBBPF_API void btf__free(struct btf *btf); | ^~~~~ | ; libbpf_common.h has: #include <string.h> #ifndef LIBBPF_API #define LIBBPF_API __attribute__((visibility("default"))) #endif So before defining LIBBPF_API it includes libc's string.h that in turn includes pahole's strings.h and now it includes: #include "lib/bpf/src/btf.h" That will need the LIBBPF_API, b00m. So lets just rename pahole's strings.h to pahole_strings.h to avoid this pitfall. This patch was moved to before this problem takes place so that we keep everything bisectable. Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Andrii Nakryiko
|
0a9b89910e |
dwarves: Expose and maintain active debug info loader operations
Maintain a pointer to debug_fmt_ops corresponding to currently used debug info format loader (DWARF, BTF, or CTF), to allow various parts of libdwarves to do things like resolve string offset to actual string pointer in a format-agnostic format. This allows to, say, load DWARF debug info, and use it for BTF generation, without either of them making assumptions about how strings are actually stored internally. This is going to be used in the next patch to allow BTF loader and encoder to use a very different way of storing strings (not a global shared gobuffer). Committer notes: Since it is available in multiple object files, add a dwarves__ prefix namespace and add an extern for it in dwarves.h. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Cc: bpf@vger.kernel.org Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Hao Luo
|
c815d26689 |
btf_encoder: Handle DW_TAG_variable that has DW_AT_specification
It is found on gcc 8.2 that global percpu variables generate the following dwarf entry in the cu where the variable is defined[1]. Take the global variable "bpf_prog_active" defined in kernel/bpf/syscall.c as an example. The debug info for syscall.c has two dwarf entries for "bpf_prog_active". > readelf -wi kernel/bpf/syscall.o 0x00013534: DW_TAG_variable DW_AT_name ("bpf_prog_active") DW_AT_decl_file ("/data/users/yhs/work/net-next/include/linux/bpf.h") DW_AT_decl_line (1074) DW_AT_decl_column (0x01) DW_AT_type (0x000000d6 "int") DW_AT_external (true) DW_AT_declaration (true) 0x00021a25: DW_TAG_variable DW_AT_specification (0x00013534 "bpf_prog_active") DW_AT_decl_file ("/data/users/yhs/work/net-next/kernel/bpf/syscall.c") DW_AT_decl_line (43) DW_AT_location (DW_OP_addr 0x0) Note that second DW_TAG_variable entry contains specification that points to the first entry. This causes problem for btf_encoder when encoding global variables. The tag generated for the second entry doesn't have the type and scope info. Therefore the BTF VARs encoded using this tag has incorrect type_id and scope. As fix, when creating variable, examine the dwarf entry. If it has a DW_AT_specification, store the referred struct variable in a 'spec' field. When encoding VARs, check this 'spec', if it's non-empty, follow the pointer to use the referred var. [1] https://www.mail-archive.com/netdev@vger.kernel.org/msg348144.html Tested: Tested using gcc 4.9 and gcc 8.2. The types and scopes of global vars are now generated correctly. [21] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [21102] VAR 'bpf_prog_active' type_id=21, linkage=global-alloc Signed-off-by: Hao Luo <haoluo@google.com> Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com> Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com> Cc: Daniel Borkmann <daniel@iogearbox.net> 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> |
||
Arnaldo Carvalho de Melo
|
8b00a5cd67 |
dwarf_loader: Support DW_TAG_string_type
FORTRAN 95 stuff: $ readelf -wi examples/fortran95/derived-type.debug | grep Fortran -A2 <9c> DW_AT_producer : (indirect string, offset: 0x1fb): GNU Fortran2008 10.2.1 20200728 [revision c0438ced53bcf57e4ebb1c38c226e41571aca892] -mtune=generic -march=x86-64 -g -fno-stack-protector -J /home/vries/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.fortran/derived-type -fintrinsic-modules-path /usr/lib64/gcc/x86_64-suse-linux/10/finclude -fpre-include=/usr/include/finclude/math-vector-fortran.h <a0> DW_AT_language : 14 (Fortran 95) <a1> DW_AT_identifier_case: 2 (down_case) <a2> DW_AT_name : (indirect string, offset: 0x365): /home/vries/gdb_versions/devel/src/gdb/testsuite/gdb.fortran/derived-type.f90 [acme@five pahole]$ readelf -wi examples/fortran95/derived-type.debug | grep DW_TAG_string_type -A2 <1><122>: Abbrev Number: 6 (DW_TAG_string_type) <123> DW_AT_byte_size : 7 $ Kinda like an array, but number of entries is given by DW_AT_byte_size, if it isn't there, then 1 byte. DW_TAG_array_type can have zero size. Next patch will pretty print it. Reported-by: Tom de Vries Bugtracker: https://github.com/acmel/dwarves/issues/9 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
b9e4063119 |
pahole: Cache the type_enum lookups into struct enumerator
I.e. when we get the type= field value, look it up in the type_enum=, get the struct enumerator and if it is the first time that we're looking up the string representation of that enumerator, do it and then cache the results, so that next time we reuse it. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
fda1825f0b |
dwarves: Introduce tag_cu_node, so that we can have the leaner tag_cu
With just tag + cu pointers. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
fdfc64ec44 |
pahole: Introduce --range
For a data structure like: $ pahole --hex ~/bin/perf --header perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ These are now equivalent: $ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --count 1 --hex < perf.data { .header = { .type = PERF_RECORD_TIME_CONV, .misc = 0, .size = 0x20, }, .time_shift = 0x1f, .time_mult = 0x3c9b3031, .time_zero = 0x18c520cf8532e, }, $ pahole ~/bin/perf --header=perf_file_header --range=data -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --count 1 --hex < perf.data { .header = { .type = PERF_RECORD_TIME_CONV, .misc = 0, .size = 0x20, }, .time_shift = 0x1f, .time_mult = 0x3c9b3031, .time_zero = 0x18c520cf8532e, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
c50b6d37e9 |
pahole: Add infrastructure to have multiple concatenated type_enum
As sometimes we have multiple enums to represent some struct type, like with perf_event_attr->type, that has 'enum perf_event_type' in Linux's UAPI and 'enum perf_user_event_type' for purely userspace types, like the ones synthesized for Intel PT, like PERF_RECORD_AUXTRACE, etc. This patch just transforms type->type_enum into a list, the support for multiple types comes next. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
dd3c0e9eb0 |
dwarves: Move the common initialization of fields for 'struct type'
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
4ece15c37b |
dwarves: Find common enumerators prefix
Allowing for filters such as 'type==MMAP', equivalent to 'type==PERF_RECORD_MMAP'. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
Arnaldo Carvalho de Melo
|
1b2cdda38c |
dwarves: Introduce tag__is_array()
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |
||
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:
|
||
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> |
||
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> |
||
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> |
||
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> |
||
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> |