I: Background
1. Storytelling
The previous post talked aboutC# program compiled to Native code
macro process, a fan friend raised a question about whether it's possible to make a change in the dotnet publish release process for theAOT compiler
Intercepting for source-level debugging is a good problem to have and a must for deep research, so let's share this one.
II: Managed and Unmanaged Debuggers
1. Introduction to the debugger
I'm sure we all know by nowAOT Compiler ()
It is written in C# code, which means it is a managed program, and there are two debuggers for debugging managed programs:
- Visual Studio Hosted Debugger
Debugging C# code it's a no-brainer, the downside is the lack of means to view unmanaged parts.
- WinDbg Unmanaged Debugger
Debugging C/C++ is a great tool, but using it to debug managed C# code, while it works, is not very intuitive in all aspects of variable display.
A screenshot is below, so all in all there are pros and cons to each:
2. Test code
For the sake of demonstration, let's start with a test code, which is very simple.
internal class Program
{
static void Main(string[] args)
{
("Hello, World!");
();
}
}
Once you have the code, you can watch how to implement the respective interceptions with Visual Studio and Windbg.
III: Debugger Interception in Action
1. WinDbg Intercept
An unmanaged debugger that is king of the Windows platform, use it to hijack theIt's very convenient to have the registry's
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\
The screenshot below shows how to configure a Deubgger key:
next usedotnet publish
Publish the program, and after a few moments you will see that windbg immediately starts to intercept it, and thenctrl+o
Open the cs file where we need to place the breakpoints, such as the kernel'sCompilation
method, and then g is executed directly after the breakpoint, as shown in the following screenshot:
From the trigrams I got a hit, and with dv I can also see the memory address of each local variable, isn't that interesting.
Overall this approach is simple and brutal to use, but debugging C# with an unmanaged debugger like windbg is always a bit of a challenge.identity or status is unverified, or inconsistent in detail
The better way would still be to use a professional-grade homebrew like visual studio, wouldn't it?
2. Visual Studio Intercept
Previous articleTold you about executiondotnet publish
The call is made from the catalog.nuget\packages\\8.0.8\tools
Under the, the screenshot is below:
In order to use the hosted debugger, we will manually compile the project to replace the one here, as shown in the following screenshot:
In order to allow VS to be attached to processes, ilc provides a--waitfordebugger
parameters, refer to the following:
PS D:\csharpapplication\21.20240910\src\Example\Example_21_1> ilc -h
Description:
.NET Native IL Compiler
Usage:
ilc <input-file-path>... [options]
Arguments:
<input-file-path> Input file(s)
Options:
-?, -h, --help Show help and usage information
...
--waitfordebugger Pause to give opportunity to attach debugger
The effect of this parameter is to pass the Let the program pause so you can use VS to Attach, that's how it's written in the source code:
public Program(ILCompilerRootCommand command)
{
_command = command;
if (Get())
{
("Waiting for debugger to attach. Press ENTER to continue");
();
}
}
But on my hand-compiled one I use I can't seem to stop it, so I'll change it slightly here, with the following reference:
public Program(ILCompilerRootCommand command)
{
_command = command;
while (!)
{
("Waiting for debugger to attach. Press ENTER to continue");
//();
(1000);
}
}
Next, recompile the project, which will generate the directoryruntime\artifacts\bin\coreclr\windows.\ilc\net8.0
Copy all files under the nugut directory to the.nuget\packages\\8.0.8\tools
Down, the screenshot is below:
Once everything is ready, the next step is to use thedotnet publish
Redistribute the program and you can see from the cmd output that it is waiting for attach to attach.
PS D:\csharpapplication\21.20240910\src\Example\Example_21_1> dotnet publish -r win-x64 -c Debug -o D:\testdump
The project to be restored is being identified…
All items are up to date,unretrievable。
Example_21_1 -> D:\csharpapplication\21.20240910\src\Example\Example_21_1\bin\Debug\net8.0\win-x64\Example_21_1.d
ll
Generating native code
Waiting for debugger to attach. Press ENTER to continue
Waiting for debugger to attach. Press ENTER to continue
Waiting for debugger to attach. Press ENTER to continue
Waiting for debugger to attach. Press ENTER to continue
Waiting for debugger to attach. Press ENTER to continue
In the VS menu Debug -> Attach to Process to our process, you can see that really hit, we look at this debugging experience is not much higher, the screenshot is as follows:
Those who have experienced this approach I believe there are some new questions, that is, it is too much trouble to repeat the debugging, can we just start with thetriggering program
way to debug? That's what we're going to talk about next.
3. VS startup debugging for ilc
For those of you who have read the previous post, you know that before every AOT compilation there is a file in the native directory that is the source of input for the AOT Compiler, the screenshot is as follows:
So it's perfectly fine to use it as a startup parameter for the project, and next we'll set the@D:\csharpapplication\21.20240910\src\Example\Example_21_1\obj\Debug\net8.0\win-x64\native\Example_21_1.
Put it in the command line of the ILCompiler project, as shown in the following screenshot:
Once the configuration is done, next put theExample_21_1.dll,Example_21_1.obj,Example_21_1.def
All three blocks are changed to full paths, referenced below:
D:\csharpapplication\21.20240910\src\Example\Example_21_1\obj\Debug\net8.0\win-x64\Example_21_1.dll
-o:D:\csharpapplication\21.20240910\src\Example\Example_21_1\obj\Debug\net8.0\win-x64\native\Example_21_1.obj
-r:C:\Users\Administrator\.nuget\packages\-x64\8.0.8\runtimes\win-x64\lib\net8.0\
-r:C:\Users\Administrator\.nuget\packages\\8.0.8\sdk\
...
--targetarch:x64
--dehydrate
-g
--exportsfile:D:\csharpapplication\21.20240910\src\Example\Example_21_1\obj\Debug\net8.0\win-x64\native\Example_21_1.def
...
Directly F5 start ILCompiler project, you can see the success of debugging easily, this way is a good solution to the problem of repeated debugging, screenshots are as follows:
III: Summary
Hijacking the AOT Compiler itself for source-level debugging is an interesting topic in its own right, and constantly intervening in the various stages of the Compiler's compilation is believed to provide some unusual means of learning AOT in depth.