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.
15 #include "sgen-conf.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"
26 #if defined(HOST_WIN32)
28 #elif defined(HAVE_UNISTD_H)
33 #ifndef DISABLE_SGEN_BINARY_PROTOCOL
35 #if defined(HOST_WIN32)
36 static const HANDLE invalid_file_value
= INVALID_HANDLE_VALUE
;
37 /* If valid, dump binary protocol to this file */
38 static HANDLE binary_protocol_file
= INVALID_HANDLE_VALUE
;
40 static const int invalid_file_value
= -1;
41 static int binary_protocol_file
= -1;
44 /* We set this to -1 to indicate an exclusive lock */
45 static volatile int binary_protocol_use_count
= 0;
47 #define BINARY_PROTOCOL_BUFFER_SIZE (65536 - 2 * 8)
49 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer
;
50 struct _BinaryProtocolBuffer
{
51 BinaryProtocolBuffer
* volatile next
;
53 unsigned char buffer
[BINARY_PROTOCOL_BUFFER_SIZE
];
56 static BinaryProtocolBuffer
* volatile binary_protocol_buffers
= NULL
;
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
;
64 filename_for_index (int index
)
68 SGEN_ASSERT (0, file_size_limit
> 0, "Indexed binary protocol filename must only be used with file size limit");
70 filename
= (char *)sgen_alloc_internal_dynamic (strlen (filename_or_prefix
) + 32, INTERNAL_MEM_BINARY_PROTOCOL
, TRUE
);
71 sprintf (filename
, "%s.%d", filename_or_prefix
, index
);
77 free_filename (char *filename
)
79 SGEN_ASSERT (0, file_size_limit
> 0, "Indexed binary protocol filename must only be used with file size limit");
81 sgen_free_internal_dynamic (filename
, strlen (filename_or_prefix
) + 32, INTERNAL_MEM_BINARY_PROTOCOL
);
85 binary_protocol_open_file (gboolean assert_on_failure
)
90 lock
.l_type
= F_WRLCK
;
91 lock
.l_whence
= SEEK_SET
;
96 if (file_size_limit
> 0)
97 filename
= filename_for_index (current_file_index
);
99 filename
= filename_or_prefix
;
101 #if defined(HOST_WIN32)
102 binary_protocol_file
= CreateFileA (filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
103 #elif defined(HAVE_UNISTD_H)
105 binary_protocol_file
= open (filename
, O_CREAT
| O_WRONLY
, 0644);
106 if (binary_protocol_file
== -1) {
110 } else if (fcntl (binary_protocol_file
, F_SETLK
, &lock
) == -1) {
111 /* The lock for the file is already taken. Fail */
112 close (binary_protocol_file
);
113 binary_protocol_file
= -1;
117 /* We have acquired the lock. Truncate the file */
118 ftruncate (binary_protocol_file
, 0);
120 } while (binary_protocol_file
== -1);
122 g_error ("sgen binary protocol: not supported");
124 if (binary_protocol_file
== invalid_file_value
&& assert_on_failure
)
125 g_error ("sgen binary protocol: failed to open file");
127 if (file_size_limit
> 0)
128 free_filename (filename
);
132 sgen_binary_protocol_init (const char *filename
, long long limit
)
134 file_size_limit
= limit
;
136 /* Original name length + . + pid length in hex + null terminator */
137 filename_or_prefix
= g_strdup_printf ("%s", filename
);
138 binary_protocol_open_file (FALSE
);
140 if (binary_protocol_file
== invalid_file_value
) {
141 /* Another process owns the file, try adding the pid suffix to the filename */
142 gint32 pid
= mono_process_current_pid ();
143 g_free (filename_or_prefix
);
144 filename_or_prefix
= g_strdup_printf ("%s.%x", filename
, pid
);
145 binary_protocol_open_file (TRUE
);
148 /* If we have a file size limit, we might need to open additional files */
149 if (file_size_limit
== 0)
150 g_free (filename_or_prefix
);
152 sgen_binary_protocol_header (PROTOCOL_HEADER_CHECK
, PROTOCOL_HEADER_VERSION
, SIZEOF_VOID_P
, G_BYTE_ORDER
== G_LITTLE_ENDIAN
);
156 sgen_binary_protocol_is_enabled (void)
158 return binary_protocol_file
!= invalid_file_value
;
162 close_binary_protocol_file (void)
164 #if defined(HOST_WIN32)
165 CloseHandle (binary_protocol_file
);
166 #elif defined(HAVE_UNISTD_H)
167 while (close (binary_protocol_file
) == -1 && errno
== EINTR
)
170 binary_protocol_file
= invalid_file_value
;
174 try_lock_exclusive (void)
177 if (binary_protocol_use_count
)
179 } while (mono_atomic_cas_i32 (&binary_protocol_use_count
, -1, 0) != 0);
180 mono_memory_barrier ();
185 unlock_exclusive (void)
187 mono_memory_barrier ();
188 SGEN_ASSERT (0, binary_protocol_use_count
== -1, "Exclusively locked count must be -1");
189 if (mono_atomic_cas_i32 (&binary_protocol_use_count
, 0, -1) != -1)
190 SGEN_ASSERT (0, FALSE
, "Somebody messed with the exclusive lock");
194 lock_recursive (void)
199 old_count
= binary_protocol_use_count
;
201 /* Exclusively locked - retry */
202 /* FIXME: short back-off */
205 } while (mono_atomic_cas_i32 (&binary_protocol_use_count
, old_count
+ 1, old_count
) != old_count
);
206 mono_memory_barrier ();
210 unlock_recursive (void)
213 mono_memory_barrier ();
215 old_count
= binary_protocol_use_count
;
216 SGEN_ASSERT (0, old_count
> 0, "Locked use count must be at least 1");
217 } while (mono_atomic_cas_i32 (&binary_protocol_use_count
, old_count
- 1, old_count
) != old_count
);
221 binary_protocol_flush_buffer (BinaryProtocolBuffer
*buffer
)
223 size_t to_write
= buffer
->index
;
225 g_assert (buffer
->index
> 0);
227 while (binary_protocol_file
!= invalid_file_value
&& written
< to_write
) {
228 #if defined(HOST_WIN32)
230 if (WriteFile (binary_protocol_file
, buffer
->buffer
+ written
, to_write
- written
, &tmp_written
, NULL
))
231 written
+= tmp_written
;
232 #elif defined(HAVE_UNISTD_H)
233 ssize_t ret
= write (binary_protocol_file
, buffer
->buffer
+ written
, to_write
- written
);
236 else if (errno
== EINTR
)
240 close_binary_protocol_file ();
243 current_file_size
+= buffer
->index
;
245 sgen_free_os_memory (buffer
, sizeof (BinaryProtocolBuffer
), SGEN_ALLOC_INTERNAL
, MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL
);
249 binary_protocol_check_file_overflow (void)
251 if (file_size_limit
<= 0 || current_file_size
< file_size_limit
)
254 close_binary_protocol_file ();
256 if (current_file_index
> 0) {
257 char *filename
= filename_for_index (current_file_index
- 1);
259 free_filename (filename
);
262 ++current_file_index
;
263 current_file_size
= 0;
265 binary_protocol_open_file (TRUE
);
269 * Flushing buffers takes an exclusive lock, so it must only be done when the world is
270 * stopped, otherwise we might end up with a deadlock because a stopped thread owns the
273 * The protocol entries that do flush have `FLUSH()` in their definition.
276 sgen_binary_protocol_flush_buffers (gboolean force
)
278 int num_buffers
= 0, i
;
279 BinaryProtocolBuffer
*header
;
280 BinaryProtocolBuffer
*buf
;
281 BinaryProtocolBuffer
**bufs
;
283 if (binary_protocol_file
== invalid_file_value
)
286 if (!force
&& !try_lock_exclusive ())
289 header
= binary_protocol_buffers
;
290 for (buf
= header
; buf
!= NULL
; buf
= buf
->next
)
292 bufs
= (BinaryProtocolBuffer
**)sgen_alloc_internal_dynamic (num_buffers
* sizeof (BinaryProtocolBuffer
*), INTERNAL_MEM_BINARY_PROTOCOL
, TRUE
);
293 for (buf
= header
, i
= 0; buf
!= NULL
; buf
= buf
->next
, i
++)
295 SGEN_ASSERT (0, i
== num_buffers
, "Binary protocol buffer count error");
298 * This might be incorrect when forcing, but all bets are off in that case, anyway,
299 * because we're trying to figure out a bug in the debugger.
301 binary_protocol_buffers
= NULL
;
303 for (i
= num_buffers
- 1; i
>= 0; --i
) {
304 binary_protocol_flush_buffer (bufs
[i
]);
305 binary_protocol_check_file_overflow ();
308 sgen_free_internal_dynamic (buf
, num_buffers
* sizeof (BinaryProtocolBuffer
*), INTERNAL_MEM_BINARY_PROTOCOL
);
316 static BinaryProtocolBuffer
*
317 binary_protocol_get_buffer (int length
)
319 BinaryProtocolBuffer
*buffer
, *new_buffer
;
321 buffer
= binary_protocol_buffers
;
322 if (buffer
&& buffer
->index
+ length
<= BINARY_PROTOCOL_BUFFER_SIZE
)
325 new_buffer
= (BinaryProtocolBuffer
*)sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer
), (SgenAllocFlags
)(SGEN_ALLOC_INTERNAL
| SGEN_ALLOC_ACTIVATE
), "debugging memory", MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL
);
326 new_buffer
->next
= buffer
;
327 new_buffer
->index
= 0;
329 if (mono_atomic_cas_ptr ((void**)&binary_protocol_buffers
, new_buffer
, buffer
) != buffer
) {
330 sgen_free_os_memory (new_buffer
, sizeof (BinaryProtocolBuffer
), SGEN_ALLOC_INTERNAL
, MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL
);
338 protocol_entry (unsigned char type
, gpointer data
, int size
)
341 gboolean include_worker_index
= type
!= PROTOCOL_ID (binary_protocol_header
);
342 int entry_size
= size
+ 1 + (include_worker_index
? 1 : 0); // type + worker_index + size
343 BinaryProtocolBuffer
*buffer
;
345 if (binary_protocol_file
== invalid_file_value
)
351 buffer
= binary_protocol_get_buffer (size
+ 1);
353 index
= buffer
->index
;
354 if (index
+ entry_size
> BINARY_PROTOCOL_BUFFER_SIZE
)
357 if (mono_atomic_cas_i32 (&buffer
->index
, index
+ entry_size
, index
) != index
)
358 goto retry_same_buffer
;
360 /* FIXME: if we're interrupted at this point, we have a buffer
361 entry that contains random data. */
363 buffer
->buffer
[index
++] = type
;
364 /* We should never change the header format */
365 if (include_worker_index
) {
367 MonoNativeThreadId tid
= mono_native_thread_id_get ();
369 * If the thread is not a worker thread we insert 0, which is interpreted
370 * as gc thread. Worker indexes are 1 based.
372 worker_index
= sgen_thread_pool_is_thread_pool_thread (tid
);
373 /* FIXME Consider using different index bases for different thread pools */
374 buffer
->buffer
[index
++] = (unsigned char) worker_index
;
376 memcpy (buffer
->buffer
+ index
, data
, size
);
379 g_assert (index
<= BINARY_PROTOCOL_BUFFER_SIZE
);
385 #define TYPE_LONGLONG long long
386 #define TYPE_SIZE size_t
387 #define TYPE_POINTER gpointer
388 #define TYPE_BOOL gboolean
390 #define BEGIN_PROTOCOL_ENTRY0(method) \
391 void sgen_ ## method (void) { \
392 int __type = PROTOCOL_ID(method); \
393 gpointer __data = NULL; \
395 CLIENT_PROTOCOL_NAME (method) ();
396 #define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
397 void sgen_ ## method (t1 f1) { \
398 PROTOCOL_STRUCT(method) __entry = { f1 }; \
399 int __type = PROTOCOL_ID(method); \
400 gpointer __data = &__entry; \
401 int __size = sizeof (PROTOCOL_STRUCT(method)); \
402 CLIENT_PROTOCOL_NAME (method) (f1);
403 #define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
404 void sgen_ ## method (t1 f1, t2 f2) { \
405 PROTOCOL_STRUCT(method) __entry = { f1, f2 }; \
406 int __type = PROTOCOL_ID(method); \
407 gpointer __data = &__entry; \
408 int __size = sizeof (PROTOCOL_STRUCT(method)); \
409 CLIENT_PROTOCOL_NAME (method) (f1, f2);
410 #define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
411 void sgen_ ## method (t1 f1, t2 f2, t3 f3) { \
412 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3 }; \
413 int __type = PROTOCOL_ID(method); \
414 gpointer __data = &__entry; \
415 int __size = sizeof (PROTOCOL_STRUCT(method)); \
416 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3);
417 #define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
418 void sgen_ ## method (t1 f1, t2 f2, t3 f3, t4 f4) { \
419 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4 }; \
420 int __type = PROTOCOL_ID(method); \
421 gpointer __data = &__entry; \
422 int __size = sizeof (PROTOCOL_STRUCT(method)); \
423 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4);
424 #define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
425 void sgen_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5) { \
426 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5 }; \
427 int __type = PROTOCOL_ID(method); \
428 gpointer __data = &__entry; \
429 int __size = sizeof (PROTOCOL_STRUCT(method)); \
430 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5);
431 #define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
432 void sgen_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6) { \
433 PROTOCOL_STRUCT(method) __entry = { f1, f2, f3, f4, f5, f6 }; \
434 int __type = PROTOCOL_ID(method); \
435 gpointer __data = &__entry; \
436 int __size = sizeof (PROTOCOL_STRUCT(method)); \
437 CLIENT_PROTOCOL_NAME (method) (f1, f2, f3, f4, f5, f6);
439 #define DEFAULT_PRINT()
440 #define CUSTOM_PRINT(_)
442 #define IS_ALWAYS_MATCH(_)
443 #define MATCH_INDEX(_)
444 #define IS_VTABLE_MATCH(_)
446 #define END_PROTOCOL_ENTRY \
447 protocol_entry (__type, __data, __size); \
450 #define END_PROTOCOL_ENTRY_FLUSH \
451 protocol_entry (__type, __data, __size); \
452 sgen_binary_protocol_flush_buffers (FALSE); \
455 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
456 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
457 BEGIN_PROTOCOL_ENTRY0 (method)
458 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
459 BEGIN_PROTOCOL_ENTRY1 (method,t1,f1)
460 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
461 BEGIN_PROTOCOL_ENTRY2 (method,t1,f1,t2,f2)
462 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
463 BEGIN_PROTOCOL_ENTRY3 (method,t1,f1,t2,f2,t3,f3)
464 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
465 BEGIN_PROTOCOL_ENTRY4 (method,t1,f1,t2,f2,t3,f3,t4,f4)
466 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
467 BEGIN_PROTOCOL_ENTRY5 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
468 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
469 BEGIN_PROTOCOL_ENTRY6 (method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
471 #define END_PROTOCOL_ENTRY_HEAVY \
474 #define BEGIN_PROTOCOL_ENTRY_HEAVY0(method)
475 #define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1)
476 #define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2)
477 #define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3)
478 #define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4)
479 #define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5)
480 #define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6)
482 #define END_PROTOCOL_ENTRY_HEAVY
485 #include "sgen-protocol-def.h"
495 #endif /* HAVE_SGEN_GC */