diff --git a/src/main.cpp b/src/main.cpp index 79950e74c..bcc4c198a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,6 +65,12 @@ Q_IMPORT_PLUGIN(qico) #include "stacktrace.h" #endif +#if defined(Q_OS_WIN) && defined(STACKTRACE_WIN) +#include +#include "stacktrace_win.h" +#include "stacktrace_win_dlg.h" +#endif + #include #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); diff --git a/src/src.pro b/src/src.pro index ae889da72..e23f3cc22 100644 --- a/src/src.pro +++ b/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 +} diff --git a/src/stacktrace_win.h b/src/stacktrace_win.h new file mode 100644 index 000000000..c8607766f --- /dev/null +++ b/src/stacktrace_win.h @@ -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 +#include +#include + +#include + +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 diff --git a/src/stacktrace_win_dlg.h b/src/stacktrace_win_dlg.h new file mode 100644 index 000000000..f54060950 --- /dev/null +++ b/src/stacktrace_win_dlg.h @@ -0,0 +1,49 @@ +#ifndef STACKTRACE_WIN_DLG_H +#define STACKTRACE_WIN_DLG_H + +#include +#include +#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 << "

" << + "qBittorrent has crashed" << + "

" << + "" << + "

" << + "Please report a bug at " << + "http://bugs.qbittorrent.org" << + " and provide the following backtrace." << + "

" << + "
" << + "


" << + "

qBittorrent version: " << VERSION << + "
Libtorrent version: " << LIBTORRENT_VERSION << + "
Qt version: " << QT_VERSION_STR << + "
Boost version: " << QString::number(BOOST_VERSION / 100000) << '.' << + QString::number((BOOST_VERSION / 100) % 1000) << '.' << + QString::number(BOOST_VERSION % 100) << "


" + "
" <<
+                 trace <<
+                 "
" << + "



"; + + errorText->setHtml(htmlStr); + } +}; + +#endif diff --git a/src/stacktrace_win_dlg.ui b/src/stacktrace_win_dlg.ui new file mode 100644 index 000000000..41f1fc7f9 --- /dev/null +++ b/src/stacktrace_win_dlg.ui @@ -0,0 +1,35 @@ + + + errorDialog + + + + 0 + 0 + 640 + 480 + + + + Crash info + + + + + + <!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> + + + true + + + + + + + +