You can debug ngen (aka prejitted) code from VS2005 and also have the VS2005 IDE launch ngenned code. In practice, images are rarely ngenned as debuggable. And usually when under a debugger, assemblies are loaded with debuggable code-gen flags (In VS, this is the default). Thus it’s rare to load ngenned images when under a debugger. But it can be done.
The quick answer is:
1. Disable the VSHosting process. See instructions here. It’s also disabled if you enable interop-debugging.
2. Ensure the code is ngenned by running “ngen install”. You can add it as a post-build step under “Project Properties : Build Events”:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ngen.exe install $(TargetPath)
“Ngen install” is smart and will work for both Debug and Release configurations because it detects the debuggable attribute. Beware that edit-and-continue is not allowed on ngenned modules.
First, read Reid Wilkes’s excellent article on Ngen in .NET 2.0 for key background info about ngen.
When does a CLR load ngen images?
Once a managed module is going to be loaded:
- The CLR first uses policy (ini files, debugger overrides, debuggable attributes, etc) to determine what codegen flags to use. The interesting code-gen flags here are debuggable code vs. optimized code. (See here for one example of the difference).
- The CLR then looks for an ngenned version of the assembly in the global ngen cache with matching properties (timestamp, etc) including matching code-gen flags (which ngen calls “scenarios” in its command line help). If it finds a matching version, it uses that. Else it jits it.
This means that you can certainly have ngenned debuggable code; and that there’s nothing that prevents you from trying to debug ngenned optimized code.
How to get a debugger to load ngen code?
At a high-level, you just need the CLR to load ngen images, per the rules above, when under a debugger. Pracitcally, this means ensuring that a) the images are actually ngenned, and b) the debugger is configured in a way to use the ngen configurations. Some practical ways to do this are:
- Attach with a debugger after the ngen code is loaded: Since the app is launched outside of the debugger, you gaurantee that the debugger does not perturb anything.
- ngen the assemblies with the code-gen flags that the debugger is using: Generally, this means ngen for debuggable code.
- change the debugger settings to use code-gen flags that match what’s in the ngen cache:
VSHost complicates this with VS2005. The VS2005 IDE will pre-launch an executable called VSHost while you’re still editing, and then when actually hit F5 to start debugging, VS does an attach to the existing VSHost process, instead of doing a launch of the target. An easy way to disable VSHost is to follow the instructions here, or to enable interop-debugging.
How to tell if the ngen image is loaded?
ICorDebug does not tell you if a managed module is ngenned by-design because we wanted to treat ngen as a performance optimization that the CLR would be free to adjust in the future. For example, if we had ICDModule::IsNGenned, how would that version if the CLR started allowing part of the module became Ngenned? (This is similar as to why ICD doesn’t tell which thread is the finalizer). But there are some very reliable heuristics for whidbey:
1) Look for “.ni” modules
Look for a native module that matches the managed module name but ends in “.ni.dll” or “.ni.exe”, such as “mscorlib.ni.dll”. Managed-only debugging doesn’t show native modules. But you’ll see it if you’re interop-debugging. Or you can use a utility like listdlls.exe to view it.
For example, the debugger may see that the managed mscorlib module comes from: “C:\WINDOWS\assembly\GAC_32\mscorlib\220.127.116.11__b77a5c561934e089\mscorlib.dll”. But when you look at the native module list, you’ll see “c:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\634bc4cfaf50134f9bc49aedecf3b262\mscorlib.ni.dll”.
2) Look at the current IP
Another way to tell if the currently executing code is ngenned is to actually compare the current IP against the native module list. If it’s ngenned code, then the code is running from a native module, and so the instruction pointer should be in the native module list. For example, I stopped in some managed code, pull up the registers window (Debug : Windows : Registers), and I can see:
EAX = 30006530 EBX = 0012F4AC ECX = 01308F7C EDX = 00000000
ESI = 00187060 EDI = 00000000 EIP = 300025E2 ESP = 0012F47C
EBP = 0012F490 EFL = 00000212
300078F0 = 022B1ED4
The numbers are in hex. I then see that there’s an ngenned module “RedirectOutput.ni.exe” at address range 30000000-30012000. Since that includes the current IP, I know that I’m actually executing ngenned code.
Rules about debugging and ngen:
Here are some key ground rules:
- The mere presence of a debugger does not change code-gen flags at the API level. This was an explicit feature in .Net 2.0. (see here). However, debuggers (such as VS) may still choose to use ICorDebug to disable optimization when under a debugger.
- ICorDebug provides APIs to enable / disable optimizations. (ICorDebugProcess2::SetDesiredNGenCompilerFlags, ICorDebugModule::EnableJITDebugging, ICorDebugModule2::SetJitCompilerFlags)
- ICorDebug does not formally expose if a module is ngenned. Ngen is considered an internal CLR performance optimization from the debugger’s perspective.
- Code can be optimized / not-optimized on a per-module basis. See the “optimized” column in VS’s managed module window. A debugger can query for this based off ICorDebugModule2::GetJITCompilerFlags.
Example 1: Launch Debuggable ngenned code from VS IDE
In my case, my app is called RedirectOuput.exe.
1. Ensure that VS’s “enable optimizations” is not checked.
2. Build the Debug configuration of the app from VS IDE like normal.
3. After you build it from the VS IDE, use ‘ngen install’, which will ngen the app and the necessary dependencies.
As noted above, you can do this manually, or you can add it as a post-build step under “Project Properties : Build Events”:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ngen.exe install $(TargetPath)
Ideally, you’d have a macro for the full ngen path instead of explicitly listing it out. The output will be something like:
C:\bug\RedirectOutput\RedirectOutput\bin\Debug>ngen install RedirectOutput.exe
Microsoft (R) CLR Native Image Generator – Version 2.0.50727.42
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Installing assembly C:\bug\RedirectOutput\RedirectOutput\bin\Debug\RedirectOutput.exe
Compiling 1 assembly:
Compiling assembly C:\bug\RedirectOutput\RedirectOutput\bin\Debug\RedirectOutput.exe …
RedirectOutput, Version=18.104.22.168, Culture=neutral, PublicKeyToken=null <debug>
Note that ‘ngen install’ automatically used the <debug> flags, because it sees the debuggable attribute (the app was compiled with csc /debug+ because it was a debug configuration).
Thus in VS’s module window while interop-debugging, you see:
|1)||RedirectOutput.exe|| RedirectOutput.exe: Managed||No||c:\bug\RedirectOutput\RedirectOutput\bin\Debug\RedirectOutput.exe|
|2)||RedirectOutput.exe|| RedirectOutput.exe: Native||N/A||C:\bug\RedirectOutput\RedirectOutput\bin\Debug\RedirectOutput.exe|
|3)||RedirectOutput.ni.exe|| RedirectOutput.exe: Native||N/A||C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\RedirectOutput\ad196335d9f0794fb468d46d9bbe711d\RedirectOutput.ni.exe|
Case 1 is the managed module for RedirectOutput.exe. This corresponds to an ICorDebugModule.
Case 2 is the native module for RedirectOutput.exe, which shows up in the native module list because managed modules are generally implemented as native modules.
Case 3 is the ngen version of the module. You’ll notice this has the “.ni” in the name, and shows up in the native module list.
There’s no VShost entry because we’re interop-debugging.
Example 2: Launch Optimized ngen code from the VS IDE
Follow example 1, but switch the configuration to “Release”. Everything should just work.
Example 3: Use VS to launch an ngenned executable
If you’ve already ngenned the exe, you can directly open up an exe from VS (File : Open : Project / Solution, and then select the exe) and launch it. This will let you launch the exe under VS without building it.