Upstream tarball 9882
[amule.git] / src / libs / common / MuleDebug.cpp
blob6de21718685a97ff997a1d512b90a8f5fdb278fa
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2005-2008 Mikkel Schubert ( xaignar@users.sourceforge.net )
5 // Copyright (c) 2005-2008 aMule Team ( admin@amule.org / http://www.amule.org )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <cstdlib> // Needed for std::abort()
28 #ifdef HAVE_CONFIG_H
29 # include "config.h" // Needed for HAVE_CXXABI and HAVE_EXECINFO
30 #endif
32 #include "MuleDebug.h" // Interface declaration
33 #include "StringFunctions.h" // Needed for unicode2char
35 #ifdef HAVE_EXECINFO
36 # include <execinfo.h>
37 #endif
39 #ifdef HAVE_CXXABI
40 # include <cxxabi.h>
41 #endif
43 #include <wx/thread.h> // Do_not_auto_remove (Old wx < 2.7)
44 #include <wx/utils.h> // Do_not_auto_remove (Old wx < 2.7)
46 #if wxUSE_STACKWALKER && defined(__WXMSW__)
47 #include <wx/stackwalk.h> // Do_not_auto_remove
48 #elif defined(HAVE_BFD)
49 #include <ansidecl.h> // Do_not_auto_remove
50 #include <bfd.h> // Do_not_auto_remove
51 #endif
53 #include <vector>
56 /**
57 * This functions displays a verbose description of
58 * any unhandled exceptions that occour and then
59 * terminate the program by raising SIGABRT.
61 void OnUnhandledException()
63 // Revert to the original exception handler, to avoid
64 // infinate recursion, in case something goes wrong in
65 // this function.
66 std::set_terminate(std::abort);
68 #ifdef HAVE_CXXABI
69 std::type_info *t = __cxxabiv1::__cxa_current_exception_type();
70 FILE* output = stderr;
71 #else
72 FILE* output = stdout;
73 bool t = true;
74 #endif
75 if (t) {
76 int status = -1;
77 char *dem = 0;
78 #ifdef HAVE_CXXABI
79 // Note that "name" is the mangled name.
80 char const *name = t->name();
82 dem = __cxxabiv1::__cxa_demangle(name, 0, 0, &status);
83 #else
84 const char* name = "Unknown";
85 #endif
86 fprintf(output, "\nTerminated after throwing an instance of '%s'\n", (status ? name : dem));
87 free(dem);
89 try {
90 throw;
91 } catch (const std::exception& e) {
92 fprintf(output, "\twhat(): %s\n", e.what());
93 } catch (const CMuleException& e) {
94 fprintf(output, "\twhat(): %s\n", (const char*)unicode2char(e.what()));
95 } catch (const wxString& e) {
96 fprintf(output, "\twhat(): %s\n", (const char*)unicode2char(e));
97 } catch (...) {
98 // Unable to retrieve cause of exception
101 fprintf(output, "\tbacktrace:\n%s\n", (const char*)unicode2char(get_backtrace(1)));
103 std::abort();
107 void InstallMuleExceptionHandler()
109 std::set_terminate(OnUnhandledException);
113 // Make it 1 for getting the file path also
114 #define TOO_VERBOSE_BACKTRACE 0
116 #if wxUSE_STACKWALKER && defined(__WXMSW__)
118 // Derived class to define the actions to be done on frame print.
119 // I was tempted to name it MuleSkyWalker
120 class MuleStackWalker : public wxStackWalker
122 public:
123 MuleStackWalker() {};
124 ~MuleStackWalker() {};
126 void OnStackFrame(const wxStackFrame& frame)
128 wxString btLine = wxString::Format(wxT("[%u] "), frame.GetLevel());
129 wxString filename = frame.GetName();
131 if (!filename.IsEmpty()) {
132 btLine += filename + wxT(" (") +
133 #if TOO_VERBOSE_BACKTRACE
134 frame.GetModule()
135 #else
136 frame.GetModule().AfterLast(wxT('/'))
137 #endif
138 + wxT(")");
139 } else {
140 btLine += wxString::Format(wxT("0x%lx"), frame.GetAddress());
143 if (frame.HasSourceLocation()) {
144 btLine += wxT(" at ") +
145 #if TOO_VERBOSE_BACKTRACE
146 frame.GetFileName()
147 #else
148 frame.GetFileName().AfterLast(wxT('/'))
149 #endif
150 + wxString::Format(wxT(":%u"),frame.GetLine());
151 } else {
152 btLine += wxT(" (Unknown file/line)");
155 //! Contains the entire backtrace
156 m_trace += btLine + wxT("\n");
159 wxString m_trace;
163 wxString get_backtrace(unsigned n)
165 MuleStackWalker walker; // Texas ranger?
166 walker.Walk(n); // Skip this one and Walk() also!
168 return walker.m_trace;
171 #elif defined(__LINUX__)
173 #ifdef HAVE_BFD
175 static bfd* s_abfd;
176 static asymbol** s_symbol_list;
177 static bool s_have_backtrace_symbols = false;
178 static const char* s_file_name;
179 static const char* s_function_name;
180 static unsigned int s_line_number;
181 static int s_found;
185 * read all symbols in the executable into an array
186 * and return the pointer to the array in symbol_list.
187 * Also return the number of actual symbols read
188 * If there's any error, return -1
190 static int get_backtrace_symbols(bfd *abfd, asymbol ***symbol_list_ptr)
192 int vectorsize = bfd_get_symtab_upper_bound(abfd);
194 if (vectorsize < 0) {
195 fprintf (stderr, "Error while getting vector size for backtrace symbols : %s",
196 bfd_errmsg(bfd_get_error()));
197 return -1;
200 if (vectorsize == 0) {
201 fprintf (stderr, "Error while getting backtrace symbols : No symbols (%s)",
202 bfd_errmsg(bfd_get_error()));
203 return -1;
206 *symbol_list_ptr = (asymbol**)malloc(vectorsize);
208 if (*symbol_list_ptr == NULL) {
209 fprintf (stderr, "Error while getting backtrace symbols : Cannot allocate memory");
210 return -1;
213 vectorsize = bfd_canonicalize_symtab(abfd, *symbol_list_ptr);
215 if (vectorsize < 0) {
216 fprintf(stderr, "Error while getting symbol table : %s",
217 bfd_errmsg(bfd_get_error()));
218 return -1;
221 return vectorsize;
226 * print file, line and function information for address
227 * The info is actually set into global variables. This
228 * function is called from the iterator bfd_map_over_sections
231 void init_backtrace_info()
233 bfd_init();
234 s_abfd = bfd_openr("/proc/self/exe", NULL);
236 if (s_abfd == NULL) {
237 fprintf(stderr, "Error while opening file for backtrace symbols : %s",
238 bfd_errmsg(bfd_get_error()));
239 return;
242 if (!(bfd_check_format_matches(s_abfd, bfd_object, NULL))) {
243 fprintf (stderr, "Error while init. backtrace symbols : %s",
244 bfd_errmsg (bfd_get_error ()));
245 bfd_close(s_abfd);
246 return;
249 s_have_backtrace_symbols = (get_backtrace_symbols(s_abfd, &s_symbol_list) > 0);
253 void get_file_line_info(bfd *abfd, asection *section, void* _address)
255 wxASSERT(s_symbol_list);
257 if (s_found) {
258 return;
261 if ((section->flags & SEC_ALLOC) == 0) {
262 return;
265 bfd_vma vma = bfd_get_section_vma(abfd, section);
267 unsigned long address = (unsigned long)_address;
268 if (address < vma) {
269 return;
272 bfd_size_type size = bfd_section_size(abfd, section);
273 if (address > (vma + size)) {
274 return;
277 s_found = bfd_find_nearest_line(abfd, section, s_symbol_list,
278 address - vma, &s_file_name, &s_function_name, &s_line_number);
281 #endif // HAVE_BFD
283 wxString demangle(const wxString& function)
285 #ifdef HAVE_CXXABI
286 wxString result;
288 if (function.Mid(0,2) == wxT("_Z")) {
289 int status;
290 char *demangled = abi::__cxa_demangle(function.mb_str(), NULL, NULL, &status);
292 if (!status) {
293 result = wxConvCurrent->cMB2WX(demangled);
296 if (demangled) {
297 free(demangled);
301 return result;
302 #else
303 return wxEmptyString;
304 #endif
308 // Print a stack backtrace if available
309 wxString get_backtrace(unsigned n)
311 #ifdef HAVE_EXECINFO
312 // (stkn) create backtrace
313 void *bt_array[100]; // 100 should be enough ?!?
314 char **bt_strings;
315 int num_entries;
317 if ((num_entries = backtrace(bt_array, 100)) < 0) {
318 fprintf(stderr, "* Could not generate backtrace\n");
319 return wxEmptyString;
322 if ((bt_strings = backtrace_symbols(bt_array, num_entries)) == NULL) {
323 fprintf(stderr, "* Could not get symbol names for backtrace\n");
324 return wxEmptyString;
327 std::vector<wxString> libname(num_entries);
328 std::vector<wxString> funcname(num_entries);
329 std::vector<wxString> address(num_entries);
330 wxString AllAddresses;
332 for (int i = 0; i < num_entries; ++i) {
333 wxString wxBtString = wxConvCurrent->cMB2WX(bt_strings[i]);
334 int posLPar = wxBtString.Find(wxT('('));
335 int posRPar = wxBtString.Find(wxT(')'));
336 int posLBra = wxBtString.Find(wxT('['));
337 int posRBra = wxBtString.Find(wxT(']'));
338 bool hasFunction = true;
339 if (posLPar == -1 || posRPar == -1) {
340 if (posLBra == -1 || posRBra == -1) {
341 /* It is important to have exactly num_entries
342 * addresses in AllAddresses */
343 AllAddresses += wxT("0x0000000 ");
344 continue;
346 posLPar = posLBra;
347 hasFunction = false;
349 /* Library name */
350 int len = posLPar;
351 libname[i] = wxBtString.Mid(0, len);
352 /* Function name */
353 if (hasFunction) {
354 int posPlus = wxBtString.Find(wxT('+'), true);
355 if (posPlus == -1)
356 posPlus = posRPar;
357 len = posPlus - posLPar - 1;
358 funcname[i] = wxBtString.Mid(posLPar + 1, len);
359 wxString demangled = demangle(funcname[i]);
360 if (!demangled.IsEmpty()) {
361 funcname[i] = demangled;
364 /* Address */
365 if ( posLBra == -1 || posRBra == -1) {
366 address[i] = wxT("0x0000000");
367 } else {
368 len = posRBra - posLBra - 1;
369 address[i] = wxBtString.Mid(posLBra + 1, len);
370 AllAddresses += address[i] + wxT(" ");
373 free(bt_strings);
375 /* Get line numbers from addresses */
376 wxArrayString out;
377 bool hasLineNumberInfo = false;
379 #ifdef HAVE_BFD
380 if (!s_have_backtrace_symbols) {
381 init_backtrace_info();
382 wxASSERT(s_have_backtrace_symbols);
385 for (int i = 0; i < num_entries; ++i) {
386 s_file_name = NULL;
387 s_function_name = NULL;
388 s_line_number = 0;
389 s_found = false ;
391 unsigned long addr;
392 address[i].ToULong(&addr,0); // As it's "0x" prepended, wx will read it as base 16. Hopefully.
394 bfd_map_over_sections(s_abfd, get_file_line_info, (void*)addr);
396 if (s_found) {
397 wxString function = wxConvCurrent->cMB2WX(s_function_name);
398 wxString demangled = demangle(function);
399 if (!demangled.IsEmpty()) {
400 function = demangled;
401 funcname[i] = demangled;
403 out.Insert(wxConvCurrent->cMB2WX(s_function_name),i*2);
404 out.Insert(wxConvCurrent->cMB2WX(s_file_name) + wxString::Format(wxT(":%u"), s_line_number),i*2+1);
405 } else {
406 out.Insert(wxT("??"),i*2);
407 out.Insert(wxT("??"),i*2+1);
411 hasLineNumberInfo = true;
413 #else /* !HAVE_BFD */
414 if (wxThread::IsMain()) {
415 wxString command;
416 command << wxT("addr2line -C -f -s -e /proc/") <<
417 getpid() << wxT("/exe ") << AllAddresses;
418 // The output of the command is this wxArrayString, in which
419 // the even elements are the function names, and the odd elements
420 // are the line numbers.
422 hasLineNumberInfo = wxExecute(command, out) != -1;
425 #endif /* HAVE_BFD / !HAVE_BFD */
427 wxString trace;
428 // Remove 'n+1' first entries (+1 because of this function)
429 for (int i = n+1; i < num_entries; ++i) {
430 /* If we have no function name, use the result from addr2line */
431 if (funcname[i].IsEmpty()) {
432 if (hasLineNumberInfo) {
433 funcname[i] = out[2*i];
434 } else {
435 funcname[i] = wxT("??");
438 wxString btLine;
439 btLine << wxT("[") << i << wxT("] ") << funcname[i] << wxT(" in ");
440 /* If addr2line did not find a line number, use bt_string */
441 if (!hasLineNumberInfo || out[2*i+1].Mid(0,2) == wxT("??")) {
442 btLine += libname[i] + wxT("[") + address[i] + wxT("]");
443 } else if (hasLineNumberInfo) {
444 #if TOO_VERBOSE_BACKTRACE
445 btLine += out[2*i+1];
446 #else
447 btLine += out[2*i+1].AfterLast(wxT('/'));
448 #endif
449 } else {
450 btLine += libname[i];
453 trace += btLine + wxT("\n");
456 return trace;
457 #else /* !HAVE_EXECINFO */
458 fprintf(stderr, "--== cannot generate backtrace ==--\n\n");
459 return wxEmptyString;
460 #endif /* HAVE_EXECINFO */
463 #else /* !__LINUX__ */
465 wxString get_backtrace(unsigned WXUNUSED(n))
467 fprintf(stderr, "--== no BACKTRACE for your platform ==--\n\n");
468 return wxEmptyString;
471 #endif /* !__LINUX__ */
473 void print_backtrace(unsigned n)
475 wxString trace = get_backtrace(n);
477 // This is because the string is ansi anyway, and the conv classes are very slow
478 fprintf(stderr, "%s\n", (const char*)unicode2char(trace.c_str()));
481 // File_checked_for_headers