[Winforms] Improve X11 keyboard handling. (#18428)
[mono-project.git] / mono / sgen / sgen-protocol.c
blobc1261020a18db4accbe770902a105b2f1ee7ab98
1 /**
2 * \file
3 * Binary protocol of internal activity, to aid debugging.
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.
12 #ifdef HAVE_SGEN_GC
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-workers.h"
20 #include "sgen-client.h"
21 #include "mono/utils/mono-membar.h"
22 #include "mono/utils/mono-proclib.h"
24 #include <errno.h>
25 #include <string.h>
26 #if defined(HOST_WIN32)
27 #include <windows.h>
28 #elif defined(HAVE_UNISTD_H)
29 #include <unistd.h>
30 #include <fcntl.h>
31 #endif
33 #if defined(HOST_WIN32)
34 static const HANDLE invalid_file_value = INVALID_HANDLE_VALUE;
35 /* If valid, dump binary protocol to this file */
36 static HANDLE binary_protocol_file = INVALID_HANDLE_VALUE;
37 #else
38 static const int invalid_file_value = -1;
39 static int binary_protocol_file = -1;
40 #endif
42 /* We set this to -1 to indicate an exclusive lock */
43 static volatile int binary_protocol_use_count = 0;
45 #define BINARY_PROTOCOL_BUFFER_SIZE (65536 - 2 * 8)
47 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer;
48 struct _BinaryProtocolBuffer {
49 BinaryProtocolBuffer * volatile next;
50 volatile int index;
51 unsigned char buffer [BINARY_PROTOCOL_BUFFER_SIZE];
54 static BinaryProtocolBuffer * volatile binary_protocol_buffers = NULL;
56 static char* filename_or_prefix = NULL;
57 static int current_file_index = 0;
58 static long long current_file_size = 0;
59 static long long file_size_limit;
61 static char*
62 filename_for_index (int index)
64 char *filename;
66 SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
68 filename = (char *)sgen_alloc_internal_dynamic (strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
69 sprintf (filename, "%s.%d", filename_or_prefix, index);
71 return filename;
74 static void
75 free_filename (char *filename)
77 SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
79 sgen_free_internal_dynamic (filename, strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL);
82 static void
83 binary_protocol_open_file (gboolean assert_on_failure)
85 char *filename;
86 #ifdef F_SETLK
87 struct flock lock;
88 lock.l_type = F_WRLCK;
89 lock.l_whence = SEEK_SET;
90 lock.l_start = 0;
91 lock.l_len = 0;
92 #endif
94 if (file_size_limit > 0)
95 filename = filename_for_index (current_file_index);
96 else
97 filename = filename_or_prefix;
99 #if defined(HOST_WIN32)
100 binary_protocol_file = CreateFileA (filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
101 #elif defined(HAVE_UNISTD_H)
102 do {
103 binary_protocol_file = open (filename, O_CREAT | O_WRONLY, 0644);
104 if (binary_protocol_file == -1) {
105 if (errno != EINTR)
106 break; /* Failed */
107 #ifdef F_SETLK
108 } else if (fcntl (binary_protocol_file, F_SETLK, &lock) == -1) {
109 /* The lock for the file is already taken. Fail */
110 close (binary_protocol_file);
111 binary_protocol_file = -1;
112 break;
113 #endif
114 } else {
115 /* We have acquired the lock. Truncate the file */
116 ftruncate (binary_protocol_file, 0);
118 } while (binary_protocol_file == -1);
119 #else
120 g_error ("sgen binary protocol: not supported");
121 #endif
122 if (binary_protocol_file == invalid_file_value && assert_on_failure)
123 g_error ("sgen binary protocol: failed to open file");
125 if (file_size_limit > 0)
126 free_filename (filename);
129 void
130 sgen_binary_protocol_init (const char *filename, long long limit)
132 file_size_limit = limit;
134 /* Original name length + . + pid length in hex + null terminator */
135 filename_or_prefix = g_strdup_printf ("%s", filename);
136 binary_protocol_open_file (FALSE);
138 if (binary_protocol_file == invalid_file_value) {
139 /* Another process owns the file, try adding the pid suffix to the filename */
140 gint32 pid = mono_process_current_pid ();
141 g_free (filename_or_prefix);
142 filename_or_prefix = g_strdup_printf ("%s.%x", filename, pid);
143 binary_protocol_open_file (TRUE);
146 /* If we have a file size limit, we might need to open additional files */
147 if (file_size_limit == 0)
148 g_free (filename_or_prefix);
150 sgen_binary_protocol_header (PROTOCOL_HEADER_CHECK, PROTOCOL_HEADER_VERSION, SIZEOF_VOID_P, G_BYTE_ORDER == G_LITTLE_ENDIAN);
153 gboolean
154 sgen_binary_protocol_is_enabled (void)
156 return binary_protocol_file != invalid_file_value;
159 static void
160 close_binary_protocol_file (void)
162 #if defined(HOST_WIN32)
163 CloseHandle (binary_protocol_file);
164 #elif defined(HAVE_UNISTD_H)
165 while (close (binary_protocol_file) == -1 && errno == EINTR)
167 #endif
168 binary_protocol_file = invalid_file_value;
171 static gboolean
172 try_lock_exclusive (void)
174 do {
175 if (binary_protocol_use_count)
176 return FALSE;
177 } while (mono_atomic_cas_i32 (&binary_protocol_use_count, -1, 0) != 0);
178 mono_memory_barrier ();
179 return TRUE;
182 static void
183 unlock_exclusive (void)
185 mono_memory_barrier ();
186 SGEN_ASSERT (0, binary_protocol_use_count == -1, "Exclusively locked count must be -1");
187 if (mono_atomic_cas_i32 (&binary_protocol_use_count, 0, -1) != -1)
188 SGEN_ASSERT (0, FALSE, "Somebody messed with the exclusive lock");
191 static void
192 lock_recursive (void)
194 int old_count;
195 do {
196 retry:
197 old_count = binary_protocol_use_count;
198 if (old_count < 0) {
199 /* Exclusively locked - retry */
200 /* FIXME: short back-off */
201 goto retry;
203 } while (mono_atomic_cas_i32 (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
204 mono_memory_barrier ();
207 static void
208 unlock_recursive (void)
210 int old_count;
211 mono_memory_barrier ();
212 do {
213 old_count = binary_protocol_use_count;
214 SGEN_ASSERT (0, old_count > 0, "Locked use count must be at least 1");
215 } while (mono_atomic_cas_i32 (&binary_protocol_use_count, old_count - 1, old_count) != old_count);
218 static void
219 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
221 ssize_t ret;
222 size_t to_write = buffer->index;
223 size_t written = 0;
224 g_assert (buffer->index > 0);
226 while (binary_protocol_file != invalid_file_value && written < to_write) {
227 #if defined(HOST_WIN32)
228 DWORD tmp_written;
229 if (WriteFile (binary_protocol_file, buffer->buffer + written, to_write - written, &tmp_written, NULL))
230 written += tmp_written;
231 #elif defined(HAVE_UNISTD_H)
232 ret = write (binary_protocol_file, buffer->buffer + written, to_write - written);
233 if (ret >= 0)
234 written += ret;
235 else if (errno == EINTR)
236 continue;
237 #endif
238 else
239 close_binary_protocol_file ();
242 current_file_size += buffer->index;
244 sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL, MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL);
247 static void
248 binary_protocol_check_file_overflow (void)
250 if (file_size_limit <= 0 || current_file_size < file_size_limit)
251 return;
253 close_binary_protocol_file ();
255 if (current_file_index > 0) {
256 char *filename = filename_for_index (current_file_index - 1);
257 unlink (filename);
258 free_filename (filename);
261 ++current_file_index;
262 current_file_size = 0;
264 binary_protocol_open_file (TRUE);
268 * Flushing buffers takes an exclusive lock, so it must only be done when the world is
269 * stopped, otherwise we might end up with a deadlock because a stopped thread owns the
270 * lock.
272 * The protocol entries that do flush have `FLUSH()` in their definition.
274 gboolean
275 sgen_binary_protocol_flush_buffers (gboolean force)
277 int num_buffers = 0, i;
278 BinaryProtocolBuffer *header;
279 BinaryProtocolBuffer *buf;
280 BinaryProtocolBuffer **bufs;
282 if (binary_protocol_file == invalid_file_value)
283 return FALSE;
285 if (!force && !try_lock_exclusive ())
286 return FALSE;
288 header = binary_protocol_buffers;
289 for (buf = header; buf != NULL; buf = buf->next)
290 ++num_buffers;
291 bufs = (BinaryProtocolBuffer **)sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
292 for (buf = header, i = 0; buf != NULL; buf = buf->next, i++)
293 bufs [i] = buf;
294 SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
297 * This might be incorrect when forcing, but all bets are off in that case, anyway,
298 * because we're trying to figure out a bug in the debugger.
300 binary_protocol_buffers = NULL;
302 for (i = num_buffers - 1; i >= 0; --i) {
303 binary_protocol_flush_buffer (bufs [i]);
304 binary_protocol_check_file_overflow ();
307 sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
309 if (!force)
310 unlock_exclusive ();
312 return TRUE;
315 static BinaryProtocolBuffer*
316 binary_protocol_get_buffer (int length)
318 BinaryProtocolBuffer *buffer, *new_buffer;
319 retry:
320 buffer = binary_protocol_buffers;
321 if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
322 return buffer;
324 new_buffer = (BinaryProtocolBuffer *)sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging memory", MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL);
325 new_buffer->next = buffer;
326 new_buffer->index = 0;
328 if (mono_atomic_cas_ptr ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
329 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL, MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL);
330 goto retry;
333 return new_buffer;
336 static void
337 protocol_entry (unsigned char type, gpointer data, int size)
339 int index;
340 gboolean include_worker_index = type != PROTOCOL_ID (binary_protocol_header);
341 int entry_size = size + 1 + (include_worker_index ? 1 : 0); // type + worker_index + size
342 BinaryProtocolBuffer *buffer;
344 if (binary_protocol_file == invalid_file_value)
345 return;
347 lock_recursive ();
349 retry:
350 buffer = binary_protocol_get_buffer (size + 1);
351 retry_same_buffer:
352 index = buffer->index;
353 if (index + entry_size > BINARY_PROTOCOL_BUFFER_SIZE)
354 goto retry;
356 if (mono_atomic_cas_i32 (&buffer->index, index + entry_size, index) != index)
357 goto retry_same_buffer;
359 /* FIXME: if we're interrupted at this point, we have a buffer
360 entry that contains random data. */
362 buffer->buffer [index++] = type;
363 /* We should never change the header format */
364 if (include_worker_index) {
365 int worker_index;
366 MonoNativeThreadId tid = mono_native_thread_id_get ();
368 * If the thread is not a worker thread we insert 0, which is interpreted
369 * as gc thread. Worker indexes are 1 based.
371 worker_index = sgen_thread_pool_is_thread_pool_thread (tid);
372 /* FIXME Consider using different index bases for different thread pools */
373 buffer->buffer [index++] = (unsigned char) worker_index;
375 memcpy (buffer->buffer + index, data, size);
376 index += size;
378 g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
380 unlock_recursive ();
383 #define TYPE_INT int
384 #define TYPE_LONGLONG long long
385 #define TYPE_SIZE size_t
386 #define TYPE_POINTER gpointer
387 #define TYPE_BOOL gboolean
389 #define BEGIN_PROTOCOL_ENTRY0(method) \
390 void sgen_ ## method (void) { \
391 int __type = PROTOCOL_ID(method); \
392 gpointer __data = NULL; \
393 int __size = 0; \
394 CLIENT_PROTOCOL_NAME (method) ();
395 #define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
396 void sgen_ ## method (t1 f1) { \
397 PROTOCOL_STRUCT(method) __entry = { f1 }; \
398 int __type = PROTOCOL_ID(method); \
399 gpointer __data = &__entry; \
400 int __size = sizeof (PROTOCOL_STRUCT(method)); \
401 CLIENT_PROTOCOL_NAME (method) (f1);
402 #define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
403 void sgen_ ## method (t1 f1, t2 f2) { \
404 PROTOCOL_STRUCT(method) __entry = { f1, f2 }; \
405 int __type = PROTOCOL_ID(method); \
406 gpointer __data = &__entry; \
407 int __size = sizeof (PROTOCOL_STRUCT(method)); \
408 CLIENT_PROTOCOL_NAME (method) (f1, f2);
409 #define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
410 void sgen_ ## method (t1 f1, t2 f2, t3 f3) { \
411 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3 }; \
412 int __type = PROTOCOL_ID(method); \
413 gpointer __data = &__entry; \
414 int __size = sizeof (PROTOCOL_STRUCT(method)); \
415 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3);
416 #define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
417 void sgen_ ## method (t1 f1, t2 f2, t3 f3, t4 f4) { \
418 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4 }; \
419 int __type = PROTOCOL_ID(method); \
420 gpointer __data = &__entry; \
421 int __size = sizeof (PROTOCOL_STRUCT(method)); \
422 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4);
423 #define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
424 void sgen_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5) { \
425 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5 }; \
426 int __type = PROTOCOL_ID(method); \
427 gpointer __data = &__entry; \
428 int __size = sizeof (PROTOCOL_STRUCT(method)); \
429 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5);
430 #define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
431 void sgen_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6) { \
432 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5, f6 }; \
433 int __type = PROTOCOL_ID(method); \
434 gpointer __data = &__entry; \
435 int __size = sizeof (PROTOCOL_STRUCT(method)); \
436 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5, f6);
438 #define DEFAULT_PRINT()
439 #define CUSTOM_PRINT(_)
441 #define IS_ALWAYS_MATCH(_)
442 #define MATCH_INDEX(_)
443 #define IS_VTABLE_MATCH(_)
445 #define END_PROTOCOL_ENTRY \
446 protocol_entry (__type, __data, __size); \
449 #define END_PROTOCOL_ENTRY_FLUSH \
450 protocol_entry (__type, __data, __size); \
451 sgen_binary_protocol_flush_buffers (FALSE); \
454 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
455 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
456 BEGIN_PROTOCOL_ENTRY0 (method)
457 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
458 BEGIN_PROTOCOL_ENTRY1 (method,t1,f1)
459 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
460 BEGIN_PROTOCOL_ENTRY2 (method,t1,f1,t2,f2)
461 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
462 BEGIN_PROTOCOL_ENTRY3 (method,t1,f1,t2,f2,t3,f3)
463 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
464 BEGIN_PROTOCOL_ENTRY4 (method,t1,f1,t2,f2,t3,f3,t4,f4)
465 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
466 BEGIN_PROTOCOL_ENTRY5 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
467 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
468 BEGIN_PROTOCOL_ENTRY6 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
470 #define END_PROTOCOL_ENTRY_HEAVY \
471 END_PROTOCOL_ENTRY
472 #else
473 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method)
474 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1)
475 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2)
476 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3)
477 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4)
478 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
479 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
481 #define END_PROTOCOL_ENTRY_HEAVY
482 #endif
484 #include "sgen-protocol-def.h"
486 #undef TYPE_INT
487 #undef TYPE_LONGLONG
488 #undef TYPE_SIZE
489 #undef TYPE_POINTER
490 #undef TYPE_BOOL
492 #endif /* HAVE_SGEN_GC */