{"id":43183,"date":"2015-01-09T07:00:00","date_gmt":"2015-01-09T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2015\/01\/09\/finding-the-leaked-object-reference-by-scanning-memory-example\/"},"modified":"2019-03-13T12:11:55","modified_gmt":"2019-03-13T19:11:55","slug":"20150109-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150109-00\/?p=43183","title":{"rendered":"Finding the leaked object reference by scanning memory: Example"},"content":{"rendered":"<p>An assertion failure was hit in some code. <\/p>\n<pre>\n    \/\/ There should be no additional references to the object at this point\n    assert(m_cRef == 1);\n<\/pre>\n<p>But the reference count was 2. That&#8217;s not good. Where is that extra reference and who took it? <\/p>\n<p>This was not code I was at all familiar with, so I went back to first principles: Let&#8217;s hope that the reference was not leaked but rather that the reference was taken and not released. And let&#8217;s hope that the memory hasn&#8217;t been paged out. (Because <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2007\/04\/26\/2277346.aspx\">debugging is an exercise in optimism<\/a>.) <\/p>\n<pre>\n1: kd&gt; s 0 0fffffff 00 86 ec 00\n04effacc  00 86 ec 00 c0 85 ec 00-00 00 00 00 00 00 00 00  ................ \/\/ us\n0532c318  00 86 ec 00 28 05 00 00-80 6d 32 05 03 00 00 00  ....(....m2..... \/\/ rogue\n<\/pre>\n<p>The first hit is the reference to the object from the code raising the assertion. The second hit is the interesting one. That&#8217;s probably the rogue reference. But who is it? <\/p>\n<pre>\n1: kd&gt; ln 532c318\n1: kd&gt;\n<\/pre>\n<p>It does not report as belong to any module, so it&#8217;s not a global variable. <\/p>\n<p>Is it a reference from a stack variable? If so, then a stack trace of the thread with the active reference may tell us who is holding the reference and why. <\/p>\n<pre>\n1: kd&gt; !process -1 4\nPROCESS 907ef980  SessionId: 2  Cid: 06cc    Peb: 7f4df000  ParentCid: 0298\n    DirBase: 9e983000  ObjectTable: a576f560  HandleCount: 330.\n    Image: contoso.exe\n\n        THREAD 8e840080  Cid 06cc.0b78  Teb: 7f4de000 Win32Thread: 9d04b3e0 WAIT\n        THREAD 91e24080  Cid 06cc.08d8  Teb: 7f4dd000 Win32Thread: 00000000 WAIT\n        THREAD 8e9a3580  Cid 06cc.09f8  Teb: 7f4dc000 Win32Thread: 9d102cc8 WAIT\n        THREAD 8e2be080  Cid 06cc.0878  Teb: 7f4db000 Win32Thread: 9d129978 WAIT\n        THREAD 82c08080  Cid 06cc.0480  Teb: 7f4da000 Win32Thread: 00000000 WAIT\n        THREAD 90552400  Cid 06cc.0f5c  Teb: 7f4d9000 Win32Thread: 9d129628 WAIT\n        THREAD 912c9080  Cid 06cc.02ec  Teb: 7f4d8000 Win32Thread: 00000000 WAIT\n        THREAD 8e9e8680  Cid 06cc.0130  Teb: 7f4d7000 Win32Thread: 9d129cc8 READY on processor 0\n        THREAD 914b8b80  Cid 06cc.02e8  Teb: 7f4d6000 Win32Thread: 9d12d568 WAIT\n        THREAD 9054ab00  Cid 06cc.0294  Teb: 7f4d5000 Win32Thread: 9d12fac0 WAIT\n        THREAD 909a2b80  Cid 06cc.0b54  Teb: 7f4d4000 Win32Thread: 00000000 WAIT\n        THREAD 90866b80  Cid 06cc.0784  Teb: 7f4d3000 Win32Thread: 93dbb4e0 RUNNING on processor 1\n        THREAD 90cfcb80  Cid 06cc.08c4  Teb: 7f3af000 Win32Thread: 93de0cc8 WAIT\n        THREAD 90c39a00  Cid 06cc.0914  Teb: 7f3ae000 Win32Thread: 00000000 WAIT\n        THREAD 90629480  Cid 06cc.0bc8  Teb: 7f3ad000 Win32Thread: 00000000 WAIT\n<\/pre>\n<p>Now I have to dump the stack boundaries to see whether the address in question lies within the stack range. <\/p>\n<pre>\n1: kd&gt; dd 7f4de000 l3\n7f4de000  ffffffff 00de0000 00dd0000\n1: kd&gt; dd 7f4dd000 l3\n7f4dd000  ffffffff 01070000 01060000\n...\n1: kd&gt; dd 7f4d7000 l3\n7f4d7000  ffffffff 04e00000 04df0000 \/\/ our stack\n...\n<\/pre>\n<p>The rogue reference did not land in any of the stack ranges, so it&#8217;s probably on the heap. Fortunately, since it&#8217;s on the heap, it&#8217;s probably part of some larger object. And let&#8217;s hope (see: optimism) that it&#8217;s an object with virtual methods. <\/p>\n<pre>\n0532c298  73617453\n0532c29c  74654d68\n0532c2a0  74616461\n0532c2a4  446e4961\n0532c2a8  00007865\n0532c2ac  00000000\n0532c2b0  76726553 USER32!_NULL_IMPORT_DESCRIPTOR  (USER32+0xb6553)\n0532c2b4  44497265\n0532c2b8  45646e49\n0532c2bc  41745378 contoso!CMumble::CMumble+0x4c\n0532c2c0  00006873\n0532c2c4  00000000\n0532c2c8  4e616843\n0532c2cc  79546567\n0532c2d0  4e496570\n0532c2d4  00786564\n0532c2d8  2856662a\n0532c2dc  080a9b87\n0532c2e0  00f59fa0\n0532c2e4  05326538\n0532c2e8  00000000\n0532c2ec  00000000\n0532c2f0  0000029c\n0532c2f4  00000001\n0532c2f8  00000230\n0532c2fc  fdfdfdfd\n0532c300  45ea1370 contoso!CFrumble::`vftable'\n0532c304  45ea134c contoso!CFrumble::`vftable'\n0532c308  00000000\n0532c30c  05b9a040\n0532c310  00000002\n0532c314  00000001\n0532c318  00ec8600\n<\/pre>\n<p>Hooray, there is a vtable a few bytes before the pointer, and the contents of the memory do appear to match a <code>CFrumble<\/code> object, so I think we found our culprit. <\/p>\n<p>I was able to hand off the next stage of the investigation (why is a Frumble being created with a reference to the object?) to another team member with more expertise with Frumbles. <\/p>\n<p>(In case anybody cared, the conclusion was that this was a variation of a known bug.) <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Digging through the haystack.<\/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":[26],"class_list":["post-43183","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Digging through the haystack.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/43183","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=43183"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/43183\/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=43183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=43183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=43183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}