Format string mayhem
[amule.git] / src / libs / common / MuleDebug.cpp
blobac7c6abc14014c548163f62140dfd467f6fb61c8
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
34 #include "Format.h" // Needed for CFormat
36 #ifdef HAVE_EXECINFO
37 # include <execinfo.h>
38 #endif
40 #ifdef HAVE_CXXABI
41 # include <cxxabi.h>
42 #endif
44 #include <wx/thread.h> // Do_not_auto_remove (Old wx < 2.7)
45 #include <wx/utils.h> // Do_not_auto_remove (Old wx < 2.7)
47 #if wxUSE_STACKWALKER && defined(__WXMSW__)
48 #include <wx/stackwalk.h> // Do_not_auto_remove
49 #elif defined(HAVE_BFD)
50 #include <ansidecl.h> // Do_not_auto_remove
51 #include <bfd.h> // Do_not_auto_remove
52 #endif
54 #include <vector>
57 /**
58 * This functions displays a verbose description of
59 * any unhandled exceptions that occour and then
60 * terminate the program by raising SIGABRT.
62 void OnUnhandledException()
64 // Revert to the original exception handler, to avoid
65 // infinate recursion, in case something goes wrong in
66 // this function.
67 std::set_terminate(std::abort);
69 #ifdef HAVE_CXXABI
70 std::type_info *t = __cxxabiv1::__cxa_current_exception_type();
71 FILE* output = stderr;
72 #else
73 FILE* output = stdout;
74 bool t = true;
75 #endif
76 if (t) {
77 int status = -1;
78 char *dem = 0;
79 #ifdef HAVE_CXXABI
80 // Note that "name" is the mangled name.
81 char const *name = t->name();
83 dem = __cxxabiv1::__cxa_demangle(name, 0, 0, &status);
84 #else
85 const char* name = "Unknown";
86 #endif
87 fprintf(output, "\nTerminated after throwing an instance of '%s'\n", (status ? name : dem));
88 free(dem);
90 try {
91 throw;
92 } catch (const std::exception& e) {
93 fprintf(output, "\twhat(): %s\n", e.what());
94 } catch (const CMuleException& e) {
95 fprintf(output, "\twhat(): %s\n", (const char*)unicode2char(e.what()));
96 } catch (const wxString& e) {
97 fprintf(output, "\twhat(): %s\n", (const char*)unicode2char(e));
98 } catch (...) {
99 // Unable to retrieve cause of exception
102 fprintf(output, "\tbacktrace:\n%s\n", (const char*)unicode2char(get_backtrace(1)));
104 std::abort();
108 void InstallMuleExceptionHandler()
110 std::set_terminate(OnUnhandledException);
114 // Make it 1 for getting the file path also
115 #define TOO_VERBOSE_BACKTRACE 0
117 #if wxUSE_STACKWALKER && defined(__WXMSW__)
119 // Derived class to define the actions to be done on frame print.
120 // I was tempted to name it MuleSkyWalker
121 class MuleStackWalker : public wxStackWalker
123 public:
124 MuleStackWalker() {};
125 ~MuleStackWalker() {};
127 void OnStackFrame(const wxStackFrame& frame)
129 wxString btLine = CFormat(wxT("[%u] ")) % frame.GetLevel();
130 wxString filename = frame.GetName();
132 if (!filename.IsEmpty()) {
133 btLine += filename + wxT(" (") +
134 #if TOO_VERBOSE_BACKTRACE
135 frame.GetModule()
136 #else
137 frame.GetModule().AfterLast(wxT('/'))
138 #endif
139 + wxT(")");
140 } else {
141 btLine += CFormat(wxT("%p")) % frame.GetAddress();
144 if (frame.HasSourceLocation()) {
145 btLine += wxT(" at ") +
146 #if TOO_VERBOSE_BACKTRACE
147 frame.GetFileName()
148 #else
149 frame.GetFileName().AfterLast(wxT('/'))
150 #endif
151 + CFormat(wxT(":%u")) % frame.GetLine();
152 } else {
153 btLine += wxT(" (Unknown file/line)");
156 //! Contains the entire backtrace
157 m_trace += btLine + wxT("\n");
160 wxString m_trace;
164 wxString get_backtrace(unsigned n)
166 MuleStackWalker walker; // Texas ranger?
167 walker.Walk(n); // Skip this one and Walk() also!
169 return walker.m_trace;
172 #elif defined(__LINUX__)
174 #ifdef HAVE_BFD
176 static bfd* s_abfd;
177 static asymbol** s_symbol_list;
178 static bool s_have_backtrace_symbols = false;
179 static const char* s_file_name;
180 static const char* s_function_name;
181 static unsigned int s_line_number;
182 static int s_found;
186 * read all symbols in the executable into an array
187 * and return the pointer to the array in symbol_list.
188 * Also return the number of actual symbols read
189 * If there's any error, return -1
191 static int get_backtrace_symbols(bfd *abfd, asymbol ***symbol_list_ptr)
193 int vectorsize = bfd_get_symtab_upper_bound(abfd);
195 if (vectorsize < 0) {
196 fprintf (stderr, "Error while getting vector size for backtrace symbols : %s",
197 bfd_errmsg(bfd_get_error()));
198 return -1;
201 if (vectorsize == 0) {
202 fprintf (stderr, "Error while getting backtrace symbols : No symbols (%s)",
203 bfd_errmsg(bfd_get_error()));
204 return -1;
207 *symbol_list_ptr = (asymbol**)malloc(vectorsize);
209 if (*symbol_list_ptr == NULL) {
210 fprintf (stderr, "Error while getting backtrace symbols : Cannot allocate memory");
211 return -1;
214 vectorsize = bfd_canonicalize_symtab(abfd, *symbol_list_ptr);
216 if (vectorsize < 0) {
217 fprintf(stderr, "Error while getting symbol table : %s",
218 bfd_errmsg(bfd_get_error()));
219 return -1;
222 return vectorsize;
227 * print file, line and function information for address
228 * The info is actually set into global variables. This
229 * function is called from the iterator bfd_map_over_sections
232 void init_backtrace_info()
234 bfd_init();
235 s_abfd = bfd_openr("/proc/self/exe", NULL);
237 if (s_abfd == NULL) {
238 fprintf(stderr, "Error while opening file for backtrace symbols : %s",
239 bfd_errmsg(bfd_get_error()));
240 return;
243 if (!(bfd_check_format_matches(s_abfd, bfd_object, NULL))) {
244 fprintf (stderr, "Error while init. backtrace symbols : %s",
245 bfd_errmsg (bfd_get_error ()));
246 bfd_close(s_abfd);
247 return;
250 s_have_backtrace_symbols = (get_backtrace_symbols(s_abfd, &s_symbol_list) > 0);
254 void get_file_line_info(bfd *abfd, asection *section, void* _address)
256 wxASSERT(s_symbol_list);
258 if (s_found) {
259 return;
262 if ((section->flags & SEC_ALLOC) == 0) {
263 return;
266 bfd_vma vma = bfd_get_section_vma(abfd, section);
268 unsigned long address = (unsigned long)_address;
269 if (address < vma) {
270 return;
273 bfd_size_type size = bfd_section_size(abfd, section);
274 if (address > (vma + size)) {
275 return;
278 s_found = bfd_find_nearest_line(abfd, section, s_symbol_list,
279 address - vma, &s_file_name, &s_function_name, &s_line_number);
282 #endif // HAVE_BFD
284 wxString demangle(const wxString& function)
286 #ifdef HAVE_CXXABI
287 wxString result;
289 if (function.Mid(0,2) == wxT("_Z")) {
290 int status;
291 char *demangled = abi::__cxa_demangle(function.mb_str(), NULL, NULL, &status);
293 if (!status) {
294 result = wxConvCurrent->cMB2WX(demangled);
297 if (demangled) {
298 free(demangled);
302 return result;
303 #else
304 return wxEmptyString;
305 #endif
309 // Print a stack backtrace if available
310 wxString get_backtrace(unsigned n)
312 #ifdef HAVE_EXECINFO
313 // (stkn) create backtrace
314 void *bt_array[100]; // 100 should be enough ?!?
315 char **bt_strings;
316 int num_entries;
318 if ((num_entries = backtrace(bt_array, 100)) < 0) {
319 fprintf(stderr, "* Could not generate backtrace\n");
320 return wxEmptyString;
323 if ((bt_strings = backtrace_symbols(bt_array, num_entries)) == NULL) {
324 fprintf(stderr, "* Could not get symbol names for backtrace\n");
325 return wxEmptyString;
328 std::vector<wxString> libname(num_entries);
329 std::vector<wxString> funcname(num_entries);
330 std::vector<wxString> address(num_entries);
331 wxString AllAddresses;
333 for (int i = 0; i < num_entries; ++i) {
334 wxString wxBtString = wxConvCurrent->cMB2WX(bt_strings[i]);
335 int posLPar = wxBtString.Find(wxT('('));
336 int posRPar = wxBtString.Find(wxT(')'));
337 int posLBra = wxBtString.Find(wxT('['));
338 int posRBra = wxBtString.Find(wxT(']'));
339 bool hasFunction = true;
340 if (posLPar == -1 || posRPar == -1) {
341 if (posLBra == -1 || posRBra == -1) {
342 /* It is important to have exactly num_entries
343 * addresses in AllAddresses */
344 AllAddresses += wxT("0x0000000 ");
345 continue;
347 posLPar = posLBra;
348 hasFunction = false;
350 /* Library name */
351 int len = posLPar;
352 libname[i] = wxBtString.Mid(0, len);
353 /* Function name */
354 if (hasFunction) {
355 int posPlus = wxBtString.Find(wxT('+'), true);
356 if (posPlus == -1)
357 posPlus = posRPar;
358 len = posPlus - posLPar - 1;
359 funcname[i] = wxBtString.Mid(posLPar + 1, len);
360 wxString demangled = demangle(funcname[i]);
361 if (!demangled.IsEmpty()) {
362 funcname[i] = demangled;
365 /* Address */
366 if ( posLBra == -1 || posRBra == -1) {
367 address[i] = wxT("0x0000000");
368 } else {
369 len = posRBra - posLBra - 1;
370 address[i] = wxBtString.Mid(posLBra + 1, len);
371 AllAddresses += address[i] + wxT(" ");
374 free(bt_strings);
376 /* Get line numbers from addresses */
377 wxArrayString out;
378 bool hasLineNumberInfo = false;
380 #ifdef HAVE_BFD
381 if (!s_have_backtrace_symbols) {
382 init_backtrace_info();
383 wxASSERT(s_have_backtrace_symbols);
386 for (int i = 0; i < num_entries; ++i) {
387 s_file_name = NULL;
388 s_function_name = NULL;
389 s_line_number = 0;
390 s_found = false ;
392 unsigned long addr;
393 address[i].ToULong(&addr,0); // As it's "0x" prepended, wx will read it as base 16. Hopefully.
395 bfd_map_over_sections(s_abfd, get_file_line_info, (void*)addr);
397 if (s_found) {
398 wxString function = wxConvCurrent->cMB2WX(s_function_name);
399 wxString demangled = demangle(function);
400 if (!demangled.IsEmpty()) {
401 function = demangled;
402 funcname[i] = demangled;
404 out.Insert(wxConvCurrent->cMB2WX(s_function_name),i*2);
405 out.Insert(wxConvCurrent->cMB2WX(s_file_name) + (CFormat(wxT(":%u")) % s_line_number),i*2+1);
406 } else {
407 out.Insert(wxT("??"),i*2);
408 out.Insert(wxT("??"),i*2+1);
412 hasLineNumberInfo = true;
414 #else /* !HAVE_BFD */
415 if (wxThread::IsMain()) {
416 wxString command;
417 command << wxT("addr2line -C -f -s -e /proc/") <<
418 getpid() << wxT("/exe ") << AllAddresses;
419 // The output of the command is this wxArrayString, in which
420 // the even elements are the function names, and the odd elements
421 // are the line numbers.
423 hasLineNumberInfo = wxExecute(command, out) != -1;
426 #endif /* HAVE_BFD / !HAVE_BFD */
428 wxString trace;
429 // Remove 'n+1' first entries (+1 because of this function)
430 for (int i = n+1; i < num_entries; ++i) {
431 /* If we have no function name, use the result from addr2line */
432 if (funcname[i].IsEmpty()) {
433 if (hasLineNumberInfo) {
434 funcname[i] = out[2*i];
435 } else {
436 funcname[i] = wxT("??");
439 wxString btLine;
440 btLine << wxT("[") << i << wxT("] ") << funcname[i] << wxT(" in ");
441 /* If addr2line did not find a line number, use bt_string */
442 if (!hasLineNumberInfo || out[2*i+1].Mid(0,2) == wxT("??")) {
443 btLine += libname[i] + wxT("[") + address[i] + wxT("]");
444 } else if (hasLineNumberInfo) {
445 #if TOO_VERBOSE_BACKTRACE
446 btLine += out[2*i+1];
447 #else
448 btLine += out[2*i+1].AfterLast(wxT('/'));
449 #endif
450 } else {
451 btLine += libname[i];
454 trace += btLine + wxT("\n");
457 return trace;
458 #else /* !HAVE_EXECINFO */
459 fprintf(stderr, "--== cannot generate backtrace ==--\n\n");
460 return wxEmptyString;
461 #endif /* HAVE_EXECINFO */
464 #else /* !__LINUX__ */
466 wxString get_backtrace(unsigned WXUNUSED(n))
468 fprintf(stderr, "--== no BACKTRACE for your platform ==--\n\n");
469 return wxEmptyString;
472 #endif /* !__LINUX__ */
474 void print_backtrace(unsigned n)
476 wxString trace = get_backtrace(n);
478 // This is because the string is ansi anyway, and the conv classes are very slow
479 fprintf(stderr, "%s\n", (const char*)unicode2char(trace.c_str()));
482 // File_checked_for_headers