2 // This file is part of the aMule Project.
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 )
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
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()
29 # include "config.h" // Needed for HAVE_CXXABI and HAVE_EXECINFO
32 #include "MuleDebug.h" // Interface declaration
33 #include "StringFunctions.h" // Needed for unicode2char
36 # include <execinfo.h>
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
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
66 std::set_terminate(std::abort
);
69 std::type_info
*t
= __cxxabiv1::__cxa_current_exception_type();
70 FILE* output
= stderr
;
72 FILE* output
= stdout
;
79 // Note that "name" is the mangled name.
80 char const *name
= t
->name();
82 dem
= __cxxabiv1::__cxa_demangle(name
, 0, 0, &status
);
84 const char* name
= "Unknown";
86 fprintf(output
, "\nTerminated after throwing an instance of '%s'\n", (status
? name
: dem
));
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
));
98 // Unable to retrieve cause of exception
101 fprintf(output
, "\tbacktrace:\n%s\n", (const char*)unicode2char(get_backtrace(1)));
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
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
136 frame
.GetModule().AfterLast(wxT('/'))
140 btLine
+= wxString::Format(wxT("0x%lx"), frame
.GetAddress());
143 if (frame
.HasSourceLocation()) {
144 btLine
+= wxT(" at ") +
145 #if TOO_VERBOSE_BACKTRACE
148 frame
.GetFileName().AfterLast(wxT('/'))
150 + wxString::Format(wxT(":%u"),frame
.GetLine());
152 btLine
+= wxT(" (Unknown file/line)");
155 //! Contains the entire backtrace
156 m_trace
+= btLine
+ wxT("\n");
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__)
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
;
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()));
200 if (vectorsize
== 0) {
201 fprintf (stderr
, "Error while getting backtrace symbols : No symbols (%s)",
202 bfd_errmsg(bfd_get_error()));
206 *symbol_list_ptr
= (asymbol
**)malloc(vectorsize
);
208 if (*symbol_list_ptr
== NULL
) {
209 fprintf (stderr
, "Error while getting backtrace symbols : Cannot allocate memory");
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()));
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()
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()));
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 ()));
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
);
261 if ((section
->flags
& SEC_ALLOC
) == 0) {
265 bfd_vma vma
= bfd_get_section_vma(abfd
, section
);
267 unsigned long address
= (unsigned long)_address
;
272 bfd_size_type size
= bfd_section_size(abfd
, section
);
273 if (address
> (vma
+ size
)) {
277 s_found
= bfd_find_nearest_line(abfd
, section
, s_symbol_list
,
278 address
- vma
, &s_file_name
, &s_function_name
, &s_line_number
);
283 wxString
demangle(const wxString
& function
)
288 if (function
.Mid(0,2) == wxT("_Z")) {
290 char *demangled
= abi::__cxa_demangle(function
.mb_str(), NULL
, NULL
, &status
);
293 result
= wxConvCurrent
->cMB2WX(demangled
);
303 return wxEmptyString
;
308 // Print a stack backtrace if available
309 wxString
get_backtrace(unsigned n
)
312 // (stkn) create backtrace
313 void *bt_array
[100]; // 100 should be enough ?!?
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 ");
351 libname
[i
] = wxBtString
.Mid(0, len
);
354 int posPlus
= wxBtString
.Find(wxT('+'), true);
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
;
365 if ( posLBra
== -1 || posRBra
== -1) {
366 address
[i
] = wxT("0x0000000");
368 len
= posRBra
- posLBra
- 1;
369 address
[i
] = wxBtString
.Mid(posLBra
+ 1, len
);
370 AllAddresses
+= address
[i
] + wxT(" ");
375 /* Get line numbers from addresses */
377 bool hasLineNumberInfo
= false;
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
) {
387 s_function_name
= NULL
;
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
);
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);
406 out
.Insert(wxT("??"),i
*2);
407 out
.Insert(wxT("??"),i
*2+1);
411 hasLineNumberInfo
= true;
413 #else /* !HAVE_BFD */
414 if (wxThread::IsMain()) {
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 */
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
];
435 funcname
[i
] = wxT("??");
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];
447 btLine
+= out
[2*i
+1].AfterLast(wxT('/'));
450 btLine
+= libname
[i
];
453 trace
+= btLine
+ wxT("\n");
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