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