]>
git.saurik.com Git - wxWidgets.git/blob - src/unix/stackwalk.cpp
   1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/unix/stackwalk.cpp 
   3 // Purpose:     wxStackWalker implementation for Unix/glibc 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2005 Vadim Zeitlin <vadim@wxwindows.org> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  29     #include "wx/string.h" 
  35 #include "wx/stackwalk.h" 
  36 #include "wx/stdpaths.h" 
  40 #ifdef HAVE_CXA_DEMANGLE 
  42 #endif // HAVE_CXA_DEMANGLE 
  44 // ---------------------------------------------------------------------------- 
  45 // tiny helper wrapper around popen/pclose() 
  46 // ---------------------------------------------------------------------------- 
  51     // ctor parameters are passed to popen() 
  52     wxStdioPipe(const char *command
, const char *type
) 
  54         m_fp 
= popen(command
, type
); 
  57     // conversion to stdio FILE 
  58     operator FILE *() const { return m_fp
; } 
  60     // dtor closes the pipe 
  70     wxDECLARE_NO_COPY_CLASS(wxStdioPipe
); 
  73 // ============================================================================ 
  75 // ============================================================================ 
  77 // ---------------------------------------------------------------------------- 
  79 // ---------------------------------------------------------------------------- 
  81 void wxStackFrame::OnGetName() 
  83     if ( !m_name
.empty() ) 
  86     // we already tried addr2line in wxStackWalker::InitFrames: it always 
  87     // gives us demangled names (even if __cxa_demangle is not available) when 
  88     // the function is part of the ELF (when it's in a shared object addr2line 
  89     // will give "??") and because it seems less error-prone. 
  90     // when it works, backtrace_symbols() sometimes returns incorrect results 
  92     // format is: "module(funcname+offset) [address]" but the part in 
  93     // parentheses can be not present 
  94     wxString syminfo 
= wxString::FromAscii(m_syminfo
); 
  95     const size_t posOpen 
= syminfo
.find(wxT('(')); 
  96     if ( posOpen 
!= wxString::npos 
) 
  98         const size_t posPlus 
= syminfo
.find(wxT('+'), posOpen 
+ 1); 
  99         if ( posPlus 
!= wxString::npos 
) 
 101             const size_t posClose 
= syminfo
.find(wxT(')'), posPlus 
+ 1); 
 102             if ( posClose 
!= wxString::npos 
) 
 104                 if ( m_name
.empty() ) 
 106                     m_name
.assign(syminfo
, posOpen 
+ 1, posPlus 
- posOpen 
- 1); 
 108 #ifdef HAVE_CXA_DEMANGLE 
 110                     char *cppfunc 
= __cxxabiv1::__cxa_demangle
 
 113                                         NULL
, // output buffer (none, alloc it) 
 114                                         NULL
, // [out] len of output buffer 
 118                         m_name 
= wxString::FromAscii(cppfunc
); 
 121 #endif // HAVE_CXA_DEMANGLE 
 125                 if ( wxString(syminfo
, posPlus 
+ 1, posClose 
- posPlus 
- 1). 
 131         m_module
.assign(syminfo
, posOpen
); 
 133     else // not in "module(funcname+offset)" format 
 140 // ---------------------------------------------------------------------------- 
 142 // ---------------------------------------------------------------------------- 
 144 // that many frames should be enough for everyone 
 145 #define MAX_FRAMES          200 
 147 // we need a char buffer big enough to contain a call to addr2line with 
 148 // up to MAX_FRAMES addresses ! 
 149 // NB: %p specifier will print the pointer in hexadecimal form 
 150 //     and thus will require 2 chars for each byte + 3 for the 
 152 #define CHARS_PER_FRAME    (sizeof(void*) * 2 + 3) 
 154 // BUFSIZE will be 2250 for 32 bit machines 
 155 #define BUFSIZE            (50 + MAX_FRAMES*CHARS_PER_FRAME) 
 158 void *wxStackWalker::ms_addresses
[MAX_FRAMES
]; 
 159 char **wxStackWalker::ms_symbols 
= NULL
; 
 160 int wxStackWalker::m_depth 
= 0; 
 161 wxString 
wxStackWalker::ms_exepath
; 
 162 static char g_buf
[BUFSIZE
]; 
 165 void wxStackWalker::SaveStack(size_t maxDepth
) 
 167     // read all frames required 
 168     maxDepth 
= wxMin(WXSIZEOF(ms_addresses
)/sizeof(void*), maxDepth
); 
 169     m_depth 
= backtrace(ms_addresses
, maxDepth
*sizeof(void*)); 
 173     ms_symbols 
= backtrace_symbols(ms_addresses
, m_depth
); 
 176 void wxStackWalker::ProcessFrames(size_t skip
) 
 178     wxStackFrame frames
[MAX_FRAMES
]; 
 180     if (!ms_symbols 
|| !m_depth
) 
 183     // we have 3 more "intermediate" frames which the calling code doesn't know 
 184     // about, account for them 
 187     // call addr2line only once since this call may be very slow 
 188     // (it has to load in memory the entire EXE of this app which may be quite 
 189     //  big, especially if it contains debug info and is compiled statically!) 
 190     int towalk 
= InitFrames(frames
, m_depth 
- skip
, &ms_addresses
[skip
], &ms_symbols
[skip
]); 
 192     // now do user-defined operations on each frame 
 193     for ( int n 
= 0; n 
< towalk 
- (int)skip
; n
++ ) 
 194         OnStackFrame(frames
[n
]); 
 197 void wxStackWalker::FreeStack() 
 199     // ms_symbols has been allocated by backtrace_symbols() and it's the responsibility 
 200     // of the caller, i.e. us, to free that pointer 
 207 int wxStackWalker::InitFrames(wxStackFrame 
*arr
, size_t n
, void **addresses
, char **syminfo
) 
 209     // we need to launch addr2line tool to get this information and we need to 
 210     // have the program name for this 
 211     wxString exepath 
= wxStackWalker::GetExePath(); 
 212     if ( exepath
.empty() ) 
 214         exepath 
= wxStandardPaths::Get().GetExecutablePath(); 
 215         if ( exepath
.empty() ) 
 217             wxLogDebug(wxT("Cannot parse stack frame because the executable ") 
 218                        wxT("path could not be detected")); 
 223     // build the (long) command line for executing addr2line in an optimized way 
 224     // (e.g. use always chars, even in Unicode build: popen() always takes chars) 
 225     int len 
= snprintf(g_buf
, BUFSIZE
, "addr2line -C -f -e \"%s\"", (const char*) exepath
.mb_str()); 
 226     len 
= (len 
<= 0) ? strlen(g_buf
) : len
;     // in case snprintf() is broken 
 227     for (size_t i
=0; i
<n
; i
++) 
 229         snprintf(&g_buf
[len
], BUFSIZE 
- len
, " %p", addresses
[i
]); 
 233     //wxLogDebug(wxT("piping the command '%s'"), g_buf);  // for debug only 
 235     wxStdioPipe 
fp(g_buf
, "r"); 
 239     // parse addr2line output (should be exactly 2 lines for each address) 
 240     // reusing the g_buf used for building the command line above 
 241     wxString name
, filename
; 
 242     unsigned long line 
= 0, 
 244     for  ( size_t i 
= 0; i 
< n
; i
++ ) 
 246         // 1st line has function name 
 247         if ( fgets(g_buf
, WXSIZEOF(g_buf
), fp
) ) 
 249             name 
= wxString::FromAscii(g_buf
); 
 250             name
.RemoveLast(); // trailing newline 
 252             if ( name 
== wxT("??") ) 
 257             wxLogDebug(wxT("cannot read addr2line output for stack frame #%lu"), 
 262         // 2nd one -- the file/line info 
 263         if ( fgets(g_buf
, WXSIZEOF(g_buf
), fp
) ) 
 265             filename 
= wxString::FromAscii(g_buf
); 
 266             filename
.RemoveLast(); 
 268             const size_t posColon 
= filename
.find(wxT(':')); 
 269             if ( posColon 
!= wxString::npos 
) 
 271                 // parse line number (it's ok if it fails, this will just leave 
 272                 // line at its current, invalid, 0 value) 
 273                 wxString(filename
, posColon 
+ 1, wxString::npos
).ToULong(&line
); 
 275                 // remove line number from 'filename' 
 276                 filename
.erase(posColon
); 
 277                 if ( filename 
== wxT("??") ) 
 282                 wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ") 
 283                            wxT("the semicolon is missing"), 
 288         // now we've got enough info to initialize curr-th stack frame 
 289         // (at worst, only addresses[i] and syminfo[i] have been initialized, 
 290         //  but wxStackFrame::OnGetName may still be able to get function name): 
 291         arr
[curr
++].Set(name
, filename
, syminfo
[i
], i
, line
, addresses
[i
]); 
 297 void wxStackWalker::Walk(size_t skip
, size_t maxDepth
) 
 299     // read all frames required 
 309 #endif // wxUSE_STACKWALKER