{"id":147,"date":"2007-07-24T17:39:23","date_gmt":"2007-07-24T22:39:23","guid":{"rendered":"http:\/\/www.nynaeve.net\/?p=147"},"modified":"2019-12-13T17:37:41","modified_gmt":"2019-12-13T22:37:41","slug":"debugger-tricks-break-on-a-specific-win32-last-error-value-in-windows-vista","status":"publish","type":"post","link":"http:\/\/www.nynaeve.net\/?p=147","title":{"rendered":"Debugger tricks: Break on a specific Win32 last error value in Windows Vista"},"content":{"rendered":"<p>Often times, one type of problem that you might want to track down in a debugger (aside from a crash) is a particular function failing in a certain way.  In the case of most Win32 functions, you&#8217;ll often get some sort of (hopefully meaningful) last error code.  Sometimes you might need to know why that error is returned, or where it originated from (in the case of a last error value that is propagated up through several functions).<\/p>\n<p>One way you might approach this is with a <a title=\"Debugger tricks: API call logging, the quick'n'dirty way (part 3)\" href=\"http:\/\/www.nynaeve.net\/?p=145\">conditional breakpoint<\/a>, but the <em>SetLastError<\/em> path is typically frequently hit, so this is often be problematic in terms of performance, even in user mode debugging on the local computer.<\/p>\n<p>On Windows Vista, there is an undocumented hook inside of NTDLL (which is now responsible for the bulk of the logic behind <em>SetLastError<\/em>) that allows you to configure a program to break into the debugger when a particular error code is being set as the last error.  This is new to Vista, and as it is not documented (at least not anywhere that I can see), it might not be around indefinitely.<\/p>\n<p>For the moment, however, you can set <em>ntdll!g_dwLastErrorToBreakOn<\/em> to a non-zero value (via the <em>ed<\/em> command in the debugger) to ask NTDLL to execute a breakpoint when it sees that last error value being set.  Obviously, this won&#8217;t catch things that modify the field in the TEB directly, but anything using <em>SetLastError<\/em> or <em>RtlSetLastWin32Error<\/em> will be checked against this value (in the context of the debuggee).<\/p>\n<p>For example, you might see something like this if you ask NTDLL to break on error 5 (ERROR_ACCESS_DENIED) and then try to open a file or directory that you don&#8217;t have access to:<\/p>\n<pre>0:002&gt; ed ntdll!g_dwLastErrorToBreakOn 5\r\n0:002&gt; g\r\n\r\n[...] Perform an operation to cause ERROR_ACCESS_DENIED\r\n\r\n(1864.2774): Break instruction exception\r\n  - code 80000003 (first chance)\r\nntdll!DbgBreakPoint:\r\n00000000`76d6fdf0 cc              int     3\r\n0:004&gt; k\r\nCall Site\r\nntdll!DbgBreakPoint\r\nntdll! ?? ::FNODOBFM::`string'+0x377b\r\nkernel32!BaseSetLastNTError+0x16\r\nkernel32!CreateFileW+0x325\r\nSHELL32!CEnumFiles::InitAndFindFirst+0x7a\r\nSHELL32!CEnumFiles::InitAndFindFirstRetry+0x3e\r\nSHELL32!CFileSysEnum::_InitFindDataEnum+0x5e\r\nSHELL32!CFileSysEnum::Init+0x135\r\nSHELL32!CFSFolder::EnumObjects+0xd3\r\nSHELL32!_GetEnumerator+0x189\r\nSHELL32!CEnumThread::_RunEnum+0x6d\r\nSHELL32!CEnumThread::s_EnumThreadProc+0x13\r\nSHLWAPI!WrapperThreadProc+0xfc\r\nkernel32!BaseThreadInitThunk+0xd\r\nntdll!RtlUserThreadStart+0x1d<\/pre>\n<p>(The debugger is slightly confused about symbol names in NTDLL due to the binary being reorganized into function chunks, but &#8220;<em>ntdll! ?? ::FNODOBFM::`string&#8217;+0x377b<\/em>&#8221; is part of <em>ntdll!RtlSetLastWin32Error<\/em>.)<\/p>\n<p>Sometimes, it can be useful to add &#8220;debugger knobs&#8221; like this to your program that can be used to enable special diagnostics behavior that might be useful while debugging something.  Several other components provide options like this; for example, there&#8217;s a global variable in NTDLL named <em>ntdll!ShowSnaps<\/em> that you can set to 1 in order to enable a large volume of debug print spew about the symbol import resolution process when the loader is resolving imported modules and symbols.<\/p>\n<p>(Incidentally, debugger-settable global variables like <em>ntdll!ShowSnaps<\/em> are a good example of a <a title=\"An introduction to DbgPrintEx (and why it isn\u00e2\u20ac\u2122t an excuse to leave DbgPrints on by default in release builds)\" href=\"http:\/\/www.nynaeve.net\/?p=143\">correct way<\/a> of using debug prints in release builds, though there are certainly many other good ways to do so.)<\/p>\n<p><em>Update: <strong>Andrew Rogers<\/strong> points out that <em>g_dwLastErrorToBreakOn<\/em> existed on Srv03 as well, though it was resident in kernel32 (<em>kernel32!g_dwLastErrorToBreakOn<\/em>) and not NTDLL in that timeframe.  As the last error logic was finally moved entirely to NTDLL in the Vista timeframe, so was the last error breakpoint hook.<\/em><\/p>\n<p><em>Update: <strong><a title=\"Pavel Lebedinsky\" href=\"http:\/\/www.nynaeve.net\/?p=147#comment-17077\">Pavel Lebedinsky<\/a><\/strong> points out that I neglected to mention that as a consequence of the internal <em>BaseSetLastNTError<\/em> routine in kernel32 on Srv03 not going through <em>kernel32!SetLastError<\/em>, the functionality available in Srv03 is generally much less useful (only catches things external to kernel32) than in Vista.  Which, to be clear, is more my fault in not making this point known and not Andrew getting it wrong.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Often times, one type of problem that you might want to track down in a debugger (aside from a crash) is a particular function failing in a certain way. In the case of most Win32 functions, you&#8217;ll often get some sort of (hopefully meaningful) last error code. Sometimes you might need to know why that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[2,5],"tags":[],"_links":{"self":[{"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=\/wp\/v2\/posts\/147"}],"collection":[{"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=147"}],"version-history":[{"count":1,"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=\/wp\/v2\/posts\/147\/revisions"}],"predecessor-version":[{"id":574,"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=\/wp\/v2\/posts\/147\/revisions\/574"}],"wp:attachment":[{"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=147"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=147"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.nynaeve.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=147"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}