2 * sgen-protocol.c: Binary protocol of internal activity, to aid
5 * Copyright 2001-2003 Ximian, Inc
6 * Copyright 2003-2010 Novell, Inc.
7 * Copyright (C) 2012 Xamarin Inc
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #include "sgen-conf.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"
30 /* FIXME Implement binary protocol IO on systems that don't have unistd */
32 /* If valid, dump binary protocol to this file */
33 static int binary_protocol_file = -1;
35 /* We set this to -1 to indicate an exclusive lock */
36 static volatile int binary_protocol_use_count = 0;
38 #define BINARY_PROTOCOL_BUFFER_SIZE (65536 - 2 * 8)
40 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer;
41 struct _BinaryProtocolBuffer {
42 BinaryProtocolBuffer * volatile next;
44 unsigned char buffer [BINARY_PROTOCOL_BUFFER_SIZE];
47 static BinaryProtocolBuffer * volatile binary_protocol_buffers = NULL;
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;
55 filename_for_index (int index)
59 SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
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);
68 free_filename (char *filename)
70 SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
72 sgen_free_internal_dynamic (filename, strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL);
76 binary_protocol_open_file (void)
80 if (file_size_limit > 0)
81 filename = filename_for_index (current_file_index);
83 filename = filename_or_prefix;
86 binary_protocol_file = open (filename, O_CREAT|O_WRONLY|O_TRUNC, 0644);
87 if (binary_protocol_file == -1 && errno != EINTR)
89 } while (binary_protocol_file == -1);
91 if (binary_protocol_file == -1)
92 g_error ("sgen binary protocol: failed to open file");
94 if (file_size_limit > 0)
95 free_filename (filename);
100 binary_protocol_init (const char *filename, long long limit)
103 filename_or_prefix = (char *)sgen_alloc_internal_dynamic (strlen (filename) + 1, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
104 strcpy (filename_or_prefix, filename);
106 file_size_limit = limit;
108 binary_protocol_open_file ();
110 g_error ("sgen binary protocol: not supported");
115 binary_protocol_is_enabled (void)
118 return binary_protocol_file != -1;
127 close_binary_protocol_file (void)
129 while (close (binary_protocol_file) == -1 && errno == EINTR)
131 binary_protocol_file = -1;
135 try_lock_exclusive (void)
138 if (binary_protocol_use_count)
140 } while (InterlockedCompareExchange (&binary_protocol_use_count, -1, 0) != 0);
141 mono_memory_barrier ();
146 unlock_exclusive (void)
148 mono_memory_barrier ();
149 SGEN_ASSERT (0, binary_protocol_use_count == -1, "Exclusively locked count must be -1");
150 if (InterlockedCompareExchange (&binary_protocol_use_count, 0, -1) != -1)
151 SGEN_ASSERT (0, FALSE, "Somebody messed with the exclusive lock");
155 lock_recursive (void)
160 old_count = binary_protocol_use_count;
162 /* Exclusively locked - retry */
163 /* FIXME: short back-off */
166 } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
167 mono_memory_barrier ();
171 unlock_recursive (void)
174 mono_memory_barrier ();
176 old_count = binary_protocol_use_count;
177 SGEN_ASSERT (0, old_count > 0, "Locked use count must be at least 1");
178 } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count - 1, old_count) != old_count);
182 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
185 size_t to_write = buffer->index;
187 g_assert (buffer->index > 0);
189 while (written < to_write) {
190 ret = write (binary_protocol_file, buffer->buffer + written, to_write - written);
193 else if (errno == EINTR)
196 close_binary_protocol_file ();
199 current_file_size += buffer->index;
201 sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
205 binary_protocol_check_file_overflow (void)
207 if (file_size_limit <= 0 || current_file_size < file_size_limit)
210 close_binary_protocol_file ();
212 if (current_file_index > 0) {
213 char *filename = filename_for_index (current_file_index - 1);
215 free_filename (filename);
218 ++current_file_index;
219 current_file_size = 0;
221 binary_protocol_open_file ();
226 * Flushing buffers takes an exclusive lock, so it must only be done when the world is
227 * stopped, otherwise we might end up with a deadlock because a stopped thread owns the
230 * The protocol entries that do flush have `FLUSH()` in their definition.
233 binary_protocol_flush_buffers (gboolean force)
236 int num_buffers = 0, i;
237 BinaryProtocolBuffer *buf;
238 BinaryProtocolBuffer **bufs;
240 if (binary_protocol_file == -1)
243 if (!force && !try_lock_exclusive ())
246 for (buf = binary_protocol_buffers; buf != NULL; buf = buf->next)
248 bufs = (BinaryProtocolBuffer **)sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
249 for (buf = binary_protocol_buffers, i = 0; buf != NULL; buf = buf->next, i++)
251 SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
253 binary_protocol_buffers = NULL;
255 for (i = num_buffers - 1; i >= 0; --i) {
256 binary_protocol_flush_buffer (bufs [i]);
257 binary_protocol_check_file_overflow ();
260 sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
268 static BinaryProtocolBuffer*
269 binary_protocol_get_buffer (int length)
271 BinaryProtocolBuffer *buffer, *new_buffer;
273 buffer = binary_protocol_buffers;
274 if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
277 new_buffer = (BinaryProtocolBuffer *)sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging memory");
278 new_buffer->next = buffer;
279 new_buffer->index = 0;
281 if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
282 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
291 protocol_entry (unsigned char type, gpointer data, int size)
295 BinaryProtocolBuffer *buffer;
297 if (binary_protocol_file == -1)
300 if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()))
306 buffer = binary_protocol_get_buffer (size + 1);
308 index = buffer->index;
309 if (index + 1 + size > BINARY_PROTOCOL_BUFFER_SIZE)
312 if (InterlockedCompareExchange (&buffer->index, index + 1 + size, index) != index)
313 goto retry_same_buffer;
315 /* FIXME: if we're interrupted at this point, we have a buffer
316 entry that contains random data. */
318 buffer->buffer [index++] = type;
319 memcpy (buffer->buffer + index, data, size);
322 g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
329 #define TYPE_LONGLONG long long
330 #define TYPE_SIZE size_t
331 #define TYPE_POINTER gpointer
332 #define TYPE_BOOL gboolean
334 #define BEGIN_PROTOCOL_ENTRY0(method) \
335 void method (void) { \
336 int __type = PROTOCOL_ID(method); \
337 gpointer __data = NULL; \
339 CLIENT_PROTOCOL_NAME (method) ();
340 #define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
341 void method (t1 f1) { \
342 PROTOCOL_STRUCT(method) __entry = { f1 }; \
343 int __type = PROTOCOL_ID(method); \
344 gpointer __data = &__entry; \
345 int __size = sizeof (PROTOCOL_STRUCT(method)); \
346 CLIENT_PROTOCOL_NAME (method) (f1);
347 #define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
348 void method (t1 f1, t2 f2) { \
349 PROTOCOL_STRUCT(method) __entry = { f1, f2 }; \
350 int __type = PROTOCOL_ID(method); \
351 gpointer __data = &__entry; \
352 int __size = sizeof (PROTOCOL_STRUCT(method)); \
353 CLIENT_PROTOCOL_NAME (method) (f1, f2);
354 #define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
355 void method (t1 f1, t2 f2, t3 f3) { \
356 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3 }; \
357 int __type = PROTOCOL_ID(method); \
358 gpointer __data = &__entry; \
359 int __size = sizeof (PROTOCOL_STRUCT(method)); \
360 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3);
361 #define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
362 void method (t1 f1, t2 f2, t3 f3, t4 f4) { \
363 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4 }; \
364 int __type = PROTOCOL_ID(method); \
365 gpointer __data = &__entry; \
366 int __size = sizeof (PROTOCOL_STRUCT(method)); \
367 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4);
368 #define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
369 void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5) { \
370 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5 }; \
371 int __type = PROTOCOL_ID(method); \
372 gpointer __data = &__entry; \
373 int __size = sizeof (PROTOCOL_STRUCT(method)); \
374 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5);
375 #define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
376 void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6) { \
377 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5, f6 }; \
378 int __type = PROTOCOL_ID(method); \
379 gpointer __data = &__entry; \
380 int __size = sizeof (PROTOCOL_STRUCT(method)); \
381 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5, f6);
383 #define DEFAULT_PRINT()
384 #define CUSTOM_PRINT(_)
386 #define IS_ALWAYS_MATCH(_)
387 #define MATCH_INDEX(_)
388 #define IS_VTABLE_MATCH(_)
390 #define END_PROTOCOL_ENTRY \
391 protocol_entry (__type, __data, __size); \
394 #define END_PROTOCOL_ENTRY_FLUSH \
395 protocol_entry (__type, __data, __size); \
396 binary_protocol_flush_buffers (FALSE); \
399 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
400 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
401 BEGIN_PROTOCOL_ENTRY0 (method)
402 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
403 BEGIN_PROTOCOL_ENTRY1 (method,t1,f1)
404 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
405 BEGIN_PROTOCOL_ENTRY2 (method,t1,f1,t2,f2)
406 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
407 BEGIN_PROTOCOL_ENTRY3 (method,t1,f1,t2,f2,t3,f3)
408 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
409 BEGIN_PROTOCOL_ENTRY4 (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 BEGIN_PROTOCOL_ENTRY5 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
412 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
413 BEGIN_PROTOCOL_ENTRY6 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
415 #define END_PROTOCOL_ENTRY_HEAVY \
418 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method)
419 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1)
420 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2)
421 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3)
422 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4)
423 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
424 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
426 #define END_PROTOCOL_ENTRY_HEAVY
429 #include "sgen-protocol-def.h"
437 #endif /* HAVE_SGEN_GC */