Check for missing Assembly.Location
[mono.git] / mono / metadata / sgen-protocol.c
1 /*
2  * sgen-protocol.c: Binary protocol of internal activity, to aid
3  * debugging.
4  *
5  * Copyright 2001-2003 Ximian, Inc
6  * Copyright 2003-2010 Novell, Inc.
7  * Copyright (C) 2012 Xamarin Inc
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License 2.0 as published by the Free Software Foundation;
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License 2.0 along with this library; if not, write to the Free
20  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 #ifdef HAVE_SGEN_GC
24
25 #include "config.h"
26 #include "sgen-conf.h"
27 #include "sgen-gc.h"
28 #include "sgen-protocol.h"
29 #include "sgen-memory-governor.h"
30 #include "utils/mono-mmap.h"
31 #include "utils/mono-threads.h"
32
33 #include <errno.h>
34 #ifdef HAVE_UNISTD_H
35 #include <fcntl.h>
36 #endif
37
38 /* FIXME Implement binary protocol IO on systems that don't have unistd */
39 #ifdef HAVE_UNISTD_H
40 /* If valid, dump binary protocol to this file */
41 static int binary_protocol_file = -1;
42
43 /* We set this to -1 to indicate an exclusive lock */
44 static volatile int binary_protocol_use_count = 0;
45
46 #define BINARY_PROTOCOL_BUFFER_SIZE     (65536 - 2 * 8)
47
48 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer;
49 struct _BinaryProtocolBuffer {
50         BinaryProtocolBuffer * volatile next;
51         volatile int index;
52         unsigned char buffer [BINARY_PROTOCOL_BUFFER_SIZE];
53 };
54
55 static BinaryProtocolBuffer * volatile binary_protocol_buffers = NULL;
56
57 static char* filename_or_prefix = NULL;
58 static int current_file_index = 0;
59 static long long current_file_size = 0;
60 static long long file_size_limit;
61
62 static char*
63 filename_for_index (int index)
64 {
65         char *filename;
66
67         SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
68
69         filename = sgen_alloc_internal_dynamic (strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
70         sprintf (filename, "%s.%d", filename_or_prefix, index);
71
72         return filename;
73 }
74
75 static void
76 free_filename (char *filename)
77 {
78         SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
79
80         sgen_free_internal_dynamic (filename, strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL);
81 }
82
83 static void
84 binary_protocol_open_file (void)
85 {
86         char *filename;
87
88         if (file_size_limit > 0)
89                 filename = filename_for_index (current_file_index);
90         else
91                 filename = filename_or_prefix;
92
93         do {
94                 binary_protocol_file = open (filename, O_CREAT|O_WRONLY|O_TRUNC, 0644);
95                 if (binary_protocol_file == -1 && errno != EINTR)
96                         break; /* Failed */
97         } while (binary_protocol_file == -1);
98
99         if (file_size_limit > 0)
100                 free_filename (filename);
101 }
102 #endif
103
104 void
105 binary_protocol_init (const char *filename, long long limit)
106 {
107 #ifdef HAVE_UNISTD_H
108         filename_or_prefix = sgen_alloc_internal_dynamic (strlen (filename) + 1, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
109         strcpy (filename_or_prefix, filename);
110
111         file_size_limit = limit;
112
113         binary_protocol_open_file ();
114 #endif
115 }
116
117 gboolean
118 binary_protocol_is_enabled (void)
119 {
120 #ifdef HAVE_UNISTD_H
121         return binary_protocol_file != -1;
122 #else
123         return FALSE;
124 #endif
125 }
126
127 #ifdef HAVE_UNISTD_H
128
129 static void
130 close_binary_protocol_file (void)
131 {
132         while (close (binary_protocol_file) == -1 && errno == EINTR)
133                 ;
134         binary_protocol_file = -1;
135 }
136
137 static gboolean
138 try_lock_exclusive (void)
139 {
140         do {
141                 if (binary_protocol_use_count)
142                         return FALSE;
143         } while (InterlockedCompareExchange (&binary_protocol_use_count, -1, 0) != 0);
144         mono_memory_barrier ();
145         return TRUE;
146 }
147
148 static void
149 unlock_exclusive (void)
150 {
151         mono_memory_barrier ();
152         SGEN_ASSERT (0, binary_protocol_use_count == -1, "Exclusively locked count must be -1");
153         if (InterlockedCompareExchange (&binary_protocol_use_count, 0, -1) != -1)
154                 SGEN_ASSERT (0, FALSE, "Somebody messed with the exclusive lock");
155 }
156
157 static void
158 lock_recursive (void)
159 {
160         int old_count;
161         do {
162         retry:
163                 old_count = binary_protocol_use_count;
164                 if (old_count < 0) {
165                         /* Exclusively locked - retry */
166                         /* FIXME: short back-off */
167                         goto retry;
168                 }
169         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
170         mono_memory_barrier ();
171 }
172
173 static void
174 unlock_recursive (void)
175 {
176         int old_count;
177         mono_memory_barrier ();
178         do {
179                 old_count = binary_protocol_use_count;
180                 SGEN_ASSERT (0, old_count > 0, "Locked use count must be at least 1");
181         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count - 1, old_count) != old_count);
182 }
183
184 static void
185 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
186 {
187         ssize_t ret;
188         size_t to_write = buffer->index;
189         size_t written = 0;
190         g_assert (buffer->index > 0);
191
192         while (written < to_write) {
193                 ret = write (binary_protocol_file, buffer->buffer + written, to_write - written);
194                 if (ret >= 0)
195                         written += ret;
196                 else if (errno == EINTR)
197                         continue;
198                 else
199                         close_binary_protocol_file ();
200         }
201
202         current_file_size += buffer->index;
203
204         sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
205 }
206
207 static void
208 binary_protocol_check_file_overflow (void)
209 {
210         if (file_size_limit <= 0 || current_file_size < file_size_limit)
211                 return;
212
213         close_binary_protocol_file ();
214
215         if (current_file_index > 0) {
216                 char *filename = filename_for_index (current_file_index - 1);
217                 unlink (filename);
218                 free_filename (filename);
219         }
220
221         ++current_file_index;
222         current_file_size = 0;
223
224         binary_protocol_open_file ();
225 }
226 #endif
227
228 void
229 binary_protocol_flush_buffers (gboolean force)
230 {
231 #ifdef HAVE_UNISTD_H
232         int num_buffers = 0, i;
233         BinaryProtocolBuffer *buf;
234         BinaryProtocolBuffer **bufs;
235
236         if (binary_protocol_file == -1)
237                 return;
238
239         if (!force && !try_lock_exclusive ())
240                 return;
241
242         for (buf = binary_protocol_buffers; buf != NULL; buf = buf->next)
243                 ++num_buffers;
244         bufs = sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
245         for (buf = binary_protocol_buffers, i = 0; buf != NULL; buf = buf->next, i++)
246                 bufs [i] = buf;
247         SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
248
249         binary_protocol_buffers = NULL;
250
251         for (i = num_buffers - 1; i >= 0; --i) {
252                 binary_protocol_flush_buffer (bufs [i]);
253                 binary_protocol_check_file_overflow ();
254         }
255
256         sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
257
258         if (!force)
259                 unlock_exclusive ();
260 #endif
261 }
262
263 #ifdef HAVE_UNISTD_H
264 static BinaryProtocolBuffer*
265 binary_protocol_get_buffer (int length)
266 {
267         BinaryProtocolBuffer *buffer, *new_buffer;
268  retry:
269         buffer = binary_protocol_buffers;
270         if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
271                 return buffer;
272
273         new_buffer = sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging memory");
274         new_buffer->next = buffer;
275         new_buffer->index = 0;
276
277         if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
278                 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
279                 goto retry;
280         }
281
282         return new_buffer;
283 }
284 #endif
285
286 static void
287 protocol_entry (unsigned char type, gpointer data, int size)
288 {
289 #ifdef HAVE_UNISTD_H
290         int index;
291         BinaryProtocolBuffer *buffer;
292
293         if (binary_protocol_file == -1)
294                 return;
295
296         if (sgen_is_worker_thread (mono_native_thread_id_get ()))
297                 type |= 0x80;
298
299         lock_recursive ();
300
301  retry:
302         buffer = binary_protocol_get_buffer (size + 1);
303  retry_same_buffer:
304         index = buffer->index;
305         if (index + 1 + size > BINARY_PROTOCOL_BUFFER_SIZE)
306                 goto retry;
307
308         if (InterlockedCompareExchange (&buffer->index, index + 1 + size, index) != index)
309                 goto retry_same_buffer;
310
311         /* FIXME: if we're interrupted at this point, we have a buffer
312            entry that contains random data. */
313
314         buffer->buffer [index++] = type;
315         memcpy (buffer->buffer + index, data, size);
316         index += size;
317
318         g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
319
320         unlock_recursive ();
321 #endif
322 }
323
324 #define TYPE_INT int
325 #define TYPE_LONGLONG long long
326 #define TYPE_SIZE size_t
327 #define TYPE_POINTER gpointer
328
329 #define BEGIN_PROTOCOL_ENTRY0(method) \
330         void method (void) { \
331                 int __type = PROTOCOL_ID(method); \
332                 gpointer __data = NULL; \
333                 int __size = 0;
334 #define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
335         void method (t1 f1) { \
336                 PROTOCOL_STRUCT(method) __entry = { f1 }; \
337                 int __type = PROTOCOL_ID(method); \
338                 gpointer __data = &__entry; \
339                 int __size = sizeof (PROTOCOL_STRUCT(method));
340 #define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
341         void method (t1 f1, t2 f2) { \
342                 PROTOCOL_STRUCT(method) __entry = { f1, f2 }; \
343                 int __type = PROTOCOL_ID(method); \
344                 gpointer __data = &__entry; \
345                 int __size = sizeof (PROTOCOL_STRUCT(method));
346 #define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
347         void method (t1 f1, t2 f2, t3 f3) { \
348                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3 }; \
349                 int __type = PROTOCOL_ID(method); \
350                 gpointer __data = &__entry; \
351                 int __size = sizeof (PROTOCOL_STRUCT(method));
352 #define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
353         void method (t1 f1, t2 f2, t3 f3, t4 f4) { \
354                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4 }; \
355                 int __type = PROTOCOL_ID(method); \
356                 gpointer __data = &__entry; \
357                 int __size = sizeof (PROTOCOL_STRUCT(method));
358 #define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
359         void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5) { \
360                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5 }; \
361                 int __type = PROTOCOL_ID(method); \
362                 gpointer __data = &__entry; \
363                 int __size = sizeof (PROTOCOL_STRUCT(method));
364 #define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
365         void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6) { \
366                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5, f6 }; \
367                 int __type = PROTOCOL_ID(method); \
368                 gpointer __data = &__entry; \
369                 int __size = sizeof (PROTOCOL_STRUCT(method));
370
371 #define FLUSH() \
372                 binary_protocol_flush_buffers (FALSE);
373
374 #define DEFAULT_PRINT()
375 #define CUSTOM_PRINT(_)
376
377 #define IS_ALWAYS_MATCH(_)
378 #define MATCH_INDEX(_)
379 #define IS_VTABLE_MATCH(_)
380
381 #define END_PROTOCOL_ENTRY \
382                 protocol_entry (__type, __data, __size); \
383         }
384
385 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
386 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
387         BEGIN_PROTOCOL_ENTRY0 (method)
388 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
389         BEGIN_PROTOCOL_ENTRY1 (method,t1,f1)
390 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
391         BEGIN_PROTOCOL_ENTRY2 (method,t1,f1,t2,f2)
392 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
393         BEGIN_PROTOCOL_ENTRY3 (method,t1,f1,t2,f2,t3,f3)
394 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
395         BEGIN_PROTOCOL_ENTRY4 (method,t1,f1,t2,f2,t3,f3,t4,f4)
396 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
397         BEGIN_PROTOCOL_ENTRY5 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
398 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
399         BEGIN_PROTOCOL_ENTRY6 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
400
401 #define END_PROTOCOL_ENTRY_HEAVY \
402         END_PROTOCOL_ENTRY
403 #else
404 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method)
405 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1)
406 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2)
407 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3)
408 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4)
409 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
410 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
411
412 #define END_PROTOCOL_ENTRY_HEAVY
413 #endif
414
415 #include "sgen-protocol-def.h"
416
417 #undef TYPE_INT
418 #undef TYPE_LONGLONG
419 #undef TYPE_SIZE
420 #undef TYPE_POINTER
421
422 #endif /* HAVE_SGEN_GC */