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
34 #include "Format.h" // Needed for CFormat
37 # include <execinfo.h>
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
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
67 std::set_terminate(std::abort
);
70 std::type_info
*t
= __cxxabiv1::__cxa_current_exception_type();
71 FILE* output
= stderr
;
73 FILE* output
= stdout
;
80 // Note that "name" is the mangled name.
81 char const *name
= t
->name();
83 dem
= __cxxabiv1::__cxa_demangle(name
, 0, 0, &status
);
85 const char* name
= "Unknown";
87 fprintf(output
, "\nTerminated after throwing an instance of '%s'\n", (status
? name
: dem
));
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
));
99 // Unable to retrieve cause of exception
102 fprintf(output
, "\tbacktrace:\n%s\n", (const char*)unicode2char(get_backtrace(1)));
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
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
137 frame
.GetModule().AfterLast(wxT('/'))
141 btLine
+= CFormat(wxT("%p")) % frame
.GetAddress();
144 if (frame
.HasSourceLocation()) {
145 btLine
+= wxT(" at ") +
146 #if TOO_VERBOSE_BACKTRACE
149 frame
.GetFileName().AfterLast(wxT('/'))
151 + CFormat(wxT(":%u")) % frame
.GetLine();
153 btLine
+= wxT(" (Unknown file/line)");
156 //! Contains the entire backtrace
157 m_trace
+= btLine
+ wxT("\n");
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__)
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
;
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()));
201 if (vectorsize
== 0) {
202 fprintf (stderr
, "Error while getting backtrace symbols : No symbols (%s)",
203 bfd_errmsg(bfd_get_error()));
207 *symbol_list_ptr
= (asymbol
**)malloc(vectorsize
);
209 if (*symbol_list_ptr
== NULL
) {
210 fprintf (stderr
, "Error while getting backtrace symbols : Cannot allocate memory");
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()));
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()
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()));
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 ()));
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
);
262 if ((section
->flags
& SEC_ALLOC
) == 0) {
266 bfd_vma vma
= bfd_get_section_vma(abfd
, section
);
268 unsigned long address
= (unsigned long)_address
;
273 bfd_size_type size
= bfd_section_size(abfd
, section
);
274 if (address
> (vma
+ size
)) {
278 s_found
= bfd_find_nearest_line(abfd
, section
, s_symbol_list
,
279 address
- vma
, &s_file_name
, &s_function_name
, &s_line_number
);
284 wxString
demangle(const wxString
& function
)
289 if (function
.Mid(0,2) == wxT("_Z")) {
291 char *demangled
= abi::__cxa_demangle(function
.mb_str(), NULL
, NULL
, &status
);
294 result
= wxConvCurrent
->cMB2WX(demangled
);
304 return wxEmptyString
;
309 // Print a stack backtrace if available
310 wxString
get_backtrace(unsigned n
)
313 // (stkn) create backtrace
314 void *bt_array
[100]; // 100 should be enough ?!?
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 ");
352 libname
[i
] = wxBtString
.Mid(0, len
);
355 int posPlus
= wxBtString
.Find(wxT('+'), true);
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
;
366 if ( posLBra
== -1 || posRBra
== -1) {
367 address
[i
] = wxT("0x0000000");
369 len
= posRBra
- posLBra
- 1;
370 address
[i
] = wxBtString
.Mid(posLBra
+ 1, len
);
371 AllAddresses
+= address
[i
] + wxT(" ");
376 /* Get line numbers from addresses */
378 bool hasLineNumberInfo
= false;
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
) {
388 s_function_name
= NULL
;
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
);
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);
407 out
.Insert(wxT("??"),i
*2);
408 out
.Insert(wxT("??"),i
*2+1);
412 hasLineNumberInfo
= true;
414 #else /* !HAVE_BFD */
415 if (wxThread::IsMain()) {
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 */
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
];
436 funcname
[i
] = wxT("??");
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];
448 btLine
+= out
[2*i
+1].AfterLast(wxT('/'));
451 btLine
+= libname
[i
];
454 trace
+= btLine
+ wxT("\n");
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