]>
git.saurik.com Git - wxWidgets.git/blob - src/unix/stackwalk.cpp
d082dfd22fd5b45d4de6c565cd638b2a3e199216
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/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
);
134 else // not in "module(funcname+offset)" format
142 // ----------------------------------------------------------------------------
144 // ----------------------------------------------------------------------------
146 // that many frames should be enough for everyone
147 #define MAX_FRAMES 200
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
154 #define CHARS_PER_FRAME (sizeof(void*) * 2 + 3)
156 // BUFSIZE will be 2250 for 32 bit machines
157 #define BUFSIZE (50 + MAX_FRAMES*CHARS_PER_FRAME)
160 void *wxStackWalker::ms_addresses
[MAX_FRAMES
];
161 char **wxStackWalker::ms_symbols
= NULL
;
162 int wxStackWalker::m_depth
= 0;
163 wxString
wxStackWalker::ms_exepath
;
164 static char g_buf
[BUFSIZE
];
167 void wxStackWalker::SaveStack(size_t maxDepth
)
169 // read all frames required
170 maxDepth
= wxMin(WXSIZEOF(ms_addresses
)/sizeof(void*), maxDepth
);
171 m_depth
= backtrace(ms_addresses
, maxDepth
*sizeof(void*));
175 ms_symbols
= backtrace_symbols(ms_addresses
, m_depth
);
178 void wxStackWalker::ProcessFrames(size_t skip
)
180 wxStackFrame frames
[MAX_FRAMES
];
182 if (!ms_symbols
|| !m_depth
)
185 // we are another level down from Walk(), so adjust the number of stack
186 // frames to skip accordingly
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!)
192 int numFrames
= InitFrames(frames
, m_depth
- skip
,
193 &ms_addresses
[skip
], &ms_symbols
[skip
]);
195 // now do user-defined operations on each frame
196 for ( int n
= 0; n
< numFrames
; n
++ )
197 OnStackFrame(frames
[n
]);
200 void wxStackWalker::FreeStack()
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
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.
215 bool ReadLine(FILE* fp
, unsigned long num
, wxString
* line
)
217 if ( !fgets(g_buf
, WXSIZEOF(g_buf
), fp
) )
219 wxLogDebug(wxS("cannot read address information for stack frame #%lu"),
224 *line
= wxString::FromAscii(g_buf
);
230 } // anonymous namespace
232 int wxStackWalker::InitFrames(wxStackFrame
*arr
, size_t n
, void **addresses
, char **syminfo
)
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() )
239 exepath
= wxStandardPaths::Get().GetExecutablePath();
240 if ( exepath
.empty() )
242 wxLogDebug(wxT("Cannot parse stack frame because the executable ")
243 wxT("path could not be detected"));
248 // build the command line for executing addr2line or atos under OS X using
249 // char* directly to avoid the conversions from Unicode
251 int len
= snprintf(g_buf
, BUFSIZE
, "atos -p %d", (int)getpid());
253 int len
= snprintf(g_buf
, BUFSIZE
, "addr2line -C -f -e \"%s\"", (const char*) exepath
.mb_str());
255 len
= (len
<= 0) ? strlen(g_buf
) : len
; // in case snprintf() is broken
256 for (size_t i
=0; i
<n
; i
++)
258 snprintf(&g_buf
[len
], BUFSIZE
- len
, " %p", addresses
[i
]);
262 //wxLogDebug(wxT("piping the command '%s'"), g_buf); // for debug only
264 wxStdioPipe
fp(g_buf
, "r");
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
270 wxString name
, filename
;
271 unsigned long line
= 0,
273 for ( size_t i
= 0; i
< n
; i
++ )
277 if ( !ReadLine(fp
, i
, &buffer
) )
283 // We can get back either the string in the following format:
285 // func(args) (in module) (file:line)
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
)
291 name
.assign(buffer
, 0, posIn
);
293 size_t posAt
= buffer
.find(") (", posIn
+ 3);
294 if ( posAt
!= wxString::npos
)
296 posAt
+= 3; // Skip ") ("
298 // Discard the two last characters which are ")\n"
299 wxString
location(buffer
, posAt
, buffer
.length() - posAt
- 2);
302 filename
= location
.BeforeFirst(':', &linenum
);
303 if ( !linenum
.empty() )
304 linenum
.ToULong(&line
);
308 // 1st line has function name
309 if ( !ReadLine(fp
, i
, &name
) )
312 name
= wxString::FromAscii(g_buf
);
313 name
.RemoveLast(); // trailing newline
315 if ( name
== wxT("??") )
318 // 2nd one -- the file/line info
319 if ( !ReadLine(fp
, i
, &filename
) )
322 const size_t posColon
= filename
.find(wxT(':'));
323 if ( posColon
!= wxString::npos
)
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
);
329 // remove line number from 'filename'
330 filename
.erase(posColon
);
331 if ( filename
== wxT("??") )
336 wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
337 wxT("the semicolon is missing"),
340 #endif // __WXOSX__/!__WXOSX__
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
]);
351 void wxStackWalker::Walk(size_t skip
, size_t maxDepth
)
353 // read all frames required
363 #endif // wxUSE_STACKWALKER