Commit Graph

1247 Commits

Author SHA1 Message Date
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 1c9c1d6bbd dwarf_loader: Store DW_AT_alignment if available in DW_TAG_{structure,union,class}_type
That is not just for DW_TAG_class_member :-)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 16:57:52 -03:00
Arnaldo Carvalho de Melo 41c55858da codiff: Add --quiet option
To avoid printing anything when there are no differences to show.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 16:46:49 -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 75f32a24c7 codiff: Improve the comparision of anonymous struct members
I.e. 'union {};', 'struct {};' members were always appearing as having
been removed, as we normally do lookup by member name, to find out if
its offset, size, type, etc changed.

For unnamed members, try a different heuristic, i.e. look for the nth
anonymous member, this way we're just trying to compare the first
unnamed member of, say, struct OLD with the first unnamed member of
struct NEW, etc.

For OLD == NEW, this works well, for OLD != NEW because some non
anonymous field got added, removed or moved around, ditto, and when the
number of unnamed fields gets decreased, then we can mix things up, and
compare the previously first in A with the previously first in B.

For the current intended use case of:

1) compile a .c file into a .o file with debugging info, say FILE.o

2) use 'pfunct --compile FILE.o > regenerated-FILE.c'

3) compile regenerated-FILE.c into regenerated-FILE.o with debugging info

4) codiff --struct FILE.o regenerated-FILE.o and find out if they match

This gets us moving forward as we'll spot differences with this algo.

For the future we can use a few more heuristics or stop using search by
name members, instead traversing both structs in tandem, spotting the
differences by comparing the fields that way.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-08 15:09:33 -03:00
Arnaldo Carvalho de Melo 6b1e43f2c1 codiff: When comparing against a file with just one CU don't bother finding by name
I.e. when we have two object files with debugging info, and one of them
jas just one CU, then compare all the CUs in the other file to this
unique CU.

Case in hand: encode BTF in a file, then the BTF info has everything in
just one "compile unit", so when looking at the types in the DWARF
originals, we should just compare its types to what is in the single BTF
"compile unit".

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-08 15:07:17 -03:00
Arnaldo Carvalho de Melo 15a754f224 core: Add nr_entries member to 'struct cus'
Will be used when considering comparing multiple CU entries in a struct
cus to the sole compile unit in a second file, like when comparing the
types in a multi-CU DWARF file like vmlinux against the combined,
deduplicated entries in that vmlinux .BTF section.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-08 11:54:06 -03:00
Arnaldo Carvalho de Melo 99750f244c pfunct: Generate a valid return type for the --compile bodies
Before:

  $ pfunct --compile examples/tcp.o > tcp.pahole.c
  $ clang --target=bpf -c -g tcp.pahole.c |& tail
  tcp.pahole.c:6154:1: warning: control reaches end of non-void function [-Wreturn-type]
  }
  ^
  tcp.pahole.c:6158:1: warning: control reaches end of non-void function [-Wreturn-type]
  }
  ^
  tcp.pahole.c:6170:1: warning: control reaches end of non-void function [-Wreturn-type]
  }
  ^
  192 warnings generated.
  $ head -6170 tcp.pahole.c | tail -3
  inline int arch_atomic_read(const atomic_t  * v)
  {
  }
  $

After:

  $ pfunct --compile examples/tcp.o > tcp.pahole.c
  $ clang --target=bpf -c -g tcp.pahole.c
  $ grep -A3 -w arch_atomic_read tcp.pahole.c
  inline int arch_atomic_read(const atomic_t  * v)
  {
	  return 0;
  }
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:33:43 -03:00
Arnaldo Carvalho de Melo 881aabd6fc reorganize: Introduce class__for_each_member_from_safe()
Reducing boilerplate, keeping consistent with the other member traversal
helpers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:16:16 -03:00
Arnaldo Carvalho de Melo 1b2e3389f3 reorganize: Introduce class__for_each_member_reverse()
Reducing boilerplate, keeping consistent with the other member traversal
helpers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:11:42 -03:00
Arnaldo Carvalho de Melo 10fef2916d reorganize: Introduce class__for_each_member_continue()
To get a lot of boilerplate behind a nice helper.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:07:30 -03:00
Arnaldo Carvalho de Melo e7a56ee8cc reorganize: Introduce class__for_each_member_from()
To get a lot of boilerplate behind a nice helper.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 16:05:28 -03:00
Arnaldo Carvalho de Melo 9a79bb6ced tag: Introduce tag__is_pointer_to()
To shorten the check if a tag is a pointer to a particular type.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 15:28:55 -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 89ce57a02e pdwtags: Find holes in structs
The pdwtags prints all tags, so call class__find_holes() for structs so
that we don't print BFAs.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-05 11:34:33 -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 7aec7dd6c2 pfunct: Do not reconstruct external functions
I.e. those with DW_AT_external set, to avoid regenerating multiple times
things like __compiletime_assert_1504:

 <5><2fc41>: Abbrev Number: 100 (DW_TAG_subprogram)
    <2fc42>   DW_AT_external    : 1
    <2fc42>   DW_AT_name        : (indirect string, offset: 0x1751f): __compiletime_assert_1504
    <2fc46>   DW_AT_decl_file   : 1
    <2fc47>   DW_AT_decl_line   : 1504
    <2fc49>   DW_AT_decl_column : 2
    <2fc4a>   DW_AT_prototyped  : 1
    <2fc4a>   DW_AT_declaration : 1
 <5><2fc4a>: Abbrev Number: 0
 <4><2fc4b>: Abbrev Number: 0
 <3><2fc4c>: Abbrev Number: 0
 <2><2fc4d>: Abbrev Number: 34 (DW_TAG_lexical_block)
 <3><2fc4e>: Abbrev Number: 12 (DW_TAG_variable)
    <2fc4f>   DW_AT_name        : (indirect string, offset: 0xbcc6): ____ptr
    <2fc53>   DW_AT_decl_file   : 1
    <2fc54>   DW_AT_decl_line   : 1504
    <2fc56>   DW_AT_decl_column : 2
    <2fc57>   DW_AT_type        : <0x6441>
 <3><2fc5b>: Abbrev Number: 34 (DW_TAG_lexical_block)
 <4><2fc5c>: Abbrev Number: 12 (DW_TAG_variable)
    <2fc5d>   DW_AT_name        : (indirect string, offset: 0xeb74): __mptr
    <2fc61>   DW_AT_decl_file   : 1
    <2fc62>   DW_AT_decl_line   : 1504
    <2fc64>   DW_AT_decl_column : 2
    <2fc65>   DW_AT_type        : <0x5a2>
 <4><2fc69>: Abbrev Number: 34 (DW_TAG_lexical_block)
 <5><2fc6a>: Abbrev Number: 100 (DW_TAG_subprogram)
    <2fc6b>   DW_AT_external    : 1
    <2fc6b>   DW_AT_name        : (indirect string, offset: 0x1751f): __compiletime_assert_1504
    <2fc6f>   DW_AT_decl_file   : 1
    <2fc70>   DW_AT_decl_line   : 1504
    <2fc72>   DW_AT_decl_column : 2
    <2fc73>   DW_AT_prototyped  : 1
    <2fc73>   DW_AT_declaration : 1

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo 163b873f81 pfunct: Do not reconstruct inline expansions of functions
I.e. those that point back to the inline function via
DW_AT_abstract_origin.

For instance:

 <1><34b65>: Abbrev Number: 156 (DW_TAG_subprogram)
    <34b67>   DW_AT_external    : 1
    <34b67>   DW_AT_name        : (indirect string, offset: 0x2404): tcp_enter_memory_pressure
    <34b6b>   DW_AT_decl_file   : 1
    <34b6c>   DW_AT_decl_line   : 324
    <34b6e>   DW_AT_decl_column : 6
    <34b6f>   DW_AT_prototyped  : 1
    <34b6f>   DW_AT_inline      : 1     (inlined)

  <SNIP>

   <1><37f45>: Abbrev Number: 149 (DW_TAG_subprogram)
    <37f47>   DW_AT_abstract_origin: <0x34b65>
    <37f4b>   DW_AT_low_pc      : 0x1000
    <37f53>   DW_AT_high_pc     : 0x48
    <37f5b>   DW_AT_frame_base  : 1 byte block: 9c      (DW_OP_call_frame_cfa)
    <37f5d>   DW_AT_GNU_all_call_sites: 1
    <37f5d>   DW_AT_sibling     : <0x38032>

Generated by:

  Compilation Unit @ offset 0x0:
   Length:        0x3b40b (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 215 (DW_TAG_compile_unit)
    <d>   DW_AT_producer    : (indirect string, offset: 0xb0bc): GNU C89 8.2.1 20181215 (Red Hat 8.2.1-6) -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -mfentry -march=x86-64 -g -O2 -std=gnu90 -p -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-delete-null-pointer-checks -fstack-protector-strong -fno-var-tracking-assignments -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fstack-check=no -fconserve-stack --param allow-store-data-races=0
    <11>   DW_AT_language    : 1        (ANSI C)
    <12>   DW_AT_name        : (indirect string, offset: 0x10daa): /home/acme/git/linux/net/ipv4/tcp.c
    <16>   DW_AT_comp_dir    : (indirect string, offset: 0x1d8c5): /home/acme/git/build/v5.0+

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo ea83b780ec pfunct: Handle unnamed struct typedefs
If we first reconstruct all the types needed for a typedef to then
reconstruct it, we may end up with the unnamed struct correctly
reconstructed and then this:

  tcp.pahole.c:1987:17: warning: useless storage class specifier in empty declaration
   typedef struct  read_descriptor_t;

I.e.:

  typedef struct {
          size_t                     written;              /*     0     8 */
          size_t                     count;                /*     8     8 */
          union {
                  char *             buf;                  /*    16     8 */
                  void *             data;                 /*    16     8 */
          } arg;                                           /*    16     8 */
          int                        error;                /*    24     4 */

          /* size: 32, cachelines: 1, members: 4 */
          /* padding: 4 */
          /* last cacheline: 32 bytes */
  } read_descriptor_t;
  typedef struct  read_descriptor_t;

So special case it.

XXX

I'll revisit this, looks suboptimal, we manage to get this right
when reconstructing nameless struct typedefs found as the types for
members of structs or unions...

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo e7ebc05d12 emit: Unwind the definitions for typedefs in type__emit_definitions()
For instance, with the existing code we ended up with:

  typedef __kernel_size_t size_t;
  size_t tcp_opt_stats_get_size(void)
  {
  }

Which lacks unwinding what a __kernel_size_t is, i.e.
type__emit_definitions() was only emitting definitions for the members
of structs and unions, do it for typedefs too, and then we end up what
we need, which is:

  typedef long unsigned int __kernel_ulong_t;
  typedef __kernel_ulong_t __kernel_size_t;
  typedef __kernel_size_t size_t;
  size_t tcp_opt_stats_get_size(void)
  {
  }

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo 093135b0bf pfunct: Do not emit a type multiple times
We need to check if tag__type(type)->definition_emitted is set before
asking for that type to be emitted, otherwise we get type redefinition
errors when trying to compile the output from pahole --expand_types.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo 3ce2c52166 pfunct: Ask for generating compilable output that generates DWARF for types
I.e. --compile is similar to --expand_types but also makes sure the
function have empty function bodies, which ends up making at least gcc
to generate the DWARF info for the types referenced by the function
arguments and return types.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:23 -03:00
Arnaldo Carvalho de Melo e7a786540d pfunct: Make --expand_types/-b without -f expand types for all functions
Previously we wouldn't expand types if the user didn't provide a
function name, make it expand the types for all function arguments if
no function name is provided.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 21:59:20 -03:00
Arnaldo Carvalho de Melo 9b2eadf97b pfunct: Follow const, restrict, volatile in --expand_types
Take:

  int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key)

We were not generating the 'struct tcp_md5sig_key' forward decl or
struct definition, stopping at 'const', which made this uncompilable.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 15:45:26 -03:00
Arnaldo Carvalho de Melo f3f86f2f89 pfunct: Reconstruct function return types for --expand_types
Not just for the function arguments, so that we are able to get
something closer to buildable.

So far we got it buildable when the return types were reconstructed
because they also appeared in one of the function arguments or
in structs used by them.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-04 15:39:26 -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
Arnaldo Carvalho de Melo d83d9f578f dwarf_loader: Handle DW_TAG_label in inline expansions
Just add it to the current lexblock.

This removes the warnings that started to appear with the fix in the
previous patch, i.e. these:

      die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aefafe> not handled!
      die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aeff8a> not handled!
      die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af02e0> not handled!

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-03 21:34:01 -03:00
Arnaldo Carvalho de Melo 73e545b144 dwarf_loader: Handle unsupported_tag in die__process_inline_expansion
It calls die__process_tag() that can return &unsupported_tag, for
instance for DW_TAG_label, as reported by Jiri, so handle that, which
ends up causing these warnings to appear in pahole:

  struct userfaultfd_wake_range {
  	long unsigned int          start;                /*     0     8 */
  	long unsigned int          len;                  /*     8     8 */

  	/* size: 16, cachelines: 1, members: 2 */
  	/* last cacheline: 16 bytes */
  };
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aefafe> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aeff8a> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af02e0> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af1903> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af19bf> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af358d> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af3e87> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af4268> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af46ec> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af4bd3> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af4f8c> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af551e> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af5815> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af5c15> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af5cad> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af79e0> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af7b34> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af7df3> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af806e> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af812c> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afa8da> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afaba5> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afb22e> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afb45e> not handled!
  struct kioctx_table {
  	struct callback_head       rcu __attribute__((__aligned__(8)); /*     0    16 */
  	unsigned int               nr;                   /*    16     4 */

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

  	struct kioctx *            table[0];             /*    24     0 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* sum members: 20, holes: 1, sum holes: 4 */
  	/* forced alignments: 1 */
  	/* last cacheline: 24 bytes */
  };
  ^C
  [acme@quaco pahole]$

But at least no segfault takes place. Next csets should take care of it
more properly.

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-03 21:23:08 -03:00
Andrii Nakryiko fe590758cb class__find_holes: Zero out bit_hole/hole on member
pahole --reorganize is calling class__find_holes() multiple times on the
same struct to re-calculate holes. If it so happens that after reorg
last struct's member had hole previously, we are not going to clear it
out, which will lead to weird output and BFA, like this:

$ pahole -F btf --reorganize -C netns_frags ~/tmp/vmlinux-default
struct netns_frags {
        long int                   high_thresh;          /*     0     8 */
        long int                   low_thresh;           /*     8     8 */
        int                        timeout;              /*    16     4 */
        int                        max_dist;             /*    20     4 */
        struct inet_frags *        f;                    /*    24     8 */
        atomic_long_t              mem;                  /*    32     8 */

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

        /* --- cacheline 1 boundary (64 bytes) --- */
        struct rhashtable          rhashtable;           /*    64   136 */

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

        /* size: 200, cachelines: 4, members: 7 */
        /* sum members: 176, holes: 1, sum holes: 80 */
        /* last cacheline: 8 bytes */

        /* BRAIN FART ALERT! 200 bytes != 176 (member bytes) + 0 (member bits) + 80 (byte holes) + 0 (bit holes), diff = -448 bits */
};   /* saved 120 bytes and 1 cacheline! */

After this change:
$ pahole -F btf --reorganize -C netns_frags ~/tmp/vmlinux-defaultstruct netns_frags {
        long int                   high_thresh;          /*     0     8 */
        long int                   low_thresh;           /*     8     8 */
        int                        timeout;              /*    16     4 */
        int                        max_dist;             /*    20     4 */
        struct inet_frags *        f;                    /*    24     8 */
        atomic_long_t              mem;                  /*    32     8 */

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

        /* --- cacheline 1 boundary (64 bytes) --- */
        struct rhashtable          rhashtable;           /*    64   136 */

        /* size: 200, cachelines: 4, members: 7 */
        /* sum members: 176, holes: 1, sum holes: 24 */
        /* last cacheline: 8 bytes */
};   /* saved 120 bytes and 1 cacheline! */

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 21:10:05 -03:00
Arnaldo Carvalho de Melo 863c2af6e9 reorganize: Disable the bitfield coalescing/moving steps
We need to fix some bugs introduced recently, till then, disable steps
that try to demote the base type of bitfields and those that
move/combine bitfields to save space.

We'll revisit those later, bringing them back to the reorg codebase.

Acked-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>#
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:35:43 -03:00
Arnaldo Carvalho de Melo b95961db69 fprintf: Show statistics about holes due to forced alignments
$ pahole -C task_struct | 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 inet_timewait_death_row
  struct inet_timewait_death_row {
	atomic_t                   tw_count;             /*     0     4 */

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

	/* --- cacheline 1 boundary (64 bytes) --- */
	struct inet_hashinfo *     hashinfo __attribute__((__aligned__(64)); /*    64     8 */
	int                        sysctl_max_tw_buckets; /*    72     4 */

	/* size: 128, cachelines: 2, members: 3 */
	/* sum members: 16, holes: 1, sum holes: 60 */
	/* padding: 52 */
	/* forced alignments: 1, forced holes: 1, sum forced holes: 60 */
  };
  $

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Arnaldo Carvalho de Melo ec772f21f6 fprintf: Show the number of forced alignments in a class
For instance, for task_struct:

  $ pahole -C task_struct | 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 */
  };
  $

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Arnaldo Carvalho de Melo 52d1c75ea4 btfdiff: Use --suppress_aligned_attribute with -F dwarf
Now that we collect DWARF5's DW_AT_alignent, btdiff shows, for instance:

  $ btfdiff examples/tcp.o
  <SNIP>
  @@ -13450,7 +13450,7 @@ struct ip6_flowlabel {
   	struct in6_addr            dst;                  /*    16    16 */
   	struct ipv6_txoptions *    opt;                  /*    32     8 */
   	long unsigned int          linger;               /*    40     8 */
  -	struct callback_head       rcu __attribute__((__aligned__(8)); /*    48    16 */
  +	struct callback_head       rcu;                  /*    48    16 */
   	/* --- cacheline 1 boundary (64 bytes) --- */
   	u8                         share;                /*    64     1 */

  @@ -13616,7 +13616,7 @@ struct fib6_node {
   	__u16                      fn_flags;             /*    42     2 */
   	int                        fn_sernum;            /*    44     4 */
   	struct fib6_info *         rr_ptr;               /*    48     8 */
  -	struct callback_head       rcu __attribute__((__aligned__(8)); /*    56    16 */
  +	struct callback_head       rcu;                  /*    56    16 */

   	/* size: 72, cachelines: 2, members: 10 */
   	/* last cacheline: 8 bytes */
  $

So ask for those attributes to be suppressed when comparing BTF and
DWARF output.

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Arnaldo Carvalho de Melo 6cd6a6bd87 dwarves_fprintf: Allow suppressing the __attribute__((__aligned__(N))
So that we can use it in things like btfdiff.

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Arnaldo Carvalho de Melo f31ea292e3 dwarf_loader: Store the DW_AT_alignment if available
DWARF got a DW_AT_alignment as described in:

  http://dwarfstd.org/ShowIssue.php?issue=140528.1

This appeared first in DWARF5:

  http://dwarfstd.org/doc/DWARF5.pdf

In:

 ----------------------------------------------------------------------

Chapter 2.

General Description

2.24    Alignment

A debugging information entry may have a DW_AT_alignment attribute whose
value of class constant is a positive, non-zero, integer describing the
alignment of the entity.

For example, an alignment attribute whose value is 8 indicates that the
entity to which it applies occurs at an address that is a multiple of
eight (not a multiple of 8 or 256)

 ----------------------------------------------------------------------

Use it on a struct present in the running kernel, i.e. not specifying
which ELF file to look for the DWARF info to use:

  $ pahole -C inet_timewait_death_row
  struct inet_timewait_death_row {
  	atomic_t                   tw_count;             /*     0     4 */

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

  	/* --- cacheline 1 boundary (64 bytes) --- */
  	struct inet_hashinfo *     hashinfo __attribute__((__aligned__(64)); /*    64     8 */
  	int                        sysctl_max_tw_buckets; /*    72     4 */

  	/* size: 128, cachelines: 2, members: 3 */
  	/* sum members: 16, holes: 1, sum holes: 60 */
  	/* padding: 52 */
  };
  $

Now to do some tweaking to get that "__attribute..." part nicely, hum,
aligned in the pahole output :-)

BTW: the original struct is in the kernel sources:

  include/net/netns/ipv4.h

  struct inet_timewait_death_row {
          atomic_t                tw_count;

          struct inet_hashinfo    *hashinfo ____cacheline_aligned_in_smp;
          int                     sysctl_max_tw_buckets;
  };

Reported-by: Mark Wielaard <mark@klomp.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Arnaldo Carvalho de Melo c002873c44 dwarves_fprintf: Move invariant printing of ; to outside if block
Will facilitate printing something just before the ;

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Andrii Nakryiko 8ce85a1ad7 reorganize: Use class__find_holes() to recalculate holes
Instead of relying on error-prone adjustment of bit/byte holes, use
class__find_holes() to re-calculate them after members are moved around.

As part of that change, fix bug with not adjusting bit_offset, when
changing byte_offset.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:15 -03:00
Andrii Nakryiko 5d1c4029bd dwarves: Fix classification of byte/bit hole for aligned bitfield
This patch fixes a bug in class__find_holes() with determining byte hole
as a bit hole in case where previous member is not bitfield, but current
one is aligned bitfield. See example below, notice hole classification
hw_stopped field..

  $ cat test/bit_test.c
  struct s {
          long unused: 62;
          /* 2 bit hole */
          int hw_stopped;
          /* 4 byte hole */
          long unused2: 55;
          /* 9 bit padding */
  };

  int main() {
          static struct s s;
          return 0;
  }

  $ clang -g test/bit_test.c -o test/bit_test

  $ pahole -JV test/bit_test
  File test/bit_test:
  [1] STRUCT s kind_flag=1 size=24 vlen=3
          unused type_id=2 bitfield_size=62 bits_offset=0
          hw_stopped type_id=3 bitfield_size=0 bits_offset=64
          unused2 type_id=2 bitfield_size=55 bits_offset=128
  [2] INT long int size=8 bit_offset=0 nr_bits=64 encoding=SIGNED
  [3] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

BEFORE:

  $ pahole -F btf test/bit_test
  struct s {
          long int                   unused:62;            /*     0: 0  8 */

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

          int                        hw_stopped;           /*     8     4 */

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

          long int                   unused2:55;           /*    16: 0  8 */

          /* size: 24, cachelines: 1, members: 3 */
          /* sum members: 4 */
          /* sum bitfield members: 117 bits, bit holes: 2, sum bit holes: 34 bits */
          /* bit_padding: 9 bits */
          /* last cacheline: 24 bytes */
  };

AFTER:

  $ pahole -F btf test/bit_test
  struct s {
          long int                   unused:62;            /*     0: 0  8 */

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

          int                        hw_stopped;           /*     8     4 */

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

          long int                   unused2:55;           /*    16: 0  8 */

          /* size: 24, cachelines: 1, members: 3 */
          /* sum members: 4, holes: 1, sum holes: 4 */
          /* sum bitfield members: 117 bits, bit holes: 1, sum bit holes: 2 bits */
          /* bit_padding: 9 bits */
          /* last cacheline: 24 bytes */
  };

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 10:37:03 -03:00
Andrii Nakryiko 78c110a7ea dwarves: Revert semantics of member bit/byte hole
pahole --reorganize heavily depends on member's bit_hole and hole fields
to denote bit/byte holes *after* member. Previous commit "dwarves: use
bit sizes and bit/byte hole info in __class__fprintf" changed its
meaning to bit/byte hole *before* member to accomodate possible bit/byte
holes at the beginning of a struct. This change broke reorganization
algorithm, though, which is quite involved and isn't trivially
modifiable to accomodate new semantics.

This patch reverts the meaning of bit_hole and hole, but also introduces
per class pre_bit_hole/pre_hole to record initial bit/byte hole of a
struct. This allows to fix reorg code more easily and still handle
initial holes cases, if at the expense of being not as elegant.

Committer testing:

  $ time pahole -F btf --packable vmlinux | sort -nr -k4 | head
  bts_ctx     12288 8192 4096
  swsusp_info  4096  432 3664
  vc_data       792  496  296
  pci_dev      2488 2320  168
  rcu_state    3392 3240  152
  ptr_ring      192   40  152
  xdp_sock      960  840  120
  zone         1664 1552  112
  rcu_data      576  472  104
  rcu_node      576  480   96

  real	0m0.038s
  user	0m0.029s
  sys	0m0.017s
  $

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 10:09:33 -03:00
Andrii Nakryiko b56fed297e dwarves_fprintf: Count bitfield member sizes separately
Counting field sizes only in bits causes confusion and lots of differing
output, when compared to previous logic. This commit changes logic so
that it counts bit size of bitfield fields separately from byte size of
non-bitfield fields. In the end, if there were bit holes, this bit size
is emitted explicitly. This makes output for struct/unions not using
bitfields identical, while also preserving correctness (and data
completeness) for cases with bitfields and bit holes.

Example (-before/+after):
 struct cfg80211_pmsr_request_peer {
        u8                         addr[6];              /*     0     6 */

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

        struct cfg80211_chan_def   chandef;              /*     8    24 */

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

        u8                         report_ap_tsf:1;      /*    32: 0  1 */

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

        struct cfg80211_pmsr_ftm_request_peer ftm;       /*    36    12 */

        /* XXX last struct has 1 byte of padding */

        /* size: 48, cachelines: 1, members: 4 */
-       /* sum members: 43, holes: 2, sum holes: 5 */
-       /* bit holes: 1, sum bit holes: 7 bits */
+       /* sum members: 42, holes: 2, sum holes: 5 */
+       /* sum bitfield members: 1 bits, bit holes: 1, sum bit holes: 7 bits */
        /* paddings: 2, sum paddings: 5 */
        /* last cacheline: 48 bytes */
 };

For cases where there is only byte or bit hole, we still emit total byte and
bit sizes of all members as to not mislead user:
 struct sched_dl_entity {
... <snip ...
        unsigned int               dl_non_contending:1;  /*    84: 3  4 */
        unsigned int               dl_overrun:1;         /*    84: 4  4 */

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

        struct hrtimer             dl_timer;             /*    88    64 */

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

        /* --- cacheline 2 boundary (128 bytes) was 24 bytes ago --- */
        struct hrtimer             inactive_timer;       /*   152    64 */

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

        /* size: 216, cachelines: 4, members: 16 */
-       /* bit holes: 1, sum bit holes: 27 bits */
+       /* sum members: 212 */
+       /* sum bitfield members: 5 bits, bit holes: 1, sum bit holes: 27 bits */
        /* paddings: 2, sum paddings: 10 */
        /* last cacheline: 24 bytes */
 };

For structs with tightly packed bitfield, we emit total number of bits and also
convert them to bytes. E.g., for struct sock output :
struct sock {
... <snip ...
        /* size: 720, cachelines: 12, members: 84 */
-       /* sum members: 712, holes: 4, sum holes: 8 */
+       /* sum members: 707, holes: 4, sum holes: 8 */
+       /* sum bitfield members: 40 bits (5 bytes) */
        /* paddings: 1, sum paddings: 4 */
        /* last cacheline: 16 bytes */
 };

Suggested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Andrii Nakryiko c0fdc5e685 dwarf_loader: Use DWARF recommended uniform bit offset scheme
Use uniform bit offset scheme as described in DWARF standard (though
apparently not really followed by major compilers), in which bit offset
is a natural extension of byte offset in both big- and little-endian
architectures.

BEFORE:

1. Bit offsets for little-endian are output as offsets from highest-order bit
of underlying int to highest-order bit of bitfield, so double-backwards for
little-endian arch and counter to how byte offsets are used, which point to
lowest-order bit of underlying type. This makes first bitfield to have bit
offset 27, instead of natural 0.

2. Bit offsets for big-endian are output as expected, by referencing
highest-order bit offset from highest-order bit of underlying int. This is
natural for big-endian platform, e.g., first bitfield has bit offset of 0.

3. Big-endian target also has problem with determining bit holes, because bit
positions have to be calculated differently for little- and big-endian
platforms and previous commit changed pahole logic to follow little-endian
semantics.

4. BTF encoder outputs uniform bit offset for both little- and big-endian
format (following DWARF's recommended bit offset scheme)

5. BTF loader, though, follows DWARF loader's format and outputs little-endian
bit offsets "double-backwards".

  $ gcc -g dwarf_test.c -o dwarf_test
  $ pahole -F dwarf dwarf_test
  struct S {
          int                        j:5;                  /*     0:27  4 */
          int                        k:6;                  /*     0:21  4 */
          int                        m:5;                  /*     0:16  4 */
          int                        n:8;                  /*     0: 8  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit_padding: 8 bits */
          /* last cacheline: 4 bytes */
  };

  $ pahole -JV dwarf_test
  File dwarf_test:
  [1] STRUCT S kind_flag=1 size=4 vlen=4
          j type_id=2 bitfield_size=5 bits_offset=0
          k type_id=2 bitfield_size=6 bits_offset=5
          m type_id=2 bitfield_size=5 bits_offset=11
          n type_id=2 bitfield_size=8 bits_offset=16
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

  $ pahole -F btf dwarf_test
  struct S {
          int                        j:5;                  /*     0:27  4 */
          int                        k:6;                  /*     0:21  4 */
          int                        m:5;                  /*     0:16  4 */
          int                        n:8;                  /*     0: 8  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit_padding: 8 bits */
          /* last cacheline: 4 bytes */
  };

  $ aarch64-linux-gnu-gcc -mbig-endian -g -c dwarf_test.c -o dwarf_test.be
  $ pahole -F dwarf dwarf_test.be
  struct S {

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

          int                        j:5;                  /*     0: 0  4 */

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

          int                        k:6;                  /*     0: 5  4 */

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

          int                        m:5;                  /*     0:11  4 */

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

          int                        n:8;                  /*     0:16  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit holes: 4, sum bit holes: 760 bits */
          /* bit_padding: 16 bits */
          /* last cacheline: 4 bytes */

          /* BRAIN FART ALERT! 4 bytes != 24 (member bits) + 0 (byte holes) + 760 (bit holes), diff = -768 bits */
  };

  $ pahole -JV dwarf_test.be
  File dwarf_test.be:
  [1] STRUCT S kind_flag=1 size=4 vlen=4
          j type_id=2 bitfield_size=5 bits_offset=0
          k type_id=2 bitfield_size=6 bits_offset=5
          m type_id=2 bitfield_size=5 bits_offset=11
          n type_id=2 bitfield_size=8 bits_offset=16
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

  $ pahole -F btf dwarf_test.be
  struct S {

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

          int                        j:5;                  /*     0: 0  4 */

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

          int                        k:6;                  /*     0: 5  4 */

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

          int                        m:5;                  /*     0:11  4 */

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

          int                        n:8;                  /*     0:16  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit holes: 4, sum bit holes: 760 bits */
          /* bit_padding: 16 bits */
          /* last cacheline: 4 bytes */

          /* BRAIN FART ALERT! 4 bytes != 24 (member bits) + 0 (byte holes) + 760 (bit holes), diff = -768 bits */
  };

AFTER:

1. Same output for little- and big-endian binaries, both for BTF and DWARF
loader.

2. For little-endian target, bit offsets are natural extensions of byte offset,
counting from lowest-order bit of underlying int to lowest-order bit of a
bitfield.

3. BTF encoder still emits correct and natural bit offsets (for both binaries).

4. No more BRAIN FART ALERTs for big-endian.

  $ pahole -F dwarf dwarf_test
  struct S {
          int                        j:5;                  /*     0: 0  4 */
          int                        k:6;                  /*     0: 5  4 */
          int                        m:5;                  /*     0:11  4 */
          int                        n:8;                  /*     0:16  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit_padding: 8 bits */
          /* last cacheline: 4 bytes */
  };

  $ pahole -JV dwarf_test
  File dwarf_test:
  [1] STRUCT S kind_flag=1 size=4 vlen=4
          j type_id=2 bitfield_size=5 bits_offset=0
          k type_id=2 bitfield_size=6 bits_offset=5
          m type_id=2 bitfield_size=5 bits_offset=11
          n type_id=2 bitfield_size=8 bits_offset=16
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

  $ pahole -F btf dwarf_test
  struct S {
          int                        j:5;                  /*     0: 0  4 */
          int                        k:6;                  /*     0: 5  4 */
          int                        m:5;                  /*     0:11  4 */
          int                        n:8;                  /*     0:16  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit_padding: 8 bits */
          /* last cacheline: 4 bytes */
  };

  $ pahole -F dwarf dwarf_test.be
  struct S {
          int                        j:5;                  /*     0: 0  4 */
          int                        k:6;                  /*     0: 5  4 */
          int                        m:5;                  /*     0:11  4 */
          int                        n:8;                  /*     0:16  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit_padding: 8 bits */
          /* last cacheline: 4 bytes */
  };

  $ pahole -JV dwarf_test.be
  File dwarf_test.be:
  [1] STRUCT S kind_flag=1 size=4 vlen=4
          j type_id=2 bitfield_size=5 bits_offset=0
          k type_id=2 bitfield_size=6 bits_offset=5
          m type_id=2 bitfield_size=5 bits_offset=11
          n type_id=2 bitfield_size=8 bits_offset=16
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED

  $ pahole -F btf dwarf_test.be
  struct S {
          int                        j:5;                  /*     0: 0  4 */
          int                        k:6;                  /*     0: 5  4 */
          int                        m:5;                  /*     0:11  4 */
          int                        n:8;                  /*     0:16  4 */

          /* size: 4, cachelines: 1, members: 4 */
          /* bit_padding: 8 bits */
          /* last cacheline: 4 bytes */
  };

FOR REFERENCE. Relevant parts of DWARF output from GCC (clang outputs exactly
the same data) for both little- and big-endian binaries:

  $ readelf -wi dwarf_test
  Contents of the .debug_info section:
  <snip>
   <1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
      <2e>   DW_AT_name        : S
      <30>   DW_AT_byte_size   : 4
      <31>   DW_AT_decl_file   : 1
      <32>   DW_AT_decl_line   : 1
      <33>   DW_AT_decl_column : 8
      <34>   DW_AT_sibling     : <0x71>
   <2><38>: Abbrev Number: 3 (DW_TAG_member)
      <39>   DW_AT_name        : j
      <3b>   DW_AT_decl_file   : 1
      <3c>   DW_AT_decl_line   : 2
      <3d>   DW_AT_decl_column : 6
      <3e>   DW_AT_type        : <0x71>
      <42>   DW_AT_byte_size   : 4
      <43>   DW_AT_bit_size    : 5
      <44>   DW_AT_bit_offset  : 27
      <45>   DW_AT_data_member_location: 0
   <2><46>: Abbrev Number: 3 (DW_TAG_member)
      <47>   DW_AT_name        : k
      <49>   DW_AT_decl_file   : 1
      <4a>   DW_AT_decl_line   : 3
      <4b>   DW_AT_decl_column : 6
      <4c>   DW_AT_type        : <0x71>
      <50>   DW_AT_byte_size   : 4
      <51>   DW_AT_bit_size    : 6
      <52>   DW_AT_bit_offset  : 21
      <53>   DW_AT_data_member_location: 0
   <2><54>: Abbrev Number: 3 (DW_TAG_member)
      <55>   DW_AT_name        : m
      <57>   DW_AT_decl_file   : 1
      <58>   DW_AT_decl_line   : 4
      <59>   DW_AT_decl_column : 6
      <5a>   DW_AT_type        : <0x71>
      <5e>   DW_AT_byte_size   : 4
      <5f>   DW_AT_bit_size    : 5
      <60>   DW_AT_bit_offset  : 16
      <61>   DW_AT_data_member_location: 0
   <2><62>: Abbrev Number: 3 (DW_TAG_member)
      <63>   DW_AT_name        : n
      <65>   DW_AT_decl_file   : 1
      <66>   DW_AT_decl_line   : 5
      <67>   DW_AT_decl_column : 6
      <68>   DW_AT_type        : <0x71>
      <6c>   DW_AT_byte_size   : 4
      <6d>   DW_AT_bit_size    : 8
      <6e>   DW_AT_bit_offset  : 8
      <6f>   DW_AT_data_member_location: 0
   <2><70>: Abbrev Number: 0
   <1><71>: Abbrev Number: 4 (DW_TAG_base_type)
      <72>   DW_AT_byte_size   : 4
      <73>   DW_AT_encoding    : 5        (signed)
      <74>   DW_AT_name        : int
  <snip>

  $ readelf -wi dwarf_test.be
  Contents of the .debug_info section:
  <snip>
   <1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
      <2e>   DW_AT_name        : S
      <30>   DW_AT_byte_size   : 4
      <31>   DW_AT_decl_file   : 1
      <32>   DW_AT_decl_line   : 1
      <33>   DW_AT_sibling     : <0x6c>
   <2><37>: Abbrev Number: 3 (DW_TAG_member)
      <38>   DW_AT_name        : j
      <3a>   DW_AT_decl_file   : 1
      <3b>   DW_AT_decl_line   : 2
      <3c>   DW_AT_type        : <0x6c>
      <40>   DW_AT_byte_size   : 4
      <41>   DW_AT_bit_size    : 5
      <42>   DW_AT_bit_offset  : 0
      <43>   DW_AT_data_member_location: 0
   <2><44>: Abbrev Number: 3 (DW_TAG_member)
      <45>   DW_AT_name        : k
      <47>   DW_AT_decl_file   : 1
      <48>   DW_AT_decl_line   : 3
      <49>   DW_AT_type        : <0x6c>
      <4d>   DW_AT_byte_size   : 4
      <4e>   DW_AT_bit_size    : 6
      <4f>   DW_AT_bit_offset  : 5
      <50>   DW_AT_data_member_location: 0
   <2><51>: Abbrev Number: 3 (DW_TAG_member)
      <52>   DW_AT_name        : m
      <54>   DW_AT_decl_file   : 1
      <55>   DW_AT_decl_line   : 4
      <56>   DW_AT_type        : <0x6c>
      <5a>   DW_AT_byte_size   : 4
      <5b>   DW_AT_bit_size    : 5
      <5c>   DW_AT_bit_offset  : 11
      <5d>   DW_AT_data_member_location: 0
   <2><5e>: Abbrev Number: 3 (DW_TAG_member)
      <5f>   DW_AT_name        : n
      <61>   DW_AT_decl_file   : 1
      <62>   DW_AT_decl_line   : 5
      <63>   DW_AT_type        : <0x6c>
      <67>   DW_AT_byte_size   : 4
      <68>   DW_AT_bit_size    : 8
      <69>   DW_AT_bit_offset  : 16
      <6a>   DW_AT_data_member_location: 0
  <snip>

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Andrii Nakryiko 5104d1bef3 loaders: Record CU's endianness in dwarf/btf/ctf loaders
This patch records for each CU whether it's in little-endian or
big-endian data format. This flag will be used in subsequent commits to
adjust bit offsets where necessary, to make them uniform across
endianness. This patch doesn't have any effect on pahole's output.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Andrii Nakryiko 975757bc88 dwarves: Use bit sizes and bit/byte hole info in __class__fprintf
This patch changes __class__fprintf to rely on bit_size/bitfield_size,
calculated in corresponding loaders, instead of trying to guess correct
sizes on its own.

Bit and byte hole information is now stored in current field member and
stores information about holes *before* field. Previously hole
information was stored in previous field and was meant to represent
"holes after a field". Such approach makes it hard to report bit holes
at the very beginning of the struct:

struct s {
	int:4; /* this is bit hole, not a field in DWARF/BTF */
	int x:8;
};

With this change and previous bitfield calculation/fixup logic fixes,
there are no more discrepancies between DWARF/BTF for allyesconfig
kernel. There are also no more BRAIN FART ALERTS!

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00