/*************************************************************************** * 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, 16, 16, QLatin1Char('0')); QString line = QString("%1 %2 Image: %3") .arg(mod.ModuleName, -25) .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); logStream << "```\n"; 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, 16, 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); logStream << "```"; return log; } #if defined(_M_IX86) && defined(Q_CC_MSVC) #pragma warning(pop) #pragma optimize("g", on) #endif