The API
[edit | edit source]Sometimes we need the call stack at a certain point in the program. These are the API functions to get basic stack information:
#include<execinfo.h> intbacktrace(void**buffer,intsize); char**backtrace_symbols(void*const*buffer,intsize); voidbacktrace_symbols_fd(void*const*buffer,intsize,intfd); #include<cxxabi.h> char*__cxa_demangle(constchar*__mangled_name,char*__output_buffer,size_t*__length,int*__status); #include<dlfcn.h> intdladdr(void*addr,Dl_info*info);
Notes:
- C++ symbols are still mangled. Use abi::__cxa_demangle() or something similar.
- Some of the these functions do allocate memory - either temporarily either explicitly - and this might be a problem if the program is unstable already.
- Some of the these functions do acquire locks (e.g. ).
- are expensive calls on some platforms (x86_64 for instance), the deeper the call stack is the more expensive the call is.
- will fail to rsolve any symbol that is not exported. Hence, to get the most out of it:
- Link with and
To extract more information (file, line number), use libbfd. Possible alternatives: libbacktrace; http://libcsdbg.sourceforge.net/
#define _GNU_SOURCE #include<stdio.h> #include<stdlib.h> #include<dlfcn.h> #include<execinfo.h> #include<signal.h> #include<bfd.h> #include<unistd.h> /* globals retained across calls to resolve. */ staticbfd*abfd=0; staticasymbol**syms=0; staticasection*text=0; staticvoidresolve(char*address){ if(!abfd){ charename[1024]; intl=readlink("/proc/self/exe",ename,sizeof(ename)); if(l==-1){ perror("failed to find executable\n"); return; } ename[l]=0; bfd_init(); abfd=bfd_openr(ename,0); if(!abfd){ perror("bfd_openr failed: "); return; } /* oddly, this is required for it to work... */ bfd_check_format(abfd,bfd_object); unsignedstorage_needed=bfd_get_symtab_upper_bound(abfd); syms=(asymbol**)malloc(storage_needed); unsignedcSymbols=bfd_canonicalize_symtab(abfd,syms); text=bfd_get_section_by_name(abfd,".text"); } longoffset=((long)address)-text->vma; if(offset>0){ constchar*file; constchar*func; unsignedline; if(bfd_find_nearest_line(abfd,text,syms,offset,&file,&func,&line)&&file) printf("file: %s, line: %u, func %s\n",file,line,func); } }
A C++ wrapper
[edit | edit source]With this simple class, getting the stack is one line away:
classcall_stack { public: staticconstintdepth=40; typedefstd::array<void*,depth>stack_t; classconst_iterator; classframe { public: frame(void*addr=0) :_addr(0) ,_dladdr_ret(false) ,_binary_name(0) ,_func_name(0) ,_demangled_func_name(0) ,_delta_sign('+') ,_delta(0L) ,_source_file_name(0) ,_line_number(0) { resolve(addr); } // frame(stack_t::iterator& it) : frame(*it) {} //C++0x frame(stack_t::const_iteratorconst&it) :_addr(0) ,_dladdr_ret(false) ,_binary_name(0) ,_func_name(0) ,_demangled_func_name(0) ,_delta_sign('+') ,_delta(0L) ,_source_file_name(0) ,_line_number(0) { resolve(*it); } frame(frameconst&other) { resolve(other._addr); } frame&operator=(frameconst&other) { if(this!=&other){ resolve(other._addr); } return*this; } ~frame() { resolve(0); } std::stringas_string()const { std::ostringstreams; s<<"["<<std::hex<<_addr<<"] " <<demangled_function() <<" ("<<binary_file()<<_delta_sign<<"0x"<<std::hex<<_delta<<")" <<" in "<<source_file()<<":"<<line_number() ; returns.str(); } constvoid*addr()const{return_addr;} constchar*binary_file()const{returnsafe(_binary_name);} constchar*function()const{returnsafe(_func_name);} constchar*demangled_function()const{returnsafe(_demangled_func_name);} chardelta_sign()const{return_delta_sign;} longdelta()const{return_delta;} constchar*source_file()const{returnsafe(_source_file_name);} intline_number()const{return_line_number;} private: constchar*safe(constchar*p)const{returnp?p:"??";} friendclassconst_iterator;// To call resolve() voidresolve(constvoid*addr) { if(_addr==addr) return; _addr=addr; _dladdr_ret=false; _binary_name=0; _func_name=0; if(_demangled_func_name){ free(_demangled_func_name); _demangled_func_name=0; } _delta_sign='+'; _delta=0L; _source_file_name=0; _line_number=0; if(!_addr) return; _dladdr_ret=(::dladdr(_addr,&_info)!=0); if(_dladdr_ret) { _binary_name=safe(_info.dli_fname); _func_name=safe(_info.dli_sname); _delta_sign=(_addr>=_info.dli_saddr)?'+':'-'; _delta=::labs(static_cast<constchar*>(_addr)-static_cast<constchar*>(_info.dli_saddr)); intstatus=0; _demangled_func_name=abi::__cxa_demangle(_func_name,0,0,&status); } } private: constvoid*_addr; constchar*_binary_name; constchar*_func_name; constchar*_demangled_func_name; char_delta_sign; long_delta; constchar*_source_file_name;//TODO: libbfd int_line_number; Dl_info_info; bool_dladdr_ret; };//frame classconst_iterator :publicstd::iterator<std::bidirectional_iterator_tag ,ptrdiff_t > { public: const_iterator(stack_t::const_iteratorconst&it) :_it(it) ,_frame(it) {} booloperator==(constconst_iterator&other)const { return_frame.addr()==other._frame.addr(); } booloperator!=(constconst_iterator&x)const { return!(*this==x); } constframe&operator*()const { return_frame; } constframe*operator->()const { return&_frame; } const_iterator&operator++() { ++_it; _frame.resolve(*_it); return*this; } const_iteratoroperator++(int) { const_iteratortmp=*this; ++_it; _frame.resolve(*_it); returntmp; } const_iterator&operator--() { --_it; _frame.resolve(*_it); return*this; } const_iteratoroperator--(int) { const_iteratortmp=*this; --_it; _frame.resolve(*_it); returntmp; } private: const_iterator(); private: frame_frame; stack_t::const_iterator_it; };//const_iterator call_stack():_num_frames(0) { _num_frames=::backtrace(_stack.data(),depth); assert(_num_frames>=0&&_num_frames<=depth); } std::stringas_string() { std::strings; const_iteratoritEnd=end(); for(const_iteratorit=begin();it!=itEnd;++it){ s+=it->as_string(); s+="\n"; } returnstd::move(s); } virtual~call_stack() { } const_iteratorbegin()const{return_stack.cbegin();} const_iteratorend()const{returnstack_t::const_iterator(&_stack[_num_frames]);} private: stack_t_stack; int_num_frames; };
This class can be used with assertions, logging or exceptions.
The full code can be found at the LPT site and it offers three symbol resolver varieties. This code will require a C++11 compiler.
- A "basic" address-only resolver, without memory side-effects
- A libc resolver
- A libbfd resolver
A boost-based version of the call stack utilities collection can be found at https://github.com/melintea/Boost-Call_stack.
A stack resolved with the libc resolver, embedded in an exception, with debug-compiled code would look like:
ExceptionPAPI_add_event:Eventexists,butcannotbecountedduetohardwareresourcelimits At: 0x41e11f??+0x41e11f At??:0 Inbinaries/bin/papitest 0x421ae0lpt::stack::call_stack<40ul>::call_stack(bool)+0x52 At??:0 Inbinaries/bin/papitest 0x420653lpt::papi::papi_error::papi_error(std::stringconst&,int)+0x6f At??:0 Inbinaries/bin/papitest 0x426ffelpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::init()+0x722 At??:0 Inbinaries/bin/papitest 0x426815lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::library()+0x5b At??:0 Inbinaries/bin/papitest 0x4265d4lpt::singleton<lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::singleton()+0x18 At??:0 Inbinaries/bin/papitest 0x42624elpt::singleton<lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::instance()+0x42 At??:0 Inbinaries/bin/papitest 0x425b47lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::init()+0x25 At??:0 Inbinaries/bin/papitest 0x425502lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::thread()+0x2e At??:0 Inbinaries/bin/papitest 0x424554lpt::singleton<lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::singleton()+0x18 At??:0 Inbinaries/bin/papitest 0x4230a5lpt::singleton<lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::instance()+0x42 At??:0 Inbinaries/bin/papitest 0x421d33lpt::papi::counters<lpt::papi::stdout_print,lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::counters(std::stringconst&)+0xc9 At??:0 Inbinaries/bin/papitest 0x41e206tests()+0x67 At??:0 Inbinaries/bin/papitest 0x41edb8??+0x11 At??:0 Inbinaries/bin/papitest 0x2ad66a7b9ead??+0xfd At??:0 In/lib/x86_64-linux-gnu/libc.so.6 0x41dea9??+0x41dea9 At??:0 Inbinaries/bin/papitest
Note how fails to resolve some of the frames. Whereas a stack resolved with the bfd resolver would show file and line numbers and would resolve all frames:
ExceptionPAPI_add_event:Eventexists,butcannotbecountedduetohardwareresourcelimits At: [0x41e20f]backtrace+0x41e20f At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/detail/glibc_call_stack.hpp:61 Inbinaries/bin/papitest [0x421c4e]lpt::stack::call_stack<40ul>::call_stack(bool)+0x52 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/call_stack.hpp:74 Inbinaries/bin/papitest [0x4207a1]lpt::papi::papi_error::papi_error(std::stringconst&,int)+0x6f At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:112 Inbinaries/bin/papitest [0x42716c]lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::init()+0x722 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:371 Inbinaries/bin/papitest [0x426983]lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::library()+0x5b At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:289 Inbinaries/bin/papitest [0x426742]lpt::singleton<lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::singleton()+0x18 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:19 Inbinaries/bin/papitest [0x4263bc]lpt::singleton<lpt::papi::library<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::instance()+0x42 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:28 Inbinaries/bin/papitest [0x425cb5]lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::init()+0x25 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:433 Inbinaries/bin/papitest [0x425670]lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::thread()+0x2e At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:409 Inbinaries/bin/papitest [0x4246c2]lpt::singleton<lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::singleton()+0x18 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:19 Inbinaries/bin/papitest [0x423213]lpt::singleton<lpt::papi::thread<lpt::papi::counting_per_thread,lpt::papi::multiplex_none>>::instance()+0x42 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:28 Inbinaries/bin/papitest [0x421ea1]lpt::papi::counters<lpt::papi::stdout_print,lpt::papi::counting_per_thread,lpt::papi::multiplex_none>::counters(std::stringconst&)+0xc9 At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:646 Inbinaries/bin/papitest [0x41e2f6]tests()+0x67 At/home/amelinte/projects/lpt/lpt/tests/papitest.cpp:51 Inbinaries/bin/papitest [0x41eea8]main+0x11 At/home/amelinte/projects/lpt/lpt/tests/papitest.cpp:176 Inbinaries/bin/papitest [0x2b5f7aa1bead]__libc_start_main+0xfd At??:0 In/lib/x86_64-linux-gnu/libc.so.6 [0x41df99]_start+0x41df99 At??:0 Inbinaries/bin/papitest
Caveats
[edit | edit source]- Do NOT use in asynchronous interrupts such as signal handlers.
- It might or it might not work when the program is unstable - prefer a core dump if possible. Same for memory issues handlers .
- Performance hit varies greatly with platform and compiler version.
From within gdb
[edit | edit source]A canned command to resolve a stack address from within gdb:
defineaddrtosym if$argc==1 printf"[%u]: ",$arg0 #whatis/ptype EXPR #info frame ADDR infosymbol$arg0 end end documentaddrtosym Resolvetheaddress(e.g.ofonestackframe).Usage:addrtosymaddr0 end
