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