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>
This commit is contained in:
Arnaldo Carvalho de Melo 2020-07-20 09:27:10 -03:00
parent 0f10fa7529
commit 446b85f727
2 changed files with 47 additions and 2 deletions

View File

@ -59,6 +59,7 @@ struct conf_load {
* @skip - Just like 'dd', skip 'count' records when pretty printing input
* @seek_bytes - Number of bytes to seek, if stdin only from start, when we have --pretty FILE, then from the end as well with negative numbers,
* may be of the form $header.MEMBER_NAME when using with --header.
* @size_bytes - Number of bytes to read, similar to seek_bytes, and when both are in place, first seek seek_bytes then read size_bytes
* @flat_arrays - a->foo[10][2] becomes a->foo[20]
* @classes_as_structs - class f becomes struct f, CTF doesn't have a "class"
* @cachelinep - pointer to current cacheline, so that when expanding types we keep track of it,
@ -76,6 +77,7 @@ struct conf_fprintf {
uint32_t count;
uint32_t *cachelinep;
const char *seek_bytes;
const char *size_bytes;
const char *header_type;
uint32_t skip;
uint8_t indent;

View File

@ -807,6 +807,7 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
#define ARGP_skip 312
#define ARGP_seek_bytes 313
#define ARGP_header_type 314
#define ARGP_size_bytes 315
static const struct argp_option pahole__options[] = {
{
@ -845,6 +846,12 @@ static const struct argp_option pahole__options[] = {
.arg = "BYTES",
.doc = "Seek COUNT input records"
},
{
.name = "size_bytes",
.key = ARGP_size_bytes,
.arg = "BYTES",
.doc = "Read only this number of bytes from this point onwards"
},
{
.name = "header_type",
.key = ARGP_header_type,
@ -1187,6 +1194,8 @@ static error_t pahole__options_parser(int key, char *arg,
conf.skip = atoi(arg); break;
case ARGP_seek_bytes:
conf.seek_bytes = arg; break;
case ARGP_size_bytes:
conf.size_bytes = arg; break;
case ARGP_header_type:
conf.header_type = arg; break;
default:
@ -1635,6 +1644,7 @@ static int tag__stdio_fprintf_value(struct tag *type, struct cu *cu, FILE *fp)
int _sizeof = tag__size(type, cu), printed = 0;
int max_sizeof = _sizeof;
void *instance = malloc(_sizeof);
uint64_t size_bytes = ULLONG_MAX;
uint32_t count = 0;
uint32_t skip = conf.skip;
@ -1704,8 +1714,36 @@ out_delete_type_instance:
}
}
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);
goto out_delete_type_instance;
}
const char *member_name = conf.size_bytes + sizeof("$header.") - 1;
int64_t value = type_instance__int_value(header, cu, member_name);
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);
}
}
type_instance__delete(header);
uint64_t read_bytes = 0;
while (fread(instance, _sizeof, 1, stdin) == 1) {
// Read it from each record/instance
int real_sizeof = tag__real_sizeof(type, cu, _sizeof, instance);
@ -1728,12 +1766,14 @@ out_delete_type_instance:
}
}
read_bytes += real_sizeof;
if (tag__type(type)->filter && type__filter_value(type, instance))
continue;
goto next_record;
if (skip) {
--skip;
continue;
goto next_record;
}
/*
@ -1786,6 +1826,9 @@ out_delete_type_instance:
if (conf.count && ++count == conf.count)
break;
next_record:
if (read_bytes >= size_bytes)
break;
}
out:
free(instance);