.Net GC 結構體GCInfo剖析,

前言:
dotnet已經發展到了 preview 7的版本,這個GC回收的文章或者視頻非常少,幾乎沒有看到,有個別的也是模糊不清或者三言兩語,或者太過於籠統,或者作者自己都沒搞明白爲啥是這樣。至於深入到GC裏面的GCinfo結構體就更少見了。因爲這個結構體裏面包含了根對象所在的地址,對於垃圾回收的第一個階段標記至關重要的一環。這篇文章來解構下這個結構體。

本文以二進制和Windbg來分析下

概念:
GCInfo實際上是一個位數組,以一個位或者多個位來標記包含的字段的意義。
如何解析這個位數組呢?


代碼:

     static void Main(string[] args)
     {
         Program pm = new Program();
         GC.Collect();
         Console.ReadLine();
     }

反彙編GCInfo命令:Windbg !u -gcinfo MethodDesc
以上代碼編譯成exe,放在windbg裏面也只能僅僅看到殘缺不齊的信息,只有所謂的根對象距離Rbp寄存器的偏移位置,而且它也沒有說明這些信息是如何得到的。如下所示:00007ffead724918是Program.Main的MethodDesc

0:000> !u -gcinfo 00007ffead724918
Normal JIT generated code
ConsoleApp11.Program.Main(System.String[])
ilAddr is 0000025D05E82050 pImport is 0000021EE1FFB040
Begin 00007FFEAD685EC0, size 67

F:\Visual Studio Project\ConsoleApp7\ConsoleApp11\Program.cs @ 40:
Prolog size: 0
Security object: <none>
GS cookie: <none>
PSPSym: <none>
Generics inst context: <none>
PSP slot: <none>
GenericInst slot: <none>
Varargs: 0
Frame pointer: rbp
Wants Report Only Leaf: 0
Size of parameter area: 20
Return Kind: Scalar
Code size: 67
Untracked: +rbp+10 +rbp-8 +rbp-10 +rbp-18
00007ffe`ad685ec0 55              push    rbp
00007ffe`ad685ec1 4883ec40        sub     rsp,40h
00007ffe`ad685ec5 488d6c2440      lea     rbp,[rsp+40h]
00007ffe`ad685eca c5d857e4        vxorps  xmm4,xmm4,xmm4
00007ffe`ad685ece c5fa7f65e8      vmovdqu xmmword ptr [rbp-18h],xmm4
//此處省略部分彙編

字段
比如以下這些字段的值從何而來,代表什麼意義?

Prolog size: 0
Security object: <none>
GS cookie: <none>
PSPSym: <none>
Generics inst context: <none>
PSP slot: <none>
GenericInst slot: <none>
Varargs: 0
Frame pointer: rbp
Wants Report Only Leaf: 0
Size of parameter area: 20
Return Kind: Scalar
Code size: 67

解析
要理清楚這些,必須通過CLR源碼來解構。
可以參考:
https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/gcinfodecoder.cpp

一.在釐清如何結構GCInfo之前,需要知道是如何獲取到GCInfo的。其實原理很簡單
1.遍歷所有的託管線程
2.通過託管線程找到託管線程變量Frame(也就是幀棧)
3.通過線程的幀棧找到線程對應的寄存器的值,比如Rip,Rsp,Rbp 等關鍵信息
4.當Rip被找到可以通過相應的API 尋找Rip所在的函數以及函數範圍,也就是實例化EECodeInfo。調用函數爲EECodeInfo::Init,而參數則爲Rip。這一步是比較重要的。
5.當初始化EECodeInfo之後,就可以計算出,當前函數裏面包含的變量信息,字段信息,函數信息等等。
6.GCInfo就是初始化EECodeInfo在此階段被找到,但是GCInfo的初始化則是在RyuJiit裏面執行。這個點要注意。
7.有了關鍵信息之後,就可以通過API函數RtlVirtualUnwind來回滾幀棧,循環找到需要的各種GC信息
具體的文章可以參考:
https://mp.weixin.qq.com/s?__biz=Mzg5NDYwNjU4MA==&mid=2247483946&idx=1&sn=6da6e76b7799ed818a6ff35affa27de0&chksm=c01c4fb1f76bc6a7cadc7e79f10dcb6a776b476a82e8628567ac178a82f44886e9190b706718&token=916104265&lang=zh_CN#rd
注意這裏麪包含了兩個循環,一個是託管線程的循環,然後是每一個託管線程裏面的幀棧循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章