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