我們一起聊聊如何使用Windbg查看C#某個線程的棧大小 ?
每一個線程都有一個叫 TEB(Thread Environment Block) 的線程環境塊數據結構,這個結構中有一個叫做 NT_TIB 的結構,它里面有兩個字段分別為 StackBase 和 StackLimit,前面叫做棧基址,也就是棧頂,后者叫做 棧邊界 ,因為??臻g是向小地址增長的,所以用 StackBase - StackLimit 就能算出所謂的棧內存大小,接下來我們用 windbg 演示一下。
一:windbg 演示
1. 使用 !teb 命令
大家可以用 windbg 直接調試你的程序,我手里剛好有一個 dump 文件,這里就從主線程看起吧。
0:000> ~0s
ntdll!NtWaitForSingleObject+0x14:
00007ffe`28b9fa74 c3 ret
0:000> !teb
TEB at 000000b4da0ae000
ExceptionList: 0000000000000000
StackBase: 000000b4d9fa0000
StackLimit: 000000b4d9f98000
SubSystemTib: 0000000000000000
FiberData: 0000000000001e00
ArbitraryUserPointer: 0000000000000000
Self: 000000b4da0ae000
EnvironmentPointer: 0000000000000000
ClientId: 0000000000000c74 . 00000000000041a4
RpcHandle: 0000000000000000
Tls Storage: 000001f90edad1d0
PEB Address: 000000b4da0ad000
LastErrorValue: 0
LastStatusValue: 103
Count Owned Locks: 0
HardErrorMode: 0
從輸出看兩個值分別為:StackBase=000000b4d9fa0000 和 StackLimit=000000b4d9f98000,那它的大小就是 32768byte = 32k。
0:000> ? 000000b4d9fa0000 - 000000b4d9f98000
Evaluate expression: 32768 = 00000000`00008000
這里要提醒一下,操作系統的內存頁是 4k 為一個粒度,也就說所有的輸出結果肯定是4k的倍數,比如當前??臻g就是 8 個內存頁。
2. 查看 NT_TIB 結構
剛才用的是快捷命令,接下來我們直接查看 _TEB 結構下的 NT_TIB struct 結構變量。
0:000> .thread
Implicit thread is now 000000b4`da0ae000
0:000> dt _NT_TIB 000000b4`da0ae000
combase!_NT_TIB
+0x000 ExceptionList : (null)
+0x008 StackBase : 0x000000b4`d9fa0000 Void
+0x010 StackLimit : 0x000000b4`d9f98000 Void
+0x018 SubSystemTib : (null)
+0x020 FiberData : 0x00000000`00001e00 Void
+0x020 Version : 0x1e00
+0x028 ArbitraryUserPointer : (null)
+0x030 Self : 0x000000b4`da0ae000 _NT_TIB
可以看到,上面的兩個值和 !teb 顯示的一模一樣。