1
0
mirror of https://github.com/d47081/qBittorrent.git synced 2025-01-11 15:27:54 +00:00

Merge pull request #2214 from Gelmir/trace

Fix backtrace functionality under MinGW
This commit is contained in:
sledgehammer999 2014-11-29 16:50:06 +02:00
commit 6c0b74b94b
3 changed files with 253 additions and 199 deletions

View File

@ -1,95 +1,124 @@
/*************************************************************************** /***************************************************************************
* Copyright (C) 2005-09 by the Quassel Project * * Copyright (C) 2005-09 by the Quassel Project *
* devel@quassel-irc.org * * devel@quassel-irc.org *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or * * the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3. * * (at your option) version 3. *
* * * *
* This program is distributed in the hope that it will be useful, * * This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. * * GNU General Public License for more details. *
* * * *
* You should have received a copy of the GNU General Public License * * You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the * * along with this program; if not, write to the *
* Free Software Foundation, Inc., * * Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include <windows.h> #include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#include <stdio.h> #include <stdio.h>
#include <QTextStream> #include <QTextStream>
#ifdef __MINGW32__
#include <cxxabi.h>
#endif
namespace straceWin{ namespace straceWin
void loadHelpStackFrame(IMAGEHLP_STACK_FRAME&, const STACKFRAME64&); {
BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO, ULONG, PVOID); void loadHelpStackFrame(IMAGEHLP_STACK_FRAME&, const STACKFRAME64&);
BOOL CALLBACK EnumModulesCB(LPCSTR, DWORD64, PVOID); BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO, ULONG, PVOID);
const QString getBacktrace(); BOOL CALLBACK EnumModulesCB(LPCSTR, DWORD64, PVOID);
struct EnumModulesContext; const QString getBacktrace();
struct EnumModulesContext;
// Also works for MinGW64
#ifdef __MINGW32__
void demangle(QString& str);
#endif
} }
void straceWin::loadHelpStackFrame(IMAGEHLP_STACK_FRAME &ihsf, const STACKFRAME64 &stackFrame) { #ifdef __MINGW32__
ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME)); void straceWin::demangle(QString& str)
ihsf.InstructionOffset = stackFrame.AddrPC.Offset; {
ihsf.FrameOffset = stackFrame.AddrFrame.Offset; char const* inStr = qPrintable("_" + str); // Really need that underline or demangling will fail
int status = 0;
size_t outSz = 0;
char* demangled_name = abi::__cxa_demangle(inStr, 0, &outSz, &status);
if (status == 0) {
str = QString::fromLocal8Bit(demangled_name);
if (outSz > 0)
free(demangled_name);
}
}
#endif
void straceWin::loadHelpStackFrame(IMAGEHLP_STACK_FRAME& ihsf, const STACKFRAME64& stackFrame)
{
ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));
ihsf.InstructionOffset = stackFrame.AddrPC.Offset;
ihsf.FrameOffset = stackFrame.AddrFrame.Offset;
} }
BOOL CALLBACK straceWin::EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user) { BOOL CALLBACK straceWin::EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user)
QStringList *params = (QStringList *)user; {
if(symInfo->Flags & SYMFLAG_PARAMETER) { Q_UNUSED(size)
params->append(symInfo->Name); QStringList* params = (QStringList*)user;
} if (symInfo->Flags & SYMFLAG_PARAMETER)
return TRUE; params->append(symInfo->Name);
return TRUE;
} }
struct straceWin::EnumModulesContext { struct straceWin::EnumModulesContext
HANDLE hProcess; {
QTextStream &stream; HANDLE hProcess;
EnumModulesContext(HANDLE hProcess, QTextStream &stream) : hProcess(hProcess), stream(stream) {} QTextStream& stream;
EnumModulesContext(HANDLE hProcess, QTextStream& stream): hProcess(hProcess), stream(stream) {}
}; };
BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext) { BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext)
IMAGEHLP_MODULE64 mod; {
EnumModulesContext *context = (EnumModulesContext *)UserContext; Q_UNUSED(ModuleName)
mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); IMAGEHLP_MODULE64 mod;
if(SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) { EnumModulesContext* context = (EnumModulesContext*)UserContext;
QString moduleBase = QString("0x%1").arg(BaseOfDll, 8, 16, QLatin1Char('0')); mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
QString line = QString("%1 %2 Image: %3") if(SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) {
.arg(mod.ModuleName, -14) QString moduleBase = QString("0x%1").arg(BaseOfDll, 16, 16, QLatin1Char('0'));
.arg(moduleBase, -13) QString line = QString("%1 %2 Image: %3")
.arg(mod.LoadedImageName); .arg(mod.ModuleName, -25)
context->stream << line << '\n'; .arg(moduleBase, -13)
.arg(mod.LoadedImageName);
context->stream << line << '\n';
QString pdbName(mod.LoadedPdbName); QString pdbName(mod.LoadedPdbName);
if(!pdbName.isEmpty()) { if(!pdbName.isEmpty()) {
QString line2 = QString("%1 %2") QString line2 = QString("%1 %2")
.arg("", 35) .arg("", 35)
.arg(pdbName); .arg(pdbName);
context->stream << line2 << '\n'; context->stream << line2 << '\n';
}
} }
} return TRUE;
return TRUE;
} }
#if defined( _M_IX86 ) && defined(Q_CC_MSVC) #if defined( _M_IX86 ) && defined(Q_CC_MSVC)
// Disable global optimization and ignore /GS waning caused by // Disable global optimization and ignore /GS waning caused by
// inline assembly. // inline assembly.
// not needed with mingw cause we can tell mingw which registers we use // not needed with mingw cause we can tell mingw which registers we use
#pragma optimize("g", off) #pragma optimize("g", off)
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4748) #pragma warning(disable : 4748)
#endif #endif
const QString straceWin::getBacktrace() { const QString straceWin::getBacktrace()
DWORD MachineType; {
CONTEXT Context; DWORD MachineType;
STACKFRAME64 StackFrame; CONTEXT Context;
STACKFRAME64 StackFrame;
#ifdef _M_IX86 #ifdef _M_IX86
ZeroMemory(&Context, sizeof(CONTEXT)); ZeroMemory(&Context, sizeof(CONTEXT));
@ -97,127 +126,143 @@ const QString straceWin::getBacktrace() {
#ifdef __MINGW32__ #ifdef __MINGW32__
asm("Label:\n\t" asm ("Label:\n\t"
"movl %%ebp,%0;\n\t" "movl %%ebp,%0;\n\t"
"movl %%esp,%1;\n\t" "movl %%esp,%1;\n\t"
"movl $Label,%%eax;\n\t" "movl $Label,%%eax;\n\t"
"movl %%eax,%2;\n\t" "movl %%eax,%2;\n\t"
:"=r"(Context.Ebp),"=r"(Context.Esp),"=r"(Context.Eip) : "=r" (Context.Ebp),"=r" (Context.Esp),"=r" (Context.Eip)
://no input : //no input
:"eax"); : "eax");
#else #else
_asm { _asm {
Label: Label:
mov [Context.Ebp], ebp; mov [Context.Ebp], ebp;
mov [Context.Esp], esp; mov [Context.Esp], esp;
mov eax, [Label]; mov eax, [Label];
mov [Context.Eip], eax; mov [Context.Eip], eax;
} }
#endif #endif
#else #else
RtlCaptureContext(&Context); RtlCaptureContext(&Context);
#endif #endif
ZeroMemory(&StackFrame, sizeof(STACKFRAME64)); ZeroMemory(&StackFrame, sizeof(STACKFRAME64));
#ifdef _M_IX86 #ifdef _M_IX86
MachineType = IMAGE_FILE_MACHINE_I386; MachineType = IMAGE_FILE_MACHINE_I386;
StackFrame.AddrPC.Offset = Context.Eip; StackFrame.AddrPC.Offset = Context.Eip;
StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrPC.Mode = AddrModeFlat;
StackFrame.AddrFrame.Offset = Context.Ebp; StackFrame.AddrFrame.Offset = Context.Ebp;
StackFrame.AddrFrame.Mode = AddrModeFlat; StackFrame.AddrFrame.Mode = AddrModeFlat;
StackFrame.AddrStack.Offset = Context.Esp; StackFrame.AddrStack.Offset = Context.Esp;
StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64 #elif _M_X64
MachineType = IMAGE_FILE_MACHINE_AMD64; MachineType = IMAGE_FILE_MACHINE_AMD64;
StackFrame.AddrPC.Offset = Context.Rip; StackFrame.AddrPC.Offset = Context.Rip;
StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrPC.Mode = AddrModeFlat;
StackFrame.AddrFrame.Offset = Context.Rsp; StackFrame.AddrFrame.Offset = Context.Rsp;
StackFrame.AddrFrame.Mode = AddrModeFlat; StackFrame.AddrFrame.Mode = AddrModeFlat;
StackFrame.AddrStack.Offset = Context.Rsp; StackFrame.AddrStack.Offset = Context.Rsp;
StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64 #elif _M_IA64
MachineType = IMAGE_FILE_MACHINE_IA64; MachineType = IMAGE_FILE_MACHINE_IA64;
StackFrame.AddrPC.Offset = Context.StIIP; StackFrame.AddrPC.Offset = Context.StIIP;
StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrPC.Mode = AddrModeFlat;
StackFrame.AddrFrame.Offset = Context.IntSp; StackFrame.AddrFrame.Offset = Context.IntSp;
StackFrame.AddrFrame.Mode = AddrModeFlat; StackFrame.AddrFrame.Mode = AddrModeFlat;
StackFrame.AddrBStore.Offset= Context.RsBSP; StackFrame.AddrBStore.Offset = Context.RsBSP;
StackFrame.AddrBStore.Mode = AddrModeFlat; StackFrame.AddrBStore.Mode = AddrModeFlat;
StackFrame.AddrStack.Offset = Context.IntSp; StackFrame.AddrStack.Offset = Context.IntSp;
StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrStack.Mode = AddrModeFlat;
#else #else
#error "Unsupported platform" #error "Unsupported platform"
#endif #endif
QString log; QString log;
QTextStream logStream(&log); QTextStream logStream(&log);
logStream << "```\n"; logStream << "```\n";
HANDLE hProcess = GetCurrentProcess(); HANDLE hProcess = GetCurrentProcess();
HANDLE hThread = GetCurrentThread(); HANDLE hThread = GetCurrentThread();
SymInitialize(hProcess, NULL, TRUE); SymInitialize(hProcess, NULL, TRUE);
DWORD64 dwDisplacement; DWORD64 dwDisplacement;
ULONG64 buffer[(sizeof(SYMBOL_INFO) + ULONG64 buffer[(sizeof(SYMBOL_INFO) +
MAX_SYM_NAME*sizeof(TCHAR) + MAX_SYM_NAME * sizeof(TCHAR) +
sizeof(ULONG64) - 1) / sizeof(ULONG64)]; sizeof(ULONG64) - 1) / sizeof(ULONG64)];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME; pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_MODULE64 mod; IMAGEHLP_MODULE64 mod;
mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
IMAGEHLP_STACK_FRAME ihsf; IMAGEHLP_STACK_FRAME ihsf;
ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME)); ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));
int i = 0; int i = 0;
while(StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) {
if(i == 128)
break;
loadHelpStackFrame(ihsf, StackFrame); while(StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) {
if(StackFrame.AddrPC.Offset != 0) { // Valid frame. if(i == 128)
break;
QString fileName("???"); loadHelpStackFrame(ihsf, StackFrame);
if(SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) { if(StackFrame.AddrPC.Offset != 0) { // Valid frame.
fileName = QString(mod.ImageName);
int slashPos = fileName.lastIndexOf('\\');
if(slashPos != -1)
fileName = fileName.mid(slashPos + 1);
}
QString funcName;
if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
funcName = QString(pSymbol->Name);
} else {
funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
}
QStringList params;
SymSetContext(hProcess, &ihsf, NULL);
SymEnumSymbols(hProcess, 0, NULL, EnumSymbolsCB, (PVOID)&params);
QString insOffset = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0')); QString fileName("???");
QString debugLine = QString("#%1 %2 %3 %4(%5)") if(SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) {
.arg(i, 3, 10) fileName = QString(mod.ImageName);
.arg(fileName, -20) int slashPos = fileName.lastIndexOf('\\');
.arg(insOffset, -11) if(slashPos != -1)
.arg(funcName) fileName = fileName.mid(slashPos + 1);
.arg(params.join(", ")); }
logStream << debugLine << '\n'; QString funcName;
i++; if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
} else { funcName = QString(pSymbol->Name);
break; // we're at the end. #ifdef __MINGW32__
demangle(funcName);
#endif
}
else {
funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
}
SymSetContext(hProcess, &ihsf, NULL);
#ifndef __MINGW32__
QStringList params;
SymEnumSymbols(hProcess, 0, NULL, EnumSymbolsCB, (PVOID)&params);
#endif
QString insOffset = QString("0x%1").arg(ihsf.InstructionOffset, 16, 16, QLatin1Char('0'));
QString formatLine = "#%1 %2 %3 %4";
#ifndef __MINGW32__
formatLine += "(%5)";
#endif
QString debugLine = formatLine
.arg(i, 3, 10)
.arg(fileName, -20)
.arg(insOffset, -11)
.arg(funcName)
#ifndef __MINGW32__
.arg(params.join(", "));
#else
;
#endif
logStream << debugLine << '\n';
i++;
}
else {
break; // we're at the end.
}
} }
}
logStream << "\n\nList of linked Modules:\n"; logStream << "\n\nList of linked Modules:\n";
EnumModulesContext modulesContext(hProcess, logStream); EnumModulesContext modulesContext(hProcess, logStream);
SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext); SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
logStream << "```"; logStream << "```";
return log; return log;
} }
#if defined(_M_IX86) && defined(Q_CC_MSVC) #if defined(_M_IX86) && defined(Q_CC_MSVC)
#pragma warning(pop) #pragma warning(pop)
#pragma optimize("g", on) #pragma optimize("g", on)
#endif #endif

View File

@ -7,43 +7,48 @@
#include "libtorrent/version.hpp" #include "libtorrent/version.hpp"
#include "ui_stacktrace_win_dlg.h" #include "ui_stacktrace_win_dlg.h"
class StraceDlg : public QDialog, private Ui::errorDialog { class StraceDlg: public QDialog, private Ui::errorDialog
Q_OBJECT {
Q_OBJECT
public: public:
StraceDlg(QWidget *parent = 0): QDialog(parent) { StraceDlg(QWidget* parent = 0): QDialog(parent)
setupUi(this); {
} setupUi(this);
}
~StraceDlg() {} ~StraceDlg()
{
}
void setStacktraceString(const QString& trace) { void setStacktraceString(const QString& trace)
QString htmlStr; {
QTextStream outStream(&htmlStr); QString htmlStr;
outStream << "<p align=center><b><font size=7 color=red>" << QTextStream outStream(&htmlStr);
"qBittorrent has crashed" << outStream << "<p align=center><b><font size=7 color=red>" <<
"</font></b></p>" << "qBittorrent has crashed" <<
"<font size=4>" << "</font></b></p>" <<
"<p>" << "<font size=4>" <<
"Please report a bug at <a href=\"http://bugs.qbittorrent.org\">" << "<p>" <<
"http://bugs.qbittorrent.org</a>" << "Please report a bug at <a href=\"http://bugs.qbittorrent.org\">" <<
" and provide the following backtrace." << "http://bugs.qbittorrent.org</a>" <<
"</p>" << " and provide the following backtrace." <<
"</font>" << "</p>" <<
"<br/><hr><br/>" << "</font>" <<
"<p align=center><font size=4>qBittorrent version: " << VERSION << "<br/><hr><br/>" <<
"<br/>Libtorrent version: " << LIBTORRENT_VERSION << "<p align=center><font size=4>qBittorrent version: " << VERSION <<
"<br/>Qt version: " << QT_VERSION_STR << "<br/>Libtorrent version: " << LIBTORRENT_VERSION <<
"<br/>Boost version: " << QString::number(BOOST_VERSION / 100000) << '.' << "<br/>Qt version: " << QT_VERSION_STR <<
QString::number((BOOST_VERSION / 100) % 1000) << '.' << "<br/>Boost version: " << QString::number(BOOST_VERSION / 100000) << '.' <<
QString::number(BOOST_VERSION % 100) << "</font></p><br/>" QString::number((BOOST_VERSION / 100) % 1000) << '.' <<
"<pre><code>" << QString::number(BOOST_VERSION % 100) << "</font></p><br/>"
trace << "<pre><code>" <<
"</code></pre>" << trace <<
"<br/><hr><br/><br/>"; "</code></pre>" <<
"<br/><hr><br/><br/>";
errorText->setHtml(htmlStr); errorText->setHtml(htmlStr);
} }
}; };
#endif #endif

View File

@ -4,13 +4,17 @@ strace_win:{
QMAKE_CXXFLAGS_RELEASE += -fno-omit-frame-pointer QMAKE_CXXFLAGS_RELEASE += -fno-omit-frame-pointer
QMAKE_CXXFLAGS_DEBUG += -fno-omit-frame-pointer QMAKE_CXXFLAGS_DEBUG += -fno-omit-frame-pointer
} }
release:{
#QMAKE_CXXFLAGS_RELEASE += -g QMAKE_LFLAGS += -Wl,--export-all-symbols
#QMAKE_LFLAGS_RELEASE -= -Wl,-s
}
LIBS += libdbghelp LIBS += libdbghelp
} }
CONFIG(debug, debug|release) {
# Make sure binary is not relocatable, otherwise debugging will fail
QMAKE_LFLAGS -= -Wl,--dynamicbase
}
RC_FILE = qbittorrent_mingw.rc RC_FILE = qbittorrent_mingw.rc
#You need to link with libtorrent > 0.15.5 (or svn) and you must #You need to link with libtorrent > 0.15.5 (or svn) and you must