How to query the size of a struct

This article describes how to query the size of c structs and how to optimize their size by changing the field order.

For debugging purposes it can be useful to determine how much space a c struct needs. In addition that information can also be very valuable in optimizing the size of c structs. Understanding the storage allocation of the struct can also be important for cache alignment and to understand false sharing scenarios. There are two different ways to determine the data structure layout.

With the ptype command GDB can show the storage layout of a c struct. The following output shows the layout of the file struct.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(gdb) ptype struct file
type = struct file {
  union {
    struct llist_node f_llist;
    struct callback_head f_rcuhead;
    unsigned int f_iocb_flags;
  };
  struct path f_path;
  struct inode *f_inode;
  const struct file_operations *f_op;
  spinlock_t f_lock;
  atomic_long_t f_count;
  unsigned int f_flags;
  fmode_t f_mode;
  struct mutex f_pos_lock;
  loff_t f_pos;
  struct fown_struct f_owner;
  const struct cred *f_cred;
  struct file_ra_state f_ra;
  u64 f_version;
  void *f_security;
  void *private_data;
  struct hlist_head *f_ep;
  struct address_space *f_mapping;
  errseq_t f_wb_err;
  errseq_t f_sb_err;
}

The ptype command can also print the struct offsets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
(gdb) ptype /o struct file
/* offset      |    size */  type = struct file {
/*      0      |      16 */    union {
/*                     8 */        struct llist_node {
/*      0      |       8 */            struct llist_node *next;

/* total size (bytes):    8 */
} f_llist;
/*                    16 */        struct callback_head {
/*      0      |       8 */            struct callback_head *next;
/*      8      |       8 */            void (*func)(struct callback_head *);

/* total size (bytes):   16 */
} f_rcuhead;
/*                     4 */        unsigned int f_iocb_flags;

/* total size (bytes):   16 */
};
/*     16      |      16 */    struct path {
/*     16      |       8 */        struct vfsmount *mnt;
/*     24      |       8 */        struct dentry *dentry;

/* total size (bytes):   16 */
} f_path;
/*     32      |       8 */    struct inode *f_inode;
/*     40      |       8 */    const struct file_operations *f_op;
/*     48      |       4 */    spinlock_t f_lock;
/* XXX  4-byte hole      */
/*     56      |       8 */    atomic_long_t f_count;
/*     64      |       4 */    unsigned int f_flags;
/*     68      |       4 */    fmode_t f_mode;
/*     72      |      32 */    struct mutex {
/*     72      |       8 */        atomic_long_t owner;
/*     80      |       4 */        raw_spinlock_t wait_lock;
/*     84      |       4 */        struct optimistic_spin_queue {
/*     84      |       4 */            atomic_t tail;

/* total size (bytes):    4 */
} osq;
/*     88      |      16 */        struct list_head {
/*     88      |       8 */            struct list_head *next;
/*     96      |       8 */            struct list_head *prev;

/* total size (bytes):   16 */
} wait_list;

/* total size (bytes):   32 */
} f_pos_lock;
/*    104      |       8 */    loff_t f_pos;
/*    112      |      32 */    struct fown_struct {
/*    112      |       8 */        rwlock_t lock;
/*    120      |       8 */        struct pid *pid;
/*    128      |       4 */        enum pid_type pid_type;
/*    132      |       4 */        kuid_t uid;
/*    136      |       4 */        kuid_t euid;
/*    140      |       4 */        int signum;

/* total size (bytes):   32 */
} f_owner;
/*    144      |       8 */    const struct cred *f_cred;
/*    152      |      32 */    struct file_ra_state {
/*    152      |       8 */        unsigned long start;
/*    160      |       4 */        unsigned int size;
/*    164      |       4 */        unsigned int async_size;
/*    168      |       4 */        unsigned int ra_pages;
/*    172      |       4 */        unsigned int mmap_miss;
/*    176      |       8 */        loff_t prev_pos;

/* total size (bytes):   32 */
} f_ra;
/*    184      |       8 */    u64 f_version;
/*    192      |       8 */    void *f_security;
/*    200      |       8 */    void *private_data;
/*    208      |       8 */    struct hlist_head *f_ep;
/*    216      |       8 */    struct address_space *f_mapping;
/*    224      |       4 */    errseq_t f_wb_err;
/*    228      |       4 */    errseq_t f_sb_err;

/* total size (bytes):  232 */
}

The pahole command can be used from the command line. It displays the data layout of c structs. It also shows the holes in the storage layout.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
> pahole file

struct file {
  union {
    struct llist_node  f_llist;              /*     0     8 */
    struct callback_head f_rcuhead;          /*     0    16 */
    unsigned int       f_iocb_flags;         /*     0     4 */
  };                                               /*     0    16 */
  struct path                f_path;               /*    16    16 */
  struct inode *             f_inode;              /*    32     8 */
  const struct file_operations  * f_op;            /*    40     8 */
  spinlock_t                 f_lock;               /*    48     4 */

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

  atomic_long_t              f_count;              /*    56     8 */
  /* --- cacheline 1 boundary (64 bytes) --- */
  unsigned int               f_flags;              /*    64     4 */
  fmode_t                    f_mode;               /*    68     4 */
  struct mutex               f_pos_lock;           /*    72    32 */
  loff_t                     f_pos;                /*   104     8 */
  struct fown_struct         f_owner;              /*   112    32 */
  /* --- cacheline 2 boundary (128 bytes) was 16 bytes ago --- */
  const struct cred  *       f_cred;               /*   144     8 */
  struct file_ra_state       f_ra;                 /*   152    32 */
  u64                        f_version;            /*   184     8 */
  /* --- cacheline 3 boundary (192 bytes) --- */
  void *                     f_security;           /*   192     8 */
  void *                     private_data;         /*   200     8 */
  struct hlist_head *        f_ep;                 /*   208     8 */
  struct address_space *     f_mapping;            /*   216     8 */
  errseq_t                   f_wb_err;             /*   224     4 */
  errseq_t                   f_sb_err;             /*   228     4 */

  /* size: 232, cachelines: 4, members: 20 */
  /* sum members: 228, holes: 1, sum holes: 4 */
  /* last cacheline: 40 bytes */
};

With the -R switch pahole gives recommendations on how to reorganize the c struct to be more space efficient.