]>
git.saurik.com Git - wxWidgets.git/blob - src/unix/stackwalk.cpp
ad78765094d27641369814f0707a4a21239ddab6
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/stackwalk.cpp
3 // Purpose: wxStackWalker implementation for Unix/glibc
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
29 #include "wx/string.h"
35 #include "wx/stackwalk.h"
36 #include "wx/stdpaths.h"
40 #ifdef HAVE_CXA_DEMANGLE
42 #endif // HAVE_CXA_DEMANGLE
44 // ----------------------------------------------------------------------------
45 // tiny helper wrapper around popen/pclose()
46 // ----------------------------------------------------------------------------
51 // ctor parameters are passed to popen()
52 wxStdioPipe(const char *command
, const char *type
)
54 m_fp
= popen(command
, type
);
57 // conversion to stdio FILE
58 operator FILE *() const { return m_fp
; }
60 // dtor closes the pipe
70 wxDECLARE_NO_COPY_CLASS(wxStdioPipe
);
73 // ============================================================================
75 // ============================================================================
77 // ----------------------------------------------------------------------------
79 // ----------------------------------------------------------------------------
81 void wxStackFrame::OnGetName()
83 if ( !m_name
.empty() )
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
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
)
98 const size_t posPlus
= syminfo
.find(wxT('+'), posOpen
+ 1);
99 if ( posPlus
!= wxString::npos
)
101 const size_t posClose
= syminfo
.find(wxT(')'), posPlus
+ 1);
102 if ( posClose
!= wxString::npos
)
104 if ( m_name
.empty() )
106 m_name
.assign(syminfo
, posOpen
+ 1, posPlus
- posOpen
- 1);
108 #ifdef HAVE_CXA_DEMANGLE
110 char *cppfunc
= __cxxabiv1::__cxa_demangle
113 NULL
, // output buffer (none, alloc it)
114 NULL
, // [out] len of output buffer
118 m_name
= wxString::FromAscii(cppfunc
);
121 #endif // HAVE_CXA_DEMANGLE
125 if ( wxString(syminfo
, posPlus
+ 1, posClose
- posPlus
- 1).
131 m_module
.assign(syminfo
, posOpen
);
133 else // not in "module(funcname+offset)" format
140 // ----------------------------------------------------------------------------
142 // ----------------------------------------------------------------------------
144 // that many frames should be enough for everyone
145 #define MAX_FRAMES 200
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
152 #define CHARS_PER_FRAME (sizeof(void*) * 2 + 3)
154 // BUFSIZE will be 2250 for 32 bit machines
155 #define BUFSIZE (50 + MAX_FRAMES*CHARS_PER_FRAME)
158 void *wxStackWalker::ms_addresses
[MAX_FRAMES
];
159 char **wxStackWalker::ms_symbols
= NULL
;
160 int wxStackWalker::m_depth
= 0;
161 wxString
wxStackWalker::ms_exepath
;
162 static char g_buf
[BUFSIZE
];
165 void wxStackWalker::SaveStack(size_t maxDepth
)
167 // read all frames required
168 maxDepth
= wxMin(WXSIZEOF(ms_addresses
)/sizeof(void*), maxDepth
);
169 m_depth
= backtrace(ms_addresses
, maxDepth
*sizeof(void*));
173 ms_symbols
= backtrace_symbols(ms_addresses
, m_depth
);
176 void wxStackWalker::ProcessFrames(size_t skip
)
178 wxStackFrame frames
[MAX_FRAMES
];
180 if (!ms_symbols
|| !m_depth
)
183 // we have 3 more "intermediate" frames which the calling code doesn't know
184 // about, account for them
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
]);
192 // now do user-defined operations on each frame
193 for ( int n
= 0; n
< towalk
- (int)skip
; n
++ )
194 OnStackFrame(frames
[n
]);
197 void wxStackWalker::FreeStack()
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
207 int wxStackWalker::InitFrames(wxStackFrame
*arr
, size_t n
, void **addresses
, char **syminfo
)
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() )
214 exepath
= wxStandardPaths::Get().GetExecutablePath();
215 if ( exepath
.empty() )
217 wxLogDebug(wxT("Cannot parse stack frame because the executable ")
218 wxT("path could not be detected"));
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
++)
229 snprintf(&g_buf
[len
], BUFSIZE
- len
, " %p", addresses
[i
]);
233 //wxLogDebug(wxT("piping the command '%s'"), g_buf); // for debug only
235 wxStdioPipe
fp(g_buf
, "r");
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,
244 for ( size_t i
= 0; i
< n
; i
++ )
246 // 1st line has function name
247 if ( fgets(g_buf
, WXSIZEOF(g_buf
), fp
) )
249 name
= wxString::FromAscii(g_buf
);
250 name
.RemoveLast(); // trailing newline
252 if ( name
== wxT("??") )
257 wxLogDebug(wxT("cannot read addr2line output for stack frame #%lu"),
262 // 2nd one -- the file/line info
263 if ( fgets(g_buf
, WXSIZEOF(g_buf
), fp
) )
265 filename
= wxString::FromAscii(g_buf
);
266 filename
.RemoveLast();
268 const size_t posColon
= filename
.find(wxT(':'));
269 if ( posColon
!= wxString::npos
)
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
);
275 // remove line number from 'filename'
276 filename
.erase(posColon
);
277 if ( filename
== wxT("??") )
282 wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
283 wxT("the semicolon is missing"),
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
]);
297 void wxStackWalker::Walk(size_t skip
, size_t maxDepth
)
299 // read all frames required
309 #endif // wxUSE_STACKWALKER