Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mono / utils / checked-build.c
1 /*
2  * checked-build.c: Expensive asserts used when mono is built with --with-checked-build=yes
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2015 Xamarin
8  */
9 #include <config.h>
10 #ifdef CHECKED_BUILD
11
12 #include <mono/utils/checked-build.h>
13 #include <mono/utils/mono-threads.h>
14 #include <mono/utils/mono-tls.h>
15 #include <glib.h>
16
17 #define MAX_NATIVE_BT 6
18 #define MAX_NATIVE_BT_PROBE (MAX_NATIVE_BT + 5)
19 #define MAX_TRANSITIONS 3
20
21
22 #ifdef HAVE_BACKTRACE_SYMBOLS
23 #include <execinfo.h>
24
25 //XXX We should collect just the IPs and lazily symbolificate them.
26 static int
27 collect_backtrace (gpointer out_data[])
28 {
29         return backtrace (out_data, MAX_NATIVE_BT_PROBE);
30 }
31
32 static char*
33 translate_backtrace (gpointer native_trace[], int size)
34 {
35         char **names = backtrace_symbols (native_trace, size);
36         GString* bt = g_string_sized_new (100);
37
38         int i, j = -1;
39
40         //Figure out the cut point of useless backtraces
41         //We'll skip up to the caller of checked_build_thread_transition
42         for (i = 0; i < size; ++i) {
43                 if (strstr (names [i], "checked_build_thread_transition")) {
44                         j = i + 1;
45                         break;
46                 }
47         }
48
49         if (j == -1)
50                 j = 0;
51         for (i = j; i < size; ++i) {
52                 if (i - j <= MAX_NATIVE_BT)
53                         g_string_append_printf (bt, "\tat %s\n", names [i]);
54         }
55
56         free (names);
57         return g_string_free (bt, FALSE);
58 }
59
60 #else
61
62 static int
63 collect_backtrace (gpointer out_data[])
64 {
65         return 0;
66 }
67
68 static char*
69 translate_backtrace (gpointer native_trace[], int size)
70 {
71         return g_strdup ("\tno backtrace available\n");
72 }
73
74 #endif
75
76
77 typedef struct {
78         GPtrArray *transitions;
79 } CheckState;
80
81 typedef struct {
82         const char *name;
83         int from_state, next_state, suspend_count, suspend_count_delta, size;
84         gpointer backtrace [MAX_NATIVE_BT_PROBE];
85 } ThreadTransition;
86
87 static MonoNativeTlsKey thread_status;
88
89 void
90 checked_build_init (void)
91 {
92         mono_native_tls_alloc (&thread_status, NULL);
93 }
94
95 static CheckState*
96 get_state (void)
97 {
98         CheckState *state = mono_native_tls_get_value (thread_status);
99         if (!state) {
100                 state = g_new0 (CheckState, 1);
101                 state->transitions = g_ptr_array_new ();
102                 mono_native_tls_set_value (thread_status, state);
103         }
104
105         return state;
106 }
107
108 static void
109 free_transition (ThreadTransition *t)
110 {
111         g_free (t);
112 }
113
114 void
115 checked_build_thread_transition (const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta)
116 {
117         MonoThreadInfo *cur = mono_thread_info_current_unchecked ();
118         CheckState *state = get_state ();
119         /* We currently don't record external changes as those are hard to reason about. */
120         if (cur != info)
121                 return;
122
123         if (state->transitions->len >= MAX_TRANSITIONS)
124                 free_transition (g_ptr_array_remove_index (state->transitions, 0));
125
126         ThreadTransition *t = g_new0 (ThreadTransition, 1);
127         t->name = transition;
128         t->from_state = from_state;
129         t->next_state = next_state;
130         t->suspend_count = suspend_count;
131         t->suspend_count_delta = suspend_count_delta;
132         t->size = collect_backtrace (t->backtrace);
133         g_ptr_array_add (state->transitions, t);
134 }
135
136 static void
137 assertion_fail (const char *msg, ...)
138 {
139         int i;
140         GString* err = g_string_sized_new (100);
141         CheckState *state = get_state ();
142
143         g_string_append_printf (err, "Assertion failure in thread %p due to: ", mono_native_thread_id_get ());
144
145         va_list args;
146         va_start (args, msg);
147         g_string_append_vprintf (err, msg, args);
148         va_end (args);
149
150         g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", state->transitions->len);
151
152         for (i = state->transitions->len - 1; i >= 0; --i) {
153                 ThreadTransition *t = state->transitions->pdata [i];
154                 char *bt = translate_backtrace (t->backtrace, t->size);
155                 g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
156                         t->name,
157                         mono_thread_state_name (t->from_state),
158                         mono_thread_state_name (t->next_state),
159                         t->suspend_count,
160                         t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
161                         t->suspend_count_delta,
162                         bt);
163                 g_free (bt);
164         }
165
166         g_error (err->str);
167         g_string_free (err, TRUE);
168 }
169
170 void
171 assert_gc_safe_mode (void)
172 {
173         MonoThreadInfo *cur = mono_thread_info_current ();
174         int state;
175
176         if (!cur)
177                 assertion_fail ("Expected GC Safe mode but thread is not attached");
178
179         switch (state = mono_thread_info_current_state (cur)) {
180         case STATE_BLOCKING:
181         case STATE_BLOCKING_AND_SUSPENDED:
182                 break;
183         default:
184                 assertion_fail ("Expected GC Safe mode but was in %s state", mono_thread_state_name (state));
185         }
186 }
187
188 void
189 assert_gc_unsafe_mode (void)
190 {
191         MonoThreadInfo *cur = mono_thread_info_current ();
192         int state;
193
194         if (!cur)
195                 assertion_fail ("Expected GC Unsafe mode but thread is not attached");
196
197         switch (state = mono_thread_info_current_state (cur)) {
198         case STATE_RUNNING:
199         case STATE_ASYNC_SUSPEND_REQUESTED:
200         case STATE_SELF_SUSPEND_REQUESTED:
201                 break;
202         default:
203                 assertion_fail ("Expected GC Unsafe mode but was in %s state", mono_thread_state_name (state));
204         }
205 }
206
207 void
208 assert_gc_neutral_mode (void)
209 {
210         MonoThreadInfo *cur = mono_thread_info_current ();
211         int state;
212
213         if (!cur)
214                 assertion_fail ("Expected GC Neutral mode but thread is not attached");
215
216         switch (state = mono_thread_info_current_state (cur)) {
217         case STATE_RUNNING:
218         case STATE_ASYNC_SUSPEND_REQUESTED:
219         case STATE_SELF_SUSPEND_REQUESTED:
220         case STATE_BLOCKING:
221         case STATE_BLOCKING_AND_SUSPENDED:
222                 break;
223         default:
224                 assertion_fail ("Expected GC Neutral mode but was in %s state", mono_thread_state_name (state));
225         }
226 }
227
228 #endif /* CHECKED_BUILD */