C ++ exception classes have no trace of stacks. To obtain stack traces, use the following functions:
#include <execinfo.h>int backtrace(void **buffer, int size);char **backtrace_symbols(void *const *buffer, int size);void backtrace_symbols_fd(void *const *buffer, int size, int fd);
Backtrace stores the call information of the current program in the buffer, while backtrace_symbols translates the buffer into a string. The latter uses malloc, so the memory needs to be manually released.
The following code is provided in the man manual:
#include <execinfo.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>voidmyfunc3(void){ int j, nptrs;#define SIZE 100 void *buffer[100]; char **strings; nptrs = backtrace(buffer, SIZE); printf("backtrace() returned %d addresses\n", nptrs); /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf("%s\n", strings[j]); free(strings);}static void /* "static" means don‘t export the symbol... */myfunc2(void){ myfunc3();}voidmyfunc(int ncalls){ if (ncalls > 1) myfunc(ncalls - 1); else myfunc2();}intmain(int argc, char *argv[]){ if (argc != 2) { fprintf(stderr, "%s num-calls\n", argv[0]); exit(EXIT_FAILURE); } myfunc(atoi(argv[1])); exit(EXIT_SUCCESS);}
Compile and execute:
$ cc -rdynamic prog.c -o prog$ ./prog 3
The output is as follows:
backtrace() returned 8 addresses./prog(myfunc3+0x1f) [0x8048783]./prog() [0x8048810]./prog(myfunc+0x21) [0x8048833]./prog(myfunc+0x1a) [0x804882c]./prog(myfunc+0x1a) [0x804882c]./prog(main+0x52) [0x8048887]/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb76174d3]./prog() [0x80486d1]
Therefore, I wrote the following exception classes. Note that the above printed results have been renamed, so we use Abi :__ cxa_demangle to restore the names.
Exception. h
# Ifndef exception_h _ # define exception_h _ # include <string> # include <exception> class exception: public STD: exception {public: explicit exception (const char * What ); explicit exception (const STD: string & what); Virtual ~ Exception () Throw (); Virtual const char * What () const throw (); const char * stacktrace () const throw (); Private: void fillstacktrace (); // fill in stack trace STD: String demangle (const char * symbol); // anti-name adaptation STD: String message _; // exception information STD: String Stack _; // stack trace}; # endif // exception_h _
Exception. cpp
# Include "exception. H "# include <cxxabi. h> # include <execinfo. h> # include <stdlib. h> # include <stdio. h> using namespace STD; Exception: exception (const char * MSG): Message _ (MSG) {fillstacktrace ();} exception: exception (const string & MSG ): message _ (MSG) {fillstacktrace ();} exception ::~ Exception () Throw () {} const char * exception: What () const throw () {return message _. c_str ();} const char * exception: stacktrace () const throw () {return stack _. c_str ();} // fill stack trace void exception: fillstacktrace () {const int Len = 200; void * buffer [Len]; int nptrs =: backtrace (buffer, len); // list the call relationships of the current function // convert the information obtained from the backtrace function into a string array char ** strings =: backtrace_symbols (buffer, nptrs ); if (strings) {(INT I = 0; I <nptrs; ++ I) {// todo demangle funcion name with ABI :: __cxa_demangle // strings [I] indicates the Call trace of a layer. Stack _. append (demangle (strings [I]); stack _. push_back ('\ n');} Free (strings) ;}// string exception: demangle (const char * symbol) {size_t size; int status; char temp [128]; char * demangled; // first, try to demangle a c ++ name if (1 = sscanf (symbol, "% * [^ (] % * [^ _] % 127 [^) +]", temp) {If (n Ull! = (Demangled = Abi ::__ cxa_demangle (temp, null, & size, & Status) {string result (demangled); free (demangled); return result ;}} // if that didn't work, try to get a regular C symbol if (1 = sscanf (symbol, "% 127 S", temp) {return temp ;} // if all else fails, just return the symbol return symbol ;}
The test code is as follows:
#include "Exception.h"#include <stdio.h>using namespace std;class Bar{ public: void test() { throw Exception("oops"); }};void foo(){ Bar b; b.test();}int main(){ try { foo(); } catch (const Exception& ex) { printf("reason: %s\n", ex.what()); printf("stack trace: %s\n", ex.stackTrace()); }}
The output is as follows:
reason: oopsstack trace: Exception::fillStackTrace()Exception::Exception(char const*)Bar::test()foo()./a.out(main+0xf)/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)./a.out()
Note that during compilation,Add the-rdynamic Option
With this class, we can handle exceptions like this in the program:
try { // } catch (const Exception& ex) { fprintf(stderr, "reason: %s\n", ex.what()); fprintf(stderr, "stack trace: %s\n", ex.stackTrace()); abort(); } catch (const std::exception& ex) { fprintf(stderr, "reason: %s\n", ex.what()); abort(); } catch (...) { fprintf(stderr, "unknown exception caught \n"); throw; // rethrow }
Exception class exception with backtrace and demangle