1.生成dump文件
这边参考博客中的源码地址,breakpaddemo不同编译环境可直接编译运行使用,可得到dmp文件
2. dump调试(msvc)
2.1 vs2010编译调试
关键是原项目需要生成pdb文件,vs需要如下配置(偷图):
项目->属性->C/C+±>General->Debug Information Format->Program Database
项目->属性->C/C+±>Optimization->Optimization->Disabled(/Od),这一步貌似也可不设置
项目->属性->Linker->Debugging->Generate Debug Info->Yes(/DEBUG)
做完以上配置后,构建生成可执行文件,运行,生成dmp文件,dmp文件拷贝到执行文件目录(保证exe,pdb,dmp文件在一个目录即可),运行dmp文件,vs会自动打开,再点击右上角的调试,即可定位到崩溃的代码片段。
2.2 Qt + msvc编译调试
类似vs,要生成pdb文件,原项目pro文件中需添加代码:
CONFIG += force_debug_info
CONFIG += separate_debug_info
之后就是构建运行,与2.1之后的操作一样
3. Qt mingw编译调试
3.1 项目pro文件代码片段
QMAKE_CFLAGS_RELEASE += -g
QMAKE_CXXFLAGS_RELEASE += -g
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE -= -O2
QMAKE_LFLAGS_RELEASE = -mthreads -W
3.2 crash类与回调函数准备
ccrashstack.h
#include
#include
class CCrashStack
{
private:
PEXCEPTION_POINTERS m_pException;
private:
QString GetModuleByRetAddr(PBYTE Ret_Addr, PBYTE & Module_Addr);
QString GetCallStack(PEXCEPTION_POINTERS pException);
QString GetVersionStr();
bool GetHardwareInaformation(QString &graphics_card, QString &sound_deivce);
public:
CCrashStack(PEXCEPTION_POINTERS pException);
QString GetExceptionInfo();
};
ccrashstack.cpp
#include "ccrashstack.h"
#include
#include
#define _WIN32_DCOM
#include
#include
//#include
#include "qdebug.h"
CCrashStack::CCrashStack(PEXCEPTION_POINTERS pException)
{
m_pException = pException;
}
QString CCrashStack::GetModuleByRetAddr(PBYTE Ret_Addr, PBYTE & Module_Addr)
{
MODULEENTRY32 M = {sizeof(M)};
HANDLE hSnapshot;
wchar_t Module_Name[MAX_PATH] = {0};
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if ((hSnapshot != INVALID_HANDLE_VALUE) &&
Module32First(hSnapshot, &M))
{
do
{
if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
{
lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
Module_Addr = M.modBaseAddr;
break;
}
} while (Module32Next(hSnapshot, &M));
}
CloseHandle(hSnapshot);
QString sRet = QString::fromWCharArray(Module_Name);
return sRet;
}
QString CCrashStack::GetCallStack(PEXCEPTION_POINTERS pException)
{
PBYTE Module_Addr_1;
char bufer[256]={0};
QString sRet;
typedef struct STACK
{
STACK * Ebp;
PBYTE Ret_Addr;
DWORD Param[0];
} STACK, * PSTACK;
STACK Stack = {0, 0};
PSTACK Ebp;
if (pException) //fake frame for exception address
{
Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
Ebp = &Stack;
}
else
{
Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack()
// Skip frame of Get_Call_Stack().
if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
Ebp = Ebp->Ebp; //caller ebp
}
// Break trace on wrong stack frame.
for (; !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
Ebp = Ebp->Ebp)
{
// If module with Ebp->Ret_Addr found.
memset(bufer,0, sizeof(0));
sprintf(bufer, "\n%08X ", (unsigned int)Ebp->Ret_Addr);
sRet.append(bufer);
QString moduleName = this->GetModuleByRetAddr(Ebp->Ret_Addr, Module_Addr_1) ;
if (moduleName.length() > 0)
{
sRet.append(moduleName);
}
}
return sRet;
} //Get_Call_Stack
QString CCrashStack::GetVersionStr()
{
OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later
if (!GetVersionEx((POSVERSIONINFO)&V))
{
ZeroMemory(&V, sizeof(V));
V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx((POSVERSIONINFO)&V);
}
if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx
QString sRet;
sRet.append(QString("Windows: %1.%2.%3, SP %4.%5, Product Type %6\n")
.arg(V.dwMajorVersion).arg(V.dwMinorVersion).arg(V.dwBuildNumber)
.arg(V.wServicePackMajor).arg(V.wServicePackMinor).arg(V.wProductType));
// QString graphics_module = "GraphicsCard: ";
// QString sound_module = "SoundDevice: ";
// GetHardwareInaformation(graphics_module, sound_module);
// sRet.append(graphics_module);
// sRet.append(sound_module);
return sRet;
}
QString CCrashStack::GetExceptionInfo()
{
WCHAR Module_Name[MAX_PATH];
PBYTE Module_Addr;
QString sRet;
char buffer[512]={0};
QString sTmp = GetVersionStr();
sRet.append(sTmp);
sRet.append("Process: ");
GetModuleFileName(NULL, Module_Name, MAX_PATH);
sRet.append(QString::fromWCharArray(Module_Name));
sRet.append("\n");
// If exception occurred.
if (m_pException)
{
EXCEPTION_RECORD & E = *m_pException->ExceptionRecord;
CONTEXT & C = *m_pException->ContextRecord;
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "Exception Addr: %08X ", (int)E.ExceptionAddress);
sRet.append(buffer);
// If module with E.ExceptionAddress found - save its path and date.
QString module = GetModuleByRetAddr((PBYTE)E.ExceptionAddress, Module_Addr);
if (module.length() > 0)
{
sRet.append(" Module: ");
sRet.append(module);
}
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "\nException Code: %08X\n", (int)E.ExceptionCode);
sRet.append(buffer);
if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
// Access violation type - Write/Read.
memset(buffer, 0, sizeof(buffer));
sprintf(buffer,"%s Address: %08X\n",
(E.ExceptionInformation[0]) ? "Write" : "Read", (int)E.ExceptionInformation[1]);
sRet.append(buffer);
}
sRet.append("Instruction: ");
for (int i = 0; i < 16; i++)
{
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, " %02X", PBYTE(E.ExceptionAddress)[i]);
sRet.append(buffer);
}
sRet.append("\nRegisters: ");
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "\nEAX: %08X EBX: %08X ECX: %08X EDX: %08X", (unsigned int)C.Eax,(unsigned int) C.Ebx, (unsigned int)C.Ecx, (unsigned int)C.Edx);
sRet.append(buffer);
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "\nESI: %08X EDI: %08X ESP: %08X EBP: %08X", (unsigned int)C.Esi, (unsigned int)C.Edi, (unsigned int)C.Esp, (unsigned int)C.Ebp);
sRet.append(buffer);
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "\nEIP: %08X EFlags: %08X", (unsigned int)C.Eip,(unsigned int) C.EFlags);
sRet.append(buffer);
} //if (pException)
sRet.append("\nCall Stack:");
QString sCallstack = this->GetCallStack(m_pException);
sRet.append(sCallstack);
return sRet;
}
main.cpp
#include "ccrashstack.h"
long __stdcall callback(_EXCEPTION_POINTERS* excp)
{
CCrashStack crashStack(excp);
QString sCrashInfo = crashStack.GetExceptionInfo();
// TCHAR my_documents[MAX_PATH];
// SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, my_documents);
// QString file_path = QString::fromWCharArray(my_documents);
QDir dir;
QString file_path = dir.currentPath();
QDir *folder_path = new QDir;
bool exist = folder_path->exists(file_path.append("\\MyApp"));
if(!exist)
{
folder_path->mkdir(file_path);
}
delete folder_path;
folder_path = nullptr;
QString sFileName = file_path + "\\crash.log";
QFile file(sFileName);
if (file.open(QIODevice::WriteOnly|QIODevice::Truncate))
{
file.write(sCrashInfo.toUtf8());
file.close();
}
return EXCEPTION_EXECUTE_HANDLER;
}
int main() {
SetUnhandledExceptionFilter(callback);
...
...
}
3.3 使用Qt工具,根据crash输出的错误地址查找错误位置
使用上述crashstack类在进程崩溃时会在执行文件目录下的MyApp目录内产生一个crash.log文件:
Windows: 6.1.7601, SP 1.0, Product Type 1
Process: …\BreakpadDemo.exe
Exception Addr: 00406621 Module: …\BreakpadDemo.exe
Exception Code: C0000005
Write Address: 00000000
Instruction: C7 00 01 00 00 00 90 C9 C3 55 89 E5 83 EC 04 89
Registers:
EAX: 00000000 EBX: 00000000 ECX: 0028FE1C EDX: 00000000
ESI: 00000029 EDI: 0280E968 ESP: 0028D3DC EBP: 0028D3EC
EIP: 00406621 EFlags: 00010202
Call Stack:
00406621 …\BreakpadDemo.exe
00406638 …\BreakpadDemo.exe
00408AED …\BreakpadDemo.exe
00408BE0 …\BreakpadDemo.exe
68C5E1FF …\Qt5Core.dll
00AB9ED0 …\Qt5Widgets.dll
00440064 …\BreakpadDemo.exe
(注意加粗的部分,在下个步骤会用到)
在Qt安装目录下找到对应32位的addr2line.exe程序,拷贝到应用程序的目录下,在cmd定位到执行文件目录再执行addr2line.exe -f -e [exe name] [addr name]
下面是我的运行结果
F:\...\release>addr2line.exe -f -e BreakpadDemo.exe 00406621
_Z5crashv
F:\.../../BreakpadDemo/src/ui/demodialog.cpp:16
[exe name]后面可跟多个addr地址,我只输入一个,即用了log文件中加粗的部分。最后输出的可以看到是demodialog.cpp:16出了问题,成功定位。
4. 参考链接
qt mingw 创建dump 并查找crash 出错行
Qt下MSVC/Mingw平台dump/crash log报告调试方法差异
C+±Dump文件的创建和调试
12.5-使用Qt实现跨平台C++崩溃捕获,看这一篇就足够了(Breakpad)
————————————————
版权声明:本文为CSDN博主「w54a3te」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/w54a3teg64c7bd/article/details/108993208