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>
This commit is contained in:
Arnaldo Carvalho de Melo 2020-07-01 09:17:38 -03:00
parent 44af7c02b5
commit fe28422144
3 changed files with 46 additions and 1 deletions

View File

@ -13,6 +13,7 @@
#include <obstack.h>
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include <sys/types.h>
#include "dutil.h"
#include "list.h"
@ -56,6 +57,7 @@ struct conf_load {
*
* @count - Just like 'dd', stop pretty printing input after 'count' records
* @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
* @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,
@ -72,6 +74,7 @@ struct conf_fprintf {
uint32_t base_offset;
uint32_t count;
uint32_t *cachelinep;
off_t seek_bytes;
uint32_t skip;
uint8_t indent;
uint8_t expand_types:1;

View File

@ -516,6 +516,14 @@ $ pahole --skip 1 --count 2 -C modversion_info drivers/scsi/sg.ko < versions
.name = "param_ops_int",
},
$
This is equivalent to:
$ pahole --seek_bytes 64 --count 1 -C modversion_info drivers/scsi/sg.ko < versions
{
.crc = 0x45e4617b,
.name = "no_llseek",
},
$
.fi
.P

View File

@ -3,7 +3,7 @@
Copyright (C) 2006 Mandriva Conectiva S.A.
Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
Copyright (C) 2007-2008 Arnaldo Carvalho de Melo <acme@redhat.com>
Copyright (C) 2007- Arnaldo Carvalho de Melo <acme@redhat.com>
*/
#include <argp.h>
@ -804,6 +804,7 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
#define ARGP_just_structs 310
#define ARGP_count 311
#define ARGP_skip 312
#define ARGP_seek_bytes 313
static const struct argp_option pahole__options[] = {
{
@ -836,6 +837,12 @@ static const struct argp_option pahole__options[] = {
.arg = "COUNT",
.doc = "Skip COUNT input records"
},
{
.name = "seek_bytes",
.key = ARGP_seek_bytes,
.arg = "BYTES",
.doc = "Seek COUNT input records"
},
{
.name = "find_pointers_to",
.key = 'f',
@ -1163,6 +1170,8 @@ static error_t pahole__options_parser(int key, char *arg,
conf.count = atoi(arg); break;
case ARGP_skip:
conf.skip = atoi(arg); break;
case ARGP_seek_bytes:
conf.seek_bytes = strtol(arg, NULL, 0); break;
default:
return ARGP_ERR_UNKNOWN;
}
@ -1329,6 +1338,25 @@ static int tag__fprintf_value(struct tag *type, struct cu *cu, void *instance, i
return tag__fprintf_hexdump_value(type, cu, instance, _sizeof, fp);
}
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 int tag__stdio_fprintf_value(struct tag *type, struct cu *cu, FILE *fp)
{
int _sizeof = tag__size(type, cu), printed = 0;
@ -1339,6 +1367,12 @@ static int tag__stdio_fprintf_value(struct tag *type, struct cu *cu, FILE *fp)
if (instance == NULL)
return -ENOMEM;
if (conf.seek_bytes && pipe_seek(stdin, conf.seek_bytes) < 0) {
int err = --errno;
fprintf(stderr, "Couldn't --seek_bytes %ld\n", conf.seek_bytes);
return err;
}
while (fread(instance, _sizeof, 1, stdin) == 1) {
if (skip) {
--skip;