Exception class Exception with backtrace and demangle, backtracedemangle
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 the-rdynamic option is added during compilation.
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 }
JavalangNullPointerException exception. No stackTrace information is output.
There is definitely a variable in your program that has no value in use, leading to a null pointer. You can debug it with the debug tool to see which variable has an exception.