mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-28 23:44:32 +00:00
Merge pull request #337 from Gelmir/stacktrace_win
Support stacktrace on Windows
This commit is contained in:
commit
b768005f44
26
src/main.cpp
26
src/main.cpp
@ -65,6 +65,12 @@ Q_IMPORT_PLUGIN(qico)
|
||||
#include "stacktrace.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(STACKTRACE_WIN)
|
||||
#include <signal.h>
|
||||
#include "stacktrace_win.h"
|
||||
#include "stacktrace_win_dlg.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "misc.h"
|
||||
#include "preferences.h"
|
||||
@ -127,7 +133,7 @@ public:
|
||||
|
||||
#include "main.moc"
|
||||
|
||||
#if defined(Q_WS_X11) || defined(Q_WS_MAC)
|
||||
#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_OS_WIN)
|
||||
void sigintHandler(int) {
|
||||
signal(SIGINT, 0);
|
||||
qDebug("Catching SIGINT, exiting cleanly");
|
||||
@ -142,19 +148,35 @@ void sigtermHandler(int) {
|
||||
void sigsegvHandler(int) {
|
||||
signal(SIGABRT, 0);
|
||||
signal(SIGSEGV, 0);
|
||||
#ifndef Q_OS_WIN
|
||||
std::cerr << "\n\n*************************************************************\n";
|
||||
std::cerr << "Catching SIGSEGV, please report a bug at http://bug.qbittorrent.org\nand provide the following backtrace:\n";
|
||||
std::cerr << "qBittorrent version: " << VERSION << std::endl;
|
||||
print_stacktrace();
|
||||
#else
|
||||
#ifdef STACKTRACE_WIN
|
||||
StraceDlg dlg;
|
||||
dlg.setStacktraceString(straceWin::getBacktrace());
|
||||
dlg.exec();
|
||||
#endif
|
||||
#endif
|
||||
raise(SIGSEGV);
|
||||
}
|
||||
void sigabrtHandler(int) {
|
||||
signal(SIGABRT, 0);
|
||||
signal(SIGSEGV, 0);
|
||||
#ifndef Q_OS_WIN
|
||||
std::cerr << "\n\n*************************************************************\n";
|
||||
std::cerr << "Catching SIGABRT, please report a bug at http://bug.qbittorrent.org\nand provide the following backtrace:\n";
|
||||
std::cerr << "qBittorrent version: " << VERSION << std::endl;
|
||||
print_stacktrace();
|
||||
#else
|
||||
#ifdef STACKTRACE_WIN
|
||||
StraceDlg dlg;
|
||||
dlg.setStacktraceString(straceWin::getBacktrace());
|
||||
dlg.exec();
|
||||
#endif
|
||||
#endif
|
||||
raise(SIGABRT);
|
||||
}
|
||||
#endif
|
||||
@ -311,7 +333,7 @@ int main(int argc, char *argv[]) {
|
||||
#ifndef DISABLE_GUI
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
#endif
|
||||
#if defined(Q_WS_X11) || defined(Q_WS_MAC)
|
||||
#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_OS_WIN)
|
||||
signal(SIGABRT, sigabrtHandler);
|
||||
signal(SIGTERM, sigtermHandler);
|
||||
signal(SIGINT, sigintHandler);
|
||||
|
35
src/src.pro
35
src/src.pro
@ -239,3 +239,38 @@ TRANSLATIONS = $$LANG_PATH/qbittorrent_fr.ts \
|
||||
$$LANG_PATH/qbittorrent_be.ts \
|
||||
$$LANG_PATH/qbittorrent_eu.ts \
|
||||
$$LANG_PATH/qbittorrent_he.ts
|
||||
|
||||
# Windows Stacktrace support
|
||||
strace_win:win32:{
|
||||
contains(QMAKE_HOST.arch, x86):{
|
||||
# i686 arch requires frame pointer preservation
|
||||
win32-g++:{
|
||||
QMAKE_CXXFLAGS_RELEASE += -fno-omit-frame-pointer
|
||||
QMAKE_CXXFLAGS_DEBUG += -fno-omit-frame-pointer
|
||||
}
|
||||
win32-msvc*:{
|
||||
QMAKE_CXXFLAGS_RELEASE += -Oy-
|
||||
QMAKE_CXXFLAGS_DEBUG += -Oy-
|
||||
}
|
||||
}
|
||||
|
||||
# Generate debug info in release builds
|
||||
release:{
|
||||
#win32-g++:{
|
||||
# QMAKE_CXXFLAGS_RELEASE += -g
|
||||
# QMAKE_LFLAGS_RELEASE -= -Wl,-s
|
||||
#}
|
||||
win32-msvc*:{
|
||||
QMAKE_CXXFLAGS_RELEASE += -Zi
|
||||
QMAKE_LFLAGS += "/DEBUG"
|
||||
}
|
||||
}
|
||||
|
||||
DEFINES += STACKTRACE_WIN
|
||||
win32-msvc*:LIBS += dbghelp.lib
|
||||
win32-g++:LIBS += libdbghelp
|
||||
|
||||
FORMS += stacktrace_win_dlg.ui
|
||||
HEADERS += stacktrace_win.h \
|
||||
stacktrace_win_dlg.h
|
||||
}
|
||||
|
221
src/stacktrace_win.h
Normal file
221
src/stacktrace_win.h
Normal file
@ -0,0 +1,221 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2005-09 by the Quassel Project *
|
||||
* devel@quassel-irc.org *
|
||||
* *
|
||||
* 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 *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) version 3. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QTextStream>
|
||||
|
||||
namespace straceWin{
|
||||
void loadHelpStackFrame(IMAGEHLP_STACK_FRAME&, const STACKFRAME64&);
|
||||
BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO, ULONG, PVOID);
|
||||
BOOL CALLBACK EnumModulesCB(LPCSTR, DWORD64, PVOID);
|
||||
const QString getBacktrace();
|
||||
struct EnumModulesContext;
|
||||
}
|
||||
|
||||
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) {
|
||||
QStringList *params = (QStringList *)user;
|
||||
if(symInfo->Flags & SYMFLAG_PARAMETER) {
|
||||
params->append(symInfo->Name);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
struct straceWin::EnumModulesContext {
|
||||
HANDLE hProcess;
|
||||
QTextStream &stream;
|
||||
EnumModulesContext(HANDLE hProcess, QTextStream &stream) : hProcess(hProcess), stream(stream) {}
|
||||
};
|
||||
|
||||
BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext) {
|
||||
IMAGEHLP_MODULE64 mod;
|
||||
EnumModulesContext *context = (EnumModulesContext *)UserContext;
|
||||
mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
||||
if(SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) {
|
||||
QString moduleBase = QString("0x%1").arg(BaseOfDll, 8, 16, QLatin1Char('0'));
|
||||
QString line = QString("%1 %2 Image: %3")
|
||||
.arg(mod.ModuleName, -14)
|
||||
.arg(moduleBase, -13)
|
||||
.arg(mod.LoadedImageName);
|
||||
context->stream << line << '\n';
|
||||
|
||||
QString pdbName(mod.LoadedPdbName);
|
||||
if(!pdbName.isEmpty()) {
|
||||
QString line2 = QString("%1 %2")
|
||||
.arg("", 35)
|
||||
.arg(pdbName);
|
||||
context->stream << line2 << '\n';
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined( _M_IX86 ) && defined(Q_CC_MSVC)
|
||||
// Disable global optimization and ignore /GS waning caused by
|
||||
// inline assembly.
|
||||
// not needed with mingw cause we can tell mingw which registers we use
|
||||
#pragma optimize("g", off)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4748)
|
||||
#endif
|
||||
const QString straceWin::getBacktrace() {
|
||||
DWORD MachineType;
|
||||
CONTEXT Context;
|
||||
STACKFRAME64 StackFrame;
|
||||
|
||||
#ifdef _M_IX86
|
||||
ZeroMemory(&Context, sizeof(CONTEXT));
|
||||
Context.ContextFlags = CONTEXT_CONTROL;
|
||||
|
||||
|
||||
#ifdef __MINGW32__
|
||||
asm("Label:\n\t"
|
||||
"movl %%ebp,%0;\n\t"
|
||||
"movl %%esp,%1;\n\t"
|
||||
"movl $Label,%%eax;\n\t"
|
||||
"movl %%eax,%2;\n\t"
|
||||
:"=r"(Context.Ebp),"=r"(Context.Esp),"=r"(Context.Eip)
|
||||
://no input
|
||||
:"eax");
|
||||
#else
|
||||
_asm {
|
||||
Label:
|
||||
mov [Context.Ebp], ebp;
|
||||
mov [Context.Esp], esp;
|
||||
mov eax, [Label];
|
||||
mov [Context.Eip], eax;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
RtlCaptureContext(&Context);
|
||||
#endif
|
||||
|
||||
ZeroMemory(&StackFrame, sizeof(STACKFRAME64));
|
||||
#ifdef _M_IX86
|
||||
MachineType = IMAGE_FILE_MACHINE_I386;
|
||||
StackFrame.AddrPC.Offset = Context.Eip;
|
||||
StackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
StackFrame.AddrFrame.Offset = Context.Ebp;
|
||||
StackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
StackFrame.AddrStack.Offset = Context.Esp;
|
||||
StackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
MachineType = IMAGE_FILE_MACHINE_AMD64;
|
||||
StackFrame.AddrPC.Offset = Context.Rip;
|
||||
StackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
StackFrame.AddrFrame.Offset = Context.Rsp;
|
||||
StackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
StackFrame.AddrStack.Offset = Context.Rsp;
|
||||
StackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
MachineType = IMAGE_FILE_MACHINE_IA64;
|
||||
StackFrame.AddrPC.Offset = Context.StIIP;
|
||||
StackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
StackFrame.AddrFrame.Offset = Context.IntSp;
|
||||
StackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
StackFrame.AddrBStore.Offset= Context.RsBSP;
|
||||
StackFrame.AddrBStore.Mode = AddrModeFlat;
|
||||
StackFrame.AddrStack.Offset = Context.IntSp;
|
||||
StackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
QString log;
|
||||
QTextStream logStream(&log);
|
||||
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
SymInitialize(hProcess, NULL, TRUE);
|
||||
|
||||
DWORD64 dwDisplacement;
|
||||
|
||||
ULONG64 buffer[(sizeof(SYMBOL_INFO) +
|
||||
MAX_SYM_NAME*sizeof(TCHAR) +
|
||||
sizeof(ULONG64) - 1) / sizeof(ULONG64)];
|
||||
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
|
||||
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
pSymbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
IMAGEHLP_MODULE64 mod;
|
||||
mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
||||
|
||||
IMAGEHLP_STACK_FRAME ihsf;
|
||||
ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));
|
||||
|
||||
int i = 0;
|
||||
while(StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) {
|
||||
if(i == 128)
|
||||
break;
|
||||
|
||||
loadHelpStackFrame(ihsf, StackFrame);
|
||||
if(StackFrame.AddrPC.Offset != 0) { // Valid frame.
|
||||
|
||||
QString fileName("???");
|
||||
if(SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) {
|
||||
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)¶ms);
|
||||
|
||||
QString insOffset = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
|
||||
QString debugLine = QString("#%1 %2 %3 %4(%5)")
|
||||
.arg(i, 3, 10)
|
||||
.arg(fileName, -20)
|
||||
.arg(insOffset, -11)
|
||||
.arg(funcName)
|
||||
.arg(params.join(", "));
|
||||
logStream << debugLine << '\n';
|
||||
i++;
|
||||
} else {
|
||||
break; // we're at the end.
|
||||
}
|
||||
}
|
||||
|
||||
logStream << "\n\nList of linked Modules:\n";
|
||||
EnumModulesContext modulesContext(hProcess, logStream);
|
||||
SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
|
||||
return log;
|
||||
}
|
||||
#if defined(_M_IX86) && defined(Q_CC_MSVC)
|
||||
#pragma warning(pop)
|
||||
#pragma optimize("g", on)
|
||||
#endif
|
49
src/stacktrace_win_dlg.h
Normal file
49
src/stacktrace_win_dlg.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef STACKTRACE_WIN_DLG_H
|
||||
#define STACKTRACE_WIN_DLG_H
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QClipboard>
|
||||
#include "boost/version.hpp"
|
||||
#include "libtorrent/version.hpp"
|
||||
#include "ui_stacktrace_win_dlg.h"
|
||||
|
||||
class StraceDlg : public QDialog, private Ui::errorDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StraceDlg(QWidget *parent = 0): QDialog(parent) {
|
||||
setupUi(this);
|
||||
}
|
||||
|
||||
~StraceDlg() {}
|
||||
|
||||
void setStacktraceString(const QString& trace) {
|
||||
QString htmlStr;
|
||||
QTextStream outStream(&htmlStr);
|
||||
outStream << "<p align=center><b><font size=7 color=red>" <<
|
||||
"qBittorrent has crashed" <<
|
||||
"</font></b></p>" <<
|
||||
"<font size=4>" <<
|
||||
"<p>" <<
|
||||
"Please report a bug at <a href=\"http://bugs.qbittorrent.org\">" <<
|
||||
"http://bugs.qbittorrent.org</a>" <<
|
||||
" and provide the following backtrace." <<
|
||||
"</p>" <<
|
||||
"</font>" <<
|
||||
"<br/><hr><br/>" <<
|
||||
"<p align=center><font size=4>qBittorrent version: " << VERSION <<
|
||||
"<br/>Libtorrent version: " << LIBTORRENT_VERSION <<
|
||||
"<br/>Qt version: " << QT_VERSION_STR <<
|
||||
"<br/>Boost version: " << QString::number(BOOST_VERSION / 100000) << '.' <<
|
||||
QString::number((BOOST_VERSION / 100) % 1000) << '.' <<
|
||||
QString::number(BOOST_VERSION % 100) << "</font></p><br/>"
|
||||
"<pre><code>" <<
|
||||
trace <<
|
||||
"</code></pre>" <<
|
||||
"<br/><hr><br/><br/>";
|
||||
|
||||
errorText->setHtml(htmlStr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
35
src/stacktrace_win_dlg.ui
Normal file
35
src/stacktrace_win_dlg.ui
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>errorDialog</class>
|
||||
<widget class="QDialog" name="errorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>640</width>
|
||||
<height>480</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Crash info</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="errorText">
|
||||
<property name="html">
|
||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user