讓程式在崩潰時體面的退出之CallStack(轉)
在我的那篇《讓程式在崩潰時體面的退出之Unhandled Exception》中提供了一個捕捉程式崩潰事件的方法,可以新增程式碼在程式崩潰的時候做出適當的處理。不過,只知道程式在什麼時候崩潰,但是不知道為什麼崩潰,這對於程式開發者來說沒有任何意義。因為如果不知道程式崩潰的原因,就沒法去找到程式碼中的缺陷,當然就沒法去修改程式碼而避免程式的崩潰。
所有除錯過程式碼的開發者都知道CallStack的重要性。如果在程式崩潰的時候得到CallStack,那麼就能定位程式崩潰的具體位置,並最終找到解決方法。那麼有沒有什麼方法在程式崩潰的時候得到CallStack呢?答案是肯定的。微軟提供了一個DbgHelp.dll,裡面包含了一系列的Windows API來供開發者呼叫。它是一個除錯跟蹤相關的模組,用於跟蹤程序工作,在程序崩潰時收集程式產生異常時的堆疊資訊,以供開發人員分析,從而很快找出使程式出現異常的原因。
下面用具體的例子程式碼來說明怎樣使用DbgHelp.dll中的Windows API來得到CallStack。程式碼裡面有詳細的註釋來幫助理解。
用VC建立一個名為Test的控制檯程式,新增下面的程式碼。
- // 一個有函式呼叫的類
- //
- class CrashTest
- {
- public:
- void Test()
- {
- Crash();
- }
- private:
- void Crash()
- {
- // 除零,人為的使程式崩潰
- //
- int i = 13;
- int j = 0;
- int m = i / j;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
-
CrashTest test;
- test.Test();
- return 0;
- }
編譯上面的程式碼,到工程的Debug目錄下找到編譯好的Test.exe,雙擊執行,程式就會崩潰。從程式碼我們知道函式呼叫順序是main() -> CrashTest::Test() -> CrashTest::Crash()。那麼怎麼在程式崩潰的時候得到CallStack呢?首先,先新增一些工具函式。
- #include <Windows.h>
- #include <DbgHelp.h>
- #include <iostream>
-
#include <vector>
- // 新增對dbghelp.lib的編譯依賴
- //
- #pragma comment(lib, "dbghelp.lib")
- usingnamespace std;
- constint MAX_ADDRESS_LENGTH = 32;
- constint MAX_NAME_LENGTH = 1024;
- // 崩潰資訊
- //
- struct CrashInfo
- {
- CHAR ErrorCode[MAX_ADDRESS_LENGTH];
- CHAR Address[MAX_ADDRESS_LENGTH];
- CHAR Flags[MAX_ADDRESS_LENGTH];
- };
- // CallStack資訊
- //
- struct CallStackInfo
- {
- CHAR ModuleName[MAX_NAME_LENGTH];
- CHAR MethodName[MAX_NAME_LENGTH];
- CHAR FileName[MAX_NAME_LENGTH];
- CHAR LineNumber[MAX_NAME_LENGTH];
- };
- // 安全拷貝字串函式
- //
- void SafeStrCpy(char* szDest, size_t nMaxDestSize, constchar* szSrc)
- {
- if (nMaxDestSize <= 0) return;
- if (strlen(szSrc) < nMaxDestSize)
- {
- strcpy_s(szDest, nMaxDestSize, szSrc);
- }
- else
- {
- strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize);
- szDest[nMaxDestSize-1] = '\0';
- }
- }
- // 得到程式崩潰資訊
- //
- CrashInfo GetCrashInfo(const EXCEPTION_RECORD *pRecord)
- {
- CrashInfo crashinfo;
- SafeStrCpy(crashinfo.Address, MAX_ADDRESS_LENGTH, "N/A");
- SafeStrCpy(crashinfo.ErrorCode, MAX_ADDRESS_LENGTH, "N/A");
- SafeStrCpy(crashinfo.Flags, MAX_ADDRESS_LENGTH, "N/A");
- sprintf_s(crashinfo.Address, "%08X", pRecord->ExceptionAddress);
- sprintf_s(crashinfo.ErrorCode, "%08X", pRecord->ExceptionCode);
- sprintf_s(crashinfo.Flags, "%08X", pRecord->ExceptionFlags);
- return crashinfo;
- }
- // 得到CallStack資訊
- //
- vector<CallStackInfo> GetCallStack(const CONTEXT *pContext)
- {
- HANDLE hProcess = GetCurrentProcess();
- SymInitialize(hProcess, NULL, TRUE);
- vector<CallStackInfo> arrCallStackInfo;
- CONTEXT c = *pContext;
- STACKFRAME64 sf;
- memset(&sf, 0, sizeof(STACKFRAME64));
- DWORD dwImageType = IMAGE_FILE_MACHINE_I386;
- // 不同的CPU型別,具體資訊可查詢MSDN
- //
- #ifdef _M_IX86
- sf.AddrPC.Offset = c.Eip;
- sf.AddrPC.Mode = AddrModeFlat;
- sf.AddrStack.Offset = c.Esp;
- sf.AddrStack.Mode = AddrModeFlat;
- sf.AddrFrame.Offset = c.Ebp;
- sf.AddrFrame.Mode = AddrModeFlat;
- #elif _M_X64
- dwImageType = IMAGE_FILE_MACHINE_AMD64;
- sf.AddrPC.Offset = c.Rip;
- sf.AddrPC.Mode = AddrModeFlat;
- sf.AddrFrame.Offset = c.Rsp;
- sf.AddrFrame.Mode = AddrModeFlat;
- sf.AddrStack.Offset = c.Rsp;
- sf.AddrStack.Mode = AddrModeFlat;
- #elif _M_IA64
- dwImageType = IMAGE_FILE_MACHINE_IA64;
- sf.AddrPC.Offset = c.StIIP;
- sf.AddrPC.Mode = AddrModeFlat;
- sf.AddrFrame.Offset = c.IntSp;
- sf.AddrFrame.Mode = AddrModeFlat;
- sf.AddrBStore.Offset = c.RsBSP;
- sf.AddrBStore.Mode = AddrModeFlat;
- sf.AddrStack.Offset = c.IntSp;
- sf.AddrStack.Mode = AddrModeFlat;
- #else
- #error "Platform not supported!"
- #endif
- HANDLE hThread = GetCurrentThread();
- while (true)
- {
- // 該函式是實現這個功能的最重要的一個函式
- // 函式的用法以及引數和返回值的具體解釋可以查詢MSDN
- //
- if (!StackWalk64(dwImageType, hProcess, hThread, &sf, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
- {
- break;
- }
- if (sf.AddrFrame.Offset == 0)
- {
- break;
- }
- CallStackInfo callstackinfo;
- SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, "N/A");
- SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, "N/A");
- SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, "N/A");
- SafeStrCpy(callstackinfo.LineNumber, MAX_NAME_LENGTH, "N/A");
- BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH];
- IMAGEHLP_SYMBOL64 *pSymbol = (IMAGEHLP_SYMBOL64*)symbolBuffer;
- memset(pSymbol, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH);
- pSymbol->SizeOfStruct = sizeof(symbolBuffer);
- pSymbol->MaxNameLength = MAX_NAME_LENGTH;
- DWORD symDisplacement = 0;
- // 得到函式名
- //
- if (SymGetSymFromAddr64(hProcess, sf.AddrPC.Offset, NULL, pSymbol))
- {
- SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, pSymbol->Name);
- }
- IMAGEHLP_LINE64 lineInfo;
- memset(&lineInfo, 0, sizeof(IMAGEHLP_LINE64));
- lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- DWORD dwLineDisplacement;
- // 得到檔名和所在的程式碼行
- //
- if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
- {
- SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, lineInfo.FileName);
- sprintf_s(callstackinfo.LineNumber, "%d", lineInfo.LineNumber);
- }
- IMAGEHLP_MODULE64 moduleInfo;
- memset(&moduleInfo, 0, sizeof(IMAGEHLP_MODULE64));
-
相關推薦
讓程式在崩潰時體面的退出之CallStack(轉)
在我的那篇《讓程式在崩潰時體面的退出之Unhandled Exception》中提供了一個捕捉程式崩潰事件的方法,可以新增程式碼在程式崩潰的時候做出適當的處理。不過,只知道程式在什麼時候崩潰,但是不知道為什麼崩潰,這對於程式開發者來說沒有任何意義。因為如果不知道程式崩潰
讓程式在崩潰時體面的退出之CallStack
在我的那篇《讓程式在崩潰時體面的退出之Unhandled Exception》中提供了一個捕捉程式崩潰事件的方法,可以新增程式碼在程式崩潰的時候做出適當的處理。不過,只知道程式在什麼時候崩潰,但是不知道為什麼崩潰,這對於程式開發者來說沒有任何意義。因為如果不知
讓程式在崩潰時體面的退出之Dump檔案
在我的那篇《讓程式在崩潰時體面的退出之CallStack》中提供了一個在程式崩潰時得到CallStack的方法。可是要想得到CallStack,必須有pdb檔案的支援。但是一般情況下,釋出出去的程式都是 Release版本的,都不會附帶pdb檔案。那麼我們怎麼能在程式崩潰的
讓程式在崩潰時體面的退出之Unhandled Exception
程式是由程式碼編譯出來的,而程式碼是由人寫的。人非聖賢,孰能無過。所以由人寫的程式碼有缺陷是很正常的。當然很多異常都在開發階段被考慮到而添加了處理程式碼,或者用try/catch對可能
使用Dump檔案讓程式在崩潰時體面的退出
在我的那篇《讓程式在崩潰時體面的退出之CallStack》中提供了一個在程式崩潰時得到CallStack的方法。可是要想得到CallStack,必須有pdb檔案的支援。但是一般情況下,釋出出去的程式都是Release版本的,都不會附帶pdb檔案。那麼我們怎麼能在程式崩潰的時
如何防止後臺執行緒丟擲的異常讓程式崩潰退出
如果你的程式拋了異常,你是怎麼處理的呢?等待程式崩潰退出?還是進行補救? 如果是做 UI 開發,很容易就找到 Dispatcher.UnhandledException 事件,然後在事件中進行補救。如果補救成功,可以設定 e.Handled = true 來阻
windows程式崩潰時自動生成dump檔案方法
/****************第一步新增createdump.h********************************* 新增一個頭檔案:createdump.h #pragma once #include <windows.h> #inclu
C++程式執行時記憶體佈局之----------區域性變數,全域性變數,靜態變數,函式程式碼,new出來的變數
宣告兩點: (1)開發測試環境為VS2010+WindowsXP32位; (2)記憶體佈局指的是虛擬記憶體地址,不是實體地址。 1.測試程式碼 #include <iostream> using namespace std; int g_int_a; i
利用Windows自帶的功能當程式崩潰時產生崩潰轉儲檔案(dmp)
何志丹 以管理員身份 執行 :OpenDump.bat 其本質是寫登錄檔。 執行後: 任何程式崩潰都會在C:\CrashDump 產生dmp檔案(比較大,約50到200M)。 至少在Win7、Win10的電腦,Win10的平板上執行正確。 OpenDump.bat @e
gdb除錯命令及程式崩潰時的核心轉存core dump
1.gcc -g filename.c -o filename 需要生成帶除錯資訊的檔案 2.除錯 方式一:gdb filename 除錯file可執行檔案 方式二:>>gdb >>file filename $gd
windows 應用程式崩潰時的記憶體轉儲及dump檔案的分析
1、在現場設定程式崩潰時的自動記憶體轉儲,得到dump檔案 在windows 登錄檔如下項: //HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/AeDebug
讓程式崩潰但是不閃退的方法(可以用在真機測試上)
/** 讓程式崩潰但是不閃退的方法 */ void handException(NSException * exception){ /** 彈出提示框 */ UIAlertView * alert = [[UIAlertView alloc]in
C++ 記錄Windows程式崩潰時的dumpfile
【原理】 windows程式當遇到異常,沒有try-catch或者try-catch也無法捕獲到的異常時,程式就會自動退出,如果這時候沒有dump檔案的話,我們是沒有得到任何程式退出的資訊。在windows程式異常退出之前,會預先呼叫一個在程式中註冊的異常處理回撥
記錄程式崩潰時的呼叫堆疊
最近有個使用者遇到程式Crash問題,但我們的機器都不能重現,於是在網上搜了一把,發現有個MSJExceptionHandler類還比較好用,故整理了一下供大家參考。 這個類的使用方法很簡單,只要把這個類加入到你的工程(不管是MFC,com,dll都可以)中一起編譯就可以了
程式崩潰時生成Dump檔案
Dump檔案是程序的記憶體映象,可以把程式執行時的狀態完整的儲存下來,之後通過除錯工具可查出崩潰大致原因。 SetUnhandledExceptionFilter()設定一個在程式崩潰時被呼叫的回撥函式。MiniDumpWriteDump()建立Dump檔案。 我寫了一個
C++程式崩潰時,生成dmp除錯檔案
包含如下標頭檔案 和 引用Lib庫 #include <dbghelp.h> #pragma comment(lib, "dbghelp.lib")定義 LONG WINAPI MyUnh
如何讓連結延時幾秒後跳轉呢? 已解決
js 用 setTimeout 引用當初做的專案一個例項 $('#real_submit').click(function () { $.ajax({ cach
ArcGIS for Android 1.1 MapView 的Activity退出時整個程式崩潰問題
在專案中當我們從一個activity中跳轉到Mapview的activity中後,點選回退鍵後,等待不長時間程式崩潰問題,此時報call to OpenGL ES API with no current context (logged once per thread),
關於QT在開啟子視窗時程式崩潰的其中一個原因分析
其實這個問題當時是糾結了我很長的一段時間,這段時間裡面,我一直在網上面找相關的資料但是卻沒有有用的資訊。 但是在後面的一個機緣巧合之下,我通過函式執行順序來Debug,慢慢的發現問題出現在什麼地方了。現在來總結一下這個問題吧。 其實我現在的經驗覺得,對於QT裡面(由於QT是基於C++的),不
利用MapFile定位程式崩潰(報紅牌)時的程式碼位置
原文:http://www.codeproject.com/KB/debug/mapfile.aspx 1、生成MapFile Project—Setting—C+±—DebugInfo,選擇Line Numbers Only Project—Setting—Link—選擇Generat