1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/unix/stackwalk.cpp
3 // Purpose:     wxStackWalker implementation for Unix/glibc
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     2005-01-18
7 // Copyright:   (c) 2005 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 #include "wx/wxprec.h"
20 
21 
22 #if wxUSE_STACKWALKER
23 
24 #ifndef WX_PRECOMP
25     #include "wx/string.h"
26     #include "wx/app.h"
27     #include "wx/log.h"
28     #include "wx/utils.h"
29 #endif
30 
31 #include "wx/stackwalk.h"
32 #include "wx/stdpaths.h"
33 
34 #include <execinfo.h>
35 
36 #ifdef HAVE_CXA_DEMANGLE
37     #include <cxxabi.h>
38 #endif // HAVE_CXA_DEMANGLE
39 
40 // ----------------------------------------------------------------------------
41 // tiny helper wrapper around popen/pclose()
42 // ----------------------------------------------------------------------------
43 
44 class wxStdioPipe
45 {
46 public:
47     // ctor parameters are passed to popen()
wxStdioPipe(const char * command,const char * type)48     wxStdioPipe(const char *command, const char *type)
49     {
50         m_fp = popen(command, type);
51     }
52 
53     // conversion to stdio FILE
operator FILE*() const54     operator FILE *() const { return m_fp; }
55 
56     // dtor closes the pipe
~wxStdioPipe()57     ~wxStdioPipe()
58     {
59         if ( m_fp )
60             pclose(m_fp);
61     }
62 
63 private:
64     FILE *m_fp;
65 
66     wxDECLARE_NO_COPY_CLASS(wxStdioPipe);
67 };
68 
69 // ============================================================================
70 // implementation
71 // ============================================================================
72 
73 // ----------------------------------------------------------------------------
74 // wxStackFrame
75 // ----------------------------------------------------------------------------
76 
OnGetName()77 void wxStackFrame::OnGetName()
78 {
79     if ( !m_name.empty() )
80         return;
81 
82     // we already tried addr2line in wxStackWalker::InitFrames: it always
83     // gives us demangled names (even if __cxa_demangle is not available) when
84     // the function is part of the ELF (when it's in a shared object addr2line
85     // will give "??") and because it seems less error-prone.
86     // when it works, backtrace_symbols() sometimes returns incorrect results
87 
88     // format is: "module(funcname+offset) [address]" but the part in
89     // parentheses can be not present
90     wxString syminfo = wxString::FromAscii(m_syminfo);
91     const size_t posOpen = syminfo.find(wxT('('));
92     if ( posOpen != wxString::npos )
93     {
94         const size_t posPlus = syminfo.find(wxT('+'), posOpen + 1);
95         if ( posPlus != wxString::npos )
96         {
97             const size_t posClose = syminfo.find(wxT(')'), posPlus + 1);
98             if ( posClose != wxString::npos )
99             {
100                 if ( m_name.empty() )
101                 {
102                     m_name.assign(syminfo, posOpen + 1, posPlus - posOpen - 1);
103 
104 #ifdef HAVE_CXA_DEMANGLE
105                     int rc = -1;
106                     char *cppfunc = __cxxabiv1::__cxa_demangle
107                                     (
108                                         m_name.mb_str(),
109                                         NULL, // output buffer (none, alloc it)
110                                         NULL, // [out] len of output buffer
111                                         &rc
112                                     );
113                     if ( rc == 0 )
114                         m_name = wxString::FromAscii(cppfunc);
115 
116                     free(cppfunc);
117 #endif // HAVE_CXA_DEMANGLE
118                 }
119 
120                 unsigned long ofs;
121                 if ( wxString(syminfo, posPlus + 1, posClose - posPlus - 1).
122                         ToULong(&ofs, 0) )
123                     m_offset = ofs;
124             }
125         }
126 
127         m_module.assign(syminfo, posOpen);
128     }
129 #ifndef __WXOSX__
130     else // not in "module(funcname+offset)" format
131     {
132         m_module = syminfo;
133     }
134 #endif // !__WXOSX__
135 }
136 
137 
138 // ----------------------------------------------------------------------------
139 // wxStackWalker
140 // ----------------------------------------------------------------------------
141 
142 // that many frames should be enough for everyone
143 #define MAX_FRAMES          200
144 
145 // we need a char buffer big enough to contain a call to addr2line with
146 // up to MAX_FRAMES addresses !
147 // NB: %p specifier will print the pointer in hexadecimal form
148 //     and thus will require 2 chars for each byte + 3 for the
149 //     " 0x" prefix
150 #define CHARS_PER_FRAME    (sizeof(void*) * 2 + 3)
151 
152 // BUFSIZE will be 2250 for 32 bit machines
153 #define BUFSIZE            (50 + MAX_FRAMES*CHARS_PER_FRAME)
154 
155 // static data
156 void *wxStackWalker::ms_addresses[MAX_FRAMES];
157 char **wxStackWalker::ms_symbols = NULL;
158 int wxStackWalker::m_depth = 0;
159 wxString wxStackWalker::ms_exepath;
160 static char g_buf[BUFSIZE];
161 
162 
SaveStack(size_t maxDepth)163 void wxStackWalker::SaveStack(size_t maxDepth)
164 {
165     // read all frames required
166     maxDepth = wxMin(WXSIZEOF(ms_addresses)/sizeof(void*), maxDepth);
167     m_depth = backtrace(ms_addresses, maxDepth*sizeof(void*));
168     if ( !m_depth )
169         return;
170 
171     ms_symbols = backtrace_symbols(ms_addresses, m_depth);
172 }
173 
ProcessFrames(size_t skip)174 void wxStackWalker::ProcessFrames(size_t skip)
175 {
176     wxStackFrame frames[MAX_FRAMES];
177 
178     if (!ms_symbols || !m_depth)
179         return;
180 
181     // we are another level down from Walk(), so adjust the number of stack
182     // frames to skip accordingly
183     skip += 1;
184 
185     // call addr2line only once since this call may be very slow
186     // (it has to load in memory the entire EXE of this app which may be quite
187     //  big, especially if it contains debug info and is compiled statically!)
188     int numFrames = InitFrames(frames, m_depth - skip,
189                                &ms_addresses[skip], &ms_symbols[skip]);
190 
191     // now do user-defined operations on each frame
192     for ( int n = 0; n < numFrames; n++ )
193         OnStackFrame(frames[n]);
194 }
195 
FreeStack()196 void wxStackWalker::FreeStack()
197 {
198     // ms_symbols has been allocated by backtrace_symbols() and it's the responsibility
199     // of the caller, i.e. us, to free that pointer
200     if (ms_symbols)
201         free( ms_symbols );
202     ms_symbols = NULL;
203     m_depth = 0;
204 }
205 
206 namespace
207 {
208 
209 // Helper function to read a line from the file and return it without the
210 // trailing newline. Line number is only used for error reporting.
ReadLine(FILE * fp,unsigned long num,wxString * line)211 bool ReadLine(FILE* fp, unsigned long num, wxString* line)
212 {
213     if ( !fgets(g_buf, WXSIZEOF(g_buf), fp) )
214     {
215         wxUnusedVar(num); // could be unused if debug tracing is disabled
216 
217         wxLogDebug(wxS("cannot read address information for stack frame #%lu"),
218                    num);
219         return false;
220     }
221 
222     *line = wxString::FromAscii(g_buf);
223     line->RemoveLast();
224 
225     return true;
226 }
227 
228 } // anonymous namespace
229 
InitFrames(wxStackFrame * arr,size_t n,void ** addresses,char ** syminfo)230 int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, char **syminfo)
231 {
232     // we need to launch addr2line tool to get this information and we need to
233     // have the program name for this
234     wxString exepath = wxStackWalker::GetExePath();
235     if ( exepath.empty() )
236     {
237         exepath = wxStandardPaths::Get().GetExecutablePath();
238         if ( exepath.empty() )
239         {
240             wxLogDebug(wxT("Cannot parse stack frame because the executable ")
241                        wxT("path could not be detected"));
242             return 0;
243         }
244     }
245 
246     // build the command line for executing addr2line or atos under OS X using
247     // char* directly to avoid the conversions from Unicode
248 #ifdef __WXOSX__
249     int len = snprintf(g_buf, BUFSIZE, "atos -p %d", (int)getpid());
250 #else
251     int len = snprintf(g_buf, BUFSIZE, "addr2line -C -f -e \"%s\"", (const char*) exepath.mb_str());
252 #endif
253     len = (len <= 0) ? strlen(g_buf) : len;     // in case snprintf() is broken
254     for (size_t i=0; i<n; i++)
255     {
256         snprintf(&g_buf[len], BUFSIZE - len, " %p", addresses[i]);
257         len = strlen(g_buf);
258     }
259 
260     //wxLogDebug(wxT("piping the command '%s'"), g_buf);  // for debug only
261 
262     wxStdioPipe fp(g_buf, "r");
263     if ( !fp )
264         return 0;
265 
266     // parse the output reusing the same buffer to avoid any big memory
267     // allocations which could fail if our program is in a bad state
268     wxString name, filename;
269     unsigned long line = 0,
270                   curr = 0;
271     for  ( size_t i = 0; i < n; i++ )
272     {
273 #ifdef __WXOSX__
274         wxString buffer;
275         if ( !ReadLine(fp, i, &buffer) )
276             return false;
277 
278         line = 0;
279         filename.clear();
280 
281         // We can get back either the string in the following format:
282         //
283         //      func(args) (in module) (file:line)
284         //
285         // or just the same address back if it couldn't be resolved.
286         const size_t posIn = buffer.find(" (in ");
287         if ( posIn != wxString::npos )
288         {
289             name.assign(buffer, 0, posIn);
290 
291             size_t posAt = buffer.find(") (", posIn + 5); // Skip " (in "
292             if ( posAt != wxString::npos )
293             {
294                 posAt += 3; // Skip ") ("
295 
296                 // Discard the last character which is ")"
297                 wxString location(buffer, posAt, buffer.length() - posAt - 1);
298 
299                 wxString linenum;
300                 filename = location.BeforeFirst(':', &linenum);
301                 if ( !linenum.empty() )
302                     linenum.ToULong(&line);
303             }
304         }
305 #else // !__WXOSX__
306         // 1st line has function name
307         if ( !ReadLine(fp, i, &name) )
308             return false;
309 
310         name = wxString::FromAscii(g_buf);
311         name.RemoveLast(); // trailing newline
312 
313         if ( name == wxT("??") )
314             name.clear();
315 
316         // 2nd one -- the file/line info
317         if ( !ReadLine(fp, i, &filename) )
318             return false;
319 
320         const size_t posColon = filename.find(wxT(':'));
321         if ( posColon != wxString::npos )
322         {
323             // parse line number (it's ok if it fails, this will just leave
324             // line at its current, invalid, 0 value)
325             wxString(filename, posColon + 1, wxString::npos).ToULong(&line);
326 
327             // remove line number from 'filename'
328             filename.erase(posColon);
329             if ( filename == wxT("??") )
330                 filename.clear();
331         }
332         else
333         {
334             wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
335                        wxT("the semicolon is missing"),
336                        filename.c_str());
337         }
338 #endif // __WXOSX__/!__WXOSX__
339 
340         // now we've got enough info to initialize curr-th stack frame
341         // (at worst, only addresses[i] and syminfo[i] have been initialized,
342         //  but wxStackFrame::OnGetName may still be able to get function name):
343         arr[curr++].Set(name, filename, syminfo[i], i, line, addresses[i]);
344     }
345 
346     return curr;
347 }
348 
Walk(size_t skip,size_t maxDepth)349 void wxStackWalker::Walk(size_t skip, size_t maxDepth)
350 {
351     // read all frames required
352     SaveStack(maxDepth);
353 
354     // process them
355     ProcessFrames(skip);
356 
357     // cleanup
358     FreeStack();
359 }
360 
361 #endif // wxUSE_STACKWALKER
362