Debugger tricks: Break on a specific Win32 last error value in Windows Vista

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’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).

One way you might approach this is with a conditional breakpoint, but the SetLastError path is typically frequently hit, so this is often be problematic in terms of performance, even in user mode debugging on the local computer.

On Windows Vista, there is an undocumented hook inside of NTDLL (which is now responsible for the bulk of the logic behind SetLastError) 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.

For the moment, however, you can set ntdll!g_dwLastErrorToBreakOn to a non-zero value (via the ed command in the debugger) to ask NTDLL to execute a breakpoint when it sees that last error value being set. Obviously, this won’t catch things that modify the field in the TEB directly, but anything using SetLastError or RtlSetLastWin32Error will be checked against this value (in the context of the debuggee).

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’t have access to:

0:002> ed ntdll!g_dwLastErrorToBreakOn 5
0:002> g

[...] Perform an operation to cause ERROR_ACCESS_DENIED

(1864.2774): Break instruction exception
  - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00000000`76d6fdf0 cc              int     3
0:004> k
Call Site
ntdll!DbgBreakPoint
ntdll! ?? ::FNODOBFM::`string'+0x377b
kernel32!BaseSetLastNTError+0x16
kernel32!CreateFileW+0x325
SHELL32!CEnumFiles::InitAndFindFirst+0x7a
SHELL32!CEnumFiles::InitAndFindFirstRetry+0x3e
SHELL32!CFileSysEnum::_InitFindDataEnum+0x5e
SHELL32!CFileSysEnum::Init+0x135
SHELL32!CFSFolder::EnumObjects+0xd3
SHELL32!_GetEnumerator+0x189
SHELL32!CEnumThread::_RunEnum+0x6d
SHELL32!CEnumThread::s_EnumThreadProc+0x13
SHLWAPI!WrapperThreadProc+0xfc
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d

(The debugger is slightly confused about symbol names in NTDLL due to the binary being reorganized into function chunks, but “ntdll! ?? ::FNODOBFM::`string’+0x377b” is part of ntdll!RtlSetLastWin32Error.)

Sometimes, it can be useful to add “debugger knobs” 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’s a global variable in NTDLL named ntdll!ShowSnaps 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.

(Incidentally, debugger-settable global variables like ntdll!ShowSnaps are a good example of a correct way of using debug prints in release builds, though there are certainly many other good ways to do so.)

Update: Andrew Rogers points out that g_dwLastErrorToBreakOn existed on Srv03 as well, though it was resident in kernel32 (kernel32!g_dwLastErrorToBreakOn) 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.

Update: Pavel Lebedinsky points out that I neglected to mention that as a consequence of the internal BaseSetLastNTError routine in kernel32 on Srv03 not going through kernel32!SetLastError, 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.

4 Responses to “Debugger tricks: Break on a specific Win32 last error value in Windows Vista”

  1. Pavel Lebedinsky says:

    On WS03 g_dwLastErrorToBreakOn works only when SetLastError is called from kernel32.dll:

    http://blogs.msdn.com/danpear/archive/2007/04/06/2033100.aspx

    So it’s not very useful in practice.

  2. Marc Sherman says:

    When you say, “reorganized into function chunks”, does that mean ntdll has been profiled and the “hottest” blocks of code are clustered together, regardless of which function they belong to?

    How did you detemine that “ntdll! ?? ::FNODOBFM::`string’+0x377b” is really part of ntdll!RtlSetLastWin32Error?

    thanks,
    Marc

  3. Skywing says:

    As in the functions are no longer contiguous in memory. Hot code paths are clustered together with hot code paths of other functions. “Cold” code paths are moved elsewhere. That way you save on paging I/O by maximizing the amount of relative data on each cdode page.

    As for how I determined that, just by disassembling RtlSetLastWin32Error and looking at all of the control flows.

  4. gary hansen says:

    i had installed nvidia geforcefx 5500 then downloaded explorer7 now i get a r6025 runtime error virtual c++ how do i fix this in easy ”i sorta know what im doing” fashion…….did i mess it up with my video card or was it in the auto update i allowed to download from microsoft update center