調試SQLSERVER (二)使用Windbg調試SQLSERVER的環境設置
調試SQLSERVER (一)生成dump文件的方法
調試SQLSERVER (三)使用Windbg調試SQLSERVER的一些命令
大家知道在Windows里面,調試可以分為兩個領域:
1、內核態調試
2、用戶態調試
一般的程序都是運行在用戶態,包括SQLSERVER,SQLServer 會依賴于操作系統的Win32/Win64 API去調用I/O或者其他他需要的服務
用戶態程序調試和內核態程序調試是不太一樣的,即使有些命令是一樣的
還有另外一個要注意的是 live debug和dump file
live debug:直接附加調試器到進程--使進程hang住,使被調試程序不能繼續執行代碼
dump file :通常是一個dump文件,dump文件是進程生成的當時這個進程的所有或部分內存內容,并且可以被調試器讀取
full dump文件 通常的擴展名是.dmp ,文件里面一般不包含已經paged out 的內存內容
mini dump文件 通常的擴展名是.mdmp ,一般只包含線程棧和加載的模塊名
Windbg: 是一個GUI調試器工具并且可以調試內核態和用戶態程序,除了Windbg調試器 當然還有其他調試器 ,例如kd, cdb, ntsd
簡單復習
應用程序以進程運行,這里我們關注的進程是sqlservr.exe ,在用戶態調試下,我們會調試一個單獨的進程,無論是使用live debug(附加進程方式)還是讀取一個dump文件
在調試的時候都能看到進程的內存空間。在內核態調試里,我們可以看到所有進程和他們的內存空間(注意:在dump文件里面我們訪問不到當生成dump文件的時候
已經被paged out 的那部分內存地址)
stack trace:單個線程里面執行的代碼的棧,一個線程可以有用戶態棧和內核態棧,在用戶態調試里面,我們只能看到每個線程的用戶態棧
一個stack trace是一個線程的一系列函數調用的list
stack是一個LIFO結構(last in first out)意思是后進先出,術語叫做push stack 和pop stack
棧的讀取是自底向上的,最上面的調用是當前正在執行的代碼
下面的例子
Child SP (Stack Pointer)
RetAddr (Return Address)
stack trace里面可以跟蹤系統的執行順序和調用代碼的返回,調試器會構造stack trace 給我們以便于更好的分析
Child-SP RetAddr Call Site 00000000`09cbe9e8 00000000`777b2f60 ntdll!NtSignalAndWaitForSingleObject+0xa 00000000`09cbe9f0 00000000`00bdc99e kernel32!SignalObjectAndWait+0x110 00000000`09cbeaa0 00000000`00bc4575 sqlservr+0x1c99e 00000000`09cbed40 00000000`00bc3ea8 sqlservr+0x4575 00000000`09cbed80 00000000`00bdcfad sqlservr+0x3ea8 00000000`09cbf370 00000000`01139d9c sqlservr+0x1cfad 00000000`09cbf430 00000000`032b34c7 sqlservr+0x579d9c 00000000`09cbf650 00000000`00bd2abb sqlservr!TlsGetValueForMsxmlSQL+0x4706d7 00000000`09cbf6c0 00000000`00bd0fda sqlservr+0x12abb 00000000`09cbf7e0 00000000`00bd2665 sqlservr+0x10fda 00000000`09cbf870 00000000`0117abb0 sqlservr+0x12665 00000000`09cbf8e0 00000000`0117c4b0 sqlservr+0x5babb0 00000000`09cbf9a0 00000000`0117a060 sqlservr+0x5bc4b0 00000000`09cbf9d0 00000000`0117a9ef sqlservr+0x5ba060 00000000`09cbfa60 00000000`734937d7 sqlservr+0x5ba9ef 00000000`09cbfaf0 00000000`73493894 MSVCR80!endthreadex+0x47 00000000`09cbfb20 00000000`7775f56d MSVCR80!endthreadex+0x104 00000000`09cbfb50 00000000`77893281 kernel32!BaseThreadInitThunk+0xd 00000000`09cbfb80 00000000`00000000 ntdll!RtlUserThreadStart+0x21
在配置調試器的過程中,最后的步驟是匹配符號,我們打開Windbg的時候我們必須設置我們的符號文件路徑
Symbolic Debugging Files(符號調試文件):幫助調試器把內存里面的各自的函數,類,變量名在內存的位置 匹配到相應的內存地址和位移
如果沒有Symbolic Debugging Files,那么我們將會看到下面只有memory addresses 的stack trace
符號文件的擴展名通常是pdb(如果大家寫C#代碼的時候肯定都會看到過在debug的時候看到bin目錄下會有debug符號文件),調試器能夠很好地解析這種文件格式
Child-SP RetAddr Call Site 00000000`09cbe9e8 00000000`777b2f60 ntdll!NtSignalAndWaitForSingleObject+0xa 00000000`09cbe9f0 00000000`00bdc99e kernel32!SignalObjectAndWait+0x110 00000000`09cbeaa0 00000000`00bc4575 sqlservr+0x1c99e 00000000`09cbed40 00000000`00bc3ea8 sqlservr+0x4575 00000000`09cbed80 00000000`00bdcfad sqlservr+0x3ea8 00000000`09cbf370 00000000`01139d9c sqlservr+0x1cfad 00000000`09cbf430 00000000`032b34c7 sqlservr+0x579d9c 00000000`09cbf650 00000000`00bd2abb sqlservr!TlsGetValueForMsxmlSQL+0x4706d7 00000000`09cbf6c0 00000000`00bd0fda sqlservr+0x12abb 00000000`09cbf7e0 00000000`00bd2665 sqlservr+0x10fda 00000000`09cbf870 00000000`0117abb0 sqlservr+0x12665 00000000`09cbf8e0 00000000`0117c4b0 sqlservr+0x5babb0 00000000`09cbf9a0 00000000`0117a060 sqlservr+0x5bc4b0 00000000`09cbf9d0 00000000`0117a9ef sqlservr+0x5ba060 00000000`09cbfa60 00000000`734937d7 sqlservr+0x5ba9ef 00000000`09cbfaf0 00000000`73493894 MSVCR80!endthreadex+0x47 00000000`09cbfb20 00000000`7775f56d MSVCR80!endthreadex+0x104 00000000`09cbfb50 00000000`77893281 kernel32!BaseThreadInitThunk+0xd 00000000`09cbfb80 00000000`00000000 ntdll!RtlUserThreadStart+0x21
上面顯示出我們的模塊,sqlservr.exe 和正在執行的代碼在地址空間里的位移,但是我們不知道具體的函數名,如果有符號映射的幫助(symbols mapped),我們可以獲取到更有意義輸出
Child-SP RetAddr Call Site 00000000`09cbe9e8 00000000`777b2f60 ntdll!NtSignalAndWaitForSingleObject+0xa 00000000`09cbe9f0 00000000`00bdc99e kernel32!SignalObjectAndWait+0x110 00000000`09cbeaa0 00000000`00bc4575 sqlservr!SOS_Scheduler::SwitchContext+0x84e 00000000`09cbed40 00000000`00bc3ea8 sqlservr!SOS_Scheduler::SuspendNonPreemptive+0xc5 00000000`09cbed80 00000000`00bdcfad sqlservr!EventInternal<Spinlock<149,1,0> >::Wait+0x428 00000000`09cbf370 00000000`01139d9c sqlservr!ResQueueBase::Dequeue+0x19d 00000000`09cbf430 00000000`032b34c7 sqlservr!CheckpointLoop+0x1aa 00000000`09cbf650 00000000`00bd2abb sqlservr!ckptproc+0x47 00000000`09cbf6c0 00000000`00bd0fda sqlservr!SOS_Task::Param::Execute+0x11b 00000000`09cbf7e0 00000000`00bd2665 sqlservr!SOS_Scheduler::RunTask+0xca 00000000`09cbf870 00000000`0117abb0 sqlservr!SOS_Scheduler::ProcessTasks+0x95 00000000`09cbf8e0 00000000`0117c4b0 sqlservr!SchedulerManager::WorkerEntryPoint+0x110 00000000`09cbf9a0 00000000`0117a060 sqlservr!SystemThread::RunWorker+0x60 00000000`09cbf9d0 00000000`0117a9ef sqlservr!SystemThreadDispatcher::ProcessWorker+0x12c 00000000`09cbfa60 00000000`734937d7 sqlservr!SchedulerManager::ThreadEntryPoint+0x12f 00000000`09cbfaf0 00000000`73493894 MSVCR80!endthreadex+0x47 00000000`09cbfb20 00000000`7775f56d MSVCR80!endthreadex+0x104 00000000`09cbfb50 00000000`77893281 kernel32!BaseThreadInitThunk+0xd 00000000`09cbfb80 00000000`00000000 ntdll!RtlUserThreadStart+0x21
從上面的輸出的函數名我們可以看到Checkpoint 線程
上面看到worker thread在CHECKPOINT_QUEUE 隊列里面等待CHECKPOINT 命令
大家可以看到加上符號文件之后,調試器會在stack trace相應的位置進行轉換,例如在模塊名那里加上模塊名,在函數名那里加上函數名
<module_name>!<function call> or <module_name>!<class_name>::<method/function call>
設置符號路徑
設置符號路徑的作用是使調試器可以找到符號文件的位置并進行加載在調試的時候
簡單介紹一下符號路徑和公私有符號文件
私有符號文件:包含了調試會話中需要的所有符號信息(只對微軟內部開放,微軟私有財產)
公有符號文件:只是有選擇地包含一些符號信息(對所有人開放)
符號信息隸屬于指定的模塊,只有調試器需要用到某個模塊時,它的符號信息才有被加載和分析的必要。
要在調試器中使用符號,我們必須首先告訴調試器這些符號文件的位置,也就是設置符號路徑。符號路徑可以是本地文件夾路徑、可訪問的UNC路徑、或者是符號服務器路徑。
符號服務器:在調試過程中,需要涉及成千上萬個符號文件,以及同一個符號文件存在不同平臺下的不同符號文件版本的時候。
手動設置符號路徑肯定是不現實的,于是引入了符號服務器的概念。
符號服務器有一套命名規則,使得調試軟件能夠正確找到需要的符號文件。一般來說,符號服務器比較大,都是共用的,放在遠程主機上。
為了降低網絡訪問的成本,又引入了符號緩存的概念,即將從服務器上下載到的符號文件,保存在本地緩存中,以后調試器需要符號文件的時候,先從緩存中尋找,找不到的時候再到服務器上下載。
1、設置符號路徑
設置符號路徑的語法如下:
.sympath [+] [路徑]
如要覆蓋原來的路徑設置,使用新路徑即可:
.sympath <新路徑>
要在原有路徑的基礎上添加一個新路徑,可使用:
.sympath+ <新增路徑>
如果不帶參數,那么輸出是當前設置的符號路徑:
0:000> .sympath Symbol search path is: <empty> //尚未設置符號路徑
假如在調試時,我知道需要的符號文件位于一下文件夾"D:\MyPdb“。
0:000> .sympath D:\MyPdb //覆蓋原有符號路徑 Symbol search path is: D:\MyPdb Expanded Symbol search path is: d:\mypdb
此時調試器將記錄上面新的符號路徑,但并不會從這個路徑中加載任何符號,要指示調試器加載符號,可以使用元命令reload。
這個命令能枚舉出進程地址空間中所有已加載的模塊,并且嘗試找出與各個模塊相關的符號文件。
0:000> .reload Reloading current modules .....
如果調試器在指定目錄無法找到文件,那么它會輸出錯誤提示:
*** ERROR:Symbol file could not be found. Defaulted to export symbols for xxx.dll
當沒有設置本地緩存路徑時,那么調試器將使用調試軟件的安裝路徑下的sym文件夾。
要特別注意的是,使用.sympath改變或新增符號路徑后,符號文件并不會自動更新,應再執行.reload命令以進行更新。
2、符號服務器與符號緩存
設置符號服務器的基本語法是:
SRV*[符號緩存]*服務器地址
語法有SRV引導,符號緩存和服務器地址的前面各有一個星號引導。
此外,我們總是應該把微軟的公用符號庫加入到我們的符號路徑中:
.sympath+ srv*<緩存地址>*http://msdl.microsoft.com/download/symbols
這是一臺微軟對外公開的服務器,使用http地址訪問,不是所有人都能牢記這個網址,所以最好的辦法就是使用.symfix命令(自動記憶了上面那個微軟符號服務器地址),語法如下:
.symfix [+] [符號緩存地址]
下面的命令等價于上面的.sympath命令,而不用輸入長長的http地址。
0:010> .symfix c:\windows\symbols 0:010> .sympath Symbol search path is: srv* Expanded Symbol search path is: SRV*c:\windows\symbols*http://msdl.microsoft.com/download/symbols
以上設置后,當需要用到符號,Windbg將自動到服務器上去下載,然后保存在C:\windows\symbols
當然,我們也可以在計算機上面設置,設置方式是:
我的電腦=》高級系統設置=》高級Tab,點擊環境變量,新建一個用戶變量如
變量名:_NT_SYMBOL_PATH
變量值:SRV*D:\PDB*http://msdl.microsoft.com/download/symbols/
或者
在安裝完符號文件之后,指定固定的路徑
變量名:_NT_SYMBOL_PATH
變量值:C:\windows\symbols
2、重新加載
如果對自己正在使用的符號文件感到疑惑,比如源代碼和行號明顯不匹配,最好就是重新加載一下符號文件。此命令語法如下:
.reload /f /v [模塊名]
.reload命令的作用是刪除指定或所有已加載的符號文件,默認情況下,調試器不會立刻根據符號路徑重新搜索并加載新的符號文件,而是推遲到調試器下一次使用此文件時。
使用/f參數將破事調試器立刻搜索并重新加載新的符號文件。
其它參數解釋如下:
/v:將搜索過程中的詳細信息都顯示出來。
/i:不檢查pdb文件的版本信息;
/l:只顯示模塊信息,內核模式下,和“lm n t”命令類似,但顯示內容比后者更多,因為包含了用戶模塊信息;
/n:僅重載內核符號,不重載用戶符號;
/o:強制覆蓋符號庫中的符號文件,即使版本相同;
/d:用戶層模式下使用Windbg時的默認選項,重載調試器模塊列表中的所有模塊;
/s:內核模式下使用Windbg時的默認選項,重載系統模塊列表中的所有模塊,另外,如果調試器在用戶模式下運行,要加載內核模塊,也必須使用/s選項,否則調試器將只會在調試器模塊列表中搜索而導致找不到內核模塊;
/u:卸載指定模塊。如發現當前符號版本不對,使用/u開關先卸載之再重新加載。
下面命令重新加載sqlservr模塊
.reload /f sqlservr.exe
上面講了一大堆,什么符號服務器,什么符號緩存,但是我們一般只使用本地符號文件,實際上概括起來我們設置符號路徑一般的做法就是
1、下載公有符號文件,并安裝,然后設置
地址:http://msdn.microsoft.com/zh-cn/windows/hardware/gg463028
雙擊下載下來的安裝包,然后安裝
下面這個路徑一定要記住!!
安裝完畢之后就可以在E:\Symbols\路徑下 看到安裝好的pdb
pdb文件
2、在Windbg里面設置symbols文件夾的位置
我們隨便加載一個mdmp文件
打開dump文件之后,在Windbg的命令框依次輸入下面命令
.sympath E:\Symbols
重新加載
.reload
然后再輸入 .sympath 就會看到當前符號路徑是E:\Symbols
當然,我們不可能每次調試dump文件的時候都指定一次符號路徑,我們可以把符號路徑添加到環境變量里
這樣下次再打開Windbg的時候就不用再指定多一次符號路徑
我們再次打開Windbg,可以發現Windbg會首先從環境變量里查找符號路徑,下圖看到Windbg已經查找成功
這樣就省去了我們每次設置符號路徑的麻煩
總結
這樣我們的調試環境就弄好了,下篇會介紹Windbg的一些命令
參考文章:
http://blogs.msdn.com/b/askjay/archive/2009/12/29/basic-debugging-concepts-and-setup.aspx
關于符號路徑,符號服務器,符號文件的更多內容請參考:Windbg符號與源碼 《第二篇》
歡迎大家拍磚o(∩_∩)o
文章列表