Commit Graph

106 Commits

Author SHA1 Message Date
Arnaldo Carvalho de Melo 4b7f8c04d0 fprintf: Honour conf_fprintf.hex when printing enumerations
Now this works:

  $ pahole --hex perf_event_type
  enum perf_event_type {
  	PERF_RECORD_MMAP            = 0x1,
  	PERF_RECORD_LOST            = 0x2,
  	PERF_RECORD_COMM            = 0x3,
  	PERF_RECORD_EXIT            = 0x4,
  	PERF_RECORD_THROTTLE        = 0x5,
  	PERF_RECORD_UNTHROTTLE      = 0x6,
  	PERF_RECORD_FORK            = 0x7,
  	PERF_RECORD_READ            = 0x8,
  	PERF_RECORD_SAMPLE          = 0x9,
  	PERF_RECORD_MMAP2           = 0xa,
  	PERF_RECORD_AUX             = 0xb,
  	PERF_RECORD_ITRACE_START    = 0xc,
  	PERF_RECORD_LOST_SAMPLES    = 0xd,
  	PERF_RECORD_SWITCH          = 0xe,
  	PERF_RECORD_SWITCH_CPU_WIDE = 0xf,
  	PERF_RECORD_NAMESPACES      = 0x10,
  	PERF_RECORD_KSYMBOL         = 0x11,
  	PERF_RECORD_BPF_EVENT       = 0x12,
  	PERF_RECORD_CGROUP          = 0x13,
  	PERF_RECORD_TEXT_POKE       = 0x14,
  	PERF_RECORD_MAX             = 0x15,
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-02-20 08:41:11 -03:00
Arnaldo Carvalho de Melo bf21da4075 fprintf: Make typedef__fprintf print anonymous enums
In all the examples the kernel BTF info is being used (/sys/kernel/btf/vmlinux).

Before:

  $ pahole ZSTD_strategy
  typedef enum  ZSTD_strategy;
  $

After:

  $ pahole ZSTD_strategy
  typedef enum {
  	ZSTD_fast    = 0,
  	ZSTD_dfast   = 1,
  	ZSTD_greedy  = 2,
  	ZSTD_lazy    = 3,
  	ZSTD_lazy2   = 4,
  	ZSTD_btlazy2 = 5,
  	ZSTD_btopt   = 6,
  	ZSTD_btopt2  = 7,
  } ZSTD_strategy;
  $

Named ones continue to work as before:

  $ pahole timespec_type
  enum timespec_type {
  	TT_NONE   = 0,
  	TT_NATIVE = 1,
  	TT_COMPAT = 2,
  };
  $

And the ones inside structs, when expanded, as well:

  $ pahole ZSTD_parameters
  typedef struct {
  	ZSTD_compressionParameters cParams;              /*     0    28 */
  	ZSTD_frameParameters       fParams;              /*    28    12 */

  	/* size: 40, cachelines: 1, members: 2 */
  	/* last cacheline: 40 bytes */
  } ZSTD_parameters;
  $ pahole -E ZSTD_parameters
  typedef struct {
  	/* typedef ZSTD_compressionParameters */ struct {
  		unsigned int       windowLog;                                            /*     0     4 */
  		unsigned int       chainLog;                                             /*     4     4 */
  		unsigned int       hashLog;                                              /*     8     4 */
  		unsigned int       searchLog;                                            /*    12     4 */
  		unsigned int       searchLength;                                         /*    16     4 */
  		unsigned int       targetLength;                                         /*    20     4 */
  		/* typedef ZSTD_strategy */ enum {
  			ZSTD_fast    = 0,
  			ZSTD_dfast   = 1,
  			ZSTD_greedy  = 2,
  			ZSTD_lazy    = 3,
  			ZSTD_lazy2   = 4,
  			ZSTD_btlazy2 = 5,
  			ZSTD_btopt   = 6,
  			ZSTD_btopt2  = 7,
  		} strategy; /*    24     4 */
  	} cParams; /*     0    28 */
  	/* typedef ZSTD_frameParameters */ struct {
  		unsigned int       contentSizeFlag;                                      /*    28     4 */
  		unsigned int       checksumFlag;                                         /*    32     4 */
  		unsigned int       noDictIDFlag;                                         /*    36     4 */
  	} fParams; /*    28    12 */

  	/* size: 40, cachelines: 1, members: 2 */
  	/* last cacheline: 40 bytes */
  } ZSTD_parameters;
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 11:41:18 -03:00
Arnaldo Carvalho de Melo 9c4bdf9331 fprintf: Align enumerators
$ pahole timespec_type
  enum timespec_type {
  	TT_NONE   = 0,
  	TT_NATIVE = 1,
  	TT_COMPAT = 2,
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 11:36:20 -03:00
Arnaldo Carvalho de Melo 89cf28228a fprintf: Add enumeration__max_entry_name_len()
Cache the results, will be used when pretty printing enumerations, to
align the entries.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 11:25:47 -03:00
Arnaldo Carvalho de Melo 932b84eb45 fprintf: Make typedef__fprintf print anonymous structs
For instance, the Dwarf_Op typedef was found but since it is an
anonymous struct we get:

  $ pahole -C Dwarf_Op build/pahole
  typedef struct  Dwarf_Op;
  $

Which is not useful, fix it:

  $ pahole -C Dwarf_Op build/pahole
  typedef struct {
  	uint8_t                    atom;                 /*     0     1 */

  	/* XXX 7 bytes hole, try to pack */

  	Dwarf_Word                 number;               /*     8     8 */
  	Dwarf_Word                 number2;              /*    16     8 */
  	Dwarf_Word                 offset;               /*    24     8 */

  	/* size: 32, cachelines: 1, members: 4 */
  	/* sum members: 25, holes: 1, sum holes: 7 */
  	/* last cacheline: 32 bytes */
  } Dwarf_Op;
  $

Reported-by: Matthias Schwarzott <zzam@gentoo.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 10:32:00 -03:00
Arnaldo Carvalho de Melo f5847773d9 fprintf: Support DW_TAG_string_type
We don't really reconstruct source code for FORTRAN, we just print it as
if it was C:

  $ pahole examples/fortran95/derived-type.debug
  struct bar {
  	integer(kind=4)            c;                    /*     0     4 */
  	real(kind=4)               d;                    /*     4     4 */

  	/* size: 8, cachelines: 1, members: 2 */
  	/* last cacheline: 8 bytes */
  };
  struct foo {
  	real(kind=4)               a;                    /*     0     4 */
  	struct bar                 x;                    /*     4     8 */
  	string                     b[7];                 /*    12     7 */

  	/* size: 20, cachelines: 1, members: 3 */
  	/* padding: 1 */
  	/* last cacheline: 20 bytes */
  };
  $

This comes from GCC build tests:

  $ readelf -wi examples/fortran95/derived-type.debug | grep Fortran -A2
      <9c>   DW_AT_producer    : (indirect string, offset: 0x1fb): GNU Fortran2008 10.2.1 20200728 [revision c0438ced53bcf57e4ebb1c38c226e41571aca892] -mtune=generic -march=x86-64 -g -fno-stack-protector -J /home/vries/gdb_versions/devel/build/gdb/testsuite/outputs/gdb.fortran/derived-type -fintrinsic-modules-path /usr/lib64/gcc/x86_64-suse-linux/10/finclude -fpre-include=/usr/include/finclude/math-vector-fortran.h
      <a0>   DW_AT_language    : 14     (Fortran 95)
      <a1>   DW_AT_identifier_case: 2   (down_case)
      <a2>   DW_AT_name        : (indirect string, offset: 0x365): /home/vries/gdb_versions/devel/src/gdb/testsuite/gdb.fortran/derived-type.f90
  [acme@five pahole]$ readelf -wi examples/fortran95/derived-type.debug | grep DW_TAG_string_type -A2
   <1><122>: Abbrev Number: 6 (DW_TAG_string_type)
      <123>   DW_AT_byte_size   : 7
  $

Now lets see whats more that is there segfaulting pahole, but for now I
think I don't have any segfaults, so just wait a bit for Hao to submit
the patch to selectively encode the per-cpu variables in BTF and then
cut v1.18.

Reported-by: Tom de Vries
Bugtracker: https://github.com/acmel/dwarves/issues/9
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-09-18 18:19:22 -03:00
Arnaldo Carvalho de Melo ae43029363 dwarves_fprintf: Export the 'tabs' variable
Will be used by pahole's pretty printer to handle nested structs.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 1bc63a4cff fprintf: Fixup truncation possibility pointed out by gcc -O2
Addresses this warning:

  [ 26%] Building C object CMakeFiles/dwarves.dir/dwarves_fprintf.c.o
  /home/acme/git/pahole/dwarves_fprintf.c: In function ‘type__fprintf’:
  /home/acme/git/pahole/dwarves_fprintf.c:709:47: error: ‘%s’ directive output may be truncated writing up to 257 bytes into a region of size 256 [-Werror=format-truncation=]
    709 |     snprintf(namebfptr, sizeof(namebfptr), "* %s", name);
        |                                               ^~
  /home/acme/git/pahole/dwarves_fprintf.c:709:5: note: ‘snprintf’ output between 3 and 260 bytes into a destination of size 258
    709 |     snprintf(namebfptr, sizeof(namebfptr), "* %s", name);
        |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  cc1: all warnings being treated as errors

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-12-16 11:19:47 -03:00
Arnaldo Carvalho de Melo 644466dce7 fprintf: Remove extraneous sizeof operator
That was affecting --suppress_aligned_attribute, detected using the
'btfdiff' utility, that uses pahole with DWARF and BTF and compares
their outputs, and since we don't have this in BTF, we use
--suppress_aligned_attribute with '-F dwarf'.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-12-13 11:35:13 -03:00
Arnaldo Carvalho de Melo a59459bb80 fprintf: Account inline type __aligned__ member types for spacing
Fixing things like that anonymous union offset/size comment
misalignment:

          union {
                  refcount_t         rcu_users;            /*  2568     4 */
                  struct callback_head rcu __attribute__((__aligned__(8))); /*  2568    16 */
  -       } __attribute__((__aligned__(8)));                                               /*  2568    16 */
  +       } __attribute__((__aligned__(8)));               /*  2568    16 */
          struct pipe_inode_info *   splice_pipe;          /*  2584     8 */
          struct page_frag           task_frag;            /*  2592    16 */
          struct task_delay_info *   delays;               /*  2608     8 */

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-19 12:23:00 -03:00
Arnaldo Carvalho de Melo 56547f133a fprintf: Fix alignment of class members that are structs/enums/unions
E.g. look at that 'completion' member in this struct:

   struct cpu_stop_done {
          atomic_t                   nr_todo;              /*     0     4 */
          int                        ret;                  /*     4     4 */
  -       struct completion  completion;                   /*     8    32 */
  +       struct completion          completion;           /*     8    32 */

          /* size: 40, cachelines: 1, members: 3 */
          /* last cacheline: 40 bytes */

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-19 11:48:36 -03:00
Arnaldo Carvalho de Melo ccf3eebfcd btf_loader: Add support for BTF_KIND_FUNC
Some changes to the fprintf routines were needed, as BTF has as the
function type just a BTF_KIND_FUNC_PROTO, while DWARF has as the type
for a function its return value type. With a function->btf flag this was
overcome and all the other goodies in pfunct are present, for instance:

  $ pahole -JV examples/tcp.o | grep -w FUNC | head
  [4068] FUNC tcp_init type_id=4067
  [4070] FUNC tcp_abort type_id=4069
  [4072] FUNC tcp_done type_id=4071
  [4074] FUNC tcp_md5_hash_key type_id=4073
  [4076] FUNC tcp_md5_hash_skb_data type_id=4075
  [4078] FUNC tcp_get_md5sig_pool type_id=4077
  [4080] FUNC tcp_alloc_md5sig_pool type_id=4079
  [4082] FUNC compat_tcp_getsockopt type_id=4081
  [4084] FUNC tcp_getsockopt type_id=4083
  [4086] FUNC tcp_get_timestamping_opt_stats type_id=4085
  $

  $ pfunct -F btf examples/tcp.o  | head
  memset
  memcpy
  tcp_enter_memory_pressure
  tcp_leave_memory_pressure
  tcp_init_sock
  tcp_init_transfer
  tcp_poll
  tcp_ioctl
  tcp_splice_read
  sk_stream_alloc_skb
  $

  $ pfunct --prototype -F btf examples/tcp.o | head
  void * memset(void * p, int c, __kernel_size_t size);
  void * memcpy(void * p, const void  * q, __kernel_size_t size);
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  __poll_t tcp_poll(struct file * file, struct socket * sock, poll_table * wait);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  ssize_t tcp_splice_read(struct socket * sock, loff_t * ppos, struct pipe_inode_info * pipe, size_t len, unsigned int flags);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  $

Now to ask just for the 'struct sock' 'methods', i.e. functions that
have as one of its arguments a pointer to the given 'class' name:

  $ pfunct --class sock -F btf examples/tcp.o | head
  tcp_abort
  tcp_done
  compat_tcp_getsockopt
  tcp_getsockopt
  tcp_get_info
  compat_tcp_setsockopt
  tcp_setsockopt
  tcp_disconnect
  tcp_write_queue_purge
  tcp_close
  $

Then ask for the prototypes, which requires -V, should have that fixed:

  $ pfunct -V --prototypes --class sock -F btf examples/tcp.o | head
  int tcp_abort(struct sock * sk, int err);
  void tcp_done(struct sock * sk);
  int compat_tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen);
  int tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen);
  void tcp_get_info(struct sock * sk, struct tcp_info * info);
  int compat_tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  int tcp_disconnect(struct sock * sk, int flags);
  void tcp_write_queue_purge(struct sock * sk);
  void tcp_close(struct sock * sk, long int timeout);
  $

Don't like prototypes with parm names, got you covered:

  $ pfunct --no_parm_names -V --prototypes --class sock -F btf examples/tcp.o | head
  int tcp_abort(struct sock *, int);
  void tcp_done(struct sock *);
  int compat_tcp_getsockopt(struct sock *, int, int, char *, int *);
  int tcp_getsockopt(struct sock *, int, int, char *, int *);
  void tcp_get_info(struct sock *, struct tcp_info *);
  int compat_tcp_setsockopt(struct sock *, int, int, char *, unsigned int);
  int tcp_setsockopt(struct sock *, int, int, char *, unsigned int);
  int tcp_disconnect(struct sock *, int);
  void tcp_write_queue_purge(struct sock *);
  void tcp_close(struct sock *, long int);
  $

Don't like long options and want just one function?

  $ pfunct -f tcp_setsockopt -F btf examples/tcp.o
  int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  $

Want to generate compileable code for all of those functions, full with
the necessary types, etc?

  $ pfunct -F btf --compile examples/tcp.o  > a.c
  $ gcc -c -o a.o a.c
  $ pfunct -F dwarf --prototypes --class sock a.o | head
  pfunct: a.o: No debugging information found
  $ gcc -g -c -o a.o a.c
  $ pfunct -V -F dwarf --prototypes --class sock a.o | head
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size);
  $

Now lets go full circle and encode BTF for this a.o generated from
source code generated from the original BTF info in that examples/tcp.o
file:

  $ pahole -JV a.o | tail
  [465] FUNC_PROTO (anon) return=35 args=(392 hp, 393 skb, 5 header_len)
  [466] FUNC tcp_md5_hash_skb_data type_id=465
  [467] FUNC_PROTO (anon) return=35 args=(392 hp, 394 key)
  [468] FUNC tcp_md5_hash_key type_id=467
  [469] FUNC_PROTO (anon) return=0 args=(49 sk)
  [470] FUNC tcp_done type_id=469
  [471] FUNC_PROTO (anon) return=35 args=(49 sk, 35 err)
  [472] FUNC tcp_abort type_id=471
  [473] FUNC_PROTO (anon) return=0 args=(void)
  [474] FUNC tcp_init type_id=473
  $

  $ pfunct -F btf -V --prototypes --class=sock a.o | head
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size);
  $

Curious about the code generated by 'pfunct -F btf --compile examples/tcp.o?

  http://vger.kernel.org/~acme/pahole/pfunct-F-BTF--compile-examples-tcp.o.txt

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-05 12:04:23 -03:00
Arnaldo Carvalho de Melo 3e8f09e304 pdwtags: Print DW_TAG_subroutine_type as well
So that we can see at least via pdwtags those tags, be it from DWARF of
BTF.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-05 10:16:25 -03:00
Arnaldo Carvalho de Melo d5e01d10e5 fprintf: Set tconf.type_spacing earlier
As there are cases where we jump to the type not found label without
having done the full copy of *conf to tconf, so at least the
type_spacing needs to have been set.

This addresses this coverity report entry:

  Error: UNINIT (CWE-457): [#def23]
  dwarves-1.13/dwarves_fprintf.c:600: var_decl: Declaring variable "tconf" without initializer.
  dwarves-1.13/dwarves_fprintf.c:779: uninit_use_in_call: Using uninitialized value "tconf.type_spacing" when calling "fprintf".
  #  777|   	return printed;
  #  778|   out_type_not_found:
  #  779|-> 	printed = fprintf(fp, "%-*s %s", tconf.type_spacing, "<ERROR>", name);
  #  780|   	goto out;
  #  781|   }

Reported-by: William Cohen <wcohen@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 16:28:50 -03:00
Arnaldo Carvalho de Melo c6a9a0eb6a fprintf: Fix up decrementing recursivity level in type__fprintf()
In some exit paths we were accessing tconf before we had copied it from
conf, and we also were losing track of the original type that could have
been expanded and where we bumped the recursivity level member, so just
store that original type and if it is set, decrement its recursivity
level.

This addresses these coverity report entries:

  Error: UNINIT (CWE-457): [#def21]
  dwarves-1.13/dwarves_fprintf.c:600: var_decl: Declaring variable "tconf" without initializer.
  dwarves-1.13/dwarves_fprintf.c:774: uninit_use: Using uninitialized value "tconf.expand_types".
  #  772|   	}
  #  773|   out:
  #  774|-> 	if (tconf.expand_types)
  #  775|   		--type->recursivity_level;
  #  776|

  Error: FORWARD_NULL (CWE-476): [#def22]
  dwarves-1.13/dwarves_fprintf.c:605: var_compare_op: Comparing "type" to null implies that "type" might be null.
  dwarves-1.13/dwarves_fprintf.c:775: var_deref_op: Dereferencing null pointer "type".
  #  773|   out:
  #  774|   	if (tconf.expand_types)
  #  775|-> 		--type->recursivity_level;
  #  776|
  #  777|   	return printed;

Reported-by: William Cohen <wcohen@redhat.com>
Fixes: f84bf73d54 ("dwarves: Move the fprintf code to a new source file.")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 16:22:49 -03:00
Arnaldo Carvalho de Melo f67c281f98 fprintf: Correct the type for the 'cacheline' variable, it should be uint32_t
[ 22%] Building C object CMakeFiles/dwarves.dir/dwarves_fprintf.c.o
  /home/acme/git/pahole/dwarves_fprintf.c: In function ‘union__fprintf’:
  /home/acme/git/pahole/dwarves_fprintf.c:963:34: error: pointer targets in assignment from ‘int *’ to ‘uint32_t *’ {aka ‘unsigned int *’} differ in signedness [-Werror=pointer-sign]
                   uconf.cachelinep = &cacheline;
                                    ^
  cc1: all warnings being treated as errors

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 12:11:27 -03:00
Arnaldo Carvalho de Melo 7b36fab5a8 fprintf: Add guard against unlikely overlapping copy
In cf459ca16f ("fprintf: Pretty print struct members that are pointers
to nameless structs") I added some recursive logic that theoretically
may end up doing an overlapping copy as reported by coverity:

  Error: OVERLAPPING_COPY: [#def19]
  dwarves-1.13/dwarves_fprintf.c:707: assign: Assigning: "name" = "namebfptr".
  dwarves-1.13/dwarves_fprintf.c:705: equal: "name" is equal to the address of "namebfptr".
  dwarves-1.13/dwarves_fprintf.c:705: overlapping_copy: In the call to function "snprintf", the arguments "name" and "namebfptr" may point to the same object.
  #  703|   			if (tag__is_struct(ptype) || tag__is_union(ptype) ||
  #  704|   			    tag__is_enumeration(ptype)) {
  #  705|-> 				snprintf(namebfptr, sizeof(namebfptr), "* %s", name);
  #  706|   				tconf.rel_offset = 1;
  #  707|

Look at cf459ca16f to see what this is about, but for now I'm just
checking if this is the case and adding a guard, at some point I'll
address this properly to allow for pointers to pointers to nameless
struct/union/enums.

Reported-by: William Cohen <wcohen@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 12:09:45 -03:00
Arnaldo Carvalho de Melo e737976c09 fprintf: Do not scrub type when looking up its type
As we end up goto'ing to a place where we need that original type and
were having just a NULL pointer, oops, fix it.

Related to:

  Error: NULL_RETURNS (CWE-476): [#def18]
  dwarves-1.13/dwarves_fprintf.c:727: returned_null: "cu__type" returns "NULL" (checked 54 out of 62 times).
  dwarves-1.13/dwarves_fprintf.c:727: var_assigned: Assigning: "type" = "NULL" return value from "cu__type".
  dwarves-1.13/dwarves_fprintf.c:686: dereference: Dereferencing "type", which is known to be "NULL".
  dwarves-1.13/codiff.c:137: example_assign: Example 1: Assigning: "old_type" = return value from "cu__type(old_cu, old->tag.type)".
  dwarves-1.13/codiff.c:141: example_checked: Example 1 (cont.): "old_type" has its value checked in "old_type == NULL".
  dwarves-1.13/ctracer.c:356: example_assign: Example 2: Assigning: "type" = return value from "cu__type(cu, tag->type)".
  dwarves-1.13/ctracer.c:358: example_checked: Example 2 (cont.): "type" has its value checked in "type == NULL".
  dwarves-1.13/dwarves.c:914: example_assign: Example 3: Assigning: "type" = return value from "cu__type(cu, tag->type)".
  dwarves-1.13/dwarves.c:916: example_checked: Example 3 (cont.): "type" has its value checked in "type == NULL".
  dwarves-1.13/dwarves.c:941: example_assign: Example 4: Assigning: "tag" = return value from "cu__type(cu, var->ip.tag.type)".
  dwarves-1.13/dwarves.c:942: example_checked: Example 4 (cont.): "tag" has its value checked in "tag != NULL".
  dwarves-1.13/dwarves_emit.c:139: example_assign: Example 5: Assigning: "ptr_type" = return value from "cu__type(cu, type->type)".
  dwarves-1.13/dwarves_emit.c:141: example_checked: Example 5 (cont.): "ptr_type" has its value checked in "ptr_type == NULL".
  #  684|
  #  685|   next_type:
  #  686|-> 	switch (type->tag) {
  #  687|   	case DW_TAG_pointer_type:
  #  688|

Reported-by: William Cohen <wcohen@redhat.com>
Fixes: 568dae4bd4 ("printf: Fixup printing "const" early with "const void"")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 10:42:52 -03:00
Arnaldo Carvalho de Melo e95dacb704 fprintf: Remove unused printf arg when printing enumerations
A cut'n'paste error, noticed by coverity:

  Error: PRINTF_ARGS: [#def16]
  dwarves-1.13/dwarves_fprintf.c:369: extra_argument: This argument was not used by the format string: "conf->suffix". [Note: The source code implementation of the function has been overridden by a builtin model.]
  #  367|   	 */
  #  368|   	if (type->size / 8 != sizeof(int))
  #  369|-> 		printed += fprintf(fp, " __attribute__((__packed__))", conf->suffix);
  #  370|
  #  371|   	if (conf->suffix)

Reported-by: William Cohen <wcohen@redhat.com>
Fixes: 28a3bc7add ("fprintf: Support packed enums")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 10:29:49 -03:00
Arnaldo Carvalho de Melo b1412a88bb fprintf: Fixup handling classes with no members
Will Cohen reported this NULL pointer dereference while processing some
object linking with cuda:

  #0  0x00007ffff7f91453 in __class__fprintf (class=0x522560, cu=0x40ff80, conf=0x7fffffffa930, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
      at /home/acme/git/pahole/dwarves_fprintf.c:1624
  #1  0x00007ffff7f92195 in tag__fprintf (tag=0x522560, cu=0x40ff80, conf=0x7fffffffa930, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
      at /home/acme/git/pahole/dwarves_fprintf.c:1835
  #2  0x00007ffff7f90b57 in __class__fprintf (class=0x5224c0, cu=0x40ff80, conf=0x7fffffffaaa0, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
      at /home/acme/git/pahole/dwarves_fprintf.c:1406
  #3  0x00007ffff7f92195 in tag__fprintf (tag=0x5224c0, cu=0x40ff80, conf=0x40a200 <conf>, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
      at /home/acme/git/pahole/dwarves_fprintf.c:1835
  #4  0x0000000000402d03 in class_formatter (class=0x5224c0, cu=0x40ff80, id=1257) at /home/acme/git/pahole/pahole.c:224
  #5  0x0000000000403074 in print_classes (cu=0x40ff80) at /home/acme/git/pahole/pahole.c:319
  #6  0x0000000000404bb2 in pahole_stealer (cu=0x40ff80, conf_load=0x40a240 <conf_load>) at /home/acme/git/pahole/pahole.c:1174
  #7  0x00007ffff7f9ff73 in finalize_cu (cus=0x40b2b0, cu=0x40ff80, dcu=0x7fffffffacf0, conf=0x40a240 <conf_load>)
      at /home/acme/git/pahole/dwarf_loader.c:2227
  #8  0x00007ffff7f9ffac in finalize_cu_immediately (cus=0x40b2b0, cu=0x40ff80, dcu=0x7fffffffacf0, conf=0x40a240 <conf_load>)
      at /home/acme/git/pahole/dwarf_loader.c:2236
  #9  0x00007ffff7fa064c in cus__load_module (cus=0x40b2b0, conf=0x40a240 <conf_load>, mod=0x40d760, dw=0x40e980, elf=0x40b360,
      filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda") at /home/acme/git/pahole/dwarf_loader.c:2389
  #10 0x00007ffff7fa0760 in cus__process_dwflmod (dwflmod=0x40d760, userdata=0x40d770, name=0x40d910 "examples/wcohen/02_Exercise.cuda",
      base=4194304, arg=0x7fffffffcf10) at /home/acme/git/pahole/dwarf_loader.c:2434
  #11 0x00007ffff7f32be1 in dwfl_getmodules () from /lib64/libdw.so.1
  #12 0x00007ffff7fa0820 in cus__process_file (cus=0x40b2b0, conf=0x40a240 <conf_load>, fd=3,
      filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda") at /home/acme/git/pahole/dwarf_loader.c:2487
  #13 0x00007ffff7fa089c in dwarf__load_file (cus=0x40b2b0, conf=0x40a240 <conf_load>, filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda")
      at /home/acme/git/pahole/dwarf_loader.c:2504
  #14 0x00007ffff7f8b0dd in cus__load_file (cus=0x40b2b0, conf=0x40a240 <conf_load>, filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda")
      at /home/acme/git/pahole/dwarves.c:1745
  #15 0x00007ffff7f8bc2a in cus__load_files (cus=0x40b2b0, conf=0x40a240 <conf_load>, filenames=0x7fffffffd150)
      at /home/acme/git/pahole/dwarves.c:2109
  #16 0x0000000000404ff0 in main (argc=2, argv=0x7fffffffd148) at /home/acme/git/pahole/pahole.c:1294
  (gdb)

  (gdb) p class__name(class, cu)
  $6 = 0x5cbb85 "__nv_hdl_helper_trait<__nv_dl_tag<int (*)(int, char**), main, 1u>, void (main(int, char**)::__lambda0::*)(int, double&)const>"
  (gdb) p class->type.nr_members
  $7 = 0
  (gdb) p last
  $8 = (struct class_member *) 0x0
  (gdb)

So, before checking for bitfield details, first check if there were
members.

Now, if we show all structs/classes in that object file and look for the
above data structure, we find it inside another:

  $ pahole examples/wcohen/02_Exercise.cuda
  <SNIP>
  struct __nv_hdl_helper_trait_outer<false, false, int, Kokkos::View<double**>, Kokkos::View<double*>, Kokkos::View<double*> > {
          struct __nv_hdl_helper_trait<__nv_dl_tag<int (*)(int, char**), main, 1u>, void (main(int, char**)::__lambda0::*)(int, double&)const> {
                  class __nv_hdl_wrapper_t<false, false, __nv_dl_tag<int (*)(int, char**), main, 1u>, void(int, double&), int, Kokkos::View<doubl get<main(int, char**)::__lambda0>(class __lambda0, int, class View<double**>, class View<double*>, class View<double*>);

                  /* size: 1, cachelines: 0, members: 0 */
                  /* padding: 1 */
                  /* last cacheline: 1 bytes */
          };

          /* size: 1, cachelines: 0, members: 0 */
          /* padding: 1 */
          /* last cacheline: 1 bytes */
  };
  <SNIP>
  $

Reported-by: William Cohen <wcohen@redhat.com>
Fixes: 13e5b9fc00 ("fprintf: Add unnamed bitfield padding at the end to rebuild original type")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-01 11:37:40 -03:00
Timo Paulssen 3ed9a67967 fprintf: Avoid null dereference with NULL configs
Adding support for suppress_packed and leaving out bitfield paddings at the end
of classes when no alignment info is available accidentally used conf instead
of cconf, where conf can be NULL, which causes cconf to have the default
fprintf config.

Fixes: 986a3b58a8 ("fprintf: Only add bitfield forced paddings when alignment info available")
Fixes: 9a4d719304 ("fprintf: Allow suppressing the inferred __attribute__((__packed__))")
Signed-off-by: Timo Paulssen <timonator@perpetuum-immobile.de>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-05-14 11:19:00 -03:00
Arnaldo Carvalho de Melo 568dae4bd4 printf: Fixup printing "const" early with "const void"
When printing const more early we're not considering "const void *", fix
it.

Fixes: ccd67bdb20 ("fprintf: Print "const" for class members more early, in type__fprintf()")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-05-01 14:56:33 -04:00
Arnaldo Carvalho de Melo 68f261d8df fprintf: Fix recursively printing named structs in --expand_types
In cf459ca16f ("fprintf: Pretty print struct members that are pointers
to nameless structs") we end up breaking 'pahole --expand_types/-E.

All we need there is to print nameless structs when they are referenced
by a pointer member in a struct, i.e. it is defined only there, inline.

Check if that struct is unnamed before emitting it, keeping that fix
while fixing the regression it caused for --expand_types.

Fixes: cf459ca16f ("fprintf: Pretty print struct members that are pointers to nameless structs")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-05-01 12:04:53 -04:00
Arnaldo Carvalho de Melo 0fb727166a pfunct: Strip inlines in the code generated for --compile
If we have:

inline void process_adjtimex_modes(const struct __kernel_timex  * txc, s32 * time_tai)
{
}

And any other struct receiving as a parameter pointers to 'struct
__kerne_timex', then the source file with the above inline, since it
doesn't have any inline expansion, i.e. 'pfunct --compile' generates
just empty function bodies, the types won't be included in the resulting
.o.

Since the original file has the expansions, type types will be there and
thus we will not be able to compare those types, so ask for any 'inline'
to be stripped, so that we keep those types and 'fullcircle' can do its
work.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 15:00:44 -03:00
Arnaldo Carvalho de Melo 30526e2848 fprintf: Print relative offsets for inner pointer structs
I.e. before:

      $ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o > a.c ; head -29 a.c
      struct parsed_partitions {
            struct block_device * bdev;                      /*     0     8 */
            char                       name[32];             /*     8    32 */
            struct {
                    sector_t           from;                 /*    40     8 */
                    sector_t           size;                 /*    48     8 */
                    int                flags;                /*    56     4 */
                    bool               has_info;             /*    60     1 */
                    struct partition_meta_info info;         /*    61   101 */
            } * parts; /*    40     8 */
            int                        next;                 /*    48     4 */
            int                        limit;                /*    52     4 */
            bool                       access_beyond_eod;    /*    56     1 */

            /* XXX 7 bytes hole, try to pack */

            /* --- cacheline 1 boundary (64 bytes) --- */
            char *                     pp_buf;               /*    64     8 */

            /* size: 72, cachelines: 2, members: 7 */
            /* sum members: 65, holes: 1, sum holes: 7 */
            /* last cacheline: 8 bytes */
      };
      $

Now:

  $ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
  struct parsed_partitions {
  	struct block_device * bdev;                      /*     0     8 */
  	char                       name[32];             /*     8    32 */
  	struct {
  		sector_t           from;                 /*     0     8 */
  		sector_t           size;                 /*     8     8 */
  		int                flags;                /*    16     4 */
  		bool               has_info;             /*    20     1 */
  		struct partition_meta_info info;         /*    21   101 */
  	} * parts; /*    40     8 */
  	int                        next;                 /*    48     4 */
  	int                        limit;                /*    52     4 */
  	bool                       access_beyond_eod;    /*    56     1 */

  	/* XXX 7 bytes hole, try to pack */

  	/* --- cacheline 1 boundary (64 bytes) --- */
  	char *                     pp_buf;               /*    64     8 */

  	/* size: 72, cachelines: 2, members: 7 */
  	/* sum members: 65, holes: 1, sum holes: 7 */
  	/* last cacheline: 8 bytes */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 14:39:51 -03:00
Arnaldo Carvalho de Melo cf459ca16f fprintf: Pretty print struct members that are pointers to nameless structs
I.e. to structs defined inside other structs, without a name:

Before:

  $ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
  struct parsed_partitions {
  	struct block_device *      bdev;                 /*     0     8 */
  	char                       name[32];             /*     8    32 */
  	struct  *                  parts;                /*    40     8 */
  	int                        next;                 /*    48     4 */
  	int                        limit;                /*    52     4 */
  	bool                       access_beyond_eod;    /*    56     1 */

  	/* XXX 7 bytes hole, try to pack */

  	/* --- cacheline 1 boundary (64 bytes) --- */
  	char *                     pp_buf;               /*    64     8 */

  	/* size: 72, cachelines: 2, members: 7 */
  	/* sum members: 65, holes: 1, sum holes: 7 */
  	/* last cacheline: 8 bytes */
  };
  $

After:

  $ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
  struct parsed_partitions {
  	struct block_device * bdev;                      /*     0     8 */
  	char                       name[32];             /*     8    32 */
  	struct {
  		sector_t           from;                 /*    40     8 */
  		sector_t           size;                 /*    48     8 */
  		int                flags;                /*    56     4 */
  		bool               has_info;             /*    60     1 */
  		struct partition_meta_info info;         /*    61   101 */
  	} * parts; /*    40     8 */
  	int                        next;                 /*    48     4 */
  	int                        limit;                /*    52     4 */
  	bool                       access_beyond_eod;    /*    56     1 */

  	/* XXX 7 bytes hole, try to pack */

  	/* --- cacheline 1 boundary (64 bytes) --- */
  	char *                     pp_buf;               /*    64     8 */

  	/* size: 72, cachelines: 2, members: 7 */
  	/* sum members: 65, holes: 1, sum holes: 7 */
  	/* last cacheline: 8 bytes */
  };
  $

Still need to align that offsets, leave this for later.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 14:33:48 -03:00
Arnaldo Carvalho de Melo 2bcb01fc2f fprintf: Handle single zero sized array member structs
/home/acme/git/build/v5.1-rc4+/fs/proc/kcore.o
  /tmp/fullcircle.Vnd2oz.c:788:29: error: flexible array member in a struct with no named members
    char                       x[];                  /*     0     0 */

Original:

include/linux/mmzone.h, line 109:

  /*
   * zone->lock and the zone lru_lock are two of the hottest locks in the kernel.
   * So add a wild amount of padding here to ensure that they fall into separate
   * cachelines.  There are very few zone structures in the machine, so space
   * consumption is not a concern here.
   */
  #if defined(CONFIG_SMP)
  struct zone_padding {
          char x[0];
  } ____cacheline_internodealigned_in_smp;
  #define ZONE_PADDING(name)      struct zone_padding name;
  #else
  #define ZONE_PADDING(name)
  #endif

B0rken:

  struct zone_padding {
          char                       x[];                 /*     0     0 */

          /* size: 0, cachelines: 0, members: 1 */
  } __attribute__((__aligned__(64)));

Fixed:

  struct zone_padding {
          char                       x[0];                 /*     0     0 */

          /* size: 0, cachelines: 0, members: 1 */
  } __attribute__((__aligned__(64)));

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 17:20:11 -03:00
Arnaldo Carvalho de Melo 0987266cd9 fprintf: Deal with zero sized arrays in the midle of a union
gcc dislikes invalid C:

  /home/acme/git/build/v5.1-rc4+/fs/ceph/export.o
  /tmp/fullcircle.CvAfpM.c:591:22: error: flexible array member in union
     __u32              raw[];                /*     0     0 */
                        ^~~
Before:

  $ pahole -C fid /home/acme/git/build/v5.1-rc4+/fs/ceph/export.o
  struct fid {
  	union {
  		struct {
  			u32        ino;                  /*     0     4 */
  			u32        gen;                  /*     4     4 */
  			u32        parent_ino;           /*     8     4 */
  			u32        parent_gen;           /*    12     4 */
  		} i32;                                   /*     0    16 */
  		struct {
  			u32        block;                /*     0     4 */
  			u16        partref;              /*     4     2 */
  			u16        parent_partref;       /*     6     2 */
  			u32        generation;           /*     8     4 */
  			u32        parent_block;         /*    12     4 */
  			u32        parent_generation;    /*    16     4 */
  		} udf;                                   /*     0    20 */
  		__u32              raw[];                /*     0     0 */
  	};                                               /*     0    20 */

  	/* size: 20, cachelines: 1, members: 1 */
  	/* last cacheline: 20 bytes */
  };
  $

After:

  $ pahole -C fid /home/acme/git/build/v5.1-rc4+/fs/ceph/export.o
  struct fid {
  	union {
  		struct {
  			u32        ino;                  /*     0     4 */
  			u32        gen;                  /*     4     4 */
  			u32        parent_ino;           /*     8     4 */
  			u32        parent_gen;           /*    12     4 */
  		} i32;                                   /*     0    16 */
  		struct {
  			u32        block;                /*     0     4 */
  			u16        partref;              /*     4     2 */
  			u16        parent_partref;       /*     6     2 */
  			u32        generation;           /*     8     4 */
  			u32        parent_block;         /*    12     4 */
  			u32        parent_generation;    /*    16     4 */
  		} udf;                                   /*     0    20 */
  		__u32              raw[0];               /*     0     0 */
  	};                                               /*     0    20 */

  	/* size: 20, cachelines: 1, members: 1 */
  	/* last cacheline: 20 bytes */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 17:09:03 -03:00
Arnaldo Carvalho de Melo 1101337a74 fprintf: Deal with zero sized arrays in the middle of a struct
Consider:

  struct ipc64_perm {
          __kernel_key_t          key;
          __kernel_uid32_t        uid;
          __kernel_gid32_t        gid;
          __kernel_uid32_t        cuid;
          __kernel_gid32_t        cgid;
          __kernel_mode_t         mode;
                                  /* pad if mode_t is u16: */
          unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
          unsigned short          seq;
          unsigned short          __pad2;
          __kernel_ulong_t        __unused1;
          __kernel_ulong_t        __unused2;
  };

That is a roundabout way of using __attribute__(__aligned__(4)), but
should work nonetheless.

We were not putting the [0] in that zero sized array which ended up
making gcc complain with:

  $ gcc -g -c shm.c
  shm.c:199:29: error: flexible array member not at end of struct
    unsigned char              __pad1[];            /*    24     0 */
                               ^~~~~~
  $

Now this works, i.e. generates compilable source code out of the
type tags, be it from BTF or from DWARF, i.e. this is all from the
internal representation of such types, agnostic wrt the original type
format.

So, the full circle:

  $ pahole -C ipc64_perm /home/acme/git/build/v5.1-rc4+/ipc/shm.o
  struct ipc64_perm {
  	__kernel_key_t             key;                  /*     0     4 */
  	__kernel_uid32_t           uid;                  /*     4     4 */
  	__kernel_gid32_t           gid;                  /*     8     4 */
  	__kernel_uid32_t           cuid;                 /*    12     4 */
  	__kernel_gid32_t           cgid;                 /*    16     4 */
  	__kernel_mode_t            mode;                 /*    20     4 */
  	unsigned char              __pad1[0];            /*    24     0 */
  	short unsigned int         seq;                  /*    24     2 */
  	short unsigned int         __pad2;               /*    26     2 */

  	/* XXX 4 bytes hole, try to pack */

  	__kernel_ulong_t           __unused1;            /*    32     8 */
  	__kernel_ulong_t           __unused2;            /*    40     8 */

  	/* size: 48, cachelines: 1, members: 11 */
  	/* sum members: 44, holes: 1, sum holes: 4 */
  	/* last cacheline: 48 bytes */
  };
  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/ipc/shm.o > shm.c
  $ gcc -g -c shm.c
  $ pahole -C ipc64_perm shm.o
  struct ipc64_perm {
  	__kernel_key_t             key;                  /*     0     4 */
  	__kernel_uid32_t           uid;                  /*     4     4 */
  	__kernel_gid32_t           gid;                  /*     8     4 */
  	__kernel_uid32_t           cuid;                 /*    12     4 */
  	__kernel_gid32_t           cgid;                 /*    16     4 */
  	__kernel_mode_t            mode;                 /*    20     4 */
  	unsigned char              __pad1[0];            /*    24     0 */
  	short unsigned int         seq;                  /*    24     2 */
  	short unsigned int         __pad2;               /*    26     2 */

  	/* XXX 4 bytes hole, try to pack */

  	__kernel_ulong_t           __unused1;            /*    32     8 */
  	__kernel_ulong_t           __unused2;            /*    40     8 */

  	/* size: 48, cachelines: 1, members: 11 */
  	/* sum members: 44, holes: 1, sum holes: 4 */
  	/* last cacheline: 48 bytes */
  };
  $

And for a chuckle, the original source code with a bit of history about
struct layout worries:

include/uapi/asm-generic/ipcbuf.h:

  /*
   * The generic ipc64_perm structure:
   * Note extra padding because this structure is passed back and forth
   * between kernel and user space.
   *
   * ipc64_perm was originally meant to be architecture specific, but
   * everyone just ended up making identical copies without specific
   * optimizations, so we may just as well all use the same one.
   *
   * Pad space is left for:
   * - 32-bit mode_t on architectures that only had 16 bit
   * - 32-bit seq
   * - 2 miscellaneous 32-bit values
   */

  struct ipc64_perm {
          __kernel_key_t          key;
          __kernel_uid32_t        uid;
          __kernel_gid32_t        gid;
          __kernel_uid32_t        cuid;
          __kernel_gid32_t        cgid;
          __kernel_mode_t         mode;
                                  /* pad if mode_t is u16: */
          unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
          unsigned short          seq;
          unsigned short          __pad2;
          __kernel_ulong_t        __unused1;
          __kernel_ulong_t        __unused2;
  };

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 16:54:15 -03:00
Arnaldo Carvalho de Melo 13aa13eb0c fprintf: Don't reuse 'type' in multiple scopes in the same function
Its confusing, so rename some variables.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 16:54:13 -03:00
Arnaldo Carvalho de Melo 6f0f9a8815 fprintf: Fixup multi-dimensional zero sized arrays const handling
Before:

  $ pahole -C piix_map_db /home/acme/git/build/v5.1-rc4+/drivers/ata/ata_piix.o
  struct piix_map_db {
  	const u32                  mask;                 /*     0     4 */
  	const u16                  port_enable;          /*     4     2 */

  	/* XXX 2 bytes hole, try to pack */

  	const const int            map[][4];             /*     8     0 */

  	/* size: 8, cachelines: 1, members: 3 */
  	/* sum members: 6, holes: 1, sum holes: 2 */
  	/* last cacheline: 8 bytes */
  };
  $

After:

  $ pahole -C piix_map_db /home/acme/git/build/v5.1-rc4+/drivers/ata/ata_piix.o
  struct piix_map_db {
  	const u32                  mask;                 /*     0     4 */
  	const u16                  port_enable;          /*     4     2 */

  	/* XXX 2 bytes hole, try to pack */

  	const int                  map[][4];             /*     8     0 */

  	/* size: 8, cachelines: 1, members: 3 */
  	/* sum members: 6, holes: 1, sum holes: 2 */
  	/* last cacheline: 8 bytes */
  };
  $

The DWARF tag sequence:

 <2><17e50>: Abbrev Number: 12 (DW_TAG_member)
    <17e51>   DW_AT_name        : map
    <17e55>   DW_AT_decl_file   : 1
    <17e56>   DW_AT_decl_line   : 160
    <17e57>   DW_AT_decl_column : 12
    <17e58>   DW_AT_type        : <0x17e78>
    <17e5c>   DW_AT_data_member_location: 8

 <1><17e78>: Abbrev Number: 15 (DW_TAG_const_type)
    <17e79>   DW_AT_type        : <0x17e63>

 <1><17e63>: Abbrev Number: 11 (DW_TAG_array_type)
    <17e64>   DW_AT_type        : <0xd8>

 <1><d8>: Abbrev Number: 15 (DW_TAG_const_type)
    <d9>   DW_AT_type        : <0xd1>

 <1><d1>: Abbrev Number: 120 (DW_TAG_base_type)
    <d2>   DW_AT_byte_size   : 4
    <d3>   DW_AT_encoding    : 5        (signed)
    <d4>   DW_AT_name        : int

  const -> array -> const -> int

So just make the check be at least one dimension, if the number of
elements is zero, then drop the double const.

With this btfdiff for the allyesconfig ppc64 reference kernel we're
using is again clean.

  $ pahole -F btf --sizes examples/vmlinux-aarch64 | wc -l
  51023
  $

> 50K types with output from BTF and DWARF matching.

Fixes: ccd67bdb20 ("fprintf: Print "const" for class members more early, in type__fprintf()")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 15:38:23 -03:00
Arnaldo Carvalho de Melo 9a4d719304 fprintf: Allow suppressing the inferred __attribute__((__packed__))
We use things like DW_AT_alignment, so not all of those attributes are
inferred by formats like BTF that lack that info, allow suppressing the
output and make btfdiff ask for both DWARF and BTF output to have this
suppressed.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 15:01:53 -03:00
Arnaldo Carvalho de Melo ec935ee422 fprintf: Allow suppressing the output of force paddings at the end of structs
Things like 'struct timex' in the linux kernel led to this output:

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 14:45:29 -03:00
Arnaldo Carvalho de Melo 986a3b58a8 fprintf: Only add bitfield forced paddings when alignment info available
I.e. BTF doesn't have DW_AT_alignment, so avoid emitting the bitfield
for padding at the end:

  $ pahole -F btf -C rseq examples/tcp.o
  struct rseq {
	  __u32                      cpu_id_start;         /*     0     4 */
	  __u32                      cpu_id;               /*     4     4 */
  	union {
  		__u64              ptr64;                /*     8     8 */
  		__u64              ptr;                  /*     8     8 */
  	} rseq_cs;                                       /*     8     8 */
  	__u32                      flags;                /*    16     4 */

  	/* Force padding: */
  	__u32                      :32;
  	__u32                      :32;
  	__u32                      :32;

  	/* size: 32, cachelines: 1, members: 4 */
  	/* padding: 12 */
  	/* last cacheline: 32 bytes */
  };
  $

After:

  $ pahole -F btf -C rseq examples/tcp.o
  struct rseq {
	__u32                      cpu_id_start;         /*     0     4 */
	__u32                      cpu_id;               /*     4     4 */
	union {
		__u64              ptr64;                /*     8     8 */
		__u64              ptr;                  /*     8     8 */
	} rseq_cs;                                       /*     8     8 */
	__u32                      flags;                /*    16     4 */

	/* size: 32, cachelines: 1, members: 4 */
	/* padding: 12 */
	/* last cacheline: 32 bytes */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 14:45:29 -03:00
Arnaldo Carvalho de Melo dc6b9437a3 emit: Handle structs with DW_AT_alignment=1 meaning __packed__
In the following struct the ceph_entity_addr entries all appear marked with a
__attribute__((__aligned__(8)), which, for the first two members of this type,
'peer_addr' and 'peer_addr_for_me', don't cause the regenerated struct to
differ in layout from the original layout put in place by the compiler as per
the original source code.

But the third member of this type, 'actual_peer_addr' ends up in a different
offset, even in a different cacheline, here is how it looks like in the code generated
from the original source code, at offset 568.

          char                       in_banner[30];        /*   472    30 */
          struct ceph_msg_connect out_connect;             /*   502    33 */
          /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
          struct ceph_msg_connect_reply in_reply;          /*   535    26 */
          struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /*   561   136 */
          /* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
          struct ceph_msg_header out_hdr;                  /*   697    53 */

          /* XXX 2 bytes hole, try to pack */

          /* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */

And here is how it looks like when built from the regenerated source code, at
offset 568:

  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c
  $ gcc -g -c  ceph.c
  $ pahole -C ceph_connection ceph.o | head -46
  struct ceph_connection {
          void *                     private;              /*     0     8 */
          const struct ceph_connection_operations  * ops;  /*     8     8 */
          struct ceph_messenger *    msgr;                 /*    16     8 */
          atomic_t                   sock_state;           /*    24     4 */

          /* XXX 4 bytes hole, try to pack */

          struct socket *            sock;                 /*    32     8 */
          struct ceph_entity_addr peer_addr __attribute__((__aligned__(8))); /*    40   136 */
          /* --- cacheline 2 boundary (128 bytes) was 48 bytes ago --- */
          struct ceph_entity_addr peer_addr_for_me __attribute__((__aligned__(8))); /*   176   136 */
          /* --- cacheline 4 boundary (256 bytes) was 56 bytes ago --- */
          long unsigned int          flags;                /*   312     8 */
          /* --- cacheline 5 boundary (320 bytes) --- */
          long unsigned int          state;                /*   320     8 */
          const char  *              error_msg;            /*   328     8 */
          struct ceph_entity_name peer_name;               /*   336     9 */

          /* XXX 7 bytes hole, try to pack */

          u64                        peer_features;        /*   352     8 */
          u32                        connect_seq;          /*   360     4 */
          u32                        peer_global_seq;      /*   364     4 */
          struct ceph_auth_handshake * auth;               /*   368     8 */
          int                        auth_retry;           /*   376     4 */

          /* XXX 4 bytes hole, try to pack */

          /* --- cacheline 6 boundary (384 bytes) --- */
          struct mutex       mutex;                        /*   384    32 */
          struct list_head   out_queue;                    /*   416    16 */
          struct list_head   out_sent;                     /*   432    16 */
          /* --- cacheline 7 boundary (448 bytes) --- */
          u64                        out_seq;              /*   448     8 */
          u64                        in_seq;               /*   456     8 */
          u64                        in_seq_acked;         /*   464     8 */
          char                       in_banner[30];        /*   472    30 */
          struct ceph_msg_connect out_connect;             /*   502    33 */
          /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
          struct ceph_msg_connect_reply in_reply;          /*   535    26 */

          /* XXX 7 bytes hole, try to pack */

          struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(8))); /*   568   136 */
          /* --- cacheline 11 boundary (704 bytes) --- */
    $

That happens because 'struct ceph_entity_addr' has that __attribute__
((__aligned__(8)) in the regenerated source code, above, now look at how it
gets regenerated:

  $ pahole -C ceph_entity_addr ceph.o
  struct ceph_entity_addr {
          __le32                     type;                 /*     0     4 */
          __le32                     nonce;                /*     4     4 */
          struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(8))); /*     8   128 */

          /* size: 136, cachelines: 3, members: 3 */
          /* forced alignments: 1 */
          /* last cacheline: 8 bytes */
  } __attribute__((__aligned__(8)));
  $

While when looking at the original DWARF:

  $ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
  struct ceph_entity_addr {
          __le32                     type;                 /*     0     4 */
          __le32                     nonce;                /*     4     4 */
          struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /*     8   128 */

          /* size: 136, cachelines: 3, members: 3 */
          /* forced alignments: 1 */
          /* last cacheline: 8 bytes */
  } __attribute__((__aligned__(1)));
  $

The confusion may further come from the fact that 'struct __kernel_sockaddr_storage' has,
in the regenerated source code, the __attribute__((__aligned__8)))

  $ pahole -C __kernel_sockaddr_storage ceph.o
  struct __kernel_sockaddr_storage {
          __kernel_sa_family_t       ss_family;            /*     0     2 */
          char                       __data[126];          /*     2   126 */

          /* size: 128, cachelines: 2, members: 2 */
  } __attribute__((__aligned__(8)));
  $

Which is the same as in the original DWARF:

  $ pahole -C __kernel_sockaddr_storage /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
  struct __kernel_sockaddr_storage {
          __kernel_sa_family_t       ss_family;            /*     0     2 */
          char                       __data[126];          /*     2   126 */

          /* size: 128, cachelines: 2, members: 2 */
  } __attribute__((__aligned__(8)));
  $

Looking at the original original source code for 'struct ceph_entity_addr'
helps here, as it reads:

  include/linux/ceph/msgr.h, line 63:

  /*
   * entity_addr -- network address
   */
  struct ceph_entity_addr {
          __le32 type;
          __le32 nonce;  /* unique id for process (e.g. pid) */
          struct sockaddr_storage in_addr;
  } __attribute__ ((packed));

So the original code has no __attribute__((__aligned__(1))), so, lets look at
what the compiler generates for 'struct ceph_entity_addr':

  $ readelf -wi /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep ceph_entity_addr -A7
      <193a6>   DW_AT_name        : (indirect string, offset: 0x1586): ceph_entity_addr
      <193aa>   DW_AT_byte_size   : 136
      <193ab>   DW_AT_alignment   : 1
      <193ac>   DW_AT_decl_file   : 296
      <193ae>   DW_AT_decl_line   : 63
      <193af>   DW_AT_decl_column : 8
      <193b0>   DW_AT_sibling     : <0x193e0>
   <2><193b4>: Abbrev Number: 5 (DW_TAG_member)
  $

So the natural alignment for 'struct ceph_entity_addr' ends up being the
natural alignment for 'struct __kernel_sockaddr_storage', which is 8, but
since 'struct ceph_entity_addr' was marked in the original source code as __packed__,
the compiler added the DW_AT_alignment: 1 to override that.

The heuristic in pahole, so far, took that __attribute__((__aligned__(1)))
literally:

  $ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
  struct ceph_entity_addr {
          __le32                     type;                 /*     0     4 */
          __le32                     nonce;                /*     4     4 */
          struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /*     8   128 */

          /* size: 136, cachelines: 3, members: 3 */
          /* forced alignments: 1 */
          /* last cacheline: 8 bytes */
  } __attribute__((__aligned__(1)));
  $

which ends up making the regenerated source code (with the __aligned__((1))),
generate a different layout, the __aligned__((8)) in one of its members
overrode that __aligned__((1)).

Take this into account and when faced with a structure which natural alignment
is not one and that has a DW_AT_alignment:1 to mean it really is __packed__.

Doing that makes the regenerated source code match the original structure
layouts, i.e. after the patch we get:

  $ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
  struct ceph_entity_addr {
          __le32                     type;                 /*     0     4 */
          __le32                     nonce;                /*     4     4 */
          struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /*     8   128 */

          /* size: 136, cachelines: 3, members: 3 */
          /* forced alignments: 1 */
          /* last cacheline: 8 bytes */
  } __attribute__((__packed__));
  $

And that member in 'struct ceph_connection', in the original, continues to read:

  $ pahole -C ceph_connection /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep -w actual_peer_addr -B4 -A6
          char                       in_banner[30];        /*   472    30 */
          struct ceph_msg_connect out_connect;             /*   502    33 */
          /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
          struct ceph_msg_connect_reply in_reply;          /*   535    26 */
          struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /*   561   136 */
          /* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
          struct ceph_msg_header out_hdr;                  /*   697    53 */

          /* XXX 2 bytes hole, try to pack */

          /* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
  $

While in the regenerated DWARF from the regenerated source code reads:

  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c
  $ gcc -g -c  ceph.c
  $ pahole -C ceph_connection ceph.o | grep -w actual_peer_addr -B4 -A6
          char                       in_banner[30];        /*   472    30 */
          struct ceph_msg_connect out_connect;             /*   502    33 */
          /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
          struct ceph_msg_connect_reply in_reply;          /*   535    26 */
          struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /*   561   136 */
          /* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
          struct ceph_msg_header out_hdr;                  /*   697    53 */

          /* XXX 2 bytes hole, try to pack */

          /* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
  $

I.e. it now matches.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 13:25:48 -03:00
Arnaldo Carvalho de Melo f78633cfb9 core: Infer __packed__ for union struct members
I.e. check that all the structs that are embedded in a union have their natural
alignment satisfied by the size of the array they are contained in,

Before this change we ended up not marking union struct members that didn't had
natural alignment violations as __packed__ even tho they had to be to be in a
struct that didn't satisfied their natural alignment requirements, which would
violate them when said union was in an array, i.e. the second entry would have
the non __packed__ union struct member in a bad position.

E.g. Before:

  $ pahole -C ceph_osd_op /home/acme/git/build/v5.1-rc4+/net/ceph/osd_client.o
  struct ceph_osd_op {
          __le16                     op;                   /*     0     2 */
          __le32                     flags;                /*     2     4 */
          union {
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     truncate_size;        /*    22     8 */
                          __le32     truncate_seq;         /*    30     4 */
                  } __attribute__((__packed__)) extent;    /*     6    28 */
                  struct {
                          __le32     name_len;             /*     6     4 */
                          __le32     value_len;            /*    10     4 */
                          __u8       cmp_op;               /*    14     1 */
                          __u8       cmp_mode;             /*    15     1 */
                  } __attribute__((__packed__)) xattr;     /*     6    10 */
                  struct {
                          __u8       class_len;            /*     6     1 */
                          __u8       method_len;           /*     7     1 */
                          __u8       argc;                 /*     8     1 */
                          __le32     indata_len;           /*     9     4 */
                  } __attribute__((__packed__)) cls;       /*     6     7 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     count;                /*    14     8 */
                  } pgls;                                  /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                  } snap;                                  /*     6     8 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     ver;                  /*    14     8 */
                          __u8       op;                   /*    22     1 */
                          __le32     gen;                  /*    23     4 */
                  } __attribute__((__packed__)) watch;     /*     6    21 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                  } notify;                                /*     6     8 */
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     src_offset;           /*    22     8 */
                  } clonerange;                            /*     6    24 */
                  struct {
                          __le64     expected_object_size; /*     6     8 */
                          __le64     expected_write_size;  /*    14     8 */
                  } alloc_hint;                            /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                          __le64     src_version;          /*    14     8 */
                          __u8       flags;                /*    22     1 */
                          __le32     src_fadvise_flags;    /*    23     4 */
                  } __attribute__((__packed__)) copy_from; /*     6    21 */
          };                                               /*     6    28 */
          __le32                     payload_len;          /*    34     4 */

          /* size: 38, cachelines: 1, members: 4 */
          /* last cacheline: 38 bytes */
  } __attribute__((__packed__));

After:

  $ pahole -C ceph_osd_op /home/acme/git/build/v5.1-rc4+/net/ceph/osd_client.o
  struct ceph_osd_op {
          __le16                     op;                   /*     0     2 */
          __le32                     flags;                /*     2     4 */
          union {
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     truncate_size;        /*    22     8 */
                          __le32     truncate_seq;         /*    30     4 */
                  } __attribute__((__packed__)) extent;    /*     6    28 */
                  struct {
                          __le32     name_len;             /*     6     4 */
                          __le32     value_len;            /*    10     4 */
                          __u8       cmp_op;               /*    14     1 */
                          __u8       cmp_mode;             /*    15     1 */
                  } __attribute__((__packed__)) xattr;     /*     6    10 */
                  struct {
                          __u8       class_len;            /*     6     1 */
                          __u8       method_len;           /*     7     1 */
                          __u8       argc;                 /*     8     1 */
                          __le32     indata_len;           /*     9     4 */
                  } __attribute__((__packed__)) cls;       /*     6     7 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     count;                /*    14     8 */
                  } pgls;                                  /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                  } snap;                                  /*     6     8 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                          __le64     ver;                  /*    14     8 */
                          __u8       op;                   /*    22     1 */
                          __le32     gen;                  /*    23     4 */
                  } __attribute__((__packed__)) watch;     /*     6    21 */
                  struct {
                          __le64     cookie;               /*     6     8 */
                  } notify;                                /*     6     8 */
                  struct {
                          __le64     offset;               /*     6     8 */
                          __le64     length;               /*    14     8 */
                          __le64     src_offset;           /*    22     8 */
                  } clonerange;                            /*     6    24 */
                  struct {
                          __le64     expected_object_size; /*     6     8 */
                          __le64     expected_write_size;  /*    14     8 */
                  } alloc_hint;                            /*     6    16 */
                  struct {
                          __le64     snapid;               /*     6     8 */
                          __le64     src_version;          /*    14     8 */
                          __u8       flags;                /*    22     1 */
                          __le32     src_fadvise_flags;    /*    23     4 */
                  } __attribute__((__packed__)) copy_from; /*     6    21 */
          };                                               /*     6    28 */
          __le32                     payload_len;          /*    34     4 */

          /* size: 38, cachelines: 1, members: 4 */
          /* last cacheline: 38 bytes */
  } __attribute__((__packed__));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-12 17:08:41 -03:00
Arnaldo Carvalho de Melo 1bb4527220 fprintf: Fixup const pointers
Before:

  $ pahole -C nft_ctx /home/acme/git/build/v5.1-rc4+/net/netfilter/nft_set_rbtree.o
  struct nft_ctx {
          struct net *               net;                  /*     0     8 */
          struct nft_table *         table;                /*     8     8 */
          struct nft_chain *         chain;                /*    16     8 */
          const const struct nlattr  *  * nla;             /*    24     8 */
          u32                        portid;               /*    32     4 */
          u32                        seq;                  /*    36     4 */
          u8                         family;               /*    40     1 */
          u8                         level;                /*    41     1 */
          bool                       report;               /*    42     1 */

          /* size: 48, cachelines: 1, members: 9 */
          /* padding: 5 */
          /* last cacheline: 48 bytes */
  };
  $

Original:

  struct nft_ctx {
          struct net                      *net;
          struct nft_table                *table;
          struct nft_chain                *chain;
          const struct nlattr * const     *nla;
          u32                             portid;
          u32                             seq;
          u8                              family;
          u8                              level;
          bool                            report;
  };

DWARF tags:

 <1><12c8a>: Abbrev Number: 12 (DW_TAG_structure_type)
    <12c8b>   DW_AT_name        : (indirect string, offset: 0xcc6f): nlattr
    <12c8f>   DW_AT_byte_size   : 4
    <12c93>   DW_AT_sibling     : <0x12cb2>

 <1><12cb2>: Abbrev Number: 17 (DW_TAG_const_type)
    <12cb3>   DW_AT_type        : <0x12c8a>

 <1><12cf9>: Abbrev Number: 4 (DW_TAG_pointer_type)
    <12cfa>   DW_AT_byte_size   : 8
    <12cfb>   DW_AT_type        : <0x12cb2>

 <1><12cff>: Abbrev Number: 17 (DW_TAG_const_type)
    <12d00>   DW_AT_type        : <0x12cf9>

 <1><1d54b>: Abbrev Number: 4 (DW_TAG_pointer_type)
    <1d54c>   DW_AT_byte_size   : 8
    <1d54d>   DW_AT_type        : <0x12cff>

 <2><1e52e>: Abbrev Number: 14 (DW_TAG_member)
    <1e52f>   DW_AT_name        : nla
    <1e536>   DW_AT_type        : <0x1d54b>
    <1e53a>   DW_AT_data_member_location: 24

Fixed now:

  $ pahole -C nft_ctx /home/acme/git/build/v5.1-rc4+/net/netfilter/nft_set_rbtree.o
  struct nft_ctx {
          struct net *               net;                  /*     0     8 */
          struct nft_table *         table;                /*     8     8 */
          struct nft_chain *         chain;                /*    16     8 */
          const struct nlattr  * const * nla;              /*    24     8 */
          u32                        portid;               /*    32     4 */
          u32                        seq;                  /*    36     4 */
          u8                         family;               /*    40     1 */
          u8                         level;                /*    41     1 */
          bool                       report;               /*    42     1 */

          /* size: 48, cachelines: 1, members: 9 */
          /* padding: 5 */
          /* last cacheline: 48 bytes */
  };
  $

So, one more full circled:

  $ fullcircle /home/acme/git/build/v5.1-rc4+/net/netfilter/nft_set_rbtree.o
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-12 12:07:46 -03:00
Arnaldo Carvalho de Melo 85c9936963 fprintf: Do not add explicit padding when struct has __aligned__ attr
Fixes: 13e5b9fc00 ("fprintf: Add unnamed bitfield padding at the end to rebuild original type")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-11 13:23:40 -03:00
Arnaldo Carvalho de Melo 28a3bc7add fprintf: Support packed enums
Check if the size is different than sizeof(int), which should be good
enough for now for both 64-bit and 32-bit targets.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-11 10:49:56 -03:00
Arnaldo Carvalho de Melo f77a442f09 fprintf: Do not print the __aligned__ attribute if asked
I.e. honour conf_fprintf.suppress_aligned_attribute, noticed with
btfdiff, as BTF doesn't carries the alignment attribute, so can't
regenerate it, we need to suppress it so as to compare the output of
DWARF with that of the equivalent BTF.

Fixes: b42d77b0bb ("fprintf: Print __attribute__((__aligned__(N))) for structs/classes")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 18:34:16 -03:00
Arnaldo Carvalho de Melo ea583dac52 fprintf: Print zero sized flat arrays as [], not [0]
To match the case when we really have just one dimension, so the
--flat-arrays should show for zero sized arrays, [], not [0]:

Noticed with btfdiff.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 18:25:37 -03:00
Arnaldo Carvalho de Melo f909f13dd7 fprintf: Fixup handling of unnamed bitfields
We were only handling holes inside bitfields as a request to change the
byte_offset, which is not the case when instead of 'int foo:0;' we have
'int foo:6;' to ask for a explicit 6 bit hole inside a bitfield, like
in:

Before this patch:

  $ pahole -F btf -C kvm_mmu_page_role /home/acme/git/build/v5.1-rc4+/arch/x86/kvm/hyperv.o
  union kvm_mmu_page_role {
          u32                        word;               /*     0     4 */
          struct {
                  unsigned int       level:4;            /*     0: 0  4 */
                  unsigned int       gpte_is_8_bytes:1;  /*     0: 4  4 */
                  unsigned int       quadrant:2;         /*     0: 5  4 */
                  unsigned int       direct:1;           /*     0: 7  4 */
                  unsigned int       access:3;           /*     0: 8  4 */
                  unsigned int       invalid:1;          /*     0:11  4 */
                  unsigned int       nxe:1;              /*     0:12  4 */
                  unsigned int       cr0_wp:1;           /*     0:13  4 */
                  unsigned int       smep_andnot_wp:1;   /*     0:14  4 */
                  unsigned int       smap_andnot_wp:1;   /*     0:15  4 */
                  unsigned int       ad_disabled:1;      /*     0:16  4 */
                  unsigned int       guest_mode:1;       /*     0:17  4 */

                  /* XXX 6 bits hole, try to pack */

                  /* Force alignment to the next boundary: */
                  unsigned int       :0;

                  unsigned int       smm:8;              /*     0:24  4 */
          };                                             /*     0     4 */
  };
  $

After:

  $ pahole -F btf -C kvm_mmu_page_role /home/acme/git/build/v5.1-rc4+/arch/x86/kvm/hyperv.o
  union kvm_mmu_page_role {
          u32                        word;               /*     0     4 */
          struct {
                  unsigned int       level:4;            /*     0: 0  4 */
                  unsigned int       gpte_is_8_bytes:1;  /*     0: 4  4 */
                  unsigned int       quadrant:2;         /*     0: 5  4 */
                  unsigned int       direct:1;           /*     0: 7  4 */
                  unsigned int       access:3;           /*     0: 8  4 */
                  unsigned int       invalid:1;          /*     0:11  4 */
                  unsigned int       nxe:1;              /*     0:12  4 */
                  unsigned int       cr0_wp:1;           /*     0:13  4 */
                  unsigned int       smep_andnot_wp:1;   /*     0:14  4 */
                  unsigned int       smap_andnot_wp:1;   /*     0:15  4 */
                  unsigned int       ad_disabled:1;      /*     0:16  4 */
                  unsigned int       guest_mode:1;       /*     0:17  4 */

                  /* XXX 6 bits hole, try to pack */
                  unsigned int       :6;

                  unsigned int       smm:8;              /*     0:24  4 */
          };                                             /*     0     4 */
  };

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Fixes: a104eb1ea1 ("fprintf: Notice explicit bitfield alignment modifications")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 18:11:55 -03:00
Arnaldo Carvalho de Melo 3247a777dc core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.

This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.

  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	int                        refcnt;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	int                        refcnt;               /*    16     4 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* padding: 4 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	int                        refcnt;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	int                        refcnt;               /*    16     4 */

  	/* size: 20, cachelines: 1, members: 3 */
  	/* last cacheline: 20 bytes */
  } __attribute__((__packed__));
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	int                        refcnt;
  	const char  *              uptr;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	int                        refcnt;               /*     8     4 */

  	/* XXX 4 bytes hole, try to pack */

  	const char  *              uptr;                 /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* sum members: 20, holes: 1, sum holes: 4 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	int                        refcnt;
  	const char  *              uptr;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	int                        refcnt;               /*     8     4 */
  	const char  *              uptr;                 /*    12     8 */

  	/* size: 20, cachelines: 1, members: 3 */
  	/* last cacheline: 20 bytes */
  } __attribute__((__packed__));
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	unsigned char              refcnt;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	unsigned char              refcnt;               /*    16     1 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* padding: 7 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	const char  *              uptr;
  	unsigned char              refcnt;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	const char  *              uptr;                 /*     8     8 */
  	unsigned char              refcnt;               /*    16     1 */

  	/* size: 17, cachelines: 1, members: 3 */
  	/* last cacheline: 17 bytes */
  } __attribute__((__packed__));
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	unsigned char              refcnt;
  	const char  *              uptr;
  };

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	unsigned char              refcnt;               /*     8     1 */

  	/* XXX 7 bytes hole, try to pack */

  	const char  *              uptr;                 /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* sum members: 17, holes: 1, sum holes: 7 */
  	/* last cacheline: 24 bytes */
  };
  $ cat a.c
  #define __packed __attribute__((__packed__))

  struct filename {
  	const char  *              name;
  	unsigned char              refcnt;
  	const char  *              uptr;
  } __packed;

  void m(struct filename *f) {}
  $ gcc -g -c a.c
  $ pahole a.o
  struct filename {
  	const char  *              name;                 /*     0     8 */
  	unsigned char              refcnt;               /*     8     1 */
  	const char  *              uptr;                 /*     9     8 */

  	/* size: 17, cachelines: 1, members: 3 */
  	/* last cacheline: 17 bytes */
  } __attribute__((__packed__));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 17:41:59 -03:00
Arnaldo Carvalho de Melo 13e5b9fc00 fprintf: Add unnamed bitfield padding at the end to rebuild original type
Just like the 'struct timex' in the linux kernel UAPI, that is now
correctly reconstructed as:

  $ pahole -IC timex /home/acme/git/build/v5.0-rc2+/kernel/time/posix-clock.o | tail -32
          __kernel_long_t            ppsfreq;              /*    96     8 */
          __kernel_long_t            jitter;               /*   104     8 */
          int                        shift;                /*   112     4 */

          /* XXX 4 bytes hole, try to pack */

          __kernel_long_t            stabil;               /*   120     8 */
          /* --- cacheline 2 boundary (128 bytes) --- */
          __kernel_long_t            jitcnt;               /*   128     8 */
          __kernel_long_t            calcnt;               /*   136     8 */
          __kernel_long_t            errcnt;               /*   144     8 */
          __kernel_long_t            stbcnt;               /*   152     8 */
          int                        tai;                  /*   160     4 */

          /* Force padding: */
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;
          int                        :32;

          /* size: 208, cachelines: 4, members: 20 */
          /* sum members: 152, holes: 3, sum holes: 12 */
          /* padding: 44 */
          /* last cacheline: 16 bytes */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 11:15:42 -03:00
Arnaldo Carvalho de Melo ccd67bdb20 fprintf: Print "const" for class members more early, in type__fprintf()
We want to reach array__fprintf() from here, with the class_member
name, as __tag__name() isn't handling arrays properly.

I.e. to print an array when we have its name we can't use __tag__name().

This also stops printing 0 for zero sized arrays and trows away the
extra DW_TAG_const_type that comes with zero sized arrays, where we
have:

   class_member type: DW_TAG_const_type 1
   DW_TAG_const_type 1: DW_TAG_array_type 2
   DW_TAG_array_type 2: 0 entries, type: DW_TAG_const_type 3
   DW_TAG_const_type 3: real type of the zero sized array

For instance, after this patch we get a sane reconstruction of this
type:

  $ pahole -C filename /home/acme/git/build/v5.0-rc2+/ipc/mqueue.o
  struct filename {
          const char  *              name;                 /*     0     8 */
          const char  *              uptr;                 /*     8     8 */
          int                        refcnt;               /*    16     4 */

          /* XXX 4 bytes hole, try to pack */

          struct audit_names *       aname;                /*    24     8 */
          const char                 iname[];              /*    32     0 */

          /* size: 32, cachelines: 1, members: 5 */
          /* sum members: 28, holes: 1, sum holes: 4 */
          /* last cacheline: 32 bytes */
  };
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 18:13:58 -03:00
Arnaldo Carvalho de Melo b42d77b0bb fprintf: Print __attribute__((__aligned__(N))) for structs/classes
For instance:

  $ pahole -C kern_ipc_perm /home/acme/git/build/v5.0-rc2+/ipc/util.o
  struct kern_ipc_perm {
        spinlock_t                 lock;                 /*     0     4 */
        bool                       deleted;              /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        int                        id;                   /*     8     4 */
        key_t                      key;                  /*    12     4 */
        kuid_t                     uid;                  /*    16     4 */
        kgid_t                     gid;                  /*    20     4 */
        kuid_t                     cuid;                 /*    24     4 */
        kgid_t                     cgid;                 /*    28     4 */
        umode_t                    mode;                 /*    32     2 */

        /* XXX 6 bytes hole, try to pack */

        long unsigned int          seq;                  /*    40     8 */
        void *                     security;             /*    48     8 */
        struct rhash_head          khtnode;              /*    56     8 */
        /* --- cacheline 1 boundary (64 bytes) --- */
        struct callback_head       rcu __attribute__((__aligned__(8))); /*    64    16 */
        refcount_t                 refcount;             /*    80     4 */

        /* size: 128, cachelines: 2, members: 14 */
        /* sum members: 75, holes: 2, sum holes: 9 */
        /* padding: 44 */
        /* forced alignments: 1 */
  } __attribute__((__aligned__(64)));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 17:03:54 -03:00
Arnaldo Carvalho de Melo a104eb1ea1 fprintf: Notice explicit bitfield alignment modifications
I.e. when we find that the last member has a bit_hole, i.e. it is part
of a bitfield, and the current field has a bitfield_size, i.e. it _also_
is part of a bitfield, the only explanation is that they were
artificially put in different base types, i.e. like in these fields
in the linux kernel 'struct task_struct', here reconstructed by pahole:

  $ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o | grep :0 -B9 -A12
          unsigned int               personality;          /*  1128     4 */
          unsigned int               sched_reset_on_fork:1; /*  1132: 0  4 */
          unsigned int               sched_contributes_to_load:1; /*  1132: 1  4 */
          unsigned int               sched_migrated:1;     /*  1132: 2  4 */
          unsigned int               sched_remote_wakeup:1; /*  1132: 3  4 */

          /* XXX 28 bits hole, try to pack */

          /* Force alignment to the next boundary: */
          unsigned int               :0;

          unsigned int               in_execve:1;          /*  1136: 0  4 */
          unsigned int               in_iowait:1;          /*  1136: 1  4 */
          unsigned int               restore_sigmask:1;    /*  1136: 2  4 */
          unsigned int               in_user_fault:1;      /*  1136: 3  4 */
          unsigned int               no_cgroup_migration:1; /*  1136: 4  4 */
          unsigned int               use_memdelay:1;       /*  1136: 5  4 */

          /* XXX 26 bits hole, try to pack */
          /* XXX 4 bytes hole, try to pack */

          long unsigned int          atomic_flags;         /*  1144     8 */
  $

This matches the original definition in the original kernel sources, and
further more, the following sequence proves that with this and DW_AT_alignment,
we can go full circle, i.e.:

1. from an object file reconstruct the source code for all the types that
   appears in function signatures, if pointers, them they will be fully defined,
   not just forward declared:

  $ pfunct --compile=sched_change_group ~/git/build/v5.1-rc2+/kernel/sched/core.o | egrep -w 'sched_change_group|task_struct {' -B10 -A5

          /* --- cacheline 3 boundary (192 bytes) --- */
          struct fpu                 fpu __attribute__((__aligned__(64))); /*   192  4160 */

          /* size: 4352, cachelines: 68, members: 21 */
          /* sum members: 4316, holes: 2, sum holes: 32 */
          /* sum bitfield members: 2 bits, bit holes: 1, sum bit holes: 30 bits */
          /* forced alignments: 1, forced holes: 1, sum forced holes: 28 */
  };

  struct task_struct {
          struct thread_info         thread_info;          /*     0    16 */

          /* XXX last struct has 4 bytes of padding */

          volatile long int          state;                /*    16     8 */
  --
          /* --- cacheline 104 boundary (6656 bytes) --- */
          struct thread_struct       thread __attribute__((__aligned__(64))); /*  6656  4352 */

          /* size: 11008, cachelines: 172, members: 207 */
          /* sum members: 10902, holes: 16, sum holes: 98 */
          /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
          /* paddings: 3, sum paddings: 14 */
          /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
  };

  void sched_change_group(struct task_struct * tsk, int type)
  {
  }
  $

2. Build the regenerated skeleton function + its types:

  $ pfunct --compile=sched_change_group ~/git/build/v5.1-rc2+/kernel/sched/core.o > sched_change_group.c
  $ gcc -g -c sched_change_group.c
  $ file sched_change_group.o
  sched_change_group.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
  $

3. Now lets see if the original 'struct task_struct' printed by pahole, matches
   the the output printed by pahole for the DWARF info generated for the regenerated
   'struct task_struct' source code in sched_change_group.c:

  $ pahole -C task_struct sched_change_group.o | tail

          /* --- cacheline 104 boundary (6656 bytes) --- */
          struct thread_struct       thread __attribute__((__aligned__(64))); /*  6656  4352 */

          /* size: 11008, cachelines: 172, members: 207 */
          /* sum members: 10902, holes: 16, sum holes: 98 */
          /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
          /* paddings: 3, sum paddings: 14 */
          /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
  };
  $ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o | tail

          /* --- cacheline 104 boundary (6656 bytes) --- */
          struct thread_struct       thread __attribute__((__aligned__(64))); /*  6656  4352 */

          /* size: 11008, cachelines: 172, members: 207 */
          /* sum members: 10902, holes: 16, sum holes: 98 */
          /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
          /* paddings: 3, sum paddings: 14 */
          /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
  };
  $

  Furthermore:

  $ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o > /tmp/original
  $ pahole -C task_struct sched_change_group.o > /tmp/regenerated
  $ diff -u /tmp/original /tmp/regenerated
  $

So one of the most complex data structures in the Linux kernel seems to be under control,
and it uses zero sized unnamed bitfields and __attribute__((aligned(N))), a DWARF5 goodie,
time to go tag v1.13!

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 15:44:42 -03:00
Arnaldo Carvalho de Melo 45ad545944 tag: Introduce tag__is_pointer()
For the usual idiom to ask if a tag is a pointer, removing a bit of
DWARFism and shortening the operation.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 15:21:55 -03:00
Arnaldo Carvalho de Melo ce6f393bc9 fprintf: Fixup the printing of const parameters
The last problem with 'pfunct --compile' at least for tcp.o:

Before:

  $ pfunct --compile examples/tcp.o > tcp.pahole.c
  $ gcc -c tcp.pahole.c -g
  tcp.pahole.c:1808:48: error: unknown type name ‘u8const’; did you mean ‘const’?
   inline void tcp_set_ca_state(struct sock * sk, u8const ca_state)
                                                  ^~~~~~~
                                                  const
  tcp.pahole.c:5346:56: error: unknown type name ‘intconst’; did you mean ‘const’?
   inline void skb_set_tail_pointer(struct sk_buff * skb, intconst offset)
                                                          ^~~~~~~~
                                                          const
  tcp.pahole.c:5914:37: error: unknown type name ‘gfp_tconst’; did you mean ‘gfp_t’?
   inline bool gfpflags_allow_blocking(gfp_tconst gfp_flags)
                                       ^~~~~~~~~~
                                       gfp_t
  tcp.pahole.c:5926:24: error: unknown type name ‘ktime_tconst’; did you mean ‘ktime_t’?
   inline s64 ktime_to_ns(ktime_tconst kt)
                          ^~~~~~~~~~~~
                          ktime_t
  tcp.pahole.c:5939:54: warning: ‘struct timespec64const’ declared inside parameter list will not be visible outside of this definition or declaration
   inline struct timespec timespec64_to_timespec(struct timespec64const ts64)
                                                        ^~~~~~~~~~~~~~~
  tcp.pahole.c:5939:70: error: parameter 1 (‘ts64’) has incomplete type
   inline struct timespec timespec64_to_timespec(struct timespec64const ts64)
                                                 ~~~~~~~~~~~~~~~~~~~~~~~^~~~
  $

After:

  $ pfunct --compile examples/tcp.o > tcp.pahole.c
  $ gcc -c tcp.pahole.c -g

Because:

  $ grep -A2 tcp_set_ca_state tcp.pahole.c
  inline void tcp_set_ca_state(struct sock * sk, const u8 ca_state)
  {
  }
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo a7d9c58cb8 fprintf: Add missing closing parens to the align attribute
Noticed while trying to use pfunct's -b option, that will show a
function prototype + the types it uses in its function signature, i.e.:

  $ pfunct -b -f tcp_sendmsg tcp.o
  typedef long long unsigned int __u64;
  typedef __u64 __addrpair;

  typedef unsigned int __u32;
  typedef __u32 __be32;

  typedef short unsigned int __u16;

  typedef __u32 __portpair;

  typedef __u16 __be16;

  struct hlist_node {
  	struct hlist_node *        next;                 /*     0     8 */
  	struct hlist_node * *      pprev;                /*     8     8 */

  	/* size: 16, cachelines: 1, members: 2 */
  	/* last cacheline: 16 bytes */
  };

  <SNIP tons of types>

  struct sock {
  	struct sock_common         __sk_common;          /*     0   136 */
  	/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
  	socket_lock_t              sk_lock;              /*   136    32 */
  	atomic_t                   sk_drops;             /*   168     4 */
  	/* --- cacheline 10 boundary (640 bytes) --- */
<SNIP the rest of the 'struct sock' members>
  	struct sock_cgroup_data    sk_cgrp_data;         /*   640     8 */
  	struct mem_cgroup *        sk_memcg;             /*   648     8 */
  	void                       (*sk_state_change)(struct sock *); /*   656     8 */
  	void                       (*sk_data_ready)(struct sock *); /*   664     8 */
  	void                       (*sk_write_space)(struct sock *); /*   672     8 */
  	void                       (*sk_error_report)(struct sock *); /*   680     8 */
  	int                        (*sk_backlog_rcv)(struct sock *, struct sk_buff *); /*   688     8 */
  	void                       (*sk_destruct)(struct sock *); /*   696     8 */
  	/* --- cacheline 11 boundary (704 bytes) --- */
  	struct sock_reuseport *    sk_reuseport_cb;      /*   704     8 */
  	struct callback_head       sk_rcu __attribute__((__aligned__(8))); /*   712    16 */

  	/* size: 728, cachelines: 12, members: 84 */
  	/* sum members: 715, holes: 4, sum holes: 8 */
  	/* sum bitfield members: 40 bits (5 bytes) */
  	/* paddings: 1, sum paddings: 4 */
  	/* forced alignments: 1 */
  	/* last cacheline: 24 bytes */
  };

<SNIP some more types>

  struct kiocb;

  struct msghdr {
  	void *                     msg_name;             /*     0     8 */
  	int                        msg_namelen;          /*     8     4 */

  	/* XXX 4 bytes hole, try to pack */

  	struct iov_iter            msg_iter;             /*    16    40 */
  	void *                     msg_control;          /*    56     8 */
  	/* --- cacheline 1 boundary (64 bytes) --- */
  	__kernel_size_t            msg_controllen;       /*    64     8 */
  	unsigned int               msg_flags;            /*    72     4 */

  	/* XXX 4 bytes hole, try to pack */

  	struct kiocb *             msg_iocb;             /*    80     8 */

  	/* size: 88, cachelines: 2, members: 7 */
  	/* sum members: 80, holes: 2, sum holes: 8 */
  	/* last cacheline: 24 bytes */
  };

  typedef __kernel_size_t size_t;

  int tcp_sendmsg(struct sock * sk, struct msghdr * msg, size_t size);
  $

So if we then redirect the output to a file and if we make it a empty
function instead of a prototype, i.e. if we make the last line above to
become this:

  int tcp_sendmsg(struct sock * sk, struct msghdr * msg, size_t size) {}

then build with gcc -g to have it build as a .o with DWARF info, then we
should be able to see if the struct rebuilt from DWARF matches the
original struct used to generate the DWARF, going full circle:

  $ pfunct -b -f tcp_sendmsg tcp.o > tcp_sendmsg_types.c
  $ gcc -c tcp_sendmsg_types.c -g
  $ file tcp_sendmsg_types.o
  tcp_sendmsg_types.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
  $ pahole -E -C sock tcp_sendmsg_types.o > tcp_sendmsg_types.o.pahole
  $ pahole -E -C sock tcp.o > tcp.o.pahole
  $ diff -u tcp_sendmsg_types.o.pahole tcp.o.pahole
  $ wc -l tcp_sendmsg_types.o.pahole
  420 tcp_sendmsg_types.o.pahole
  $

So all the types that come from sock are expanded and all its details
are reconstructed in the same way for both cases.

  $ pahole -C sock tcp.o | tail
	struct sock_reuseport *    sk_reuseport_cb;      /*   704     8 */
	struct callback_head       sk_rcu __attribute__((__aligned__(8))); /*   712    16 */

	/* size: 728, cachelines: 12, members: 84 */
	/* sum members: 715, holes: 4, sum holes: 8 */
	/* sum bitfield members: 40 bits (5 bytes) */
	/* paddings: 1, sum paddings: 4 */
	/* forced alignments: 1 */
	/* last cacheline: 24 bytes */
  };
  $ pahole -C sock tcp_sendmsg_types.o | tail
	struct sock_reuseport *    sk_reuseport_cb;      /*   704     8 */
	struct callback_head       sk_rcu __attribute__((__aligned__(8))); /*   712    16 */

	/* size: 728, cachelines: 12, members: 84 */
	/* sum members: 715, holes: 4, sum holes: 8 */
	/* sum bitfield members: 40 bits (5 bytes) */
	/* paddings: 1, sum paddings: 4 */
	/* forced alignments: 1 */
	/* last cacheline: 24 bytes */
  };
  $

Reported-by: Jiri Olsa <jolsa@kernel.org>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 10:48:17 -03:00