日本国产亚洲-日本国产一区-日本国产一区二区三区-日本韩国欧美一区-日本韩国欧美在线-日本韩国欧美在线观看

當(dāng)前位置:雨林木風(fēng)下載站 > 技術(shù)開發(fā)教程 > 詳細(xì)頁面

用WinDbg探索CLR世界 [3] 跟蹤辦法的 JIT 過程

用WinDbg探索CLR世界 [3] 跟蹤辦法的 JIT 過程

更新時間:2022-04-26 文章作者:未知 信息來源:網(wǎng)絡(luò) 閱讀次數(shù):

本來想按照 sos 的幫助文件上命令的分類逐步介紹 WinDbg 下使用 sos 調(diào)試 CLR 程序,但發(fā)現(xiàn)這樣實(shí)在不夠直觀。索性改成根據(jù)我分析 CLR 的實(shí)際案例,step by step 介紹功能,這樣結(jié)構(gòu)上雖然混亂一點(diǎn),但更加直觀,也易于上手 :P

前面兩篇文章里面分別介紹了 WinDbg 的調(diào)試配置和線程的基本概念,這篇文章將針對 JIT 編譯對象方法的流程進(jìn)行分析,逐步介紹如何使用 WinDbg 調(diào)試 CLR 程序。

用WinDbg探索CLR世界 [1] - 安裝與環(huán)境配置
用WinDbg探索CLR世界 [2] - 線程

首先寫一個簡單的例子程序 demo.cs 并編譯為 demo.exe,使用配置好的 WinDbg 打開之:


以下為引用:

using System;

namespace flier
{
class EntryPoint
{
public void m1()
{
System.Console.Write("EntryPoint.m1()");
}

public void m2()
{
System.Console.Write("EntryPoint.m2()");
}

public static void Main()
{
EntryPoint ep = new EntryPoint();

ep.m1();
ep.m2();
}
}
}




WinDbg 會在載入 demo.exe 后中斷執(zhí)行。此時可以使用 .load sos 命令加載 sos.dll 命令擴(kuò)展,并用 .chain 驗(yàn)證加載是否成功;然后用 ld demo 命令加載 demo.exe 的調(diào)試符號文件,用 lm 命令驗(yàn)證加載是否成功。
然后用 ld kernel32 加載 Kernel32 的調(diào)試符號文件,并用 bp kernel32!LoadLibraryExW "du poi(esp+4)" 命令在載入 DLL 的函數(shù)入口加上斷點(diǎn)。接下來就是一路 g 指令,直到 mscorwks.dll 被加載。這個 mscorwks.dll 就是類似 JVM 中 jvm.dll 的虛擬機(jī)實(shí)現(xiàn)代碼,我們要了解的大部分功能都在其中。詳細(xì)的解釋可以參看我以前的一篇文章《.Net平臺下CLR程序載入原理分析》

在 mscorwks.dll 被載入后用 ld mscorwks 命令載入其調(diào)試符號庫,就可以正式開始我們的探索工作了 :D

目前使用到的 WinDbg 命令如下



以下為引用:

.load sos // 加載 sos 調(diào)試擴(kuò)展模塊,可使用 .chain 命令驗(yàn)證

ld demo // 加載 demo.exe 調(diào)試符號庫,可使用 lm 命令驗(yàn)證

ld kernel32 // 加載 kernel32.exe 調(diào)試符號庫

bp kernel32!LoadLibraryExW "du poi(esp+4)" // 設(shè)置斷點(diǎn)監(jiān)視何時 mscorwks.dll 被載入

g // 執(zhí)行直到 mscorwks.dll被加載

bd 0 // 清除前面設(shè)置的斷點(diǎn),開始對 mscorwks.dll 進(jìn)行處理

ld mscorwks // 加載 mscorwks.dll 調(diào)試符號庫





Don Box 在《.NET本質(zhì)論 第1卷:公共語言運(yùn)行庫》的第六章介紹了方法調(diào)用的內(nèi)部實(shí)現(xiàn)流程。其中提到方法表在 JIT 之前,保存的都是 call mscorwks.dll!PreStubWorker 調(diào)用,直到第一次使用時,才會對目標(biāo) IL 代碼進(jìn)行 JIT 編譯,并調(diào)用之。因此我們第一步可以在此函數(shù)上設(shè)置斷點(diǎn)(bp mscorwks!PreStubWorker),看看系統(tǒng)是如何調(diào)用此函數(shù)的。


以下為引用:

0:000> bp mscorwks!PreStubWorker
0:000> g
ModLoad: 70ad0000 70bb6000 E:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.100.0_x-ww_8417450B\comctl32.dll
ModLoad: 79780000 79980000 e:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll
ModLoad: 79980000 79ca6000 e:\windows\assembly\nativeimages1_v1.1.4322\mscorlib\1.0.5000.0__b77a5c561934e089_ed6bc96c\mscorlib.dll
ModLoad: 79510000 79523000 E:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorsn.dll
Breakpoint 1 hit
eax=0012f7c0 ebx=00148c60 ecx=04aa112c edx=00000004 esi=0012f784 edi=0012f9a8
eip=791d6a4a esp=0012f764 ebp=0012f79c iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
mscorwks!PreStubWorker:
791d6a4a 55 push ebp




斷點(diǎn)被激活就代表函數(shù)被調(diào)用。我們先使用 k 看看函數(shù)被調(diào)用時的上下文環(huán)境。


以下為引用:

0:000> k
ChildEBP RetAddr
0012f760 0014930e mscorwks!PreStubWorker
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f79c 791da434 0x14930e
0012f8b4 791dd2ec mscorwks!MethodDesc::CallDescr+0x1b6
0012f96c 79240405 mscorwks!MethodDesc::Call+0xc5
0012fa18 79240520 mscorwks!AppDomain::InitializeDomainContext+0x10f
0012fa7c 7923d744 mscorwks!SystemDomain::InitializeDefaultDomain+0x11c
0012fd60 791c6e73 mscorwks!SystemDomain::ExecuteMainMethod+0x120
0012ffa0 791c6ef3 mscorwks!ExecuteEXE+0x1c0
0012ffb0 7880a53e mscorwks!_CorExeMain+0x59
0012ffc0 77e1f38c mscoree!_CorExeMain+0x30 [f:\dd\ndp\clr\src\dlls\shim\shim.cpp @ 5426]
0012fff0 00000000 KERNEL32!BaseProcessStart+0x23




這里可以看到從 mscoree!_CorExeMain 一路執(zhí)行下來的步驟,而那個警告說明這個 stack frame 不在任意一個已知模塊中。這是很正常的,因?yàn)檫@個棧幀實(shí)際上是指向由 JIT 動態(tài)生成的代碼。我們監(jiān)視的 mscorwks!PreStubWorker 函數(shù)只是作為方法表中函數(shù)的入口 stub,系統(tǒng)啟動時還會通過其他方式調(diào)用 JIT 完成代碼的編譯執(zhí)行。
接下來用 SOS 的 !clrstack 命令看看 CLR 的調(diào)用堆棧,顯示如下:


以下為引用:

0:000> !clrstack
succeeded
Loaded Son of Strike data table version 5 from "E:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll"
Thread 0
ESP EIP
0012f784 791d6a4a [FRAME: PrestubMethodFrame] [DEFAULT] [hasThis] Void System.AppDomain.SetupDomain(ValueClass System.LoaderOptimization,String,String)
0012f9a8 791d6a4a [FRAME: GCFrame]
0012fad0 791d6a4a [FRAME: DebuggerClassInitMarkFrame]
0012fa94 791d6a4a [FRAME: GCFrame]




如果需要更為詳細(xì)的詳細(xì),可以使用 -p, -l 或 -r 參數(shù)分別顯示參數(shù)、局部變量和寄存器,當(dāng)然前兩者需要調(diào)試符號庫的支持才行。

如此一路 g; !clrstack 執(zhí)行下去,直到 flier.EntryPoint.m1 函數(shù)需要被處理為止:


以下為引用:

0:000> !clrstack
Thread 0
ESP EIP
0012f68c 791d6a4a [FRAME: PrestubMethodFrame] [DEFAULT] [hasThis] Void flier.EntryPoint.m1()
0012f69c 06d90080 [DEFAULT] Void flier.EntryPoint.Main()
0012f9b0 791da717 [FRAME: GCFrame]
0012fa94 791da717 [FRAME: GCFrame]




此時用 !dumpstackobjects 命令可以查看當(dāng)前線程堆棧中使用的所有對象


以下為引用:

0:000> !dumpstackobjects
ESP/REG Object Name
ecx 04aa1a90 flier.EntryPoint
0012f678 04aa1a90 flier.EntryPoint
0012f67c 04aa1a90 flier.EntryPoint
0012f680 04aa1a90 flier.EntryPoint




這里的 flier.EntryPoint 對象地址 0x04aa1a90 就是我們要分析的對象在內(nèi)存中的位置。

這一階段使用到的 WinDbg 命令如下:


以下為引用:

bp mscorwks!PreStubWorker // 設(shè)置代碼斷點(diǎn)

g // 繼續(xù)運(yùn)行至斷點(diǎn)

k // 查看函數(shù)調(diào)用時的 Native 堆棧調(diào)用

!clrstack // 查看函數(shù)調(diào)用時的 CLR 堆棧調(diào)用

!dumpstackobjects // 查看線程堆棧中使用到的所有對象





知道地址后,就可以用 !dumpobj 命令查看對象的詳細(xì)信息


以下為引用:

0:000> !dumpobj 04aa1a90
Name: flier.EntryPoint
MethodTable 0x009750a8
EEClass 0x06c632e8
Size 12(0xc) bytes
mdToken: 02000002 (D:\Temp\demo.exe)




信息包括對象的類型名字(Name)和類型信息的地址(EEClass),以及對象的大小(Size)和 Token (mdToken),而方法表 (MethodTable) 正是我們分析方法調(diào)用的目標(biāo)。我們可以用 !dumpclass 命令先進(jìn)一步查看對象的類型信息:


以下為引用:

0:000> !dumpclass 0x6c632e8
Class Name : flier.EntryPoint
mdToken : 02000002 ()
Parent Class : 79b7c3c8
ClassLoader : 00153850
Method Table : 009750a8
Vtable Slots : 4
Total Method Slots : 8
Class Attributes : 100000 :
Flags : 1000003
NumInstanceFields: 0
NumStaticFields: 0
ThreadStaticOffset: 0
ThreadStaticsSize: 0
ContextStaticOffset: 0
ContextStaticsSize: 0




可以發(fā)現(xiàn)其信息與對象信息有很多符合之處,正如 Don Box 所說,一個對象引用指向一個類型 EEClass 實(shí)例,而方法表為類型所有,其對象共有。我們可以使用 !dumpmt 命令進(jìn)一步查看方法表的信息,-md 參數(shù)表示需要查看每個方法描述 (MethodDesc):


以下為引用:

0:000> !dumpmt -md 0x09750a8
EEClass : 06c632e8
Module : 0014e090
Name: flier.EntryPoint
mdToken: 02000002 (D:\Temp\demo.exe)
MethodTable Flags : 80000
Number of IFaces in IFaceMap : 0
Interface Map : 009750f4
Slots in VTable : 8
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79b7c4eb 79b7c4f0 None [DEFAULT] [hasThis] String System.Object.ToString()
79b7c473 79b7c478 None [DEFAULT] [hasThis] Boolean System.Object.Equals(Object)
79b7c48b 79b7c490 None [DEFAULT] [hasThis] I4 System.Object.GetHashCode()
79b7c52b 79b7c530 None [DEFAULT] [hasThis] Void System.Object.Finalize()
0097506b 00975070 None [DEFAULT] [hasThis] Void flier.EntryPoint.m1()
0097507b 00975080 None [DEFAULT] [hasThis] Void flier.EntryPoint.m2()
0097508b 00975090 None [DEFAULT] Void flier.EntryPoint.Main()
0097509b 009750a0 None [DEFAULT] [hasThis] Void flier.EntryPoint..ctor()




可以看到方法表中共有8個表項(xiàng),其中前4個已經(jīng)綁定到使用 ngen 預(yù)編譯好的靜態(tài)函數(shù)上


以下為引用:

0:000> u 79b7c4eb
mscorlib_79980000+0x1fc4eb:
79b7c4eb e8909cfeff call mscorlib_79980000+0x1e6180 (79b66180)
79b7c4f0 0000 add [eax],al
79b7c4f2 0080d86206c0 add [eax+0xc00662d8],al
79b7c4f8 06 push es
79b7c4f9 00fc add ah,bh
79b7c4fb e8809cfeff call mscorlib_79980000+0x1e6180 (79b66180)
79b7c500 07 pop es
79b7c501 0010 add [eax],dl




后四個則作為可被覆蓋的虛方法在方法表中,這也是為什么在查看類型信息時 Vtable Slots = 4 而 Total Method Slots = 8 的原因。

對方法表的每個項(xiàng)目,可以用 !DumpMD 命令查看詳細(xì)描述,如


以下為引用:

0:000> !DumpMD 0x00975070
Method Name : [DEFAULT] [hasThis] Void flier.EntryPoint.m1()
MethodTable 9750a8
Module: 14e090
mdToken: 06000001 (D:\Temp\demo.exe)
Flags : 0
IL RVA : 00002050




IL RVA 說明此方法的 IL 代碼相對虛擬地址(IL RVA),也就是說此方法還沒有被 JIT,仍以 IL 代碼形式存在。對于已經(jīng)完成 JIT 的方法,將顯示其 JIT 后函數(shù)體代碼的虛擬地址(Method VA):


以下為引用:

0:000> !DumpMD 0x009750a0
Method Name : [DEFAULT] [hasThis] Void flier.EntryPoint..ctor()
MethodTable 9750a8
Module: 14e090
mdToken: 06000004 (D:\Temp\demo.exe)
Flags : 0
Method VA : 06d900a8





這一階段使用到的 WinDbg 命令如下:


以下為引用:

!dumpobj 04aa1a90 // 查看對象的詳細(xì)信息

!dumpclass 0x6c632e8 // 查看類型的詳細(xì)信息

!dumpmt -md 0x09750a8 // 查看方法表的詳細(xì)信息

!dumpmd 0x00975070 // 查看方法表項(xiàng)的方法描述的詳細(xì)信息

u 0x79b7c4eb // 反匯編指定地址的指令





我們反匯編一下 !DumpMT 命令列出的幾個方法,就會發(fā)現(xiàn)正如 Don Box 所說,已經(jīng)被 JIT 的代碼指向一個jmp指令,直接跳轉(zhuǎn)到編譯后的方法體,如:


以下為引用:

0:000> u 0097509b
0097509b e908b04106 jmp 06d900a8




而沒有被 JIT 的函數(shù),則指向一個call指令,調(diào)用一個 prolog 代碼,間接調(diào)用 mscorwks!PreStubWorker 函數(shù)完成實(shí)際 JIT 工作,如:


以下為引用:

0:000> u 0x0097506b
0097506b e878427dff call 001492e8

0:000> u 0x0097507b
0097507b e868427dff call 001492e8




這個 prolog 代碼很簡單,負(fù)責(zé)構(gòu)造 mscorwks!PreStubWorker 所需的調(diào)用堆棧


以下為引用:

0:000> u 0x001492e8
001492e8 52 push edx
001492e9 68f0301b79 push 0x791b30f0
001492ee 55 push ebp
001492ef 53 push ebx
001492f0 56 push esi
001492f1 57 push edi
001492f2 8d742410 lea esi,[esp+0x10]
001492f6 51 push ecx
001492f7 52 push edx
001492f8 648b1d2c0e0000 mov ebx,fs:[00000e2c]
001492ff 8b7b08 mov edi,[ebx+0x8]
00149302 897e04 mov [esi+0x4],edi
00149305 897308 mov [ebx+0x8],esi
00149308 56 push esi
00149309 e83cd70879 call mscorwks!PreStubWorker (791d6a4a)
0014930e 897b08 mov [ebx+0x8],edi
00149311 894604 mov [esi+0x4],eax
00149314 5a pop edx
00149315 59 pop ecx
00149316 5f pop edi
00149317 5e pop esi
00149318 5b pop ebx
00149319 5d pop ebp
0014931a 83c404 add esp,0x4
0014931d 8f0424 pop [esp]
00149320 c3 ret




而這段 prolog 代碼是由類似 ROTOR 中的 GeneratePrestub 函數(shù)(vm\i386\cgenx86.cpp:1829) 動態(tài)生成的,完成對 PreStubWorker 函數(shù)調(diào)用的封裝。而 PreStubWorker 函數(shù)會調(diào)用 JIT 完成真正的函數(shù)編譯工作,并將方法表的入口改為指向編譯后函數(shù)體的 jmp 指令。具體的流程請參考Don Box 在《.NET本質(zhì)論 第1卷:公共語言運(yùn)行庫》的第六章中的介紹,這里就不再羅嗦了。以后有機(jī)會再寫篇文章詳細(xì)分析一下 JIT 的工作流程。

在 JIT 處理 flier.EntryPoint.m1 時,用 g 命令執(zhí)行,再回頭來分析 m1 函數(shù)的入口,就會發(fā)現(xiàn)如前面所述,調(diào)用 JIT 過程的 call 指令變成了直接調(diào)用 Native 函數(shù)體的 jmp 指令。:D


這一小節(jié),我們介紹了使用 WinDbg 跟蹤調(diào)試 CLR 程序的一遍流程,并了解了對堆棧、對象和類信息進(jìn)行分析的 SOS 命令,希望大家能夠借此開始探索 CLR 內(nèi)部世界的旅程。 :P

Jason Zander在其 BLog 的一篇文章,SOS Debugging with the CLR (Part 1),里面也詳細(xì)介紹了使用 WinDbg 和 SOS 調(diào)試 CLR 程序的部分方法,

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

主站蜘蛛池模板: 999精品免费视频 | 国产成人短视频在线观看免费 | 国产乳摇福利视频在线观看 | 中文字幕2020 | 国产蜜汁tv福利在线 | 日韩一级精品久久久久 | 亚洲欧美日本综合 | 日本粉色视频 | 国产午夜精品理论片久久影视 | 日韩一区二区三区免费 | 天天拍夜夜添久久精品免费 | 久久国产三级精品 | 人人狠狠视频在线观看 | 中文字幕欧美成人免费 | 永久免费视频网站在线观看 | 亚洲精品国产综合久久一线 | 国产l精品国产亚洲区久久 国产nv精品你懂得 国产se98视频精品在这里 | 亚洲国产一区二区三区四区五区 | 日韩一区二区三区在线 | 一区二区三区无码高清视频 | 日日操夜夜摸 | 日本道久久 | 欧美性一区 | 欧美日韩久久中文字幕 | 久久久青青久久国产精品 | 草逼综合| 欧美乱妇高清无乱码免费 | 亚洲欧美另类日本久久影院 | 99精品欧美一区二区三区 | 免费看日本泡妞视频网站 | 日本www网站 | 日朝欧美亚洲精品 | 91麻豆精品国产高清在线 | 成人免费观看国产高清 | 亚洲欧洲第一页 | 色婷婷视频在线观看 | 国产精品福利午夜h视频 | 国产激情一区二区三区在线观看 | 亚洲欧美色综合一区二区在线 | 69香蕉视频 | 午夜精品久久久久久影视riav |