object-introspection/assets/js/2ed49688.623c954e.js
github-actions[bot] b50beb8bce deploy: f4a1bd3d99
2023-10-07 02:49:29 +00:00

1 line
13 KiB
JavaScript

"use strict";(self.webpackChunkoi_web=self.webpackChunkoi_web||[]).push([[770],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>u});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){o(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},p="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),p=l(n),m=o,u=p["".concat(c,".").concat(m)]||p[m]||h[m]||r;return n?a.createElement(u,i(i({ref:t},d),{},{components:n})):a.createElement(u,i({ref:t},d))}));function u(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[p]="string"==typeof e?e:o,i[1]=s;for(var l=2;l<r;l++)i[l]=n[l];return a.createElement.apply(null,i)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},6693:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var a=n(7462),o=(n(7294),n(3905));const r={title:"this pointers"},i=void 0,s={unversionedId:"addrbook-this",id:"addrbook-this",title:"this pointers",description:"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:",source:"@site/docs/addrbook-this.md",sourceDirName:".",slug:"/addrbook-this",permalink:"/docs/addrbook-this",draft:!1,editUrl:"https://github.com/facebookexperimental/object-introspection/docs/addrbook-this.md",tags:[],version:"current",frontMatter:{title:"this pointers"},sidebar:"mysidebar",previous:{title:"A Simple Address Book Example",permalink:"/docs/addrbook-intro"},next:{title:"Function Arguments",permalink:"/docs/addrbook-funcargs"}},c={},l=[],d={toc:l},p="wrapper";function h(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"We specify exactly which function and arguments are to be introspected through a ",(0,o.kt)("strong",{parentName:"p"},(0,o.kt)("em",{parentName:"strong"},"probe specification"))," (terminology borrowed from ",(0,o.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/DTrace"},"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 ",(0,o.kt)("inlineCode",{parentName:"p"},"AddressBook")," object we can measure it at the entry to its ",(0,o.kt)("inlineCode",{parentName:"p"},"DumpContacts()")," method and the specification would be:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"}," entry:_ZN11AddressBook12DumpContactsEv:this\n")),(0,o.kt)("p",null,"There are a few points to note here:"),(0,o.kt)("ul",null,(0,o.kt)("li",null,"We are using the ",(0,o.kt)("code",null,"AddressBook::DumpContacts")," method. Note that we specify the function name using the mangled C++ name which can be found using ",(0,o.kt)("code",null,"readelf -sW /path/to/binary | grep symbol"),"."),(0,o.kt)("li",null,"We are introspecting the object itself through the ",(0,o.kt)("code",null,"this")," specifier."),(0,o.kt)("li",null,"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 ",(0,o.kt)("code",null,"AddressBook::DumpContacts()")," method which would be useful if state had been altered during execution of he method.")),(0,o.kt)("p",null,"We use the ",(0,o.kt)("inlineCode",{parentName:"p"},"oid")," debugger to capture the object itself from the address book application. Every second a contact is added to the address book with the ",(0,o.kt)("inlineCode",{parentName:"p"},"AddContact()")," method and the address book is dumped with the ",(0,o.kt)("inlineCode",{parentName:"p"},"DumpContacts()")," method. After running for a minute or so let's capture the Address book object:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ build/oid -S 'entry:_ZN11AddressBook12DumpContactsEv:this' -p `pgrep addrbook` -c build/oid-cfg.toml -J\nAttached to pid 4039830\nSUCCESS\n")),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"-J")," flag instructed ",(0,o.kt)("inlineCode",{parentName:"p"},"oid")," to dump the captured objects introspection data in a JSON file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ ~/object-introspection# ls -l oid_out.json\n-rw-r--r-- 1 root root 78975 Dec 16 20:35 oid_out.json\n")),(0,o.kt)("p",null,"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:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ jq . oid_out.json\n[\n {\n "name": "this",\n "typePath": "this",\n "typeName": "AddressBook",\n "isTypedef": false,\n "staticSize": 64,\n "dynamicSize": 23185,\n "paddingSavingsSize": 4,\n "members": [\n')),(0,o.kt)("p",null,"The root type is an ",(0,o.kt)("code",null,"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."),(0,o.kt)("p",null,"The first member is simply an integer for the ",(0,o.kt)("code",null,"rev")," data member which has a 4 byte static memory footprint on this platform."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},' "members": [\n {\n "name": "rev",\n "typePath": "rev",\n "typeName": "int",\n "isTypedef": false,\n "staticSize": 4,\n "dynamicSize": 0\n },\n')),(0,o.kt)("p",null,"Next we have a ",(0,o.kt)("code",null,"std::string")," object for the top level ",(0,o.kt)("code",null,"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 ",(0,o.kt)("code",null,"dynamicSize")," of 0. The ",(0,o.kt)("code",null,"capacity")," of 15 bytes shows the Short String optimization buffer in a libstdc++ ",(0,o.kt)("code",null,"std::string")," object."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},' {\n "name": "Owner",\n "typePath": "Owner",\n "typeName": "string",\n "isTypedef": true,\n "staticSize": 32,\n "dynamicSize": 0,\n "members": [\n {\n "name": "",\n "typePath": "",\n "typeName": "basic_string<char, std::char_traits<char>, std::alloca\ntor<char> >",\n "isTypedef": false,\n "staticSize": 32,\n "dynamicSize": 0,\n "length": 0,\n "capacity": 15,\n "elementStaticSize": 1\n }\n ]\n },\n')),(0,o.kt)("p",null,"The ",(0,o.kt)("code",null,"Entries")," member gets a bit more interesting as it introduces usage of a C++ container class, the ",(0,o.kt)("code",null,"std::vector"),". More accurately, it is a vector of ",(0,o.kt)("code",null,"Contact")," objects as can be seen in the ",(0,o.kt)("code",null,"typeName")," JSON member for the root of the ",(0,o.kt)("code",null,"Entries")," object shown below. Note that the vector currently has an allocated ",(0,o.kt)("code",null,"capacity")," of 128 ",(0,o.kt)("code",null,"Contact")," elements of which 71 are actual ",(0,o.kt)("code",null,"Contact")," objects. The 23185 bytes of dynamically allocated objects in the vector is composed of the 71 x 96 byte ",(0,o.kt)("code",null,"Contact")," objects plus whatever memory that has been dynamically allocated for string content in those objects:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},' {\n "name": "Entries",\n "typePath": "Entries",\n "typeName": "vector<Contact, std::allocator<Contact> >",\n "isTypedef": false,\n "staticSize": 24,\n "dynamicSize": 23185,\n "pointer": 140720550450568,\n "length": 71,\n "capacity": 128,\n "elementStaticSize": 96,\n')),(0,o.kt)("p",null,"For the sake of brevity let's just inspect the first ",(0,o.kt)("inlineCode",{parentName:"p"},"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:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n "members": [\n {\n "name": "",\n "typePath": "Contact[]",\n "typeName": "Contact",\n "isTypedef": false,\n "staticSize": 96,\n "dynamicSize": 65,\n "members": [\n {\n "name": "firstName",\n "typePath": "firstName",\n "typeName": "string",\n "isTypedef": true,\n "staticSize": 32,\n "dynamicSize": 35,\n "members": [\n {\n "name": "",\n "typePath": "",\n "typeName": "basic_string<char, std::char_traits<char>, std::allocator<char> >",\n "isTypedef": false,\n "staticSize": 32,\n "dynamicSize": 35,\n "length": 35,\n "capacity": 35,\n "elementStaticSize": 1\n }\n ]\n },\n {\n "name": "lastName",\n "typePath": "lastName",\n "typeName": "string",\n "isTypedef": true,\n "staticSize": 32,\n "dynamicSize": 0,\n "members": [\n {\n "name": "",\n "typePath": "",\n "typeName": "basic_string<char, std::char_traits<char>, std::allocator<char> >",\n "isTypedef": false,\n "staticSize": 32,\n "dynamicSize": 0,\n "length": 14,\n "capacity": 15,\n "elementStaticSize": 1\n }\n ]\n },\n {\n "name": "number",\n "typePath": "number",\n "typeName": "string",\n "isTypedef": true,\n "staticSize": 32,\n "dynamicSize": 30,\n "members": [\n {\n "name": "",\n "typePath": "",\n "typeName": "basic_string<char, std::char_traits<char>, std::allocator<char> >",\n "isTypedef": false,\n "staticSize": 32,\n "dynamicSize": 30,\n "length": 18,\n "capacity": 30,\n "elementStaticSize": 1\n }\n ]\n }\n')),(0,o.kt)("p",null,"In total we have 71 ",(0,o.kt)("inlineCode",{parentName:"p"},"Contact")," objects introspected here and each can be analyzed as we have done above:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ ~/object-introspection# jq . /tmp/oit.oit.json.fmt | grep -c "\\"typeName\\": \\"Contact\\""\n71\n')))}h.isMDXComponent=!0}}]);