一:背景
1.講故事
前幾天 B 站上有位朋友讓我從高級調試的角度來解讀下 .NET7 新出來的 AOT,畢竟這東西是新的,所以這一篇我就簡單摸索一下。
二:AOT 的幾個問題
1. 如何在 .NET7 中開啟 AOT 功能
在 .NET7 中開啟 AOT 非常方便,先來段測試代碼。
internal?class?Program{static?void?Main(string[]?args){Console.WriteLine("hello?world!");Debugger.Break();}}
然后在項目配置上新增 <PublishAot>true</PublishAot>
節點,如下輸出:
<Project?Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net7.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><PublishAot>true</PublishAot></PropertyGroup>
</Project>
接下來在項目中右鍵選擇 發布
,選擇一個輸出地,這樣一個 AOT 程序就完成了。

2. SOS 可以調試 AOT 程序嗎
這是很多朋友關心的話題,我們都知道 SOS 是用來撬開 CoreCLR 的,只要能看到 CoreCLR.dll,那 SOS 就能用,接下來用 WinDbg 附加到 ConsoleApp2.exe
上,使用 lm
觀察。
0:000>?lm
start?????????????end?????????????????module?name
00007ff6`11680000?00007ff6`1196f000???ConsoleApp2?C?(private?pdb?symbols)??C:\test\ConsoleApp2.pdb
00007ffe`692b0000?00007ffe`692c3000???kernel_appcore???(deferred)?????????????
00007ffe`6b3e0000?00007ffe`6b47d000???msvcp_win???(deferred)?????????????
00007ffe`6b480000?00007ffe`6b4ff000???bcryptPrimitives???(deferred)?????????????
00007ffe`6b660000?00007ffe`6b687000???bcrypt?????(deferred)?????????????
00007ffe`6b690000?00007ffe`6b6b2000???win32u?????(deferred)?????????????
00007ffe`6b720000?00007ffe`6b82a000???gdi32full???(deferred)?????????????
00007ffe`6b830000?00007ffe`6b930000???ucrtbase???(deferred)?????????????
00007ffe`6b9e0000?00007ffe`6bca7000???KERNELBASE???(deferred)?????????????
00007ffe`6bcb0000?00007ffe`6bd5a000???ADVAPI32???(deferred)?????????????
00007ffe`6be50000?00007ffe`6be7a000???GDI32??????(deferred)?????????????
00007ffe`6be80000?00007ffe`6bf1b000???sechost????(deferred)?????????????
00007ffe`6c180000?00007ffe`6c2a3000???RPCRT4?????(deferred)?????????????
00007ffe`6c440000?00007ffe`6c470000???IMM32??????(deferred)?????????????
00007ffe`6c600000?00007ffe`6c729000???ole32??????(deferred)?????????????
00007ffe`6c730000?00007ffe`6c7ce000???msvcrt?????(deferred)?????????????
00007ffe`6cc50000?00007ffe`6cfa4000???combase????(deferred)?????????????
00007ffe`6d160000?00007ffe`6d300000???USER32?????(deferred)?????????????
00007ffe`6d410000?00007ffe`6d4cd000???KERNEL32???(deferred)?????????????
00007ffe`6dc50000?00007ffe`6de44000???ntdll??????(pdb?symbols)??????????c:\mysymbols\ntdll.pdb\63E12347526A46144B98F8CF61CDED791\ntdll.pdb
從上面的輸出中驚訝的發現,居然沒有 clrjit.dll
和 coreclr.dll
,前者沒有很好理解,后者沒有就很奇怪了。。。
既然沒看到 coreclr.dll
這個動態鏈接庫,那至少目前用 sos 肯定是無法調試的,即使你強制加載也會報錯。
0:000>?.load??C:\Users\Administrator\.dotnet\sos64\sos.dll
0:000>?!t
Failed?to?find?runtime?module?(coreclr.dll?or?clr.dll?or?libcoreclr.so),?0x80004002
Extension?commands?need?it?in?order?to?have?something?to?do.
For?more?information?see?https://go.microsoft.com/fwlink/?linkid=2135652
到這里我的個人結論是:目前SOS無法對這類程序進行調試,如果大家用在生產上出現各種內存暴漲,CPU爆高問題,就要當心了。
3. AOT 真的沒有 CoreCLR 嗎
其實仔細想一想,這是不可能的,C# 的出發點就是作為一門托管語言而存在,再怎么發展也不會忘記這個初衷,所謂不忘初心,方得始終。
我們回過頭看下 ConsoleApp.exe
這個程序,有沒有發現,它居然有 3M 大小。

聰明的朋友應該猜到了,對,就是把 CoreCLR 打包到 exe 中了,這個太牛了,那怎么驗證呢?可以用 IDA 打開一下。

從圖中可以清晰的看到各種 gc_heap
相關的函數,這也驗證了為什么一個簡簡單單的 ConsoleApp.exe
有這么大Size的原因。
4. 真的無法調試 AOT 程序嗎
在 Windows 平臺上就沒有 WinDbg 不能調試的程序,所以 AOT 程序自然不在話下,畢竟按托管不行,大不了按非托管調試,這里我們舉一個 GC.Collect()
的源碼調試吧。
一段簡單的測試代碼。
internal?class?Program{static?void?Main(string[]?args){Debugger.Break();GC.Collect();}}
下斷點
熟悉 GC 的朋友應該知道我只需用 bp coreclr!WKS::GCHeap::GarbageCollect
下一個斷點就可以了,但剛才我也說了,內存中并沒有 coreclr
模塊,下面的 x 寫法肯定會報錯。
0:000>?x?coreclr!WKS::GCHeap::GarbageCollect^?Couldn't?resolve?'x?coreclr'
那怎么下呢?先輸個 k
觀察下調用棧有沒有什么新發現。
0:000>?k#?Child-SP??????????RetAddr???????????????Call?Site
00?00000011`5e52f628?00007ff6`7f288c5a?????ConsoleApp2!RhDebugBreak+0x2?[D:\a\_work\1\s\src\coreclr\nativeaot\Runtime\MiscHelpers.cpp?@?45]?
01?00000011`5e52f630?00007ff6`7f2f0e28?????ConsoleApp2!S_P_CoreLib_System_Diagnostics_Debugger__Break+0x3a?[/_/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs?@?17]?
02?00000011`5e52f6c0?00007ff6`7f1fe37e?????ConsoleApp2!ConsoleApp2__Module___StartupCodeMain+0x118
03?00000011`5e52f720?00007ff6`7f1f9540?????ConsoleApp2!wmain+0xae?[D:\a\_work\1\s\src\coreclr\nativeaot\Bootstrap\main.cpp?@?205]?
04?(Inline?Function)?--------`--------?????ConsoleApp2!invoke_main+0x22?[D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl?@?90]?
05?00000011`5e52f770?00007ffe`6d426fd4?????ConsoleApp2!__scrt_common_main_seh+0x10c?[D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl?@?288]?
06?00000011`5e52f7b0?00007ffe`6dc9cec1?????KERNEL32!BaseThreadInitThunk+0x14
07?00000011`5e52f7e0?00000000`00000000?????ntdll!RtlUserThreadStart+0x21
我去,int 3
函數也換了,成了 ConsoleApp2!RhDebugBreak+0x2
,不過也能看出來,應該將 coreclr 改成 ConsoleApp2 即可,輸出如下:
0:000>?bp?ConsoleApp2!WKS::GCHeap::GarbageCollect
breakpoint?0?redefined
0:000>?g
Breakpoint?0?hit
ConsoleApp2!WKS::GCHeap::GarbageCollect:
00007ff6`7f1a9410?48894c2408??????mov?????qword?ptr?[rsp+8],rcx?ss:00000011`5e52f5f0=0000000000000000
源碼也看的清清楚楚,路徑也是在 gc 目錄下。如下圖所示:

4. AOT 的實現源碼在哪里
觀察剛才的線程棧中的 D:\a\_work\1\s\src\coreclr\nativeaot\Bootstrap\main.cpp
可以發現,新增了一個名為 nativeaot
的目錄,這在 .NET 6
的 coreclr 源碼中是沒有的。

如果有感興趣的朋友,可以研究下源碼。
三:總結
總的來說,AOT 目前還是一個雛形階段,大家慎用吧,一旦出了問題,可不好事后調試哦,希望后續加強對 SOS 的支持。