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