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>
This commit is contained in:
Arnaldo Carvalho de Melo 2020-07-15 17:10:21 -03:00
parent 451d66ec02
commit 3c4ee1d91f
1 changed files with 25 additions and 0 deletions

View File

@ -1274,6 +1274,29 @@ static int base_type__fprintf_value(void *instance, int _sizeof, FILE *fp)
return fprintf(fp, "%#" PRIx64, value);
}
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)
{
return fprintf(fp, "%#" PRIx64, class_member__bitfield_value(member, instance));
}
static const char *enumeration__lookup_value(struct type *enumeration, struct cu *cu, uint64_t value)
{
struct enumerator *entry;
@ -1370,6 +1393,8 @@ static int __class__fprintf_value(struct tag *tag, struct cu *cu, void *instance
if (member == type->type_member && type->type_enum) {
printed += base_type__fprintf_enum_value(member_contents, member->byte_size, type->type_enum, cu, fp);
} else if (member->bitfield_size) {
printed += class_member__fprintf_bitfield_value(member, member_contents, fp);
} 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)) {