VOOZH about

URL: https://en.wikibooks.org/wiki/Linux_Applications_Debugging_Techniques/The_call_stack

⇱ Linux Applications Debugging Techniques/The call stack - Wikibooks, open books for an open world


Jump to content
From Wikibooks, open books for an open world
This page may need to be reviewed for quality.

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&lt;40ul&gt;::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&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::init()+0x722
At??:0
Inbinaries/bin/papitest
0x426815lpt::papi::library&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::library()+0x5b
At??:0
Inbinaries/bin/papitest
0x4265d4lpt::singleton&lt;lpt::papi::library&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::singleton()+0x18
At??:0
Inbinaries/bin/papitest
0x42624elpt::singleton&lt;lpt::papi::library&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::instance()+0x42
At??:0
Inbinaries/bin/papitest
0x425b47lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::init()+0x25
At??:0
Inbinaries/bin/papitest
0x425502lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::thread()+0x2e
At??:0
Inbinaries/bin/papitest
0x424554lpt::singleton&lt;lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::singleton()+0x18
At??:0
Inbinaries/bin/papitest
0x4230a5lpt::singleton&lt;lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::instance()+0x42
At??:0
Inbinaries/bin/papitest
0x421d33lpt::papi::counters&lt;lpt::papi::stdout_print,lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::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&lt;40ul&gt;::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&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::init()+0x722
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:371
Inbinaries/bin/papitest
[0x426983]lpt::papi::library&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::library()+0x5b
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:289
Inbinaries/bin/papitest
[0x426742]lpt::singleton&lt;lpt::papi::library&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::singleton()+0x18
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:19
Inbinaries/bin/papitest
[0x4263bc]lpt::singleton&lt;lpt::papi::library&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::instance()+0x42
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:28
Inbinaries/bin/papitest
[0x425cb5]lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::init()+0x25
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:433
Inbinaries/bin/papitest
[0x425670]lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::thread()+0x2e
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/papi.hpp:409
Inbinaries/bin/papitest
[0x4246c2]lpt::singleton&lt;lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::singleton()+0x18
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:19
Inbinaries/bin/papitest
[0x423213]lpt::singleton&lt;lpt::papi::thread&lt;lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;&gt;::instance()+0x42
At/home/amelinte/projects/lpt/lpt/lpt/include/lpt/singleton.hpp:28
Inbinaries/bin/papitest
[0x421ea1]lpt::papi::counters&lt;lpt::papi::stdout_print,lpt::papi::counting_per_thread,lpt::papi::multiplex_none&gt;::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

Links

[edit | edit source]