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>
This commit is contained in:
Arnaldo Carvalho de Melo 2020-07-14 13:31:59 -03:00
parent 163c330d31
commit 78cdde5cb7
2 changed files with 61 additions and 14 deletions

View File

@ -934,6 +934,7 @@ bool tag__is_array(const struct tag *tag, const struct cu *cu);
* @sizeof_member: Use this to find the size of the record
* @type_member: Use this to select a member from where to get an id on an enum to find a type
* to cast for, needs to be used with the upcoming type_enum.
* @type_enum: A enumeration to use together with type_member to find a type to cast
*/
struct type {
struct namespace namespace;
@ -945,6 +946,7 @@ struct type {
uint32_t alignment;
struct class_member *sizeof_member;
struct class_member *type_member;
struct type *type_enum;
uint16_t natural_alignment;
bool packed_attributes_inferred;
uint8_t declaration; /* only one bit used */

View File

@ -1251,20 +1251,49 @@ static int tag__fprintf_hexdump_value(struct tag *type, struct cu *cu, void *ins
return printed;
}
static uint64_t base_type__value(void *instance, int _sizeof)
{
if (_sizeof == sizeof(int))
return *(int *)instance;
else if (_sizeof == sizeof(long))
return *(long *)instance;
else if (_sizeof == sizeof(long long))
return *(long long *)instance;
else if (_sizeof == sizeof(char))
return *(char *)instance;
else if (_sizeof == sizeof(short))
return *(short *)instance;
return 0;
}
static int base_type__fprintf_value(void *instance, int _sizeof, uint64_t *value, FILE *fp)
{
*value = 0;
*value = base_type__value(instance, _sizeof);
if (_sizeof == sizeof(int))
*value = *(int *)instance;
else if (_sizeof == sizeof(long))
*value = *(long *)instance;
else if (_sizeof == sizeof(long long))
*value = *(long long *)instance;
else if (_sizeof == sizeof(char))
*value = *(char *)instance;
else if (_sizeof == sizeof(short))
*value = *(short *)instance;
return fprintf(fp, "%#" PRIx64, *value);
}
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 int base_type__fprintf_enum_value(void *instance, int _sizeof, uint64_t *value, struct type *enumeration, struct cu *cu, FILE *fp)
{
*value = base_type__value(instance, _sizeof);
const char *entry = enumeration__lookup_value(enumeration, cu, *value);
if (entry)
return fprintf(fp, "%s", entry);
return fprintf(fp, "%#" PRIx64, *value);
}
@ -1324,7 +1353,9 @@ static int class__fprintf_value(struct tag *tag, struct cu *cu, void *instance,
printed += fprintf(fp, "\n\t.%s = ", class_member__name(member, cu));
if (tag__is_base_type(member_type, cu)) {
if (member == type->type_member && type->type_enum) {
printed += base_type__fprintf_enum_value(member_contents, member->byte_size, &value, type->type_enum, cu, fp);
} else if (tag__is_base_type(member_type, cu)) {
printed += base_type__fprintf_value(member_contents, member->byte_size, &value, fp);
} else if (tag__is_array(member_type, cu)) {
printed += array__fprintf_value(member_type, cu, member_contents, member->byte_size, fp);
@ -1494,7 +1525,8 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
strlist__for_each_entry_safe(class_names, pos, n) {
const char *sizeof_member = NULL, // Overriding sizeof(class)?
*type_member = NULL; // Member to get a cast type via an enum
*type_member = NULL, // Member to get a cast type via an enum
*type_enum = NULL; // Enumerator to use with the type member
char *name = (char *)pos->s;
const char *args_open = strchr(name, '(');
@ -1556,8 +1588,12 @@ next_arg:
type_member = value;
if (global_verbose)
fprintf(stderr, "pahole: type member for '%s' is '%s'\n", name, type_member);
} else if (strcmp(args, "type_enum") == 0) {
type_enum = value;
if (global_verbose)
fprintf(stderr, "pahole: type enum for '%s' is '%s'\n", name, type_enum);
} else {
fprintf(stderr, "pahole: invalid arg '%s' in '%s' (known args: sizeof=member, type=member)\n", args, pos->s);
fprintf(stderr, "pahole: invalid arg '%s' in '%s' (known args: sizeof=member, type=member, type_enum=enum)\n", args, pos->s);
goto free_and_stop;
}
@ -1604,6 +1640,15 @@ out_free_name:
goto out_free_name;
}
}
if (type_enum) {
type->type_enum = tag__type(cu__find_enumeration_by_name(cu, type_enum, NULL));
if (type->type_enum == NULL) {
fprintf(stderr, "pahole: the type enum '%s' wasn't found in '%s'\n",
type_enum, cu->name);
goto out_free_name;
}
}
}
}