use popen() instead of wxExecute(), it works inside wxYield() unlike the latter
[wxWidgets.git] / src / unix / stackwalk.cpp
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
37 #include <execinfo.h>
38
39 #ifdef HAVE_CXA_DEMANGLE
40 #include <cxxabi.h>
41 #endif // HAVE_CXA_DEMANGLE
42
43 // ----------------------------------------------------------------------------
44 // tiny helper wrapper around popen/pclose()
45 // ----------------------------------------------------------------------------
46
47 class wxStdioPipe
48 {
49 public:
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
66 private:
67 FILE *m_fp;
68
69 DECLARE_NO_COPY_CLASS(wxStdioPipe)
70 };
71
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 wxString wxStackWalker::ms_exepath;
77
78 // ----------------------------------------------------------------------------
79 // wxStackFrame
80 // ----------------------------------------------------------------------------
81
82 void wxStackFrame::OnGetName()
83 {
84 if ( m_hasName )
85 return;
86
87 m_hasName = true;
88
89 // try addr2line first because it always gives us demangled names (even if
90 // __cxa_demangle is not available) and because it seems less error-prone
91 // when it works, backtrace_symbols() sometimes returns incorrect results
92 OnGetLocation();
93
94 // format is: "module(funcname+offset) [address]" but the part in
95 // parentheses can be not present
96 wxString syminfo = wxString::FromAscii(m_syminfo);
97 const size_t posOpen = syminfo.find(_T('('));
98 if ( posOpen != wxString::npos )
99 {
100 const size_t posPlus = syminfo.find(_T('+'), posOpen + 1);
101 if ( posPlus != wxString::npos )
102 {
103 const size_t posClose = syminfo.find(_T(')'), posPlus + 1);
104 if ( posClose != wxString::npos )
105 {
106 if ( m_name.empty() )
107 {
108 m_name.assign(syminfo, posOpen + 1, posPlus - posOpen - 1);
109
110 #ifdef HAVE_CXA_DEMANGLE
111 int rc = -1;
112 char *cppfunc = __cxxabiv1::__cxa_demangle
113 (
114 m_name.mb_str(),
115 NULL, // output buffer (none, alloc it)
116 NULL, // [out] len of output buffer
117 &rc
118 );
119 if ( rc == 0 )
120 m_name = wxString::FromAscii(cppfunc);
121
122 free(cppfunc);
123 #endif // HAVE_CXA_DEMANGLE
124 }
125
126 unsigned long ofs;
127 if ( wxString(syminfo, posPlus + 1, posClose - posPlus - 1).
128 ToULong(&ofs, 0) )
129 m_offset = ofs;
130 }
131 }
132 }
133
134 m_module.assign(syminfo, posOpen);
135 }
136
137 void wxStackFrame::OnGetLocation()
138 {
139 if ( m_hasLocation )
140 return;
141
142 m_hasLocation = true;
143
144 // we need to launch addr2line tool to get this information and we need to
145 // have the program name for this
146 wxString exepath = wxStackWalker::GetExePath();
147 if ( exepath.empty() )
148 {
149 if ( !wxTheApp || !wxTheApp->argv )
150 return;
151 exepath = wxTheApp->argv[0];
152 }
153
154 wxStdioPipe fp(wxString::Format(_T("addr2line -C -f -e \"%s\" %p"),
155 exepath.c_str(), m_address).mb_str(),
156 "r");
157
158 if ( !fp )
159 return;
160
161 // parse addr2line output
162 char buf[1024];
163 if ( !fgets(buf, WXSIZEOF(buf), fp) )
164 {
165 wxLogDebug(_T("Empty addr2line output?"));
166 return;
167 }
168
169 // 1st line has function name
170 if ( GetName().empty() )
171 {
172 m_name = wxString::FromAscii(buf);
173 m_name.RemoveLast(); // trailing newline
174
175 if ( m_name == _T("??") )
176 m_name.clear();
177 }
178
179 // 2nd one -- the file/line info
180 if ( fgets(buf, WXSIZEOF(buf), fp) )
181 {
182 wxString output(wxString::FromAscii(buf));
183 output.RemoveLast();
184
185 const size_t posColon = output.find(_T(':'));
186 if ( posColon != wxString::npos )
187 {
188 m_filename.assign(output, 0, posColon);
189 if ( m_filename == _T("??") )
190 {
191 m_filename.clear();
192 }
193 else
194 {
195 unsigned long line;
196 if ( wxString(output, posColon + 1, wxString::npos).
197 ToULong(&line) )
198 m_line = line;
199 }
200 }
201 else
202 {
203 wxLogDebug(_T("Unexpected addr2line format: \"%s\""),
204 output.c_str());
205 }
206 }
207 }
208
209 // ----------------------------------------------------------------------------
210 // wxStackWalker
211 // ----------------------------------------------------------------------------
212
213 void wxStackWalker::Walk(size_t skip)
214 {
215 // that many frames should be enough for everyone
216 void *addresses[200];
217
218 int depth = backtrace(addresses, WXSIZEOF(addresses));
219 if ( !depth )
220 return;
221
222 char **symbols = backtrace_symbols(addresses, depth);
223
224 for ( int n = 0; n < depth; n++ )
225 {
226 wxStackFrame frame(n, addresses[n], symbols[n]);
227 OnStackFrame(frame);
228 }
229 }
230
231 #endif // wxUSE_STACKWALKER