1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
16 #include <mono/jit/jit.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/assembly.h>
19 #include <mono/metadata/debug-helpers.h>
21 /* because this header sucks */
22 #include <mono/metadata/mono-debug.h>
24 #include <mono/metadata/mono-config.h>
25 #include <mono/metadata/threads.h>
28 #include <sys/syscall.h>
35 #define UNW_LOCAL_ONLY
36 #include <libunwind.h>
43 // Define to enable stack traces for managed frames.
44 #define MONO_STACK_ENABLED 1
47 static bool vm_stack_trace_enabled
= false;
50 enable_vm_stack_trace (void)
52 vm_stack_trace_enabled
= true;
56 get_method_name_from_ip (void *ip
)
58 if (!vm_stack_trace_enabled
)
61 #if MONO_STACK_ENABLED
66 MonoDomain
*domain
= mono_domain_get ();
68 ji
= mono_jit_info_table_find (domain
, (char*) ip
);
72 mi
= mono_jit_info_get_method (ji
);
73 method
= mono_method_full_name (mi
, TRUE
);
75 res
= g_strdup_printf ("%s", method
);
86 get_method_from_ip (void *ip
)
88 if (!vm_stack_trace_enabled
)
91 #if MONO_STACK_ENABLED
98 MonoDomain
*domain
= mono_domain_get ();
99 MonoDebugSourceLocation
*location
;
101 ji
= mono_jit_info_table_find (domain
, (char*) ip
);
105 mi
= mono_jit_info_get_method (ji
);
106 jit_start
= mono_jit_info_get_code_start (ji
);
107 jit_size
= mono_jit_info_get_code_size (ji
);
108 method
= mono_method_full_name (mi
, TRUE
);
110 location
= mono_debug_lookup_source_location (mi
, (guint32
)((guint8
*)ip
- (guint8
*)jit_start
), domain
);
113 res
= g_strdup_printf (" %s in %s:%i,%i", method
, location
->source_file
, location
->row
, location
->column
);
115 res
= g_strdup_printf (" %s + 0x%x", method
, (int)((char*)ip
- (char*)jit_start
));
117 mono_debug_free_source_location (location
);
127 char* get_stack_trace (void)
129 return get_stack_trace_prefix ("\t");
132 void print_stack_trace (void)
134 print_stack_trace_prefix ("\t");
137 static char tohex
[16] = {
138 '0', '1', '2', '3', '4', '5', '6', '7',
139 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
143 hexdump_addr (void *addr
, size_t n
)
145 const unsigned char *mem
= (const unsigned char *) addr
;
146 char outbuf
[80], *outptr
;
150 for (i
= 0; i
< n
; i
+= 16) {
153 // write out the offset
154 *outptr
++ = tohex
[(i
>> 28) & 0xf];
155 *outptr
++ = tohex
[(i
>> 24) & 0xf];
156 *outptr
++ = tohex
[(i
>> 20) & 0xf];
157 *outptr
++ = tohex
[(i
>> 16) & 0xf];
158 *outptr
++ = tohex
[(i
>> 12) & 0xf];
159 *outptr
++ = tohex
[(i
>> 8) & 0xf];
160 *outptr
++ = tohex
[(i
>> 4) & 0xf];
161 *outptr
++ = tohex
[i
& 0xf];
163 // write out up to 16 hex-encoded octets
164 for (j
= i
; j
< n
&& j
< i
+ 16; j
++) {
170 *outptr
++ = tohex
[(c
>> 4) & 0xf];
171 *outptr
++ = tohex
[c
& 0xf];
174 // pad out to the expected column
175 for ( ; j
< i
+ 16; j
++) {
188 // write out up to 16 raw octets
189 for (j
= i
; j
< n
&& j
< i
+ 16; j
++) {
191 if (isprint ((int) c
)) {
192 *outptr
++ = (char) c
;
201 fputs (outbuf
, stdout
);
205 typedef struct Addr2LineData Addr2LineData
;
207 struct Addr2LineData
{
216 static __thread Addr2LineData
*addr2line_pipes
= NULL
;
219 library_of_ip (gpointer ip
, gpointer
* base_address
)
221 /* non-linux platforms will need different code here */
223 FILE* maps
= fopen ("/proc/self/maps", "r");
224 char * buffer
= NULL
;
225 size_t buffer_length
= 0;
227 char* current_library
= NULL
;
228 gpointer current_base_address
= NULL
;
230 char entire_line
[2000];
233 gint64 buffer_read
= getline (&buffer
, &buffer_length
, maps
);
238 memcpy (entire_line
, buffer
, buffer_read
);
239 entire_line
[buffer_read
+ 1] = 0;
241 if (buffer_read
< 20)
244 buffer
[buffer_read
- 1] = 0; // Strip off the newline.
246 const char delimiters
[] = " ";
247 char *saveptr
= NULL
;
248 char *range
= strtok_r (buffer
, delimiters
, &saveptr
);
249 char *a
= strtok_r (NULL
, delimiters
, &saveptr
);
250 char *b
= strtok_r (NULL
, delimiters
, &saveptr
);
251 char *c
= strtok_r (NULL
, delimiters
, &saveptr
);
252 char *d
= strtok_r (NULL
, delimiters
, &saveptr
);
253 char *lib
= strtok_r (NULL
, delimiters
, &saveptr
);
256 current_library
= NULL
;
260 if (lib
[0] != '/' && lib
[0] != '[') {
261 printf ("Something's wrong, lib: %s\n", lib
);
262 printf ("range: %s, a: %s, b: %s, c: %s, d: %s, lib: %s, line: %s",
263 range
, a
, b
, c
, d
, lib
, entire_line
);
267 char* start_range
= strtok_r (range
, "-", &saveptr
);
268 char* end_range
= strtok_r (NULL
, "-", &saveptr
);
271 start
= start_range
? (gpointer
) strtoull (start_range
, &tail
, 16) : NULL
;
272 end
= end_range
? (gpointer
) strtoull (end_range
, &tail
, 16) : NULL
;
274 if (current_library
== NULL
|| strcmp (lib
, current_library
) != 0) {
275 current_library
= lib
;
277 current_base_address
= start
;
279 if (start
<= ip
&& end
>= ip
) {
280 result
= g_strdup (lib
);
281 *base_address
= current_base_address
;
282 // printf ("IP %p is in library %s\n", ip, result);
293 static char* addr2line_offset (gpointer ip
, bool use_offset
);
296 addr2line (gpointer ip
)
298 char* result
= addr2line_offset (ip
, true);
300 result
= addr2line_offset (ip
, false);
306 addr2line_offset (gpointer ip
, bool use_offset
)
309 Addr2LineData
*addr2line
;
310 gpointer base_address
;
312 char* binary
= library_of_ip (ip
, &base_address
);
313 //printf ("library_of_ip (%p, %p): %s\n", ip, base_address, binary);
318 if (binary
[0] == '[') {
323 for (addr2line
= addr2line_pipes
; addr2line
; addr2line
= addr2line
->next
) {
324 if (strcmp (binary
, addr2line
->binary
) == 0)
329 const char *addr_argv
[] = {"addr2line", "-f", "-e", binary
, "-C", NULL
};
333 if (!g_spawn_async_with_pipes (NULL
, (char**)addr_argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
,
334 &child_pid
, &ch_in
, &ch_out
, NULL
, NULL
)) {
339 addr2line
= g_new0 (Addr2LineData
, 1);
340 addr2line
->base
= base_address
;
341 addr2line
->child_pid
= child_pid
;
342 addr2line
->binary
= g_strdup (binary
);
343 addr2line
->pipein
= fdopen (ch_in
, "w");
344 addr2line
->pipeout
= fdopen (ch_out
, "r");
345 addr2line
->next
= addr2line_pipes
;
346 addr2line_pipes
= addr2line
;
353 offset
= (gpointer
) (((size_t) ip
) - ((size_t) addr2line
->base
));
357 // printf ("Checking ip: %p, offset: %p, base: %p\n", ip, offset, addr2line->base);
358 fprintf (addr2line
->pipein
, "%p\n", offset
);
359 fflush (addr2line
->pipein
);
361 /* we first get the func name and then file:lineno in a second line */
368 result
= fgets (buf
, sizeof (buf
), addr2line
->pipeout
);
373 if (result
[0] == '?' || result
[0] == 0)
376 result_length
= strlen (result
);
377 result
[result_length
- 1] = 0;
380 result
= fgets (buf
+ result_length
, sizeof (buf
) - result_length
, addr2line
->pipeout
);
385 result_length
= strlen (result
);
386 result
[result_length
- 1] = 0;
389 res
= g_strdup_printf ("%s [%p] %s %s", addr2line
->binary
, ip
, first
, second
);
391 // printf ("Final result: %s\n", res);
397 get_managed_frame (gpointer ip
)
399 return get_method_from_ip (ip
);
403 get_stack_trace_prefix (const char* prefix
, int maxframes
)
407 int total_length
= 0;
408 int prefix_length
= strlen (prefix
);
409 void *ips
[maxframes
];
410 char *frames
[maxframes
];
413 address_count
= backtrace (ips
, maxframes
);
415 for (int i
= 2; i
< address_count
; i
++) {
418 char* frame
= addr2line (ip
);
420 if (frame
== NULL
&& mono_domain_get ())
421 frame
= get_managed_frame (ip
);
423 if (frame
== NULL
|| strlen (frame
) == 0 || frame
[0] == '?') {
425 names
= backtrace_symbols (&ip
, 1);
426 frame
= g_strdup (names
[0]);
430 total_length
+= prefix_length
+ strlen (frame
) + 1;
433 char* result
= (char*) g_malloc0 (total_length
+ 1);
435 for (int i
= 2; i
< address_count
; i
++) {
436 char* frame
= frames
[i
];
437 size_t frame_length
= strlen (frame
);
439 memcpy (result
+ position
, prefix
, prefix_length
);
440 position
+= prefix_length
;
441 memcpy (result
+ position
, frame
, frame_length
);
442 position
+= frame_length
;
443 memcpy (result
+ position
, "\n", 1);
453 print_stack_trace_prefix (const char* prefix
, int maxframes
)
455 char* st
= get_stack_trace_prefix (prefix
, maxframes
);
470 struct Frame
: List::Node
{
478 virtual char *ToString ()
480 return g_strdup_printf ("%d|%lx|%s", type
, ptr
, name
);
484 struct Frames
: List::Node
{
496 List
*allframes
= NULL
;
500 print_reftrace (const char * act
, const char * typname
, int refcount
, bool keep
)
503 unw_cursor_t cursor
; unw_context_t uc
;
504 unw_word_t ip
, sp
, bp
;
507 unw_init_local(&cursor
, &uc
);
509 char framename
[1024];
511 List
*frames
= new List ();
514 while (unw_step(&cursor
) > 0 && count
< MAX_STACK_FRAMES
) {
516 frame
= new Frame ();
518 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
519 unw_get_reg(&cursor
, UNW_REG_SP
, &sp
);
521 unw_get_reg(&cursor
, UNW_X86_EBP
, &bp
);
523 unw_get_reg(&cursor
, UNW_X86_64_RBP
, &bp
);
526 unw_get_proc_name (&cursor
, framename
, sizeof(framename
), 0);
529 if (mono_domain_get ()) {
530 frame
->ptr
= (long)ip
;
531 char * ret
= get_method_name_from_ip ((void*)ip
);
532 frame
->name
= g_strdup (ret
);
541 char * demangled
= cplus_demangle (framename
, 0);
543 if (strstr (demangled
, ":") > 0) {
544 frame
->ptr
= *(int*)(bp
+8);
545 frame
->name
= g_strdup (demangled
);
546 frame
->type
= INSTANCE
;
548 frame
->ptr
= (long)bp
;
549 frame
->name
= g_strdup (demangled
);
550 frame
->type
= STATIC
;
553 frame
->ptr
= (long)bp
;
554 frame
->name
= g_strdup (framename
);
559 frames
->Append (frame
);
564 if (allframes
== NULL
)
565 allframes
= new List ();
566 if (allframes
->Length() % 50)
568 Frames
*f
= new Frames ();
570 f
->act
= g_strdup (act
);
571 f
->typname
= g_strdup (typname
);
572 f
->refcount
= refcount
;
573 allframes
->Append (f
);
576 printf("trace:%s|%s|%d;", act
, typname
,refcount
);
577 frame
= (Frame
*)frames
->First ();
578 while (frame
!= NULL
) {
579 char *s
= frame
->ToString ();
582 frame
= (Frame
*)frame
->next
;
592 void dump_frames (void)
595 Frames
*frames
= (Frames
*)allframes
->First ();
596 while (frames
!= NULL
) {
597 printf("trace:%s|%s|%d;", frames
->act
, frames
->typname
, frames
->refcount
);
598 Frame
*frame
= (Frame
*)frames
->list
->First ();
599 while (frame
!= NULL
) {
600 char *s
= frame
->ToString ();
603 frame
= (Frame
*)frame
->next
;
606 frames
= (Frames
*)frames
->next
;
608 allframes
->Clear (true);
617 * moonlight_handle_native_sigsegv:
618 * (this is a slightly modified version of mono_handle_native_sigsegv from mono/mono/mini/mini-exceptions.c)
623 static bool handlers_installed
= false;
624 static bool handling_sigsegv
= false;
629 /* Try to get more meaningful information using gdb */
630 #if !defined(PLATFORM_WIN32)
631 /* From g_spawn_command_line_sync () in eglib */
633 int stdout_pipe
[2] = { -1, -1 };
635 const char *argv
[16];
640 res
= pipe (stdout_pipe
);
641 g_assert (res
!= -1);
644 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
645 * it will deadlock. Call the syscall directly instead.
647 pid
= syscall (SYS_fork
);
649 close (stdout_pipe
[0]);
650 dup2 (stdout_pipe
[1], STDOUT_FILENO
);
652 for (int i
= getdtablesize () - 1; i
>= 3; i
--)
655 argv
[0] = g_find_program_in_path ("gdb");
656 if (argv
[0] == NULL
) {
657 close (STDOUT_FILENO
);
662 sprintf (buf1
, "attach %ld", (long)getpid ());
665 argv
[4] = "info threads";
667 argv
[6] = "thread apply all bt";
668 argv
[7] = "--batch";
671 execv (argv
[0], (char**)argv
);
675 close (stdout_pipe
[1]);
677 fprintf (stderr
, "\nDebug info from gdb:\n\n");
680 int nread
= read (stdout_pipe
[0], buffer
, 1024);
684 write (STDERR_FILENO
, buffer
, nread
);
687 waitpid (pid
, &status
, WNOHANG
);
693 static moonlight_handle_native_sigsegv (int signal
)
695 const char *signal_str
;
698 case SIGSEGV
: signal_str
= "SIGSEGV"; break;
699 case SIGFPE
: signal_str
= "SIGFPE"; break;
700 case SIGABRT
: signal_str
= "SIGABRT"; break;
701 case SIGQUIT
: signal_str
= "SIGQUIT"; break;
702 default: signal_str
= "UNKNOWN"; break;
705 if (handling_sigsegv
) {
707 * In our normal sigsegv handling we do signal-unsafe things to provide better
708 * output to what actually happened. If we get another one, do only signal-safe
715 /* To prevent infinite loops when the stack walk causes a crash */
716 handling_sigsegv
= true;
719 * A SIGSEGV indicates something went very wrong so we can no longer depend
720 * on anything working. So try to print out lots of diagnostics, starting
721 * with ones which have a greater chance of working.
725 "=============================================================\n"
726 "Got a %s while executing native code. \n"
727 " We'll first ask gdb for a stack trace, then try our own \n"
728 " stack walking method (usually not as good as gdb, but it \n"
729 " can do managed and native stack traces together) \n"
730 "=============================================================\n"
735 fprintf (stderr
, "\nDebug info from libmoon:\n\n");
736 print_stack_trace ();
738 if (signal
!= SIGQUIT
) {
741 handling_sigsegv
= false;
746 moonlight_install_signal_handlers ()
750 if (handlers_installed
)
752 handlers_installed
= true;
754 printf ("Moonlight: Installing signal handlers for crash reporting.\n");
756 sa
.sa_handler
= moonlight_handle_native_sigsegv
;
757 sigemptyset (&sa
.sa_mask
);
760 g_assert (sigaction (SIGSEGV
, &sa
, NULL
) != -1);
761 g_assert (sigaction (SIGFPE
, &sa
, NULL
) != -1);
762 g_assert (sigaction (SIGQUIT
, &sa
, NULL
) != -1);