]>
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
7 // Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 #include "wx/wxprec.h"
28 #include "wx/string.h"
34 #include "wx/stackwalk.h"
35 #include "wx/stdpaths.h"
39 #ifdef HAVE_CXA_DEMANGLE
41 #endif // HAVE_CXA_DEMANGLE
43 // ----------------------------------------------------------------------------
44 // tiny helper wrapper around popen/pclose()
45 // ----------------------------------------------------------------------------
50 // ctor parameters are passed to popen()
51 wxStdioPipe(const char *command
, const char *type
)
53 m_fp
= popen(command
, type
);
56 // conversion to stdio FILE
57 operator FILE *() const { return m_fp
; }
59 // dtor closes the pipe
69 wxDECLARE_NO_COPY_CLASS(wxStdioPipe
);
72 // ============================================================================
74 // ============================================================================
76 // ----------------------------------------------------------------------------
78 // ----------------------------------------------------------------------------
80 void wxStackFrame::OnGetName()
82 if ( !m_name
.empty() )
85 // we already tried addr2line in wxStackWalker::InitFrames: it always
86 // gives us demangled names (even if __cxa_demangle is not available) when
87 // the function is part of the ELF (when it's in a shared object addr2line
88 // will give "??") and because it seems less error-prone.
89 // when it works, backtrace_symbols() sometimes returns incorrect results
91 // format is: "module(funcname+offset) [address]" but the part in
92 // parentheses can be not present
93 wxString syminfo
= wxString::FromAscii(m_syminfo
);
94 const size_t posOpen
= syminfo
.find(wxT('('));
95 if ( posOpen
!= wxString::npos
)
97 const size_t posPlus
= syminfo
.find(wxT('+'), posOpen
+ 1);
98 if ( posPlus
!= wxString::npos
)
100 const size_t posClose
= syminfo
.find(wxT(')'), posPlus
+ 1);
101 if ( posClose
!= wxString::npos
)
103 if ( m_name
.empty() )
105 m_name
.assign(syminfo
, posOpen
+ 1, posPlus
- posOpen
- 1);
107 #ifdef HAVE_CXA_DEMANGLE
109 char *cppfunc
= __cxxabiv1::__cxa_demangle
112 NULL
, // output buffer (none, alloc it)
113 NULL
, // [out] len of output buffer
117 m_name
= wxString::FromAscii(cppfunc
);
120 #endif // HAVE_CXA_DEMANGLE
124 if ( wxString(syminfo
, posPlus
+ 1, posClose
- posPlus
- 1).
130 m_module
.assign(syminfo
, posOpen
);
133 else // not in "module(funcname+offset)" format
141 // ----------------------------------------------------------------------------
143 // ----------------------------------------------------------------------------
145 // that many frames should be enough for everyone
146 #define MAX_FRAMES 200
148 // we need a char buffer big enough to contain a call to addr2line with
149 // up to MAX_FRAMES addresses !
150 // NB: %p specifier will print the pointer in hexadecimal form
151 // and thus will require 2 chars for each byte + 3 for the
153 #define CHARS_PER_FRAME (sizeof(void*) * 2 + 3)
155 // BUFSIZE will be 2250 for 32 bit machines
156 #define BUFSIZE (50 + MAX_FRAMES*CHARS_PER_FRAME)
159 void *wxStackWalker::ms_addresses
[MAX_FRAMES
];
160 char **wxStackWalker::ms_symbols
= NULL
;
161 int wxStackWalker::m_depth
= 0;
162 wxString
wxStackWalker::ms_exepath
;
163 static char g_buf
[BUFSIZE
];
166 void wxStackWalker::SaveStack(size_t maxDepth
)
168 // read all frames required
169 maxDepth
= wxMin(WXSIZEOF(ms_addresses
)/sizeof(void*), maxDepth
);
170 m_depth
= backtrace(ms_addresses
, maxDepth
*sizeof(void*));
174 ms_symbols
= backtrace_symbols(ms_addresses
, m_depth
);
177 void wxStackWalker::ProcessFrames(size_t skip
)
179 wxStackFrame frames
[MAX_FRAMES
];
181 if (!ms_symbols
|| !m_depth
)
184 // we are another level down from Walk(), so adjust the number of stack
185 // frames to skip accordingly
188 // call addr2line only once since this call may be very slow
189 // (it has to load in memory the entire EXE of this app which may be quite
190 // big, especially if it contains debug info and is compiled statically!)
191 int numFrames
= InitFrames(frames
, m_depth
- skip
,
192 &ms_addresses
[skip
], &ms_symbols
[skip
]);
194 // now do user-defined operations on each frame
195 for ( int n
= 0; n
< numFrames
; n
++ )
196 OnStackFrame(frames
[n
]);
199 void wxStackWalker::FreeStack()
201 // ms_symbols has been allocated by backtrace_symbols() and it's the responsibility
202 // of the caller, i.e. us, to free that pointer
212 // Helper function to read a line from the file and return it without the
213 // trailing newline. Line number is only used for error reporting.
214 bool ReadLine(FILE* fp
, unsigned long num
, wxString
* line
)
216 if ( !fgets(g_buf
, WXSIZEOF(g_buf
), fp
) )
218 wxLogDebug(wxS("cannot read address information for stack frame #%lu"),
223 *line
= wxString::FromAscii(g_buf
);
229 } // anonymous namespace
231 int wxStackWalker::InitFrames(wxStackFrame
*arr
, size_t n
, void **addresses
, char **syminfo
)
233 // we need to launch addr2line tool to get this information and we need to
234 // have the program name for this
235 wxString exepath
= wxStackWalker::GetExePath();
236 if ( exepath
.empty() )
238 exepath
= wxStandardPaths::Get().GetExecutablePath();
239 if ( exepath
.empty() )
241 wxLogDebug(wxT("Cannot parse stack frame because the executable ")
242 wxT("path could not be detected"));
247 // build the command line for executing addr2line or atos under OS X using
248 // char* directly to avoid the conversions from Unicode
250 int len
= snprintf(g_buf
, BUFSIZE
, "atos -p %d", (int)getpid());
252 int len
= snprintf(g_buf
, BUFSIZE
, "addr2line -C -f -e \"%s\"", (const char*) exepath
.mb_str());
254 len
= (len
<= 0) ? strlen(g_buf
) : len
; // in case snprintf() is broken
255 for (size_t i
=0; i
<n
; i
++)
257 snprintf(&g_buf
[len
], BUFSIZE
- len
, " %p", addresses
[i
]);
261 //wxLogDebug(wxT("piping the command '%s'"), g_buf); // for debug only
263 wxStdioPipe
fp(g_buf
, "r");
267 // parse the output reusing the same buffer to avoid any big memory
268 // allocations which could fail if our program is in a bad state
269 wxString name
, filename
;
270 unsigned long line
= 0,
272 for ( size_t i
= 0; i
< n
; i
++ )
276 if ( !ReadLine(fp
, i
, &buffer
) )
282 // We can get back either the string in the following format:
284 // func(args) (in module) (file:line)
286 // or just the same address back if it couldn't be resolved.
287 const size_t posIn
= buffer
.find("(in ");
288 if ( posIn
!= wxString::npos
)
290 name
.assign(buffer
, 0, posIn
);
292 size_t posAt
= buffer
.find(") (", posIn
+ 3);
293 if ( posAt
!= wxString::npos
)
295 posAt
+= 3; // Skip ") ("
297 // Discard the two last characters which are ")\n"
298 wxString
location(buffer
, posAt
, buffer
.length() - posAt
- 2);
301 filename
= location
.BeforeFirst(':', &linenum
);
302 if ( !linenum
.empty() )
303 linenum
.ToULong(&line
);
307 // 1st line has function name
308 if ( !ReadLine(fp
, i
, &name
) )
311 name
= wxString::FromAscii(g_buf
);
312 name
.RemoveLast(); // trailing newline
314 if ( name
== wxT("??") )
317 // 2nd one -- the file/line info
318 if ( !ReadLine(fp
, i
, &filename
) )
321 const size_t posColon
= filename
.find(wxT(':'));
322 if ( posColon
!= wxString::npos
)
324 // parse line number (it's ok if it fails, this will just leave
325 // line at its current, invalid, 0 value)
326 wxString(filename
, posColon
+ 1, wxString::npos
).ToULong(&line
);
328 // remove line number from 'filename'
329 filename
.erase(posColon
);
330 if ( filename
== wxT("??") )
335 wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
336 wxT("the semicolon is missing"),
339 #endif // __WXOSX__/!__WXOSX__
341 // now we've got enough info to initialize curr-th stack frame
342 // (at worst, only addresses[i] and syminfo[i] have been initialized,
343 // but wxStackFrame::OnGetName may still be able to get function name):
344 arr
[curr
++].Set(name
, filename
, syminfo
[i
], i
, line
, addresses
[i
]);
350 void wxStackWalker::Walk(size_t skip
, size_t maxDepth
)
352 // read all frames required
362 #endif // wxUSE_STACKWALKER