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"
22 #include "mono/utils/mono-proclib.h"
31 /* FIXME Implement binary protocol IO on systems that don't have unistd */
33 /* If valid, dump binary protocol to this file */
34 static int binary_protocol_file = -1;
36 /* We set this to -1 to indicate an exclusive lock */
37 static volatile int binary_protocol_use_count = 0;
39 #define BINARY_PROTOCOL_BUFFER_SIZE (65536 - 2 * 8)
41 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer;
42 struct _BinaryProtocolBuffer {
43 BinaryProtocolBuffer * volatile next;
45 unsigned char buffer [BINARY_PROTOCOL_BUFFER_SIZE];
48 static BinaryProtocolBuffer * volatile binary_protocol_buffers = NULL;
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;
56 filename_for_index (int index)
60 SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
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);
69 free_filename (char *filename)
71 SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
73 sgen_free_internal_dynamic (filename, strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL);
77 binary_protocol_open_file (gboolean assert_on_failure)
82 lock.l_type = F_WRLCK;
83 lock.l_whence = SEEK_SET;
88 if (file_size_limit > 0)
89 filename = filename_for_index (current_file_index);
91 filename = filename_or_prefix;
94 binary_protocol_file = open (filename, O_CREAT | O_WRONLY, 0644);
95 if (binary_protocol_file == -1) {
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;
106 /* We have acquired the lock. Truncate the file */
107 ftruncate (binary_protocol_file, 0);
109 } while (binary_protocol_file == -1);
111 if (binary_protocol_file == -1 && assert_on_failure)
112 g_error ("sgen binary protocol: failed to open file");
114 if (file_size_limit > 0)
115 free_filename (filename);
120 binary_protocol_init (const char *filename, long long limit)
123 file_size_limit = limit;
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);
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);
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);
141 binary_protocol_header (PROTOCOL_HEADER_CHECK, PROTOCOL_HEADER_VERSION, SIZEOF_VOID_P, G_BYTE_ORDER == G_LITTLE_ENDIAN);
143 g_error ("sgen binary protocol: not supported");
148 binary_protocol_is_enabled (void)
151 return binary_protocol_file != -1;
160 close_binary_protocol_file (void)
162 while (close (binary_protocol_file) == -1 && errno == EINTR)
164 binary_protocol_file = -1;
168 try_lock_exclusive (void)
171 if (binary_protocol_use_count)
173 } while (InterlockedCompareExchange (&binary_protocol_use_count, -1, 0) != 0);
174 mono_memory_barrier ();
179 unlock_exclusive (void)
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");
188 lock_recursive (void)
193 old_count = binary_protocol_use_count;
195 /* Exclusively locked - retry */
196 /* FIXME: short back-off */
199 } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
200 mono_memory_barrier ();
204 unlock_recursive (void)
207 mono_memory_barrier ();
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);
215 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
218 size_t to_write = buffer->index;
220 g_assert (buffer->index > 0);
222 while (written < to_write) {
223 ret = write (binary_protocol_file, buffer->buffer + written, to_write - written);
226 else if (errno == EINTR)
229 close_binary_protocol_file ();
232 current_file_size += buffer->index;
234 sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
238 binary_protocol_check_file_overflow (void)
240 if (file_size_limit <= 0 || current_file_size < file_size_limit)
243 close_binary_protocol_file ();
245 if (current_file_index > 0) {
246 char *filename = filename_for_index (current_file_index - 1);
248 free_filename (filename);
251 ++current_file_index;
252 current_file_size = 0;
254 binary_protocol_open_file (TRUE);
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
263 * The protocol entries that do flush have `FLUSH()` in their definition.
266 binary_protocol_flush_buffers (gboolean force)
269 int num_buffers = 0, i;
270 BinaryProtocolBuffer *buf;
271 BinaryProtocolBuffer **bufs;
273 if (binary_protocol_file == -1)
276 if (!force && !try_lock_exclusive ())
279 for (buf = binary_protocol_buffers; buf != NULL; buf = buf->next)
281 bufs = (BinaryProtocolBuffer **)sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
282 for (buf = binary_protocol_buffers, i = 0; buf != NULL; buf = buf->next, i++)
284 SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
286 binary_protocol_buffers = NULL;
288 for (i = num_buffers - 1; i >= 0; --i) {
289 binary_protocol_flush_buffer (bufs [i]);
290 binary_protocol_check_file_overflow ();
293 sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
301 static BinaryProtocolBuffer*
302 binary_protocol_get_buffer (int length)
304 BinaryProtocolBuffer *buffer, *new_buffer;
306 buffer = binary_protocol_buffers;
307 if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
310 new_buffer = (BinaryProtocolBuffer *)sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging memory");
311 new_buffer->next = buffer;
312 new_buffer->index = 0;
314 if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
315 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
324 protocol_entry (unsigned char type, gpointer data, int size)
328 BinaryProtocolBuffer *buffer;
330 if (binary_protocol_file == -1)
333 if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()))
339 buffer = binary_protocol_get_buffer (size + 1);
341 index = buffer->index;
342 if (index + 1 + size > BINARY_PROTOCOL_BUFFER_SIZE)
345 if (InterlockedCompareExchange (&buffer->index, index + 1 + size, index) != index)
346 goto retry_same_buffer;
348 /* FIXME: if we're interrupted at this point, we have a buffer
349 entry that contains random data. */
351 buffer->buffer [index++] = type;
352 memcpy (buffer->buffer + index, data, size);
355 g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
362 #define TYPE_LONGLONG long long
363 #define TYPE_SIZE size_t
364 #define TYPE_POINTER gpointer
365 #define TYPE_BOOL gboolean
367 #define BEGIN_PROTOCOL_ENTRY0(method) \
368 void method (void) { \
369 int __type = PROTOCOL_ID(method); \
370 gpointer __data = NULL; \
372 CLIENT_PROTOCOL_NAME (method) ();
373 #define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
374 void method (t1 f1) { \
375 PROTOCOL_STRUCT(method) __entry = { f1 }; \
376 int __type = PROTOCOL_ID(method); \
377 gpointer __data = &__entry; \
378 int __size = sizeof (PROTOCOL_STRUCT(method)); \
379 CLIENT_PROTOCOL_NAME (method) (f1);
380 #define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
381 void method (t1 f1, t2 f2) { \
382 PROTOCOL_STRUCT(method) __entry = { f1, f2 }; \
383 int __type = PROTOCOL_ID(method); \
384 gpointer __data = &__entry; \
385 int __size = sizeof (PROTOCOL_STRUCT(method)); \
386 CLIENT_PROTOCOL_NAME (method) (f1, f2);
387 #define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
388 void method (t1 f1, t2 f2, t3 f3) { \
389 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3 }; \
390 int __type = PROTOCOL_ID(method); \
391 gpointer __data = &__entry; \
392 int __size = sizeof (PROTOCOL_STRUCT(method)); \
393 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3);
394 #define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
395 void method (t1 f1, t2 f2, t3 f3, t4 f4) { \
396 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4 }; \
397 int __type = PROTOCOL_ID(method); \
398 gpointer __data = &__entry; \
399 int __size = sizeof (PROTOCOL_STRUCT(method)); \
400 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4);
401 #define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
402 void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5) { \
403 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5 }; \
404 int __type = PROTOCOL_ID(method); \
405 gpointer __data = &__entry; \
406 int __size = sizeof (PROTOCOL_STRUCT(method)); \
407 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5);
408 #define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
409 void method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6) { \
410 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5, f6 }; \
411 int __type = PROTOCOL_ID(method); \
412 gpointer __data = &__entry; \
413 int __size = sizeof (PROTOCOL_STRUCT(method)); \
414 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5, f6);
416 #define DEFAULT_PRINT()
417 #define CUSTOM_PRINT(_)
419 #define IS_ALWAYS_MATCH(_)
420 #define MATCH_INDEX(_)
421 #define IS_VTABLE_MATCH(_)
423 #define END_PROTOCOL_ENTRY \
424 protocol_entry (__type, __data, __size); \
427 #define END_PROTOCOL_ENTRY_FLUSH \
428 protocol_entry (__type, __data, __size); \
429 binary_protocol_flush_buffers (FALSE); \
432 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
433 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
434 BEGIN_PROTOCOL_ENTRY0 (method)
435 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
436 BEGIN_PROTOCOL_ENTRY1 (method,t1,f1)
437 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
438 BEGIN_PROTOCOL_ENTRY2 (method,t1,f1,t2,f2)
439 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
440 BEGIN_PROTOCOL_ENTRY3 (method,t1,f1,t2,f2,t3,f3)
441 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
442 BEGIN_PROTOCOL_ENTRY4 (method,t1,f1,t2,f2,t3,f3,t4,f4)
443 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
444 BEGIN_PROTOCOL_ENTRY5 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
445 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
446 BEGIN_PROTOCOL_ENTRY6 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
448 #define END_PROTOCOL_ENTRY_HEAVY \
451 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method)
452 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1)
453 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2)
454 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3)
455 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4)
456 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
457 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
459 #define END_PROTOCOL_ENTRY_HEAVY
462 #include "sgen-protocol-def.h"
470 #endif /* HAVE_SGEN_GC */