]>
git.saurik.com Git - wxWidgets.git/blob - src/unix/stackwalk.cpp
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 have 3 more "intermediate" frames which the calling code doesn't know
186 // about, account for them
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 towalk
= InitFrames(frames
, m_depth
- skip
, &ms_addresses
[skip
], &ms_symbols
[skip
]);
194 // now do user-defined operations on each frame
195 for ( int n
= 0; n
< towalk
- (int)skip
; n
++ )
196 OnStackFrame(frames
[n
]);
199 void wxStackWalker::FreeStack()
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
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.
214 bool ReadLine(FILE* fp
, unsigned long num
, wxString
* line
)
216 if ( !fgets(g_buf
, WXSIZEOF(g_buf
), fp
) )
218 wxLogDebug(wxS("cannot read address information for stack frame #%lu"),
223 *line
= wxString::FromAscii(g_buf
);
229 } // anonymous namespace
231 int wxStackWalker::InitFrames(wxStackFrame
*arr
, size_t n
, void **addresses
, char **syminfo
)
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() )
238 exepath
= wxStandardPaths::Get().GetExecutablePath();
239 if ( exepath
.empty() )
241 wxLogDebug(wxT("Cannot parse stack frame because the executable ")
242 wxT("path could not be detected"));
247 // build the command line for executing addr2line or atos under OS X using
248 // char* directly to avoid the conversions from Unicode
250 int len
= snprintf(g_buf
, BUFSIZE
, "atos -p %d", (int)getpid());
252 int len
= snprintf(g_buf
, BUFSIZE
, "addr2line -C -f -e \"%s\"", (const char*) exepath
.mb_str());
254 len
= (len
<= 0) ? strlen(g_buf
) : len
; // in case snprintf() is broken
255 for (size_t i
=0; i
<n
; i
++)
257 snprintf(&g_buf
[len
], BUFSIZE
- len
, " %p", addresses
[i
]);
261 //wxLogDebug(wxT("piping the command '%s'"), g_buf); // for debug only
263 wxStdioPipe
fp(g_buf
, "r");
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
269 wxString name
, filename
;
270 unsigned long line
= 0,
272 for ( size_t i
= 0; i
< n
; i
++ )
276 if ( !ReadLine(fp
, i
, &buffer
) )
282 // We can get back either the string in the following format:
284 // func(args) (in module) (file:line)
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
)
290 name
.assign(buffer
, 0, posIn
);
292 size_t posAt
= buffer
.find(") (", posIn
+ 3);
293 if ( posAt
!= wxString::npos
)
295 posAt
+= 3; // Skip ") ("
297 // Discard the two last characters which are ")\n"
298 wxString
location(buffer
, posAt
, buffer
.length() - posAt
- 2);
301 filename
= location
.BeforeFirst(':', &linenum
);
302 if ( !linenum
.empty() )
303 linenum
.ToULong(&line
);
307 // 1st line has function name
308 if ( !ReadLine(fp
, i
, &name
) )
311 name
= wxString::FromAscii(g_buf
);
312 name
.RemoveLast(); // trailing newline
314 if ( name
== wxT("??") )
317 // 2nd one -- the file/line info
318 if ( !ReadLine(fp
, i
, &filename
) )
321 const size_t posColon
= filename
.find(wxT(':'));
322 if ( posColon
!= wxString::npos
)
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
);
328 // remove line number from 'filename'
329 filename
.erase(posColon
);
330 if ( filename
== wxT("??") )
335 wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
336 wxT("the semicolon is missing"),
339 #endif // __WXOSX__/!__WXOSX__
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
]);
350 void wxStackWalker::Walk(size_t skip
, size_t maxDepth
)
352 // read all frames required
362 #endif // wxUSE_STACKWALKER