Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
BackTrace.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cstdlib> // to force __GLIBCXX__ define reference
7#include <sstream>
8
9#if qStroika_Foundation_Common_Platform_Linux
10#include <execinfo.h>
11#include <unistd.h>
12#if defined(__GNUC__) && defined(__GLIBCXX__)
13#include <cxxabi.h>
14#endif
15#elif qStroika_Foundation_Common_Platform_MacOS
16#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED 1
17#elif qStroika_Foundation_Common_Platform_Windows
18#include <Windows.h>
19// BOOST_STACKTRACE_USE_WINDBG seems to be default on windows, and very slow
20#define BOOST_STACKTRACE_USE_WINDBG_CACHED 1
21#endif
22
23#if __cpp_lib_stacktrace >= 202011 && !qCompilerAndStdLib_StdBacktraceCompile_Buggy
24#include <stacktrace>
25#endif
26
27#if qStroika_HasComponent_boost
28#include <boost/stacktrace.hpp>
29#endif
30
31#include "Stroika/Foundation/Characters/LineEndings.h"
32#include "Stroika/Foundation/Characters/SDKString.h"
35
36#include "BackTrace.h"
37
38using namespace Stroika::Foundation;
40using namespace Stroika::Foundation::Debug;
41
42/*
43 ********************************************************************************
44 ************************ Debug::BackTrace::Capture *****************************
45 ********************************************************************************
46 */
47wstring Debug::BackTrace::Capture ([[maybe_unused]] const BackTrace::Options& options)
48{
50 [[maybe_unused]] unsigned int useSkipFrames = options.fSkipFrames.value_or (BackTrace::Options::sDefault_SkipFrames);
51
52 useSkipFrames += 1; // always skip this frame, because anyone calling BackTrace() doens't care to see its implementation in the trace
53
54 [[maybe_unused]] unsigned usingMaxFrames = options.fMaxFrames.value_or (BackTrace::Options::sDefault_MaxFrames);
55
56#if __cpp_lib_stacktrace >= 202011 && !qCompilerAndStdLib_stacktraceLinkError_Buggy && !qCompilerAndStdLib_StdBacktraceCompile_Buggy
57 auto st = std::stacktrace::current ();
58 stringstream o;
59 bool firstEntry = true;
60 for (const stacktrace_entry& entry : st) {
61 string eText = entry.description ();
62 bool useFileName = eText.empty ();
63 if (useFileName) {
64 // if using file, always write line#
65 eText = entry.source_file ();
66 }
67 else if (useFileName or options.fIncludeSourceLines.value_or (BackTrace::Options::sDefault_IncludeSourceLines)) {
68 eText += "#" + to_string (entry.source_line ());
69 }
70 if (firstEntry) {
71 firstEntry = false;
72 }
73 else {
74 o << "; ";
75 }
76 o << eText;
77 }
78 return Characters::NarrowSDK2Wide (o.str (), eIgnoreErrors);
79#elif qStroika_HasComponent_boost
80 using namespace boost;
81
82 auto bt = stacktrace::stacktrace ();
83
84 //
85 // Simple default usage with boost
86 // auto x = String::FromNarrowSDKString (stacktrace::to_string (bt)).As<wstring> ();
87 //
88
89 wstringstream result; // avoid use of StringBuild to avoid dependencies on the rest of stroika
90 streamsize w = result.width ();
91 size_t frames = bt.size ();
92
93 useSkipFrames += 2; // boost (as checking on windows as of 2020-03-01) appears to leave in two layers of its own
94
95 if (useSkipFrames != 0 and frames != 0) {
96 result << L"..." << Characters::kEOL<wchar_t>;
97 }
98
99 bool includeSrcLines = options.fIncludeSourceLines.value_or (BackTrace::Options::sDefault_IncludeSourceLines);
100 for (size_t i = 0; i < frames; ++i) {
101 if (i < useSkipFrames) {
102 continue;
103 }
104 result.width (2);
105 result << i;
106 result.width (w);
107 result << L"# ";
108 if (includeSrcLines) {
109 result << String::FromNarrowSDKString (boost::stacktrace::to_string (bt[i])).As<wstring> ();
110 }
111 else {
112 result << String::FromNarrowSDKString (bt[i].name ()).As<wstring> ();
113 }
114 result << L";" << Characters::kEOL<wchar_t>;
115 if (i - useSkipFrames >= usingMaxFrames) {
116 break;
117 }
118 }
119 return result.str ();
120#elif qStroika_Foundation_Common_Platform_Linux
121 /*
122 * @see http://man7.org/linux/man-pages/man3/backtrace.3.html
123 */
124 constexpr size_t kMaxStackSize_ = 100; // could look at return size and re-run if equals exactly...
125 // @todo combine maxFrames with trial and error on backtrace() calls
126 void* stackTraceBuf[kMaxStackSize_]{};
127 int nptrs = ::backtrace (stackTraceBuf, std::size (stackTraceBuf));
128 char** syms = ::backtrace_symbols (stackTraceBuf, nptrs);
129 if (syms == NULL) {
130 //DbgTrace ("%d errno", errno); // perror("backtrace_symbols");
131 return wstring{};
132 }
133 auto narrow2Wide = [] (const char* s) -> wstring {
134 wstring symStr;
135 for (const char* p = s; *p != '\0'; ++p) {
136 symStr += *p;
137 }
138 return symStr;
139 };
140 [[maybe_unused]] auto&& cleanup = Execution::Finally ([syms] () noexcept {
141 if (syms != nullptr)
142 ::free (syms);
143 });
144 wstring out;
145 if (useSkipFrames != 0 and nptrs != 0) {
146 out += wstring{L"..."} + Characters::kEOL<wchar_t>;
147 }
148 for (int j = 0; j < nptrs; j++) {
149 if (j < useSkipFrames) {
150 continue;
151 }
152 wstring symStr = narrow2Wide (syms[j]);
153#if defined(__GNUC__) && defined(__GLIBCXX__)
154 //
155 // Example output from backtrace_symbols:
156 // /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Builds/Debug/Samples_SimpleService(_ZN7Stroika10Foundation9Execution8Private_8GetBT_wsEv+0x21) [0x7fc677]
157 //
158 // abi::__cxa_demangle requires just the _ZN7Stroika10Foundation9Execution8Private_8GetBT_wsEv part, so extract it.
159 //
160 const char* beginOfName = ::strchr (syms[j], '(');
161 if (beginOfName != nullptr) {
162 beginOfName++;
163 }
164 const char* endOfName = (beginOfName == nullptr) ? nullptr : ::strchr (beginOfName, '+');
165 //if (endOfName == nullptr and beginOfName != nullptr) {
166 // endOfName = ::strchr (beginOfName, ')'); // maybe no + sometimes?
167 //}
168 if (beginOfName != nullptr and endOfName != nullptr) {
169 int status = -1;
170 string tmp{beginOfName, endOfName};
171 char* realname = abi::__cxa_demangle (tmp.c_str (), 0, 0, &status);
172 if (status == 0) {
173 symStr = narrow2Wide ((string{static_cast<const char*> (syms[j]), beginOfName} + realname + endOfName).c_str ());
174 }
175 if (realname != nullptr) {
176 ::free (realname);
177 }
178 }
179#endif
180 out += symStr + L";" + Characters::kEOL<wchar_t>;
181 if (j - useSkipFrames >= usingMaxFrames) {
182 break;
183 }
184 }
185 return out;
186#elif qStroika_Foundation_Common_Platform_Windows
187 // No real need todo this because boost does so well, but could be done pretty easily - see
188 // http://www.debuginfo.com/examples/src/SymFromAddr.cpp -- LGP 2020-03-01
189 return wstring{};
190#else
191 return wstring{};
192#endif
193}