調(diào)試SQLSERVER (一)生成dump文件的方法調(diào)試SQLSERVER (三)使用Windbg調(diào)試SQLSERVER的一些命令
大家知道在Windows里面,調(diào)試可以分為兩個(gè)領(lǐng)域:
1、內(nèi)核態(tài)調(diào)試
2、用戶態(tài)調(diào)試
一般的程序都是運(yùn)行在用戶態(tài),包括SQLSERVER,SQLServer 會(huì)依賴于操作系統(tǒng)的Win32/Win64 API去調(diào)用I/O或者其他他需要的服務(wù)
用戶態(tài)程序調(diào)試和內(nèi)核態(tài)程序調(diào)試是不太一樣的,即使有些命令是一樣的
還有另外一個(gè)要注意的是live debug和dump file
live debug:直接附加調(diào)試器到進(jìn)程--使進(jìn)程hang住,使被調(diào)試程序不能繼續(xù)執(zhí)行代碼
dump file :通常是一個(gè)dump文件,dump文件是進(jìn)程生成的當(dāng)時(shí)這個(gè)進(jìn)程的所有或部分內(nèi)存內(nèi)容,并且可以被調(diào)試器讀取
full dump文件 通常的擴(kuò)展名是.dmp ,文件里面一般不包含已經(jīng)paged out 的內(nèi)存內(nèi)容
mini dump文件 通常的擴(kuò)展名是.mdmp ,一般只包含線程棧和加載的模塊名
Windbg: 是一個(gè)GUI調(diào)試器工具并且可以調(diào)試內(nèi)核態(tài)和用戶態(tài)程序,除了Windbg調(diào)試器 當(dāng)然還有其他調(diào)試器 ,例如kd, cdb, ntsd
簡(jiǎn)單復(fù)習(xí)
應(yīng)用程序以進(jìn)程運(yùn)行,這里我們關(guān)注的進(jìn)程是sqlservr.exe ,在用戶態(tài)調(diào)試下,我們會(huì)調(diào)試一個(gè)單獨(dú)的進(jìn)程,無論是使用live debug(附加進(jìn)程方式)還是讀取一個(gè)dump文件
在調(diào)試的時(shí)候都能看到進(jìn)程的內(nèi)存空間。在內(nèi)核態(tài)調(diào)試?yán)铮覀兛梢钥吹剿羞M(jìn)程和他們的內(nèi)存空間(注意:在dump文件里面我們?cè)L問不到當(dāng)生成dump文件的時(shí)候
已經(jīng)被paged out 的那部分內(nèi)存地址)
stack trace:?jiǎn)蝹€(gè)線程里面執(zhí)行的代碼的棧,一個(gè)線程可以有用戶態(tài)棧和內(nèi)核態(tài)棧,在用戶態(tài)調(diào)試?yán)锩妫覀冎荒芸吹矫總€(gè)線程的用戶態(tài)棧
一個(gè)stack trace是一個(gè)線程的一系列函數(shù)調(diào)用的list
stack是一個(gè)LIFO結(jié)構(gòu)(last in first out)意思是后進(jìn)先出,術(shù)語叫做push stack 和pop stack
棧的讀取是自底向上的,最上面的調(diào)用是當(dāng)前正在執(zhí)行的代碼
下面的例子
Child SP (Stack Pointer)
RetAddr (Return Address)
stack trace里面可以跟蹤系統(tǒng)的執(zhí)行順序和調(diào)用代碼的返回,調(diào)試器會(huì)構(gòu)造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
在配置調(diào)試器的過程中,最后的步驟是匹配符號(hào),我們打開Windbg的時(shí)候我們必須設(shè)置我們的符號(hào)文件路徑
Symbolic Debugging Files(符號(hào)調(diào)試文件):幫助調(diào)試器把內(nèi)存里面的各自的函數(shù),類,變量名在內(nèi)存的位置 匹配到相應(yīng)的內(nèi)存地址和位移
如果沒有Symbolic Debugging Files,那么我們將會(huì)看到下面只有memory addresses 的stack trace
符號(hào)文件的擴(kuò)展名通常是pdb(如果大家寫C#代碼的時(shí)候肯定都會(huì)看到過在debug的時(shí)候看到bin目錄下會(huì)有debug符號(hào)文件),調(diào)試器能夠很好地解析這種文件格式
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 和正在執(zhí)行的代碼在地址空間里的位移,但是我們不知道具體的函數(shù)名,如果有符號(hào)映射的幫助(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+0x11b00000000`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
從上面的輸出的函數(shù)名我們可以看到Checkpoint 線程
上面看到worker thread在CHECKPOINT_QUEUE 隊(duì)列里面等待CHECKPOINT 命令
大家可以看到加上符號(hào)文件之后,調(diào)試器會(huì)在stack trace相應(yīng)的位置進(jìn)行轉(zhuǎn)換,例如在模塊名那里加上模塊名,在函數(shù)名那里加上函數(shù)名
<module_name>!<function call>or<module_name>!<class_name>::<method/function call>
設(shè)置符號(hào)路徑
設(shè)置符號(hào)路徑的作用是使調(diào)試器可以找到符號(hào)文件的位置并進(jìn)行加載在調(diào)試的時(shí)候
簡(jiǎn)單介紹一下符號(hào)路徑和公私有符號(hào)文件
私有符號(hào)文件:包含了調(diào)試會(huì)話中需要的所有符號(hào)信息(只對(duì)微軟內(nèi)部開放,微軟私有財(cái)產(chǎn))公有符號(hào)文件:只是有選擇地包含一些符號(hào)信息(對(duì)所有人開放)
符號(hào)信息隸屬于指定的模塊,只有調(diào)試器需要用到某個(gè)模塊時(shí),它的符號(hào)信息才有被加載和分析的必要。
要在調(diào)試器中使用符號(hào),我們必須首先告訴調(diào)試器這些符號(hào)文件的位置,也就是設(shè)置符號(hào)路徑。符號(hào)路徑可以是本地文件夾路徑、可訪問的UNC路徑、或者是符號(hào)服務(wù)器路徑。
符號(hào)服務(wù)器:在調(diào)試過程中,需要涉及成千上萬個(gè)符號(hào)文件,以及同一個(gè)符號(hào)文件存在不同平臺(tái)下的不同符號(hào)文件版本的時(shí)候。
手動(dòng)設(shè)置符號(hào)路徑肯定是不現(xiàn)實(shí)的,于是引入了符號(hào)服務(wù)器的概念。
符號(hào)服務(wù)器有一套命名規(guī)則,使得調(diào)試軟件能夠正確找到需要的符號(hào)文件。一般來說,符號(hào)服務(wù)器比較大,都是共用的,放在遠(yuǎn)程主機(jī)上。
為了降低網(wǎng)絡(luò)訪問的成本,又引入了符號(hào)緩存的概念,即將從服務(wù)器上下載到的符號(hào)文件,保存在本地緩存中,以后調(diào)試器需要符號(hào)文件的時(shí)候,先從緩存中尋找,找不到的時(shí)候再到服務(wù)器上下載。
1、設(shè)置符號(hào)路徑
設(shè)置符號(hào)路徑的語法如下:
.sympath [+] [路徑]
如要覆蓋原來的路徑設(shè)置,使用新路徑即可:
.sympath <新路徑>
要在原有路徑的基礎(chǔ)上添加一個(gè)新路徑,可使用:
.sympath+ <新增路徑>
如果不帶參數(shù),那么輸出是當(dāng)前設(shè)置的符號(hào)路徑:
0:000> .sympathSymbol search path is: <empty> //尚未設(shè)置符號(hào)路徑
假如在調(diào)試時(shí),我知道需要的符號(hào)文件位于一下文件夾"D:/MyPdb“。
0:000> .sympath D:/MyPdb //覆蓋原有符號(hào)路徑Symbol search path is: D:/MyPdbExpanded Symbol search path is: d:/mypdb
此時(shí)調(diào)試器將記錄上面新的符號(hào)路徑,但并不會(huì)從這個(gè)路徑中加載任何符號(hào),要指示調(diào)試器加載符號(hào),可以使用元命令reload。
這個(gè)命令能枚舉出進(jìn)程地址空間中所有已加載的模塊,并且嘗試找出與各個(gè)模塊相關(guān)的符號(hào)文件。
0:000> .reloadReloading current modules.....
如果調(diào)試器在指定目錄無法找到文件,那么它會(huì)輸出錯(cuò)誤提示:
*** ERROR:Symbol file could not be found. Defaulted to export symbols for xxx.dll
當(dāng)沒有設(shè)置本地緩存路徑時(shí),那么調(diào)試器將使用調(diào)試軟件的安裝路徑下的sym文件夾。
要特別注意的是,使用.sympath改變或新增符號(hào)路徑后,符號(hào)文件并不會(huì)自動(dòng)更新,應(yīng)再執(zhí)行.reload命令以進(jìn)行更新。
2、符號(hào)服務(wù)器與符號(hào)緩存
設(shè)置符號(hào)服務(wù)器的基本語法是:
SRV*[符號(hào)緩存]*服務(wù)器地址
語法有SRV引導(dǎo),符號(hào)緩存和服務(wù)器地址的前面各有一個(gè)星號(hào)引導(dǎo)。
此外,我們總是應(yīng)該把微軟的公用符號(hào)庫加入到我們的符號(hào)路徑中:
.sympath+ srv*<緩存地址>*http://msdl.microsoft.com/download/symbols
這是一臺(tái)微軟對(duì)外公開的服務(wù)器,使用http地址訪問,不是所有人都能牢記這個(gè)網(wǎng)址,所以最好的辦法就是使用.symfix命令(自動(dòng)記憶了上面
新聞熱點(diǎn)
疑難解答
圖片精選