{"id":605,"date":"2016-04-15T21:06:19","date_gmt":"2016-04-15T21:06:19","guid":{"rendered":"http:\/\/int03.co.uk\/blog\/?p=605"},"modified":"2016-04-15T21:20:46","modified_gmt":"2016-04-15T21:20:46","slug":"heisenbugs-the-dark-side-of-debugging","status":"publish","type":"post","link":"https:\/\/int03.co.uk\/blog\/2016\/04\/15\/heisenbugs-the-dark-side-of-debugging\/","title":{"rendered":"Heisenbugs; the Dark Side of Debugging"},"content":{"rendered":"<p>Some\u00a0of the most\u00a0difficult software bugs to track down are those that\u00a0disappear when you try to study them&#8230; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Heisenbug\" target=\"_blank\">Heisenbugs<\/a>! For example, when an issue is reproducible in a Release build, but disappears when you run the software in the Debugger.<\/p>\n<p>There are many possible explanations for this\u00a0which\u00a0include differences in memory initialisation, timing, code optimisation, code and memory\u00a0layout between Debug and Release builds. I won&#8217;t elaborate on the details\u00a0here, as they are already very\u00a0<a href=\"http:\/\/stackoverflow.com\/questions\/186237\/program-only-crashes-as-release-build-how-to-debug\" target=\"_blank\">well covered<\/a> on <a href=\"http:\/\/stackoverflow.com\/questions\/312312\/what-are-some-reasons-a-release-build-would-run-differently-than-a-debug-build?rq=1\" target=\"_blank\">stack overflow<\/a>.<\/p>\n<p>The reason for this post is that we recently encountered an issue in some code which has the following behaviour:<\/p>\n<ol>\n<li>Has\u00a0a reproducible bug in release mode (not an outright crash or exception, but fails to behave as expected).<\/li>\n<li>Runs perfectly well when started in the debugger, with no issues.<\/li>\n<li>When started without debugging, and the debugger is attached later, several\u00a0first chance exceptions are seen.<\/li>\n<\/ol>\n<p>My theory was that the exceptions weren&#8217;t being triggered when started from the Debugger because the memory initialisation (or other Debug\/Release differences) were preventing the exceptions from occurring. However, there was some debate on this point, with someone else suggesting that attaching the Debugger later was causing spurious exceptions. Their\u00a0suggestion\u00a0was that if these exceptions were genuine, they would also occur when started in the Debugger.<\/p>\n<p>I&#8217;ve seen this debate crop up frequently enough, that I thought it would be useful to create a very simple example to demonstrate an exception\u00a0that reproduces in Release, but works fine in Debug. Also, to confirm whether the same exception would reproduce when the Debugger is attached to a running process.<\/p>\n<p>Here is a first attempt at creating a very small Heisenbug demonstrator in C++, which has\u00a0a deliberate access violation:<\/p>\n<pre>#include &lt;iostream&gt;\r\n\r\nvoid main() {\r\n    int *x = new int[3];\r\n    if (x[1] != x[2]) delete [] x;\r\n    std::cin.get();\r\n    delete [] x;\r\n}<\/pre>\n<p>When\u00a0a Release build is\u00a0run in Visual Studio 2013, it demonstrates the following behaviour:<\/p>\n<ol>\n<li>When started without the Debugger and a key is pressed, it causes an exception.<\/li>\n<li>When started under the Debugger and a key is pressed, it exits cleanly and works fine.<\/li>\n<li>When started without the Debugger, and\u00a0the Debugger is later attached and a key is pressed, it causes an exception.<\/li>\n<\/ol>\n<p>If you run this a few times, you might find that Windows enables the &#8220;Fault Tolerant Heap&#8221; for the executable, and then you won&#8217;t be able to reproduce the exceptions. The solution is to <a href=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20120125-00\/?p=8463\" target=\"_blank\">disable the Fault Tolerant Heap for your executable<\/a>, by going to these keys:<\/p>\n<pre>HKLM\\<code>Software\\<wbr \/>Microsoft\\<wbr \/>Windows NT\\<wbr \/>CurrentVersion\\<wbr \/>AppCompatFlags\\<wbr \/>Layers\\<em>heisenbug<\/em><\/code><i>.exe\r\n\r\n<\/i>HKCU\\S<code>oftware\\<wbr \/>Microsoft\\<wbr \/>Windows NT\\<wbr \/>CurrentVersion\\<wbr \/>AppCompatFlags\\<wbr \/>Layers\\<em>heisenbug<\/em><\/code><em>.exe<\/em><\/pre>\n<p>Then delete the <strong>Fault\u00adTolerant\u00adHeap<\/strong> entry on both of these keys. In my case, only the HKLM version was needed.<\/p>\n<p>Returning to the code to explain how this works, here&#8217;s a commented version, explaining each line:<\/p>\n<pre>\/\/ create an array of 3 integers, without initialisation\r\nint *x = new int[3];\r\n\r\n\/\/ compare elements 1 and 2 (which are not initialised)\r\n\/\/ if they are different, delete the array and leave\r\n\/\/ the invalid pointer in x\r\nif (x[1] != x[2]) delete [] x;\r\n\r\n\/\/ wait for a key press\r\nstd::cin.get();\r\n\r\n\/\/ delete the array (potential access violation)\r\ndelete [] x;<\/pre>\n<p>There are two intentional bugs in this particular line of code:<\/p>\n<pre>if (x[1] != x[2]) delete [] x;<\/pre>\n<p>The first bug is that we are accessing uninitialised memory, because\u00a0the\u00a0array <strong>x<\/strong>\u00a0has not beeen initialised. When we compare <strong>x[1]<\/strong> and <strong>x[2]<\/strong> the result is unpredictable.\u00a0In a Release build there&#8217;s a good chance\u00a0(but not absolute certainty) that\u00a0these values will be different, and it will delete the array. Second, after deleting the array, the variable <strong>x<\/strong> still contains the (now invalid) pointer address. Later on, when this final line of code executes, it will access an invalid pointer, usually causing an access violation exception:<\/p>\n<pre>delete [] x;<\/pre>\n<p>However, when running in the Debugger, it&#8217;s very likely that the memory will have been initialised by the Debugger, and<strong> x[1]<\/strong> and <strong>x[2]<\/strong> will usually contain the same value. This prevents the pointer from being deleted, and avoids the access violation. The code runs without issue when being debugged.<\/p>\n<p>This serves as a very simple example of\u00a0a bug which only appears in Release, and disappears in the Debugger, due to accessing uninitialised memory (as mentioned above, this is only one of many possible causes of such an issue).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some\u00a0of the most\u00a0difficult software bugs to track down are those that\u00a0disappear when you try to study them&#8230; Heisenbugs! For example, when an issue is reproducible in a Release build, but disappears when you run the software in the Debugger. There are many possible explanations for this\u00a0which\u00a0include differences in memory initialisation, timing, code optimisation, code and &hellip; <a href=\"https:\/\/int03.co.uk\/blog\/2016\/04\/15\/heisenbugs-the-dark-side-of-debugging\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Heisenbugs; the Dark Side of Debugging<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/posts\/605"}],"collection":[{"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=605"}],"version-history":[{"count":6,"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/posts\/605\/revisions"}],"predecessor-version":[{"id":611,"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/posts\/605\/revisions\/611"}],"wp:attachment":[{"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=605"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=605"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/int03.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=605"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}