swift
stacktrace.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "misc/stacktrace.h"
5 
6 #include <array>
7 #include <cstdlib>
8 #include <mutex>
9 
10 #include <QByteArray>
11 #include <QLatin1String>
12 #include <QMutexLocker>
13 #include <QStringBuilder>
14 
15 #if defined(Q_CC_MSVC)
16 # include <Windows.h>
17 
18 # pragma warning(push)
19 # pragma warning(disable : 4091)
20 # include <DbgHelp.h>
21 
22 # pragma warning(pop)
23 #elif defined(Q_OS_WIN) && defined(Q_CC_GNU)
24 # include <dbghelp.h>
25 # include <windows.h>
26 #elif defined(Q_CC_GNU)
27 # include <cxxabi.h>
28 # include <execinfo.h>
29 
30 # include <cstring>
31 #endif
32 
33 namespace swift::misc
34 {
35 
36 #if defined(QT_DEBUG)
37  QStringList getStackTrace() { return getStackTraceAlways(); }
38 #else
39  QStringList getStackTrace() { return { "No stack trace with release build" }; }
40 #endif
41 
42 #if defined(Q_OS_WIN32)
43  QStringList getStackTraceAlways()
44  {
45  static QMutex mutex;
46  QMutexLocker lock(&mutex);
47 
48  static std::once_flag flag;
49  std::call_once(flag, [] {
50  SymInitialize(GetCurrentProcess(), nullptr, true);
51  std::atexit([] { SymCleanup(GetCurrentProcess()); });
52  });
53 
54  auto process = GetCurrentProcess();
55  SymRefreshModuleList(process);
56 
57  using stackarray = std::array<void *, 100>;
58  stackarray stack;
59  auto frames = CaptureStackBackTrace(1, static_cast<DWORD>(stack.size()), stack.data(), nullptr);
60 
61  struct
62  {
63  SYMBOL_INFO info;
64  std::array<char, 256> name;
65  } symbol;
66  symbol.info.MaxNameLen = static_cast<ULONG>(symbol.name.size() - 1);
67  symbol.info.SizeOfStruct = sizeof(symbol.info);
68 
69  QStringList result;
70  for (stackarray::size_type i = 0; i < frames; ++i)
71  {
72  DWORD displacement = 0;
73  IMAGEHLP_LINE64 line;
74  line.SizeOfStruct = sizeof(line);
75  SymFromAddr(process, reinterpret_cast<quintptr>(stack[i]), nullptr, &symbol.info);
76  SymGetLineFromAddr64(process, reinterpret_cast<quintptr>(stack[i]), &displacement, &line);
77 
78  result.push_back(QLatin1String(symbol.info.Name) % u" line " % QString::number(line.LineNumber));
79  }
80  return result;
81  }
82 #elif defined(Q_CC_GNU)
83  QStringList getStackTraceAlways()
84  {
85  std::array<void *, 100> stack;
86  auto frames = backtrace(stack.data(), stack.size());
87  auto *symbols = backtrace_symbols(stack.data(), frames);
88 
89  QStringList result;
90  char *demangled = nullptr;
91  size_t size = 0;
92  for (int i = 0; i < frames; ++i)
93  {
94  // Using C string functions to avoid unnecessary dynamic memory allocation
95  auto *basename = std::strrchr(symbols[i], '/');
96  if (!basename) { continue; }
97  basename++;
98  auto *symbol = std::strchr(basename, '(');
99  if (!symbol) { continue; }
100  auto *basenameEnd = symbol++;
101  auto *end = std::strrchr(symbol, ')');
102  if (!end) { continue; }
103  auto *offset = std::strrchr(symbol, '+');
104  auto *symbolEnd = offset ? offset++ : end;
105 
106  auto *temp = demangled; // avoid leaking memory if __cxa_demangle returns nullptr
107 
108  demangled =
109  abi::__cxa_demangle(QByteArray(symbol, symbolEnd - symbol).constData(), demangled, &size, nullptr);
110 
111  if (demangled)
112  {
113  result.push_back(QLatin1String(demangled) % u' ' % u'(' %
114  QLatin1String(basename, basenameEnd - basename) % u' ' %
115  QLatin1String(symbol, end - symbol) % u')');
116  }
117  else
118  {
119  result.push_back(u'(' % QLatin1String(basename, basenameEnd - basename) % u' ' %
120  QLatin1String(symbol, end - symbol) % u')');
121  demangled = temp;
122  }
123  }
124  free(symbols);
125  free(demangled);
126  return result;
127  }
128 #else
129  // cppcheck-suppress unusedFunction
130  QStringList getStackTraceAlways() { return { "No stack trace on this platform" }; }
131 #endif
132 
133 } // namespace swift::misc
Free functions in swift::misc.
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:338
QStringList getStackTraceAlways()
Returns a stack trace of the current thread of execution as a list of function names.
Definition: stacktrace.cpp:130
QStringList getStackTrace()
Returns a stack trace of the current thread of execution as a list of function names.
Definition: stacktrace.cpp:39
unsigned long DWORD
Fake Windows DWORD.