dump檔案,windbg
dump檔案,在VC中的除錯還是非常非常非常有用的,因為我們也不會經每一行程式碼都加上日誌,當然如果你願意,也可以每一行都加上日誌;
在Windows上,新增dump檔案有兩種方法:
方法一:一個是在程式中新增程式碼;
方法二:修改登錄檔(參考後面的bat檔案寫法,在win7上用管理員程式執行);建議用這個方法,方便實用;(http://blog.csdn.net/hgy413/article/details/7586957#)
from:
http://blog.csdn.net/byxdaz/article/details/25872151
http://blog.csdn.net/starlee/article/details/6630816
在Windows平臺下用C++開發應用程式,最不想見到的情況恐怕就是程式崩潰,而要想解決引起問題的bug,最困難的應該就是除錯release版本了。因為release版本來就少了很多除錯資訊,更何況一般都是釋出出去由使用者使用,crash的現場很難保留和重現。目前有一些方法可以解決:崩潰地址 + MAP檔案;MAP檔案;SetUnhandledExceptionFilter + Minidump。本文重點解決Minidump方式。
一、Minidump檔案生成
1、Minidump概念
minidump(小儲存器轉儲)可以理解為一個dump檔案,裡面記錄了能夠幫助除錯crash的最小有用資訊。實際上,如果你在系統屬性 -> 高階 -> 啟動和故障恢復 -> 設定 -> 寫入除錯資訊中選擇“小記憶體轉儲(64 KB)”的話,當系統意外停止時都會在C:\Windows\Minidump\路徑下生成一個.dmp字尾的檔案,這個檔案就是minidump檔案,只不過這個是核心態的minidump。
我們要生成的是使用者態的minidump,檔案中包含了程式執行的模組資訊、執行緒資訊、堆疊呼叫資訊等。而且為了符合其mini的特性,dump檔案是壓縮過的。
2、生成minidump檔案
通過drwtsn32、NTSD、CDB等除錯工具生成Dump檔案, drwtsn32存在的缺點雖然NTSD、CDB可以完全解決,但並不是所有的作業系統中都安裝了NTSD、CDB等除錯工具。根據MiniDumpWriteDump介面,完全可以程式自動生成Dump檔案。
3、 自動生成Minidump檔案
當程式遇到未處理異常(主要指非指標造成)導致程式崩潰死,如果在異常發生之前呼叫了SetUnhandledExceptionFilter()函式,異常交給函式處理。MSDN中描述為:
Issuing SetUnhandledExceptionFilter replaces the existing top-level exception filter for all existing and all future threads in the calling process.
因而,在程式開始處增加SetUnhandledExceptionFilter()函式,並在函式中利用適當的方法生成Dump檔案,即可實現需要的功能。
生成dump檔案類(minidump.h)
- #pragma once
- #include <windows.h>
- #include <imagehlp.h>
- #include <stdlib.h>
- #pragma comment(lib, "dbghelp.lib")
- inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
- {
- if(pModuleName == 0)
- {
- return FALSE;
- }
- WCHAR szFileName[_MAX_FNAME] = L"";
- _wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
- if(wcsicmp(szFileName, L"ntdll") == 0)
- return TRUE;
- return FALSE;
- }
- inline BOOL CALLBACK MiniDumpCallback(PVOID pParam,
- const PMINIDUMP_CALLBACK_INPUT pInput,
- PMINIDUMP_CALLBACK_OUTPUT pOutput)
- {
- if(pInput == 0 || pOutput == 0)
- return FALSE;
- switch(pInput->CallbackType)
- {
- case ModuleCallback:
- if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
- if(!IsDataSectionNeeded(pInput->Module.FullPath))
- pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
- case IncludeModuleCallback:
- case IncludeThreadCallback:
- case ThreadCallback:
- case ThreadExCallback:
- return TRUE;
- default:;
- }
- return FALSE;
- }
- //建立Dump檔案
- inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName)
- {
- HANDLE hFile = CreateFile(strFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
- {
- MINIDUMP_EXCEPTION_INFORMATION mdei;
- mdei.ThreadId = GetCurrentThreadId();
- mdei.ExceptionPointers = pep;
- mdei.ClientPointers = FALSE;
- MINIDUMP_CALLBACK_INFORMATION mci;
- mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
- mci.CallbackParam = 0;
- MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)0x0000ffff;
- MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);
- CloseHandle(hFile);
- }
- }
- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
- {
- return NULL;
- }
- BOOL PreventSetUnhandledExceptionFilter()
- {
- HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
- if (hKernel32 == NULL)
- return FALSE;
- void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
- if(pOrgEntry == NULL)
- return FALSE;
- unsigned char newJump[ 100 ];
- DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
- dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
- void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
- DWORD dwNewEntryAddr = (DWORD) pNewFunc;
- DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
- newJump[ 0 ] = 0xE9; // JMP absolute
- memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
- SIZE_T bytesWritten;
- BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
- return bRet;
- }
- LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
- {
- TCHAR szMbsFile[MAX_PATH] = { 0 };
- ::GetModuleFileName(NULL, szMbsFile, MAX_PATH);
- TCHAR* pFind = _tcsrchr(szMbsFile, '\\');
- if(pFind)
- {
- *(pFind+1) = 0;
- _tcscat(szMbsFile, _T("CreateMiniDump.dmp"));
- CreateMiniDump(pException,szMbsFile);
- }
- // TODO: MiniDumpWriteDump
- FatalAppExit(-1, _T("Fatal Error"));
- return EXCEPTION_CONTINUE_SEARCH;
- }
- //執行異常處理
- void RunCrashHandler()
- {
- SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
- PreventSetUnhandledExceptionFilter();
- }
//測試實現檔案
// 一個有函式呼叫的類
//
class CrashTest
{
public:
void Test()
{
Crash();
}
private:
void Crash()
{
strcpy(NULL,"adfadfg");
}
};
int _tmain(int argc, _TCHAR* argv[])
{
//設定異常處理函式
RunCrashHandler();
CrashTest test;
test.Test();
getchar();
return 0;
}
注意事項
1、需要配置debug選項,在C/C++選項à常規à除錯資訊格式(設定為程式資料庫(/Zi));在聯結器選項—>除錯à生成除錯資訊(設定為是);C/C++選項à優化à禁用。(參見下圖)
2、 可執行檔案(exe)必須找到dbghelp.dll,才能生成Dump檔案。這個DLL可以從除錯工具包中找到。
3、*.exe、*.pdb、*.dump、dbghelp.dll 這四個檔案需要放在同一目錄下才好除錯,雙擊dump檔案時,就可以自動關聯到出錯程式碼位置。
4、為了獲取更多更深入的除錯資訊,需要把程式優化開關設定成禁用。
5、 當異常程式碼定位成功以後,如果無法阻止異常的產生,可以用 __try 結構包裝異常程式碼,__try 和 try 不同,前者可以捕獲非法指標產生的異常。
__try {
// 會異常的函式
}
__except( EXCEPTION_EXECUTE_HANDLER ){
// 異常處理
}
二、除錯Minidump檔案
- 雙擊minidump檔案(*.dmp)。預設會啟動vs2008。
- 選單Tools/Options, Debugging/Symbols,增加PDB檔案路徑。注:如果minidump檔案與pdb檔案在同一目錄,就不用設定這個了。
- 若除錯的程式需要微軟基礎庫的PDB資訊,可以增加一個路徑為:
- 在介面下方Cache Symbol From symbol…選擇本地儲存這些Symbols的路徑。 注:如果本地已儲存過微軟基礎庫的pdb,就直接按照此步操作設定本地路徑,不必執行上一步操作了。
- 設定程式碼路徑:
設定程式碼路徑:
剛開啟的dmp工程,進入解決方案的屬性。在這裡輸入源程式的程式碼路徑。注:一定是sln所在的路徑,而不是vcproj的路徑!
按F5,debug吧。
//-
#pragma once
#include <windows.h>
#include <imagehlp.h>
#include <stdlib.h>
#pragma comment(lib, "dbghelp.lib")
inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
if(pModuleName == 0)
{
return FALSE;
}
WCHAR szFileName[_MAX_FNAME] = L"";
_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
if(wcsicmp(szFileName, L"ntdll") == 0)
return TRUE;
return FALSE;
}
inline BOOL CALLBACK MiniDumpCallback(PVOID pParam,
const PMINIDUMP_CALLBACK_INPUT pInput,
PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
if(pInput == 0 || pOutput == 0)
return FALSE;
switch(pInput->CallbackType)
{
case ModuleCallback:
if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
if(!IsDataSectionNeeded(pInput->Module.FullPath))
pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:;
}
return FALSE;
}
//建立Dump檔案
inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName)
{
HANDLE hFile = CreateFile(strFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = FALSE;
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)0x0000ffff;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);
CloseHandle(hFile);
}
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}
BOOL PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
if (hKernel32 == NULL)
return FALSE;
void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
if(pOrgEntry == NULL)
return FALSE;
unsigned char newJump[ 100 ];
DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
DWORD dwNewEntryAddr = (DWORD) pNewFunc;
DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
newJump[ 0 ] = 0xE9; // JMP absolute
memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
SIZE_T bytesWritten;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
return bRet;
}
LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
TCHAR szMbsFile[MAX_PATH] = { 0 };
::GetModuleFileName(NULL, szMbsFile, MAX_PATH);
TCHAR* pFind = _tcsrchr(szMbsFile, '\\');
if(pFind)
{
*(pFind+1) = 0;
_tcscat(szMbsFile, _T("CreateMiniDump.dmp"));
CreateMiniDump(pException,szMbsFile);
}
// TODO: MiniDumpWriteDump
FatalAppExit(-1, _T("Fatal Error"));
return EXCEPTION_CONTINUE_SEARCH;
}
//執行異常處理
void RunCrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
除錯方法不用上述那麼麻煩,直接在Debug目錄下 或者 Release目錄下 exe + pdb + dmp 在同一目錄,然後雙擊dmp檔案VC2010就可以開啟,然後點選右上角的除錯按鈕就可以直接定位到崩潰的程式碼了;
參考這裡: http://blog.csdn.net/starlee/article/details/6630816
別人機器上的dump除錯:
http://blog.csdn.net/xhk456/article/details/7523150
這段時間突然發現,要一下做一個金剛不壞之身的程式是不太可能滴,至於對我來說吧。
這個程式也要經過千錘百煉才能夠練就一個強大的自信心。
我現在做系統就不考慮一下把程式做的足夠強壯了,因為我也做不到,現在做系統時,總考慮的一個問題:
當系統異常的時候怎麼去處理?
我不怕系統程式出現異常,甚至直接Over,只要能在異常時處理異常後繼續運作,在崩潰重啟後能夠繼續把沒
乾的活給幹了,那麼這個在我能夠承受的範圍內,也在大多數客戶的承受範圍內,因為這樣就是我們所說的將
損失減小到最低,其實是不是最低只有自己能夠知道。
當然了,我更希望能夠做出一個健壯無比的牛逼程式,所以我想知道程式是在什麼情況下崩潰的,可是有些問題
你懂的,老在客戶機器上或者生產環境下出現,卻在自己的機器上和測試環境就他媽的不出現,遇見這種情況我是
跳樓或者殺人的心情都有了,偶爾我也犯過情緒,想提出辭職申請,換個行業去,告別這苦逼的程式設計師生涯,
可總不知道是什麼力量支援著我,讓我堅強依舊滴做著程式設計師,過著狗日的日子。
後來,不經意間,一位同事給我說了一個種在系統中異常或者崩潰的時候,來生成dump檔案,然後用偵錯程式來除錯。
這樣就可以在生產環境中的dmp檔案,拷貝到自己的開發機器上,除錯就可以找到錯誤的位置,配合程式除錯符號pdb檔案,
直接可以定位到原始碼中位置,真是太他媽的神奇了,雖然Release版本下的很多變數的值是不對滴,但並不影響我這個
這麼有執著心的coder來找bug。
同事給了我他寫的示例,往空指標拷貝資料,在非除錯下執行後,果然的崩了,果斷滴生成了一個副檔名為dmp的檔案,
然後他用vs2010開啟那個dmp檔案,vs2010很果斷滴定位到了那個往空指標拷貝資料那裡。
看他那嫻熟的操作,頓時感覺到了他的強大和微軟的牛逼。
後來我就學他,在程式中加入程式異常時產生dump檔案的功能,待系統釋出後,在一次不經意間一個程式掛掉了。
在客戶的謾罵中,我面帶笑容說:這個問題很好解決。我滿懷信心滴從伺服器上拷貝了程式崩潰產生dump檔案,
然後學著那個同事用vs2010開啟,我了個去,咋沒有定位到原始碼中內,只定位到了可執行檔案的一個地址,這讓哥
情何以堪吶!
還好,我對pdb瞭解還比較熟悉,想來應該是符號檔案的問題,於是就開始摸索的,不經意見的在
堆疊處右擊了下,發現選單裡竟然有“載入符號”,而且還有“符號路徑”,我想這大概就是讓我來選擇
對應的pdb檔案吧,頓時感覺曙光就在前面。
點選了“符號路徑”後如下圖:
才發現了,它並不是來選擇符號檔案,而是選擇對應的可執行程式的路徑,選擇了後果斷滴定位到了原始碼的位置,
才發現一個很簡單很美麗的bug,修改後,在測試後重現釋出,系統的健壯性又提高了一個臺階。
回頭想了想,我同事給我演示的時候,他程式執行的目錄和就是他直接用vs2010生成的目錄,所以此種情況下
用vs2010開啟dmp檔案即可定位到原始碼檔案。而釋出後的程式,一般情況下你根本不知道別人放在什麼地方去執行的,
因此除錯時還並必須選相同版本的可執行檔案,然後pdb檔案才會好好工作,要不沒可執行檔案,咋個除錯嘛。
哎,這同事,居然還留了一手,坑爹啊。
不過還是要感謝他滴,我又掌握了一些東西,又增強了我這個苦逼程式設計師寫好程式的信心。
在寫這個之前看了相關文章,感覺比較好的推薦一哈:
到這裡,你就可以在你的工程中通過程式碼的方式新增,在程式崩潰的時候回建立dump檔案了;
.dump
程式崩潰(crash)的時候, 為了以後能夠除錯分析問題, 可以使用WinDBG要把當時程式記憶體空間資料都儲存下來,生成的檔案稱為dump 檔案。 步驟:
1) 開啟WinDBG並將之Attach 到crash的程式程序
2) 輸入產生dump 檔案的命令
直接用.dump -?可以看到它的簡單說明:
- 0:000> .dump -?
- Usage: .dump [options] filename
- Options are:
- /a - Create dumps for all processes (requires -u)
- /b[a] - Package dump in a CAB and delete dump
- /c <comment> - Add a comment (not supported in all formats)
- /j <addr> - Provide a JIT_DEBUG_INFO address
- /f - Create a legacy style full dump
- /m[acdfFhiprRtuw] - Create a minidump (default)
- /o - Overwrite any existing file
- /u - Append unique identifier to dump name
/o :覆蓋具有相同名字的dump檔案。如果沒有使用該選項又存在一個相同名字的檔案,則dump檔案不會被寫入:比如我的C盤原有一個myapp.dmp檔案:
- 0:000> .dump c:/myapp.dmp
- Unable to create file 'c:/myapp.dmp' - Win32 error 0n80
- "檔案存在。"
- 0:000> .dump /o c:/myapp.dmp
- Creating c:/myapp.dmp - mini user dump
- Dump successfully written
/f (使用者模式:) 建立一個完整使用者模式dump,這裡要注意不要字面理解,
完整使用者模式dump是基本的使用者模式dump檔案。這種dump檔案包含程序的完整記憶體空間、程式本身的可執行映像、控制代碼表和其他對偵錯程式有用的資訊
注意 和名字無關,最大的"minidump"檔案實際上可以提供比完整使用者模式dump更多的資訊。例如,.dump /mf或.dump /ma將建立比.dump /f更大更完整的檔案。
使用者模式下,使用.dump /m[MiniOptions] 是最好的選擇。通過這個開關建立的dump檔案可以很小也可以很大。通過指定合適的MiniOptions 可以控制究竟需要包含哪些資訊。
- 0:000> .dump /o/f c:/myapp.dmp
- *****************************************************************************
- * .dump /ma is the recommend method of creating a complete memory dump *
- * of a user mode process. *
- *****************************************************************************
- Creating c:/myapp.dmp - user full dump
- Dump successfully written
我們看到了,系統給出了提示:.dump /ma是建立完整dump的推薦方式(使用者模式下)
/m[MiniOptions] 建立一個小記憶體dump(核心模式)或者 minidump (使用者模式)。如果沒有指定 /f 和/m ,/m 是預設選項。
使用者模式下,/m 後面可以跟附加的MiniOptions 用來指定dump檔案中包含的資料。如果沒有使用MiniOptions ,dump檔案包含模組、執行緒和呼叫堆疊資訊,但是沒有其他附加資訊
MiniOption | 作用 |
---|---|
a | 建立一個包含所有附加選項的minidump。/ma選項相當於/mfFhut —它會在minidump中新增完整的記憶體資料、控制代碼資料、已解除安裝模組資訊、基本記憶體資訊和執行緒時間資訊。 |
f | 在minidump中包含完整記憶體資料。目標程式擁有的所有 可訪問的已交付的頁面(committed pages)都會包含進去。 |
F | 在minidump中新增所有基本記憶體資訊。這會將一個流加入到包含完整基本記憶體資訊的minidump中,而不單是可使用的記憶體。這樣可以使得偵錯程式能夠重建minidump生成時程序的完整虛擬記憶體佈局。 |
h | 在minidump中包含和目標程序相關的控制代碼資訊。 |
u | 在minidump中包含已解除安裝模組資訊。僅在Windows Server 2003和之後版本的Windows中可用。 |
t | 在minidump中包含附加的執行緒資訊。包括可以在除錯minidump時使用!runaway擴充套件命令或.ttime (Display Thread Times)命令進行顯示的執行緒時間。 |
i | 在minidump中包含次級記憶體(secondary memory)。次級記憶體是由堆疊中的指標或備份儲存(backing store)中引用到的任何記憶體,加上該地址周圍的一小段區域。 |
p | 在minidump中包含程序環境塊(PEB)和執行緒環境塊(TEB)。這在想訪問程式的程序和執行緒相關的Windows系統資訊時很有用。 |
w | 將所有已交付的可讀寫的私有頁面包含進minidump。 |
d | 在minidump中包含可執行映像中所有可讀寫的資料段。 |
c | 加入映像中的程式碼段。 |
r | 從minidump中去掉對重建呼叫堆疊無用的堆疊和儲存記憶體部分。區域性變數和其他資料型別值也被刪除。這個選項不會使得minidump變小(因為這些記憶體節僅僅是變成0),但是當想保護其他程式中的機密資訊時有用。 |
R | 在minidump中去掉完整的模組路徑。僅包含模組名。如果想保護使用者的目錄結構時該選項有用。 |
選項(1): /m
命令列示例:.dump /m C:/dumps/myapp.dmp
註解: 預設選項,生成標準的minidump, 轉儲檔案通常較小,便於在網路上通過郵件或其他方式傳輸。 這種檔案的資訊量較少,只包含系統資訊、載入的模組(DLL)資訊、 程序資訊和執行緒資訊。
選項(2): /ma
命令列示例:.dump /ma C:/dumps/myapp.dmp
註解: 帶有儘量多選項的minidump(包括完整的記憶體內容、控制代碼、未載入的模組,等等),檔案很大,但如果條件允許(本機除錯,區域網環境), 推薦使用這中dump。
選項(3):/mFhutwd
命令列示例:.dump /mFhutwd C:/dumps/myapp.dmp
註解:帶有資料段、非共享的讀/寫記憶體頁和其他有用的資訊的minidump。包含了通過minidump能夠得到的最多的資訊。是一種折中方案。
Fhutwd按字母對應上面的MiniOptions表示
//-xp自動生成dump-----------------------------------------------------------------------------------------------------------------------------------------------------------------
那怎麼自動生成dump檔案呢,比如對方的電腦沒有windbg,這裡用到一個window XP系統自帶工具,Dr.Watson
執行方式很簡單:
直接run-輸入drwtsn32 -i就可以了,會提示這樣的:
這個命令真難記,實話,記華生醫生吧,福爾摩斯中的
如果有程式崩潰,會自動生成dump,這時再輸入drwtsn32就會執行這個程式:
找到對應路徑的DMP檔案就行了,一般放在如下路徑:
C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson
//-win7自動生成dump-----------------------------------------------------------------------------------------------------------------------------------------------------------------
win7下需開啟regedit--> 找到:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting]
在它下面加一項LocalDumps,並做如下項配置:
Value | 描述 | Type | 預設值 |
DumpFolder | 檔案儲存路徑 | REG_EXPAND_SZ | %LOCALAPPDATA%CrashDumps |
DumpCount | dump檔案的最大數目 | REG_DWORD | 10 |
DumpType | 指定生成的dump型別: 0:Custom dump 1:Mini dump 2:Full dump |
REG_DWORD | 1 |
CustomDumpFlags | 僅在DumpType為0時使用 為MINIDUMP_TYPE的組合 |
REG_DWORD |
|
可以寫成.bat:
- @echo off
- echo 設定Dump...
- reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps"
- reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpFolder /t REG_EXPAND_SZ /d "C:\MyDump" /f
- reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpType /t REG_DWORD /d 2 /f
- reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpCount /t REG_DWORD /d 10 /f
- echo Dump已經設定
- pause
- @echo on
- @echo off
- echo 正在取消設定Dump...
- reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /f
- echo Dump已經取消設定
- pause
- @echo on
LocalDumps是全域性的,如果想針對指定程序單獨設定,如test1.exe,則在/LocalDumps下新建子項test1.exe,同時在test1目錄下複製上表的選項,這樣,系統就會先讀全域性設定,再讀子項test1.exe的設定
簡單測試:
void CtMFC5Dlg::OnBnClickedButton1()
{
// TODO: 在此新增控制元件通知處理程式程式碼
char * pstr = NULL;
int n = strlen( pstr );
}
在Windows 7上可以由多個方法產生dump檔案:
轉一篇文章:
如何使用dump檔案
我最近在開發一個windows下的程式(win7/win8),有一些case下會crash,如果在自己開發機器上除錯比較簡單:執行程式,然後vs attach到程序上即可,但是在每臺QA的機器上安裝vs時不現實的,因此我們要用到dump檔案。
微軟網站有一篇文章講述如何建立dump檔案:
http://support.microsoft.com/kb/931673
第一種: 通過工作管理員:這種適用在程式掛了(crash)的時候程序還未退出,比如我執行程式,出現了下面的錯:
此時開啟工作管理員,右擊相應程序,點選"Create Dump File“:
一會建立完成:
然後把這個DMP檔案拷到開發機器上,用VS開啟: 會出現下面的介面,要想知道發生錯誤時候的呼叫棧,需要設定symbol的路徑,點選”Set Symbol Paths“:
注意這個pdb要對應於crash的exe,否則呼叫棧沒法顯示:
設定完成後,點選”Debug with Native Only“ 你就可以看到呼叫棧了。
第二種: 改登錄檔
如果程式crash的時候沒有框蹦出來,可以通過改登錄檔的設定讓作業系統在程式crash的時候自動生成dump,並放到特定的目錄下
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps
- Value Name = DumpType
- Data type: REG_DWORD
- Value Data = 1
其中Value Data=1代表的含義是:
- 0 = Create a custom dump
- 1 = Mini dump
- 2 = Full dump
設定完成後,crash發生時,作業系統生成dump,路徑在%LOCALAPPDATA%\CrashDumps下,詳細可以參考:
http://msdn.microsoft.com/en-us/library/bb787181%28v=VS.85%29.aspx
(完)
這裡再介紹一個dump工具:
procdump.exe / procdump64.exe;
通過命令列執行,這裡簡化,寫到bat檔案中;
procdump.exe -l -ma -b -e -t -n 1 36280 -o ./testtestdump.dump
36280 :指的是 程序的PID,可以從人物管理器獲取;
更多引數,可以直接cmd執行procdump.exe檢視;
這裡要說明一個問題,這個產生的dmp檔案,在VS2017中除錯的時候,定位不準確,但是我已經將pdb檔案路徑設定了;
不過用windbg.exe,可以很好的定位;
《windgb的簡單應用》 定位,和資訊更清楚;
簡單說明:
1:設定pdb檔案路徑:File -》 Symbol file path...
2:File -》Open Crash Dump
3:等待載入完成後,執行命令:
!analyze -v // 詳細顯示當前異常資訊
4:檢視輸出資訊;
5:View -》 Call Stack
6:雙擊可以直接定位原始碼檔案,所以,前面不需要設定原始檔路徑;
windgb命令:
!analyze -v // 詳細顯示當前異常資訊
!peb // 格式化輸出PEB資訊(process's environment block)
!gle // 列印當前執行緒最近的錯誤資訊
!gle -all // 列印所有執行緒的最近的錯誤資訊
!error 897// 顯示錯誤碼為897的詳細描述資訊