Open
Graph Drawing
Framework

 v. 2025.10-dev (Foxglove)
 

Loading...
Searching...
No Matches
backward.hpp
Go to the documentation of this file.
1/*
2 * backward.hpp
3 * Copyright 2013 Google Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include <ogdf/basic/internal/config.h> // IWYU pragma: keep
25
26#ifdef OGDF_EXTERNAL_BACKWARD
27#include <backward.hpp>
28#elif !defined(H_6B9572DA_A64B_49E6_B234_051480991C89)
29#define H_6B9572DA_A64B_49E6_B234_051480991C89
30
31#ifndef __cplusplus
32# error "It's not going to compile without a C++ compiler..."
33#endif
34
35#if defined(BACKWARD_CXX11)
36#elif defined(BACKWARD_CXX98)
37#else
38# if __cplusplus >= 201103L
39# define BACKWARD_CXX11
40# define BACKWARD_ATLEAST_CXX11
41# define BACKWARD_ATLEAST_CXX98
42# else
43# define BACKWARD_CXX98
44# define BACKWARD_ATLEAST_CXX98
45# endif
46#endif
47
48// You can define one of the following (or leave it to the auto-detection):
49//
50// #define BACKWARD_SYSTEM_LINUX
51// - specialization for linux
52//
53// #define BACKWARD_SYSTEM_UNKNOWN
54// - placebo implementation, does nothing.
55//
56#if defined(BACKWARD_SYSTEM_LINUX)
57#elif defined(BACKWARD_SYSTEM_UNKNOWN)
58#else
59# if defined(__linux)
60# define BACKWARD_SYSTEM_LINUX
61# else
62# define BACKWARD_SYSTEM_UNKNOWN
63# endif
64#endif
65
66#include <fstream>
67#include <iostream>
68#include <sstream>
69#include <algorithm>
70#include <cstdlib>
71#include <cstdio>
72#include <cstring>
73#include <cctype>
74#include <string>
75#include <new>
76#include <iomanip>
77#include <vector>
78
79#if defined(BACKWARD_SYSTEM_LINUX)
80
81// On linux, backtrace can back-trace or "walk" the stack using the following
82// libraries:
83//
84// #define BACKWARD_HAS_UNWIND 1
85// - unwind comes from libgcc, but I saw an equivalent inside clang itself.
86// - with unwind, the stacktrace is as accurate as it can possibly be, since
87// this is used by the C++ runtine in gcc/clang for stack unwinding on
88// exception.
89// - normally libgcc is already linked to your program by default.
90//
91// #define BACKWARD_HAS_BACKTRACE == 1
92// - backtrace seems to be a little bit more portable than libunwind, but on
93// linux, it uses unwind anyway, but abstract away a tiny information that is
94// sadly really important in order to get perfectly accurate stack traces.
95// - backtrace is part of the (e)glib library.
96//
97// The default is:
98// #define BACKWARD_HAS_UNWIND == 1
99//
100// Note that only one of the define should be set to 1 at a time.
101//
102# if BACKWARD_HAS_UNWIND == 1
103# elif BACKWARD_HAS_BACKTRACE == 1
104# else
105# undef BACKWARD_HAS_UNWIND
106# define BACKWARD_HAS_UNWIND 1
107# undef BACKWARD_HAS_BACKTRACE
108# define BACKWARD_HAS_BACKTRACE 0
109# endif
110
111// On linux, backward can extract detailed information about a stack trace
112// using one of the following libraries:
113//
114// #define BACKWARD_HAS_DW 1
115// - libdw gives you the most juicy details out of your stack traces:
116// - object filename
117// - function name
118// - source filename
119// - line and column numbers
120// - source code snippet (assuming the file is accessible)
121// - variables name and values (if not optimized out)
122// - You need to link with the lib "dw":
123// - apt-get install libdw-dev
124// - g++/clang++ -ldw ...
125//
126// #define BACKWARD_HAS_BFD 1
127// - With libbfd, you get a fair amount of details:
128// - object filename
129// - function name
130// - source filename
131// - line numbers
132// - source code snippet (assuming the file is accessible)
133// - You need to link with the lib "bfd":
134// - apt-get install binutils-dev
135// - g++/clang++ -lbfd ...
136//
137// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1
138// - backtrace provides minimal details for a stack trace:
139// - object filename
140// - function name
141// - backtrace is part of the (e)glib library.
142//
143// The default is:
144// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1
145//
146// Note that only one of the define should be set to 1 at a time.
147//
148# if BACKWARD_HAS_DW == 1
149# elif BACKWARD_HAS_BFD == 1
150# elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1
151# else
152# undef BACKWARD_HAS_DW
153# define BACKWARD_HAS_DW 0
154# undef BACKWARD_HAS_BFD
155# define BACKWARD_HAS_BFD 0
156# undef BACKWARD_HAS_BACKTRACE_SYMBOL
157# define BACKWARD_HAS_BACKTRACE_SYMBOL 1
158# endif
159
160
161# if BACKWARD_HAS_UNWIND == 1
162
163# include <unwind.h>
164// while gcc's unwind.h defines something like that:
165// extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *);
166// extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *);
167//
168// clang's unwind.h defines something like this:
169// uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context);
170//
171// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we
172// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr
173// anyway.
174//
175// Luckily we can play on the fact that the guard macros have a different name:
176#ifdef __CLANG_UNWIND_H
177// In fact, this function still comes from libgcc (on my different linux boxes,
178// clang links against libgcc).
179# include <inttypes.h>
180extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context*, int*);
181#endif
182
183# endif
184
185# include <cxxabi.h>
186# include <fcntl.h>
187# include <link.h>
188# include <sys/stat.h>
189# include <syscall.h>
190# include <unistd.h>
191# include <signal.h>
192
193# if BACKWARD_HAS_BFD == 1
194// NOTE: defining PACKAGE{,_VERSION} is required before including
195// bfd.h on some platforms, see also:
196// https://sourceware.org/bugzilla/show_bug.cgi?id=14243
197# ifndef PACKAGE
198# define PACKAGE
199# endif
200# ifndef PACKAGE_VERSION
201# define PACKAGE_VERSION
202# endif
203# include <bfd.h>
204# ifndef _GNU_SOURCE
205# define _GNU_SOURCE
206# include <dlfcn.h>
207# undef _GNU_SOURCE
208# else
209# include <dlfcn.h>
210# endif
211# endif
212
213# if BACKWARD_HAS_DW == 1
214# include <elfutils/libdw.h>
215# include <elfutils/libdwfl.h>
216# include <dwarf.h>
217# endif
218
219# if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1)
220 // then we shall rely on backtrace
221# include <execinfo.h>
222# endif
223
224#endif // defined(BACKWARD_SYSTEM_LINUX)
225
226#ifdef BACKWARD_ATLEAST_CXX11
227# include <unordered_map>
228# include <utility> // for std::swap
229 namespace backward {
230 namespace details {
231 template <typename K, typename V>
232 struct hashtable {
233 using type = std::unordered_map<K, V>;
234 };
235 using std::move;
236 }
237 }
238#else // NOT BACKWARD_ATLEAST_CXX11
239# include <map>
240 namespace backward {
241 namespace details {
242 template <typename K, typename V>
243 struct hashtable {
244 using type = std::map<K, V>;
245 };
246 template <typename T>
247 const T& move(const T& v) { return v; }
248 template <typename T>
249 T& move(T& v) { return v; }
250 }
251 }
252#endif // BACKWARD_ATLEAST_CXX11
253
254namespace backward {
255
256namespace system_tag {
257 struct linux_tag; // seems that I cannot call that "linux" because the name
258 // is already defined... so I am adding _tag everywhere.
259 struct unknown_tag;
260
261#if defined(BACKWARD_SYSTEM_LINUX)
262 using current_tag = linux_tag;
263#elif defined(BACKWARD_SYSTEM_UNKNOWN)
264 using current_tag = unknown_tag;
265#else
266# error "May I please get my system defines?"
267#endif
268}
269
270
271namespace trace_resolver_tag {
272#ifdef BACKWARD_SYSTEM_LINUX
273 struct libdw;
274 struct libbfd;
275 struct backtrace_symbol;
276
277# if BACKWARD_HAS_DW == 1
278 using current = libdw;
279# elif BACKWARD_HAS_BFD == 1
280 using current = libbfd;
281# elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1
282 using current = backtrace_symbol;
283# else
284# error "You shall not pass, until you know what you want."
285# endif
286#endif // BACKWARD_SYSTEM_LINUX
287}
288
289
290namespace details {
291
292template <typename T>
293 struct rm_ptr { using type = T; };
294
295template <typename T>
296 struct rm_ptr<T*> { using type = T; };
297
298template <typename T>
299 struct rm_ptr<const T*> { using type = const T; };
300
301template <typename R, typename T, R (*F)(T)>
302struct deleter {
303 template <typename U>
304 void operator()(U& ptr) const {
305 (*F)(ptr);
306 }
307};
308
309template <typename T>
311 void operator()(T& ptr) const {
312 delete ptr;
313 }
314};
315
316template <typename T, typename Deleter = deleter<void, void*, &::free> >
317class handle {
318 struct dummy;
320 bool _empty;
321
322#ifdef BACKWARD_ATLEAST_CXX11
323 handle(const handle&) = delete;
324 handle& operator=(const handle&) = delete;
325#endif
326
327public:
329 if (!_empty) {
330 Deleter()(_val);
331 }
332 }
333
334 explicit handle(): _val(), _empty(true) {}
335 explicit handle(T val): _val(val), _empty(false) {}
336
337#ifdef BACKWARD_ATLEAST_CXX11
338 handle(handle&& from): _empty(true) {
339 swap(from);
340 }
341 handle& operator=(handle&& from) {
342 swap(from); return *this;
343 }
344#else
345 explicit handle(const handle& from): _empty(true) {
346 // some sort of poor man's move semantic.
347 swap(const_cast<handle&>(from));
348 }
349 handle& operator=(const handle& from) {
350 // some sort of poor man's move semantic.
351 swap(const_cast<handle&>(from)); return *this;
352 }
353#endif
354
355 void reset(T new_val) {
356 handle tmp(new_val);
357 swap(tmp);
358 }
359 operator const dummy*() const {
360 if (_empty) {
361 return 0;
362 }
363 return reinterpret_cast<const dummy*>(_val);
364 }
365 T get() {
366 return _val;
367 }
369 _empty = true;
370 return _val;
371 }
372 void swap(handle& b) {
373 using std::swap;
374 swap(b._val, _val); // can throw, we are safe here.
375 swap(b._empty, _empty); // should not throw: if you cannot swap two
376 // bools without throwing... It's a lost cause anyway!
377 }
378
379 T operator->() { return _val; }
380 const T operator->() const { return _val; }
381
382 using ref_t = typename rm_ptr<T>::type&;
383 using const_ref_t = const typename rm_ptr<T>::type&;
384 ref_t operator*() { return *_val; }
385 const_ref_t operator*() const { return *_val; }
386 ref_t operator[](size_t idx) { return _val[idx]; }
387
388 // Watch out, we've got a badass over here
390 _empty = false;
391 return &_val;
392 }
393};
394
395// Default demangler implementation (do nothing).
396template <typename TAG>
398 static std::string demangle(const char* funcname) {
399 return funcname;
400 }
401};
402
403#ifdef BACKWARD_SYSTEM_LINUX
404
405template <>
406struct demangler_impl<system_tag::current_tag> {
407 demangler_impl(): _demangle_buffer_length(0) {}
408
409 std::string demangle(const char* funcname) {
410 using namespace details;
411 _demangle_buffer.reset(
412 abi::__cxa_demangle(funcname, NULL,
413 &_demangle_buffer_length, 0)
414 );
415 if (_demangle_buffer) {
416 return _demangle_buffer.get();
417 }
418 return funcname;
419 }
420
421private:
422 details::handle<char*> _demangle_buffer;
423 size_t _demangle_buffer_length;
424};
425
426#endif // BACKWARD_SYSTEM_LINUX
427
429 public demangler_impl<system_tag::current_tag> {};
430
431}
432
433/*************** A TRACE ***************/
434
435struct Trace {
436 void* addr;
437 unsigned idx;
438
440 addr(0), idx(0) {}
441
442 explicit Trace(void* _addr, size_t _idx):
443 addr(_addr), idx(_idx) {}
444};
445
446struct ResolvedTrace: public Trace {
447
448 struct SourceLoc {
449 std::string function;
450 std::string filename;
451 unsigned line;
452 unsigned col;
453
454 SourceLoc(): line(0), col(0) {}
455
456 bool operator==(const SourceLoc& b) const {
457 return function == b.function
458 && filename == b.filename
459 && line == b.line
460 && col == b.col;
461 }
462
463 bool operator!=(const SourceLoc& b) const {
464 return !(*this == b);
465 }
466 };
467
468 // In which binary object this trace is located.
469 std::string object_filename;
470
471 // The function in the object that contain the trace. This is not the same
472 // as source.function which can be an function inlined in object_function.
473 std::string object_function;
474
475 // The source location of this trace. It is possible for filename to be
476 // empty and for line/col to be invalid (value 0) if this information
477 // couldn't be deduced, for example if there is no debug information in the
478 // binary object.
480
481 // An optionals list of "inliners". All the successive sources location
482 // from where the source location of the trace (the attribute right above)
483 // is inlined. It is especially useful when you compiled with optimization.
484 using source_locs_t = std::vector<SourceLoc>;
486
488 Trace() {}
489 ResolvedTrace(const Trace& mini_trace):
490 Trace(mini_trace) {}
491};
492
493/*************** STACK TRACE ***************/
494
495// default implemention.
496template <typename TAG>
498public:
499 size_t size() const { return 0; }
500 Trace operator[](size_t) { return Trace(); }
501 size_t load_here(size_t=0) { return 0; }
502 size_t load_from(void*, size_t=0) { return 0; }
503 unsigned thread_id() const { return 0; }
504 void skip_n_firsts(size_t) { }
505};
506
507#ifdef BACKWARD_SYSTEM_LINUX
508
509class StackTraceLinuxImplBase {
510public:
511 StackTraceLinuxImplBase(): _thread_id(0), _skip(0) {}
512
513 unsigned thread_id() const {
514 return _thread_id;
515 }
516
517 void skip_n_firsts(size_t n) { _skip = n; }
518
519protected:
520 void load_thread_info() {
521 _thread_id = syscall(SYS_gettid);
522 if (_thread_id == (size_t) getpid()) {
523 // If the thread is the main one, let's hide that.
524 // I like to keep little secret sometimes.
525 _thread_id = 0;
526 }
527 }
528
529 size_t skip_n_firsts() const { return _skip; }
530
531private:
532 size_t _thread_id;
533 size_t _skip;
534};
535
536class StackTraceLinuxImplHolder: public StackTraceLinuxImplBase {
537public:
538 size_t size() const {
539 return _stacktrace.size() ? _stacktrace.size() - skip_n_firsts() : 0;
540 }
541 Trace operator[](size_t idx) {
542 if (idx >= size()) {
543 return Trace();
544 }
545 return Trace(_stacktrace[idx + skip_n_firsts()], idx);
546 }
547 void** begin() {
548 if (size()) {
549 return &_stacktrace[skip_n_firsts()];
550 }
551 return 0;
552 }
553
554protected:
555 std::vector<void*> _stacktrace;
556};
557
558
559#if BACKWARD_HAS_UNWIND == 1
560
561namespace details {
562
563template <typename F>
564class Unwinder {
565public:
566 size_t operator()(F& f, size_t depth) {
567 _f = &f;
568 _index = -1;
569 _depth = depth;
570 _Unwind_Backtrace(&this->backtrace_trampoline, this);
571 return _index;
572 }
573
574private:
575 F* _f;
576 ssize_t _index;
577 size_t _depth;
578
579 static _Unwind_Reason_Code backtrace_trampoline(
580 _Unwind_Context* ctx, void *self) {
581 return ((Unwinder*)self)->backtrace(ctx);
582 }
583
584 _Unwind_Reason_Code backtrace(_Unwind_Context* ctx) {
585 if (_index >= 0 && static_cast<size_t>(_index) >= _depth)
586 return _URC_END_OF_STACK;
587
588 int ip_before_instruction = 0;
589 uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
590
591 if (!ip_before_instruction) {
592 ip -= 1;
593 }
594
595 if (_index >= 0) { // ignore first frame.
596 (*_f)(_index, (void*)ip);
597 }
598 _index += 1;
599 return _URC_NO_REASON;
600 }
601};
602
603template <typename F>
604size_t unwind(F f, size_t depth) {
605 Unwinder<F> unwinder;
606 return unwinder(f, depth);
607}
608
609}
610
611
612template <>
613class StackTraceImpl<system_tag::linux_tag>: public StackTraceLinuxImplHolder {
614public:
615 __attribute__ ((noinline)) // TODO use some macro
616 size_t load_here(size_t depth=32) {
617 load_thread_info();
618 if (depth == 0) {
619 return 0;
620 }
621 _stacktrace.resize(depth);
622 size_t trace_cnt = details::unwind(callback(*this), depth);
623 _stacktrace.resize(trace_cnt);
624 skip_n_firsts(0);
625 return size();
626 }
627 size_t load_from(void* addr, size_t depth=32) {
628 load_here(depth + 8);
629
630 for (size_t i = 0; i < _stacktrace.size(); ++i) {
631 if (_stacktrace[i] == addr) {
632 skip_n_firsts(i);
633 break;
634 }
635 }
636
637 _stacktrace.resize(std::min(_stacktrace.size(),
638 skip_n_firsts() + depth));
639 return size();
640 }
641
642private:
643 struct callback {
644 StackTraceImpl& self;
645 callback(StackTraceImpl& _self): self(_self) {}
646
647 void operator()(size_t idx, void* addr) {
648 self._stacktrace[idx] = addr;
649 }
650 };
651};
652
653
654#else // BACKWARD_HAS_UNWIND == 0
655
656template <>
657class StackTraceImpl<system_tag::linux_tag>: public StackTraceLinuxImplHolder {
658public:
659 __attribute__ ((noinline)) // TODO use some macro
660 size_t load_here(size_t depth=32) {
661 load_thread_info();
662 if (depth == 0) {
663 return 0;
664 }
665 _stacktrace.resize(depth + 1);
666 size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size());
667 _stacktrace.resize(trace_cnt);
668 skip_n_firsts(1);
669 return size();
670 }
671
672 size_t load_from(void* addr, size_t depth=32) {
673 load_here(depth + 8);
674
675 for (size_t i = 0; i < _stacktrace.size(); ++i) {
676 if (_stacktrace[i] == addr) {
677 skip_n_firsts(i);
678 _stacktrace[i] = (void*)( (uintptr_t)_stacktrace[i] + 1);
679 break;
680 }
681 }
682
683 _stacktrace.resize(std::min(_stacktrace.size(),
684 skip_n_firsts() + depth));
685 return size();
686 }
687};
688
689#endif // BACKWARD_HAS_UNWIND
690#endif // BACKWARD_SYSTEM_LINUX
691
693 public StackTraceImpl<system_tag::current_tag> {};
694
695/*************** TRACE RESOLVER ***************/
696
697template <typename TAG>
699
700#ifdef BACKWARD_SYSTEM_UNKNOWN
701
702template <>
703class TraceResolverImpl<system_tag::unknown_tag> {
704public:
705 template <class ST>
706 void load_stacktrace(ST&) {}
708 return t;
709 }
710};
711
712#endif
713
714#ifdef BACKWARD_SYSTEM_LINUX
715
716class TraceResolverLinuxImplBase {
717protected:
718 std::string demangle(const char* funcname) {
719 return _demangler.demangle(funcname);
720 }
721
722private:
723 details::demangler _demangler;
724};
725
726template <typename STACKTRACE_TAG>
727class TraceResolverLinuxImpl;
728
729#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1
730
731template <>
732class TraceResolverLinuxImpl<trace_resolver_tag::backtrace_symbol>:
733 public TraceResolverLinuxImplBase {
734public:
735 template <class ST>
736 void load_stacktrace(ST& st) {
737 using namespace details;
738 if (st.size() == 0) {
739 return;
740 }
741 _symbols.reset(
742 backtrace_symbols(st.begin(), st.size())
743 );
744 }
745
746 ResolvedTrace resolve(ResolvedTrace trace) {
747 char* filename = _symbols[trace.idx];
748 char* funcname = filename;
749 while (*funcname && *funcname != '(') {
750 funcname += 1;
751 }
752 trace.object_filename.assign(filename, funcname++);
753 char* funcname_end = funcname;
754 while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') {
755 funcname_end += 1;
756 }
757 *funcname_end = '\0';
758 trace.object_function = this->demangle(funcname);
759 trace.source.function = trace.object_function; // we cannot do better.
760 return trace;
761 }
762
763private:
764 details::handle<char**> _symbols;
765};
766
767#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1
768
769#if BACKWARD_HAS_BFD == 1
770
771template <>
772class TraceResolverLinuxImpl<trace_resolver_tag::libbfd>:
773 public TraceResolverLinuxImplBase {
774public:
775 TraceResolverLinuxImpl(): _bfd_loaded(false) {}
776
777 template <class ST>
778 void load_stacktrace(ST&) {}
779
780 ResolvedTrace resolve(ResolvedTrace trace) {
781 Dl_info symbol_info;
782
783 // trace.addr is a virtual address in memory pointing to some code.
784 // Let's try to find from which loaded object it comes from.
785 // The loaded object can be yourself btw.
786 if (!dladdr(trace.addr, &symbol_info)) {
787 return trace; // dat broken trace...
788 }
789
790 // Now we get in symbol_info:
791 // .dli_fname:
792 // pathname of the shared object that contains the address.
793 // .dli_fbase:
794 // where the object is loaded in memory.
795 // .dli_sname:
796 // the name of the nearest symbol to trace.addr, we expect a
797 // function name.
798 // .dli_saddr:
799 // the exact address corresponding to .dli_sname.
800
801 if (symbol_info.dli_sname) {
802 trace.object_function = demangle(symbol_info.dli_sname);
803 }
804
805 if (!symbol_info.dli_fname) {
806 return trace;
807 }
808
809 trace.object_filename = symbol_info.dli_fname;
810 bfd_fileobject& fobj = load_object_with_bfd(symbol_info.dli_fname);
811 if (!fobj.handle) {
812 return trace; // sad, we couldn't load the object :(
813 }
814
815
816 find_sym_result* details_selected; // to be filled.
817
818 // trace.addr is the next instruction to be executed after returning
819 // from the nested stack frame. In C++ this usually relate to the next
820 // statement right after the function call that leaded to a new stack
821 // frame. This is not usually what you want to see when printing out a
822 // stacktrace...
823 find_sym_result details_call_site = find_symbol_details(fobj,
824 trace.addr, symbol_info.dli_fbase);
825 details_selected = &details_call_site;
826
827#if BACKWARD_HAS_UNWIND == 0
828 // ...this is why we also try to resolve the symbol that is right
829 // before the return address. If we are lucky enough, we will get the
830 // line of the function that was called. But if the code is optimized,
831 // we might get something absolutely not related since the compiler
832 // can reschedule the return address with inline functions and
833 // tail-call optimisation (among other things that I don't even know
834 // or cannot even dream about with my tiny limited brain).
835 find_sym_result details_adjusted_call_site = find_symbol_details(fobj,
836 (void*) (uintptr_t(trace.addr) - 1),
837 symbol_info.dli_fbase);
838
839 // In debug mode, we should always get the right thing(TM).
840 if (details_call_site.found && details_adjusted_call_site.found) {
841 // Ok, we assume that details_adjusted_call_site is a better estimation.
842 details_selected = &details_adjusted_call_site;
843 trace.addr = (void*) (uintptr_t(trace.addr) - 1);
844 }
845
846 if (details_selected == &details_call_site && details_call_site.found) {
847 // we have to re-resolve the symbol in order to reset some
848 // internal state in BFD... so we can call backtrace_inliners
849 // thereafter...
850 details_call_site = find_symbol_details(fobj, trace.addr,
851 symbol_info.dli_fbase);
852 }
853#endif // BACKWARD_HAS_UNWIND
854
855 if (details_selected->found) {
856 if (details_selected->filename) {
857 trace.source.filename = details_selected->filename;
858 }
859 trace.source.line = details_selected->line;
860
861 if (details_selected->funcname) {
862 // this time we get the name of the function where the code is
863 // located, instead of the function were the address is
864 // located. In short, if the code was inlined, we get the
865 // function correspoding to the code. Else we already got in
866 // trace.function.
867 trace.source.function = demangle(details_selected->funcname);
868
869 if (!symbol_info.dli_sname) {
870 // for the case dladdr failed to find the symbol name of
871 // the function, we might as well try to put something
872 // here.
873 trace.object_function = trace.source.function;
874 }
875 }
876
877 // Maybe the source of the trace got inlined inside the function
878 // (trace.source.function). Let's see if we can get all the inlined
879 // calls along the way up to the initial call site.
880 trace.inliners = backtrace_inliners(fobj, *details_selected);
881
882#if 0
883 if (trace.inliners.size() == 0) {
884 // Maybe the trace was not inlined... or maybe it was and we
885 // are lacking the debug information. Let's try to make the
886 // world better and see if we can get the line number of the
887 // function (trace.source.function) now.
888 //
889 // We will get the location of where the function start (to be
890 // exact: the first instruction that really start the
891 // function), not where the name of the function is defined.
892 // This can be quite far away from the name of the function
893 // btw.
894 //
895 // If the source of the function is the same as the source of
896 // the trace, we cannot say if the trace was really inlined or
897 // not. However, if the filename of the source is different
898 // between the function and the trace... we can declare it as
899 // an inliner. This is not 100% accurate, but better than
900 // nothing.
901
902 if (symbol_info.dli_saddr) {
903 find_sym_result details = find_symbol_details(fobj,
904 symbol_info.dli_saddr,
905 symbol_info.dli_fbase);
906
907 if (details.found) {
908 ResolvedTrace::SourceLoc diy_inliner;
909 diy_inliner.line = details.line;
910 if (details.filename) {
911 diy_inliner.filename = details.filename;
912 }
913 if (details.funcname) {
914 diy_inliner.function = demangle(details.funcname);
915 } else {
916 diy_inliner.function = trace.source.function;
917 }
918 if (diy_inliner != trace.source) {
919 trace.inliners.push_back(diy_inliner);
920 }
921 }
922 }
923 }
924#endif
925 }
926
927 return trace;
928 }
929
930private:
931 bool _bfd_loaded;
932
933 using bfd_handle_t = details::handle<bfd*,
934 details::deleter<bfd_boolean, bfd*, &bfd_close>
935 >;
936
937 using bfd_symtab_t = details::handle<asymbol**>;
938
939
940 struct bfd_fileobject {
941 bfd_handle_t handle;
942 bfd_vma base_addr;
943 bfd_symtab_t symtab;
944 bfd_symtab_t dynamic_symtab;
945 };
946
947 using fobj_bfd_map_t = details::hashtable<std::string, bfd_fileobject>::type;
948 fobj_bfd_map_t _fobj_bfd_map;
949
950 bfd_fileobject& load_object_with_bfd(const std::string& filename_object) {
951 using namespace details;
952
953 if (!_bfd_loaded) {
954 using namespace details;
955 bfd_init();
956 _bfd_loaded = true;
957 }
958
959 fobj_bfd_map_t::iterator it =
960 _fobj_bfd_map.find(filename_object);
961 if (it != _fobj_bfd_map.end()) {
962 return it->second;
963 }
964
965 // this new object is empty for now.
966 bfd_fileobject& r = _fobj_bfd_map[filename_object];
967
968 // we do the work temporary in this one;
969 bfd_handle_t bfd_handle;
970
971 int fd = open(filename_object.c_str(), O_RDONLY);
972 bfd_handle.reset(
973 bfd_fdopenr(filename_object.c_str(), "default", fd)
974 );
975 if (!bfd_handle) {
976 close(fd);
977 return r;
978 }
979
980 if (!bfd_check_format(bfd_handle.get(), bfd_object)) {
981 return r; // not an object? You lose.
982 }
983
984 if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) {
985 return r; // that's what happen when you forget to compile in debug.
986 }
987
988 ssize_t symtab_storage_size =
989 bfd_get_symtab_upper_bound(bfd_handle.get());
990
991 ssize_t dyn_symtab_storage_size =
992 bfd_get_dynamic_symtab_upper_bound(bfd_handle.get());
993
994 if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) {
995 return r; // weird, is the file is corrupted?
996 }
997
998 bfd_symtab_t symtab, dynamic_symtab;
999 ssize_t symcount = 0, dyn_symcount = 0;
1000
1001 if (symtab_storage_size > 0) {
1002 symtab.reset(
1003 (bfd_symbol**) malloc(symtab_storage_size)
1004 );
1005 symcount = bfd_canonicalize_symtab(
1006 bfd_handle.get(), symtab.get()
1007 );
1008 }
1009
1010 if (dyn_symtab_storage_size > 0) {
1011 dynamic_symtab.reset(
1012 (bfd_symbol**) malloc(dyn_symtab_storage_size)
1013 );
1014 dyn_symcount = bfd_canonicalize_dynamic_symtab(
1015 bfd_handle.get(), dynamic_symtab.get()
1016 );
1017 }
1018
1019
1020 if (symcount <= 0 && dyn_symcount <= 0) {
1021 return r; // damned, that's a stripped file that you got there!
1022 }
1023
1024 r.handle = move(bfd_handle);
1025 r.symtab = move(symtab);
1026 r.dynamic_symtab = move(dynamic_symtab);
1027 return r;
1028 }
1029
1030 struct find_sym_result {
1031 bool found;
1032 const char* filename;
1033 const char* funcname;
1034 unsigned int line;
1035 };
1036
1037 struct find_sym_context {
1038 TraceResolverLinuxImpl* self;
1039 bfd_fileobject* fobj;
1040 void* addr;
1041 void* base_addr;
1042 find_sym_result result;
1043 };
1044
1045 find_sym_result find_symbol_details(bfd_fileobject& fobj, void* addr,
1046 void* base_addr) {
1047 find_sym_context context;
1048 context.self = this;
1049 context.fobj = &fobj;
1050 context.addr = addr;
1051 context.base_addr = base_addr;
1052 context.result.found = false;
1053 bfd_map_over_sections(fobj.handle.get(), &find_in_section_trampoline,
1054 (void*)&context);
1055 return context.result;
1056 }
1057
1058 static void find_in_section_trampoline(bfd*, asection* section,
1059 void* data) {
1060 find_sym_context* context = static_cast<find_sym_context*>(data);
1061 context->self->find_in_section(
1062 reinterpret_cast<bfd_vma>(context->addr),
1063 reinterpret_cast<bfd_vma>(context->base_addr),
1064 *context->fobj,
1065 section, context->result
1066 );
1067 }
1068
1069 void find_in_section(bfd_vma addr, bfd_vma base_addr,
1070 bfd_fileobject& fobj, asection* section, find_sym_result& result)
1071 {
1072 if (result.found) return;
1073
1074 if ((bfd_get_section_flags(fobj.handle.get(), section)
1075 & SEC_ALLOC) == 0)
1076 return; // a debug section is never loaded automatically.
1077
1078 bfd_vma sec_addr = bfd_get_section_vma(fobj.handle.get(), section);
1079 bfd_size_type size = bfd_get_section_size(section);
1080
1081 // are we in the boundaries of the section?
1082 if (addr < sec_addr || addr >= sec_addr + size) {
1083 addr -= base_addr; // oups, a relocated object, lets try again...
1084 if (addr < sec_addr || addr >= sec_addr + size) {
1085 return;
1086 }
1087 }
1088
1089 if (!result.found && fobj.symtab) {
1090 result.found = bfd_find_nearest_line(fobj.handle.get(), section,
1091 fobj.symtab.get(), addr - sec_addr, &result.filename,
1092 &result.funcname, &result.line);
1093 }
1094
1095 if (!result.found && fobj.dynamic_symtab) {
1096 result.found = bfd_find_nearest_line(fobj.handle.get(), section,
1097 fobj.dynamic_symtab.get(), addr - sec_addr,
1098 &result.filename, &result.funcname, &result.line);
1099 }
1100
1101 }
1102
1103 ResolvedTrace::source_locs_t backtrace_inliners(bfd_fileobject& fobj,
1104 find_sym_result previous_result) {
1105 // This function can be called ONLY after a SUCCESSFUL call to
1106 // find_symbol_details. The state is global to the bfd_handle.
1107 ResolvedTrace::source_locs_t results;
1108 while (previous_result.found) {
1109 find_sym_result result;
1110 result.found = bfd_find_inliner_info(fobj.handle.get(),
1111 &result.filename, &result.funcname, &result.line);
1112
1113 if (result.found) /* and not (
1114 cstrings_eq(previous_result.filename, result.filename)
1115 and cstrings_eq(previous_result.funcname, result.funcname)
1116 and result.line == previous_result.line
1117 )) */ {
1118 ResolvedTrace::SourceLoc src_loc;
1119 src_loc.line = result.line;
1120 if (result.filename) {
1121 src_loc.filename = result.filename;
1122 }
1123 if (result.funcname) {
1124 src_loc.function = demangle(result.funcname);
1125 }
1126 results.push_back(src_loc);
1127 }
1128 previous_result = result;
1129 }
1130 return results;
1131 }
1132
1133 bool cstrings_eq(const char* a, const char* b) {
1134 if (!a || !b) {
1135 return false;
1136 }
1137 return strcmp(a, b) == 0;
1138 }
1139
1140};
1141#endif // BACKWARD_HAS_BFD == 1
1142
1143#if BACKWARD_HAS_DW == 1
1144
1145template <>
1146class TraceResolverLinuxImpl<trace_resolver_tag::libdw>:
1147 public TraceResolverLinuxImplBase {
1148public:
1149 TraceResolverLinuxImpl(): _dwfl_handle_initialized(false) {}
1150
1151 template <class ST>
1152 void load_stacktrace(ST&) {}
1153
1154 ResolvedTrace resolve(ResolvedTrace trace) {
1155 using namespace details;
1156
1157 Dwarf_Addr trace_addr = (Dwarf_Addr) trace.addr;
1158
1159 if (!_dwfl_handle_initialized) {
1160 // initialize dwfl...
1161 _dwfl_cb.reset(new Dwfl_Callbacks);
1162 _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf;
1163 _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo;
1164 _dwfl_cb->debuginfo_path = 0;
1165
1166 _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get()));
1167 _dwfl_handle_initialized = true;
1168
1169 if (!_dwfl_handle) {
1170 return trace;
1171 }
1172
1173 // ...from the current process.
1174 dwfl_report_begin(_dwfl_handle.get());
1175 int r = dwfl_linux_proc_report (_dwfl_handle.get(), getpid());
1176 dwfl_report_end(_dwfl_handle.get(), NULL, NULL);
1177 if (r < 0) {
1178 return trace;
1179 }
1180 }
1181
1182 if (!_dwfl_handle) {
1183 return trace;
1184 }
1185
1186 // find the module (binary object) that contains the trace's address.
1187 // This is not using any debug information, but the addresses ranges of
1188 // all the currently loaded binary object.
1189 Dwfl_Module* mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr);
1190 if (mod) {
1191 // now that we found it, lets get the name of it, this will be the
1192 // full path to the running binary or one of the loaded library.
1193 const char* module_name = dwfl_module_info (mod,
1194 0, 0, 0, 0, 0, 0, 0);
1195 if (module_name) {
1196 trace.object_filename = module_name;
1197 }
1198 // We also look after the name of the symbol, equal or before this
1199 // address. This is found by walking the symtab. We should get the
1200 // symbol corresponding to the function (mangled) containing the
1201 // address. If the code corresponding to the address was inlined,
1202 // this is the name of the out-most inliner function.
1203 const char* sym_name = dwfl_module_addrname(mod, trace_addr);
1204 if (sym_name) {
1205 trace.object_function = demangle(sym_name);
1206 }
1207 }
1208
1209 // now let's get serious, and find out the source location (file and
1210 // line number) of the address.
1211
1212 // This function will look in .debug_aranges for the address and map it
1213 // to the location of the compilation unit DIE in .debug_info and
1214 // return it.
1215 Dwarf_Addr mod_bias = 0;
1216 Dwarf_Die* cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias);
1217
1218#if 1
1219 if (!cudie) {
1220 // Sadly clang does not generate the section .debug_aranges, thus
1221 // dwfl_module_addrdie will fail early. Clang doesn't either set
1222 // the lowpc/highpc/range info for every compilation unit.
1223 //
1224 // So in order to save the world:
1225 // for every compilation unit, we will iterate over every single
1226 // DIEs. Normally functions should have a lowpc/highpc/range, which
1227 // we will use to infer the compilation unit.
1228
1229 // note that this is probably badly inefficient.
1230 while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) {
1231 Dwarf_Die die_mem;
1232 Dwarf_Die* fundie = find_fundie_by_pc(cudie,
1233 trace_addr - mod_bias, &die_mem);
1234 if (fundie) {
1235 break;
1236 }
1237 }
1238 }
1239#endif
1240
1241//#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE
1242#ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE
1243 if (!cudie) {
1244 // If it's still not enough, lets dive deeper in the shit, and try
1245 // to save the world again: for every compilation unit, we will
1246 // load the corresponding .debug_line section, and see if we can
1247 // find our address in it.
1248
1249 Dwarf_Addr cfi_bias;
1250 Dwarf_CFI* cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias);
1251
1252 Dwarf_Addr bias;
1253 while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) {
1254 if (dwarf_getsrc_die(cudie, trace_addr - bias)) {
1255
1256 // ...but if we get a match, it might be a false positive
1257 // because our (address - bias) might as well be valid in a
1258 // different compilation unit. So we throw our last card on
1259 // the table and lookup for the address into the .eh_frame
1260 // section.
1261
1262 handle<Dwarf_Frame*> frame;
1263 dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame);
1264 if (frame) {
1265 break;
1266 }
1267 }
1268 }
1269 }
1270#endif
1271
1272 if (!cudie) {
1273 return trace; // this time we lost the game :/
1274 }
1275
1276 // Now that we have a compilation unit DIE, this function will be able
1277 // to load the corresponding section in .debug_line (if not already
1278 // loaded) and hopefully find the source location mapped to our
1279 // address.
1280 Dwarf_Line* srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias);
1281
1282 if (srcloc) {
1283 const char* srcfile = dwarf_linesrc(srcloc, 0, 0);
1284 if (srcfile) {
1285 trace.source.filename = srcfile;
1286 }
1287 int line = 0, col = 0;
1288 dwarf_lineno(srcloc, &line);
1289 dwarf_linecol(srcloc, &col);
1290 trace.source.line = line;
1291 trace.source.col = col;
1292 }
1293
1294 deep_first_search_by_pc(cudie, trace_addr - mod_bias,
1295 inliners_search_cb(trace));
1296 if (trace.source.function.size() == 0) {
1297 // fallback.
1298 trace.source.function = trace.object_function;
1299 }
1300
1301 return trace;
1302 }
1303
1304private:
1305 using dwfl_handle_t = details::handle<Dwfl*, details::deleter<void, Dwfl*, &dwfl_end>>;
1306 details::handle<Dwfl_Callbacks*, details::default_delete<Dwfl_Callbacks*> >
1307 _dwfl_cb;
1308 dwfl_handle_t _dwfl_handle;
1309 bool _dwfl_handle_initialized;
1310
1311 // defined here because in C++98, template function cannot take locally
1312 // defined types... grrr.
1313 struct inliners_search_cb {
1314 void operator()(Dwarf_Die* die) {
1315 switch (dwarf_tag(die)) {
1316 const char* name;
1317 case DW_TAG_subprogram:
1318 if ((name = dwarf_diename(die))) {
1319 trace.source.function = name;
1320 }
1321 break;
1322
1323 case DW_TAG_inlined_subroutine:
1324 ResolvedTrace::SourceLoc sloc;
1325 Dwarf_Attribute attr_mem;
1326
1327 if ((name = dwarf_diename(die))) {
1328 sloc.function = name;
1329 }
1330 if ((name = die_call_file(die))) {
1331 sloc.filename = name;
1332 }
1333
1334 Dwarf_Word line = 0, col = 0;
1335 dwarf_formudata(dwarf_attr(die, DW_AT_call_line,
1336 &attr_mem), &line);
1337 dwarf_formudata(dwarf_attr(die, DW_AT_call_column,
1338 &attr_mem), &col);
1339 sloc.line = line;
1340 sloc.col = col;
1341
1342 trace.inliners.push_back(sloc);
1343 break;
1344 };
1345 }
1346 ResolvedTrace& trace;
1347 inliners_search_cb(ResolvedTrace& t): trace(t) {}
1348 };
1349
1350
1351 static bool die_has_pc(Dwarf_Die* die, Dwarf_Addr pc) {
1352 Dwarf_Addr low, high;
1353
1354 // continuous range
1355 if (dwarf_hasattr(die, DW_AT_low_pc) and
1356 dwarf_hasattr(die, DW_AT_high_pc)) {
1357 if (dwarf_lowpc(die, &low) != 0) {
1358 return false;
1359 }
1360 if (dwarf_highpc(die, &high) != 0) {
1361 Dwarf_Attribute attr_mem;
1362 Dwarf_Attribute* attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem);
1363 Dwarf_Word value;
1364 if (dwarf_formudata(attr, &value) != 0) {
1365 return false;
1366 }
1367 high = low + value;
1368 }
1369 return pc >= low && pc < high;
1370 }
1371
1372 // non-continuous range.
1373 Dwarf_Addr base;
1374 ptrdiff_t offset = 0;
1375 while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) {
1376 if (pc >= low && pc < high) {
1377 return true;
1378 }
1379 }
1380 return false;
1381 }
1382
1383 static Dwarf_Die* find_fundie_by_pc(Dwarf_Die* parent_die, Dwarf_Addr pc,
1384 Dwarf_Die* result) {
1385 if (dwarf_child(parent_die, result) != 0) {
1386 return 0;
1387 }
1388
1389 Dwarf_Die* die = result;
1390 do {
1391 switch (dwarf_tag(die)) {
1392 case DW_TAG_subprogram:
1393 case DW_TAG_inlined_subroutine:
1394 if (die_has_pc(die, pc)) {
1395 return result;
1396 }
1397 };
1398 bool declaration = false;
1399 Dwarf_Attribute attr_mem;
1400 dwarf_formflag(dwarf_attr(die, DW_AT_declaration,
1401 &attr_mem), &declaration);
1402 if (!declaration) {
1403 // let's be curious and look deeper in the tree,
1404 // function are not necessarily at the first level, but
1405 // might be nested inside a namespace, structure etc.
1406 Dwarf_Die die_mem;
1407 Dwarf_Die* indie = find_fundie_by_pc(die, pc, &die_mem);
1408 if (indie) {
1409 *result = die_mem;
1410 return result;
1411 }
1412 }
1413 } while (dwarf_siblingof(die, result) == 0);
1414 return 0;
1415 }
1416
1417 template <typename CB>
1418 static bool deep_first_search_by_pc(Dwarf_Die* parent_die,
1419 Dwarf_Addr pc, CB cb) {
1420 Dwarf_Die die_mem;
1421 if (dwarf_child(parent_die, &die_mem) != 0) {
1422 return false;
1423 }
1424
1425 bool branch_has_pc = false;
1426 Dwarf_Die* die = &die_mem;
1427 do {
1428 bool declaration = false;
1429 Dwarf_Attribute attr_mem;
1430 dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), &declaration);
1431 if (!declaration) {
1432 // let's be curious and look deeper in the tree, function are
1433 // not necessarily at the first level, but might be nested
1434 // inside a namespace, structure, a function, an inlined
1435 // function etc.
1436 branch_has_pc = deep_first_search_by_pc(die, pc, cb);
1437 }
1438 if (!branch_has_pc) {
1439 branch_has_pc = die_has_pc(die, pc);
1440 }
1441 if (branch_has_pc) {
1442 cb(die);
1443 }
1444 } while (dwarf_siblingof(die, &die_mem) == 0);
1445 return branch_has_pc;
1446 }
1447
1448 static const char* die_call_file(Dwarf_Die *die) {
1449 Dwarf_Attribute attr_mem;
1450 Dwarf_Sword file_idx = 0;
1451
1452 dwarf_formsdata(dwarf_attr(die, DW_AT_call_file, &attr_mem),
1453 &file_idx);
1454
1455 if (file_idx == 0) {
1456 return 0;
1457 }
1458
1459 Dwarf_Die die_mem;
1460 Dwarf_Die* cudie = dwarf_diecu(die, &die_mem, 0, 0);
1461 if (!cudie) {
1462 return 0;
1463 }
1464
1465 Dwarf_Files* files = 0;
1466 size_t nfiles;
1467 dwarf_getsrcfiles(cudie, &files, &nfiles);
1468 if (!files) {
1469 return 0;
1470 }
1471
1472 return dwarf_filesrc(files, file_idx, 0, 0);
1473 }
1474
1475};
1476#endif // BACKWARD_HAS_DW == 1
1477
1478template<>
1479class TraceResolverImpl<system_tag::linux_tag>:
1480 public TraceResolverLinuxImpl<trace_resolver_tag::current> {};
1481
1482#endif // BACKWARD_SYSTEM_LINUX
1483
1485 public TraceResolverImpl<system_tag::current_tag> {};
1486
1487/*************** CODE SNIPPET ***************/
1488
1490public:
1491 using lines_t = std::vector<std::pair<unsigned, std::string>>;
1492
1494 SourceFile(const std::string& path): _file(new std::ifstream(path.c_str())) {}
1495 bool is_open() const { return _file->is_open(); }
1496
1497 lines_t& get_lines(unsigned line_start, unsigned line_count, lines_t& lines) {
1498 using namespace std;
1499 // This function make uses of the dumbest algo ever:
1500 // 1) seek(0)
1501 // 2) read lines one by one and discard until line_start
1502 // 3) read line one by one until line_start + line_count
1503 //
1504 // If you are getting snippets many time from the same file, it is
1505 // somewhat a waste of CPU, feel free to benchmark and propose a
1506 // better solution ;)
1507
1508 _file->clear();
1509 _file->seekg(0);
1510 string line;
1511 unsigned line_idx;
1512
1513 for (line_idx = 1; line_idx < line_start; ++line_idx) {
1514 std::getline(*_file, line);
1515 if (!*_file) {
1516 return lines;
1517 }
1518 }
1519
1520 // think of it like a lambda in C++98 ;)
1521 // but look, I will reuse it two times!
1522 // What a good boy am I.
1523 struct isspace {
1524 bool operator()(char c) {
1525 return std::isspace(c);
1526 }
1527 };
1528
1529 bool started = false;
1530 for (; line_idx < line_start + line_count; ++line_idx) {
1531 getline(*_file, line);
1532 if (!*_file) {
1533 return lines;
1534 }
1535 if (!started) {
1536 if (std::find_if(line.begin(), line.end(),
1537 not_isspace()) == line.end())
1538 continue;
1539 started = true;
1540 }
1541 lines.push_back(make_pair(line_idx, line));
1542 }
1543
1544 lines.erase(
1545 std::find_if(lines.rbegin(), lines.rend(),
1546 not_isempty()).base(), lines.end()
1547 );
1548 return lines;
1549 }
1550
1551 lines_t get_lines(unsigned line_start, unsigned line_count) {
1552 lines_t lines;
1553 return get_lines(line_start, line_count, lines);
1554 }
1555
1556 // there is no find_if_not in C++98, lets do something crappy to
1557 // workaround.
1559 bool operator()(char c) {
1560 return !std::isspace(c);
1561 }
1562 };
1563 // and define this one here because C++98 is not happy with local defined
1564 // struct passed to template functions, fuuuu.
1566 bool operator()(const lines_t::value_type& p) {
1567 return !(std::find_if(p.second.begin(), p.second.end(),
1568 not_isspace()) == p.second.end());
1569 }
1570 };
1571
1572 void swap(SourceFile& b) {
1573 _file.swap(b._file);
1574 }
1575
1576#ifdef BACKWARD_ATLEAST_CXX11
1577 SourceFile(SourceFile&& from): _file(0) {
1578 swap(from);
1579 }
1581 swap(from); return *this;
1582 }
1583#else
1584 explicit SourceFile(const SourceFile& from) {
1585 // some sort of poor man's move semantic.
1586 swap(const_cast<SourceFile&>(from));
1587 }
1589 // some sort of poor man's move semantic.
1590 swap(const_cast<SourceFile&>(from)); return *this;
1591 }
1592#endif
1593
1594private:
1595 details::handle<std::ifstream*,
1598
1599#ifdef BACKWARD_ATLEAST_CXX11
1600 SourceFile(const SourceFile&) = delete;
1601 SourceFile& operator=(const SourceFile&) = delete;
1602#endif
1603};
1604
1606public:
1608
1609 lines_t get_snippet(const std::string& filename,
1610 unsigned line_start, unsigned context_size) {
1611
1612 SourceFile& src_file = get_src_file(filename);
1613 unsigned start = line_start - context_size / 2;
1614 return src_file.get_lines(start, context_size);
1615 }
1616
1618 const std::string& filename_a, unsigned line_a,
1619 const std::string& filename_b, unsigned line_b,
1620 unsigned context_size) {
1621 SourceFile& src_file_a = get_src_file(filename_a);
1622 SourceFile& src_file_b = get_src_file(filename_b);
1623
1624 lines_t lines = src_file_a.get_lines(line_a - context_size / 4,
1625 context_size / 2);
1626 src_file_b.get_lines(line_b - context_size / 4, context_size / 2,
1627 lines);
1628 return lines;
1629 }
1630
1631 lines_t get_coalesced_snippet(const std::string& filename,
1632 unsigned line_a, unsigned line_b, unsigned context_size) {
1633 SourceFile& src_file = get_src_file(filename);
1634
1635 using std::min; using std::max;
1636 unsigned a = min(line_a, line_b);
1637 unsigned b = max(line_a, line_b);
1638
1639 if ((b - a) < (context_size / 3)) {
1640 return src_file.get_lines((a + b - context_size + 1) / 2,
1641 context_size);
1642 }
1643
1644 lines_t lines = src_file.get_lines(a - context_size / 4,
1645 context_size / 2);
1646 src_file.get_lines(b - context_size / 4, context_size / 2, lines);
1647 return lines;
1648 }
1649
1650
1651private:
1654
1655 SourceFile& get_src_file(const std::string& filename) {
1656 src_files_t::iterator it = _src_files.find(filename);
1657 if (it != _src_files.end()) {
1658 return it->second;
1659 }
1660 SourceFile& new_src_file = _src_files[filename];
1661 new_src_file = SourceFile(filename);
1662 return new_src_file;
1663 }
1664};
1665
1666/*************** PRINTER ***************/
1667
1668#ifdef BACKWARD_SYSTEM_LINUX
1669
1670namespace Color {
1671 enum type {
1672 yellow = 33,
1673 purple = 35,
1674 reset = 39
1675 };
1676}
1677
1678class Colorize {
1679public:
1680 Colorize(std::ostream& os):
1681 _os(os), _reset(false), _use_colors(false) {}
1682
1683 void activate() {
1684 _use_colors = true;
1685 // in a colorful environment, reset at the beginning
1687 }
1688
1689 void activate_if_tty(std::FILE *desc) {
1690 if (isatty(fileno(desc))) {
1691 activate();
1692 }
1693 }
1694
1695 void set_color(Color::type ccode) {
1696 if (!_use_colors) return;
1697
1698 // I assume that the terminal can handle basic colors. Seriously I
1699 // don't want to deal with all the termcap shit.
1700 _os << "\033[" << static_cast<int>(ccode) << "m";
1701 _reset = (ccode != Color::reset);
1702 }
1703
1704 ~Colorize() {
1705 if (_reset) {
1707 }
1708 }
1709
1710private:
1711 std::ostream& _os;
1712 bool _reset;
1713 bool _use_colors;
1714};
1715
1716#else // ndef BACKWARD_SYSTEM_LINUX
1717
1718
1719namespace Color {
1720 enum type {
1723 reset = 0
1725}
1726
1728public:
1729 Colorize(std::ostream&) {}
1730 void activate() {}
1733};
1734
1735#endif // BACKWARD_SYSTEM_LINUX
1736
1737class Printer {
1738public:
1740 bool color;
1745
1747 snippet(true),
1748 color(true),
1749 address(false),
1750 object(false),
1753 {}
1754
1755 template <typename ST>
1756 FILE* print(ST& st, FILE* os = stderr) {
1757 std::stringstream ss;
1758 Colorize colorize(ss);
1759 if (color) {
1760 colorize.activate_if_tty(os);
1761 }
1762 print(st, ss, colorize);
1763 fprintf(os, "%s", ss.str().c_str());
1764 return os;
1765 }
1766
1767 template <typename ST>
1768 void print(ST& st, std::ostream& os) {
1769 Colorize colorize(os);
1770 if (color) {
1771 colorize.activate();
1772 }
1773 print(st, os, colorize);
1774 }
1775
1776 template <typename IT>
1777 FILE* print(IT begin, IT end, FILE* os = stderr, size_t thread_id = 0) {
1778 std::stringstream ss;
1779 Colorize colorize(ss);
1780 if (color) {
1781 colorize.activate_if_tty(os);
1782 }
1783 print(begin, end, ss, thread_id, colorize);
1784 fprintf(os, "%s", ss.str().c_str());
1785 return os;
1786 }
1787
1788 template <typename IT>
1789 void print(IT begin, IT end, std::ostream& os, size_t thread_id = 0) {
1790 Colorize colorize(os);
1791 if (color) {
1792 colorize.activate();
1793 }
1794 print(begin, end, os, thread_id, colorize);
1795 }
1796
1797private:
1800
1801 template <typename ST>
1802 void print(ST& st, std::ostream& os, Colorize& colorize) {
1803 print_header(os, st.thread_id());
1804 _resolver.load_stacktrace(st);
1805 for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) {
1806 print_trace(os, _resolver.resolve(st[trace_idx-1]), colorize);
1807 }
1808 }
1809
1810 template <typename IT>
1811 void print(IT begin, IT end, std::ostream& os, size_t thread_id, Colorize& colorize) {
1812 print_header(os, thread_id);
1813 for (; begin != end; ++begin) {
1814 print_trace(os, *begin, colorize);
1815 }
1816 }
1817
1818 void print_header(std::ostream& os, unsigned thread_id) {
1819 os << "Stack trace (most recent call last)";
1820 if (thread_id) {
1821 os << " in thread " << thread_id;
1822 }
1823 os << ":\n";
1824 }
1825
1826 void print_trace(std::ostream& os, const ResolvedTrace& trace,
1827 Colorize& colorize) {
1828 os << "#"
1829 << std::left << std::setw(2) << trace.idx
1830 << std::right;
1831 bool already_indented = true;
1832
1833 if (!trace.source.filename.size() || object) {
1834 os << " Object \""
1835 << trace.object_filename
1836 << ", at "
1837 << trace.addr
1838 << ", in "
1839 << trace.object_function
1840 << "\n";
1841 already_indented = false;
1842 }
1843
1844 for (size_t inliner_idx = trace.inliners.size();
1845 inliner_idx > 0; --inliner_idx) {
1846 if (!already_indented) {
1847 os << " ";
1848 }
1849 const ResolvedTrace::SourceLoc& inliner_loc
1850 = trace.inliners[inliner_idx-1];
1851 print_source_loc(os, " | ", inliner_loc);
1852 if (snippet) {
1853 print_snippet(os, " | ", inliner_loc,
1855 }
1856 already_indented = false;
1857 }
1858
1859 if (trace.source.filename.size()) {
1860 if (!already_indented) {
1861 os << " ";
1862 }
1863 print_source_loc(os, " ", trace.source, trace.addr);
1864 if (snippet) {
1865 print_snippet(os, " ", trace.source,
1867 }
1868 }
1869 }
1870
1871 void print_snippet(std::ostream& os, const char* indent,
1872 const ResolvedTrace::SourceLoc& source_loc,
1873 Colorize& colorize, Color::type color_code,
1874 int context_size)
1875 {
1876 using namespace std;
1877 using lines_t = SnippetFactory::lines_t;
1878
1879 lines_t lines = _snippets.get_snippet(source_loc.filename,
1880 source_loc.line, context_size);
1881
1882 for (lines_t::const_iterator it = lines.begin();
1883 it != lines.end(); ++it) {
1884 if (it-> first == source_loc.line) {
1885 colorize.set_color(color_code);
1886 os << indent << ">";
1887 } else {
1888 os << indent << " ";
1889 }
1890 os << std::setw(4) << it->first
1891 << ": "
1892 << it->second
1893 << "\n";
1894 if (it-> first == source_loc.line) {
1895 colorize.set_color(Color::reset);
1896 }
1897 }
1898 }
1899
1900 void print_source_loc(std::ostream& os, const char* indent,
1901 const ResolvedTrace::SourceLoc& source_loc,
1902 void* addr=0) {
1903 os << indent
1904 << "Source \""
1905 << source_loc.filename
1906 << "\", line "
1907 << source_loc.line
1908 << ", in "
1909 << source_loc.function;
1910
1911 if (address && addr != 0) {
1912 os << " [" << addr << "]";
1913 }
1914 os << "\n";
1915 }
1916};
1917
1918/*************** SIGNALS HANDLING ***************/
1919
1920#ifdef BACKWARD_SYSTEM_LINUX
1921
1922
1923class SignalHandling {
1924public:
1925 static std::vector<int> make_default_signals() {
1926 const int posix_signals[] = {
1927 // default action: Core
1928 SIGILL,
1929 SIGABRT,
1930 SIGFPE,
1931 SIGSEGV,
1932 SIGBUS,
1933 // I am not sure the following signals should be enabled by
1934 // default:
1935 // default action: Term
1936 SIGHUP,
1937 SIGINT,
1938 SIGPIPE,
1939 SIGALRM,
1940 SIGTERM,
1941 SIGUSR1,
1942 SIGUSR2,
1943 SIGPOLL,
1944 SIGPROF,
1945 SIGVTALRM,
1946 SIGIO,
1947 SIGPWR,
1948 // default action: Core
1949 SIGQUIT,
1950 SIGSYS,
1951 SIGTRAP,
1952 SIGXCPU,
1953 SIGXFSZ
1954 };
1955 return std::vector<int>(posix_signals, posix_signals + sizeof posix_signals / sizeof posix_signals[0] );
1956 }
1957
1958 SignalHandling(const std::vector<int>& posix_signals = make_default_signals()):
1959 _loaded(false) {
1960 bool success = true;
1961
1962 const size_t stack_size = 1024 * 1024 * 8;
1963 _stack_content.reset((char*)malloc(stack_size));
1964 if (_stack_content) {
1965 stack_t ss;
1966 ss.ss_sp = _stack_content.get();
1967 ss.ss_size = stack_size;
1968 ss.ss_flags = 0;
1969 if (sigaltstack(&ss, 0) < 0) {
1970 success = false;
1971 }
1972 } else {
1973 success = false;
1974 }
1975
1976 for (size_t i = 0; i < posix_signals.size(); ++i) {
1977 struct sigaction action;
1978 memset(&action, 0, sizeof action);
1979 action.sa_flags = (SA_SIGINFO | SA_ONSTACK | SA_NODEFER |
1980 SA_RESETHAND);
1981 sigfillset(&action.sa_mask);
1982 sigdelset(&action.sa_mask, posix_signals[i]);
1983 action.sa_sigaction = &sig_handler;
1984
1985 int r = sigaction(posix_signals[i], &action, 0);
1986 if (r < 0) success = false;
1987 }
1988
1989 _loaded = success;
1990 }
1991
1992 bool loaded() const { return _loaded; }
1993
1994private:
1995 details::handle<char*> _stack_content;
1996 bool _loaded;
1997
1998 static void sig_handler(int, siginfo_t* info, void* _ctx) {
1999 ucontext_t *uctx = (ucontext_t*) _ctx;
2000
2001 StackTrace st;
2002 void* error_addr = 0;
2003#ifdef REG_RIP // x86_64
2004 error_addr = reinterpret_cast<void*>(uctx->uc_mcontext.gregs[REG_RIP]);
2005#elif defined(REG_EIP) // x86_32
2006 error_addr = reinterpret_cast<void*>(uctx->uc_mcontext.gregs[REG_EIP]);
2007#elif defined(__arm__)
2008 error_addr = reinterpret_cast<void*>(uctx->uc_mcontext.arm_pc);
2009#else
2010# warning ":/ sorry, ain't know no nothing none not of your architecture!"
2011#endif
2012 if (error_addr) {
2013 st.load_from(error_addr, 32);
2014 } else {
2015 st.load_here(32);
2016 }
2017
2018 Printer printer;
2019 printer.address = true;
2020 printer.print(st, stderr);
2021
2022#if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
2023 psiginfo(info, 0);
2024#endif
2025
2026 // try to forward the signal.
2027 raise(info->si_signo);
2028
2029 // terminate the process immediately.
2030 puts("watf? exit");
2031 _exit(EXIT_FAILURE);
2032 }
2033};
2034
2035#endif // BACKWARD_SYSTEM_LINUX
2036
2037#ifdef BACKWARD_SYSTEM_UNKNOWN
2038
2040public:
2041 SignalHandling(const std::vector<int>& = std::vector<int>()) {}
2042 bool init() { return false; }
2043 bool loaded() { return false; }
2044};
2045
2046#endif // BACKWARD_SYSTEM_UNKNOWN
2047
2048}
2049
2050#endif /* H_GUARD */
Colorize(std::ostream &)
void set_color(Color::type)
void print_header(std::ostream &os, unsigned thread_id)
void print_source_loc(std::ostream &os, const char *indent, const ResolvedTrace::SourceLoc &source_loc, void *addr=0)
FILE * print(ST &st, FILE *os=stderr)
TraceResolver _resolver
void print(IT begin, IT end, std::ostream &os, size_t thread_id, Colorize &colorize)
void print(IT begin, IT end, std::ostream &os, size_t thread_id=0)
FILE * print(IT begin, IT end, FILE *os=stderr, size_t thread_id=0)
void print(ST &st, std::ostream &os, Colorize &colorize)
void print(ST &st, std::ostream &os)
SnippetFactory _snippets
void print_trace(std::ostream &os, const ResolvedTrace &trace, Colorize &colorize)
void print_snippet(std::ostream &os, const char *indent, const ResolvedTrace::SourceLoc &source_loc, Colorize &colorize, Color::type color_code, int context_size)
SignalHandling(const std::vector< int > &=std::vector< int >())
SourceFile & get_src_file(const std::string &filename)
SourceFile::lines_t lines_t
lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, unsigned line_b, unsigned context_size)
lines_t get_snippet(const std::string &filename, unsigned line_start, unsigned context_size)
details::hashtable< std::string, SourceFile >::type src_files_t
lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, const std::string &filename_b, unsigned line_b, unsigned context_size)
lines_t get_lines(unsigned line_start, unsigned line_count)
bool is_open() const
std::vector< std::pair< unsigned, std::string > > lines_t
SourceFile & operator=(const SourceFile &from)
SourceFile(const SourceFile &from)
void swap(SourceFile &b)
lines_t & get_lines(unsigned line_start, unsigned line_count, lines_t &lines)
details::handle< std::ifstream *, details::default_delete< std::ifstream * > > _file
SourceFile(const std::string &path)
Trace operator[](size_t)
Definition backward.hpp:500
size_t load_from(void *, size_t=0)
Definition backward.hpp:502
size_t load_here(size_t=0)
Definition backward.hpp:501
void skip_n_firsts(size_t)
Definition backward.hpp:504
unsigned thread_id() const
Definition backward.hpp:503
handle(const handle &from)
Definition backward.hpp:345
const typename rm_ptr< T >::type & const_ref_t
Definition backward.hpp:383
handle & operator=(const handle &from)
Definition backward.hpp:349
ref_t operator[](size_t idx)
Definition backward.hpp:386
const_ref_t operator*() const
Definition backward.hpp:385
typename rm_ptr< T >::type & ref_t
Definition backward.hpp:382
void reset(T new_val)
Definition backward.hpp:355
const T operator->() const
Definition backward.hpp:380
Basic configuration file.
const T & move(const T &v)
Definition backward.hpp:247
unknown_tag current_tag
Definition backward.hpp:264
HypergraphRegistry< HypernodeElement >::iterator begin(const HypergraphRegistry< HypernodeElement > &self)
Definition GML.h:111
bool operator==(const SourceLoc &b) const
Definition backward.hpp:456
bool operator!=(const SourceLoc &b) const
Definition backward.hpp:463
ResolvedTrace(const Trace &mini_trace)
Definition backward.hpp:489
std::vector< SourceLoc > source_locs_t
Definition backward.hpp:484
std::string object_filename
Definition backward.hpp:469
source_locs_t inliners
Definition backward.hpp:485
std::string object_function
Definition backward.hpp:473
bool operator()(const lines_t::value_type &p)
Trace(void *_addr, size_t _idx)
Definition backward.hpp:442
void operator()(U &ptr) const
Definition backward.hpp:304
static std::string demangle(const char *funcname)
Definition backward.hpp:398