1 /***************************************************************************
2 * Copyright (C) 2005-09 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
27 #include <QCoreApplication>
29 #include <QTextStream>
34 #include "base/global.h"
38 void loadHelpStackFrame(IMAGEHLP_STACK_FRAME
&, const STACKFRAME64
&);
39 BOOL CALLBACK
EnumSymbolsCB(PSYMBOL_INFO
, ULONG
, PVOID
);
40 BOOL CALLBACK
EnumModulesCB(LPCSTR
, DWORD64
, PVOID
);
41 const QString
getBacktrace();
42 struct EnumModulesContext
;
43 // Also works for MinGW64
45 void demangle(QString
& str
);
48 QString
getSourcePathAndLineNumber(HANDLE hProcess
, DWORD64 addr
);
49 bool makeRelativePath(const QString
& dir
, QString
& file
);
53 void straceWin::demangle(QString
& str
)
55 char const* inStr
= qPrintable(u
"_" + str
); // Really need that underline or demangling will fail
58 char* demangled_name
= abi::__cxa_demangle(inStr
, 0, &outSz
, &status
);
61 str
= QString::fromLocal8Bit(demangled_name
);
68 void straceWin::loadHelpStackFrame(IMAGEHLP_STACK_FRAME
& ihsf
, const STACKFRAME64
& stackFrame
)
70 ZeroMemory(&ihsf
, sizeof(IMAGEHLP_STACK_FRAME
));
71 ihsf
.InstructionOffset
= stackFrame
.AddrPC
.Offset
;
72 ihsf
.FrameOffset
= stackFrame
.AddrFrame
.Offset
;
75 BOOL CALLBACK
straceWin::EnumSymbolsCB(PSYMBOL_INFO symInfo
, ULONG size
, PVOID user
)
78 auto params
= static_cast<QStringList
*>(user
);
79 if (symInfo
->Flags
& SYMFLAG_PARAMETER
)
80 params
->append(QString::fromUtf8(symInfo
->Name
));
85 struct straceWin::EnumModulesContext
89 EnumModulesContext(HANDLE hProcess
, QTextStream
& stream
): hProcess(hProcess
), stream(stream
) {}
92 BOOL CALLBACK
straceWin::EnumModulesCB(LPCSTR ModuleName
, DWORD64 BaseOfDll
, PVOID UserContext
)
95 IMAGEHLP_MODULE64 mod
;
96 auto context
= static_cast<EnumModulesContext
*>(UserContext
);
97 mod
.SizeOfStruct
= sizeof(IMAGEHLP_MODULE64
);
98 if(SymGetModuleInfo64(context
->hProcess
, BaseOfDll
, &mod
))
100 QString moduleBase
= QString::fromLatin1("0x%1").arg(BaseOfDll
, 16, 16, QLatin1Char('0'));
101 QString line
= QString::fromLatin1("%1 %2 Image: %3")
102 .arg(QString::fromUtf8(mod
.ModuleName
), -25)
103 .arg(moduleBase
, -13)
104 .arg(QString::fromUtf8(mod
.LoadedImageName
));
105 context
->stream
<< line
<< '\n';
107 const auto pdbName
= QString::fromUtf8(mod
.LoadedPdbName
);
108 if(!pdbName
.isEmpty())
110 QString line2
= QString::fromLatin1("%1 %2")
113 context
->stream
<< line2
<< '\n';
121 * Cuts off leading 'dir' path from 'file' path, otherwise leaves it unchanged
122 * returns true if 'dir' is an ancestor of 'file', otherwise - false
124 bool straceWin::makeRelativePath(const QString
&dir
, QString
&file
)
126 QString d
= QDir::toNativeSeparators(QDir(dir
).absolutePath());
127 QString f
= QDir::toNativeSeparators(QFileInfo(file
).absoluteFilePath());
129 // append separator at the end of dir
130 QChar separator
= QDir::separator();
131 if (!d
.isEmpty() && (d
[d
.length() - 1] != separator
))
134 if (f
.startsWith(d
, Qt::CaseInsensitive
))
136 f
.remove(0, d
.length());
145 QString
straceWin::getSourcePathAndLineNumber(HANDLE hProcess
, DWORD64 addr
)
147 IMAGEHLP_LINE64 line
{};
148 line
.SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
149 DWORD dwDisplacement
= 0;
151 if (SymGetLineFromAddr64(hProcess
, addr
, &dwDisplacement
, &line
))
153 auto path
= QString::fromUtf8(line
.FileName
);
155 #if defined STACKTRACE_WIN_PROJECT_PATH || defined STACKTRACE_WIN_MAKEFILE_PATH
157 #define STACKTRACE_WIN_QUOTE(x) #x
158 #define STACKTRACE_WIN_STRING(x) STACKTRACE_WIN_QUOTE(x)
160 //prune leading project directory path or build target directory path
162 bool success
= false;
163 #ifdef STACKTRACE_WIN_PROJECT_PATH
164 const auto projectPath
= QStringLiteral(STACKTRACE_WIN_STRING(STACKTRACE_WIN_PROJECT_PATH
));
165 success
= makeRelativePath(projectPath
, path
);
168 #ifdef STACKTRACE_WIN_MAKEFILE_PATH
171 const auto targetPath
= QStringLiteral(STACKTRACE_WIN_STRING(STACKTRACE_WIN_MAKEFILE_PATH
));
172 makeRelativePath(targetPath
, path
);
176 return QString::fromLatin1("%1 : %2").arg(path
).arg(line
.LineNumber
);
183 #if defined( _M_IX86 ) && defined(Q_CC_MSVC)
184 // Disable global optimization and ignore /GS waning caused by
186 // not needed with mingw cause we can tell mingw which registers we use
187 #pragma optimize("g", off)
188 #pragma warning(push)
189 #pragma warning(disable : 4748)
191 const QString
straceWin::getBacktrace()
195 STACKFRAME64 StackFrame
;
198 ZeroMemory(&Context
, sizeof(CONTEXT
));
199 Context
.ContextFlags
= CONTEXT_CONTROL
;
206 "movl $Label,%%eax;\n\t"
208 : "=r" (Context
.Ebp
),"=r" (Context
.Esp
),"=r" (Context
.Eip
)
215 mov
[Context
.Ebp
], ebp
;
216 mov
[Context
.Esp
], esp
;
218 mov
[Context
.Eip
], eax
;
222 RtlCaptureContext(&Context
);
225 ZeroMemory(&StackFrame
, sizeof(STACKFRAME64
));
227 MachineType
= IMAGE_FILE_MACHINE_I386
;
228 StackFrame
.AddrPC
.Offset
= Context
.Eip
;
229 StackFrame
.AddrPC
.Mode
= AddrModeFlat
;
230 StackFrame
.AddrFrame
.Offset
= Context
.Ebp
;
231 StackFrame
.AddrFrame
.Mode
= AddrModeFlat
;
232 StackFrame
.AddrStack
.Offset
= Context
.Esp
;
233 StackFrame
.AddrStack
.Mode
= AddrModeFlat
;
235 MachineType
= IMAGE_FILE_MACHINE_AMD64
;
236 StackFrame
.AddrPC
.Offset
= Context
.Rip
;
237 StackFrame
.AddrPC
.Mode
= AddrModeFlat
;
238 StackFrame
.AddrFrame
.Offset
= Context
.Rsp
;
239 StackFrame
.AddrFrame
.Mode
= AddrModeFlat
;
240 StackFrame
.AddrStack
.Offset
= Context
.Rsp
;
241 StackFrame
.AddrStack
.Mode
= AddrModeFlat
;
243 MachineType
= IMAGE_FILE_MACHINE_IA64
;
244 StackFrame
.AddrPC
.Offset
= Context
.StIIP
;
245 StackFrame
.AddrPC
.Mode
= AddrModeFlat
;
246 StackFrame
.AddrFrame
.Offset
= Context
.IntSp
;
247 StackFrame
.AddrFrame
.Mode
= AddrModeFlat
;
248 StackFrame
.AddrBStore
.Offset
= Context
.RsBSP
;
249 StackFrame
.AddrBStore
.Mode
= AddrModeFlat
;
250 StackFrame
.AddrStack
.Offset
= Context
.IntSp
;
251 StackFrame
.AddrStack
.Mode
= AddrModeFlat
;
253 #error "Unsupported platform"
257 QTextStream
logStream(&log
);
258 logStream
<< "```\n";
260 HANDLE hProcess
= GetCurrentProcess();
261 HANDLE hThread
= GetCurrentThread();
262 SymInitializeW(hProcess
, QCoreApplication::applicationDirPath().toStdWString().c_str(), TRUE
);
264 DWORD64 dwDisplacement
;
266 ULONG64 buffer
[(sizeof(SYMBOL_INFO
) +
267 MAX_SYM_NAME
* sizeof(TCHAR
) +
268 sizeof(ULONG64
) - 1) / sizeof(ULONG64
)];
269 auto pSymbol
= reinterpret_cast<PSYMBOL_INFO
>(buffer
);
270 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
271 pSymbol
->MaxNameLen
= MAX_SYM_NAME
;
273 IMAGEHLP_MODULE64 mod
;
274 mod
.SizeOfStruct
= sizeof(IMAGEHLP_MODULE64
);
276 IMAGEHLP_STACK_FRAME ihsf
;
277 ZeroMemory(&ihsf
, sizeof(IMAGEHLP_STACK_FRAME
));
281 while(StackWalk64(MachineType
, hProcess
, hThread
, &StackFrame
, &Context
, NULL
, NULL
, NULL
, NULL
))
286 loadHelpStackFrame(ihsf
, StackFrame
);
287 if(StackFrame
.AddrPC
.Offset
!= 0)
290 auto fileName
= u
"???"_qs
;
291 if(SymGetModuleInfo64(hProcess
, ihsf
.InstructionOffset
, &mod
))
293 fileName
= QString::fromUtf8(mod
.ImageName
);
294 int slashPos
= fileName
.lastIndexOf(u
'\\');
296 fileName
= fileName
.mid(slashPos
+ 1);
300 if(SymFromAddr(hProcess
, ihsf
.InstructionOffset
, &dwDisplacement
, pSymbol
))
302 funcName
= QString::fromUtf8(pSymbol
->Name
);
307 // now ihsf.InstructionOffset points to the instruction that follows CALL instruction
308 // decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
309 sourceFile
= getSourcePathAndLineNumber(hProcess
, ihsf
.InstructionOffset
- 1);
313 funcName
= QString::fromLatin1("0x%1").arg(ihsf
.InstructionOffset
, 8, 16, QLatin1Char('0'));
315 SymSetContext(hProcess
, &ihsf
, NULL
);
318 SymEnumSymbols(hProcess
, 0, NULL
, EnumSymbolsCB
, (PVOID
)¶ms
);
321 QString insOffset
= QString::fromLatin1("0x%1").arg(ihsf
.InstructionOffset
, 16, 16, QLatin1Char('0'));
322 auto formatLine
= u
"#%1 %2 %3 %4"_qs
;
324 formatLine
+= u
"(%5)"_qs
;
326 QString debugLine
= formatLine
332 .arg(params
.join(u
", "));
334 if (!sourceFile
.isEmpty())
335 debugLine
+= QString::fromLatin1("[ %1 ]").arg(sourceFile
);
339 logStream
<< debugLine
<< '\n';
344 break; // we're at the end.
348 //logStream << "\n\nList of linked Modules:\n";
349 //EnumModulesContext modulesContext(hProcess, logStream);
350 //SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
351 SymCleanup(hProcess
);
356 #if defined(_M_IX86) && defined(Q_CC_MSVC)
358 #pragma optimize("g", on)