Dumping a C++ object’s memory layout with Clang

December 17th, 2012 at 5:25 am

When one wants to understand the memory layout of structures and classes, the C/C++ operators sizeof and offsetof are very useful. However, when large C++ class hierarchies are involved, using these operators becomes tedious. Luckily, Clang has a very handly command-line flag to dump object layouts in a useful manner. This flag is somewhat hidden since it’s only accepted by the Clang front-end (the one you get when you pass -cc1 to clang) and not the gcc-compatible compiler driver (the one you get when simply executing clang).

Consider this code, for example:

class Base {
protected:
  int foo;
public:
  int method(int p) {
    return foo + p;
  }
};

struct Point {
  double cx, cy;
};

class Derived : public Base {
public:
  int method(int p) {
    return foo + bar + p;
  }
protected:
  int bar, baz;
  Point a_point;
  char c;
};

int main(int argc, char** argv) {
  return sizeof(Derived);
}

To see the layout, run clang -cc1 -fdump-record-layouts myfile.cpp. It will produce a separate report for each class and struct defined, but the most interesting one is for class Derived:

*** Dumping AST Record Layout
   0 | class Derived
   0 |   class Base (base)
   0 |     int foo
   4 |   int bar
   8 |   int baz
  16 |   struct Point a_point
  16 |     double cx
  24 |     double cy
     |   [sizeof=16, dsize=16, align=8
     |    nvsize=16, nvalign=8]

  32 |   char c
     | [sizeof=40, dsize=33, align=8
     |  nvsize=33, nvalign=8]

(the above is the output of Clang 3.2 running on 64-bit Linux)

We can see the layout of Derived objects, with the offset of every field (including the fields coming from base classes) in the left-most column. Some additional information is printed in the bottom – for example, sizeof – the total size, and dsize – data size without tail padding.

If we make method virtual in the Base and Derived classes, the size of the virtual-table pointer is also accounted for:

*** Dumping AST Record Layout
   0 | class Derived
   0 |   class Base (primary base)
   0 |     (Base vtable pointer)
   0 |     (Base vftable pointer)
   8 |     int foo
  12 |   int bar
  16 |   int baz
  24 |   struct Point a_point
  24 |     double cx
  32 |     double cy
     |   [sizeof=16, dsize=16, align=8
     |    nvsize=16, nvalign=8]

  40 |   char c
     | [sizeof=48, dsize=41, align=8
     |  nvsize=41, nvalign=8]

I’ll wrap up with a tip about using clang -cc1. Since this isn’t the compiler driver, it won’t go look for standard headers in the expected places, so using it on realistic source files can be a pain. The easiest way to do it, IMHO, is to run it on preprocessed source. How your source gets preprocessed depends on your build process, but it’s usually something like:

clang -E [your -I flags] myfile.cpp > myfile_pp.cpp

Related posts:

  1. How Clang handles the type / variable name ambiguity of C/C++
  2. Parsing C++ in Python with Clang
  3. Pure virtual destructors in C++
  4. Basic source-to-source transformation with Clang
  5. Stack frame layout on x86-64

9 Responses to “Dumping a C++ object’s memory layout with Clang”

  1. MarekNo Gravatar Says:

    If you are using Visual Studio, you can print the same using undocumented /d1reportAllClassLayout switch:
    class Derived size(40):
    +---
    | +--- (base class Base)
    0 | | foo
    | +---
    4 | bar
    8 | baz
    | <alignment member> (size=4)
    16 | Point a_point
    32 | c
    | <alignment member> (size=7)
    +---

    and second example:
    class Derived size(40):
    +---
    | +--- (base class Base)
    0 | | {vfptr}
    4 | | foo
    | +---
    8 | bar
    12 | baz
    16 | Point a_point
    32 | c
    | <alignment member> (size=7)
    +---
    Derived::$vftable@:
    | &Derived_meta
    | 0
    0 | &Derived::method

    Derived::method this adjustor: 0

  2. MarekNo Gravatar Says:

    To make the output of VC readable use undname.exe to it.

  3. Eden CohenNo Gravatar Says:

    Small typo (how can it be – isn’t this the output of clang ?):
    In the first dump [int foo] is shown as [int f]

  4. elibenNo Gravatar Says:

    @Eden,

    Fixed, thanks.

  5. WilfredNo Gravatar Says:

    Hi Eli,

    Very helpful post, thanks. Do you have any idea why “sizeof” is required in order to get the object’s layout? If the class is not instantiated with the “new” keyword, or its size is not checked in the code, its layout won’t be printed at all. For example, the following example succeeds:

    struct s { bool b; };
    
    int main(){
    	s *p = new s;
    	return 1;
    }

    The layout of the struct “s” is printed correctly. But this won’t work:

    struct s { bool b; };
    
    int main(){
    	s o;
    	s *op = &o;
    	return 1;
    }

    I get no output at all from “clang -cc1 -fdump-record-layouts test.cpp” in this case. Is this a bug in the clang frontend?

  6. elibenNo Gravatar Says:

    @Wilfred,

    Good question – I noticed this also. Actually I don’t know the exact cases in which Clang prints out the layout – maybe it makes sense to ask in the cfe-dev list.

  7. NilsNo Gravatar Says:

    clang -cc1 -fdump-record-layouts does not work! it will not complain, but also not print out anything. Use clang++ instead ;)

  8. AndNo Gravatar Says:

    that’s an interesting feature I immediately wanted to try out. Unfortunately, my code uses some c++-2011 features and ‘clang -cc1′ thus rejects it. any idea how to bring it into c++-2011 mode? (passing ‘-std=c++11′ to ‘clang -cc1′ only produces an error message…)

  9. elibenNo Gravatar Says:

    @And,

    Clang is supposed to support C++11 features; could you be using an old version of Clang? If you believe you run into a real problem, send the code you’re trying to build + the exact invocation line to the cfe-dev@ mailing list.

Leave a Reply

To post code with preserved formatting, enclose it in `backticks` (even multiple lines)