{"id":1983,"date":"2014-01-24T07:00:00","date_gmt":"2014-01-24T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/01\/24\/non-psychic-debugging-looking-for-leaked-objects-by-their-vtable\/"},"modified":"2014-01-24T07:00:00","modified_gmt":"2014-01-24T07:00:00","slug":"non-psychic-debugging-looking-for-leaked-objects-by-their-vtable","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140124-00\/?p=1983\/","title":{"rendered":"Non-psychic debugging: Looking for leaked objects by their vtable"},"content":{"rendered":"<p>\nA programmer on the GHI team\nreported that they were hitting an assertion failure\nusing an internal library and asked for help debugging it.\n<\/p>\n<pre>\nABC!CFactoryBase::AssertZeroRef+0x2e\nABC!CFactoryBase::Unregister+0x1c\nABC!CWidgetFactory::Unregister+0x2d\nDEF!CWidgetManager::Uninitialize+0x4a\nDEF!CWidget::`scalar deleting destructor'+0xd\nABC!operator delete()+0x6\nABC!CFrame::Destroy+0xb\nABC!CFrame::WndProc+0x362\nUSER32!InternalCallWinProc+0x23\nUSER32!UserCallWinProcCheckWow+0x121\nUSER32!DispatchClientMessage+0x12d\nUSER32!__fnEMPTY+0x24\nntdll!KiUserCallbackDispatcher+0x3d\nUSER32!NtUserDestroyWindow+0xa\nABC!CFrame::WndProc+0x34\nUSER32!InternalCallWinProc+0x23\nUSER32!UserCallWinProcCheckWow+0x121\nUSER32!DispatchMessageWorker+0x411\nUSER32!DispatchMessageW+0x10\nGHI!wWinMain+0xa0\nGHI!__wmainCRTStartup+0x153\nKERNEL32!BaseThreadInitThunk+0xe\nntdll!__RtlUserThreadStart+0x23\nntdll!_RtlUserThreadStart+0x1b\n<\/pre>\n<p>\nI didn&#8217;t work on this internal library, but on the other hand\nI&#8217;m also not afraid to look inside and around.\n<\/p>\n<p>\nThe assertion failure said,\n&#8220;Assertion failed:\nAll widgets from a factory\nmust be destroyed before you can unregister\nthe factory.&#8221;\n<\/p>\n<p>\nThe factory does not keep a list of all the widgets it created.\nIt merely keeps a count and asserts that the count is zero\nwhen the factory is unregistered.&#8221;\n<\/p>\n<p>\nA good start would be to find the widgets that are still outstanding,\nso we can try to figure out why they weren&#8217;t destroyed.\n<\/p>\n<pre>\n0:000&gt; u ABC!CWidget::CWidget\n...\n1071158b mov     dword ptr [esi],offset ABC!CWidget::`vftable' (<font COLOR=\"red\">106da08c<\/font>)\n<\/pre>\n<p>\nThis gives us the widget vtable, so a memory scan should find\nall the outstanding widgets.\n<\/p>\n<pre>\n0:000&gt; !heap -search 106da08c\n    _HEAP @ 950000\n      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state\n        01eb12d8 000e 0000  [00]   <font COLOR=\"red\">01eb12e0<\/font>    00064 - (busy)\n          ABC!CWidget::`vftable'\n<\/pre>\n<p>\nOkay, so a search of the heap shows that there is only one widget,\nand it is at <code>0x01eb12e0<\/code>.\nLet&#8217;s see what that widget can tell us about who it is.\n<\/p>\n<pre>\n0:000&gt; dt ABC!CWidget 01eb12e0\n   +0x000 __VFN_table : 0x106da08c\n   +0x004 m_uBucketId      : 2\n   +0x008 m_rgClassData    :\n   +0x050 m_rgSharedData   :\n   +0x05c m_fLocked        : 1\n   +0x060 <font COLOR=\"red\">m_pszName<\/font>        : 0x01eba4c0  \"<font COLOR=\"red\">GHI_widget<\/font>\"\n<\/pre>\n<p>\nHey, how about that.\nThe widget conveniently has the name\n<code>GHI_widget<\/code>,\nwhich seems like a pretty good sign that the GHI component\nleaked a widget.\n<\/p>\n<p>\nNotice that I didn&#8217;t use any special knowledge of Widgets,\nWidget Factories,\nthe ABC component,\nor the GHI component.\nAll I did was take the error message that said,\n&#8220;You leaked a widget&#8221; and said,\n&#8220;Maybe I should go look for that widget.\nThat may tell us something.&#8221;\nI disassembled the widget constructor to look for a unique tag\ncommon to all widgets,\nand then scanned memory looking for that vtable.\nFrom the found object, I dumped its member variables looking\nfor some sort of clue as to its identity,\nand by an amazing stroke of luck,\nthe widget had a name.\n<\/p>\n<p>\nBack in my trainee days in tech support,\nif a customer asked a question that we couldn&#8217;t answer,\nwe escalated the problem to the next higher level and were\nencouraged to tag along and learn from the subject matter expert.\nThat way, when the problem came up again, we could solve it ourselves.\n<\/p>\n<p>\nIn other words, we were encouraged not to run away from information,\nbut to run <i>toward<\/i> it.\n<\/p>\n<p>\n(It helped that we weren&#8217;t graded on &#8220;number of cases closed per second.&#8221;)\n<\/p>\n<p>\nOne of the most important skills in a programmer is the willingness\nto look at code that you didn&#8217;t write.\nWhen I joined Microsoft,\nthis instinct to run toward information\nled me to watch as somebody else debugged a problem and learn from them.\nI would then go back and read the code that they debugged\nto see how much of it I could understand.\nAnd if I ran into a problem of my own,\nI dove in and read the source code to the component that\nwas giving me trouble,\neven if it was not a component I remotely had any responsibility for.\nMaybe I could figure out what it was doing,\nmaybe I couldn&#8217;t,\nbut at least I gave it a try.\nAnd when I went to another developer with my theory,\nI was told either that my understanding was correct,\nor that I had gotten it wrong and was told the correct answer.\nEither way, I learned a little bit more that day.\n<\/p>\n<p>\n<b>Exercise<\/b>:\nIf the widget had not had a name,\nwhat would be a reasonable next step in the investigation?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A programmer on the GHI team reported that they were hitting an assertion failure using an internal library and asked for help debugging it. ABC!CFactoryBase::AssertZeroRef+0x2e ABC!CFactoryBase::Unregister+0x1c ABC!CWidgetFactory::Unregister+0x2d DEF!CWidgetManager::Uninitialize+0x4a DEF!CWidget::`scalar deleting destructor&#8217;+0xd ABC!operator delete()+0x6 ABC!CFrame::Destroy+0xb ABC!CFrame::WndProc+0x362 USER32!InternalCallWinProc+0x23 USER32!UserCallWinProcCheckWow+0x121 USER32!DispatchClientMessage+0x12d USER32!__fnEMPTY+0x24 ntdll!KiUserCallbackDispatcher+0x3d USER32!NtUserDestroyWindow+0xa ABC!CFrame::WndProc+0x34 USER32!InternalCallWinProc+0x23 USER32!UserCallWinProcCheckWow+0x121 USER32!DispatchMessageWorker+0x411 USER32!DispatchMessageW+0x10 GHI!wWinMain+0xa0 GHI!__wmainCRTStartup+0x153 KERNEL32!BaseThreadInitThunk+0xe ntdll!__RtlUserThreadStart+0x23 ntdll!_RtlUserThreadStart+0x1b I didn&#8217;t work on [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-1983","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A programmer on the GHI team reported that they were hitting an assertion failure using an internal library and asked for help debugging it. ABC!CFactoryBase::AssertZeroRef+0x2e ABC!CFactoryBase::Unregister+0x1c ABC!CWidgetFactory::Unregister+0x2d DEF!CWidgetManager::Uninitialize+0x4a DEF!CWidget::`scalar deleting destructor&#8217;+0xd ABC!operator delete()+0x6 ABC!CFrame::Destroy+0xb ABC!CFrame::WndProc+0x362 USER32!InternalCallWinProc+0x23 USER32!UserCallWinProcCheckWow+0x121 USER32!DispatchClientMessage+0x12d USER32!__fnEMPTY+0x24 ntdll!KiUserCallbackDispatcher+0x3d USER32!NtUserDestroyWindow+0xa ABC!CFrame::WndProc+0x34 USER32!InternalCallWinProc+0x23 USER32!UserCallWinProcCheckWow+0x121 USER32!DispatchMessageWorker+0x411 USER32!DispatchMessageW+0x10 GHI!wWinMain+0xa0 GHI!__wmainCRTStartup+0x153 KERNEL32!BaseThreadInitThunk+0xe ntdll!__RtlUserThreadStart+0x23 ntdll!_RtlUserThreadStart+0x1b I didn&#8217;t work on [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/1983","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=1983"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/1983\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=1983"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=1983"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=1983"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}