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 for(int idx(0); idx < size; ++idx)
114 {
115 char const * stack_string(stack_string_list.get()[idx]);
116 stack_trace.push_back(stack_string);
117 }
118 }
119
120 return stack_trace;
121}
122
123
150{
151 stack_trace_t stack_trace;
152
153 if(stack_trace_depth > 0)
154 {
155 std::vector<void *> array;
156 array.resize(stack_trace_depth);
157 int const size(backtrace(&array[0], stack_trace_depth));
158
159 // save a copy of the system array in our class
160 //
161 std::unique_ptr<char *, decltype(&::free)> stack_string_list(backtrace_symbols(&array[0], size), &::free);
162 for(int idx(0); idx < size; ++idx)
163 {
164 char const * raw_stack_string(stack_string_list.get()[idx]);
165
166 // the raw stack string is expected to be composed of:
167 // <filename>(<function-name>+<offset>) [<addr>]
168 //
169 // we extract all those elements and use addr2line and c++filt
170 // to convert that data to a usable function name and line number
171 //
172 std::string filename;
173 std::string raw_function_name;
174 std::string addr;
175 std::string result;
176
177 // go to end of filename
178 //
179 char const * s(raw_stack_string);
180 char const * start(s);
181 while(*s != '('
182 && *s != '\0')
183 {
184 ++s;
185 }
186 if(*s == '(')
187 {
188 filename = std::string(start, s - start);
189 ++s;
190
191 start = s;
192 while(*s != '+'
193 && *s != ')'
194 && *s != '\0')
195 {
196 ++s;
197 }
198
199 if(*s == '+')
200 {
201 raw_function_name = std::string(start, s - start);
202 ++s;
203
204 // skip the offset
205 //
206 while(*s != ')'
207 && *s != '\0')
208 {
209 ++s;
210 }
211 }
212 //else if(*s == ')') {} -- no function name
213
214 if(*s == ')'
215 && s[1] == ' '
216 && s[2] == '['
217 && s[3] == '0'
218 && s[4] == 'x')
219 {
220 s += 5;
221
222 start = s;
223 while(*s != ']'
224 && *s != '\0')
225 {
226 ++s;
227 }
228
229 if(*s == ']')
230 {
231 addr = std::string(start, s - start);
232
233 result.clear();
234
235 // here we have our info, use it to get sane data
236 //
237 // TODO: look into whether we could rewrite the
238 // eu-addr2line tool in C++ to avoid having to
239 // run it (because that would be faster)
240 //
241 // I first used the "... -e <filename> ..." option
242 // but with the --pid=..., it ought to be faster.
243 //
244 {
245 std::string addr2line("eu-addr2line --pid="
246 + std::to_string(getpid())
247 + " "
248 + addr);
249 std::unique_ptr<FILE, int(*)(FILE *)> p(popen(addr2line.c_str(), "r"), &::pclose);
250 std::string line;
251 for(;;)
252 {
253 int const c(fgetc(p.get()));
254 if(c == EOF)
255 {
256 break;
257 }
258 if(c != '\n')
259 {
260 line += c;
261 }
262 }
263 if(line == "??:0")
264 {
265 // this means addr2line failed to find the
266 // debug info in your executable/.so
267 // we fallback to the default which may help
268 // if you have a version with debug info
269 //
270 result += filename
271 + "["
272 + addr
273 + "]";
274 }
275 else
276 {
277 result = line;
278 }
279 }
280
281 if(!raw_function_name.empty())
282 {
283 result += " in ";
284 result += demangle_cpp_name(raw_function_name.c_str());
285 }
286 else
287 {
288 result += " <no function name>";
289 }
290 }
291 }
292 }
293
294 if(result.empty())
295 {
296 // use the raw line as a fallback if we could not parse it
297 // correctly or a conversion somehow fails...
298 //
299 stack_trace.push_back(std::string(raw_stack_string)); // LCOV_EXCL_LINE
300 }
301 else
302 {
303 stack_trace.push_back(result);
304 }
305 }
306 }
307
308 return stack_trace;
309}
310
311
312
313
314}
315// namespace libexcept
316// 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.