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