1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 #include <cstdlib>  // for free
7 #include <sstream>  // for std::ostringstream
8 
9 #ifdef WIN32
10   #define NOMINMAX
11   #include <Windows.h>
12   #include <WinBase.h>
13   #include <DbgHelp.h>
14 #else
15   #include <execinfo.h>  // for backtrace()
16   #include <ciso646>
17   #if !defined(_LIBCPP_VERSION)
18     #include <cxxabi.h>  // for abi::__cxa_demangle
19   #endif
20 #endif
21 
22 constexpr int MAX_FRAMES = 25;
23 
24 namespace axom
25 {
26 namespace slic
27 {
28 namespace internal
29 {
30 #ifdef WIN32
31 
stacktrace()32 std::string stacktrace()
33 {
34   void* stack[MAX_FRAMES];
35   std::ostringstream oss;
36 
37   unsigned short frames;
38   SYMBOL_INFO* symbol;
39   HANDLE process;
40 
41   process = GetCurrentProcess();
42 
43   SymInitialize(process, NULL, TRUE);
44 
45   frames = CaptureStackBackTrace(0, MAX_FRAMES, stack, NULL);
46   symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
47   symbol->MaxNameLen = 255;
48   symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
49 
50   oss << "\n** StackTrace of " << frames << " frames **\n";
51   for(int i = 0; i < frames; i++)
52   {
53     char outString[512];
54     SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
55 
56     sprintf_s(outString,
57               "%i: %s - 0x%0X",
58               frames - i - 1,
59               symbol->Name,
60               symbol->Address);
61     oss << outString << std::endl;
62   }
63 
64   free(symbol);
65   oss << "=====\n\n";
66 
67   return (oss.str());
68 }
69 
70 #else /* #ifdef WIN32 */
71 
72 //------------------------------------------------------------------------------
73 std::string demangle(char* backtraceString, int frame)
74 {
75   char* mangledName = nullptr;
76   char* functionOffset = nullptr;
77   char* returnOffset = nullptr;
78 
79   #ifdef __APPLE__
80   /* On apple machines the mangled function name always starts at the 58th
81    * character */
82   constexpr int APPLE_OFFSET = 58;
83   mangledName = backtraceString + APPLE_OFFSET;
84   for(char* p = backtraceString; *p; ++p)
85   {
86     if(*p == '+')
87     {
88       functionOffset = p;
89     }
90     returnOffset = p;
91   }
92   #else
93   for(char* p = backtraceString; *p; ++p)
94   {
95     if(*p == '(')
96     {
97       mangledName = p;
98     }
99     else if(*p == '+')
100     {
101       functionOffset = p;
102     }
103     else if(*p == ')')
104     {
105       returnOffset = p;
106       break;
107     }
108   }
109   #endif
110 
111   std::ostringstream oss;
112 
113   // if the line could be processed, attempt to demangle the symbol
114   if(mangledName && functionOffset && returnOffset && mangledName < functionOffset)
115   {
116     *mangledName = 0;
117     mangledName++;
118   #ifdef __APPLE__
119     *(functionOffset - 1) = 0;
120   #endif
121     *functionOffset = 0;
122     ++functionOffset;
123     *returnOffset = 0;
124     ++returnOffset;
125 
126     int status = false;
127   #if !defined(_LIBCPP_VERSION)
128     char* realName = abi::__cxa_demangle(mangledName, nullptr, nullptr, &status);
129   #else
130     char* realName = mangledName;
131   #endif
132 
133     // if demangling is successful, output the demangled function name
134     if(status == 0)
135     {
136       oss << "Frame " << frame << ": " << realName << std::endl;
137     }
138     // otherwise, output the mangled function name
139     else
140     {
141       oss << "Frame " << frame << ": " << mangledName << std::endl;
142     }
143 
144   #if !defined(_LIBCPP_VERSION)
145     free(realName);
146   #endif
147   }
148   // otherwise, print the whole line
149   else
150   {
151     oss << "Frame " << frame << ": " << backtraceString << std::endl;
152   }
153 
154   return (oss.str());
155 }
156 
157 std::string stacktrace()
158 {
159   void* array[MAX_FRAMES];
160 
161   const int size = backtrace(array, MAX_FRAMES);
162   char** strings = backtrace_symbols(array, size);
163 
164   // skip first stack frame (points here)
165   std::ostringstream oss;
166   oss << "\n** StackTrace of " << size - 1 << " frames **\n";
167   for(int i = 1; i < size && strings != nullptr; ++i)
168   {
169     oss << internal::demangle(strings[i], i);
170   }
171 
172   oss << "=====\n\n";
173 
174   free(strings);
175 
176   return (oss.str());
177 }
178 
179 #endif /* #ifdef WIN32 */
180 
181 } /* namespace internal */
182 
183 } /* namespace slic */
184 
185 } /* namespace axom */
186