Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / sgen / sgen-protocol.c
1 /**
2  * \file
3  * Binary protocol of internal activity, to aid debugging.
4  *
5  * Copyright 2001-2003 Ximian, Inc
6  * Copyright 2003-2010 Novell, Inc.
7  * Copyright (C) 2012 Xamarin Inc
8  *
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #ifdef HAVE_SGEN_GC
13
14 #include "config.h"
15 #include "sgen-conf.h"
16 #include "sgen-gc.h"
17 #include "sgen-protocol.h"
18 #include "sgen-memory-governor.h"
19 #include "sgen-workers.h"
20 #include "sgen-client.h"
21 #include "mono/utils/mono-membar.h"
22 #include "mono/utils/mono-proclib.h"
23
24 #include <errno.h>
25 #include <string.h>
26 #if defined(HAVE_UNISTD_H)
27 #include <unistd.h>
28 #include <fcntl.h>
29 #elif defined(HOST_WIN32)
30 #include <windows.h>
31 #endif
32
33 #if defined(HOST_WIN32)
34 static const HANDLE invalid_file_value = INVALID_HANDLE_VALUE;
35 /* If valid, dump binary protocol to this file */
36 static HANDLE binary_protocol_file = INVALID_HANDLE_VALUE;
37 #else
38 static const int invalid_file_value = -1;
39 static int binary_protocol_file = -1;
40 #endif
41
42 /* We set this to -1 to indicate an exclusive lock */
43 static volatile int binary_protocol_use_count = 0;
44
45 #define BINARY_PROTOCOL_BUFFER_SIZE     (65536 - 2 * 8)
46
47 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer;
48 struct _BinaryProtocolBuffer {
49         BinaryProtocolBuffer * volatile next;
50         volatile int index;
51         unsigned char buffer [BINARY_PROTOCOL_BUFFER_SIZE];
52 };
53
54 static BinaryProtocolBuffer * volatile binary_protocol_buffers = NULL;
55
56 static char* filename_or_prefix = NULL;
57 static int current_file_index = 0;
58 static long long current_file_size = 0;
59 static long long file_size_limit;
60
61 static char*
62 filename_for_index (int index)
63 {
64         char *filename;
65
66         SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
67
68         filename = (char *)sgen_alloc_internal_dynamic (strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
69         sprintf (filename, "%s.%d", filename_or_prefix, index);
70
71         return filename;
72 }
73
74 static void
75 free_filename (char *filename)
76 {
77         SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
78
79         sgen_free_internal_dynamic (filename, strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL);
80 }
81
82 static void
83 binary_protocol_open_file (gboolean assert_on_failure)
84 {
85         char *filename;
86 #ifdef F_SETLK
87         struct flock lock;
88         lock.l_type = F_WRLCK;
89         lock.l_whence = SEEK_SET;
90         lock.l_start = 0;
91         lock.l_len = 0;
92 #endif
93
94         if (file_size_limit > 0)
95                 filename = filename_for_index (current_file_index);
96         else
97                 filename = filename_or_prefix;
98
99 #if defined(HAVE_UNISTD_H)
100         do {
101                 binary_protocol_file = open (filename, O_CREAT | O_WRONLY, 0644);
102                 if (binary_protocol_file == -1) {
103                         if (errno != EINTR)
104                                 break; /* Failed */
105 #ifdef F_SETLK
106                 } else if (fcntl (binary_protocol_file, F_SETLK, &lock) == -1) {
107                         /* The lock for the file is already taken. Fail */
108                         close (binary_protocol_file);
109                         binary_protocol_file = -1;
110                         break;
111 #endif
112                 } else {
113                         /* We have acquired the lock. Truncate the file */
114                         ftruncate (binary_protocol_file, 0);
115                 }
116         } while (binary_protocol_file == -1);
117 #elif defined(HOST_WIN32)
118         binary_protocol_file = CreateFileA (filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
119 #else
120         g_error ("sgen binary protocol: not supported");
121 #endif
122
123         if (binary_protocol_file == invalid_file_value && assert_on_failure)
124                 g_error ("sgen binary protocol: failed to open file");
125
126         if (file_size_limit > 0)
127                 free_filename (filename);
128 }
129
130 void
131 binary_protocol_init (const char *filename, long long limit)
132 {
133         file_size_limit = limit;
134
135         /* Original name length + . + pid length in hex + null terminator */
136         filename_or_prefix = g_strdup_printf ("%s", filename);
137         binary_protocol_open_file (FALSE);
138
139         if (binary_protocol_file == invalid_file_value) {
140                 /* Another process owns the file, try adding the pid suffix to the filename */
141                 gint32 pid = mono_process_current_pid ();
142                 g_free (filename_or_prefix);
143                 filename_or_prefix = g_strdup_printf ("%s.%x", filename, pid);
144                 binary_protocol_open_file (TRUE);
145         }
146
147         /* If we have a file size limit, we might need to open additional files */
148         if (file_size_limit == 0)
149                 g_free (filename_or_prefix);
150
151         binary_protocol_header (PROTOCOL_HEADER_CHECK, PROTOCOL_HEADER_VERSION, SIZEOF_VOID_P, G_BYTE_ORDER == G_LITTLE_ENDIAN);
152 }
153
154 gboolean
155 binary_protocol_is_enabled (void)
156 {
157         return binary_protocol_file != invalid_file_value;
158 }
159
160 static void
161 close_binary_protocol_file (void)
162 {
163 #if defined(HAVE_UNISTD_H)
164         while (close (binary_protocol_file) == -1 && errno == EINTR)
165                 ;
166 #elif defined(HOST_WIN32)
167         CloseHandle (binary_protocol_file);
168 #endif
169         binary_protocol_file = invalid_file_value;
170 }
171
172 static gboolean
173 try_lock_exclusive (void)
174 {
175         do {
176                 if (binary_protocol_use_count)
177                         return FALSE;
178         } while (InterlockedCompareExchange (&binary_protocol_use_count, -1, 0) != 0);
179         mono_memory_barrier ();
180         return TRUE;
181 }
182
183 static void
184 unlock_exclusive (void)
185 {
186         mono_memory_barrier ();
187         SGEN_ASSERT (0, binary_protocol_use_count == -1, "Exclusively locked count must be -1");
188         if (InterlockedCompareExchange (&binary_protocol_use_count, 0, -1) != -1)
189                 SGEN_ASSERT (0, FALSE, "Somebody messed with the exclusive lock");
190 }
191
192 static void
193 lock_recursive (void)
194 {
195         int old_count;
196         do {
197         retry:
198                 old_count = binary_protocol_use_count;
199                 if (old_count < 0) {
200                         /* Exclusively locked - retry */
201                         /* FIXME: short back-off */
202                         goto retry;
203                 }
204         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
205         mono_memory_barrier ();
206 }
207
208 static void
209 unlock_recursive (void)
210 {
211         int old_count;
212         mono_memory_barrier ();
213         do {
214                 old_count = binary_protocol_use_count;
215                 SGEN_ASSERT (0, old_count > 0, "Locked use count must be at least 1");
216         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count - 1, old_count) != old_count);
217 }
218
219 static void
220 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
221 {
222         ssize_t ret;
223         size_t to_write = buffer->index;
224         size_t written = 0;
225         g_assert (buffer->index > 0);
226
227         while (written < to_write) {
228 #if defined(HAVE_UNISTD_H)
229                 ret = write (binary_protocol_file, buffer->buffer + written, to_write - written);
230                 if (ret >= 0)
231                         written += ret;
232                 else if (errno == EINTR)
233                         continue;
234                 else
235                         close_binary_protocol_file ();
236 #elif defined(HOST_WIN32)
237                 int tmp_written;
238                 if (WriteFile (binary_protocol_file, buffer->buffer + written, to_write - written, &tmp_written, NULL))
239                         written += tmp_written;
240                 else
241                         close_binary_protocol_file ();
242 #endif
243         }
244
245         current_file_size += buffer->index;
246
247         sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL, MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL);
248 }
249
250 static void
251 binary_protocol_check_file_overflow (void)
252 {
253         if (file_size_limit <= 0 || current_file_size < file_size_limit)
254                 return;
255
256         close_binary_protocol_file ();
257
258         if (current_file_index > 0) {
259                 char *filename = filename_for_index (current_file_index - 1);
260                 unlink (filename);
261                 free_filename (filename);
262         }
263
264         ++current_file_index;
265         current_file_size = 0;
266
267         binary_protocol_open_file (TRUE);
268 }
269
270 /*
271  * Flushing buffers takes an exclusive lock, so it must only be done when the world is
272  * stopped, otherwise we might end up with a deadlock because a stopped thread owns the
273  * lock.
274  *
275  * The protocol entries that do flush have `FLUSH()` in their definition.
276  */
277 gboolean
278 binary_protocol_flush_buffers (gboolean force)
279 {
280         int num_buffers = 0, i;
281         BinaryProtocolBuffer *header;
282         BinaryProtocolBuffer *buf;
283         BinaryProtocolBuffer **bufs;
284
285         if (binary_protocol_file == invalid_file_value)
286                 return FALSE;
287
288         if (!force && !try_lock_exclusive ())
289                 return FALSE;
290
291         header = binary_protocol_buffers;
292         for (buf = header; buf != NULL; buf = buf->next)
293                 ++num_buffers;
294         bufs = (BinaryProtocolBuffer **)sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
295         for (buf = header, i = 0; buf != NULL; buf = buf->next, i++)
296                 bufs [i] = buf;
297         SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
298
299         /*
300          * This might be incorrect when forcing, but all bets are off in that case, anyway,
301          * because we're trying to figure out a bug in the debugger.
302          */
303         binary_protocol_buffers = NULL;
304
305         for (i = num_buffers - 1; i >= 0; --i) {
306                 binary_protocol_flush_buffer (bufs [i]);
307                 binary_protocol_check_file_overflow ();
308         }
309
310         sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
311
312         if (!force)
313                 unlock_exclusive ();
314
315         return TRUE;
316 }
317
318 static BinaryProtocolBuffer*
319 binary_protocol_get_buffer (int length)
320 {
321         BinaryProtocolBuffer *buffer, *new_buffer;
322  retry:
323         buffer = binary_protocol_buffers;
324         if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
325                 return buffer;
326
327         new_buffer = (BinaryProtocolBuffer *)sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging memory", MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL);
328         new_buffer->next = buffer;
329         new_buffer->index = 0;
330
331         if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
332                 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL, MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL);
333                 goto retry;
334         }
335
336         return new_buffer;
337 }
338
339 static void
340 protocol_entry (unsigned char type, gpointer data, int size)
341 {
342         int index;
343         gboolean include_worker_index = type != PROTOCOL_ID (binary_protocol_header);
344         int entry_size = size + 1 + (include_worker_index ? 1 : 0); // type + worker_index + size
345         BinaryProtocolBuffer *buffer;
346
347         if (binary_protocol_file == invalid_file_value)
348                 return;
349
350         lock_recursive ();
351
352  retry:
353         buffer = binary_protocol_get_buffer (size + 1);
354  retry_same_buffer:
355         index = buffer->index;
356         if (index + entry_size > BINARY_PROTOCOL_BUFFER_SIZE)
357                 goto retry;
358
359         if (InterlockedCompareExchange (&buffer->index, index + entry_size, index) != index)
360                 goto retry_same_buffer;
361
362         /* FIXME: if we're interrupted at this point, we have a buffer
363            entry that contains random data. */
364
365         buffer->buffer [index++] = type;
366         /* We should never change the header format */
367         if (include_worker_index) {
368                 int worker_index;
369                 MonoNativeThreadId tid = mono_native_thread_id_get ();
370                 /*
371                  * If the thread is not a worker thread we insert 0, which is interpreted
372                  * as gc thread. Worker indexes are 1 based.
373                  */
374                 worker_index = sgen_thread_pool_is_thread_pool_thread (tid);
375                 /* FIXME Consider using different index bases for different thread pools */
376                 buffer->buffer [index++] = (unsigned char) worker_index;
377         }
378         memcpy (buffer->buffer + index, data, size);
379         index += size;
380
381         g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
382
383         unlock_recursive ();
384 }
385
386 #define TYPE_INT int
387 #define TYPE_LONGLONG long long
388 #define TYPE_SIZE size_t
389 #define TYPE_POINTER gpointer
390 #define TYPE_BOOL gboolean
391
392 #define BEGIN_PROTOCOL_ENTRY0(method) \
393         void method (void) { \
394                 int __type = PROTOCOL_ID(method); \
395                 gpointer __data = NULL; \
396                 int __size = 0; \
397                 CLIENT_PROTOCOL_NAME (method) ();
398 #define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
399         void method (t1 f1) { \
400                 PROTOCOL_STRUCT(method) __entry = { f1 }; \
401                 int __type = PROTOCOL_ID(method); \
402                 gpointer __data = &__entry; \
403                 int __size = sizeof (PROTOCOL_STRUCT(method)); \
404                 CLIENT_PROTOCOL_NAME (method) (f1);
405 #define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
406         void method (t1 f1, t2 f2) { \
407                 PROTOCOL_STRUCT(method) __entry = { f1, f2 }; \
408                 int __type = PROTOCOL_ID(method); \
409                 gpointer __data = &__entry; \
410                 int __size = sizeof (PROTOCOL_STRUCT(method)); \
411                 CLIENT_PROTOCOL_NAME (method) (f1, f2);
412 #define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
413         void method (t1 f1, t2 f2, t3 f3) { \
414                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3 }; \
415                 int __type = PROTOCOL_ID(method); \
416                 gpointer __data = &__entry; \
417                 int __size = sizeof (PROTOCOL_STRUCT(method)); \
418                 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3);
419 #define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
420         void method (t1 f1, t2 f2, t3 f3, t4 f4) { \
421                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4 }; \
422                 int __type = PROTOCOL_ID(method); \
423                 gpointer __data = &__entry; \
424                 int __size = sizeof (PROTOCOL_STRUCT(method)); \
425                 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4);
426 #define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
427         void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5) { \
428                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5 }; \
429                 int __type = PROTOCOL_ID(method); \
430                 gpointer __data = &__entry; \
431                 int __size = sizeof (PROTOCOL_STRUCT(method)); \
432                 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5);
433 #define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
434         void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6) { \
435                 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5, f6 }; \
436                 int __type = PROTOCOL_ID(method); \
437                 gpointer __data = &__entry; \
438                 int __size = sizeof (PROTOCOL_STRUCT(method)); \
439                 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5, f6);
440
441 #define DEFAULT_PRINT()
442 #define CUSTOM_PRINT(_)
443
444 #define IS_ALWAYS_MATCH(_)
445 #define MATCH_INDEX(_)
446 #define IS_VTABLE_MATCH(_)
447
448 #define END_PROTOCOL_ENTRY \
449                 protocol_entry (__type, __data, __size); \
450         }
451
452 #define END_PROTOCOL_ENTRY_FLUSH \
453                 protocol_entry (__type, __data, __size); \
454                 binary_protocol_flush_buffers (FALSE); \
455         }
456
457 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
458 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
459         BEGIN_PROTOCOL_ENTRY0 (method)
460 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
461         BEGIN_PROTOCOL_ENTRY1 (method,t1,f1)
462 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
463         BEGIN_PROTOCOL_ENTRY2 (method,t1,f1,t2,f2)
464 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
465         BEGIN_PROTOCOL_ENTRY3 (method,t1,f1,t2,f2,t3,f3)
466 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
467         BEGIN_PROTOCOL_ENTRY4 (method,t1,f1,t2,f2,t3,f3,t4,f4)
468 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
469         BEGIN_PROTOCOL_ENTRY5 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
470 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
471         BEGIN_PROTOCOL_ENTRY6 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
472
473 #define END_PROTOCOL_ENTRY_HEAVY \
474         END_PROTOCOL_ENTRY
475 #else
476 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method)
477 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1)
478 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2)
479 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3)
480 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4)
481 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
482 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
483
484 #define END_PROTOCOL_ENTRY_HEAVY
485 #endif
486
487 #include "sgen-protocol-def.h"
488
489 #undef TYPE_INT
490 #undef TYPE_LONGLONG
491 #undef TYPE_SIZE
492 #undef TYPE_POINTER
493 #undef TYPE_BOOL
494
495 #endif /* HAVE_SGEN_GC */