]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/unix/stackwalk.cpp
preserve type when loaded image is rescaled, #11543
[wxWidgets.git] / src / unix / stackwalk.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: msw/stackwalk.cpp
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"
30 #include "wx/app.h"
31 #include "wx/log.h"
32 #include "wx/utils.h"
33#endif
34
35#include "wx/stackwalk.h"
36#include "wx/stdpaths.h"
37
38#include <execinfo.h>
39
40#ifdef HAVE_CXA_DEMANGLE
41 #include <cxxabi.h>
42#endif // HAVE_CXA_DEMANGLE
43
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
70 wxDECLARE_NO_COPY_CLASS(wxStdioPipe);
71};
72
73// ============================================================================
74// implementation
75// ============================================================================
76
77// ----------------------------------------------------------------------------
78// wxStackFrame
79// ----------------------------------------------------------------------------
80
81void wxStackFrame::OnGetName()
82{
83 if ( !m_name.empty() )
84 return;
85
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
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);
95 const size_t posOpen = syminfo.find(wxT('('));
96 if ( posOpen != wxString::npos )
97 {
98 const size_t posPlus = syminfo.find(wxT('+'), posOpen + 1);
99 if ( posPlus != wxString::npos )
100 {
101 const size_t posClose = syminfo.find(wxT(')'), posPlus + 1);
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 }
130
131 m_module.assign(syminfo, posOpen);
132 }
133 else // not in "module(funcname+offset)" format
134 {
135 m_module = syminfo;
136 }
137}
138
139
140// ----------------------------------------------------------------------------
141// wxStackWalker
142// ----------------------------------------------------------------------------
143
144// that many frames should be enough for everyone
145#define MAX_FRAMES 200
146
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
151// " 0x" prefix
152#define CHARS_PER_FRAME (sizeof(void*) * 2 + 3)
153
154// BUFSIZE will be 2250 for 32 bit machines
155#define BUFSIZE (50 + MAX_FRAMES*CHARS_PER_FRAME)
156
157// static data
158void *wxStackWalker::ms_addresses[MAX_FRAMES];
159char **wxStackWalker::ms_symbols = NULL;
160int wxStackWalker::m_depth = 0;
161wxString wxStackWalker::ms_exepath;
162static char g_buf[BUFSIZE];
163
164
165void wxStackWalker::SaveStack(size_t maxDepth)
166{
167 // read all frames required
168 maxDepth = wxMin(WXSIZEOF(ms_addresses)/sizeof(void*), maxDepth);
169 m_depth = backtrace(ms_addresses, maxDepth*sizeof(void*));
170 if ( !m_depth )
171 return;
172
173 ms_symbols = backtrace_symbols(ms_addresses, m_depth);
174}
175
176void wxStackWalker::ProcessFrames(size_t skip)
177{
178 wxStackFrame frames[MAX_FRAMES];
179
180 if (!ms_symbols || !m_depth)
181 return;
182
183 // we have 3 more "intermediate" frames which the calling code doesn't know
184 // about, account for them
185 skip += 3;
186
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]);
191
192 // now do user-defined operations on each frame
193 for ( int n = 0; n < towalk - (int)skip; n++ )
194 OnStackFrame(frames[n]);
195}
196
197void wxStackWalker::FreeStack()
198{
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
201 if (ms_symbols)
202 free( ms_symbols );
203 ms_symbols = NULL;
204 m_depth = 0;
205}
206
207int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, char **syminfo)
208{
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() )
213 {
214 exepath = wxStandardPaths::Get().GetExecutablePath();
215 if ( exepath.empty() )
216 {
217 wxLogDebug(wxT("Cannot parse stack frame because the executable ")
218 wxT("path could not be detected"));
219 return 0;
220 }
221 }
222
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++)
228 {
229 snprintf(&g_buf[len], BUFSIZE - len, " %p", addresses[i]);
230 len = strlen(g_buf);
231 }
232
233 //wxLogDebug(wxT("piping the command '%s'"), g_buf); // for debug only
234
235 wxStdioPipe fp(g_buf, "r");
236 if ( !fp )
237 return 0;
238
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,
243 curr = 0;
244 for ( size_t i = 0; i < n; i++ )
245 {
246 // 1st line has function name
247 if ( fgets(g_buf, WXSIZEOF(g_buf), fp) )
248 {
249 name = wxString::FromAscii(g_buf);
250 name.RemoveLast(); // trailing newline
251
252 if ( name == wxT("??") )
253 name.clear();
254 }
255 else
256 {
257 wxLogDebug(wxT("cannot read addr2line output for stack frame #%lu"),
258 (unsigned long)i);
259 return false;
260 }
261
262 // 2nd one -- the file/line info
263 if ( fgets(g_buf, WXSIZEOF(g_buf), fp) )
264 {
265 filename = wxString::FromAscii(g_buf);
266 filename.RemoveLast();
267
268 const size_t posColon = filename.find(wxT(':'));
269 if ( posColon != wxString::npos )
270 {
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);
274
275 // remove line number from 'filename'
276 filename.erase(posColon);
277 if ( filename == wxT("??") )
278 filename.clear();
279 }
280 else
281 {
282 wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
283 wxT("the semicolon is missing"),
284 filename.c_str());
285 }
286 }
287
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]);
292 }
293
294 return curr;
295}
296
297void wxStackWalker::Walk(size_t skip, size_t maxDepth)
298{
299 // read all frames required
300 SaveStack(maxDepth);
301
302 // process them
303 ProcessFrames(skip);
304
305 // cleanup
306 FreeStack();
307}
308
309#endif // wxUSE_STACKWALKER