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