Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mono / mini / mini-exceptions-native-unwinder.c
1 /*
2  * mini-exceptions-native-unwinder.c: libcorkscrew-based native unwinder
3  *
4  * Authors:
5  *   Alex Rønne Petersen (alexrp@xamarin.com)
6  *
7  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10 #include <config.h>
11
12 /*
13  * Attempt to handle native SIGSEGVs with libunwind or libcorkscrew.
14  */
15
16 #ifdef HAVE_SIGNAL_H
17 #include <signal.h>
18 #endif
19
20 #include <mono/utils/mono-signal-handler.h>
21 #include "mini.h"
22
23 #if defined (PLATFORM_ANDROID)
24
25 #include <signal.h>
26 #include <sys/types.h>
27 #include <mono/utils/mono-dl.h>
28
29 #define UNW_LOCAL_ONLY
30 #undef _U /* ctype.h apparently defines this and it screws up the libunwind headers. */
31 #include "android-libunwind/libunwind.h"
32 #define _U 0x01
33
34 #define FUNC_NAME_LENGTH 512
35 #define FRAMES_TO_UNWIND 256
36
37 /* Expand the SYM argument. */
38 #define LOAD_SYM(DL, ERR, SYM, VAR) _LOAD_SYM(DL, ERR, SYM, VAR)
39 #define _LOAD_SYM(DL, ERR, SYM, VAR) \
40         do { \
41                 if ((ERR = mono_dl_symbol (DL, #SYM, (void **) &VAR))) { \
42                         mono_dl_close (DL); \
43                         return ERR; \
44                 } \
45         } while (0)
46
47 typedef int (*unw_init_local_t) (unw_cursor_t *, unw_context_t *);
48 typedef int (*unw_get_reg_t) (unw_cursor_t *, int, unw_word_t *);
49 typedef int (*unw_get_proc_name_t) (unw_cursor_t *, char *, size_t, unw_word_t *);
50 typedef int (*unw_step_t) (unw_cursor_t *);
51
52 static char *
53 mono_extension_handle_native_sigsegv_libunwind (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
54 {
55         char *dl_err;
56         int unw_err;
57
58         unw_init_local_t unw_init_local_fn;
59         unw_get_reg_t unw_get_reg_fn;
60         unw_get_proc_name_t unw_get_proc_name_fn;
61         unw_step_t unw_step_fn;
62
63         unw_cursor_t cursor;
64
65         size_t frames = 0;
66
67         MonoDl *dl = mono_dl_open ("libunwind.so", MONO_DL_LAZY, &dl_err);
68
69         if (!dl)
70                 return dl_err;
71
72         LOAD_SYM (dl, dl_err, UNW_OBJ (init_local), unw_init_local_fn);
73         LOAD_SYM (dl, dl_err, UNW_OBJ (get_reg), unw_get_reg_fn);
74         LOAD_SYM (dl, dl_err, UNW_OBJ (get_proc_name), unw_get_proc_name_fn);
75         LOAD_SYM (dl, dl_err, UNW_OBJ (step), unw_step_fn);
76
77         if ((unw_err = unw_init_local_fn (&cursor, ctx))) {
78                 mono_dl_close (dl);
79
80                 return g_strdup_printf ("unw_init_local () returned %d", unw_err);
81         }
82
83         do {
84                 int reg_err;
85
86                 unw_word_t ip, off;
87                 char name [FUNC_NAME_LENGTH];
88
89                 if ((reg_err = unw_get_reg_fn (&cursor, UNW_REG_IP, &ip))) {
90                         mono_runtime_printf_err ("unw_get_reg (UNW_REG_IP) returned %d", reg_err);
91                         break;
92                 }
93
94                 reg_err = unw_get_proc_name_fn (&cursor, name, FUNC_NAME_LENGTH, &off);
95
96                 if (reg_err == -UNW_ENOINFO)
97                         strcpy (name, "???");
98
99                 mono_runtime_printf_err (" at %s+%zu [0x%zx]", name, off, ip);
100
101                 unw_err = unw_step_fn (&cursor);
102                 frames++;
103         } while (unw_err > 0 && frames < FRAMES_TO_UNWIND);
104
105         if (unw_err < 0)
106                 mono_runtime_printf_err ("unw_step () returned %d", unw_err);
107
108         mono_dl_close (dl);
109
110         return NULL;
111 }
112
113 /*
114  * This code is based on the AOSP header system/core/include/corkscrew/backtrace.h.
115  *
116  * This is copied here because libcorkscrew is not a stable library and the header (and
117  * other headers that it depends on) will eventually go away.
118  *
119  * We can probably remove this one day when libunwind becomes the norm.
120  */
121
122 typedef struct {
123         uintptr_t absolute_pc;
124         uintptr_t stack_top;
125         size_t stack_size;
126 } backtrace_frame_t;
127
128 typedef struct {
129         uintptr_t relative_pc;
130         uintptr_t relative_symbol_addr;
131         char *map_name;
132         char *symbol_name;
133         char *demangled_name;
134 } backtrace_symbol_t;
135
136 typedef void (*get_backtrace_symbols_t) (const backtrace_frame_t *backtrace, size_t frames, backtrace_symbol_t *backtrace_symbols);
137 typedef void (*free_backtrace_symbols_t) (backtrace_symbol_t *backtrace_symbols, size_t frames);
138
139 enum {
140         MAX_BACKTRACE_LINE_LENGTH = 800,
141 };
142
143 /* Internals that we're exploiting to work in a signal handler. Only works on ARM/x86. */
144
145 typedef struct map_info_t map_info_t;
146
147 typedef ssize_t (*unwind_backtrace_signal_arch_t) (siginfo_t *si, void *sc, const map_info_t *lst, backtrace_frame_t *bt, size_t ignore_depth, size_t max_depth);
148 typedef map_info_t *(*acquire_my_map_info_list_t) (void);
149 typedef void (*release_my_map_info_list_t) (map_info_t *milist);
150
151 static char *
152 mono_extension_handle_native_sigsegv_libcorkscrew (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
153 {
154 #if defined (__arm__) || defined (__i386__)
155         char *dl_err;
156
157         get_backtrace_symbols_t get_backtrace_symbols;
158         free_backtrace_symbols_t free_backtrace_symbols;
159         unwind_backtrace_signal_arch_t unwind_backtrace_signal_arch;
160         acquire_my_map_info_list_t acquire_my_map_info_list;
161         release_my_map_info_list_t release_my_map_info_list;
162
163         backtrace_frame_t frames [FRAMES_TO_UNWIND];
164         backtrace_symbol_t symbols [FRAMES_TO_UNWIND];
165
166         map_info_t *map_info;
167         ssize_t frames_unwound;
168         size_t i;
169
170         MonoDl *dl = mono_dl_open ("libcorkscrew.so", MONO_DL_LAZY, &dl_err);
171
172         if (!dl)
173                 return dl_err;
174
175         LOAD_SYM (dl, dl_err, get_backtrace_symbols, get_backtrace_symbols);
176         LOAD_SYM (dl, dl_err, free_backtrace_symbols, free_backtrace_symbols);
177         LOAD_SYM (dl, dl_err, unwind_backtrace_signal_arch, unwind_backtrace_signal_arch);
178         LOAD_SYM (dl, dl_err, acquire_my_map_info_list, acquire_my_map_info_list);
179         LOAD_SYM (dl, dl_err, release_my_map_info_list, release_my_map_info_list);
180
181         map_info = acquire_my_map_info_list ();
182         frames_unwound = unwind_backtrace_signal_arch (info, ctx, map_info, frames, 0, FRAMES_TO_UNWIND);
183         release_my_map_info_list (map_info);
184
185         if (frames_unwound == -1) {
186                 mono_dl_close (dl);
187
188                 return g_strdup ("unwind_backtrace_signal_arch () returned -1");
189         }
190
191         get_backtrace_symbols (frames, frames_unwound, symbols);
192
193         for (i = 0; i < frames_unwound; i++) {
194                 backtrace_frame_t *frame = frames + i;
195                 backtrace_symbol_t *symbol = symbols + i;
196
197                 const char *name = symbol->demangled_name ? symbol->demangled_name : (symbol->symbol_name ? symbol->symbol_name : "???");
198                 uintptr_t off = symbol->relative_pc - symbol->relative_symbol_addr;
199                 uintptr_t ip = frame->absolute_pc;
200
201                 mono_runtime_printf_err ("  at %s+%zu [0x%zx]", name, off, ip);
202         }
203
204         free_backtrace_symbols (symbols, frames_unwound);
205
206         mono_dl_close (dl);
207
208         return NULL;
209 #else
210         return g_strdup ("libcorkscrew is only supported on 32-bit ARM/x86");
211 #endif
212 }
213
214 void
215 mono_exception_native_unwind (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
216 {
217         char *unwind_err, *corkscrew_err;
218
219         mono_runtime_printf_err ("\nAttempting native Android stacktrace:\n");
220
221         unwind_err = mono_extension_handle_native_sigsegv_libunwind (ctx, info);
222
223         if (unwind_err) {
224                 corkscrew_err = mono_extension_handle_native_sigsegv_libcorkscrew (ctx, info);
225
226                 if (corkscrew_err) {
227                         mono_runtime_printf_err ("\tCould not unwind with `libunwind.so`: %s", unwind_err);
228                         mono_runtime_printf_err ("\tCould not unwind with `libcorkscrew.so`: %s", corkscrew_err);
229                         mono_runtime_printf_err ("\n\tNo options left to get a native stacktrace :-(");
230
231                         g_free (corkscrew_err);
232                 }
233
234                 g_free (unwind_err);
235         }
236 }
237
238 #else
239
240 void
241 mono_exception_native_unwind (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
242 {
243 }
244
245 #endif