dwarves/pahole.c

2856 lines
70 KiB
C
Raw Normal View History

/*
SPDX-License-Identifier: GPL-2.0-only
Copyright (C) 2006 Mandriva Conectiva S.A.
Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
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 14:17:38 +02:00
Copyright (C) 2007- Arnaldo Carvalho de Melo <acme@redhat.com>
*/
#include <argp.h>
#include <assert.h>
#include <stdio.h>
#include <dwarf.h>
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
#include <inttypes.h>
#include <search.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
#include <unistd.h>
#include "dwarves_reorganize.h"
#include "dwarves.h"
#include "dutil.h"
#include "ctf_encoder.h"
#include "btf_encoder.h"
#include "libbtf.h"
#include "lib/bpf/src/libbpf.h"
static bool btf_encode;
static bool ctf_encode;
static bool first_obj_only;
static bool skip_encoding_btf_vars;
static bool btf_encode_force;
static const char *base_btf_file;
static uint8_t class__include_anonymous;
static uint8_t class__include_nested_anonymous;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
static uint8_t word_size, original_word_size;
static char *class__exclude_prefix;
static size_t class__exclude_prefix_len;
static char *class__include_prefix;
static size_t class__include_prefix_len;
static char *cu__exclude_prefix;
static size_t cu__exclude_prefix_len;
static char *decl_exclude_prefix;
static size_t decl_exclude_prefix_len;
static uint16_t nr_holes;
static uint16_t nr_bit_holes;
[LIB]: Introduce class__has_hole_ge() That returns if the class has a hole greater or equal to the size specified. Pahole now has a --hole_size_ge command line option to use it. Example on a linux kernel built for x86_64 where we list the structs that have holes bigger than 32 bytes, that provides an approximation of structs with ____cacheline_aligned_in_smp annotated members: [acme@filo pahole]$ pahole --hole_size_ge 32 examples/vmlinux-x86_64 inet_hashinfo rcu_ctrlblk hh_cache net_device files_struct module zone For instance, look at struct zone clever use of such construct: _pad1_ is defined with ZONE_PADDING(_pad1_), that is: /* <40e> /home/acme/git/net-2.6.22/include/linux/mmzone.h:179 */ struct zone { long unsigned int pages_min; /* 0 8 */ long unsigned int pages_low; /* 8 8 */ long unsigned int pages_high; /* 16 8 */ long unsigned int lowmem_reserve[3]; /* 24 24 */ int node; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int min_unmapped_pages; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int min_slab_pages; /* 64 8 */ struct per_cpu_pageset * pageset[255]; /* 72 2040 */ /* --- cacheline 33 boundary (2112 bytes) --- */ spinlock_t lock; /* 2112 4 */ /* XXX 4 bytes hole, try to pack */ struct free_area free_area[11]; /* 2120 264 */ /* XXX 48 bytes hole, try to pack */ /* --- cacheline 38 boundary (2432 bytes) --- */ struct zone_padding _pad1_; /* 2432 0 */ spinlock_t lru_lock; /* 2432 4 */ /* XXX 4 bytes hole, try to pack */ struct list_head active_list; /* 2440 16 */ struct list_head inactive_list; /* 2456 16 */ long unsigned int nr_scan_active; /* 2472 8 */ long unsigned int nr_scan_inactive; /* 2480 8 */ long unsigned int pages_scanned; /* 2488 8 */ /* --- cacheline 39 boundary (2496 bytes) --- */ int all_unreclaimable; /* 2496 4 */ atomic_t reclaim_in_progress; /* 2500 4 */ atomic_long_t vm_stat[20]; /* 2504 160 */ /* --- cacheline 41 boundary (2624 bytes) was 40 bytes ago --- */ int prev_priority; /* 2664 4 */ /* XXX 20 bytes hole, try to pack */ /* --- cacheline 42 boundary (2688 bytes) --- */ struct zone_padding _pad2_; /* 2688 0 */ wait_queue_head_t * wait_table; /* 2688 8 */ long unsigned int wait_table_hash_nr_entries; /* 2696 8 */ long unsigned int wait_table_bits; /* 2704 8 */ struct pglist_data * zone_pgdat; /* 2712 8 */ long unsigned int zone_start_pfn; /* 2720 8 */ long unsigned int spanned_pages; /* 2728 8 */ long unsigned int present_pages; /* 2736 8 */ const char * name; /* 2744 8 */ /* --- cacheline 43 boundary (2752 bytes) --- */ }; /* size: 2752, cachelines: 43 */ /* sum members: 2672, holes: 5, sum holes: 80 */ /* definitions: 933 */ Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 18:32:53 +02:00
static uint16_t hole_size_ge;
static uint8_t show_packable;
[PAHOLE]: Reorganize bitfields This cset also does a fixup for cases where the compiler keeps the type specified by the programmer for a bitfield but uses less space to combine with the next, non-bitfield member, these cases can be caught using plain pahole and will appear with this comment: /* --- cacheline 1 boundary (64 bytes) --- */ int bitfield1:1; /* 64 4 */ int bitfield2:1; /* 64 4 */ /* XXX 14 bits hole, try to pack */ /* Bitfield WARNING: DWARF size=4, real size=2 */ short int d; /* 66 2 */ The fixup is done prior to reorganizing the fields. Now an example of this code in action: [acme@filo examples]$ cat swiss_cheese.c <SNIP> struct cheese { char id; short number; char name[52]; int a:1; int b; int bitfield1:1; int bitfield2:1; short d; short e; short last:5; }; <SNIP> [acme@filo examples]$ Lets look at the layout: [acme@filo examples]$ pahole swiss_cheese cheese /* <11b> /home/acme/git/pahole/examples/swiss_cheese.c:3 */ struct cheese { char id; /* 0 1 */ /* XXX 1 byte hole, try to pack */ short int number; /* 2 2 */ char name[52]; /* 4 52 */ int a:1; /* 56 4 */ /* XXX 31 bits hole, try to pack */ int b; /* 60 4 */ /* --- cacheline 1 boundary (64 bytes) --- */ int bitfield1:1; /* 64 4 */ int bitfield2:1; /* 64 4 */ /* XXX 14 bits hole, try to pack */ /* Bitfield WARNING: DWARF size=4, real size=2 */ short int d; /* 66 2 */ short int e; /* 68 2 */ short int last:5; /* 70 2 */ }; /* size: 72, cachelines: 2 */ /* sum members: 71, holes: 1, sum holes: 1 */ /* bit holes: 2, sum bit holes: 45 bits */ /* bit_padding: 11 bits */ /* last cacheline: 8 bytes */ [acme@filo examples]$ Full of holes, has bit padding and uses more than one 64 bytes cacheline. Now lets ask pahole to reorganize it: [acme@filo examples]$ pahole --reorganize --verbose swiss_cheese cheese /* Demoting bitfield ('a' ... 'a') from 'int' to 'unsigned char' */ /* Demoting bitfield ('bitfield1' ... 'bitfield2') from 'short unsigned int' to 'unsigned char' */ /* Demoting bitfield ('last') from 'short int' to 'unsigned char' */ /* Moving 'bitfield2:1' from after 'bitfield1' to after 'a:1' */ /* Moving 'bitfield1:1' from after 'b' to after 'bitfield2:1' */ /* Moving 'last:5' from after 'e' to after 'bitfield1:1' */ /* Moving bitfield('a' ... 'last') from after 'name' to after 'id' */ /* Moving 'e' from after 'd' to after 'b' */ /* <11b> /home/acme/git/pahole/examples/swiss_cheese.c:3 */ struct cheese { char id; /* 0 1 */ unsigned char a:1; /* 1 1 */ unsigned char bitfield2:1; /* 1 1 */ unsigned char bitfield1:1; /* 1 1 */ unsigned char last:5; /* 1 1 */ short int number; /* 2 2 */ char name[52]; /* 4 52 */ int b; /* 56 4 */ short int e; /* 60 2 */ short int d; /* 62 2 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* size: 64, cachelines: 1 */ /* saved 8 bytes and 1 cacheline! */ [acme@filo examples]$ Instant karma, it gets completely packed, and look ma, no __attribute__((packed)) :-) With this struct task_struct in the linux kernel is shrunk by 12 bytes, there is more 4 bytes to save with another technique that involves not combining holes, but using the last single hole to fill it with members at the tail of the struct. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2007-02-01 13:51:16 +01:00
static uint8_t global_verbose;
[PAHOLE]: Introduce --recursive For now only affects the --contains output. Example showing the structs that include struct list_head in a linux kernel module: [acme@filo pahole]$ pahole --recursive --contains list_head examples/ipv6.ko.debug.x86-64 inet_protosw proto sock_iocb key_type msg_queue msg_msg nf_hook_ops softnet_data net_device softnet_data dma_device dma_client dma_chan class_device net_device softnet_data dma_chan class klist_node device_driver device klist device_driver bus_type device file_system_type nfs_lock_info file_lock block_device address_space inode dquot mem_dqinfo super_block inode signal_struct page kioctx file kiocb work_struct delayed_work kioctx timer_list ifmcaddr6 inet6_dev inet6_ifaddr neigh_table neighbour net_device softnet_data sock inet_sock delayed_work kioctx plist_head task_struct sigpending signal_struct task_struct user_struct device dev_pm_info device mutex_waiter mutex seq_file block_device quota_info super_block dquot super_block inode zone per_cpu_pages free_area kset bus_type subsystem class bus_type __wait_queue_head __wait_queue rw_semaphore quota_info super_block super_block inode key blocking_notifier_head bus_type subsystem class bus_type mm_struct dentry vm_area_struct kobject class_device net_device softnet_data dma_chan device_driver module_kobject module device kset bus_type subsystem class bus_type lock_class module mm_struct task_struct Handling in multi-cu objects is not very precise, as the same struct has different dwarf offsets (id) in each CU. A mitigation for this problem will be provided with the --cu_list and --cu_name upcoming options, where one will be able to get a list of the object files in a, for instance, linux kernel .ko module and also to specify a cu name to be the only to be considered when processing multi-cu files (again, such as .ko linux kernel modules). This ends up being also useful to generate a reverse class hierarchy :-) Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 00:11:51 +02:00
static uint8_t recursive;
static size_t cacheline_size;
static uint8_t find_containers;
static uint8_t find_pointers_in_structs;
static int reorganize;
static bool show_private_classes;
static bool defined_in;
2020-01-15 15:35:25 +01:00
static bool just_unions;
static bool just_structs;
pahole: Implement --packed To show just packed structs. For instance, here are the top packed structures in the Linux kernel, using BTF data: $ pahole --packed --sizes | sort -k2 -nr | head e820_table 64004 0 boot_params 4096 0 btrfs_super_block 3531 0 efi_variable 2084 0 ntb_info_regs 800 0 tboot 568 0 _legacy_mbr 512 0 disklabel 512 0 btrfs_root_item 439 0 saved_context 317 0 $ If you then look at: $ pahole e820_table struct e820_table { __u32 nr_entries; /* 0 4 */ struct e820_entry entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * The whole array of E820 entries: */ struct e820_table { __u32 nr_entries; struct e820_entry entries[E820_MAX_ENTRIES]; }; I.e. no explicit __packed__ attributes, but if we expand this a bit: $ pahole -E e820_table struct e820_table { /* typedef __u32 */ unsigned int nr_entries; /* 0 4 */ struct e820_entry { /* typedef u64 -> __u64 */ long long unsigned int addr; /* 4 8 */ /* typedef u64 -> __u64 */ long long unsigned int size; /* 12 8 */ enum e820_type type; /* 20 4 */ } __attribute__((__packed__)) entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ We see that is that entries member that is packed, because: $ pahole e820_entry struct e820_entry { u64 addr; /* 0 8 */ u64 size; /* 8 8 */ enum e820_type type; /* 16 4 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * A single E820 map entry, describing a memory range of [addr...addr+size-1], * of 'type' memory type: * * (We pack it because there can be thousands of them on large systems.) */ struct e820_entry { u64 addr; u64 size; enum e820_type type; } __attribute__((packed)); So yeah, it is there, BTF doesn't explicitly states it is packed (as DWARF does) and pahole was able to infer that correctly. Tested-by: Richard Weinberger <richard@nod.at> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-08 14:27:42 +02:00
static bool just_packed_structs;
static int show_reorg_steps;
static const char *class_name;
static LIST_HEAD(class_names);
static char separator = '\t';
static struct conf_fprintf conf = {
.emit_stats = 1,
};
static struct conf_load conf_load = {
.conf_fprintf = &conf,
};
struct structure {
struct list_head node;
struct rb_node rb_node;
char *name;
uint32_t nr_files;
uint32_t nr_methods;
};
static struct structure *structure__new(const char *name)
{
struct structure *st = malloc(sizeof(*st));
if (st != NULL) {
st->name = strdup(name);
if (st->name == NULL) {
free(st);
return NULL;
}
st->nr_files = 1;
st->nr_methods = 0;
}
return st;
}
static void structure__delete(struct structure *st)
{
free(st->name);
free(st);
}
static struct rb_root structures__tree = RB_ROOT;
static LIST_HEAD(structures__list);
static struct structure *structures__add(struct class *class,
const struct cu *cu,
bool *existing_entry)
{
struct rb_node **p = &structures__tree.rb_node;
struct rb_node *parent = NULL;
struct structure *str;
const char *new_class_name = class__name(class, cu);
while (*p != NULL) {
int rc;
parent = *p;
str = rb_entry(parent, struct structure, rb_node);
rc = strcmp(str->name, new_class_name);
if (rc > 0)
p = &(*p)->rb_left;
else if (rc < 0)
p = &(*p)->rb_right;
else {
*existing_entry = true;
return str;
}
}
str = structure__new(new_class_name);
if (str == NULL)
return NULL;
*existing_entry = false;
rb_link_node(&str->rb_node, parent, p);
rb_insert_color(&str->rb_node, &structures__tree);
/* For linear traversals */
list_add_tail(&str->node, &structures__list);
return str;
}
void structures__delete(void)
{
struct rb_node *next = rb_first(&structures__tree);
while (next) {
struct structure *pos = rb_entry(next, struct structure, rb_node);
next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, &structures__tree);
structure__delete(pos);
}
}
static void nr_definitions_formatter(struct structure *st)
{
printf("%s%c%u\n", st->name, separator, st->nr_files);
}
static void nr_members_formatter(struct class *class,
struct cu *cu, uint32_t id __unused)
{
printf("%s%c%u\n", class__name(class, cu), separator,
class__nr_members(class));
}
static void nr_methods_formatter(struct structure *st)
{
printf("%s%c%u\n", st->name, separator, st->nr_methods);
}
static void size_formatter(struct class *class,
struct cu *cu, uint32_t id __unused)
{
printf("%s%c%d%c%u\n", class__name(class, cu), separator,
class__size(class), separator, tag__is_union(class__tag(class)) ? 0 : class->nr_holes);
}
static void class_name_len_formatter(struct class *class, struct cu *cu,
uint32_t id __unused)
{
const char *name = class__name(class, cu);
printf("%s%c%zd\n", name, separator, strlen(name));
}
static void class_name_formatter(struct class *class,
struct cu *cu, uint32_t id __unused)
[LIB]: Introduce class__has_hole_ge() That returns if the class has a hole greater or equal to the size specified. Pahole now has a --hole_size_ge command line option to use it. Example on a linux kernel built for x86_64 where we list the structs that have holes bigger than 32 bytes, that provides an approximation of structs with ____cacheline_aligned_in_smp annotated members: [acme@filo pahole]$ pahole --hole_size_ge 32 examples/vmlinux-x86_64 inet_hashinfo rcu_ctrlblk hh_cache net_device files_struct module zone For instance, look at struct zone clever use of such construct: _pad1_ is defined with ZONE_PADDING(_pad1_), that is: /* <40e> /home/acme/git/net-2.6.22/include/linux/mmzone.h:179 */ struct zone { long unsigned int pages_min; /* 0 8 */ long unsigned int pages_low; /* 8 8 */ long unsigned int pages_high; /* 16 8 */ long unsigned int lowmem_reserve[3]; /* 24 24 */ int node; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int min_unmapped_pages; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int min_slab_pages; /* 64 8 */ struct per_cpu_pageset * pageset[255]; /* 72 2040 */ /* --- cacheline 33 boundary (2112 bytes) --- */ spinlock_t lock; /* 2112 4 */ /* XXX 4 bytes hole, try to pack */ struct free_area free_area[11]; /* 2120 264 */ /* XXX 48 bytes hole, try to pack */ /* --- cacheline 38 boundary (2432 bytes) --- */ struct zone_padding _pad1_; /* 2432 0 */ spinlock_t lru_lock; /* 2432 4 */ /* XXX 4 bytes hole, try to pack */ struct list_head active_list; /* 2440 16 */ struct list_head inactive_list; /* 2456 16 */ long unsigned int nr_scan_active; /* 2472 8 */ long unsigned int nr_scan_inactive; /* 2480 8 */ long unsigned int pages_scanned; /* 2488 8 */ /* --- cacheline 39 boundary (2496 bytes) --- */ int all_unreclaimable; /* 2496 4 */ atomic_t reclaim_in_progress; /* 2500 4 */ atomic_long_t vm_stat[20]; /* 2504 160 */ /* --- cacheline 41 boundary (2624 bytes) was 40 bytes ago --- */ int prev_priority; /* 2664 4 */ /* XXX 20 bytes hole, try to pack */ /* --- cacheline 42 boundary (2688 bytes) --- */ struct zone_padding _pad2_; /* 2688 0 */ wait_queue_head_t * wait_table; /* 2688 8 */ long unsigned int wait_table_hash_nr_entries; /* 2696 8 */ long unsigned int wait_table_bits; /* 2704 8 */ struct pglist_data * zone_pgdat; /* 2712 8 */ long unsigned int zone_start_pfn; /* 2720 8 */ long unsigned int spanned_pages; /* 2728 8 */ long unsigned int present_pages; /* 2736 8 */ const char * name; /* 2744 8 */ /* --- cacheline 43 boundary (2752 bytes) --- */ }; /* size: 2752, cachelines: 43 */ /* sum members: 2672, holes: 5, sum holes: 80 */ /* definitions: 933 */ Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 18:32:53 +02:00
{
puts(class__name(class, cu));
[LIB]: Introduce class__has_hole_ge() That returns if the class has a hole greater or equal to the size specified. Pahole now has a --hole_size_ge command line option to use it. Example on a linux kernel built for x86_64 where we list the structs that have holes bigger than 32 bytes, that provides an approximation of structs with ____cacheline_aligned_in_smp annotated members: [acme@filo pahole]$ pahole --hole_size_ge 32 examples/vmlinux-x86_64 inet_hashinfo rcu_ctrlblk hh_cache net_device files_struct module zone For instance, look at struct zone clever use of such construct: _pad1_ is defined with ZONE_PADDING(_pad1_), that is: /* <40e> /home/acme/git/net-2.6.22/include/linux/mmzone.h:179 */ struct zone { long unsigned int pages_min; /* 0 8 */ long unsigned int pages_low; /* 8 8 */ long unsigned int pages_high; /* 16 8 */ long unsigned int lowmem_reserve[3]; /* 24 24 */ int node; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int min_unmapped_pages; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int min_slab_pages; /* 64 8 */ struct per_cpu_pageset * pageset[255]; /* 72 2040 */ /* --- cacheline 33 boundary (2112 bytes) --- */ spinlock_t lock; /* 2112 4 */ /* XXX 4 bytes hole, try to pack */ struct free_area free_area[11]; /* 2120 264 */ /* XXX 48 bytes hole, try to pack */ /* --- cacheline 38 boundary (2432 bytes) --- */ struct zone_padding _pad1_; /* 2432 0 */ spinlock_t lru_lock; /* 2432 4 */ /* XXX 4 bytes hole, try to pack */ struct list_head active_list; /* 2440 16 */ struct list_head inactive_list; /* 2456 16 */ long unsigned int nr_scan_active; /* 2472 8 */ long unsigned int nr_scan_inactive; /* 2480 8 */ long unsigned int pages_scanned; /* 2488 8 */ /* --- cacheline 39 boundary (2496 bytes) --- */ int all_unreclaimable; /* 2496 4 */ atomic_t reclaim_in_progress; /* 2500 4 */ atomic_long_t vm_stat[20]; /* 2504 160 */ /* --- cacheline 41 boundary (2624 bytes) was 40 bytes ago --- */ int prev_priority; /* 2664 4 */ /* XXX 20 bytes hole, try to pack */ /* --- cacheline 42 boundary (2688 bytes) --- */ struct zone_padding _pad2_; /* 2688 0 */ wait_queue_head_t * wait_table; /* 2688 8 */ long unsigned int wait_table_hash_nr_entries; /* 2696 8 */ long unsigned int wait_table_bits; /* 2704 8 */ struct pglist_data * zone_pgdat; /* 2712 8 */ long unsigned int zone_start_pfn; /* 2720 8 */ long unsigned int spanned_pages; /* 2728 8 */ long unsigned int present_pages; /* 2736 8 */ const char * name; /* 2744 8 */ /* --- cacheline 43 boundary (2752 bytes) --- */ }; /* size: 2752, cachelines: 43 */ /* sum members: 2672, holes: 5, sum holes: 80 */ /* definitions: 933 */ Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 18:32:53 +02:00
}
static void class_formatter(struct class *class, struct cu *cu, uint32_t id)
{
struct tag *typedef_alias = NULL;
struct tag *tag = class__tag(class);
const char *name = class__name(class, cu);
if (name == NULL) {
/*
* Find the first typedef for this struct, this is enough
* as if we optimize the struct all the typedefs will be
* affected.
*/
typedef_alias = cu__find_first_typedef_of_type(cu, id);
/*
* If there is no typedefs for this anonymous struct it is
* found just inside another struct, and in this case it'll
* be printed when the type it is in is printed, but if
* the user still wants to see its statistics, just use
* --nested_anon_include.
*/
if (typedef_alias == NULL && !class__include_nested_anonymous)
return;
}
if (typedef_alias != NULL) {
struct type *tdef = tag__type(typedef_alias);
conf.prefix = "typedef";
conf.suffix = type__name(tdef, cu);
} else
conf.prefix = conf.suffix = NULL;
tag__fprintf(tag, cu, &conf, stdout);
putchar('\n');
}
static void print_packable_info(struct class *c, struct cu *cu, uint32_t id)
{
const struct tag *t = class__tag(c);
const size_t orig_size = class__size(c);
const size_t new_size = class__size(c->priv);
const size_t savings = orig_size - new_size;
const char *name = class__name(c, cu);
/* Anonymous struct? Try finding a typedef */
if (name == NULL) {
const struct tag *tdef =
cu__find_first_typedef_of_type(cu, id);
if (tdef != NULL)
name = class__name(tag__class(tdef), cu);
}
if (name != NULL)
printf("%s%c%zd%c%zd%c%zd\n",
name, separator,
orig_size, separator,
new_size, separator,
savings);
else
printf("%s(%d)%c%zd%c%zd%c%zd\n",
tag__decl_file(t, cu),
tag__decl_line(t, cu),
separator,
orig_size, separator,
new_size, separator,
savings);
}
static void (*stats_formatter)(struct structure *st);
static void print_stats(void)
{
struct structure *pos;
list_for_each_entry(pos, &structures__list, node)
stats_formatter(pos);
}
static struct class *class__filter(struct class *class, struct cu *cu,
uint32_t tag_id);
static void (*formatter)(struct class *class,
struct cu *cu, uint32_t id) = class_formatter;
static void print_classes(struct cu *cu)
{
uint32_t id;
struct class *pos;
cu__for_each_struct_or_union(cu, id, pos) {
bool existing_entry;
struct structure *str;
if (pos->type.namespace.name == 0 &&
!(class__include_anonymous ||
class__include_nested_anonymous))
continue;
if (!class__filter(pos, cu, id))
continue;
/*
* FIXME: No sense in adding an anonymous struct to the list of
* structs already printed, as we look for the name... The
* right fix probably will be to call class__fprintf on a
* in-memory FILE, do a hash, and look it by full contents, not
* by name. And this is needed for CTF as well, but its late now
* and I'm sleepy, will leave for later...
*/
if (pos->type.namespace.name != 0) {
str = structures__add(pos, cu, &existing_entry);
if (str == NULL) {
fprintf(stderr, "pahole: insufficient memory for "
"processing %s, skipping it...\n", cu->name);
return;
}
/* Already printed... */
if (existing_entry) {
str->nr_files++;
continue;
}
}
if (show_packable && !global_verbose)
print_packable_info(pos, cu, id);
else if (formatter != NULL)
formatter(pos, cu, id);
}
}
static struct cu *cu__filter(struct cu *cu)
{
if (cu__exclude_prefix != NULL &&
(cu->name == NULL ||
strncmp(cu__exclude_prefix, cu->name,
cu__exclude_prefix_len) == 0))
return NULL;
return cu;
}
static int class__packable(struct class *class, struct cu *cu)
{
struct class *clone;
if (class->nr_holes == 0 && class->nr_bit_holes == 0)
return 0;
clone = class__clone(class, NULL, cu);
if (clone == NULL)
return 0;
class__reorganize(clone, cu, 0, stdout);
if (class__size(class) > class__size(clone)) {
class->priv = clone;
return 1;
}
/* FIXME: we need to free in the right order,
* cu->obstack is being corrupted...
class__delete(clone, cu);
*/
return 0;
}
static struct class *class__filter(struct class *class, struct cu *cu,
uint32_t tag_id)
{
struct tag *tag = class__tag(class);
const char *name;
2020-01-15 15:35:25 +01:00
if (just_unions && !tag__is_union(tag))
return NULL;
if (just_structs && !tag__is_struct(tag))
return NULL;
pahole: Implement --packed To show just packed structs. For instance, here are the top packed structures in the Linux kernel, using BTF data: $ pahole --packed --sizes | sort -k2 -nr | head e820_table 64004 0 boot_params 4096 0 btrfs_super_block 3531 0 efi_variable 2084 0 ntb_info_regs 800 0 tboot 568 0 _legacy_mbr 512 0 disklabel 512 0 btrfs_root_item 439 0 saved_context 317 0 $ If you then look at: $ pahole e820_table struct e820_table { __u32 nr_entries; /* 0 4 */ struct e820_entry entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * The whole array of E820 entries: */ struct e820_table { __u32 nr_entries; struct e820_entry entries[E820_MAX_ENTRIES]; }; I.e. no explicit __packed__ attributes, but if we expand this a bit: $ pahole -E e820_table struct e820_table { /* typedef __u32 */ unsigned int nr_entries; /* 0 4 */ struct e820_entry { /* typedef u64 -> __u64 */ long long unsigned int addr; /* 4 8 */ /* typedef u64 -> __u64 */ long long unsigned int size; /* 12 8 */ enum e820_type type; /* 20 4 */ } __attribute__((__packed__)) entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ We see that is that entries member that is packed, because: $ pahole e820_entry struct e820_entry { u64 addr; /* 0 8 */ u64 size; /* 8 8 */ enum e820_type type; /* 16 4 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * A single E820 map entry, describing a memory range of [addr...addr+size-1], * of 'type' memory type: * * (We pack it because there can be thousands of them on large systems.) */ struct e820_entry { u64 addr; u64 size; enum e820_type type; } __attribute__((packed)); So yeah, it is there, BTF doesn't explicitly states it is packed (as DWARF does) and pahole was able to infer that correctly. Tested-by: Richard Weinberger <richard@nod.at> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-08 14:27:42 +02:00
if (just_packed_structs) {
/* Is it not packed? */
if (!class__infer_packed_attributes(class, cu))
return NULL;
}
if (!tag->top_level) {
class__find_holes(class);
if (!show_private_classes)
return NULL;
}
dwarves: Remove some more DWARF details from the core Had to be a big sweeping change, but the regression tests shows just improvements :-) Now we stop using an id in struct tag, only storing the type, that now uses 16 bits only, as CTF does. Each format loader has to go on adding the types to the core, that figures out if it is a tag that can be on the tag->type field (tag__is_tag_type). Formats that already have the types separated and in sequence, such as CTF, just ask the core to insert in the types_table directly with its original ID. For DWARF, we ask the core to put it on the table, in sequence, and return the index, that is then stashed with the DWARF specific info (original id, type, decl_line, etc) and hashed by the original id. Later we recode everything, looking up via the original type, getting the small_id to put on the tag->type. The underlying debugging info not needed by the core is stashed in tag->priv, and the DWARF loader now just allocates sizeof(struct dwarf_tag) at the end of the core tag and points it there, and makes that info available thru cu->orig_info. In the future we can ask, when loading a cu, that this info be trown away, so that we reduce the memory footprint for big multi-cu files such as the Linux kernel. There is also a routine to ask for inserting a NULL, as we still have bugs in the CTF decoding and thus some entries are being lost, to avoid using an undefined pointer when traversing the types_table the ctf loader puts a NULL there via cu__table_nullify_type_entry() and then cu__for_each_type skips those. There is some more cleanups for leftovers that I avoided cleaning to reduce this changeset. And also while doing this I saw that enums can appear without any enumerators and that an array with DW_TAG_GNU_vector is actually a different tag, encoded this way till we get to DWARF4 ;-) So now we don't have to lookup on a hash table looking for DWARF offsets, we can do the more sensible thing of just indexing the types_tags array. Now to do some cleanups and try to get the per cu encoder done. Then order all the cus per number of type entries, pick the one with more, then go on merging/recoding the types of the others and putting the parent linkage in place. Just to show the extent of the changes: $ codiff /tmp/libdwarves.so.1.0.0 build/libdwarves.so.1.0.0 /home/acme/git/pahole/dwarves.c: struct cu | -4048 struct tag | -32 struct ptr_to_member_type | -32 struct namespace | -32 struct type | -32 struct class | -32 struct base_type | -32 struct array_type | -32 struct class_member | -32 struct lexblock | -32 struct ftype | -32 struct function | -64 struct parameter | -32 struct variable | -32 struct inline_expansion | -32 struct label | -32 struct enumerator | -32 17 structs changed tag__follow_typedef | +3 tag__fprintf_decl_info | +25 array_type__fprintf | +6 type__name | -126 type__find_first_biggest_size_base_type_member | -3 typedef__fprintf | +16 imported_declaration__fprintf | +6 imported_module__fprintf | +3 cu__new | +26 cu__delete | +26 hashtags__hash | -65 hash_64 | -124 hlist_add_head | -78 hashtags__find | -157 cu__hash | -80 cu__add_tag | +20 tag__prefix | -3 cu__find_tag_by_id | -2 cu__find_type_by_id | -3 cu__find_first_typedef_of_type | +38 cu__find_base_type_by_name | +68 cu__find_base_type_by_name_and_size | +72 cu__find_struct_by_name | +59 cus__find_struct_by_name | +8 cus__find_tag_by_id | +5 cus__find_cu_by_name | -6 lexblock__find_tag_by_id | -173 cu__find_variable_by_id | -197 list__find_tag_by_id | -308 cu__find_parameter_by_id | -60 tag__ptr_name | +6 tag__name | +15 variable__type | +13 variable__name | +7 class_member__size | +6 parameter__name | -119 tag__parameter | -14 parameter__type | -143 type__fprintf | -29 union__fprintf | +6 class__add_vtable_entry | -9 type__add_member | -6 type__clone_members | -3 enumeration__add | -6 function__name | -156 ftype__has_parm_of_type | -39 class__find_holes | -27 class__has_hole_ge | -3 type__nr_members_of_type | +3 lexblock__account_inline_expansions | +3 cu__account_inline_expansions | -18 ftype__fprintf_parms | +46 function__tag_fprintf | +24 lexblock__fprintf | -6 ftype__fprintf | +3 function__fprintf_stats | -18 function__size | -6 class__vtable_fprintf | -11 class__fprintf | -21 tag__fprintf | -35 60 functions changed, 513 bytes added, 2054 bytes removed, diff: -1541 /home/acme/git/pahole/ctf_loader.c: struct ctf_short_type | +0 14 structs changed type__init | -14 type__new | -9 class__new | -12 create_new_base_type | -7 create_new_base_type_float | -7 create_new_array | -8 create_new_subroutine_type | -9 create_full_members | -18 create_short_members | -18 create_new_class | +1 create_new_union | +1 create_new_enumeration | -19 create_new_forward_decl | -2 create_new_typedef | +3 create_new_tag | -5 load_types | +16 class__fixup_ctf_bitfields | -3 17 functions changed, 21 bytes added, 131 bytes removed, diff: -110 /home/acme/git/pahole/dwarf_loader.c: 17 structs changed zalloc | -56 tag__init | +3 array_type__new | +20 type__init | -24 class_member__new | +46 inline_expansion__new | +12 class__new | +81 lexblock__init | +19 function__new | +43 die__create_new_array | +20 die__create_new_parameter | +4 die__create_new_label | +4 die__create_new_subroutine_type | +113 die__create_new_enumeration | -21 die__process_class | +79 die__process_namespace | +76 die__create_new_inline_expansion | +4 die__process_function | +147 __die__process_tag | +34 die__process_unit | +56 die__process | +90 21 functions changed, 851 bytes added, 101 bytes removed, diff: +750 /home/acme/git/pahole/dwarves.c: struct ptr_table | +16 struct cu_orig_info | +32 2 structs changed tag__decl_line | +68 tag__decl_file | +70 tag__orig_id | +71 ptr_table__init | +46 ptr_table__exit | +37 ptr_table__add | +183 ptr_table__add_with_id | +165 ptr_table__entry | +64 cu__table_add_tag | +171 cu__table_nullify_type_entry | +38 10 functions changed, 913 bytes added, diff: +913 /home/acme/git/pahole/ctf_loader.c: 2 structs changed tag__alloc | +52 1 function changed, 52 bytes added, diff: +52 /home/acme/git/pahole/dwarf_loader.c: struct dwarf_tag | +48 struct dwarf_cu | +4104 4 structs changed dwarf_cu__init | +83 hashtags__hash | +61 hash_64 | +124 hlist_add_head | +78 hashtags__find | +161 cu__hash | +95 tag__is_tag_type | +171 tag__is_type | +85 tag__is_union | +28 tag__is_struct | +57 tag__is_typedef | +28 tag__is_enumeration | +28 dwarf_cu__find_tag_by_id | +56 dwarf_cu__find_type_by_id | +63 tag__alloc | +114 __tag__print_type_not_found | +108 namespace__recode_dwarf_types | +346 tag__namespace | +14 tag__has_namespace | +86 tag__is_namespace | +28 type__recode_dwarf_specification | +182 tag__type | +14 __tag__print_abstract_origin_not_found | +105 ftype__recode_dwarf_types | +322 tag__ftype | +14 tag__parameter | +14 lexblock__recode_dwarf_types | +736 tag__lexblock | +14 tag__label | +14 tag__recode_dwarf_type | +766 tag__ptr_to_member_type | +14 cu__recode_dwarf_types_table | +88 cu__recode_dwarf_types | +48 dwarf_tag__decl_file | +77 strings__ptr | +33 dwarf_tag__decl_line | +59 dwarf_tag__orig_id | +59 dwarf_tag__orig_type | +59 38 functions changed, 4432 bytes added, diff: +4432 build/libdwarves.so.1.0.0: 147 functions changed, 6782 bytes added, 2286 bytes removed, diff: +4496 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-03-06 00:29:35 +01:00
name = class__name(class, cu);
if (class__is_declaration(class))
return NULL;
if (!class__include_anonymous && name == NULL)
return NULL;
if (class__exclude_prefix != NULL) {
if (name == NULL) {
const struct tag *tdef =
dwarves: Remove some more DWARF details from the core Had to be a big sweeping change, but the regression tests shows just improvements :-) Now we stop using an id in struct tag, only storing the type, that now uses 16 bits only, as CTF does. Each format loader has to go on adding the types to the core, that figures out if it is a tag that can be on the tag->type field (tag__is_tag_type). Formats that already have the types separated and in sequence, such as CTF, just ask the core to insert in the types_table directly with its original ID. For DWARF, we ask the core to put it on the table, in sequence, and return the index, that is then stashed with the DWARF specific info (original id, type, decl_line, etc) and hashed by the original id. Later we recode everything, looking up via the original type, getting the small_id to put on the tag->type. The underlying debugging info not needed by the core is stashed in tag->priv, and the DWARF loader now just allocates sizeof(struct dwarf_tag) at the end of the core tag and points it there, and makes that info available thru cu->orig_info. In the future we can ask, when loading a cu, that this info be trown away, so that we reduce the memory footprint for big multi-cu files such as the Linux kernel. There is also a routine to ask for inserting a NULL, as we still have bugs in the CTF decoding and thus some entries are being lost, to avoid using an undefined pointer when traversing the types_table the ctf loader puts a NULL there via cu__table_nullify_type_entry() and then cu__for_each_type skips those. There is some more cleanups for leftovers that I avoided cleaning to reduce this changeset. And also while doing this I saw that enums can appear without any enumerators and that an array with DW_TAG_GNU_vector is actually a different tag, encoded this way till we get to DWARF4 ;-) So now we don't have to lookup on a hash table looking for DWARF offsets, we can do the more sensible thing of just indexing the types_tags array. Now to do some cleanups and try to get the per cu encoder done. Then order all the cus per number of type entries, pick the one with more, then go on merging/recoding the types of the others and putting the parent linkage in place. Just to show the extent of the changes: $ codiff /tmp/libdwarves.so.1.0.0 build/libdwarves.so.1.0.0 /home/acme/git/pahole/dwarves.c: struct cu | -4048 struct tag | -32 struct ptr_to_member_type | -32 struct namespace | -32 struct type | -32 struct class | -32 struct base_type | -32 struct array_type | -32 struct class_member | -32 struct lexblock | -32 struct ftype | -32 struct function | -64 struct parameter | -32 struct variable | -32 struct inline_expansion | -32 struct label | -32 struct enumerator | -32 17 structs changed tag__follow_typedef | +3 tag__fprintf_decl_info | +25 array_type__fprintf | +6 type__name | -126 type__find_first_biggest_size_base_type_member | -3 typedef__fprintf | +16 imported_declaration__fprintf | +6 imported_module__fprintf | +3 cu__new | +26 cu__delete | +26 hashtags__hash | -65 hash_64 | -124 hlist_add_head | -78 hashtags__find | -157 cu__hash | -80 cu__add_tag | +20 tag__prefix | -3 cu__find_tag_by_id | -2 cu__find_type_by_id | -3 cu__find_first_typedef_of_type | +38 cu__find_base_type_by_name | +68 cu__find_base_type_by_name_and_size | +72 cu__find_struct_by_name | +59 cus__find_struct_by_name | +8 cus__find_tag_by_id | +5 cus__find_cu_by_name | -6 lexblock__find_tag_by_id | -173 cu__find_variable_by_id | -197 list__find_tag_by_id | -308 cu__find_parameter_by_id | -60 tag__ptr_name | +6 tag__name | +15 variable__type | +13 variable__name | +7 class_member__size | +6 parameter__name | -119 tag__parameter | -14 parameter__type | -143 type__fprintf | -29 union__fprintf | +6 class__add_vtable_entry | -9 type__add_member | -6 type__clone_members | -3 enumeration__add | -6 function__name | -156 ftype__has_parm_of_type | -39 class__find_holes | -27 class__has_hole_ge | -3 type__nr_members_of_type | +3 lexblock__account_inline_expansions | +3 cu__account_inline_expansions | -18 ftype__fprintf_parms | +46 function__tag_fprintf | +24 lexblock__fprintf | -6 ftype__fprintf | +3 function__fprintf_stats | -18 function__size | -6 class__vtable_fprintf | -11 class__fprintf | -21 tag__fprintf | -35 60 functions changed, 513 bytes added, 2054 bytes removed, diff: -1541 /home/acme/git/pahole/ctf_loader.c: struct ctf_short_type | +0 14 structs changed type__init | -14 type__new | -9 class__new | -12 create_new_base_type | -7 create_new_base_type_float | -7 create_new_array | -8 create_new_subroutine_type | -9 create_full_members | -18 create_short_members | -18 create_new_class | +1 create_new_union | +1 create_new_enumeration | -19 create_new_forward_decl | -2 create_new_typedef | +3 create_new_tag | -5 load_types | +16 class__fixup_ctf_bitfields | -3 17 functions changed, 21 bytes added, 131 bytes removed, diff: -110 /home/acme/git/pahole/dwarf_loader.c: 17 structs changed zalloc | -56 tag__init | +3 array_type__new | +20 type__init | -24 class_member__new | +46 inline_expansion__new | +12 class__new | +81 lexblock__init | +19 function__new | +43 die__create_new_array | +20 die__create_new_parameter | +4 die__create_new_label | +4 die__create_new_subroutine_type | +113 die__create_new_enumeration | -21 die__process_class | +79 die__process_namespace | +76 die__create_new_inline_expansion | +4 die__process_function | +147 __die__process_tag | +34 die__process_unit | +56 die__process | +90 21 functions changed, 851 bytes added, 101 bytes removed, diff: +750 /home/acme/git/pahole/dwarves.c: struct ptr_table | +16 struct cu_orig_info | +32 2 structs changed tag__decl_line | +68 tag__decl_file | +70 tag__orig_id | +71 ptr_table__init | +46 ptr_table__exit | +37 ptr_table__add | +183 ptr_table__add_with_id | +165 ptr_table__entry | +64 cu__table_add_tag | +171 cu__table_nullify_type_entry | +38 10 functions changed, 913 bytes added, diff: +913 /home/acme/git/pahole/ctf_loader.c: 2 structs changed tag__alloc | +52 1 function changed, 52 bytes added, diff: +52 /home/acme/git/pahole/dwarf_loader.c: struct dwarf_tag | +48 struct dwarf_cu | +4104 4 structs changed dwarf_cu__init | +83 hashtags__hash | +61 hash_64 | +124 hlist_add_head | +78 hashtags__find | +161 cu__hash | +95 tag__is_tag_type | +171 tag__is_type | +85 tag__is_union | +28 tag__is_struct | +57 tag__is_typedef | +28 tag__is_enumeration | +28 dwarf_cu__find_tag_by_id | +56 dwarf_cu__find_type_by_id | +63 tag__alloc | +114 __tag__print_type_not_found | +108 namespace__recode_dwarf_types | +346 tag__namespace | +14 tag__has_namespace | +86 tag__is_namespace | +28 type__recode_dwarf_specification | +182 tag__type | +14 __tag__print_abstract_origin_not_found | +105 ftype__recode_dwarf_types | +322 tag__ftype | +14 tag__parameter | +14 lexblock__recode_dwarf_types | +736 tag__lexblock | +14 tag__label | +14 tag__recode_dwarf_type | +766 tag__ptr_to_member_type | +14 cu__recode_dwarf_types_table | +88 cu__recode_dwarf_types | +48 dwarf_tag__decl_file | +77 strings__ptr | +33 dwarf_tag__decl_line | +59 dwarf_tag__orig_id | +59 dwarf_tag__orig_type | +59 38 functions changed, 4432 bytes added, diff: +4432 build/libdwarves.so.1.0.0: 147 functions changed, 6782 bytes added, 2286 bytes removed, diff: +4496 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-03-06 00:29:35 +01:00
cu__find_first_typedef_of_type(cu, tag_id);
if (tdef != NULL) {
struct class *c = tag__class(tdef);
name = class__name(c, cu);
}
}
if (name != NULL && strncmp(class__exclude_prefix, name,
class__exclude_prefix_len) == 0)
return NULL;
}
if (class__include_prefix != NULL) {
if (name == NULL) {
const struct tag *tdef =
dwarves: Remove some more DWARF details from the core Had to be a big sweeping change, but the regression tests shows just improvements :-) Now we stop using an id in struct tag, only storing the type, that now uses 16 bits only, as CTF does. Each format loader has to go on adding the types to the core, that figures out if it is a tag that can be on the tag->type field (tag__is_tag_type). Formats that already have the types separated and in sequence, such as CTF, just ask the core to insert in the types_table directly with its original ID. For DWARF, we ask the core to put it on the table, in sequence, and return the index, that is then stashed with the DWARF specific info (original id, type, decl_line, etc) and hashed by the original id. Later we recode everything, looking up via the original type, getting the small_id to put on the tag->type. The underlying debugging info not needed by the core is stashed in tag->priv, and the DWARF loader now just allocates sizeof(struct dwarf_tag) at the end of the core tag and points it there, and makes that info available thru cu->orig_info. In the future we can ask, when loading a cu, that this info be trown away, so that we reduce the memory footprint for big multi-cu files such as the Linux kernel. There is also a routine to ask for inserting a NULL, as we still have bugs in the CTF decoding and thus some entries are being lost, to avoid using an undefined pointer when traversing the types_table the ctf loader puts a NULL there via cu__table_nullify_type_entry() and then cu__for_each_type skips those. There is some more cleanups for leftovers that I avoided cleaning to reduce this changeset. And also while doing this I saw that enums can appear without any enumerators and that an array with DW_TAG_GNU_vector is actually a different tag, encoded this way till we get to DWARF4 ;-) So now we don't have to lookup on a hash table looking for DWARF offsets, we can do the more sensible thing of just indexing the types_tags array. Now to do some cleanups and try to get the per cu encoder done. Then order all the cus per number of type entries, pick the one with more, then go on merging/recoding the types of the others and putting the parent linkage in place. Just to show the extent of the changes: $ codiff /tmp/libdwarves.so.1.0.0 build/libdwarves.so.1.0.0 /home/acme/git/pahole/dwarves.c: struct cu | -4048 struct tag | -32 struct ptr_to_member_type | -32 struct namespace | -32 struct type | -32 struct class | -32 struct base_type | -32 struct array_type | -32 struct class_member | -32 struct lexblock | -32 struct ftype | -32 struct function | -64 struct parameter | -32 struct variable | -32 struct inline_expansion | -32 struct label | -32 struct enumerator | -32 17 structs changed tag__follow_typedef | +3 tag__fprintf_decl_info | +25 array_type__fprintf | +6 type__name | -126 type__find_first_biggest_size_base_type_member | -3 typedef__fprintf | +16 imported_declaration__fprintf | +6 imported_module__fprintf | +3 cu__new | +26 cu__delete | +26 hashtags__hash | -65 hash_64 | -124 hlist_add_head | -78 hashtags__find | -157 cu__hash | -80 cu__add_tag | +20 tag__prefix | -3 cu__find_tag_by_id | -2 cu__find_type_by_id | -3 cu__find_first_typedef_of_type | +38 cu__find_base_type_by_name | +68 cu__find_base_type_by_name_and_size | +72 cu__find_struct_by_name | +59 cus__find_struct_by_name | +8 cus__find_tag_by_id | +5 cus__find_cu_by_name | -6 lexblock__find_tag_by_id | -173 cu__find_variable_by_id | -197 list__find_tag_by_id | -308 cu__find_parameter_by_id | -60 tag__ptr_name | +6 tag__name | +15 variable__type | +13 variable__name | +7 class_member__size | +6 parameter__name | -119 tag__parameter | -14 parameter__type | -143 type__fprintf | -29 union__fprintf | +6 class__add_vtable_entry | -9 type__add_member | -6 type__clone_members | -3 enumeration__add | -6 function__name | -156 ftype__has_parm_of_type | -39 class__find_holes | -27 class__has_hole_ge | -3 type__nr_members_of_type | +3 lexblock__account_inline_expansions | +3 cu__account_inline_expansions | -18 ftype__fprintf_parms | +46 function__tag_fprintf | +24 lexblock__fprintf | -6 ftype__fprintf | +3 function__fprintf_stats | -18 function__size | -6 class__vtable_fprintf | -11 class__fprintf | -21 tag__fprintf | -35 60 functions changed, 513 bytes added, 2054 bytes removed, diff: -1541 /home/acme/git/pahole/ctf_loader.c: struct ctf_short_type | +0 14 structs changed type__init | -14 type__new | -9 class__new | -12 create_new_base_type | -7 create_new_base_type_float | -7 create_new_array | -8 create_new_subroutine_type | -9 create_full_members | -18 create_short_members | -18 create_new_class | +1 create_new_union | +1 create_new_enumeration | -19 create_new_forward_decl | -2 create_new_typedef | +3 create_new_tag | -5 load_types | +16 class__fixup_ctf_bitfields | -3 17 functions changed, 21 bytes added, 131 bytes removed, diff: -110 /home/acme/git/pahole/dwarf_loader.c: 17 structs changed zalloc | -56 tag__init | +3 array_type__new | +20 type__init | -24 class_member__new | +46 inline_expansion__new | +12 class__new | +81 lexblock__init | +19 function__new | +43 die__create_new_array | +20 die__create_new_parameter | +4 die__create_new_label | +4 die__create_new_subroutine_type | +113 die__create_new_enumeration | -21 die__process_class | +79 die__process_namespace | +76 die__create_new_inline_expansion | +4 die__process_function | +147 __die__process_tag | +34 die__process_unit | +56 die__process | +90 21 functions changed, 851 bytes added, 101 bytes removed, diff: +750 /home/acme/git/pahole/dwarves.c: struct ptr_table | +16 struct cu_orig_info | +32 2 structs changed tag__decl_line | +68 tag__decl_file | +70 tag__orig_id | +71 ptr_table__init | +46 ptr_table__exit | +37 ptr_table__add | +183 ptr_table__add_with_id | +165 ptr_table__entry | +64 cu__table_add_tag | +171 cu__table_nullify_type_entry | +38 10 functions changed, 913 bytes added, diff: +913 /home/acme/git/pahole/ctf_loader.c: 2 structs changed tag__alloc | +52 1 function changed, 52 bytes added, diff: +52 /home/acme/git/pahole/dwarf_loader.c: struct dwarf_tag | +48 struct dwarf_cu | +4104 4 structs changed dwarf_cu__init | +83 hashtags__hash | +61 hash_64 | +124 hlist_add_head | +78 hashtags__find | +161 cu__hash | +95 tag__is_tag_type | +171 tag__is_type | +85 tag__is_union | +28 tag__is_struct | +57 tag__is_typedef | +28 tag__is_enumeration | +28 dwarf_cu__find_tag_by_id | +56 dwarf_cu__find_type_by_id | +63 tag__alloc | +114 __tag__print_type_not_found | +108 namespace__recode_dwarf_types | +346 tag__namespace | +14 tag__has_namespace | +86 tag__is_namespace | +28 type__recode_dwarf_specification | +182 tag__type | +14 __tag__print_abstract_origin_not_found | +105 ftype__recode_dwarf_types | +322 tag__ftype | +14 tag__parameter | +14 lexblock__recode_dwarf_types | +736 tag__lexblock | +14 tag__label | +14 tag__recode_dwarf_type | +766 tag__ptr_to_member_type | +14 cu__recode_dwarf_types_table | +88 cu__recode_dwarf_types | +48 dwarf_tag__decl_file | +77 strings__ptr | +33 dwarf_tag__decl_line | +59 dwarf_tag__orig_id | +59 dwarf_tag__orig_type | +59 38 functions changed, 4432 bytes added, diff: +4432 build/libdwarves.so.1.0.0: 147 functions changed, 6782 bytes added, 2286 bytes removed, diff: +4496 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-03-06 00:29:35 +01:00
cu__find_first_typedef_of_type(cu, tag_id);
if (tdef != NULL) {
struct class *c = tag__class(tdef);
name = class__name(c, cu);
}
}
if (name != NULL && strncmp(class__include_prefix, name,
class__include_prefix_len) != 0)
return NULL;
}
if (decl_exclude_prefix != NULL &&
dwarves: Remove some more DWARF details from the core Had to be a big sweeping change, but the regression tests shows just improvements :-) Now we stop using an id in struct tag, only storing the type, that now uses 16 bits only, as CTF does. Each format loader has to go on adding the types to the core, that figures out if it is a tag that can be on the tag->type field (tag__is_tag_type). Formats that already have the types separated and in sequence, such as CTF, just ask the core to insert in the types_table directly with its original ID. For DWARF, we ask the core to put it on the table, in sequence, and return the index, that is then stashed with the DWARF specific info (original id, type, decl_line, etc) and hashed by the original id. Later we recode everything, looking up via the original type, getting the small_id to put on the tag->type. The underlying debugging info not needed by the core is stashed in tag->priv, and the DWARF loader now just allocates sizeof(struct dwarf_tag) at the end of the core tag and points it there, and makes that info available thru cu->orig_info. In the future we can ask, when loading a cu, that this info be trown away, so that we reduce the memory footprint for big multi-cu files such as the Linux kernel. There is also a routine to ask for inserting a NULL, as we still have bugs in the CTF decoding and thus some entries are being lost, to avoid using an undefined pointer when traversing the types_table the ctf loader puts a NULL there via cu__table_nullify_type_entry() and then cu__for_each_type skips those. There is some more cleanups for leftovers that I avoided cleaning to reduce this changeset. And also while doing this I saw that enums can appear without any enumerators and that an array with DW_TAG_GNU_vector is actually a different tag, encoded this way till we get to DWARF4 ;-) So now we don't have to lookup on a hash table looking for DWARF offsets, we can do the more sensible thing of just indexing the types_tags array. Now to do some cleanups and try to get the per cu encoder done. Then order all the cus per number of type entries, pick the one with more, then go on merging/recoding the types of the others and putting the parent linkage in place. Just to show the extent of the changes: $ codiff /tmp/libdwarves.so.1.0.0 build/libdwarves.so.1.0.0 /home/acme/git/pahole/dwarves.c: struct cu | -4048 struct tag | -32 struct ptr_to_member_type | -32 struct namespace | -32 struct type | -32 struct class | -32 struct base_type | -32 struct array_type | -32 struct class_member | -32 struct lexblock | -32 struct ftype | -32 struct function | -64 struct parameter | -32 struct variable | -32 struct inline_expansion | -32 struct label | -32 struct enumerator | -32 17 structs changed tag__follow_typedef | +3 tag__fprintf_decl_info | +25 array_type__fprintf | +6 type__name | -126 type__find_first_biggest_size_base_type_member | -3 typedef__fprintf | +16 imported_declaration__fprintf | +6 imported_module__fprintf | +3 cu__new | +26 cu__delete | +26 hashtags__hash | -65 hash_64 | -124 hlist_add_head | -78 hashtags__find | -157 cu__hash | -80 cu__add_tag | +20 tag__prefix | -3 cu__find_tag_by_id | -2 cu__find_type_by_id | -3 cu__find_first_typedef_of_type | +38 cu__find_base_type_by_name | +68 cu__find_base_type_by_name_and_size | +72 cu__find_struct_by_name | +59 cus__find_struct_by_name | +8 cus__find_tag_by_id | +5 cus__find_cu_by_name | -6 lexblock__find_tag_by_id | -173 cu__find_variable_by_id | -197 list__find_tag_by_id | -308 cu__find_parameter_by_id | -60 tag__ptr_name | +6 tag__name | +15 variable__type | +13 variable__name | +7 class_member__size | +6 parameter__name | -119 tag__parameter | -14 parameter__type | -143 type__fprintf | -29 union__fprintf | +6 class__add_vtable_entry | -9 type__add_member | -6 type__clone_members | -3 enumeration__add | -6 function__name | -156 ftype__has_parm_of_type | -39 class__find_holes | -27 class__has_hole_ge | -3 type__nr_members_of_type | +3 lexblock__account_inline_expansions | +3 cu__account_inline_expansions | -18 ftype__fprintf_parms | +46 function__tag_fprintf | +24 lexblock__fprintf | -6 ftype__fprintf | +3 function__fprintf_stats | -18 function__size | -6 class__vtable_fprintf | -11 class__fprintf | -21 tag__fprintf | -35 60 functions changed, 513 bytes added, 2054 bytes removed, diff: -1541 /home/acme/git/pahole/ctf_loader.c: struct ctf_short_type | +0 14 structs changed type__init | -14 type__new | -9 class__new | -12 create_new_base_type | -7 create_new_base_type_float | -7 create_new_array | -8 create_new_subroutine_type | -9 create_full_members | -18 create_short_members | -18 create_new_class | +1 create_new_union | +1 create_new_enumeration | -19 create_new_forward_decl | -2 create_new_typedef | +3 create_new_tag | -5 load_types | +16 class__fixup_ctf_bitfields | -3 17 functions changed, 21 bytes added, 131 bytes removed, diff: -110 /home/acme/git/pahole/dwarf_loader.c: 17 structs changed zalloc | -56 tag__init | +3 array_type__new | +20 type__init | -24 class_member__new | +46 inline_expansion__new | +12 class__new | +81 lexblock__init | +19 function__new | +43 die__create_new_array | +20 die__create_new_parameter | +4 die__create_new_label | +4 die__create_new_subroutine_type | +113 die__create_new_enumeration | -21 die__process_class | +79 die__process_namespace | +76 die__create_new_inline_expansion | +4 die__process_function | +147 __die__process_tag | +34 die__process_unit | +56 die__process | +90 21 functions changed, 851 bytes added, 101 bytes removed, diff: +750 /home/acme/git/pahole/dwarves.c: struct ptr_table | +16 struct cu_orig_info | +32 2 structs changed tag__decl_line | +68 tag__decl_file | +70 tag__orig_id | +71 ptr_table__init | +46 ptr_table__exit | +37 ptr_table__add | +183 ptr_table__add_with_id | +165 ptr_table__entry | +64 cu__table_add_tag | +171 cu__table_nullify_type_entry | +38 10 functions changed, 913 bytes added, diff: +913 /home/acme/git/pahole/ctf_loader.c: 2 structs changed tag__alloc | +52 1 function changed, 52 bytes added, diff: +52 /home/acme/git/pahole/dwarf_loader.c: struct dwarf_tag | +48 struct dwarf_cu | +4104 4 structs changed dwarf_cu__init | +83 hashtags__hash | +61 hash_64 | +124 hlist_add_head | +78 hashtags__find | +161 cu__hash | +95 tag__is_tag_type | +171 tag__is_type | +85 tag__is_union | +28 tag__is_struct | +57 tag__is_typedef | +28 tag__is_enumeration | +28 dwarf_cu__find_tag_by_id | +56 dwarf_cu__find_type_by_id | +63 tag__alloc | +114 __tag__print_type_not_found | +108 namespace__recode_dwarf_types | +346 tag__namespace | +14 tag__has_namespace | +86 tag__is_namespace | +28 type__recode_dwarf_specification | +182 tag__type | +14 __tag__print_abstract_origin_not_found | +105 ftype__recode_dwarf_types | +322 tag__ftype | +14 tag__parameter | +14 lexblock__recode_dwarf_types | +736 tag__lexblock | +14 tag__label | +14 tag__recode_dwarf_type | +766 tag__ptr_to_member_type | +14 cu__recode_dwarf_types_table | +88 cu__recode_dwarf_types | +48 dwarf_tag__decl_file | +77 strings__ptr | +33 dwarf_tag__decl_line | +59 dwarf_tag__orig_id | +59 dwarf_tag__orig_type | +59 38 functions changed, 4432 bytes added, diff: +4432 build/libdwarves.so.1.0.0: 147 functions changed, 6782 bytes added, 2286 bytes removed, diff: +4496 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-03-06 00:29:35 +01:00
(!tag__decl_file(tag, cu) ||
strncmp(decl_exclude_prefix, tag__decl_file(tag, cu),
decl_exclude_prefix_len) == 0))
return NULL;
2020-01-15 15:35:25 +01:00
/*
* if --unions was used and we got here, its a union and we satisfy the other
* filters/options, so don't filter it.
*/
if (just_unions)
return class;
/*
* The following only make sense for structs, i.e. 'struct class',
* and as we can get here with a union, that is represented by a 'struct type',
* bail out if we get here with an union and we are not looking for things
* that need finding holes, like --packable, --nr_holes, etc
*/
if (!tag__is_struct(tag))
return (just_structs || show_packable || nr_holes || nr_bit_holes || hole_size_ge) ? NULL : class;
if (tag->top_level)
class__find_holes(class);
if (class->nr_holes < nr_holes ||
[LIB]: Introduce class__has_hole_ge() That returns if the class has a hole greater or equal to the size specified. Pahole now has a --hole_size_ge command line option to use it. Example on a linux kernel built for x86_64 where we list the structs that have holes bigger than 32 bytes, that provides an approximation of structs with ____cacheline_aligned_in_smp annotated members: [acme@filo pahole]$ pahole --hole_size_ge 32 examples/vmlinux-x86_64 inet_hashinfo rcu_ctrlblk hh_cache net_device files_struct module zone For instance, look at struct zone clever use of such construct: _pad1_ is defined with ZONE_PADDING(_pad1_), that is: /* <40e> /home/acme/git/net-2.6.22/include/linux/mmzone.h:179 */ struct zone { long unsigned int pages_min; /* 0 8 */ long unsigned int pages_low; /* 8 8 */ long unsigned int pages_high; /* 16 8 */ long unsigned int lowmem_reserve[3]; /* 24 24 */ int node; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int min_unmapped_pages; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int min_slab_pages; /* 64 8 */ struct per_cpu_pageset * pageset[255]; /* 72 2040 */ /* --- cacheline 33 boundary (2112 bytes) --- */ spinlock_t lock; /* 2112 4 */ /* XXX 4 bytes hole, try to pack */ struct free_area free_area[11]; /* 2120 264 */ /* XXX 48 bytes hole, try to pack */ /* --- cacheline 38 boundary (2432 bytes) --- */ struct zone_padding _pad1_; /* 2432 0 */ spinlock_t lru_lock; /* 2432 4 */ /* XXX 4 bytes hole, try to pack */ struct list_head active_list; /* 2440 16 */ struct list_head inactive_list; /* 2456 16 */ long unsigned int nr_scan_active; /* 2472 8 */ long unsigned int nr_scan_inactive; /* 2480 8 */ long unsigned int pages_scanned; /* 2488 8 */ /* --- cacheline 39 boundary (2496 bytes) --- */ int all_unreclaimable; /* 2496 4 */ atomic_t reclaim_in_progress; /* 2500 4 */ atomic_long_t vm_stat[20]; /* 2504 160 */ /* --- cacheline 41 boundary (2624 bytes) was 40 bytes ago --- */ int prev_priority; /* 2664 4 */ /* XXX 20 bytes hole, try to pack */ /* --- cacheline 42 boundary (2688 bytes) --- */ struct zone_padding _pad2_; /* 2688 0 */ wait_queue_head_t * wait_table; /* 2688 8 */ long unsigned int wait_table_hash_nr_entries; /* 2696 8 */ long unsigned int wait_table_bits; /* 2704 8 */ struct pglist_data * zone_pgdat; /* 2712 8 */ long unsigned int zone_start_pfn; /* 2720 8 */ long unsigned int spanned_pages; /* 2728 8 */ long unsigned int present_pages; /* 2736 8 */ const char * name; /* 2744 8 */ /* --- cacheline 43 boundary (2752 bytes) --- */ }; /* size: 2752, cachelines: 43 */ /* sum members: 2672, holes: 5, sum holes: 80 */ /* definitions: 933 */ Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 18:32:53 +02:00
class->nr_bit_holes < nr_bit_holes ||
(hole_size_ge != 0 && !class__has_hole_ge(class, hole_size_ge)))
return NULL;
if (show_packable && !class__packable(class, cu))
return NULL;
return class;
}
static void union__find_new_size(struct tag *tag, struct cu *cu);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
static void class__resize_LP(struct tag *tag, struct cu *cu)
{
struct tag *tag_pos;
struct class *class = tag__class(tag);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
size_t word_size_diff;
size_t orig_size = class->type.size;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
if (tag__type(tag)->resized)
return;
tag__type(tag)->resized = 1;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
if (original_word_size > word_size)
word_size_diff = original_word_size - word_size;
else
word_size_diff = word_size - original_word_size;
type__for_each_tag(tag__type(tag), tag_pos) {
struct tag *type;
size_t diff = 0;
size_t array_multiplier = 1;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
/* we want only data members, i.e. with byte_offset attr */
if (tag_pos->tag != DW_TAG_member &&
tag_pos->tag != DW_TAG_inheritance)
continue;
type = cu__type(cu, tag_pos->type);
tag__assert_search_result(type);
if (type->tag == DW_TAG_array_type) {
int i;
for (i = 0; i < tag__array_type(type)->dimensions; ++i)
array_multiplier *= tag__array_type(type)->nr_entries[i];
type = cu__type(cu, type->type);
tag__assert_search_result(type);
}
if (tag__is_typedef(type)) {
type = tag__follow_typedef(type, cu);
tag__assert_search_result(type);
}
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
switch (type->tag) {
case DW_TAG_base_type: {
struct base_type *bt = tag__base_type(type);
char bf[64];
const char *name = base_type__name(bt, cu, bf,
sizeof(bf));
if (strcmp(name, "long int") != 0 &&
strcmp(name, "long unsigned int") != 0)
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
break;
/* fallthru */
}
case DW_TAG_pointer_type:
diff = word_size_diff;
break;
case DW_TAG_structure_type:
case DW_TAG_union_type:
if (tag__is_union(type))
union__find_new_size(type, cu);
else
class__resize_LP(type, cu);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
diff = tag__type(type)->size_diff;
break;
}
diff *= array_multiplier;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
if (diff != 0) {
struct class_member *m = tag__class_member(tag_pos);
if (original_word_size > word_size) {
class->type.size -= diff;
class__subtract_offsets_from(class, m, diff);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
} else {
class->type.size += diff;
class__add_offsets_from(class, m, diff);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
}
}
}
if (original_word_size > word_size)
tag__type(tag)->size_diff = orig_size - class->type.size;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
else
tag__type(tag)->size_diff = class->type.size - orig_size;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
class__find_holes(class);
class__fixup_alignment(class, cu);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
}
static void union__find_new_size(struct tag *tag, struct cu *cu)
{
struct tag *tag_pos;
struct type *type = tag__type(tag);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
size_t max_size = 0;
if (type->resized)
return;
type->resized = 1;
type__for_each_tag(type, tag_pos) {
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
struct tag *type;
size_t size;
/* we want only data members, i.e. with byte_offset attr */
if (tag_pos->tag != DW_TAG_member &&
tag_pos->tag != DW_TAG_inheritance)
continue;
type = cu__type(cu, tag_pos->type);
tag__assert_search_result(type);
if (tag__is_typedef(type))
type = tag__follow_typedef(type, cu);
if (tag__is_union(type))
union__find_new_size(type, cu);
else if (tag__is_struct(type))
class__resize_LP(type, cu);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
size = tag__size(type, cu);
if (size > max_size)
max_size = size;
}
if (max_size > type->size)
type->size_diff = max_size - type->size;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
else
type->size_diff = type->size - max_size;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
type->size = max_size;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
}
static void tag__fixup_word_size(struct tag *tag, struct cu *cu)
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
{
if (tag__is_struct(tag) || tag__is_union(tag)) {
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
struct tag *pos;
namespace__for_each_tag(tag__namespace(tag), pos)
tag__fixup_word_size(pos, cu);
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
}
switch (tag->tag) {
case DW_TAG_base_type: {
struct base_type *bt = tag__base_type(tag);
/*
* This shouldn't happen, but at least on a tcp_ipv6.c
* built with GNU C 4.3.0 20080130 (Red Hat 4.3.0-0.7),
* one was found, so just bail out.
*/
if (!bt->name)
return;
char bf[64];
const char *name = base_type__name(bt, cu, bf, sizeof(bf));
if (strcmp(name, "long int") == 0 ||
strcmp(name, "long unsigned int") == 0)
bt->bit_size = word_size * 8;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
}
break;
case DW_TAG_structure_type:
class__resize_LP(tag, cu);
break;
case DW_TAG_union_type:
union__find_new_size(tag, cu);
break;
}
return;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
}
static void cu_fixup_word_size_iterator(struct cu *cu)
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
{
original_word_size = cu->addr_size;
cu->addr_size = word_size;
uint32_t id;
struct tag *pos;
cu__for_each_type(cu, id, pos)
tag__fixup_word_size(pos, cu);
}
static void cu__account_nr_methods(struct cu *cu)
{
struct function *pos_function;
struct structure *str;
uint32_t id;
cu__for_each_function(cu, id, pos_function) {
struct class_member *pos;
function__for_each_parameter(pos_function, cu, pos) {
struct tag *type = cu__type(cu, pos->tag.type);
if (type == NULL || !tag__is_pointer(type))
continue;
type = cu__type(cu, type->type);
if (type == NULL || !tag__is_struct(type))
continue;
struct type *ctype = tag__type(type);
if (ctype->namespace.name == 0)
continue;
struct class *class = tag__class(type);
if (!class__filter(class, cu, 0))
continue;
bool existing_entry;
str = structures__add(class, cu, &existing_entry);
if (str == NULL) {
fprintf(stderr, "pahole: insufficient memory "
"for processing %s, skipping it...\n",
cu->name);
return;
}
if (!existing_entry)
class__find_holes(class);
++str->nr_methods;
}
}
}
[PAHOLE]: Introduce --recursive For now only affects the --contains output. Example showing the structs that include struct list_head in a linux kernel module: [acme@filo pahole]$ pahole --recursive --contains list_head examples/ipv6.ko.debug.x86-64 inet_protosw proto sock_iocb key_type msg_queue msg_msg nf_hook_ops softnet_data net_device softnet_data dma_device dma_client dma_chan class_device net_device softnet_data dma_chan class klist_node device_driver device klist device_driver bus_type device file_system_type nfs_lock_info file_lock block_device address_space inode dquot mem_dqinfo super_block inode signal_struct page kioctx file kiocb work_struct delayed_work kioctx timer_list ifmcaddr6 inet6_dev inet6_ifaddr neigh_table neighbour net_device softnet_data sock inet_sock delayed_work kioctx plist_head task_struct sigpending signal_struct task_struct user_struct device dev_pm_info device mutex_waiter mutex seq_file block_device quota_info super_block dquot super_block inode zone per_cpu_pages free_area kset bus_type subsystem class bus_type __wait_queue_head __wait_queue rw_semaphore quota_info super_block super_block inode key blocking_notifier_head bus_type subsystem class bus_type mm_struct dentry vm_area_struct kobject class_device net_device softnet_data dma_chan device_driver module_kobject module device kset bus_type subsystem class bus_type lock_class module mm_struct task_struct Handling in multi-cu objects is not very precise, as the same struct has different dwarf offsets (id) in each CU. A mitigation for this problem will be provided with the --cu_list and --cu_name upcoming options, where one will be able to get a list of the object files in a, for instance, linux kernel .ko module and also to specify a cu name to be the only to be considered when processing multi-cu files (again, such as .ko linux kernel modules). This ends up being also useful to generate a reverse class hierarchy :-) Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 00:11:51 +02:00
static char tab[128];
static void print_structs_with_pointer_to(struct cu *cu, uint32_t type)
{
struct class *pos;
struct class_member *pos_member;
uint32_t id;
cu__for_each_struct_or_union(cu, id, pos) {
bool looked = false;
/*
* Set it to NULL just to silence the compiler, as the printf
* at the end of the type__for_each_member() loop is only reached
* after str _is_ set, as looked starts as false, str is used with
* structures_add and if it is NULL, we return.
*/
struct structure *str = NULL;
if (pos->type.namespace.name == 0)
continue;
if (!class__filter(pos, cu, id))
continue;
type__for_each_member(&pos->type, pos_member) {
struct tag *ctype = cu__type(cu, pos_member->tag.type);
tag__assert_search_result(ctype);
if (!tag__is_pointer_to(ctype, type))
continue;
if (!looked) {
bool existing_entry;
str = structures__add(pos, cu, &existing_entry);
if (str == NULL) {
fprintf(stderr, "pahole: insufficient memory for "
"processing %s, skipping it...\n",
cu->name);
return;
}
/*
* We already printed this struct in another CU
*/
if (existing_entry)
break;
looked = true;
}
printf("%s: %s\n", str->name,
class_member__name(pos_member, cu));
}
}
}
static int type__print_containers(struct type *type, struct cu *cu, uint32_t contained_type_id, int ident)
{
const uint32_t n = type__nr_members_of_type(type, contained_type_id);
if (n == 0)
return 0;
if (ident == 0) {
bool existing_entry;
struct structure *str = structures__add(type__class(type), cu, &existing_entry);
if (str == NULL) {
fprintf(stderr, "pahole: insufficient memory for "
"processing %s, skipping it...\n",
cu->name);
return -1;
}
/*
* We already printed this struct in another CU
*/
if (existing_entry)
return 0;
}
printf("%.*s%s", ident * 2, tab, type__name(type, cu));
if (global_verbose)
printf(": %u", n);
putchar('\n');
if (recursive) {
struct class_member *member;
type__for_each_member(type, member) {
struct tag *member_type = cu__type(cu, member->tag.type);
if (tag__is_struct(member_type) || tag__is_union(member_type))
type__print_containers(tag__type(member_type), cu, contained_type_id, ident + 1);
}
}
return 0;
}
static void print_containers(struct cu *cu, uint32_t type, int ident)
{
struct class *pos;
uint32_t id;
cu__for_each_struct_or_union(cu, id, pos) {
if (pos->type.namespace.name == 0)
continue;
if (!class__filter(pos, cu, id))
continue;
if (type__print_containers(&pos->type, cu, type, ident))
break;
}
}
/* Name and version of program. */
ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
#define ARGP_flat_arrays 300
#define ARGP_show_private_classes 301
#define ARGP_fixup_silly_bitfields 302
#define ARGP_first_obj_only 303
#define ARGP_classes_as_structs 304
#define ARGP_hex_fmt 305
#define ARGP_suppress_aligned_attribute 306
#define ARGP_suppress_force_paddings 307
#define ARGP_suppress_packed 308
2020-01-15 15:35:25 +01:00
#define ARGP_just_unions 309
#define ARGP_just_structs 310
#define ARGP_count 311
#define ARGP_skip 312
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 14:17:38 +02:00
#define ARGP_seek_bytes 313
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-07-20 13:25:45 +02:00
#define ARGP_header_type 314
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-07-20 14:27:10 +02:00
#define ARGP_size_bytes 315
#define ARGP_range 316
#define ARGP_skip_encoding_btf_vars 317
#define ARGP_btf_encode_force 318
pahole: Implement --packed To show just packed structs. For instance, here are the top packed structures in the Linux kernel, using BTF data: $ pahole --packed --sizes | sort -k2 -nr | head e820_table 64004 0 boot_params 4096 0 btrfs_super_block 3531 0 efi_variable 2084 0 ntb_info_regs 800 0 tboot 568 0 _legacy_mbr 512 0 disklabel 512 0 btrfs_root_item 439 0 saved_context 317 0 $ If you then look at: $ pahole e820_table struct e820_table { __u32 nr_entries; /* 0 4 */ struct e820_entry entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * The whole array of E820 entries: */ struct e820_table { __u32 nr_entries; struct e820_entry entries[E820_MAX_ENTRIES]; }; I.e. no explicit __packed__ attributes, but if we expand this a bit: $ pahole -E e820_table struct e820_table { /* typedef __u32 */ unsigned int nr_entries; /* 0 4 */ struct e820_entry { /* typedef u64 -> __u64 */ long long unsigned int addr; /* 4 8 */ /* typedef u64 -> __u64 */ long long unsigned int size; /* 12 8 */ enum e820_type type; /* 20 4 */ } __attribute__((__packed__)) entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ We see that is that entries member that is packed, because: $ pahole e820_entry struct e820_entry { u64 addr; /* 0 8 */ u64 size; /* 8 8 */ enum e820_type type; /* 16 4 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * A single E820 map entry, describing a memory range of [addr...addr+size-1], * of 'type' memory type: * * (We pack it because there can be thousands of them on large systems.) */ struct e820_entry { u64 addr; u64 size; enum e820_type type; } __attribute__((packed)); So yeah, it is there, BTF doesn't explicitly states it is packed (as DWARF does) and pahole was able to infer that correctly. Tested-by: Richard Weinberger <richard@nod.at> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-08 14:27:42 +02:00
#define ARGP_just_packed_structs 319
#define ARGP_numeric_version 320
#define ARGP_btf_base 321
#define ARGP_btf_gen_floats 322
#define ARGP_btf_gen_all 323
static const struct argp_option pahole__options[] = {
{
.name = "bit_holes",
.key = 'B',
.arg = "NR_HOLES",
.doc = "Show only structs at least NR_HOLES bit holes"
},
{
.name = "cacheline_size",
.key = 'c',
.arg = "SIZE",
.doc = "set cacheline size to SIZE"
},
{
.name = "class_name",
.key = 'C',
.arg = "CLASS_NAME",
.doc = "Show just this class"
},
{
.name = "count",
.key = ARGP_count,
.arg = "COUNT",
.doc = "Print only COUNT input records"
},
{
.name = "skip",
.key = ARGP_skip,
.arg = "COUNT",
.doc = "Skip COUNT input records"
},
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 14:17:38 +02:00
{
.name = "seek_bytes",
.key = ARGP_seek_bytes,
.arg = "BYTES",
.doc = "Seek COUNT input records"
},
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-07-20 14:27:10 +02:00
{
.name = "size_bytes",
.key = ARGP_size_bytes,
.arg = "BYTES",
.doc = "Read only this number of bytes from this point onwards"
},
{
.name = "range",
.key = ARGP_range,
.arg = "STRUCT",
.doc = "Data struct with 'offset' and 'size' fields to determine --seek_bytes and --size_bytes"
},
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-07-20 13:25:45 +02:00
{
.name = "header_type",
.key = ARGP_header_type,
.arg = "TYPE",
.doc = "File header type"
},
{
.name = "find_pointers_to",
.key = 'f',
.arg = "CLASS_NAME",
.doc = "Find pointers to CLASS_NAME"
},
{
.name = "format_path",
.key = 'F',
.arg = "FORMAT_LIST",
.doc = "List of debugging formats to try"
},
{
.name = "contains",
.key = 'i',
.arg = "CLASS_NAME",
.doc = "Show classes that contains CLASS_NAME"
},
{
.name = "show_decl_info",
.key = 'I',
.doc = "Show the file and line number where the tags were defined"
},
{
.name = "holes",
.key = 'H',
.arg = "NR_HOLES",
[LIB]: Introduce class__has_hole_ge() That returns if the class has a hole greater or equal to the size specified. Pahole now has a --hole_size_ge command line option to use it. Example on a linux kernel built for x86_64 where we list the structs that have holes bigger than 32 bytes, that provides an approximation of structs with ____cacheline_aligned_in_smp annotated members: [acme@filo pahole]$ pahole --hole_size_ge 32 examples/vmlinux-x86_64 inet_hashinfo rcu_ctrlblk hh_cache net_device files_struct module zone For instance, look at struct zone clever use of such construct: _pad1_ is defined with ZONE_PADDING(_pad1_), that is: /* <40e> /home/acme/git/net-2.6.22/include/linux/mmzone.h:179 */ struct zone { long unsigned int pages_min; /* 0 8 */ long unsigned int pages_low; /* 8 8 */ long unsigned int pages_high; /* 16 8 */ long unsigned int lowmem_reserve[3]; /* 24 24 */ int node; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int min_unmapped_pages; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int min_slab_pages; /* 64 8 */ struct per_cpu_pageset * pageset[255]; /* 72 2040 */ /* --- cacheline 33 boundary (2112 bytes) --- */ spinlock_t lock; /* 2112 4 */ /* XXX 4 bytes hole, try to pack */ struct free_area free_area[11]; /* 2120 264 */ /* XXX 48 bytes hole, try to pack */ /* --- cacheline 38 boundary (2432 bytes) --- */ struct zone_padding _pad1_; /* 2432 0 */ spinlock_t lru_lock; /* 2432 4 */ /* XXX 4 bytes hole, try to pack */ struct list_head active_list; /* 2440 16 */ struct list_head inactive_list; /* 2456 16 */ long unsigned int nr_scan_active; /* 2472 8 */ long unsigned int nr_scan_inactive; /* 2480 8 */ long unsigned int pages_scanned; /* 2488 8 */ /* --- cacheline 39 boundary (2496 bytes) --- */ int all_unreclaimable; /* 2496 4 */ atomic_t reclaim_in_progress; /* 2500 4 */ atomic_long_t vm_stat[20]; /* 2504 160 */ /* --- cacheline 41 boundary (2624 bytes) was 40 bytes ago --- */ int prev_priority; /* 2664 4 */ /* XXX 20 bytes hole, try to pack */ /* --- cacheline 42 boundary (2688 bytes) --- */ struct zone_padding _pad2_; /* 2688 0 */ wait_queue_head_t * wait_table; /* 2688 8 */ long unsigned int wait_table_hash_nr_entries; /* 2696 8 */ long unsigned int wait_table_bits; /* 2704 8 */ struct pglist_data * zone_pgdat; /* 2712 8 */ long unsigned int zone_start_pfn; /* 2720 8 */ long unsigned int spanned_pages; /* 2728 8 */ long unsigned int present_pages; /* 2736 8 */ const char * name; /* 2744 8 */ /* --- cacheline 43 boundary (2752 bytes) --- */ }; /* size: 2752, cachelines: 43 */ /* sum members: 2672, holes: 5, sum holes: 80 */ /* definitions: 933 */ Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 18:32:53 +02:00
.doc = "show only structs with at least NR_HOLES holes",
},
{
.name = "hole_size_ge",
.key = 'z',
.arg = "HOLE_SIZE",
.doc = "show only structs with at least one hole greater "
"or equal to HOLE_SIZE",
},
{
.name = "packable",
[PAHOLE]: Use cus__loadfl, i.e. libdwfl Now we have: [acme@filo pahole]$ pahole --help Usage: pahole [OPTION...] [FILE] {[CLASS]} -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE -D, --decl_exclude=PREFIX exclude classes declared in files with PREFIX -E, --expand_types expand class members -H, --holes=NR_HOLES show only structs at least NR_HOLES holes -m, --nr_methods show number of methods -n, --nr_members show number of members -N, --class_name_len show size of classes -P, --packable show only structs that has holes that can be packed -R, --reorganize reorg struct trying to kill holes -s, --sizes show size of classes -S, --show_reorg_steps show the struct layout at each reorganization step -t, --nr_definitions show how many times struct was defined -V, --verbose be verbose -x, --exclude=PREFIX exclude PREFIXed classes -X, --cu_exclude=PREFIX exclude PREFIXed compilation units Input Selection: --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID -?, --help Give this help list --usage Give a short usage message Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-03-30 18:25:51 +02:00
.key = 'P',
.doc = "show only structs that has holes that can be packed",
},
{
.name = "expand_types",
[PAHOLE]: Use cus__loadfl, i.e. libdwfl Now we have: [acme@filo pahole]$ pahole --help Usage: pahole [OPTION...] [FILE] {[CLASS]} -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE -D, --decl_exclude=PREFIX exclude classes declared in files with PREFIX -E, --expand_types expand class members -H, --holes=NR_HOLES show only structs at least NR_HOLES holes -m, --nr_methods show number of methods -n, --nr_members show number of members -N, --class_name_len show size of classes -P, --packable show only structs that has holes that can be packed -R, --reorganize reorg struct trying to kill holes -s, --sizes show size of classes -S, --show_reorg_steps show the struct layout at each reorganization step -t, --nr_definitions show how many times struct was defined -V, --verbose be verbose -x, --exclude=PREFIX exclude PREFIXed classes -X, --cu_exclude=PREFIX exclude PREFIXed compilation units Input Selection: --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID -?, --help Give this help list --usage Give a short usage message Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-03-30 18:25:51 +02:00
.key = 'E',
.doc = "expand class members",
},
{
.name = "nr_members",
.key = 'n',
.doc = "show number of members",
},
{
.name = "rel_offset",
.key = 'r',
.doc = "show relative offsets of members in inner structs"
},
[PAHOLE]: Introduce --recursive For now only affects the --contains output. Example showing the structs that include struct list_head in a linux kernel module: [acme@filo pahole]$ pahole --recursive --contains list_head examples/ipv6.ko.debug.x86-64 inet_protosw proto sock_iocb key_type msg_queue msg_msg nf_hook_ops softnet_data net_device softnet_data dma_device dma_client dma_chan class_device net_device softnet_data dma_chan class klist_node device_driver device klist device_driver bus_type device file_system_type nfs_lock_info file_lock block_device address_space inode dquot mem_dqinfo super_block inode signal_struct page kioctx file kiocb work_struct delayed_work kioctx timer_list ifmcaddr6 inet6_dev inet6_ifaddr neigh_table neighbour net_device softnet_data sock inet_sock delayed_work kioctx plist_head task_struct sigpending signal_struct task_struct user_struct device dev_pm_info device mutex_waiter mutex seq_file block_device quota_info super_block dquot super_block inode zone per_cpu_pages free_area kset bus_type subsystem class bus_type __wait_queue_head __wait_queue rw_semaphore quota_info super_block super_block inode key blocking_notifier_head bus_type subsystem class bus_type mm_struct dentry vm_area_struct kobject class_device net_device softnet_data dma_chan device_driver module_kobject module device kset bus_type subsystem class bus_type lock_class module mm_struct task_struct Handling in multi-cu objects is not very precise, as the same struct has different dwarf offsets (id) in each CU. A mitigation for this problem will be provided with the --cu_list and --cu_name upcoming options, where one will be able to get a list of the object files in a, for instance, linux kernel .ko module and also to specify a cu name to be the only to be considered when processing multi-cu files (again, such as .ko linux kernel modules). This ends up being also useful to generate a reverse class hierarchy :-) Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 00:11:51 +02:00
{
.name = "recursive",
.key = 'd',
.doc = "recursive mode, affects several other flags",
},
{
.name = "reorganize",
[PAHOLE]: Use cus__loadfl, i.e. libdwfl Now we have: [acme@filo pahole]$ pahole --help Usage: pahole [OPTION...] [FILE] {[CLASS]} -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE -D, --decl_exclude=PREFIX exclude classes declared in files with PREFIX -E, --expand_types expand class members -H, --holes=NR_HOLES show only structs at least NR_HOLES holes -m, --nr_methods show number of methods -n, --nr_members show number of members -N, --class_name_len show size of classes -P, --packable show only structs that has holes that can be packed -R, --reorganize reorg struct trying to kill holes -s, --sizes show size of classes -S, --show_reorg_steps show the struct layout at each reorganization step -t, --nr_definitions show how many times struct was defined -V, --verbose be verbose -x, --exclude=PREFIX exclude PREFIXed classes -X, --cu_exclude=PREFIX exclude PREFIXed compilation units Input Selection: --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID -?, --help Give this help list --usage Give a short usage message Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-03-30 18:25:51 +02:00
.key = 'R',
.doc = "reorg struct trying to kill holes",
},
{
.name = "show_reorg_steps",
.key = 'S',
.doc = "show the struct layout at each reorganization step",
},
{
.name = "class_name_len",
.key = 'N',
.doc = "show size of classes",
},
{
.name = "show_first_biggest_size_base_type_member",
.key = 'l',
.doc = "show first biggest size base_type member",
},
{
.name = "nr_methods",
.key = 'm',
.doc = "show number of methods",
},
{
.name = "show_only_data_members",
.key = 'M',
.doc = "show only the members that use space in the class layout",
},
{
.name = "expand_pointers",
.key = 'p',
.doc = "expand class pointer members",
},
{
.name = "sizes",
.key = 's',
.doc = "show size of classes",
},
{
.name = "separator",
.key = 't',
.arg = "SEP",
.doc = "use SEP as the field separator",
},
{
.name = "nr_definitions",
.key = 'T',
.doc = "show how many times struct was defined",
},
{
.name = "decl_exclude",
.key = 'D',
.arg = "PREFIX",
.doc = "exclude classes declared in files with PREFIX",
},
{
.name = "exclude",
.key = 'x',
.arg = "PREFIX",
.doc = "exclude PREFIXed classes",
},
{
.name = "prefix_filter",
.key = 'y',
.arg = "PREFIX",
.doc = "include PREFIXed classes",
},
{
.name = "cu_exclude",
.key = 'X',
.arg = "PREFIX",
.doc = "exclude PREFIXed compilation units",
},
{
.name = "anon_include",
.key = 'a',
.doc = "include anonymous classes",
},
{
.name = "nested_anon_include",
.key = 'A',
.doc = "include nested (inside other structs) anonymous classes",
},
{
.name = "quiet",
.key = 'q',
.doc = "be quieter",
},
{
.name = "defined_in",
.key = 'u',
.doc = "show CUs where CLASS_NAME (-C) is defined",
},
{
.name = "verbose",
.key = 'V',
.doc = "be verbose",
},
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
{
.name = "word_size",
.key = 'w',
.arg = "WORD_SIZE",
.doc = "change the arch word size to WORD_SIZE"
},
{
.name = "ctf_encode",
.key = 'Z',
.doc = "Encode as CTF",
},
{
.name = "flat_arrays",
.key = ARGP_flat_arrays,
.doc = "Flat arrays",
},
{
.name = "suppress_aligned_attribute",
.key = ARGP_suppress_aligned_attribute,
.doc = "Suppress __attribute__((aligned(N))",
},
{
.name = "suppress_force_paddings",
.key = ARGP_suppress_force_paddings,
.doc = "Suppress int :N paddings at the end",
},
{
.name = "suppress_packed",
.key = ARGP_suppress_packed,
.doc = "Suppress output of inferred __attribute__((__packed__))",
},
{
.name = "show_private_classes",
.key = ARGP_show_private_classes,
.doc = "Show classes that are defined inside other classes or in functions",
},
{
.name = "fixup_silly_bitfields",
.key = ARGP_fixup_silly_bitfields,
.doc = "Fix silly bitfields such as int foo:32",
},
{
.name = "first_obj_only",
.key = ARGP_first_obj_only,
.doc = "Only process the first object file in the binary",
},
{
.name = "classes_as_structs",
.key = ARGP_classes_as_structs,
.doc = "Use 'struct' when printing classes",
},
{
.name = "hex",
.key = ARGP_hex_fmt,
.doc = "Print offsets and sizes in hexadecimal",
},
{
.name = "btf_base",
.key = ARGP_btf_base,
.arg = "PATH",
.doc = "Path to the base BTF file",
},
{
.name = "btf_encode",
.key = 'J',
.doc = "Encode as BTF",
},
{
.name = "skip_encoding_btf_vars",
.key = ARGP_skip_encoding_btf_vars,
.doc = "Do not encode VARs in BTF."
},
btf_encoder: Teach pahole to store percpu variables in vmlinux BTF. On SMP systems, the global percpu variables are placed in a special '.data..percpu' section, which is stored in a segment whose initial address is set to 0, the addresses of per-CPU variables are relative positive addresses [1]. This patch extracts these variables from vmlinux and places them with their type information in BTF. More specifically, when BTF is encoded, we find the index of the '.data..percpu' section and then traverse the symbol table to find those global objects which are in this section. For each of these objects, we push a BTF_KIND_VAR into the types buffer, and a BTF_VAR_SECINFO into another buffer, percpu_secinfo. When all the CUs have finished processing, we push a BTF_KIND_DATASEC into the btfe->types buffer, followed by the percpu_secinfo's content. In a v5.8-rc3 linux kernel, I was able to extract 288 such variables. The build time overhead is small and the space overhead is also small. See testings below. A found variable can be invalid in two ways: - Its name found in elf_sym__name is invalid. - Its size identified by elf_sym__size is 0. In either case, the BTF containing such symbols will be rejected by the BTF verifier. Normally we should not see such symbols. But if one is seen during BTF encoding, the encoder will exit with error. An new flag '-j' (or '--force') is implemented to help testing, which skips the invalid symbols and force emit a BTF. Testing: - vmlinux size has increased by ~12kb. Before: $ readelf -SW vmlinux | grep BTF [25] .BTF PROGBITS ffffffff821a905c 13a905c 2d2bf8 00 After: $ pahole -J vmlinux $ readelf -SW vmlinux | grep BTF [25] .BTF PROGBITS ffffffff821a905c 13a905c 2d5bca 00 - Common global percpu VARs and DATASEC are found in BTF section. $ bpftool btf dump file vmlinux | grep runqueues [14152] VAR 'runqueues' type_id=13778, linkage=global-alloc $ bpftool btf dump file vmlinux | grep 'cpu_stopper' [17582] STRUCT 'cpu_stopper' size=72 vlen=5 [17601] VAR 'cpu_stopper' type_id=17582, linkage=static $ bpftool btf dump file vmlinux | grep ' DATASEC ' [63652] DATASEC '.data..percpu' size=179288 vlen=288 - Tested bpf selftests. - pahole exits with error if an invalid symbol is seen during encoding, make -f Makefile -j 36 -s PAHOLE: Error: Found symbol of zero size when encoding btf (sym: 'yyy', cu: 'xxx.c'). PAHOLE: Error: Use '-j' or '--force_emit' to ignore such symbols and force emit the btf. scripts/link-vmlinux.sh: line 137: 2475712 Segmentation fault LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} - With the flag '-j' or '--force', the invalid symbols are ignored. - Further in verbose mode and with '-j' or '--force' set, a warning is generated: PAHOLE: Warning: Found symbol of zero size when encoding btf, ignored (sym: 'yyy', cu: 'xxx.c'). PAHOLE: Warning: Found symbol of invalid name when encoding btf, ignored (sym: 'zzz', cu: 'sss.c'). References: [1] https://lwn.net/Articles/531148/ Signed-off-by: Hao Luo <haoluo@google.com> Tested-by: Andrii Nakryiko <andriin@fb.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Andrii Nakryiko <andriin@fb.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: Oleg Rombakh <olegrom@google.com> Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-08 22:44:10 +02:00
{
.name = "btf_encode_force",
.key = ARGP_btf_encode_force,
btf_encoder: Teach pahole to store percpu variables in vmlinux BTF. On SMP systems, the global percpu variables are placed in a special '.data..percpu' section, which is stored in a segment whose initial address is set to 0, the addresses of per-CPU variables are relative positive addresses [1]. This patch extracts these variables from vmlinux and places them with their type information in BTF. More specifically, when BTF is encoded, we find the index of the '.data..percpu' section and then traverse the symbol table to find those global objects which are in this section. For each of these objects, we push a BTF_KIND_VAR into the types buffer, and a BTF_VAR_SECINFO into another buffer, percpu_secinfo. When all the CUs have finished processing, we push a BTF_KIND_DATASEC into the btfe->types buffer, followed by the percpu_secinfo's content. In a v5.8-rc3 linux kernel, I was able to extract 288 such variables. The build time overhead is small and the space overhead is also small. See testings below. A found variable can be invalid in two ways: - Its name found in elf_sym__name is invalid. - Its size identified by elf_sym__size is 0. In either case, the BTF containing such symbols will be rejected by the BTF verifier. Normally we should not see such symbols. But if one is seen during BTF encoding, the encoder will exit with error. An new flag '-j' (or '--force') is implemented to help testing, which skips the invalid symbols and force emit a BTF. Testing: - vmlinux size has increased by ~12kb. Before: $ readelf -SW vmlinux | grep BTF [25] .BTF PROGBITS ffffffff821a905c 13a905c 2d2bf8 00 After: $ pahole -J vmlinux $ readelf -SW vmlinux | grep BTF [25] .BTF PROGBITS ffffffff821a905c 13a905c 2d5bca 00 - Common global percpu VARs and DATASEC are found in BTF section. $ bpftool btf dump file vmlinux | grep runqueues [14152] VAR 'runqueues' type_id=13778, linkage=global-alloc $ bpftool btf dump file vmlinux | grep 'cpu_stopper' [17582] STRUCT 'cpu_stopper' size=72 vlen=5 [17601] VAR 'cpu_stopper' type_id=17582, linkage=static $ bpftool btf dump file vmlinux | grep ' DATASEC ' [63652] DATASEC '.data..percpu' size=179288 vlen=288 - Tested bpf selftests. - pahole exits with error if an invalid symbol is seen during encoding, make -f Makefile -j 36 -s PAHOLE: Error: Found symbol of zero size when encoding btf (sym: 'yyy', cu: 'xxx.c'). PAHOLE: Error: Use '-j' or '--force_emit' to ignore such symbols and force emit the btf. scripts/link-vmlinux.sh: line 137: 2475712 Segmentation fault LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} - With the flag '-j' or '--force', the invalid symbols are ignored. - Further in verbose mode and with '-j' or '--force' set, a warning is generated: PAHOLE: Warning: Found symbol of zero size when encoding btf, ignored (sym: 'yyy', cu: 'xxx.c'). PAHOLE: Warning: Found symbol of invalid name when encoding btf, ignored (sym: 'zzz', cu: 'sss.c'). References: [1] https://lwn.net/Articles/531148/ Signed-off-by: Hao Luo <haoluo@google.com> Tested-by: Andrii Nakryiko <andriin@fb.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Andrii Nakryiko <andriin@fb.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: Oleg Rombakh <olegrom@google.com> Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-08 22:44:10 +02:00
.doc = "Ignore those symbols found invalid when encoding BTF."
},
{
.name = "btf_gen_floats",
.key = ARGP_btf_gen_floats,
.doc = "Allow producing BTF_KIND_FLOAT entries."
},
{
.name = "btf_gen_all",
.key = ARGP_btf_gen_all,
.doc = "Allow using all the BTF features supported by pahole."
},
{
.name = "structs",
.key = ARGP_just_structs,
.doc = "Show just structs",
},
2020-01-15 15:35:25 +01:00
{
.name = "unions",
.key = ARGP_just_unions,
.doc = "Show just unions",
},
pahole: Implement --packed To show just packed structs. For instance, here are the top packed structures in the Linux kernel, using BTF data: $ pahole --packed --sizes | sort -k2 -nr | head e820_table 64004 0 boot_params 4096 0 btrfs_super_block 3531 0 efi_variable 2084 0 ntb_info_regs 800 0 tboot 568 0 _legacy_mbr 512 0 disklabel 512 0 btrfs_root_item 439 0 saved_context 317 0 $ If you then look at: $ pahole e820_table struct e820_table { __u32 nr_entries; /* 0 4 */ struct e820_entry entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * The whole array of E820 entries: */ struct e820_table { __u32 nr_entries; struct e820_entry entries[E820_MAX_ENTRIES]; }; I.e. no explicit __packed__ attributes, but if we expand this a bit: $ pahole -E e820_table struct e820_table { /* typedef __u32 */ unsigned int nr_entries; /* 0 4 */ struct e820_entry { /* typedef u64 -> __u64 */ long long unsigned int addr; /* 4 8 */ /* typedef u64 -> __u64 */ long long unsigned int size; /* 12 8 */ enum e820_type type; /* 20 4 */ } __attribute__((__packed__)) entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ We see that is that entries member that is packed, because: $ pahole e820_entry struct e820_entry { u64 addr; /* 0 8 */ u64 size; /* 8 8 */ enum e820_type type; /* 16 4 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * A single E820 map entry, describing a memory range of [addr...addr+size-1], * of 'type' memory type: * * (We pack it because there can be thousands of them on large systems.) */ struct e820_entry { u64 addr; u64 size; enum e820_type type; } __attribute__((packed)); So yeah, it is there, BTF doesn't explicitly states it is packed (as DWARF does) and pahole was able to infer that correctly. Tested-by: Richard Weinberger <richard@nod.at> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-08 14:27:42 +02:00
{
.name = "packed",
.key = ARGP_just_packed_structs,
.doc = "Show just packed structs",
},
{
.name = "numeric_version",
.key = ARGP_numeric_version,
.doc = "Print a numeric version, i.e. 119 instead of v1.19"
},
{
.name = NULL,
}
};
static error_t pahole__options_parser(int key, char *arg,
[PAHOLE]: Use cus__loadfl, i.e. libdwfl Now we have: [acme@filo pahole]$ pahole --help Usage: pahole [OPTION...] [FILE] {[CLASS]} -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE -D, --decl_exclude=PREFIX exclude classes declared in files with PREFIX -E, --expand_types expand class members -H, --holes=NR_HOLES show only structs at least NR_HOLES holes -m, --nr_methods show number of methods -n, --nr_members show number of members -N, --class_name_len show size of classes -P, --packable show only structs that has holes that can be packed -R, --reorganize reorg struct trying to kill holes -s, --sizes show size of classes -S, --show_reorg_steps show the struct layout at each reorganization step -t, --nr_definitions show how many times struct was defined -V, --verbose be verbose -x, --exclude=PREFIX exclude PREFIXed classes -X, --cu_exclude=PREFIX exclude PREFIXed compilation units Input Selection: --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID -?, --help Give this help list --usage Give a short usage message Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-03-30 18:25:51 +02:00
struct argp_state *state)
{
switch (key) {
case ARGP_KEY_INIT:
if (state->child_inputs != NULL)
state->child_inputs[0] = state->input;
break;
case 'A': class__include_nested_anonymous = 1; break;
case 'a': class__include_anonymous = 1; break;
case 'B': nr_bit_holes = atoi(arg); break;
case 'C': class_name = arg; break;
case 'c': cacheline_size = atoi(arg); break;
case 'D': decl_exclude_prefix = arg;
decl_exclude_prefix_len = strlen(decl_exclude_prefix);
conf_load.extra_dbg_info = 1; break;
[PAHOLE]: Introduce --recursive For now only affects the --contains output. Example showing the structs that include struct list_head in a linux kernel module: [acme@filo pahole]$ pahole --recursive --contains list_head examples/ipv6.ko.debug.x86-64 inet_protosw proto sock_iocb key_type msg_queue msg_msg nf_hook_ops softnet_data net_device softnet_data dma_device dma_client dma_chan class_device net_device softnet_data dma_chan class klist_node device_driver device klist device_driver bus_type device file_system_type nfs_lock_info file_lock block_device address_space inode dquot mem_dqinfo super_block inode signal_struct page kioctx file kiocb work_struct delayed_work kioctx timer_list ifmcaddr6 inet6_dev inet6_ifaddr neigh_table neighbour net_device softnet_data sock inet_sock delayed_work kioctx plist_head task_struct sigpending signal_struct task_struct user_struct device dev_pm_info device mutex_waiter mutex seq_file block_device quota_info super_block dquot super_block inode zone per_cpu_pages free_area kset bus_type subsystem class bus_type __wait_queue_head __wait_queue rw_semaphore quota_info super_block super_block inode key blocking_notifier_head bus_type subsystem class bus_type mm_struct dentry vm_area_struct kobject class_device net_device softnet_data dma_chan device_driver module_kobject module device kset bus_type subsystem class bus_type lock_class module mm_struct task_struct Handling in multi-cu objects is not very precise, as the same struct has different dwarf offsets (id) in each CU. A mitigation for this problem will be provided with the --cu_list and --cu_name upcoming options, where one will be able to get a list of the object files in a, for instance, linux kernel .ko module and also to specify a cu name to be the only to be considered when processing multi-cu files (again, such as .ko linux kernel modules). This ends up being also useful to generate a reverse class hierarchy :-) Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-05-11 00:11:51 +02:00
case 'd': recursive = 1; break;
case 'E': conf.expand_types = 1; break;
case 'f': find_pointers_in_structs = 1;
class_name = arg; break;
case 'F': conf_load.format_path = arg; break;
case 'H': nr_holes = atoi(arg); break;
case 'I': conf.show_decl_info = 1;
conf_load.extra_dbg_info = 1; break;
case 'i': find_containers = 1;
class_name = arg; break;
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 b18354f64cc2 ("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-18 23:09:41 +01:00
case 'J': btf_encode = 1;
btf_encoder: Teach pahole to store percpu variables in vmlinux BTF. On SMP systems, the global percpu variables are placed in a special '.data..percpu' section, which is stored in a segment whose initial address is set to 0, the addresses of per-CPU variables are relative positive addresses [1]. This patch extracts these variables from vmlinux and places them with their type information in BTF. More specifically, when BTF is encoded, we find the index of the '.data..percpu' section and then traverse the symbol table to find those global objects which are in this section. For each of these objects, we push a BTF_KIND_VAR into the types buffer, and a BTF_VAR_SECINFO into another buffer, percpu_secinfo. When all the CUs have finished processing, we push a BTF_KIND_DATASEC into the btfe->types buffer, followed by the percpu_secinfo's content. In a v5.8-rc3 linux kernel, I was able to extract 288 such variables. The build time overhead is small and the space overhead is also small. See testings below. A found variable can be invalid in two ways: - Its name found in elf_sym__name is invalid. - Its size identified by elf_sym__size is 0. In either case, the BTF containing such symbols will be rejected by the BTF verifier. Normally we should not see such symbols. But if one is seen during BTF encoding, the encoder will exit with error. An new flag '-j' (or '--force') is implemented to help testing, which skips the invalid symbols and force emit a BTF. Testing: - vmlinux size has increased by ~12kb. Before: $ readelf -SW vmlinux | grep BTF [25] .BTF PROGBITS ffffffff821a905c 13a905c 2d2bf8 00 After: $ pahole -J vmlinux $ readelf -SW vmlinux | grep BTF [25] .BTF PROGBITS ffffffff821a905c 13a905c 2d5bca 00 - Common global percpu VARs and DATASEC are found in BTF section. $ bpftool btf dump file vmlinux | grep runqueues [14152] VAR 'runqueues' type_id=13778, linkage=global-alloc $ bpftool btf dump file vmlinux | grep 'cpu_stopper' [17582] STRUCT 'cpu_stopper' size=72 vlen=5 [17601] VAR 'cpu_stopper' type_id=17582, linkage=static $ bpftool btf dump file vmlinux | grep ' DATASEC ' [63652] DATASEC '.data..percpu' size=179288 vlen=288 - Tested bpf selftests. - pahole exits with error if an invalid symbol is seen during encoding, make -f Makefile -j 36 -s PAHOLE: Error: Found symbol of zero size when encoding btf (sym: 'yyy', cu: 'xxx.c'). PAHOLE: Error: Use '-j' or '--force_emit' to ignore such symbols and force emit the btf. scripts/link-vmlinux.sh: line 137: 2475712 Segmentation fault LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} - With the flag '-j' or '--force', the invalid symbols are ignored. - Further in verbose mode and with '-j' or '--force' set, a warning is generated: PAHOLE: Warning: Found symbol of zero size when encoding btf, ignored (sym: 'yyy', cu: 'xxx.c'). PAHOLE: Warning: Found symbol of invalid name when encoding btf, ignored (sym: 'zzz', cu: 'sss.c'). References: [1] https://lwn.net/Articles/531148/ Signed-off-by: Hao Luo <haoluo@google.com> Tested-by: Andrii Nakryiko <andriin@fb.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Andrii Nakryiko <andriin@fb.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: Oleg Rombakh <olegrom@google.com> Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-08 22:44:10 +02:00
conf_load.get_addr_info = true;
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 b18354f64cc2 ("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-18 23:09:41 +01:00
no_bitfield_type_recode = true; break;
case 'l': conf.show_first_biggest_size_base_type_member = 1; break;
case 'M': conf.show_only_data_members = 1; break;
case 'm': stats_formatter = nr_methods_formatter; break;
case 'N': formatter = class_name_len_formatter; break;
case 'n': formatter = nr_members_formatter; break;
case 'P': show_packable = 1;
conf_load.extra_dbg_info = 1; break;
case 'p': conf.expand_pointers = 1; break;
case 'q': conf.emit_stats = 0;
conf.suppress_comments = 1;
conf.suppress_offset_comment = 1; break;
[PAHOLE]: Use cus__loadfl, i.e. libdwfl Now we have: [acme@filo pahole]$ pahole --help Usage: pahole [OPTION...] [FILE] {[CLASS]} -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE -D, --decl_exclude=PREFIX exclude classes declared in files with PREFIX -E, --expand_types expand class members -H, --holes=NR_HOLES show only structs at least NR_HOLES holes -m, --nr_methods show number of methods -n, --nr_members show number of members -N, --class_name_len show size of classes -P, --packable show only structs that has holes that can be packed -R, --reorganize reorg struct trying to kill holes -s, --sizes show size of classes -S, --show_reorg_steps show the struct layout at each reorganization step -t, --nr_definitions show how many times struct was defined -V, --verbose be verbose -x, --exclude=PREFIX exclude PREFIXed classes -X, --cu_exclude=PREFIX exclude PREFIXed compilation units Input Selection: --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID -?, --help Give this help list --usage Give a short usage message Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-03-30 18:25:51 +02:00
case 'R': reorganize = 1; break;
case 'r': conf.rel_offset = 1; break;
case 'S': show_reorg_steps = 1; break;
case 's': formatter = size_formatter; break;
case 'T': stats_formatter = nr_definitions_formatter;
formatter = NULL; break;
case 't': separator = arg[0]; break;
case 'u': defined_in = 1; break;
case 'V': global_verbose = 1; break;
[PAHOLE]: Allow changing the architecture word-size Using a x86_64 binary: [acme@doppio pahole]$ build/pahole -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 8 */ union { struct { long unsigned int arg0; /* 8 8 */ long unsigned int arg1; /* 16 8 */ long unsigned int arg2; /* 24 8 */ long unsigned int arg3; /* 32 8 */ }; /* 32 */ struct { u32 * uaddr; /* 8 8 */ u32 val; /* 16 4 */ u32 flags; /* 20 4 */ u64 time; /* 24 8 */ } fu; /* 24 */ }; /* 8 32 */ /* size: 40, cachelines: 1 */ /* last cacheline: 40 bytes */ }; Changing the word-size from 8 to 4 bytes: [acme@doppio pahole]$ build/pahole -w 4 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 4 */ union { struct { long unsigned int arg0; /* 4 4 */ long unsigned int arg1; /* 8 4 */ long unsigned int arg2; /* 12 4 */ long unsigned int arg3; /* 16 4 */ }; /* 16 */ struct { u32 * uaddr; /* 4 4 */ u32 val; /* 8 4 */ u32 flags; /* 12 4 */ u64 time; /* 16 8 */ } fu; /* 20 */ }; /* 4 20 */ /* size: 24, cachelines: 1 */ /* last cacheline: 24 bytes */ }; And from 8 to 16: [acme@doppio pahole]$ build/pahole -w 16 -C restart_block /usr/lib/debug/lib/modules/2.6.21-65.el5rt/kernel/drivers/net/e1000/e1000.ko.debug struct restart_block { long int (*fn)(struct restart_block *); /* 0 16 */ union { struct { long unsigned int arg0; /* 16 16 */ long unsigned int arg1; /* 32 16 */ long unsigned int arg2; /* 48 16 */ long unsigned int arg3; /* 64 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ }; /* 64 */ struct { u32 * uaddr; /* 16 16 */ u32 val; /* 32 4 */ u32 flags; /* 36 4 */ u64 time; /* 40 8 */ } fu; /* 32 */ }; /* 16 64 */ /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ /* size: 80, cachelines: 2 */ /* last cacheline: 16 bytes */ }; More work is required to specify different alignment rules. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2008-01-12 16:14:40 +01:00
case 'w': word_size = atoi(arg); break;
case 'X': cu__exclude_prefix = arg;
cu__exclude_prefix_len = strlen(cu__exclude_prefix);
break;
case 'x': class__exclude_prefix = arg;
class__exclude_prefix_len = strlen(class__exclude_prefix);
break;
case 'y': class__include_prefix = arg;
class__include_prefix_len = strlen(class__include_prefix);
break;
case 'z':
hole_size_ge = atoi(arg);
if (!global_verbose)
formatter = class_name_formatter;
break;
case 'Z': ctf_encode = 1; break;
case ARGP_flat_arrays: conf.flat_arrays = 1; break;
case ARGP_suppress_aligned_attribute:
conf.suppress_aligned_attribute = 1; break;
case ARGP_suppress_force_paddings:
conf.suppress_force_paddings = 1; break;
case ARGP_suppress_packed:
conf.suppress_packed = 1; break;
case ARGP_show_private_classes:
show_private_classes = true;
conf.show_only_data_members = 1; break;
case ARGP_fixup_silly_bitfields:
conf_load.fixup_silly_bitfields = 1; break;
case ARGP_first_obj_only:
first_obj_only = true; break;
case ARGP_classes_as_structs:
conf.classes_as_structs = 1; break;
case ARGP_hex_fmt:
conf.hex_fmt = 1; break;
2020-01-15 15:35:25 +01:00
case ARGP_just_unions:
just_unions = true; break;
case ARGP_just_structs:
just_structs = true; break;
pahole: Implement --packed To show just packed structs. For instance, here are the top packed structures in the Linux kernel, using BTF data: $ pahole --packed --sizes | sort -k2 -nr | head e820_table 64004 0 boot_params 4096 0 btrfs_super_block 3531 0 efi_variable 2084 0 ntb_info_regs 800 0 tboot 568 0 _legacy_mbr 512 0 disklabel 512 0 btrfs_root_item 439 0 saved_context 317 0 $ If you then look at: $ pahole e820_table struct e820_table { __u32 nr_entries; /* 0 4 */ struct e820_entry entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * The whole array of E820 entries: */ struct e820_table { __u32 nr_entries; struct e820_entry entries[E820_MAX_ENTRIES]; }; I.e. no explicit __packed__ attributes, but if we expand this a bit: $ pahole -E e820_table struct e820_table { /* typedef __u32 */ unsigned int nr_entries; /* 0 4 */ struct e820_entry { /* typedef u64 -> __u64 */ long long unsigned int addr; /* 4 8 */ /* typedef u64 -> __u64 */ long long unsigned int size; /* 12 8 */ enum e820_type type; /* 20 4 */ } __attribute__((__packed__)) entries[3200]; /* 4 64000 */ /* size: 64004, cachelines: 1001, members: 2 */ /* last cacheline: 4 bytes */ } __attribute__((__packed__)); $ We see that is that entries member that is packed, because: $ pahole e820_entry struct e820_entry { u64 addr; /* 0 8 */ u64 size; /* 8 8 */ enum e820_type type; /* 16 4 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ In arch/x86/include/asm/e820/types.h we have: /* * A single E820 map entry, describing a memory range of [addr...addr+size-1], * of 'type' memory type: * * (We pack it because there can be thousands of them on large systems.) */ struct e820_entry { u64 addr; u64 size; enum e820_type type; } __attribute__((packed)); So yeah, it is there, BTF doesn't explicitly states it is packed (as DWARF does) and pahole was able to infer that correctly. Tested-by: Richard Weinberger <richard@nod.at> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-08 14:27:42 +02:00
case ARGP_just_packed_structs:
just_structs = true;
just_packed_structs = true; break;
case ARGP_count:
conf.count = atoi(arg); break;
case ARGP_skip:
conf.skip = atoi(arg); break;
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 14:17:38 +02:00
case ARGP_seek_bytes:
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-07-20 13:25:45 +02:00
conf.seek_bytes = arg; break;
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-07-20 14:27:10 +02:00
case ARGP_size_bytes:
conf.size_bytes = arg; break;
case ARGP_range:
conf.range = arg; break;
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-07-20 13:25:45 +02:00
case ARGP_header_type:
conf.header_type = arg; break;
case ARGP_skip_encoding_btf_vars:
skip_encoding_btf_vars = true; break;
case ARGP_btf_encode_force:
btf_encode_force = true; break;
case ARGP_btf_base:
base_btf_file = arg; break;
case ARGP_numeric_version:
print_numeric_version = true; break;
case ARGP_btf_gen_floats:
btf_gen_floats = true; break;
case ARGP_btf_gen_all:
btf_gen_floats = true; break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static const char pahole__args_doc[] = "FILE";
static struct argp pahole__argp = {
.options = pahole__options,
.parser = pahole__options_parser,
.args_doc = pahole__args_doc,
};
static void do_reorg(struct tag *class, struct cu *cu)
{
int savings;
const uint8_t reorg_verbose =
show_reorg_steps ? 2 : global_verbose;
struct class *clone = class__clone(tag__class(class), NULL, cu);
if (clone == NULL) {
fprintf(stderr, "pahole: out of memory!\n");
exit(EXIT_FAILURE);
}
class__reorganize(clone, cu, reorg_verbose, stdout);
savings = class__size(tag__class(class)) - class__size(clone);
if (savings != 0 && reorg_verbose) {
putchar('\n');
if (show_reorg_steps)
puts("/* Final reorganized struct: */");
}
tag__fprintf(class__tag(clone), cu, &conf, stdout);
if (savings != 0) {
const size_t cacheline_savings =
(tag__nr_cachelines(class, cu) -
tag__nr_cachelines(class__tag(clone), cu));
printf(" /* saved %d byte%s", savings,
savings != 1 ? "s" : "");
if (cacheline_savings != 0)
printf(" and %zu cacheline%s",
cacheline_savings,
cacheline_savings != 1 ?
"s" : "");
puts("! */");
} else
putchar('\n');
/* FIXME: we need to free in the right order,
* cu->obstack is being corrupted...
class__delete(clone, cu);
*/
}
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
static int tag__fprintf_hexdump_value(struct tag *type, struct cu *cu, void *instance, int _sizeof, FILE *fp)
{
uint8_t *contents = instance;
int i, printed = 0;
for (i = 0; i < _sizeof; ++i) {
if (i != 0) {
fputc(' ', fp);
++printed;
}
printed += fprintf(fp, "0x%02x", contents[i]);
}
return printed;
}
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-07-14 18:31:59 +02:00
static uint64_t base_type__value(void *instance, int _sizeof)
{
if (_sizeof == sizeof(int))
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-07-14 18:31:59 +02:00
return *(int *)instance;
else if (_sizeof == sizeof(long))
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-07-14 18:31:59 +02:00
return *(long *)instance;
else if (_sizeof == sizeof(long long))
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-07-14 18:31:59 +02:00
return *(long long *)instance;
else if (_sizeof == sizeof(char))
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-07-14 18:31:59 +02:00
return *(char *)instance;
else if (_sizeof == sizeof(short))
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-07-14 18:31:59 +02:00
return *(short *)instance;
return 0;
}
pahole: Honour --hex_fmt when pretty printing I.e. by default now we print in decimal, as we do for printing just the struct definitions, use --hex to ask for hexadecimal formatting, i.e.: $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ That is, the perf_event_attr for the only event in the perf.data file (default file processed by perf tools such as 'perf evlist'), now with pahole: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' --count=1 < perf.data | grep -v '= 0,' { .type = PERF_TYPE_HARDWARE, .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, $ --count=1 was used because what is in that $header.attrs.offset/size range are instances of: $ pahole ~/bin/perf -C perf_file_attr struct perf_file_attr { struct perf_event_attr attr; /* 0 120 */ /* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */ struct perf_file_section ids; /* 120 16 */ /* size: 136, cachelines: 3, members: 2 */ /* last cacheline: 8 bytes */ }; $ We can drop --count=1 if we use the right type, 'struct perf_file_attr': $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 104, .size = 64, }, }, We lose the ability to use type + type_enum, as we still don't support specifying fields in fields for those class attributes, i.e. we need to support: -C 'perf_event_attr(sizeof=attr.size,type=attr.type,type_enum=perf_type_id)' Which is easy enough, we already support that for $header.data.offset, for instance, will generalise that and reuse with class attributes. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-22 14:06:48 +02:00
static int fprintf__value(FILE* fp, uint64_t value)
{
const char *format = conf.hex_fmt ? "%#" PRIx64 : "%" PRIi64;
return fprintf(fp, format, value);
}
static int base_type__fprintf_value(void *instance, int _sizeof, FILE *fp)
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-07-14 18:31:59 +02:00
{
uint64_t value = base_type__value(instance, _sizeof);
pahole: Honour --hex_fmt when pretty printing I.e. by default now we print in decimal, as we do for printing just the struct definitions, use --hex to ask for hexadecimal formatting, i.e.: $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ That is, the perf_event_attr for the only event in the perf.data file (default file processed by perf tools such as 'perf evlist'), now with pahole: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' --count=1 < perf.data | grep -v '= 0,' { .type = PERF_TYPE_HARDWARE, .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, $ --count=1 was used because what is in that $header.attrs.offset/size range are instances of: $ pahole ~/bin/perf -C perf_file_attr struct perf_file_attr { struct perf_event_attr attr; /* 0 120 */ /* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */ struct perf_file_section ids; /* 120 16 */ /* size: 136, cachelines: 3, members: 2 */ /* last cacheline: 8 bytes */ }; $ We can drop --count=1 if we use the right type, 'struct perf_file_attr': $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 104, .size = 64, }, }, We lose the ability to use type + type_enum, as we still don't support specifying fields in fields for those class attributes, i.e. we need to support: -C 'perf_event_attr(sizeof=attr.size,type=attr.type,type_enum=perf_type_id)' Which is easy enough, we already support that for $header.data.offset, for instance, will generalise that and reuse with class attributes. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-22 14:06:48 +02:00
return fprintf__value(fp, value);
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-07-14 18:31:59 +02:00
}
pahole: Pretty print bitfields So for such a bitfieldy struct: $ pahole perf_event_attr | grep : __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ $ Using a domain specific tool to show which of those bitfield entries have non-zero value for a given perf.data file (the default input file for perf): $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ We can see that we now manage to decode them, at least on x86_64 using a x86_64 perf.data file: $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data | grep -v ' = 0,' { .type = PERF_TYPE_HARDWARE, .size = 0x78, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x1, .inherit = 0x1, .exclude_kernel = 0x1, .mmap = 0x1, .comm = 0x1, .freq = 0x1, .enable_on_exec = 0x1, .task = 0x1, .precise_ip = 0x3, .sample_id_all = 0x1, .exclude_guest = 0x1, .mmap2 = 0x1, .comm_exec = 0x1, .ksymbol = 0x1, .bpf_event = 0x1, }, $ And this reminds me that defaulting to eliding fields with a zero value is valuable... Also, this was in a rush, probably processing files from a different arch will produce bad results as bitfields are tricky, so probably we'll need more features to state how bitfields should be processed, etc. The plan is to have a way to create variables on the command line, where we'll read a header, give it a name, keep its contents accessible, then in subsequent entries in the command line, refer to them and, say, set the endianness from fields in the header, refer to offsets in header members to state what type to use to pretty print a range of the provided file or stdin, etc. A rabbit hole... Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 22:10:21 +02:00
static uint64_t class_member__bitfield_value(struct class_member *member, void *instance)
{
int byte_size = member->byte_size;
uint64_t value = base_type__value(instance, byte_size);
uint64_t mask = 0;
int bits = member->bitfield_size;
while (bits) {
mask |= 1;
if (--bits)
mask <<= 1;
}
mask <<= member->bitfield_offset;
return (value & mask) >> member->bitfield_offset;
}
static int class_member__fprintf_bitfield_value(struct class_member *member, void *instance, FILE *fp)
{
pahole: Honour --hex_fmt when pretty printing I.e. by default now we print in decimal, as we do for printing just the struct definitions, use --hex to ask for hexadecimal formatting, i.e.: $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ That is, the perf_event_attr for the only event in the perf.data file (default file processed by perf tools such as 'perf evlist'), now with pahole: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' --count=1 < perf.data | grep -v '= 0,' { .type = PERF_TYPE_HARDWARE, .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, $ --count=1 was used because what is in that $header.attrs.offset/size range are instances of: $ pahole ~/bin/perf -C perf_file_attr struct perf_file_attr { struct perf_event_attr attr; /* 0 120 */ /* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */ struct perf_file_section ids; /* 120 16 */ /* size: 136, cachelines: 3, members: 2 */ /* last cacheline: 8 bytes */ }; $ We can drop --count=1 if we use the right type, 'struct perf_file_attr': $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 104, .size = 64, }, }, We lose the ability to use type + type_enum, as we still don't support specifying fields in fields for those class attributes, i.e. we need to support: -C 'perf_event_attr(sizeof=attr.size,type=attr.type,type_enum=perf_type_id)' Which is easy enough, we already support that for $header.data.offset, for instance, will generalise that and reuse with class attributes. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-22 14:06:48 +02:00
const char *format = conf.hex_fmt ? "%#" PRIx64 : "%" PRIi64;
return fprintf(fp, format, class_member__bitfield_value(member, instance));
pahole: Pretty print bitfields So for such a bitfieldy struct: $ pahole perf_event_attr | grep : __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ $ Using a domain specific tool to show which of those bitfield entries have non-zero value for a given perf.data file (the default input file for perf): $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ We can see that we now manage to decode them, at least on x86_64 using a x86_64 perf.data file: $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data | grep -v ' = 0,' { .type = PERF_TYPE_HARDWARE, .size = 0x78, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x1, .inherit = 0x1, .exclude_kernel = 0x1, .mmap = 0x1, .comm = 0x1, .freq = 0x1, .enable_on_exec = 0x1, .task = 0x1, .precise_ip = 0x3, .sample_id_all = 0x1, .exclude_guest = 0x1, .mmap2 = 0x1, .comm_exec = 0x1, .ksymbol = 0x1, .bpf_event = 0x1, }, $ And this reminds me that defaulting to eliding fields with a zero value is valuable... Also, this was in a rush, probably processing files from a different arch will produce bad results as bitfields are tricky, so probably we'll need more features to state how bitfields should be processed, etc. The plan is to have a way to create variables on the command line, where we'll read a header, give it a name, keep its contents accessible, then in subsequent entries in the command line, refer to them and, say, set the endianness from fields in the header, refer to offsets in header members to state what type to use to pretty print a range of the provided file or stdin, etc. A rabbit hole... Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 22:10:21 +02:00
}
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-07-14 18:31:59 +02:00
static const char *enumeration__lookup_value(struct type *enumeration, struct cu *cu, uint64_t value)
{
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
if (entry->value == value)
return enumerator__name(entry, cu);
}
return NULL;
}
static const char *enumerations__lookup_value(struct list_head *enumerations, uint64_t value)
{
struct tag_cu_node *pos;
list_for_each_entry(pos, enumerations, node) {
const char *s = enumeration__lookup_value(tag__type(pos->tc.tag), pos->tc.cu, value);
if (s)
return s;
}
return NULL;
}
static struct enumerator *enumeration__lookup_entry_from_value(struct type *enumeration, struct cu *cu, uint64_t value)
{
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
if (entry->value == value)
return entry;
}
return NULL;
}
static struct enumerator *enumerations__lookup_entry_from_value(struct list_head *enumerations, struct cu **cup, uint64_t value)
{
struct tag_cu_node *pos;
list_for_each_entry(pos, enumerations, node) {
struct enumerator *enumerator = enumeration__lookup_entry_from_value(tag__type(pos->tc.tag), pos->tc.cu, value);
if (enumerator) {
*cup = pos->tc.cu;
return enumerator;
}
}
return NULL;
}
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-07-17 17:35:26 +02:00
static int64_t enumeration__lookup_enumerator(struct type *enumeration, struct cu *cu, const char *enumerator)
{
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
const char *entry_name = enumerator__name(entry, cu);
if (!strcmp(entry_name, enumerator))
return entry->value;
if (enumeration->member_prefix_len &&
!strcmp(entry_name + enumeration->member_prefix_len, enumerator))
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-07-17 17:35:26 +02:00
return entry->value;
}
return -1;
}
static int64_t enumerations__lookup_enumerator(struct list_head *enumerations, const char *enumerator)
{
struct tag_cu_node *pos;
list_for_each_entry(pos, enumerations, node) {
int64_t value = enumeration__lookup_enumerator(tag__type(pos->tc.tag), pos->tc.cu, enumerator);
if (value != -1)
return value;
}
return -1;
}
static int base_type__fprintf_enum_value(void *instance, int _sizeof, struct list_head *enumerations, FILE *fp)
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-07-14 18:31:59 +02:00
{
uint64_t value = base_type__value(instance, _sizeof);
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-07-14 18:31:59 +02:00
const char *entry = enumerations__lookup_value(enumerations, value);
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-07-14 18:31:59 +02:00
if (entry)
return fprintf(fp, "%s", entry);
pahole: Honour --hex_fmt when pretty printing I.e. by default now we print in decimal, as we do for printing just the struct definitions, use --hex to ask for hexadecimal formatting, i.e.: $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ That is, the perf_event_attr for the only event in the perf.data file (default file processed by perf tools such as 'perf evlist'), now with pahole: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' --count=1 < perf.data | grep -v '= 0,' { .type = PERF_TYPE_HARDWARE, .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, $ --count=1 was used because what is in that $header.attrs.offset/size range are instances of: $ pahole ~/bin/perf -C perf_file_attr struct perf_file_attr { struct perf_event_attr attr; /* 0 120 */ /* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */ struct perf_file_section ids; /* 120 16 */ /* size: 136, cachelines: 3, members: 2 */ /* last cacheline: 8 bytes */ }; $ We can drop --count=1 if we use the right type, 'struct perf_file_attr': $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 104, .size = 64, }, }, We lose the ability to use type + type_enum, as we still don't support specifying fields in fields for those class attributes, i.e. we need to support: -C 'perf_event_attr(sizeof=attr.size,type=attr.type,type_enum=perf_type_id)' Which is easy enough, we already support that for $header.data.offset, for instance, will generalise that and reuse with class attributes. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-22 14:06:48 +02:00
return fprintf__value(fp, value);
}
static int string__fprintf_value(char *instance, int _sizeof, FILE *fp)
{
return fprintf(fp, "\"%-.*s\"", _sizeof, instance);
}
static int array__fprintf_base_type_value(struct tag *tag, struct cu *cu, void *instance, int _sizeof, FILE *fp)
{
struct array_type *array = tag__array_type(tag);
struct tag *array_type = cu__type(cu, tag->type);
void *contents = instance;
if (array->dimensions != 1) {
// Support multi dimensional arrays later
return tag__fprintf_hexdump_value(tag, cu, instance, _sizeof, fp);
}
if (tag__is_typedef(array_type))
array_type = tag__follow_typedef(array_type, cu);
int i, printed = 0, sizeof_entry = base_type__size(array_type);
printed += fprintf(fp, "{ ");
int nr_entries = array->nr_entries[0];
// Look for zero sized arrays
if (nr_entries == 0)
nr_entries = _sizeof / sizeof_entry;
for (i = 0; i < nr_entries; ++i) {
if (i > 0)
printed += fprintf(fp, ", ");
printed += base_type__fprintf_value(contents, sizeof_entry, fp);
contents += sizeof_entry;
}
return printed + fprintf(fp, " }");
}
static int array__fprintf_value(struct tag *tag, struct cu *cu, void *instance, int _sizeof, FILE *fp)
{
struct tag *array_type = cu__type(cu, tag->type);
char type_name[1024];
if (strcmp(tag__name(array_type, cu, type_name, sizeof(type_name), NULL), "char") == 0)
return string__fprintf_value(instance, _sizeof, fp);
if (tag__is_base_type(array_type, cu))
return array__fprintf_base_type_value(tag, cu, instance, _sizeof, fp);
return tag__fprintf_hexdump_value(tag, cu, instance, _sizeof, fp);
}
pahole: Pretty print unions too, cope with unnamed ones See those unnamed unions? $ pahole perf_event_attr struct perf_event_attr { __u32 type; /* 0 4 */ __u32 size; /* 4 4 */ __u64 config; /* 8 8 */ union { __u64 sample_period; /* 16 8 */ __u64 sample_freq; /* 16 8 */ }; /* 16 8 */ __u64 sample_type; /* 24 8 */ __u64 read_format; /* 32 8 */ __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ union { __u32 wakeup_events; /* 48 4 */ __u32 wakeup_watermark; /* 48 4 */ }; /* 48 4 */ __u32 bp_type; /* 52 4 */ union { __u64 bp_addr; /* 56 8 */ __u64 kprobe_func; /* 56 8 */ __u64 uprobe_path; /* 56 8 */ __u64 config1; /* 56 8 */ }; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ union { __u64 bp_len; /* 64 8 */ __u64 kprobe_addr; /* 64 8 */ __u64 probe_offset; /* 64 8 */ __u64 config2; /* 64 8 */ }; /* 64 8 */ __u64 branch_sample_type; /* 72 8 */ __u64 sample_regs_user; /* 80 8 */ __u32 sample_stack_user; /* 88 4 */ __s32 clockid; /* 92 4 */ __u64 sample_regs_intr; /* 96 8 */ __u32 aux_watermark; /* 104 4 */ __u16 sample_max_stack; /* 108 2 */ __u16 __reserved_2; /* 110 2 */ __u32 aux_sample_size; /* 112 4 */ __u32 __reserved_3; /* 116 4 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ }; $ So, there is at least one in a perf.data file, lets see where that is: $ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ So, at that 0xa8 offset we should find this; $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one can access it as attr->sample_freq or attr->sample_period. Now to the bitfields, all those with value 0x6195b723... $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data { .type = PERF_TYPE_HARDWARE, .size = 0x78, .config = 0, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x6195b723, .inherit = 0x6195b723, .pinned = 0x6195b723, .exclusive = 0x6195b723, .exclude_user = 0x6195b723, .exclude_kernel = 0x6195b723, .exclude_hv = 0x6195b723, .exclude_idle = 0x6195b723, .mmap = 0x6195b723, .comm = 0x6195b723, .freq = 0x6195b723, .inherit_stat = 0x6195b723, .enable_on_exec = 0x6195b723, .task = 0x6195b723, .watermark = 0x6195b723, .precise_ip = 0x6195b723, .mmap_data = 0x6195b723, .sample_id_all = 0x6195b723, .exclude_host = 0x6195b723, .exclude_guest = 0x6195b723, .exclude_callchain_kernel = 0x6195b723, .exclude_callchain_user = 0x6195b723, .mmap2 = 0x6195b723, .comm_exec = 0x6195b723, .use_clockid = 0x6195b723, .context_switch = 0x6195b723, .write_backward = 0x6195b723, .namespaces = 0x6195b723, .ksymbol = 0x6195b723, .bpf_event = 0x6195b723, .aux_output = 0x6195b723, .cgroup = 0x6195b723, .__reserved_1 = 0x6195b723, .wakeup_events = 0, .wakeup_watermark = 0, .bp_type = 0, .bp_addr = 0, .kprobe_func = 0, .uprobe_path = 0, .config1 = 0, .bp_len = 0, .kprobe_addr = 0, .probe_offset = 0, .config2 = 0, .branch_sample_type = 0, .sample_regs_user = 0, .sample_stack_user = 0, .clockid = 0, .sample_regs_intr = 0, .aux_watermark = 0, .sample_max_stack = 0, .__reserved_2 = 0, .aux_sample_size = 0, .__reserved_3 = 0, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 20:41:25 +02:00
static int __class__fprintf_value(struct tag *tag, struct cu *cu, void *instance, int _sizeof, int indent, bool brackets, FILE *fp)
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
{
struct type *type = tag__type(tag);
struct class_member *member;
pahole: Pretty print unions too, cope with unnamed ones See those unnamed unions? $ pahole perf_event_attr struct perf_event_attr { __u32 type; /* 0 4 */ __u32 size; /* 4 4 */ __u64 config; /* 8 8 */ union { __u64 sample_period; /* 16 8 */ __u64 sample_freq; /* 16 8 */ }; /* 16 8 */ __u64 sample_type; /* 24 8 */ __u64 read_format; /* 32 8 */ __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ union { __u32 wakeup_events; /* 48 4 */ __u32 wakeup_watermark; /* 48 4 */ }; /* 48 4 */ __u32 bp_type; /* 52 4 */ union { __u64 bp_addr; /* 56 8 */ __u64 kprobe_func; /* 56 8 */ __u64 uprobe_path; /* 56 8 */ __u64 config1; /* 56 8 */ }; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ union { __u64 bp_len; /* 64 8 */ __u64 kprobe_addr; /* 64 8 */ __u64 probe_offset; /* 64 8 */ __u64 config2; /* 64 8 */ }; /* 64 8 */ __u64 branch_sample_type; /* 72 8 */ __u64 sample_regs_user; /* 80 8 */ __u32 sample_stack_user; /* 88 4 */ __s32 clockid; /* 92 4 */ __u64 sample_regs_intr; /* 96 8 */ __u32 aux_watermark; /* 104 4 */ __u16 sample_max_stack; /* 108 2 */ __u16 __reserved_2; /* 110 2 */ __u32 aux_sample_size; /* 112 4 */ __u32 __reserved_3; /* 116 4 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ }; $ So, there is at least one in a perf.data file, lets see where that is: $ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ So, at that 0xa8 offset we should find this; $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one can access it as attr->sample_freq or attr->sample_period. Now to the bitfields, all those with value 0x6195b723... $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data { .type = PERF_TYPE_HARDWARE, .size = 0x78, .config = 0, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x6195b723, .inherit = 0x6195b723, .pinned = 0x6195b723, .exclusive = 0x6195b723, .exclude_user = 0x6195b723, .exclude_kernel = 0x6195b723, .exclude_hv = 0x6195b723, .exclude_idle = 0x6195b723, .mmap = 0x6195b723, .comm = 0x6195b723, .freq = 0x6195b723, .inherit_stat = 0x6195b723, .enable_on_exec = 0x6195b723, .task = 0x6195b723, .watermark = 0x6195b723, .precise_ip = 0x6195b723, .mmap_data = 0x6195b723, .sample_id_all = 0x6195b723, .exclude_host = 0x6195b723, .exclude_guest = 0x6195b723, .exclude_callchain_kernel = 0x6195b723, .exclude_callchain_user = 0x6195b723, .mmap2 = 0x6195b723, .comm_exec = 0x6195b723, .use_clockid = 0x6195b723, .context_switch = 0x6195b723, .write_backward = 0x6195b723, .namespaces = 0x6195b723, .ksymbol = 0x6195b723, .bpf_event = 0x6195b723, .aux_output = 0x6195b723, .cgroup = 0x6195b723, .__reserved_1 = 0x6195b723, .wakeup_events = 0, .wakeup_watermark = 0, .bp_type = 0, .bp_addr = 0, .kprobe_func = 0, .uprobe_path = 0, .config1 = 0, .bp_len = 0, .kprobe_addr = 0, .probe_offset = 0, .config2 = 0, .branch_sample_type = 0, .sample_regs_user = 0, .sample_stack_user = 0, .clockid = 0, .sample_regs_intr = 0, .aux_watermark = 0, .sample_max_stack = 0, .__reserved_2 = 0, .aux_sample_size = 0, .__reserved_3 = 0, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 20:41:25 +02:00
int printed = 0;
if (brackets)
printed += fprintf(fp, "{");
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
type__for_each_member(type, member) {
void *member_contents = instance + member->byte_offset;
struct tag *member_type = cu__type(cu, member->tag.type);
pahole: Pretty print unions too, cope with unnamed ones See those unnamed unions? $ pahole perf_event_attr struct perf_event_attr { __u32 type; /* 0 4 */ __u32 size; /* 4 4 */ __u64 config; /* 8 8 */ union { __u64 sample_period; /* 16 8 */ __u64 sample_freq; /* 16 8 */ }; /* 16 8 */ __u64 sample_type; /* 24 8 */ __u64 read_format; /* 32 8 */ __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ union { __u32 wakeup_events; /* 48 4 */ __u32 wakeup_watermark; /* 48 4 */ }; /* 48 4 */ __u32 bp_type; /* 52 4 */ union { __u64 bp_addr; /* 56 8 */ __u64 kprobe_func; /* 56 8 */ __u64 uprobe_path; /* 56 8 */ __u64 config1; /* 56 8 */ }; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ union { __u64 bp_len; /* 64 8 */ __u64 kprobe_addr; /* 64 8 */ __u64 probe_offset; /* 64 8 */ __u64 config2; /* 64 8 */ }; /* 64 8 */ __u64 branch_sample_type; /* 72 8 */ __u64 sample_regs_user; /* 80 8 */ __u32 sample_stack_user; /* 88 4 */ __s32 clockid; /* 92 4 */ __u64 sample_regs_intr; /* 96 8 */ __u32 aux_watermark; /* 104 4 */ __u16 sample_max_stack; /* 108 2 */ __u16 __reserved_2; /* 110 2 */ __u32 aux_sample_size; /* 112 4 */ __u32 __reserved_3; /* 116 4 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ }; $ So, there is at least one in a perf.data file, lets see where that is: $ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ So, at that 0xa8 offset we should find this; $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one can access it as attr->sample_freq or attr->sample_period. Now to the bitfields, all those with value 0x6195b723... $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data { .type = PERF_TYPE_HARDWARE, .size = 0x78, .config = 0, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x6195b723, .inherit = 0x6195b723, .pinned = 0x6195b723, .exclusive = 0x6195b723, .exclude_user = 0x6195b723, .exclude_kernel = 0x6195b723, .exclude_hv = 0x6195b723, .exclude_idle = 0x6195b723, .mmap = 0x6195b723, .comm = 0x6195b723, .freq = 0x6195b723, .inherit_stat = 0x6195b723, .enable_on_exec = 0x6195b723, .task = 0x6195b723, .watermark = 0x6195b723, .precise_ip = 0x6195b723, .mmap_data = 0x6195b723, .sample_id_all = 0x6195b723, .exclude_host = 0x6195b723, .exclude_guest = 0x6195b723, .exclude_callchain_kernel = 0x6195b723, .exclude_callchain_user = 0x6195b723, .mmap2 = 0x6195b723, .comm_exec = 0x6195b723, .use_clockid = 0x6195b723, .context_switch = 0x6195b723, .write_backward = 0x6195b723, .namespaces = 0x6195b723, .ksymbol = 0x6195b723, .bpf_event = 0x6195b723, .aux_output = 0x6195b723, .cgroup = 0x6195b723, .__reserved_1 = 0x6195b723, .wakeup_events = 0, .wakeup_watermark = 0, .bp_type = 0, .bp_addr = 0, .kprobe_func = 0, .uprobe_path = 0, .config1 = 0, .bp_len = 0, .kprobe_addr = 0, .probe_offset = 0, .config2 = 0, .branch_sample_type = 0, .sample_regs_user = 0, .sample_stack_user = 0, .clockid = 0, .sample_regs_intr = 0, .aux_watermark = 0, .sample_max_stack = 0, .__reserved_2 = 0, .aux_sample_size = 0, .__reserved_3 = 0, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 20:41:25 +02:00
const char *name = class_member__name(member, cu);
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
pahole: Pretty print unions too, cope with unnamed ones See those unnamed unions? $ pahole perf_event_attr struct perf_event_attr { __u32 type; /* 0 4 */ __u32 size; /* 4 4 */ __u64 config; /* 8 8 */ union { __u64 sample_period; /* 16 8 */ __u64 sample_freq; /* 16 8 */ }; /* 16 8 */ __u64 sample_type; /* 24 8 */ __u64 read_format; /* 32 8 */ __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ union { __u32 wakeup_events; /* 48 4 */ __u32 wakeup_watermark; /* 48 4 */ }; /* 48 4 */ __u32 bp_type; /* 52 4 */ union { __u64 bp_addr; /* 56 8 */ __u64 kprobe_func; /* 56 8 */ __u64 uprobe_path; /* 56 8 */ __u64 config1; /* 56 8 */ }; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ union { __u64 bp_len; /* 64 8 */ __u64 kprobe_addr; /* 64 8 */ __u64 probe_offset; /* 64 8 */ __u64 config2; /* 64 8 */ }; /* 64 8 */ __u64 branch_sample_type; /* 72 8 */ __u64 sample_regs_user; /* 80 8 */ __u32 sample_stack_user; /* 88 4 */ __s32 clockid; /* 92 4 */ __u64 sample_regs_intr; /* 96 8 */ __u32 aux_watermark; /* 104 4 */ __u16 sample_max_stack; /* 108 2 */ __u16 __reserved_2; /* 110 2 */ __u32 aux_sample_size; /* 112 4 */ __u32 __reserved_3; /* 116 4 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ }; $ So, there is at least one in a perf.data file, lets see where that is: $ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ So, at that 0xa8 offset we should find this; $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one can access it as attr->sample_freq or attr->sample_period. Now to the bitfields, all those with value 0x6195b723... $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data { .type = PERF_TYPE_HARDWARE, .size = 0x78, .config = 0, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x6195b723, .inherit = 0x6195b723, .pinned = 0x6195b723, .exclusive = 0x6195b723, .exclude_user = 0x6195b723, .exclude_kernel = 0x6195b723, .exclude_hv = 0x6195b723, .exclude_idle = 0x6195b723, .mmap = 0x6195b723, .comm = 0x6195b723, .freq = 0x6195b723, .inherit_stat = 0x6195b723, .enable_on_exec = 0x6195b723, .task = 0x6195b723, .watermark = 0x6195b723, .precise_ip = 0x6195b723, .mmap_data = 0x6195b723, .sample_id_all = 0x6195b723, .exclude_host = 0x6195b723, .exclude_guest = 0x6195b723, .exclude_callchain_kernel = 0x6195b723, .exclude_callchain_user = 0x6195b723, .mmap2 = 0x6195b723, .comm_exec = 0x6195b723, .use_clockid = 0x6195b723, .context_switch = 0x6195b723, .write_backward = 0x6195b723, .namespaces = 0x6195b723, .ksymbol = 0x6195b723, .bpf_event = 0x6195b723, .aux_output = 0x6195b723, .cgroup = 0x6195b723, .__reserved_1 = 0x6195b723, .wakeup_events = 0, .wakeup_watermark = 0, .bp_type = 0, .bp_addr = 0, .kprobe_func = 0, .uprobe_path = 0, .config1 = 0, .bp_len = 0, .kprobe_addr = 0, .probe_offset = 0, .config2 = 0, .branch_sample_type = 0, .sample_regs_user = 0, .sample_stack_user = 0, .clockid = 0, .sample_regs_intr = 0, .aux_watermark = 0, .sample_max_stack = 0, .__reserved_2 = 0, .aux_sample_size = 0, .__reserved_3 = 0, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 20:41:25 +02:00
if (name)
printed += fprintf(fp, "\n%.*s\t.%s = ", indent, tabs, name);
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
if (member == type->type_member && !list_empty(&type->type_enum)) {
printed += base_type__fprintf_enum_value(member_contents, member->byte_size, &type->type_enum, fp);
pahole: Pretty print bitfields So for such a bitfieldy struct: $ pahole perf_event_attr | grep : __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ $ Using a domain specific tool to show which of those bitfield entries have non-zero value for a given perf.data file (the default input file for perf): $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ We can see that we now manage to decode them, at least on x86_64 using a x86_64 perf.data file: $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data | grep -v ' = 0,' { .type = PERF_TYPE_HARDWARE, .size = 0x78, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x1, .inherit = 0x1, .exclude_kernel = 0x1, .mmap = 0x1, .comm = 0x1, .freq = 0x1, .enable_on_exec = 0x1, .task = 0x1, .precise_ip = 0x3, .sample_id_all = 0x1, .exclude_guest = 0x1, .mmap2 = 0x1, .comm_exec = 0x1, .ksymbol = 0x1, .bpf_event = 0x1, }, $ And this reminds me that defaulting to eliding fields with a zero value is valuable... Also, this was in a rush, probably processing files from a different arch will produce bad results as bitfields are tricky, so probably we'll need more features to state how bitfields should be processed, etc. The plan is to have a way to create variables on the command line, where we'll read a header, give it a name, keep its contents accessible, then in subsequent entries in the command line, refer to them and, say, set the endianness from fields in the header, refer to offsets in header members to state what type to use to pretty print a range of the provided file or stdin, etc. A rabbit hole... Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 22:10:21 +02:00
} else if (member->bitfield_size) {
printed += class_member__fprintf_bitfield_value(member, member_contents, fp);
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-07-14 18:31:59 +02:00
} else if (tag__is_base_type(member_type, cu)) {
printed += base_type__fprintf_value(member_contents, member->byte_size, fp);
} else if (tag__is_array(member_type, cu)) {
pahole: Support zero sized base type arrays Like the one in: $ pahole -C perf_record_sample ~/bin/perf struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ So now we get those: $ perf report -D <SNIP> 0x420 [0x60]: event: 10 . ... raw event: size 96 bytes . 0000: 0a 00 00 00 02 00 60 00 50 7e 00 00 50 7e 00 00 ......`.P~..P~.. . 0010: 00 90 b6 d8 ff 7f 00 00 00 20 00 00 00 00 00 00 ..<B6><D8><FF>.... ...... . 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ . 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ . 0040: 05 00 00 00 02 10 00 00 5b 76 64 73 6f 5d 00 00 ........[vdso].. . 0050: 50 7e 00 00 50 7e 00 00 e7 c5 2a c3 aa 90 01 00 P~..P~..<E7><C5>*<C3><AA>... 440538069911015 0x420 [0x60]: PERF_RECORD_MMAP2 32336/32336: [0x7fffd8b69000(0x2000) @ 0 00:00 0 0]: r-xp [vdso] 0x480 [0x28]: event: 9 . ... raw event: size 40 bytes . 0000: 09 00 00 00 01 40 28 00 27 0b c0 88 ff ff ff ff .....@(.'.<C0>.<FF><FF><FF><FF> . 0010: 50 7e 00 00 50 7e 00 00 44 35 2b c3 aa 90 01 00 P~..P~..D5+<C3><AA>... . 0020: 01 00 00 00 00 00 00 00 ........ 440538069939524 0x480 [0x28]: PERF_RECORD_SAMPLE(IP, 0x4001): 32336/32336: 0xffffffff88c00b27 period: 1 addr: 0 ... thread: sleep:32336 ...... dso: <not found> 0x4a8 [0x28]: event: 9 . ... raw event: size 40 bytes . 0000: 09 00 00 00 01 40 28 00 27 0b c0 88 ff ff ff ff .....@(.'.<C0>.<FF><FF><FF><FF> . 0010: 50 7e 00 00 50 7e 00 00 90 4a 2b c3 aa 90 01 00 P~..P~...J+<C3><AA>... . 0020: 01 00 00 00 00 00 00 00 ........ 440538069944976 0x4a8 [0x28]: PERF_RECORD_SAMPLE(IP, 0x4001): 32336/32336: 0xffffffff88c00b27 period: 1 addr: 0 $ pahole --seek_bytes=0x420 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 3 ~/bin/perf < perf.data { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b3544, 0x1 }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b4a90, 0x1 }, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 13:58:29 +02:00
int sizeof_member = member->byte_size;
// zero sized array, at the end of the struct?
if (sizeof_member == 0 && list_is_last(&member->tag.node, &type->namespace.tags))
sizeof_member = _sizeof - member->byte_offset;
printed += array__fprintf_value(member_type, cu, member_contents, sizeof_member, fp);
pahole: Support nested structs I.e. $ pahole -C perf_record_mmap2 ~/bin/perf struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ And others will have the 'header' field pretty printed indented: $ pahole --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 ~/bin/perf < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x28, }, .id = 0x1, .path = "/", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x30, }, .id = 0x611, .path = "/system.slice", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x30, }, .id = 0x8, .path = "/machine.slice", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x80, }, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x58, }, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = { .type = PERF_RECORD_COMM, .misc = 0, .size = 0x28, }, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = { .type = PERF_RECORD_COMM, .misc = 0x2000, .size = 0x28, }, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x68, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x70, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x60, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b3544, 0x1 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b4a90, 0x1 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b57c8, 0x6 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b644b, 0x3a }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b7279, 0x266 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b97c7, 0x16cd }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f53040c814b, 0x7e5000007e50, 0x190aac32bc2b8, 0x5f03 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f53040dd5af, 0x7e5000007e50, 0x190aac32c1af2, 0x16141 }, }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x70, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f53040d7281, 0x7e5000007e50, 0x190aac32ee484, 0x31f6d }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f5304011e55, 0x7e5000007e50, 0x190aac331dd50, 0x34455 }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 14:22:58 +02:00
} else if (tag__is_struct(member_type)) {
pahole: Pretty print unions too, cope with unnamed ones See those unnamed unions? $ pahole perf_event_attr struct perf_event_attr { __u32 type; /* 0 4 */ __u32 size; /* 4 4 */ __u64 config; /* 8 8 */ union { __u64 sample_period; /* 16 8 */ __u64 sample_freq; /* 16 8 */ }; /* 16 8 */ __u64 sample_type; /* 24 8 */ __u64 read_format; /* 32 8 */ __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ union { __u32 wakeup_events; /* 48 4 */ __u32 wakeup_watermark; /* 48 4 */ }; /* 48 4 */ __u32 bp_type; /* 52 4 */ union { __u64 bp_addr; /* 56 8 */ __u64 kprobe_func; /* 56 8 */ __u64 uprobe_path; /* 56 8 */ __u64 config1; /* 56 8 */ }; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ union { __u64 bp_len; /* 64 8 */ __u64 kprobe_addr; /* 64 8 */ __u64 probe_offset; /* 64 8 */ __u64 config2; /* 64 8 */ }; /* 64 8 */ __u64 branch_sample_type; /* 72 8 */ __u64 sample_regs_user; /* 80 8 */ __u32 sample_stack_user; /* 88 4 */ __s32 clockid; /* 92 4 */ __u64 sample_regs_intr; /* 96 8 */ __u32 aux_watermark; /* 104 4 */ __u16 sample_max_stack; /* 108 2 */ __u16 __reserved_2; /* 110 2 */ __u32 aux_sample_size; /* 112 4 */ __u32 __reserved_3; /* 116 4 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ }; $ So, there is at least one in a perf.data file, lets see where that is: $ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ So, at that 0xa8 offset we should find this; $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one can access it as attr->sample_freq or attr->sample_period. Now to the bitfields, all those with value 0x6195b723... $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data { .type = PERF_TYPE_HARDWARE, .size = 0x78, .config = 0, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x6195b723, .inherit = 0x6195b723, .pinned = 0x6195b723, .exclusive = 0x6195b723, .exclude_user = 0x6195b723, .exclude_kernel = 0x6195b723, .exclude_hv = 0x6195b723, .exclude_idle = 0x6195b723, .mmap = 0x6195b723, .comm = 0x6195b723, .freq = 0x6195b723, .inherit_stat = 0x6195b723, .enable_on_exec = 0x6195b723, .task = 0x6195b723, .watermark = 0x6195b723, .precise_ip = 0x6195b723, .mmap_data = 0x6195b723, .sample_id_all = 0x6195b723, .exclude_host = 0x6195b723, .exclude_guest = 0x6195b723, .exclude_callchain_kernel = 0x6195b723, .exclude_callchain_user = 0x6195b723, .mmap2 = 0x6195b723, .comm_exec = 0x6195b723, .use_clockid = 0x6195b723, .context_switch = 0x6195b723, .write_backward = 0x6195b723, .namespaces = 0x6195b723, .ksymbol = 0x6195b723, .bpf_event = 0x6195b723, .aux_output = 0x6195b723, .cgroup = 0x6195b723, .__reserved_1 = 0x6195b723, .wakeup_events = 0, .wakeup_watermark = 0, .bp_type = 0, .bp_addr = 0, .kprobe_func = 0, .uprobe_path = 0, .config1 = 0, .bp_len = 0, .kprobe_addr = 0, .probe_offset = 0, .config2 = 0, .branch_sample_type = 0, .sample_regs_user = 0, .sample_stack_user = 0, .clockid = 0, .sample_regs_intr = 0, .aux_watermark = 0, .sample_max_stack = 0, .__reserved_2 = 0, .aux_sample_size = 0, .__reserved_3 = 0, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 20:41:25 +02:00
printed += __class__fprintf_value(member_type, cu, member_contents, member->byte_size, indent + 1, true, fp);
} else if (tag__is_union(member_type)) {
printed += __class__fprintf_value(member_type, cu, member_contents, member->byte_size, indent + (name ? 1 : 0), !!name, fp);
if (!name)
continue;
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
} else {
printed += tag__fprintf_hexdump_value(member_type, cu, member_contents, member->byte_size, fp);
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
}
fputc(',', fp);
++printed;
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
}
pahole: Pretty print unions too, cope with unnamed ones See those unnamed unions? $ pahole perf_event_attr struct perf_event_attr { __u32 type; /* 0 4 */ __u32 size; /* 4 4 */ __u64 config; /* 8 8 */ union { __u64 sample_period; /* 16 8 */ __u64 sample_freq; /* 16 8 */ }; /* 16 8 */ __u64 sample_type; /* 24 8 */ __u64 read_format; /* 32 8 */ __u64 disabled:1; /* 40: 0 8 */ __u64 inherit:1; /* 40: 1 8 */ __u64 pinned:1; /* 40: 2 8 */ __u64 exclusive:1; /* 40: 3 8 */ __u64 exclude_user:1; /* 40: 4 8 */ __u64 exclude_kernel:1; /* 40: 5 8 */ __u64 exclude_hv:1; /* 40: 6 8 */ __u64 exclude_idle:1; /* 40: 7 8 */ __u64 mmap:1; /* 40: 8 8 */ __u64 comm:1; /* 40: 9 8 */ __u64 freq:1; /* 40:10 8 */ __u64 inherit_stat:1; /* 40:11 8 */ __u64 enable_on_exec:1; /* 40:12 8 */ __u64 task:1; /* 40:13 8 */ __u64 watermark:1; /* 40:14 8 */ __u64 precise_ip:2; /* 40:15 8 */ __u64 mmap_data:1; /* 40:17 8 */ __u64 sample_id_all:1; /* 40:18 8 */ __u64 exclude_host:1; /* 40:19 8 */ __u64 exclude_guest:1; /* 40:20 8 */ __u64 exclude_callchain_kernel:1; /* 40:21 8 */ __u64 exclude_callchain_user:1; /* 40:22 8 */ __u64 mmap2:1; /* 40:23 8 */ __u64 comm_exec:1; /* 40:24 8 */ __u64 use_clockid:1; /* 40:25 8 */ __u64 context_switch:1; /* 40:26 8 */ __u64 write_backward:1; /* 40:27 8 */ __u64 namespaces:1; /* 40:28 8 */ __u64 ksymbol:1; /* 40:29 8 */ __u64 bpf_event:1; /* 40:30 8 */ __u64 aux_output:1; /* 40:31 8 */ __u64 cgroup:1; /* 40:32 8 */ __u64 __reserved_1:31; /* 40:33 8 */ union { __u32 wakeup_events; /* 48 4 */ __u32 wakeup_watermark; /* 48 4 */ }; /* 48 4 */ __u32 bp_type; /* 52 4 */ union { __u64 bp_addr; /* 56 8 */ __u64 kprobe_func; /* 56 8 */ __u64 uprobe_path; /* 56 8 */ __u64 config1; /* 56 8 */ }; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ union { __u64 bp_len; /* 64 8 */ __u64 kprobe_addr; /* 64 8 */ __u64 probe_offset; /* 64 8 */ __u64 config2; /* 64 8 */ }; /* 64 8 */ __u64 branch_sample_type; /* 72 8 */ __u64 sample_regs_user; /* 80 8 */ __u32 sample_stack_user; /* 88 4 */ __s32 clockid; /* 92 4 */ __u64 sample_regs_intr; /* 96 8 */ __u32 aux_watermark; /* 104 4 */ __u16 sample_max_stack; /* 108 2 */ __u16 __reserved_2; /* 110 2 */ __u32 aux_sample_size; /* 112 4 */ __u32 __reserved_3; /* 116 4 */ /* size: 120, cachelines: 2, members: 53 */ /* last cacheline: 56 bytes */ }; $ So, there is at least one in a perf.data file, lets see where that is: $ pahole ~/bin/perf -C perf_file_header --count 1 < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0xa8, .size = 0x88, }, .data = { .offset = 0x130, .size = 0x588, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ So, at that 0xa8 offset we should find this; $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ Looks legit, freq = 4000 (0x0xfa0), and since its an unamed union, one can access it as attr->sample_freq or attr->sample_period. Now to the bitfields, all those with value 0x6195b723... $ pahole --seek_bytes=0xa8 -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' --count 1 < perf.data { .type = PERF_TYPE_HARDWARE, .size = 0x78, .config = 0, .sample_period = 0xfa0, .sample_freq = 0xfa0, .sample_type = 0x107, .read_format = 0x4, .disabled = 0x6195b723, .inherit = 0x6195b723, .pinned = 0x6195b723, .exclusive = 0x6195b723, .exclude_user = 0x6195b723, .exclude_kernel = 0x6195b723, .exclude_hv = 0x6195b723, .exclude_idle = 0x6195b723, .mmap = 0x6195b723, .comm = 0x6195b723, .freq = 0x6195b723, .inherit_stat = 0x6195b723, .enable_on_exec = 0x6195b723, .task = 0x6195b723, .watermark = 0x6195b723, .precise_ip = 0x6195b723, .mmap_data = 0x6195b723, .sample_id_all = 0x6195b723, .exclude_host = 0x6195b723, .exclude_guest = 0x6195b723, .exclude_callchain_kernel = 0x6195b723, .exclude_callchain_user = 0x6195b723, .mmap2 = 0x6195b723, .comm_exec = 0x6195b723, .use_clockid = 0x6195b723, .context_switch = 0x6195b723, .write_backward = 0x6195b723, .namespaces = 0x6195b723, .ksymbol = 0x6195b723, .bpf_event = 0x6195b723, .aux_output = 0x6195b723, .cgroup = 0x6195b723, .__reserved_1 = 0x6195b723, .wakeup_events = 0, .wakeup_watermark = 0, .bp_type = 0, .bp_addr = 0, .kprobe_func = 0, .uprobe_path = 0, .config1 = 0, .bp_len = 0, .kprobe_addr = 0, .probe_offset = 0, .config2 = 0, .branch_sample_type = 0, .sample_regs_user = 0, .sample_stack_user = 0, .clockid = 0, .sample_regs_intr = 0, .aux_watermark = 0, .sample_max_stack = 0, .__reserved_2 = 0, .aux_sample_size = 0, .__reserved_3 = 0, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 20:41:25 +02:00
if (brackets)
printed += fprintf(fp, "\n%.*s}", indent, tabs);
return printed;
}
static int class__fprintf_value(struct tag *tag, struct cu *cu, void *instance, int _sizeof, int indent, FILE *fp)
{
return __class__fprintf_value(tag, cu, instance, _sizeof, indent, true, fp);
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
}
static int tag__fprintf_value(struct tag *type, struct cu *cu, void *instance, int _sizeof, FILE *fp)
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
{
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
if (tag__is_struct(type))
pahole: Support nested structs I.e. $ pahole -C perf_record_mmap2 ~/bin/perf struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ And others will have the 'header' field pretty printed indented: $ pahole --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 ~/bin/perf < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x28, }, .id = 0x1, .path = "/", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x30, }, .id = 0x611, .path = "/system.slice", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x30, }, .id = 0x8, .path = "/machine.slice", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x80, }, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 0x58, }, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = { .type = PERF_RECORD_COMM, .misc = 0, .size = 0x28, }, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = { .type = PERF_RECORD_COMM, .misc = 0x2000, .size = 0x28, }, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x68, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x70, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x60, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b3544, 0x1 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b4a90, 0x1 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b57c8, 0x6 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b644b, 0x3a }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b7279, 0x266 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4001, .size = 0x28, }, .array = { 0xffffffff88c00b27, 0x7e5000007e50, 0x190aac32b97c7, 0x16cd }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f53040c814b, 0x7e5000007e50, 0x190aac32bc2b8, 0x5f03 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f53040dd5af, 0x7e5000007e50, 0x190aac32c1af2, 0x16141 }, }, { .header = { .type = PERF_RECORD_MMAP2, .misc = 0x2, .size = 0x70, }, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f53040d7281, 0x7e5000007e50, 0x190aac32ee484, 0x31f6d }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0x4002, .size = 0x28, }, .array = { 0x7f5304011e55, 0x7e5000007e50, 0x190aac331dd50, 0x34455 }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 14:22:58 +02:00
return class__fprintf_value(type, cu, instance, _sizeof, 0, fp);
pahole: Pretty print base types in structs from stdin $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ Then, since this 'struct alt_instr' hasn't changed, lets use the shorter form of just stating the struct name, so that pahole will use /sys/kernel/btf/vmlinux and do all faster and more concisely: $ pahole alt_instr struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ And then, lets pretty print the structs, using gcc's designated initializer syntax: $ pahole alt_instr < .altinstructions | head -32 { .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, }, { .instr_offset = 0xfffffffffec5722a, .repl_offset = 0x4863, .cpuid = 0xeb, .instrlen = 0x2, .replacementlen = 0, .padlen = 0, }, { .instr_offset = 0xfffffffffec57222, .repl_offset = 0x4856, .cpuid = 0x91, .instrlen = 0x5, .replacementlen = 0x5, .padlen = 0x5, }, $ Probably we need to align the =, that is easy, but not strictly needed at this point. Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 19:16:07 +02:00
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
return tag__fprintf_hexdump_value(type, cu, instance, _sizeof, fp);
}
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 14:17:38 +02:00
static int pipe_seek(FILE *fp, off_t offset)
{
char bf[4096];
int chunk = sizeof(bf);
if (chunk > offset)
chunk = offset;
while (fread(bf, chunk, 1, stdin) == 1) {
offset -= chunk;
if (offset == 0)
return 0;
if (chunk > offset)
chunk = offset;
}
return offset == 0 ? 0 : -1;
}
static uint64_t tag__real_sizeof(struct tag *tag, struct cu *cu, int _sizeof, void *instance)
{
if (tag__is_struct(tag)) {
struct type *type = tag__type(tag);
if (type->sizeof_member) {
struct class_member *member = type->sizeof_member;
return base_type__value(instance + member->byte_offset, member->byte_size);
}
}
return _sizeof;
}
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-07-17 17:35:26 +02:00
/*
* Classes should start close to where they are needed, then moved elsewhere, remember:
* "Premature optimization is the root of all evil" (Knuth till unproven).
*
* So far just the '==' operator is supported, so just a struct member + a value are
* needed, no, strings are not supported so far.
*
* If the class member is the 'type=' and we have a 'type_enum=' in place, then we will
* resolve that at parse time and convert that to an uint64_t and it'll do the trick.
*
* More to come, when needed.
*/
struct class_member_filter {
struct class_member *left;
uint64_t right;
};
static bool type__filter_value(struct tag *tag, void *instance)
{
// this has to be a type, otherwise we'd not have a type->filter
struct type *type = tag__type(tag);
struct class_member_filter *filter = type->filter;
struct class_member *member = filter->left;
uint64_t value = base_type__value(instance + member->byte_offset, member->byte_size);
// Only operator supported so far is '=='
return value != filter->right;
}
static struct tag *tag__real_type(struct tag *tag, struct cu **cup, void *instance)
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
{
if (tag__is_struct(tag)) {
struct type *type = tag__type(tag);
if (!list_empty(&type->type_enum) && type->type_member) {
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
struct class_member *member = type->type_member;
uint64_t value = base_type__value(instance + member->byte_offset, member->byte_size);
struct cu *cu_enumerator;
struct enumerator *enumerator = enumerations__lookup_entry_from_value(&type->type_enum, &cu_enumerator, value);
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
char name[1024];
if (!enumerator)
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
return tag;
if (enumerator->type_enum.tag) {
*cup = enumerator->type_enum.cu;
return enumerator->type_enum.tag;
}
snprintf(name, sizeof(name), "%s", enumerator__name(enumerator, cu_enumerator));
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
strlwr(name);
struct tag *real_type = cu__find_type_by_name(*cup, name, false, NULL);
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
if (!real_type)
return NULL;
if (tag__is_struct(real_type)) {
enumerator->type_enum.tag = real_type;
enumerator->type_enum.cu = *cup;
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
return real_type;
}
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
}
}
return tag;
}
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-07-20 13:25:45 +02:00
struct type_instance {
struct type *type;
struct cu *cu;
bool read_already;
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-07-20 13:25:45 +02:00
char instance[0];
};
static struct type_instance *type_instance__new(struct type *type, struct cu *cu)
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-07-20 13:25:45 +02:00
{
if (type == NULL)
return NULL;
struct type_instance *instance = malloc(sizeof(*instance) + type->size);
if (instance) {
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-07-20 13:25:45 +02:00
instance->type = type;
instance->cu = cu;
instance->read_already = false;
}
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-07-20 13:25:45 +02:00
return instance;
}
static void type_instance__delete(struct type_instance *instance)
{
if (!instance)
return;
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-07-20 13:25:45 +02:00
instance->type = NULL;
free(instance);
}
static int64_t type_instance__int_value(struct type_instance *instance, const char *member_name_orig)
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-07-20 13:25:45 +02:00
{
struct cu *cu = instance->cu;
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-07-20 13:25:45 +02:00
struct class_member *member = type__find_member_by_name(instance->type, cu, member_name_orig);
int byte_offset = 0;
if (!member) {
char *sep = strchr(member_name_orig, '.');
if (!sep)
return -1;
char *member_name_alloc = strdup(member_name_orig);
if (!member_name_alloc)
return -1;
char *member_name = member_name_alloc;
struct type *type = instance->type;
sep = member_name_alloc + (sep - member_name_orig);
*sep = 0;
while (1) {
member = type__find_member_by_name(type, cu, member_name);
if (!member) {
out_free_member_name:
free(member_name_alloc);
return -1;
}
byte_offset += member->byte_offset;
type = tag__type(cu__type(cu, member->tag.type));
if (type == NULL)
goto out_free_member_name;
member_name = sep + 1;
sep = strchr(member_name, '.');
if (!sep)
break;
}
member = type__find_member_by_name(type, cu, member_name);
free(member_name_alloc);
if (member == NULL)
return -1;
}
byte_offset += member->byte_offset;
struct tag *member_type = cu__type(cu, member->tag.type);
if (!tag__is_base_type(member_type, cu))
return -1;
return base_type__value(&instance->instance[byte_offset], member->byte_size);
}
static int64_t type__instance_read_once(struct type_instance *instance, FILE *fp)
{
if (!instance || instance->read_already)
return 0;
instance->read_already = true;
return fread(instance->instance, instance->type->size, 1, stdin) != 1 ? -1 : instance->type->size;
}
/*
* struct prototype - split arguments to a type
*
* @name - type name
* @type - name of the member containing a type id
* @type_enum - translate @type into a enum entry/string
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
* @type_enum_resolved - if this was already resolved, i.e. if the enums were find in some CU
* @size - the member with the size for variable sized records
* @filter - filter expression using record contents and values or enum entries
* @range - from where to get seek_bytes and size_bytes where to pretty print this specific class
*/
struct prototype {
struct list_head node;
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
struct tag *class;
struct cu *cu;
const char *type,
*type_enum,
*size,
*range;
char *filter;
uint16_t nr_args;
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
bool type_enum_resolved;
char name[0];
};
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
static int prototype__stdio_fprintf_value(struct prototype *prototype, struct type_instance *header, FILE *fp)
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
{
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
struct tag *type = prototype->class;
struct cu *cu = prototype->cu;
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
int _sizeof = tag__size(type, cu), printed = 0;
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
int max_sizeof = _sizeof;
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
void *instance = malloc(_sizeof);
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-07-20 14:27:10 +02:00
uint64_t size_bytes = ULLONG_MAX;
uint32_t count = 0;
uint32_t skip = conf.skip;
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
if (instance == NULL)
return -ENOMEM;
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (type__instance_read_once(header, stdin) < 0) {
int err = --errno;
fprintf(stderr, "pahole: --header (%s) type not be read\n", conf.header_type);
return err;
}
if (conf.range || prototype->range) {
off_t seek_bytes;
const char *range = conf.range ?: prototype->range;
if (!header) {
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (conf.header_type)
fprintf(stderr, "pahole: --header_type=%s not found\n", conf.header_type);
else
fprintf(stderr, "pahole: range (%s) requires --header\n", range);
return -ESRCH;
}
char *member_name = NULL;
if (asprintf(&member_name, "%s.%s", range, "offset") == -1) {
fprintf(stderr, "pahole: not enough memory for range=%s\n", range);
return -ENOMEM;
}
int64_t value = type_instance__int_value(header, member_name);
if (value < 0) {
fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating range=%s\n",
member_name, conf.header_type, range);
free(member_name);
return -ESRCH;
}
seek_bytes = value;
free(member_name);
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
off_t total_read_bytes = ftell(stdin);
// Since we're reading stdin, we need to account for what we already read
if (seek_bytes < total_read_bytes) {
fprintf(stderr, "pahole: can't go back in stdin, already read %" PRIu64 " bytes, can't go to position %#" PRIx64 "\n",
total_read_bytes, seek_bytes);
return -ENOMEM;
}
if (global_verbose) {
fprintf(fp, "pahole: range.seek_bytes evaluated from range=%s is %#" PRIx64 " \n",
range, seek_bytes);
}
seek_bytes -= total_read_bytes;
if (asprintf(&member_name, "%s.%s", range, "size") == -1) {
fprintf(stderr, "pahole: not enough memory for range=%s\n", range);
return -ENOMEM;
}
value = type_instance__int_value(header, member_name);
if (value < 0) {
fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating range=%s\n",
member_name, conf.header_type, range);
free(member_name);
return -ESRCH;
}
size_bytes = value;
if (global_verbose) {
fprintf(fp, "pahole: range.size_bytes evaluated from range=%s is %#" PRIx64 " \n",
range, size_bytes);
}
free(member_name);
if (pipe_seek(stdin, seek_bytes) < 0) {
int err = --errno;
fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes);
return err;
}
goto do_read;
}
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-07-20 13:25:45 +02:00
if (conf.seek_bytes) {
off_t seek_bytes;
if (strstarts(conf.seek_bytes, "$header.")) {
if (!header) {
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-07-20 13:25:45 +02:00
fprintf(stderr, "pahole: --seek_bytes (%s) makes reference to --header but it wasn't specified\n",
conf.seek_bytes);
return -ESRCH;
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-07-20 13:25:45 +02:00
}
const char *member_name = conf.seek_bytes + sizeof("$header.") - 1;
int64_t value = type_instance__int_value(header, member_name);
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-07-20 13:25:45 +02:00
if (value < 0) {
fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating --seek_bytes=%s\n",
member_name, conf.header_type, conf.seek_bytes);
return -ESRCH;
}
seek_bytes = value;
if (global_verbose)
fprintf(stdout, "pahole: seek bytes evaluated from --seek_bytes=%s is %#" PRIx64 " \n",
conf.seek_bytes, seek_bytes);
if (seek_bytes < header->type->size) {
fprintf(stderr, "pahole: seek bytes evaluated from --seek_bytes=%s is less than the header type size\n",
conf.seek_bytes);
return -EINVAL;
}
} else {
seek_bytes = strtol(conf.seek_bytes, NULL, 0);
}
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-07-20 13:25:45 +02:00
if (header) {
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-07-20 13:25:45 +02:00
// Since we're reading stdin, we need to account for already read header:
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
seek_bytes -= ftell(stdin);
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-07-20 13:25:45 +02:00
}
if (pipe_seek(stdin, seek_bytes) < 0) {
int err = --errno;
fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes);
return err;
}
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 14:17:38 +02:00
}
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-07-20 14:27:10 +02:00
if (conf.size_bytes) {
if (strstarts(conf.size_bytes, "$header.")) {
if (!header) {
fprintf(stderr, "pahole: --size_bytes (%s) makes reference to --header but it wasn't specified\n",
conf.size_bytes);
return -ESRCH;
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-07-20 14:27:10 +02:00
}
const char *member_name = conf.size_bytes + sizeof("$header.") - 1;
int64_t value = type_instance__int_value(header, member_name);
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-07-20 14:27:10 +02:00
if (value < 0) {
fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating --size_bytes=%s\n",
member_name, conf.header_type, conf.size_bytes);
return -ESRCH;
}
size_bytes = value;
if (global_verbose)
fprintf(stdout, "pahole: size bytes evaluated from --size_bytes=%s is %#" PRIx64 " \n",
conf.size_bytes, size_bytes);
} else {
size_bytes = strtol(conf.size_bytes, NULL, 0);
}
}
do_read:
{
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-07-20 14:27:10 +02:00
uint64_t read_bytes = 0;
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
off_t record_offset = ftell(stdin);
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-07-20 14:27:10 +02:00
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
while (fread(instance, _sizeof, 1, stdin) == 1) {
// Read it from each record/instance
int real_sizeof = tag__real_sizeof(type, cu, _sizeof, instance);
pahole: Allow specifying a struct member based sizeof() override When we deal with things like: $ pahole ~/bin/perf -C 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 */ }; $ Records are put on a perf.data file with size specifying that the next record is at: perf_event_header->size - sizeof(struct perf_event_header) after the current record, so allow for that with the following syntax: $ pahole -C 'perf_event_header(sizeof=size)' ~/bin/perf < perf.data I.e. pahole should go on reading from stdin 'struct perf_event_header' records that use that 'size' field to figure out where the next header is. E.g.: Having: $ perf report -D | head # 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! $ We see that the first record is at offset 0x130, furthermore: $ perf report -D | grep "Aggregated stats:" -A 1 Aggregated stats: TOTAL events: 26 $ So we have 26 events, so lets use --seek_bytes to start reading from the offset for the first event and ask for those 26 events to be decoded, and adding the decimal representation for the type, so that we can match it up with 'perf report -D' output, we see: $ pahole --seek_bytes=0x130 -C 'perf_event_header(sizeof=size)' --count 26 ~/bin/perf < perf.data { .type = 0x4f (79), .misc = 0 (0), .size = 0x20 (32), }, { .type = 0x49 (73), .misc = 0 (0), .size = 0x28 (40), }, { .type = 0x4a (74), .misc = 0 (0), .size = 0x20 (32), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x28 (40), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x30 (48), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x30 (48), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x80 (128), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x58 (88), }, { .type = 0x3 (3), .misc = 0 (0), .size = 0x28 (40), }, { .type = 0x3 (3), .misc = 0x2000 (8192), .size = 0x28 (40), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x68 (104), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x70 (112), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x60 (96), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x70 (112), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0x4 (4), .misc = 0 (0), .size = 0x30 (48), }, { .type = 0x44 (68), .misc = 0 (0), .size = 0x8 (8), }, Which matches the output from 'perf report -D': $ perf report -D | grep ^0x 0x130 [0x20]: event: 79 0x150 [0x28]: event: 73 0x178 [0x20]: event: 74 0x198 [0x28]: event: 19 0x1c0 [0x30]: event: 19 0x1f0 [0x30]: event: 19 0x220 [0x80]: event: 19 0x2a0 [0x58]: event: 19 0x2f8 [0x28]: event: 3 0x6b0 [0x8]: event: 68 0x320 [0x28]: event: 3 0x348 [0x68]: event: 10 0x3b0 [0x70]: event: 10 0x420 [0x60]: event: 10 0x480 [0x28]: event: 9 0x4a8 [0x28]: event: 9 0x4d0 [0x28]: event: 9 0x4f8 [0x28]: event: 9 0x520 [0x28]: event: 9 0x548 [0x28]: event: 9 0x570 [0x28]: event: 9 0x598 [0x28]: event: 9 0x5c0 [0x70]: event: 10 0x630 [0x28]: event: 9 0x658 [0x28]: event: 9 0x680 [0x30]: event: 4 $ There is only one "reordering" as pahole is decoding this in the on-disk order, while 'perf report -D' knows about the timestamps encoded as perf those events perf_event_attr->sample_type and orders those events before printing. So we're on our way to a universal type (BTF, CTF, DWARF, others) dumper, now I need a name for this tool, 'spretty' comes to mind, but, humm, the urban dict seems to know about that already ;-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-13 18:25:06 +02:00
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
if (real_sizeof > _sizeof) {
if (real_sizeof > max_sizeof) {
void *new_instance = realloc(instance, real_sizeof);
if (!new_instance) {
fprintf(stderr, "Couldn't allocate space for a record, too big: %d bytes\n", real_sizeof);
printed = -1;
goto out;
}
instance = new_instance;
max_sizeof = real_sizeof;
}
if (fread(instance + _sizeof, real_sizeof - _sizeof, 1, stdin) != 1) {
fprintf(stderr, "Couldn't read record: %d bytes\n", real_sizeof);
printed = -1;
goto out;
}
}
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-07-20 14:27:10 +02:00
read_bytes += real_sizeof;
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-07-17 17:35:26 +02:00
if (tag__type(type)->filter && type__filter_value(type, instance))
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-07-20 14:27:10 +02:00
goto next_record;
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-07-17 17:35:26 +02:00
if (skip) {
--skip;
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-07-20 14:27:10 +02:00
goto next_record;
}
pahole: Allow specifying a struct member based sizeof() override When we deal with things like: $ pahole ~/bin/perf -C 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 */ }; $ Records are put on a perf.data file with size specifying that the next record is at: perf_event_header->size - sizeof(struct perf_event_header) after the current record, so allow for that with the following syntax: $ pahole -C 'perf_event_header(sizeof=size)' ~/bin/perf < perf.data I.e. pahole should go on reading from stdin 'struct perf_event_header' records that use that 'size' field to figure out where the next header is. E.g.: Having: $ perf report -D | head # 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! $ We see that the first record is at offset 0x130, furthermore: $ perf report -D | grep "Aggregated stats:" -A 1 Aggregated stats: TOTAL events: 26 $ So we have 26 events, so lets use --seek_bytes to start reading from the offset for the first event and ask for those 26 events to be decoded, and adding the decimal representation for the type, so that we can match it up with 'perf report -D' output, we see: $ pahole --seek_bytes=0x130 -C 'perf_event_header(sizeof=size)' --count 26 ~/bin/perf < perf.data { .type = 0x4f (79), .misc = 0 (0), .size = 0x20 (32), }, { .type = 0x49 (73), .misc = 0 (0), .size = 0x28 (40), }, { .type = 0x4a (74), .misc = 0 (0), .size = 0x20 (32), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x28 (40), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x30 (48), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x30 (48), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x80 (128), }, { .type = 0x13 (19), .misc = 0 (0), .size = 0x58 (88), }, { .type = 0x3 (3), .misc = 0 (0), .size = 0x28 (40), }, { .type = 0x3 (3), .misc = 0x2000 (8192), .size = 0x28 (40), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x68 (104), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x70 (112), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x60 (96), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4001 (16385), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0xa (10), .misc = 0x2 (2), .size = 0x70 (112), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0x9 (9), .misc = 0x4002 (16386), .size = 0x28 (40), }, { .type = 0x4 (4), .misc = 0 (0), .size = 0x30 (48), }, { .type = 0x44 (68), .misc = 0 (0), .size = 0x8 (8), }, Which matches the output from 'perf report -D': $ perf report -D | grep ^0x 0x130 [0x20]: event: 79 0x150 [0x28]: event: 73 0x178 [0x20]: event: 74 0x198 [0x28]: event: 19 0x1c0 [0x30]: event: 19 0x1f0 [0x30]: event: 19 0x220 [0x80]: event: 19 0x2a0 [0x58]: event: 19 0x2f8 [0x28]: event: 3 0x6b0 [0x8]: event: 68 0x320 [0x28]: event: 3 0x348 [0x68]: event: 10 0x3b0 [0x70]: event: 10 0x420 [0x60]: event: 10 0x480 [0x28]: event: 9 0x4a8 [0x28]: event: 9 0x4d0 [0x28]: event: 9 0x4f8 [0x28]: event: 9 0x520 [0x28]: event: 9 0x548 [0x28]: event: 9 0x570 [0x28]: event: 9 0x598 [0x28]: event: 9 0x5c0 [0x70]: event: 10 0x630 [0x28]: event: 9 0x658 [0x28]: event: 9 0x680 [0x30]: event: 4 $ There is only one "reordering" as pahole is decoding this in the on-disk order, while 'perf report -D' knows about the timestamps encoded as perf those events perf_event_attr->sample_type and orders those events before printing. So we're on our way to a universal type (BTF, CTF, DWARF, others) dumper, now I need a name for this tool, 'spretty' comes to mind, but, humm, the urban dict seems to know about that already ;-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-13 18:25:06 +02:00
/*
* pahole -C 'perf_event_header(sizeof=size,typeid=type,enum2type=perf_event_type)
*
* So that it gets the 'type' field as the type id, look this
* up in the 'enum perf_event_type' and find the type to cast the
* whole shebang, i.e.:
*
$ pahole ~/bin/perf -C 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 * /
};
$
enum perf_event_type {
PERF_RECORD_MMAP = 1,
PERF_RECORD_LOST = 2,
PERF_RECORD_COMM = 3,
PERF_RECORD_EXIT = 4,
<SNIP>
}
* So from the type field get the lookup into the enum and from the result, look
* for a type with that name as-is or in lower case, which will produce, when type = 3:
$ pahole -C perf_record_comm ~/bin/perf
struct perf_record_comm {
struct perf_event_header header; / * 0 8 * /
__u32 pid; / * 8 4 * /
__u32 tid; / * 12 4 * /
char comm[16]; / * 16 16 * /
/ * size: 32, cachelines: 1, members: 4 * /
/ * last cacheline: 32 bytes * /
};
$
*/
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
struct cu *real_type_cu = cu;
struct tag *real_type = tag__real_type(type, &real_type_cu, instance);
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
if (real_type == NULL)
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
real_type = type;
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (global_verbose) {
printed += fprintf(fp, "// type=%s, offset=%#" PRIx64 ", sizeof=%d", type__name(tag__type(type), cu), record_offset, _sizeof);
if (real_sizeof != _sizeof)
printed += fprintf(fp, ", real_sizeof=%d\n", real_sizeof);
else
printed += fprintf(fp, "\n");
}
printed += tag__fprintf_value(real_type, real_type_cu, instance, real_sizeof, fp);
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
printed += fprintf(fp, ",\n");
if (conf.count && ++count == conf.count)
break;
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-07-20 14:27:10 +02:00
next_record:
if (read_bytes >= size_bytes)
break;
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
record_offset = ftell(stdin);
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
}
}
pahole: Make 'type' + 'type_enum' select a type to cast a variable sized record There is this usecase where a header member selects how to interpret the bytes after the header as some other type, and when this is coupled with an enum that have as its entries the names of the structs that decode thsoe types, we should take advantage of it, for sure. That is the case with, hopefully, many on-disk formats, and probably even with things that never touch disks. It is mostly the case with perf.data files, where we have as the fixed header part this, and since it is a kernel ABI, lets remember that Linux kernels comes with BTF for all its types, right? Ok, so lets use the super concise form: $ pahole perf_event_header struct perf_event_header { __u32 type; /* 0 4 */ __u16 misc; /* 4 2 */ __u16 size; /* 6 2 */ /* size: 8, cachelines: 1, members: 3 */ /* last cacheline: 8 bytes */ }; $ See that 'type' field? Now it is as interesting as that 'size' one (remember, that is what says how really sized is a record), so, I know that in perf land, that type is associated with this enum: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ See? Now lets try to see if the perf developers used some sensible naming for the structs representing the payload associated with those perf_event_header types, and now we need to use whatever debuginfo is in the perf tool binary when built with -g, which is DWARF, since the kernel was not so nice and didn't provide types for those records: $ pahole perf_event_type enum perf_event_type { PERF_RECORD_MMAP = 1, PERF_RECORD_LOST = 2, PERF_RECORD_COMM = 3, PERF_RECORD_EXIT = 4, PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, PERF_RECORD_FORK = 7, PERF_RECORD_READ = 8, PERF_RECORD_SAMPLE = 9, PERF_RECORD_MMAP2 = 10, PERF_RECORD_AUX = 11, PERF_RECORD_ITRACE_START = 12, PERF_RECORD_LOST_SAMPLES = 13, PERF_RECORD_SWITCH = 14, PERF_RECORD_SWITCH_CPU_WIDE = 15, PERF_RECORD_NAMESPACES = 16, PERF_RECORD_KSYMBOL = 17, PERF_RECORD_BPF_EVENT = 18, PERF_RECORD_CGROUP = 19, PERF_RECORD_MAX = 20, }; $ pahole ~/bin/perf -C perf_record_mmap struct perf_record_mmap { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ char filename[4096]; /* 40 4096 */ /* size: 4136, cachelines: 65, members: 7 */ /* last cacheline: 40 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost struct perf_record_lost { struct perf_event_header header; /* 0 8 */ __u64 id; /* 8 8 */ __u64 lost; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ }; $ pahole ~/bin/perf -C perf_record_comm struct perf_record_comm { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ char comm[16]; /* 16 16 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exit $ pahole ~/bin/perf -C perf_record_throttle struct perf_record_throttle { struct perf_event_header header; /* 0 8 */ __u64 time; /* 8 8 */ __u64 id; /* 16 8 */ __u64 stream_id; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_unthrottle $ pahole ~/bin/perf -C perf_record_fork struct perf_record_fork { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 ppid; /* 12 4 */ __u32 tid; /* 16 4 */ __u32 ptid; /* 20 4 */ __u64 time; /* 24 8 */ /* size: 32, cachelines: 1, members: 6 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_exec $ pahole ~/bin/perf -C perf_record_read struct perf_record_read { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 value; /* 16 8 */ __u64 time_enabled; /* 24 8 */ __u64 time_running; /* 32 8 */ __u64 id; /* 40 8 */ /* size: 48, cachelines: 1, members: 7 */ /* last cacheline: 48 bytes */ }; $ pahole ~/bin/perf -C perf_record_sample struct perf_record_sample { struct perf_event_header header; /* 0 8 */ __u64 array[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_mmap2 struct perf_record_mmap2 { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ __u64 start; /* 16 8 */ __u64 len; /* 24 8 */ __u64 pgoff; /* 32 8 */ __u32 maj; /* 40 4 */ __u32 min; /* 44 4 */ __u64 ino; /* 48 8 */ __u64 ino_generation; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prot; /* 64 4 */ __u32 flags; /* 68 4 */ char filename[4096]; /* 72 4096 */ /* size: 4168, cachelines: 66, members: 13 */ /* last cacheline: 8 bytes */ }; $ pahole ~/bin/perf -C perf_record_aux struct perf_record_aux { struct perf_event_header header; /* 0 8 */ __u64 aux_offset; /* 8 8 */ __u64 aux_size; /* 16 8 */ __u64 flags; /* 24 8 */ /* size: 32, cachelines: 1, members: 4 */ /* last cacheline: 32 bytes */ }; $ pahole ~/bin/perf -C perf_record_itrace_start struct perf_record_itrace_start { struct perf_event_header header; /* 0 8 */ __u32 pid; /* 8 4 */ __u32 tid; /* 12 4 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ pahole ~/bin/perf -C perf_record_lost_samples struct perf_record_lost_samples { struct perf_event_header header; /* 0 8 */ __u64 lost; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* last cacheline: 16 bytes */ }; $ And so on. So most can be obtained this way, i.e. casts based on a type selector present on the common part. $ pahole ~/bin/perf --seek_bytes=0x130 -C 'perf_event_header(sizeof=size,type=type,type_enum=perf_event_type)' --count 26 < perf.data { .type = 0x4f, .misc = 0, .size = 0x20, }, { .type = 0x49, .misc = 0, .size = 0x28, }, { .type = 0x4a, .misc = 0, .size = 0x20, }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .id = 0x1, .path = "/", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x611, .path = "/system.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x30 0x00, .id = 0x8, .path = "/machine.slice", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x80 0x00, .id = 0x1e94, .path = "/machine.slice/libpod-42be8e8d4eb9d22405845005f0d04ea398548dccc934a150fbaa3c1f1f9492c2.scope", }, { .header = 0x13 0x00 0x00 0x00 0x00 0x00 0x58 0x00, .id = 0xd, .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x00 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "perf", }, { .header = 0x03 0x00 0x00 0x00 0x00 0x20 0x28 0x00, .pid = 0x7e50, .tid = 0x7e50, .comm = "sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x68 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x55ea2a865000, .len = 0x4000, .pgoff = 0x2000, .maj = 0xfd, .min = 0, .ino = 0x1e0526, .ino_generation = 0xc97273c3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/bin/sleep", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f53040c7000, .len = 0x20000, .pgoff = 0x1000, .maj = 0xfd, .min = 0, .ino = 0x1e1faf, .ino_generation = 0x83ee2ed3, .prot = 0x5, .flags = 0x1802, .filename = "/usr/lib64/ld-2.29.so", }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x60 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7fffd8b69000, .len = 0x2000, .pgoff = 0, .maj = 0, .min = 0, .ino = 0, .ino_generation = 0, .prot = 0x5, .flags = 0x1002, .filename = "[vdso]", }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x01 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x0a 0x00 0x00 0x00 0x02 0x00 0x70 0x00, .pid = 0x7e50, .tid = 0x7e50, .start = 0x7f5303efe000, .len = 0x14d000, .pgoff = 0x22000, .maj = 0xfd, .min = 0, .ino = 0x1e028a, .ino_generation = 0xd5947787, .prot = 0x5, .flags = 0x1002, .filename = "/usr/lib64/libc-2.29.so", }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .header = 0x09 0x00 0x00 0x00 0x02 0x40 0x28 0x00, .array = { }, }, { .type = PERF_RECORD_EXIT, .misc = 0, .size = 0x30, }, { .type = 0x44, .misc = 0, .size = 0x8, }, $ So now we go and get the whole thing in this command line: Ok, we lost some stuff along the way, namely we need to do recursive pretty printing, the .header field is being now defaulted to the primitive hex dumper that is the last resort when pahole wasn't instructed to dtrt... Tomorrow, probably :-) But I'll point this to global kernel variables, that now we know the types, --expand_pointers is there for just showing the structure, with support for files, in addition to reading from stdin, we'll be able to get things from random places, not just sequential, etc. Addictive :-\ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-15 02:49:34 +02:00
out:
free(instance);
pahole: Hex dump a type from stdio when it isn't a tty For now the only aspect of the type that is considered is its size, so one line each sizeof(type) will be hex dumped, e.g.: $ objcopy -O binary --only-section=.altinstructions ~/git/build/v5.7-rc2+/vmlinux .altinstructions $ ls -la .altinstructions -rwxrwxr-x. 1 acme acme 18551 Jun 24 09:09 .altinstructions $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux struct alt_instr { s32 instr_offset; /* 0 4 */ s32 repl_offset; /* 4 4 */ u16 cpuid; /* 8 2 */ u8 instrlen; /* 10 1 */ u8 replacementlen; /* 11 1 */ u8 padlen; /* 12 1 */ /* size: 13, cachelines: 1, members: 6 */ /* last cacheline: 13 bytes */ } __attribute__((__packed__)); $ pahole -C alt_instr ~/git/build/v5.7-rc2+/vmlinux < .altinstructions | head 0x90 0xe6 0xe0 0xff 0x73 0x48 0x00 0x00 0x70 0x00 0x05 0x05 0x00, 0x83 0xe6 0xe0 0xff 0x6b 0x48 0x00 0x00 0x29 0x01 0x05 0x05 0x00, 0x2a 0x72 0xc5 0xfe 0x63 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x22 0x72 0xc5 0xfe 0x56 0x48 0x00 0x00 0x91 0x00 0x05 0x05 0x05, 0x0c 0x73 0xc5 0xfe 0x4e 0x48 0x00 0x00 0xeb 0x00 0x02 0x00 0x00, 0x04 0x73 0xc5 0xfe 0x41 0x48 0x00 0x00 0x91 0x00 0x02 0x00 0x00, 0x72 0x73 0xc5 0xfe 0x34 0x48 0x00 0x00 0xf3 0x00 0x2b 0x2b 0x29, 0xca 0x73 0xc5 0xfe 0x52 0x48 0x00 0x00 0xec 0x00 0x18 0x18 0x16, 0xbd 0x73 0xc5 0xfe 0x5d 0x48 0x00 0x00 0xed 0x00 0x18 0x05 0x16, 0xd3 0x7a 0xc5 0xfe 0x55 0x48 0x00 0x00 0x34 0x01 0x03 0x03 0x03, $ Suggested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-06-24 14:08:17 +02:00
return printed;
}
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-07-17 17:35:26 +02:00
static int class_member_filter__parse(struct class_member_filter *filter, struct type *type, struct cu *cu, char *sfilter)
{
const char *member_name = sfilter;
char *sep = strstr(sfilter, "==");
if (!sep) {
if (global_verbose)
fprintf(stderr, "No supported operator ('==' so far) found in filter '%s'\n", sfilter);
return -1;
}
char *value = sep + 2, *s = sep;
while (isspace(*--s))
if (s == sfilter) {
if (global_verbose)
fprintf(stderr, "No left operand (struct field) found in filter '%s'\n", sfilter);
return -1; // nothing before ==
}
char before = s[1];
s[1] = '\0';
filter->left = type__find_member_by_name(type, cu, member_name);
if (!filter->left) {
if (global_verbose)
fprintf(stderr, "The '%s' member wasn't found in '%s'\n", member_name, type__name(type, cu));
s[1] = before;
return -1;
}
s[1] = before;
while (isspace(*value))
if (*++value == '\0') {
if (global_verbose)
fprintf(stderr, "The '%s' member was asked without a value to filter '%s'\n", member_name, type__name(type, cu));
return -1; // no value
}
char *endptr;
filter->right = strtoll(value, &endptr, 0);
if (endptr > value && (*endptr == '\0' || isspace(*endptr)))
return 0;
// If t he filter member is the 'type=' one:
if (list_empty(&type->type_enum) || type->type_member != filter->left) {
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-07-17 17:35:26 +02:00
if (global_verbose)
fprintf(stderr, "Symbolic right operand in '%s' but no way to resolve it to a number (type= + type_enum= so far)\n", sfilter);
return -1;
}
enumerations__calc_prefix(&type->type_enum);
int64_t enumerator_value = enumerations__lookup_enumerator(&type->type_enum, value);
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-07-17 17:35:26 +02:00
if (enumerator_value < 0) {
if (global_verbose)
fprintf(stderr, "Couldn't resolve right operand ('%s') in '%s' with the specified 'type=%s' and type_enum' \n",
value, sfilter, class_member__name(type->type_member, cu));
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-07-17 17:35:26 +02:00
return -1;
}
filter->right = enumerator_value;
return 0;
}
static struct class_member_filter *class_member_filter__new(struct type *type, struct cu *cu, char *sfilter)
{
struct class_member_filter *filter = malloc(sizeof(*filter));
if (filter && class_member_filter__parse(filter, type, cu, sfilter)) {
free(filter);
filter = NULL;
}
return filter;
}
static struct prototype *prototype__new(const char *expression)
{
struct prototype *prototype = zalloc(sizeof(*prototype) + strlen(expression) + 1);
if (prototype == NULL)
goto out_enomem;
strcpy(prototype->name, expression);
const char *name = prototype->name;
prototype->nr_args = 0;
char *args_open = strchr(name, '(');
if (!args_open)
goto out;
char *args_close = strchr(args_open, ')');
if (args_close == NULL)
goto out_no_closing_parens;
char *args = args_open;
*args++ = *args_close = '\0';
while (isspace(*args))
++args;
if (args == args_close)
goto out; // empty args, just ignore the parens, i.e. 'foo()'
next_arg:
{
char *comma = strchr(args, ','), *value;
if (comma)
*comma = '\0';
char *assign = strchr(args, '=');
if (assign == NULL) {
if (strcmp(args, "sizeof") == 0) {
value = "size";
goto do_sizeof;
} else if (strcmp(args, "type") == 0) {
value = "type";
goto do_type;
}
goto out_missing_assign;
}
// accept foo==bar as filter=foo==bar
if (assign[1] == '=') {
value = args;
goto do_filter;
}
*assign = 0;
value = assign + 1;
while (isspace(*value))
++value;
if (value == args_close)
goto out_missing_value;
if (strcmp(args, "sizeof") == 0) {
do_sizeof:
if (global_verbose)
printf("pahole: sizeof_operator for '%s' is '%s'\n", name, value);
prototype->size = value;
} else if (strcmp(args, "type") == 0) {
do_type:
if (global_verbose)
printf("pahole: type member for '%s' is '%s'\n", name, value);
prototype->type = value;
} else if (strcmp(args, "type_enum") == 0) {
if (global_verbose)
printf("pahole: type enum for '%s' is '%s'\n", name, value);
prototype->type_enum = value;
} else if (strcmp(args, "filter") == 0) {
do_filter:
if (global_verbose)
printf("pahole: filter for '%s' is '%s'\n", name, value);
prototype->filter = value;
} else if (strcmp(args, "range") == 0) {
if (global_verbose)
printf("pahole: range for '%s' is '%s'\n", name, value);
prototype->range = value;
} else
goto out_invalid_arg;
++prototype->nr_args;
if (comma) {
args = comma + 1;
goto next_arg;
}
}
out:
return prototype;
out_enomem:
fprintf(stderr, "pahole: not enough memory for '%s'\n", expression);
goto out;
out_invalid_arg:
fprintf(stderr, "pahole: invalid arg '%s' in '%s' (known args: sizeof=member, type=member, type_enum=enum)\n", args, expression);
goto out_free;
out_missing_value:
fprintf(stderr, "pahole: invalid, missing value in '%s'\n", expression);
goto out_free;
out_no_closing_parens:
fprintf(stderr, "pahole: invalid, no closing parens in '%s'\n", expression);
goto out_free;
out_missing_assign:
fprintf(stderr, "pahole: invalid, missing '=' in '%s'\n", args);
goto out_free;
out_free:
free(prototype);
return NULL;
}
#ifdef DEBUG_CHECK_LEAKS
static void prototype__delete(struct prototype *prototype)
{
if (prototype) {
memset(prototype, 0xff, sizeof(*prototype));
free(prototype);
}
}
#endif
static struct tag_cu_node *tag_cu_node__new(struct tag *tag, struct cu *cu)
{
struct tag_cu_node *tc = malloc(sizeof(*tc));
if (tc) {
tc->tc.tag = tag;
tc->tc.cu = cu;
}
return tc;
}
static int type__add_type_enum(struct type *type, struct tag *type_enum, struct cu *cu)
{
struct tag_cu_node *tc = tag_cu_node__new(type_enum, cu);
if (!tc)
return -1;
list_add_tail(&tc->node, &type->type_enum);
return 0;
}
static int type__find_type_enum(struct type *type, struct cu *cu, const char *type_enum)
{
struct tag *te = cu__find_enumeration_by_name(cu, type_enum, NULL);
if (te)
return type__add_type_enum(type, te, cu);
pahole: Support multiple enums in type_enum= See the pahole man page: Some of the records are not found in 'type_enum=perf_event_type' so some of the records don't get converted to a type that fully shows its contents. For perf we know that those are in another enumeration, 'enum perf_user_event_type', so, for these cases, we can create a 'virtual enum', i.e. the sum of two enums and then get all those entries decoded and properly casted, first few records with just 'enum perf_event_type': $ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type)' --count 4 < perf.data { .type = 79, .misc = 0, .size = 32, }, { .type = 73, .misc = 0, .size = 40, }, { .type = 74, .misc = 0, .size = 32, }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 40, }, .id = 1, .path = "/", }, $ Now with both enumerations, i.e. with 'type_enum=perf_event_type+perf_user_event_type': $ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --count 5 < perf.data { .header = { .type = PERF_RECORD_TIME_CONV, .misc = 0, .size = 32, }, .time_shift = 31, .time_mult = 1016803377, .time_zero = 435759009518382, }, { .header = { .type = PERF_RECORD_THREAD_MAP, .misc = 0, .size = 40, }, .nr = 1, .entries = 0x50 0x7e 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00, }, { .header = { .type = PERF_RECORD_CPU_MAP, .misc = 0, .size = 32, }, .data = { .type = 1, .data = "", }, }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 40, }, .id = 1, .path = "/", }, { .header = { .type = PERF_RECORD_CGROUP, .misc = 0, .size = 48, }, .id = 1553, .path = "/system.slice", }, $ And since the fun never ends, this needs to be properly supported (arrays of structs): $ pahole ~/bin/perf -C perf_record_thread_map_entry struct perf_record_thread_map_entry { __u64 pid; /* 0 8 */ char comm[16]; /* 8 16 */ /* size: 24, cachelines: 1, members: 2 */ /* last cacheline: 24 bytes */ }; $ that 'nr' field: $ pahole ~/bin/perf -C perf_record_thread_map struct perf_record_thread_map { struct perf_event_header header; /* 0 8 */ __u64 nr; /* 8 8 */ struct perf_record_thread_map_entry entries[]; /* 16 0 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ So probably we need something like a file with types and its pretty printing details, with one for 'struct perf_record_thread_map': perf_record_thread_map(entries.nr_entries=$nr) Meaning: the perf_record_thread_map 'entries' has a number of sizeof(typeof entries) that is defined in the perf_record_thread_map 'nr' member. Everything starting with a '$' needs to be evaluated, like the '$header.*' we already have, since there is no 'namespace selector' ($header. in the header case) this means: the current record. So, when pretty printing a record that has such type (perf_record_thread_map) this has to be taken into account, and validated by the containing 'struct perf_event_header->size' field. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-27 17:42:43 +02:00
// Now look at a 'virtual enum', i.e. the concatenation of multiple enums
char *sep = strchr(type_enum, '+');
if (!sep)
return -1;
char *type_enums = strdup(type_enum);
if (!type_enums)
return -1;
int ret = -1;
sep = type_enums + (sep - type_enum);
type_enum = type_enums;
*sep = '\0';
while (1) {
te = cu__find_enumeration_by_name(cu, type_enum, NULL);
if (!te)
goto out;
ret = type__add_type_enum(type, te, cu);
if (ret)
goto out;
if (sep == NULL)
break;
type_enum = sep + 1;
sep = strchr(type_enum, '+');
}
ret = 0;
out:
free(type_enums);
return ret;
}
static struct type_instance *header;
static enum load_steal_kind pahole_stealer(struct cu *cu,
struct conf_load *conf_load __unused)
{
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
int ret = LSK__DELETE;
if (!cu__filter(cu))
goto filter_it;
if (btf_encode) {
if (cu__encode_btf(cu, global_verbose, btf_encode_force,
skip_encoding_btf_vars)) {
fprintf(stderr, "Encountered error while encoding BTF.\n");
exit(1);
}
return LSK__DELETE;
}
if (ctf_encode) {
cu__encode_ctf(cu, global_verbose);
/*
* We still have to get the type signature code merged to eliminate
* dups, reference another CTF file, etc, so for now just encode the
* first cu that is let thru by cu__filter.
*/
goto dump_and_stop;
}
if (class_name == NULL) {
if (stats_formatter == nr_methods_formatter) {
cu__account_nr_methods(cu);
goto dump_it;
}
if (word_size != 0)
cu_fixup_word_size_iterator(cu);
print_classes(cu);
goto dump_it;
}
[PAHOLE]: Use cus__loadfl, i.e. libdwfl Now we have: [acme@filo pahole]$ pahole --help Usage: pahole [OPTION...] [FILE] {[CLASS]} -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE -D, --decl_exclude=PREFIX exclude classes declared in files with PREFIX -E, --expand_types expand class members -H, --holes=NR_HOLES show only structs at least NR_HOLES holes -m, --nr_methods show number of methods -n, --nr_members show number of members -N, --class_name_len show size of classes -P, --packable show only structs that has holes that can be packed -R, --reorganize reorg struct trying to kill holes -s, --sizes show size of classes -S, --show_reorg_steps show the struct layout at each reorganization step -t, --nr_definitions show how many times struct was defined -V, --verbose be verbose -x, --exclude=PREFIX exclude PREFIXed classes -X, --cu_exclude=PREFIX exclude PREFIXed compilation units Input Selection: --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID -?, --help Give this help list --usage Give a short usage message Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
2007-03-30 18:25:51 +02:00
if (header == NULL && conf.header_type) {
header = type_instance__new(tag__type(cu__find_type_by_name(cu, conf.header_type, false, NULL)), cu);
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (header)
ret = LSK__KEEPIT;
}
bool include_decls = find_pointers_in_structs != 0 || stats_formatter == nr_methods_formatter;
struct prototype *prototype, *n;
list_for_each_entry_safe(prototype, n, &class_names, node) {
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
/* See if we already found it */
if (prototype->class) {
if (prototype->type_enum && !prototype->type_enum_resolved)
prototype->type_enum_resolved = type__find_type_enum(tag__type(prototype->class), cu, prototype->type_enum) == 0;
continue;
}
static type_id_t class_id;
struct tag *class = cu__find_type_by_name(cu, prototype->name, include_decls, &class_id);
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
if (class == NULL)
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
return ret; // couldn't find that class name in this CU, continue to the next one.
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
if (prototype->nr_args != 0 && !tag__is_struct(class)) {
fprintf(stderr, "pahole: attributes are only supported with 'class' and 'struct' types\n");
goto dump_and_stop;
}
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
struct type *type = tag__type(class);
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-07-20 13:25:45 +02:00
if (prototype->size) {
type->sizeof_member = type__find_member_by_name(type, cu, prototype->size);
if (type->sizeof_member == NULL) {
fprintf(stderr, "pahole: the sizeof member '%s' wasn't found in the '%s' type\n",
prototype->size, prototype->name);
goto dump_and_stop;
}
}
if (prototype->type) {
type->type_member = type__find_member_by_name(type, cu, prototype->type);
if (type->type_member == NULL) {
fprintf(stderr, "pahole: the type member '%s' wasn't found in the '%s' type\n",
prototype->type, prototype->name);
goto dump_and_stop;
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
}
}
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
if (prototype->type_enum) {
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
prototype->type_enum_resolved = type__find_type_enum(type, cu, prototype->type_enum) == 0;
}
if (prototype->filter) {
type->filter = class_member_filter__new(type, cu, prototype->filter);
if (type->filter == NULL) {
fprintf(stderr, "pahole: invalid filter '%s' for '%s'\n",
prototype->filter, prototype->name);
goto dump_and_stop;
}
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
}
if (class == NULL) {
if (strcmp(prototype->name, "void"))
pahole: Allow simple parser for arguments to classes For now we need the simplest parser to specify a field that should be used as the sizeof operator in structs that have a header for a variable sized record, i.e., perf.data file and its 'struct perf_event_header' + 'struct perf_record_{mmap,comm,exec,etc}': $ perf report -D <SNIP> 0x348 [0x68]: event: 10 . . ... raw event: size 104 bytes . 0000: 0a 00 00 00 02 00 68 00 50 7e 00 00 50 7e 00 00 ......h.P~..P~.. . 0010: 00 50 86 2a ea 55 00 00 00 40 00 00 00 00 00 00 .P.*<EA>U...@...... . 0020: 00 20 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 . ......<FD>....... . 0030: 26 05 1e 00 00 00 00 00 c3 73 72 c9 00 00 00 00 &.......<C3>sr<C9>.... . 0040: 05 00 00 00 02 18 00 00 2f 75 73 72 2f 62 69 6e ......../usr/bin . 0050: 2f 73 6c 65 65 70 00 00 50 7e 00 00 50 7e 00 00 /sleep..P~..P~.. . 0060: 31 2d 2a c3 aa 90 01 00 1-*<C3><AA>... 440538069871921 0x348 [0x68]: PERF_RECORD_MMAP2 32336/32336: [0x55ea2a865000(0x4000) @ 0x2000 fd:00 1967398 3379721155]: r-xp /usr/bin/sleep <SNIP> $ pahole -C 'perf_event_header' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Empty list of args is ok, just chop that off and do the same as above, without (): $ pahole -C 'perf_event_header()' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Boolean args (without a value) not yet supported: $ pahole -C 'perf_event_header(sizeof)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing '=' in 'perf_event_header(sizeof)' $ A value is required: $ pahole -C 'perf_event_header(sizeof=)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data pahole: invalid, missing value in 'perf_event_header(sizeof=)' $ Valid form: $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes 0x348 --count 1 ~/bin/perf < perf.data { .type = 0xa, .misc = 0x2, .size = 0x68, }, $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-09 14:18:39 +02:00
continue;
class_id = 0;
}
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (!isatty(0)) {
prototype->class = class;
prototype->cu = cu;
continue;
}
/*
* Ok, found it, so remove from the list to avoid printing it
* twice, in another CU.
*/
list_del_init(&prototype->node);
if (defined_in) {
puts(cu->name);
goto dump_it;
}
if (class)
class__find_holes(tag__class(class));
if (reorganize) {
if (class && tag__is_struct(class))
do_reorg(class, cu);
} else if (find_containers)
print_containers(cu, class_id, 0);
else if (find_pointers_in_structs)
print_structs_with_pointer_to(cu, class_id);
else if (class) {
/*
* We don't need to print it for every compile unit
* but the previous options need
*/
tag__fprintf(class, cu, &conf, stdout);
putchar('\n');
}
}
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
// If we got here with pretty printing is because we have everything solved except for type_enum
if (!isatty(0)) {
// Check if we need to continue loading CUs to get those type_enum= resolved
list_for_each_entry(prototype, &class_names, node) {
if (prototype->type_enum && !prototype->type_enum_resolved)
return LSK__KEEPIT;
}
// All set, pretty print it!
list_for_each_entry_safe(prototype, n, &class_names, node) {
list_del_init(&prototype->node);
if (prototype__stdio_fprintf_value(prototype, header, stdout) < 0)
break;
}
return LSK__STOP_LOADING;
}
/*
* If we found all the entries in --class_name, stop
*/
if (list_empty(&class_names)) {
dump_and_stop:
ret = LSK__STOP_LOADING;
}
dump_it:
if (first_obj_only)
ret = LSK__STOP_LOADING;
filter_it:
return ret;
}
static int prototypes__add(struct list_head *prototypes, const char *entry)
{
struct prototype *prototype = prototype__new(entry);
if (prototype == NULL)
return -ENOMEM;
list_add_tail(&prototype->node, prototypes);
return 0;
}
#ifdef DEBUG_CHECK_LEAKS
static void prototypes__delete(struct list_head *prototypes)
{
struct prototype *prototype, *n;
list_for_each_entry_safe(prototype, n, prototypes, node) {
list_del_init(&prototype->node);
prototype__delete(prototype);
}
}
#endif
static int prototypes__load(struct list_head *prototypes, const char *filename)
{
char entry[1024];
int err = -1;
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return -1;
while (fgets(entry, sizeof(entry), fp) != NULL) {
const size_t len = strlen(entry);
if (len == 0)
continue;
entry[len - 1] = '\0';
if (prototypes__add(&class_names, entry))
goto out;
}
err = 0;
out:
fclose(fp);
return err;
}
static int add_class_name_entry(const char *s)
{
if (strncmp(s, "file://", 7) == 0) {
if (prototypes__load(&class_names, s + 7))
return -1;
} else switch (prototypes__add(&class_names, s)) {
case -EEXIST:
if (global_verbose)
fprintf(stderr,
"pahole: %s dup in -C, ignoring\n", s);
break;
case -ENOMEM:
return -1;
}
return 0;
}
static int populate_class_names(void)
{
char *s = strdup(class_name), *sep;
char *sdup = s, *end = s + strlen(s);
int ret = 0;
if (!s) {
fprintf(stderr, "Not enough memory for populating class names ('%s')\n", class_name);
return -1;
}
/*
* Commas inside parameters shouldn't be considered, as those don't
* separate classes, but arguments to a particular class hack a simple
* parser, but really this will end up needing lex/yacc...
*/
while ((sep = strchr(s, ',')) != NULL) {
char *parens = strchr(s, '(');
// perf_event_header(sizeof=size),a
if (parens && parens < sep) {
char *close_parens = strchr(parens, ')');
ret = -1;
if (!close_parens) {
fprintf(stderr, "Unterminated '(' in '%s'\n", class_name);
fprintf(stderr, " %*.s^\n", (int)(parens - sdup), "");
goto out_free;
}
if (close_parens > sep)
sep = close_parens + 1;
}
*sep = '\0';
ret = add_class_name_entry(s);
if (ret)
goto out_free;
while (isspace(*sep))
++sep;
if (sep == end)
goto out_free;
s = sep + 1;
}
ret = add_class_name_entry(s);
out_free:
free(sdup);
return ret;
}
int main(int argc, char *argv[])
{
int err, remaining, rc = EXIT_FAILURE;
pahole: Honour --hex_fmt when pretty printing I.e. by default now we print in decimal, as we do for printing just the struct definitions, use --hex to ask for hexadecimal formatting, i.e.: $ perf evlist -v cycles:u: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, precise_ip: 3, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ That is, the perf_event_attr for the only event in the perf.data file (default file processed by perf tools such as 'perf evlist'), now with pahole: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_event_attr(sizeof=size,type=type,type_enum=perf_type_id)' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' --count=1 < perf.data | grep -v '= 0,' { .type = PERF_TYPE_HARDWARE, .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, $ --count=1 was used because what is in that $header.attrs.offset/size range are instances of: $ pahole ~/bin/perf -C perf_file_attr struct perf_file_attr { struct perf_event_attr attr; /* 0 120 */ /* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */ struct perf_file_section ids; /* 120 16 */ /* size: 136, cachelines: 3, members: 2 */ /* last cacheline: 8 bytes */ }; $ We can drop --count=1 if we use the right type, 'struct perf_file_attr': $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr' \ --seek_bytes='$header.attrs.offset' \ --size_bytes='$header.attrs.size' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 263, .read_format = 4, .disabled = 1, .inherit = 1, .exclude_kernel = 1, .mmap = 1, .comm = 1, .freq = 1, .enable_on_exec = 1, .task = 1, .precise_ip = 3, .sample_id_all = 1, .exclude_guest = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 104, .size = 64, }, }, We lose the ability to use type + type_enum, as we still don't support specifying fields in fields for those class attributes, i.e. we need to support: -C 'perf_event_attr(sizeof=attr.size,type=attr.type,type_enum=perf_type_id)' Which is easy enough, we already support that for $header.data.offset, for instance, will generalise that and reuse with class attributes. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-22 14:06:48 +02:00
if (!isatty(0))
conf.hex_fmt = 0;
pahole: Do not require a class name to operate without a file name Since we default to operating on the running kernel data structures, we should make the default to, with no options passed, to pretty print all the running kernel data structures, or do what was asked in terms of number of members, size of structs, etc, i.e.: [root@quaco ~]# pahole --help |& head Usage: pahole [OPTION...] FILE -a, --anon_include include anonymous classes -A, --nested_anon_include include nested (inside other structs) anonymous classes -B, --bit_holes=NR_HOLES Show only structs at least NR_HOLES bit holes -c, --cacheline_size=SIZE set cacheline size to SIZE --classes_as_structs Use 'struct' when printing classes -C, --class_name=CLASS_NAME Show just this class -d, --recursive recursive mode, affects several other flags [root@quaco ~]# Continues working as before, but if you do: pahole It will work just as if you did: pahole vmlinux and that vmlinux file is the running kernel vmlinux. And since the default now is to read BTF info, then it will do all its operations on /sys/kernel/btf/vmlinux, when present, i.e. want to know what are the fattest data structures in the running kernel: [root@quaco ~]# pahole -s | sort -k2 -nr | head cmp_data 290904 1 dec_data 274520 1 cpu_entry_area 217088 0 pglist_data 172928 4 saved_cmdlines_buffer 131104 1 debug_store_buffers 131072 0 hid_parser 110848 1 hid_local 110608 0 zonelist 81936 0 e820_table 64004 0 [root@quaco ~]# How many data structures in the running kernel vmlinux area embbed 'struct list_head'? [root@quaco ~]# pahole -i list_head | wc -l 260 [root@quaco ~]# Lets see some of those? [root@quaco ~]# pahole -C fsnotify_event struct fsnotify_event { struct list_head list; /* 0 16 */ struct inode * inode; /* 16 8 */ /* size: 24, cachelines: 1, members: 2 */ /* last cacheline: 24 bytes */ }; [root@quaco ~]# pahole -C audit_chunk struct audit_chunk { struct list_head hash; /* 0 16 */ long unsigned int key; /* 16 8 */ struct fsnotify_mark * mark; /* 24 8 */ struct list_head trees; /* 32 16 */ int count; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ atomic_long_t refs; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ struct callback_head head; /* 64 16 */ struct node owners[]; /* 80 0 */ /* size: 80, cachelines: 2, members: 8 */ /* sum members: 76, holes: 1, sum holes: 4 */ /* last cacheline: 16 bytes */ }; [root@quaco ~]# Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-01-06 17:53:37 +01:00
if (argp_parse(&pahole__argp, argc, argv, 0, &remaining, NULL)) {
argp_help(&pahole__argp, stderr, ARGP_HELP_SEE, argv[0]);
goto out;
}
if (print_numeric_version) {
dwarves_print_numeric_version(stdout);
return 0;
}
if (dwarves__init(cacheline_size)) {
fputs("pahole: insufficient memory\n", stderr);
goto out;
}
if (base_btf_file) {
base_btf = btf__parse(base_btf_file, NULL);
if (libbpf_get_error(base_btf)) {
fprintf(stderr, "Failed to parse base BTF '%s': %ld\n",
base_btf_file, libbpf_get_error(base_btf));
goto out;
}
if (!btf_encode && !ctf_encode) {
// Force "btf" since a btf_base is being informed
conf_load.format_path = "btf";
}
}
struct cus *cus = cus__new();
if (cus == NULL) {
fputs("pahole: insufficient memory\n", stderr);
goto out_dwarves_exit;
}
memset(tab, ' ', sizeof(tab) - 1);
conf_load.steal = pahole_stealer;
// Make 'pahole --header type < file' a shorter form of 'pahole -C type --count 1 < file'
if (conf.header_type && !class_name && !isatty(0)) {
conf.count = 1;
class_name = conf.header_type;
conf.header_type = 0; // so that we don't read it and then try to read the -C type
}
try_sole_arg_as_class_names:
if (class_name && populate_class_names())
goto out_dwarves_exit;
if (base_btf_file == NULL) {
const char *filename = argv[remaining];
if (filename &&
strstarts(filename, "/sys/kernel/btf/") &&
strstr(filename, "/vmlinux") == NULL) {
base_btf_file = "/sys/kernel/btf/vmlinux";
base_btf = btf__parse(base_btf_file, NULL);
if (libbpf_get_error(base_btf)) {
fprintf(stderr, "Failed to parse base BTF '%s': %ld\n",
base_btf_file, libbpf_get_error(base_btf));
goto out;
}
}
}
err = cus__load_files(cus, &conf_load, argv + remaining);
if (err != 0) {
pahole: Only try using a single file name as a type name if not encoding BTF or CTF Otherwise we end up trying to encode without any debug info and this causes a segfault: Before: $ pahole -J vmlinuz-5.9.0-rc6+ tag__check_id_drift: subroutine_type id drift, core_id: 1145, btf_type_id: 1143, type_id_off: 0 pahole: type 'vmlinuz-5.9.0-rc6+' not found libbpf: Unsupported BTF_KIND:0 btf_elf__encode: btf__new failed! free(): double free detected in tcache 2 Aborted (core dumped) $ The vmlinuz file doesn't contain any debugging info, fixing it we get: $ pahole -J vmlinuz-5.9.0-rc6+ pahole: vmlinuz-5.9.0-rc6+: No debugging information found $ If debugging info is available, it all works as before: Using /sys/kernel/btf/vmlinux $ ls -la /sys/kernel/btf/vmlinux -r--r--r--. 1 root root 3393761 Oct 1 09:50 /sys/kernel/btf/vmlinux $ pahole -E fw_cache_entry struct fw_cache_entry { struct list_head { struct list_head * next; /* 0 8 */ struct list_head * prev; /* 8 8 */ } list; /* 0 16 */ const char * name; /* 16 8 */ /* size: 24, cachelines: 1, members: 2 */ /* last cacheline: 24 bytes */ }; $ Or explicitely asking for DWARF, where it will find the appropriate vmlinux according to its buildid in /sys/kernel/notes: $ pahole -F dwarf pm_clock_entry struct pm_clock_entry { struct list_head node; /* 0 16 */ char * con_id; /* 16 8 */ struct clk * clk; /* 24 8 */ enum pce_status status; /* 32 4 */ /* size: 40, cachelines: 1, members: 4 */ /* padding: 4 */ /* last cacheline: 40 bytes */ }; $ pahole -F dwarf --expand_types pm_clock_entry struct pm_clock_entry { struct list_head { struct list_head * next; /* 0 8 */ struct list_head * prev; /* 8 8 */ } node; /* 0 16 */ char * con_id; /* 16 8 */ struct clk * clk; /* 24 8 */ enum pce_status status; /* 32 4 */ /* size: 40, cachelines: 1, members: 4 */ /* padding: 4 */ /* last cacheline: 40 bytes */ }; $ Reported-by: Kevin Sheldrake <Kevin.Sheldrake@microsoft.com> Acked-by: Toke Høiland-Jørgensen <toke@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-01 15:10:50 +02:00
if (class_name == NULL && !btf_encode && !ctf_encode) {
class_name = argv[remaining];
if (access(class_name, R_OK) == 0) {
fprintf(stderr, "pahole: file '%s' has no %s type information.\n",
class_name, conf_load.format_path ?: "supported");
goto out_dwarves_exit;
}
remaining = argc;
goto try_sole_arg_as_class_names;
}
cus__fprintf_load_files_err(cus, "pahole", argv + remaining, err, stderr);
goto out_cus_delete;
}
if (!list_empty(&class_names)) {
struct prototype *prototype;
list_for_each_entry(prototype, &class_names, node) {
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (prototype->class == NULL) {
fprintf(stderr, "pahole: type '%s' not found%s\n", prototype->name,
prototype->nr_args ? " or arguments not validated" : "");
break;
} else {
struct type *type = tag__type(prototype->class);
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (prototype->type && !type->type_member) {
fprintf(stderr, "pahole: member 'type=%s' not found in '%s' type\n",
prototype->type, prototype->name);
}
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (prototype->size && !type->sizeof_member) {
fprintf(stderr, "pahole: member 'sizeof=%s' not found in '%s' type\n",
prototype->size, prototype->name);
}
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (prototype->filter && !type->filter) {
fprintf(stderr, "pahole: filter 'filter=%s' couldn't be evaluated for '%s' type\n",
prototype->filter, prototype->name);
}
pahole: Support multiple types for pretty printing For now one has to specify them in the order they appear in the file, i.e. for perf.data files where we have: $ pahole --hex ~/bin/perf --header=perf_file_header < perf.data { .magic = 0x32454c4946524550, .size = 0x68, .attr_size = 0x88, .attrs = { .offset = 0x168, .size = 0x220, }, .data = { .offset = 0x388, .size = 0x306698, }, .event_types = { .offset = 0, .size = 0, }, .adds_features = { 0x16717ffc, 0, 0, 0 }, }, $ We need to ask for pretty printing the attrs then the data sections, as: $ pahole ~/bin/perf --header=perf_file_header \ -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data Notice that both types have the range= setting where in the header it should find the instances of its respective types, the result for this perf.data file: $ perf evlist instructions cycles cache-misses dummy:HG $ Those events have these attributes, which we'll match in the pahole output for the header 'attrs' range: $ perf evlist -v instructions: size: 120, config: 0x1, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cycles: size: 120, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 cache-misses: size: 120, config: 0x3, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1, exclude_guest: 1 dummy:HG: type: 1, size: 120, config: 0x9, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ID|CPU|PERIOD, read_format: ID, inherit: 1, mmap: 1, comm: 1, freq: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 $ To make it more compact lets remove zeroed fields using grep: $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' { .attr = { .size = 120, .config = 1, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 104, .size = 64, }, }, { .attr = { .size = 120, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 168, .size = 64, }, }, { .attr = { .size = 120, .config = 3, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .disabled = 1, .inherit = 1, .freq = 1, .sample_id_all = 1, .exclude_guest = 1, }, .ids = { .offset = 232, .size = 64, }, }, { .attr = { .type = 1, .size = 120, .config = 9, .sample_period = 4000, .sample_freq = 4000, .sample_type = 455, .read_format = 4, .inherit = 1, .mmap = 1, .comm = 1, .freq = 1, .task = 1, .sample_id_all = 1, .mmap2 = 1, .comm_exec = 1, .ksymbol = 1, .bpf_event = 1, }, .ids = { .offset = 296, .size = 64, }, }, { .header = { .type = PERF_RECORD_TIME_CONV, .size = 32, }, .time_shift = 31, .time_mult = 1016798081, .time_zero = 670877213069232, }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 96, }, .pid = -1, .start = -1929379840, .len = 14683553, .pgoff = -1929379840, .filename = "[kernel.kallsyms]_text", }, { .header = { .type = PERF_RECORD_MMAP, .misc = 1, .size = 136, }, .pid = -1, .start = -1072852992, .len = 139264, .filename = "/lib/modules/5.7.8-200.fc32.x86_64/kernel/fs/fuse/fuse.ko.xz", }, <SNIP> { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1927972873, 14267881360602, 671090228720656, 8685165, 7, 93802 }, }, { .header = { .type = PERF_RECORD_SAMPLE, .misc = 1, .size = 56, }, .array = { -1928098583, 0, 671090229714951, 8685165, 7, 79438 }, }, { .type = PERF_RECORD_FINISHED_ROUND, .size = 8, }, $ Validation is done all around: $ pahole ~/bin/perf --header=paerf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: --header_type=paerf_file_header not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_atatr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_file_atatr' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=atrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: couldn't read the 'atrs.offset' member of 'perf_file_header' for evaluating range=atrs $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_hader(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' pahole: type 'perf_event_hader' not found $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=daata,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: couldn't read the 'daata.offset' member of 'perf_file_header' for evaluating range=daata $ $ pahole ~/bin/perf --header=perf_file_header -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof=fads,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data | grep -v '= 0,' > /dev/null pahole: the sizeof member 'fads' wasn't found in the 'perf_event_header' type pahole: type 'perf_event_header' not found or attributes not validated $ The algorithm to find the types was improved to not fallback at the end, but instead go on saving types that were found, which increases the possibility of resolving all of them, at the end we just need to check if all that is needed was found, printing relevant messages when this isn't the case. More to do, like pretty printing flags such as sample_type, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 22:33:15 +02:00
if (prototype->type_enum && !prototype->type_enum_resolved) {
fprintf(stderr, "pahole: 'type_enum=%s' couldn't be evaluated for '%s' type\n",
prototype->type_enum, prototype->name);
}
}
}
}
type_instance__delete(header);
header = NULL;
btf: Allow multiple cu's in dwarf->btf conversion Currently, the pahole dwarf->btf conversion only supports one compilation unit. This is not ideal since we would like using pahole to generate BTF for vmlinux which has a lot of compilation units. This patch added support to process multiple compilation units per ELF file. Multiple ELF files are also supported properly. The following is a demonstration example: -bash-4.4$ cat t1.c struct t1 { int a1; } g1; int main(void) { return 0; } -bash-4.4$ cat t2.c struct t2 { char a2; } g2; int main() { return 0; } -bash-4.4$ cat t3.c struct t3 { unsigned char a1:4; } g1; int main(void) { return 0; } -bash-4.4$ cat t4.c struct t4 { volatile char a4; } g2; int main() { return 0; } -bash-4.4$ gcc -O2 -o t1 -g t1.c t2.c -bash-4.4$ gcc -O2 -o t3 -g t3.c t4.c Note that both the binary "t1" and "t3" have two compilation units in their respective dwarf debug_info sections. The following is the pahole verbose output for BTF conversion for these two binaries. -bash-4.4$ pahole -JV t1 t3 File t1: [1] STRUCT t1 size=4 vlen=1 a1 type_id=2 bits_offset=0 [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED [3] STRUCT t2 size=1 vlen=1 a2 type_id=4 bits_offset=0 [4] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none) [5] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED File t3: [1] STRUCT t3 size=1 vlen=1 a1 type_id=3 bits_offset=0 [2] INT unsigned char size=1 bit_offset=0 nr_bits=8 encoding=(none) [3] INT unsigned char size=1 bit_offset=0 nr_bits=4 encoding=(none) [4] INT (anon) size=4 bit_offset=0 nr_bits=32 encoding=(none) [5] STRUCT t4 size=1 vlen=1 a4 type_id=6 bits_offset=0 [6] VOLATILE (anon) type_id=7 [7] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none) [8] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED Signed-off-by: Andrii Nakryiko <andriin@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> Cc: Yonghong Song <yhs@fb.com> Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-18 23:09:39 +01:00
if (btf_encode) {
err = btf_encoder__encode();
if (err) {
fputs("Failed to encode BTF\n", stderr);
goto out_cus_delete;
}
}
if (stats_formatter != NULL)
print_stats();
rc = EXIT_SUCCESS;
out_cus_delete:
#ifdef DEBUG_CHECK_LEAKS
cus__delete(cus);
structures__delete();
btf__free(base_btf);
#endif
out_dwarves_exit:
#ifdef DEBUG_CHECK_LEAKS
dwarves__exit();
#endif
out:
#ifdef DEBUG_CHECK_LEAKS
prototypes__delete(&class_names);
#endif
return rc;
}