libexcept 1.1.19
Stack trace along C++ exceptions
stack_trace.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2025 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/
4// contact@m2osw.com
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 2 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
20// self
21//
22#include "libexcept/exception.h"
23
24#include "libexcept/demangle.h"
25
26
27// C++
28//
29#include <iostream>
30#include <memory>
31#include <vector>
32
33
34// C
35//
36#include <execinfo.h>
37#include <link.h>
38#include <unistd.h>
39
40
59namespace libexcept
60{
61
62
63
100stack_trace_t collect_stack_trace(int stack_trace_depth)
101{
102 stack_trace_t stack_trace;
103
104 if(stack_trace_depth > 0)
105 {
106 std::vector<void *> array;
107 array.resize(std::max(stack_trace_depth, 1'000));
108 int const size(backtrace(&array[0], stack_trace_depth));
109
110 // save a copy of the system array in our class
111 //
112 std::unique_ptr<char *, decltype(&::free)> stack_string_list(backtrace_symbols(&array[0], size), &::free);
113 if(stack_string_list != nullptr)
114 {
115 for(int idx(0); idx < size; ++idx)
116 {
117 char const * stack_string(stack_string_list.get()[idx]);
118 stack_trace.push_back(stack_string);
119 }
120 }
121 }
122
123 return stack_trace;
124}
125
126
153{
154 stack_trace_t stack_trace;
155
156 if(stack_trace_depth > 0)
157 {
158 std::vector<void *> array;
159 array.resize(stack_trace_depth);
160 int const size(backtrace(&array[0], stack_trace_depth));
161
162 // save a copy of the system array in our class
163 //
164 std::unique_ptr<char *, decltype(&::free)> stack_string_list(backtrace_symbols(&array[0], size), &::free);
165 for(int idx(0); idx < size; ++idx)
166 {
167 char const * raw_stack_string(stack_string_list.get()[idx]);
168
169 // the raw stack string is expected to be composed of:
170 // <filename>(<function-name>+<offset>) [<addr>]
171 //
172 // we extract all those elements and use addr2line and c++filt
173 // to convert that data to a usable function name and line number
174 //
175 std::string filename;
176 std::string raw_function_name;
177 std::string addr;
178 std::string result;
179
180 // go to end of filename
181 //
182 char const * s(raw_stack_string);
183 char const * start(s);
184 while(*s != '('
185 && *s != '\0')
186 {
187 ++s;
188 }
189 if(*s == '(')
190 {
191 filename = std::string(start, s - start);
192 ++s;
193
194 start = s;
195 while(*s != '+'
196 && *s != ')'
197 && *s != '\0')
198 {
199 ++s;
200 }
201
202 if(*s == '+')
203 {
204 raw_function_name = std::string(start, s - start);
205 ++s;
206
207 // skip the offset
208 //
209 while(*s != ')'
210 && *s != '\0')
211 {
212 ++s;
213 }
214 }
215 //else if(*s == ')') {} -- no function name
216
217 if(*s == ')'
218 && s[1] == ' '
219 && s[2] == '['
220 && s[3] == '0'
221 && s[4] == 'x')
222 {
223 s += 5;
224
225 start = s;
226 while(*s != ']'
227 && *s != '\0')
228 {
229 ++s;
230 }
231
232 if(*s == ']')
233 {
234 addr = std::string(start, s - start);
235
236 result.clear();
237
238 // here we have our info, use it to get sane data
239 //
240 // TODO: look into whether we could rewrite the
241 // eu-addr2line tool in C++ to avoid having to
242 // run it (because that would be faster)
243 //
244 // I first used the "... -e <filename> ..." option
245 // but with the --pid=..., it ought to be faster.
246 //
247 {
248 std::string addr2line("eu-addr2line --pid="
249 + std::to_string(getpid())
250 + " "
251 + addr);
252 std::unique_ptr<FILE, int(*)(FILE *)> p(popen(addr2line.c_str(), "r"), &::pclose);
253 std::string line;
254 for(;;)
255 {
256 int const c(fgetc(p.get()));
257 if(c == EOF)
258 {
259 break;
260 }
261 if(c != '\n')
262 {
263 line += c;
264 }
265 }
266 if(line == "??:0")
267 {
268 // this means addr2line failed to find the
269 // debug info in your executable/.so
270 // we fallback to the default which may help
271 // if you have a version with debug info
272 //
273 result += filename
274 + "["
275 + addr
276 + "]";
277 }
278 else
279 {
280 result = line;
281 }
282 }
283
284 if(!raw_function_name.empty())
285 {
286 result += " in ";
287 result += demangle_cpp_name(raw_function_name.c_str());
288 }
289 else
290 {
291 result += " <no function name>";
292 }
293 }
294 }
295 }
296
297 if(result.empty())
298 {
299 // use the raw line as a fallback if we could not parse it
300 // correctly or a conversion somehow fails...
301 //
302 stack_trace.push_back(std::string(raw_stack_string)); // LCOV_EXCL_LINE
303 }
304 else
305 {
306 stack_trace.push_back(result);
307 }
308 }
309 }
310
311 return stack_trace;
312}
313
314
315
316
317}
318// namespace libexcept
319// vim: ts=4 sw=4 et
Declarations of demangle functions we support.
Declarations of the exception library.
stack_trace_t collect_stack_trace(int stack_trace_depth)
Collect the raw stack trace in a list of strings.
std::string demangle_cpp_name(char const *type_id_name)
Demangle the specified type string.
Definition demangle.cpp:95
std::list< std::string > stack_trace_t
The stack trace results.
Definition stack_trace.h:41
stack_trace_t collect_stack_trace_with_line_numbers(int stack_trace_depth)
Collect the stack trace in a list of strings.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.