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