Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

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