corrected code to really skip stack frames in the beginning and to number the remaini...
[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 m_module.assign(syminfo, posOpen);
134 }
135 else // not in "module(funcname+offset)" format
136 {
137 m_module = syminfo;
138 }
139 }
140
141 void wxStackFrame::OnGetLocation()
142 {
143 if ( m_hasLocation )
144 return;
145
146 m_hasLocation = true;
147
148 // we need to launch addr2line tool to get this information and we need to
149 // have the program name for this
150 wxString exepath = wxStackWalker::GetExePath();
151 if ( exepath.empty() )
152 {
153 if ( !wxTheApp || !wxTheApp->argv )
154 return;
155 exepath = wxTheApp->argv[0];
156 }
157
158 wxStdioPipe fp(wxString::Format(_T("addr2line -C -f -e \"%s\" %p"),
159 exepath.c_str(), m_address).mb_str(),
160 "r");
161
162 if ( !fp )
163 return;
164
165 // parse addr2line output
166 char buf[1024];
167 if ( !fgets(buf, WXSIZEOF(buf), fp) )
168 {
169 wxLogDebug(_T("Empty addr2line output?"));
170 return;
171 }
172
173 // 1st line has function name
174 if ( GetName().empty() )
175 {
176 m_name = wxString::FromAscii(buf);
177 m_name.RemoveLast(); // trailing newline
178
179 if ( m_name == _T("??") )
180 m_name.clear();
181 }
182
183 // 2nd one -- the file/line info
184 if ( fgets(buf, WXSIZEOF(buf), fp) )
185 {
186 wxString output(wxString::FromAscii(buf));
187 output.RemoveLast();
188
189 const size_t posColon = output.find(_T(':'));
190 if ( posColon != wxString::npos )
191 {
192 m_filename.assign(output, 0, posColon);
193 if ( m_filename == _T("??") )
194 {
195 m_filename.clear();
196 }
197 else
198 {
199 unsigned long line;
200 if ( wxString(output, posColon + 1, wxString::npos).
201 ToULong(&line) )
202 m_line = line;
203 }
204 }
205 else
206 {
207 wxLogDebug(_T("Unexpected addr2line format: \"%s\""),
208 output.c_str());
209 }
210 }
211 }
212
213 // ----------------------------------------------------------------------------
214 // wxStackWalker
215 // ----------------------------------------------------------------------------
216
217 void wxStackWalker::Walk(size_t skip)
218 {
219 // that many frames should be enough for everyone
220 void *addresses[200];
221
222 int depth = backtrace(addresses, WXSIZEOF(addresses));
223 if ( !depth )
224 return;
225
226 char **symbols = backtrace_symbols(addresses, depth);
227
228 // we have 3 more "intermediate" frames which the calling code doesn't know
229 // about., account for them
230 skip += 3;
231
232 for ( int n = skip; n < depth; n++ )
233 {
234 wxStackFrame frame(n - skip, addresses[n], symbols[n]);
235 OnStackFrame(frame);
236 }
237 }
238
239 #endif // wxUSE_STACKWALKER