object-introspection/website/docs/addrbook-this.md
2022-12-19 06:37:51 -08:00

7.8 KiB

title
this pointers

We specify exactly which function and arguments are to be introspected through a probe specification (terminology borrowed from DTrace). It's simply a colon delimited tuple that specifies exactly what object we are interested in and where in the code we want to observe it. For example, to introspect the AddressBook object we can measure it at the entry to its DumpContacts() method and the specification would be:

    entry:_ZN11AddressBook12DumpContactsEv:this

There are a few points to note here:

  • We are using the AddressBook::DumpContacts method. Note that we specify the function name using the mangled C++ name which can be found using readelf -sW /path/to/binary | grep symbol.
  • We are introspecting the object itself through the this specifier.
  • We are introspecting on entry to the method but we could have chosen to introspect the object the object at the return point from the AddressBook::DumpContacts() method which would be useful if state had been altered during execution of he method.

We use the oid debugger to capture the object itself from the address book application. Every second a contact is added to the address book with the AddContact() method and the address book is dumped with the DumpContacts() method. After running for a minute or so let's capture the Address book object:

$ build/oid -S 'entry:_ZN11AddressBook12DumpContactsEv:this' -p `pgrep addrbook` -c build/oid-cfg.toml -J
Attached to pid 4039830
SUCCESS

The -J flag instructed oid to dump the captured objects introspection data in a JSON file:

$ ~/object-introspection# ls -l oid_out.json
-rw-r--r-- 1 root root 78975 Dec 16 20:35 oid_out.json

This is a simple object but the resulting JSON becomes quite large when more than a handful of contacts have been added. Let's examine some sections to see what we can glean about the object that has been captured:

$ jq . oid_out.json
[
  {
    "name": "this",
    "typePath": "this",
    "typeName": "AddressBook",
    "isTypedef": false,
    "staticSize": 64,
    "dynamicSize": 23185,
    "paddingSavingsSize": 4,
    "members": [

The root type is an AddressBook object as we'd expect and it has a static footprint of 64 bytes with its data members in total having a dynamic memory footprint of 23185 bytes.

The first member is simply an integer for the rev data member which has a 4 byte static memory footprint on this platform.

    "members": [
      {
        "name": "rev",
        "typePath": "rev",
        "typeName": "int",
        "isTypedef": false,
        "staticSize": 4,
        "dynamicSize": 0
      },

Next we have a std::string object for the top level Owner member. Note how this is expanded down to the base types which show the string has a static footprint of 32 bytes but we can see that it is empty as it has a dynamicSize of 0. The capacity of 15 bytes shows the Short String optimization buffer in a libstdc++ std::string object.

      {
        "name": "Owner",
        "typePath": "Owner",
        "typeName": "string",
        "isTypedef": true,
        "staticSize": 32,
        "dynamicSize": 0,
        "members": [
          {
            "name": "",
            "typePath": "",
            "typeName": "basic_string<char, std::char_traits<char>, std::alloca
tor<char> >",
            "isTypedef": false,
            "staticSize": 32,
            "dynamicSize": 0,
            "length": 0,
            "capacity": 15,
            "elementStaticSize": 1
          }
        ]
      },

The Entries member gets a bit more interesting as it introduces usage of a C++ container class, the std::vector. More accurately, it is a vector of Contact objects as can be seen in the typeName JSON member for the root of the Entries object shown below. Note that the vector currently has an allocated capacity of 128 Contact elements of which 71 are actual Contact objects. The 23185 bytes of dynamically allocated objects in the vector is composed of the 71 x 96 byte Contact objects plus whatever memory that has been dynamically allocated for string content in those objects:

      {
        "name": "Entries",
        "typePath": "Entries",
        "typeName": "vector<Contact, std::allocator<Contact> >",
        "isTypedef": false,
        "staticSize": 24,
        "dynamicSize": 23185,
        "pointer": 140720550450568,
        "length": 71,
        "capacity": 128,
        "elementStaticSize": 96,

For the sake of brevity let's just inspect the first Contact object shown below. It's static footprint at 96 bytes is the three strings at 32 bytes each. We can see that the first and third string have strings larger than the 15 bytes allocated for the short string optimization buffer and are therefore allocated in dynamic memory that is external to the string objects themselves. The second string has no dynamic memory allocated as the 14 byte character sequence it is housing fits within the 15 byte pre-allocated buffer:


        "members": [
          {
            "name": "",
            "typePath": "Contact[]",
            "typeName": "Contact",
            "isTypedef": false,
            "staticSize": 96,
            "dynamicSize": 65,
            "members": [
              {
                "name": "firstName",
                "typePath": "firstName",
                "typeName": "string",
                "isTypedef": true,
                "staticSize": 32,
                "dynamicSize": 35,
                "members": [
                  {
                    "name": "",
                    "typePath": "",
                    "typeName": "basic_string<char, std::char_traits<char>, std::allocator<char> >",
                    "isTypedef": false,
                    "staticSize": 32,
                    "dynamicSize": 35,
                    "length": 35,
                    "capacity": 35,
                    "elementStaticSize": 1
                  }
                ]
              },
              {
                "name": "lastName",
                "typePath": "lastName",
                "typeName": "string",
                "isTypedef": true,
                "staticSize": 32,
                "dynamicSize": 0,
                "members": [
                  {
                    "name": "",
                    "typePath": "",
                    "typeName": "basic_string<char, std::char_traits<char>, std::allocator<char> >",
                    "isTypedef": false,
                    "staticSize": 32,
                    "dynamicSize": 0,
                    "length": 14,
                    "capacity": 15,
                    "elementStaticSize": 1
                  }
                ]
              },
              {
                "name": "number",
                "typePath": "number",
                "typeName": "string",
                "isTypedef": true,
                "staticSize": 32,
                "dynamicSize": 30,
                "members": [
                  {
                    "name": "",
                    "typePath": "",
                    "typeName": "basic_string<char, std::char_traits<char>, std::allocator<char> >",
                    "isTypedef": false,
                    "staticSize": 32,
                    "dynamicSize": 30,
                    "length": 18,
                    "capacity": 30,
                    "elementStaticSize": 1
                  }
                ]
              }

In total we have 71 Contact objects introspected here and each can be analyzed as we have done above:

$ ~/object-introspection# jq . /tmp/oit.oit.json.fmt | grep -c "\"typeName\": \"Contact\""
71