Merge pull request #1304 from slluis/mac-proxy-autoconfig
[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-gc.h"
27 #include "sgen-protocol.h"
28 #include "sgen-memory-governor.h"
29 #include "utils/mono-mmap.h"
30 #include "utils/mono-threads.h"
31
32 /* If not null, dump binary protocol to this file */
33 static FILE *binary_protocol_file = NULL;
34
35 /* We set this to -1 to indicate an exclusive lock */
36 static volatile int binary_protocol_use_count = 0;
37
38 #define BINARY_PROTOCOL_BUFFER_SIZE     (65536 - 2 * 8)
39
40 typedef struct _BinaryProtocolBuffer BinaryProtocolBuffer;
41 struct _BinaryProtocolBuffer {
42         BinaryProtocolBuffer * volatile next;
43         volatile int index;
44         unsigned char buffer [BINARY_PROTOCOL_BUFFER_SIZE];
45 };
46
47 static BinaryProtocolBuffer * volatile binary_protocol_buffers = NULL;
48
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;
53
54 static char*
55 filename_for_index (int index)
56 {
57         char *filename;
58
59         SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
60
61         filename = sgen_alloc_internal_dynamic (strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
62         sprintf (filename, "%s.%d", filename_or_prefix, index);
63
64         return filename;
65 }
66
67 static void
68 free_filename (char *filename)
69 {
70         SGEN_ASSERT (0, file_size_limit > 0, "Indexed binary protocol filename must only be used with file size limit");
71
72         sgen_free_internal_dynamic (filename, strlen (filename_or_prefix) + 32, INTERNAL_MEM_BINARY_PROTOCOL);
73 }
74
75 static void
76 binary_protocol_open_file (void)
77 {
78         char *filename;
79
80         if (file_size_limit > 0)
81                 filename = filename_for_index (current_file_index);
82         else
83                 filename = filename_or_prefix;
84
85         binary_protocol_file = fopen (filename, "w");
86
87         if (file_size_limit > 0)
88                 free_filename (filename);
89 }
90
91 void
92 binary_protocol_init (const char *filename, long long limit)
93 {
94         filename_or_prefix = sgen_alloc_internal_dynamic (strlen (filename) + 1, INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
95         strcpy (filename_or_prefix, filename);
96
97         file_size_limit = limit;
98
99         binary_protocol_open_file ();
100 }
101
102 gboolean
103 binary_protocol_is_enabled (void)
104 {
105         return binary_protocol_file != NULL;
106 }
107
108 static gboolean
109 try_lock_exclusive (void)
110 {
111         do {
112                 if (binary_protocol_use_count)
113                         return FALSE;
114         } while (InterlockedCompareExchange (&binary_protocol_use_count, -1, 0) != 0);
115         mono_memory_barrier ();
116         return TRUE;
117 }
118
119 static void
120 unlock_exclusive (void)
121 {
122         mono_memory_barrier ();
123         SGEN_ASSERT (0, binary_protocol_use_count == -1, "Exclusively locked count must be -1");
124         if (InterlockedCompareExchange (&binary_protocol_use_count, 0, -1) != -1)
125                 SGEN_ASSERT (0, FALSE, "Somebody messed with the exclusive lock");
126 }
127
128 static void
129 lock_recursive (void)
130 {
131         int old_count;
132         do {
133         retry:
134                 old_count = binary_protocol_use_count;
135                 if (old_count < 0) {
136                         /* Exclusively locked - retry */
137                         /* FIXME: short back-off */
138                         goto retry;
139                 }
140         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
141         mono_memory_barrier ();
142 }
143
144 static void
145 unlock_recursive (void)
146 {
147         int old_count;
148         mono_memory_barrier ();
149         do {
150                 old_count = binary_protocol_use_count;
151                 SGEN_ASSERT (0, old_count > 0, "Locked use count must be at least 1");
152         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count - 1, old_count) != old_count);
153 }
154
155 static void
156 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
157 {
158         g_assert (buffer->index > 0);
159         fwrite (buffer->buffer, 1, buffer->index, binary_protocol_file);
160         current_file_size += buffer->index;
161
162         sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
163 }
164
165 static void
166 binary_protocol_check_file_overflow (void)
167 {
168         if (file_size_limit <= 0 || current_file_size < file_size_limit)
169                 return;
170
171         fclose (binary_protocol_file);
172         binary_protocol_file = NULL;
173
174         if (current_file_index > 0) {
175                 char *filename = filename_for_index (current_file_index - 1);
176                 unlink (filename);
177                 free_filename (filename);
178         }
179
180         ++current_file_index;
181         current_file_size = 0;
182
183         binary_protocol_open_file ();
184 }
185
186 void
187 binary_protocol_flush_buffers (gboolean force)
188 {
189         int num_buffers = 0, i;
190         BinaryProtocolBuffer *buf;
191         BinaryProtocolBuffer **bufs;
192
193         if (!binary_protocol_file)
194                 return;
195
196         if (!force && !try_lock_exclusive ())
197                 return;
198
199         for (buf = binary_protocol_buffers; buf != NULL; buf = buf->next)
200                 ++num_buffers;
201         bufs = sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
202         for (buf = binary_protocol_buffers, i = 0; buf != NULL; buf = buf->next, i++)
203                 bufs [i] = buf;
204         SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
205
206         binary_protocol_buffers = NULL;
207
208         for (i = num_buffers - 1; i >= 0; --i) {
209                 binary_protocol_flush_buffer (bufs [i]);
210                 binary_protocol_check_file_overflow ();
211         }
212
213         sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
214
215         if (!force)
216                 unlock_exclusive ();
217
218         fflush (binary_protocol_file);
219 }
220
221 static BinaryProtocolBuffer*
222 binary_protocol_get_buffer (int length)
223 {
224         BinaryProtocolBuffer *buffer, *new_buffer;
225
226  retry:
227         buffer = binary_protocol_buffers;
228         if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
229                 return buffer;
230
231         new_buffer = sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging memory");
232         new_buffer->next = buffer;
233         new_buffer->index = 0;
234
235         if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
236                 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
237                 goto retry;
238         }
239
240         return new_buffer;
241 }
242
243
244 static void
245 protocol_entry (unsigned char type, gpointer data, int size)
246 {
247         int index;
248         BinaryProtocolBuffer *buffer;
249
250         if (!binary_protocol_file)
251                 return;
252
253         if (sgen_is_worker_thread (mono_native_thread_id_get ()))
254                 type |= 0x80;
255
256         lock_recursive ();
257
258  retry:
259         buffer = binary_protocol_get_buffer (size + 1);
260  retry_same_buffer:
261         index = buffer->index;
262         if (index + 1 + size > BINARY_PROTOCOL_BUFFER_SIZE)
263                 goto retry;
264
265         if (InterlockedCompareExchange (&buffer->index, index + 1 + size, index) != index)
266                 goto retry_same_buffer;
267
268         /* FIXME: if we're interrupted at this point, we have a buffer
269            entry that contains random data. */
270
271         buffer->buffer [index++] = type;
272         memcpy (buffer->buffer + index, data, size);
273         index += size;
274
275         g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
276
277         unlock_recursive ();
278 }
279
280 void
281 binary_protocol_collection_force (int generation)
282 {
283         SGenProtocolCollectionForce entry = { generation };
284         binary_protocol_flush_buffers (FALSE);
285         protocol_entry (SGEN_PROTOCOL_COLLECTION_FORCE, &entry, sizeof (SGenProtocolCollectionForce));
286 }
287
288 void
289 binary_protocol_collection_begin (int index, int generation)
290 {
291         SGenProtocolCollection entry = { index, generation };
292         binary_protocol_flush_buffers (FALSE);
293         protocol_entry (SGEN_PROTOCOL_COLLECTION_BEGIN, &entry, sizeof (SGenProtocolCollection));
294 }
295
296 void
297 binary_protocol_collection_end (int index, int generation)
298 {
299         SGenProtocolCollection entry = { index, generation };
300         binary_protocol_flush_buffers (FALSE);
301         protocol_entry (SGEN_PROTOCOL_COLLECTION_END, &entry, sizeof (SGenProtocolCollection));
302 }
303
304 void
305 binary_protocol_concurrent_start (void)
306 {
307         protocol_entry (SGEN_PROTOCOL_CONCURRENT_START, NULL, 0);
308 }
309
310 void
311 binary_protocol_concurrent_update_finish (void)
312 {
313         protocol_entry (SGEN_PROTOCOL_CONCURRENT_UPDATE_FINISH, NULL, 0);
314 }
315
316 void
317 binary_protocol_world_stopping (long long timestamp)
318 {
319         SGenProtocolWorldStopping entry = { timestamp };
320         protocol_entry (SGEN_PROTOCOL_WORLD_STOPPING, &entry, sizeof (SGenProtocolWorldStopping));
321 }
322
323 void
324 binary_protocol_world_stopped (long long timestamp, long long total_major_cards,
325                 long long marked_major_cards, long long total_los_cards, long long marked_los_cards)
326 {
327         SGenProtocolWorldStopped entry = { timestamp, total_major_cards, marked_major_cards, total_los_cards, marked_los_cards };
328         protocol_entry (SGEN_PROTOCOL_WORLD_STOPPED, &entry, sizeof (SGenProtocolWorldStopped));
329 }
330
331 void
332 binary_protocol_world_restarting (int generation, long long timestamp,
333                 long long total_major_cards, long long marked_major_cards, long long total_los_cards, long long marked_los_cards)
334 {
335         SGenProtocolWorldRestarting entry = { generation, timestamp, total_major_cards, marked_major_cards, total_los_cards, marked_los_cards };
336         protocol_entry (SGEN_PROTOCOL_WORLD_RESTARTING, &entry, sizeof (SGenProtocolWorldRestarting));
337 }
338
339 void
340 binary_protocol_world_restarted (int generation, long long timestamp)
341 {
342         SGenProtocolWorldRestarted entry = { generation, timestamp };
343         protocol_entry (SGEN_PROTOCOL_WORLD_RESTARTED, &entry, sizeof (SGenProtocolWorldRestarted));
344 }
345
346 void
347 binary_protocol_thread_suspend (gpointer thread, gpointer stopped_ip)
348 {
349         SGenProtocolThreadSuspend entry = { thread, stopped_ip };
350         protocol_entry (SGEN_PROTOCOL_THREAD_SUSPEND, &entry, sizeof (SGenProtocolThreadSuspend));
351 }
352
353 void
354 binary_protocol_thread_restart (gpointer thread)
355 {
356         SGenProtocolThreadRestart entry = { thread };
357         protocol_entry (SGEN_PROTOCOL_THREAD_RESTART, &entry, sizeof (SGenProtocolThreadRestart));
358 }
359
360 void
361 binary_protocol_thread_register (gpointer thread)
362 {
363         SGenProtocolThreadRegister entry = { thread };
364         protocol_entry (SGEN_PROTOCOL_THREAD_REGISTER, &entry, sizeof (SGenProtocolThreadRegister));
365
366 }
367
368 void
369 binary_protocol_thread_unregister (gpointer thread)
370 {
371         SGenProtocolThreadUnregister entry = { thread };
372         protocol_entry (SGEN_PROTOCOL_THREAD_UNREGISTER, &entry, sizeof (SGenProtocolThreadUnregister));
373
374 }
375
376 void
377 binary_protocol_missing_remset (gpointer obj, gpointer obj_vtable, int offset, gpointer value, gpointer value_vtable, int value_pinned)
378 {
379         SGenProtocolMissingRemset entry = { obj, obj_vtable, offset, value, value_vtable, value_pinned };
380         protocol_entry (SGEN_PROTOCOL_MISSING_REMSET, &entry, sizeof (SGenProtocolMissingRemset));
381
382 }
383
384 void
385 binary_protocol_cement (gpointer obj, gpointer vtable, int size)
386 {
387         SGenProtocolCement entry = { obj, vtable, size };
388         protocol_entry (SGEN_PROTOCOL_CEMENT, &entry, sizeof (SGenProtocolCement));
389 }
390
391 void
392 binary_protocol_cement_reset (void)
393 {
394         protocol_entry (SGEN_PROTOCOL_CEMENT_RESET, NULL, 0);
395 }
396
397 void
398 binary_protocol_domain_unload_begin (gpointer domain)
399 {
400         SGenProtocolDomainUnload entry = { domain };
401         protocol_entry (SGEN_PROTOCOL_DOMAIN_UNLOAD_BEGIN, &entry, sizeof (SGenProtocolDomainUnload));
402 }
403
404 void
405 binary_protocol_domain_unload_end (gpointer domain)
406 {
407         SGenProtocolDomainUnload entry = { domain };
408         protocol_entry (SGEN_PROTOCOL_DOMAIN_UNLOAD_END, &entry, sizeof (SGenProtocolDomainUnload));
409 }
410
411 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
412 void
413 binary_protocol_alloc (gpointer obj, gpointer vtable, int size)
414 {
415         SGenProtocolAlloc entry = { obj, vtable, size };
416         protocol_entry (SGEN_PROTOCOL_ALLOC, &entry, sizeof (SGenProtocolAlloc));
417 }
418
419 void
420 binary_protocol_alloc_pinned (gpointer obj, gpointer vtable, int size)
421 {
422         SGenProtocolAlloc entry = { obj, vtable, size };
423         protocol_entry (SGEN_PROTOCOL_ALLOC_PINNED, &entry, sizeof (SGenProtocolAlloc));
424 }
425
426 void
427 binary_protocol_alloc_degraded (gpointer obj, gpointer vtable, int size)
428 {
429         SGenProtocolAlloc entry = { obj, vtable, size };
430         protocol_entry (SGEN_PROTOCOL_ALLOC_DEGRADED, &entry, sizeof (SGenProtocolAlloc));
431 }
432
433 void
434 binary_protocol_copy (gpointer from, gpointer to, gpointer vtable, int size)
435 {
436         SGenProtocolCopy entry = { from, to, vtable, size };
437         protocol_entry (SGEN_PROTOCOL_COPY, &entry, sizeof (SGenProtocolCopy));
438 }
439
440 void
441 binary_protocol_pin (gpointer obj, gpointer vtable, int size)
442 {
443         SGenProtocolPin entry = { obj, vtable, size };
444         protocol_entry (SGEN_PROTOCOL_PIN, &entry, sizeof (SGenProtocolPin));
445 }
446
447 void
448 binary_protocol_mark (gpointer obj, gpointer vtable, int size)
449 {
450         SGenProtocolMark entry = { obj, vtable, size };
451         protocol_entry (SGEN_PROTOCOL_MARK, &entry, sizeof (SGenProtocolMark));
452 }
453
454 void
455 binary_protocol_scan_begin (gpointer obj, gpointer vtable, int size)
456 {
457         SGenProtocolScanBegin entry = { obj, vtable, size };
458         protocol_entry (SGEN_PROTOCOL_SCAN_BEGIN, &entry, sizeof (SGenProtocolScanBegin));
459 }
460
461 void
462 binary_protocol_scan_vtype_begin (gpointer obj, int size)
463 {
464         SGenProtocolScanVTypeBegin entry = { obj, size };
465         protocol_entry (SGEN_PROTOCOL_SCAN_VTYPE_BEGIN, &entry, sizeof (SGenProtocolScanVTypeBegin));
466 }
467
468 void
469 binary_protocol_wbarrier (gpointer ptr, gpointer value, gpointer value_vtable)
470 {
471         SGenProtocolWBarrier entry = { ptr, value, value_vtable };
472         protocol_entry (SGEN_PROTOCOL_WBARRIER, &entry, sizeof (SGenProtocolWBarrier));
473 }
474
475 void
476 binary_protocol_global_remset (gpointer ptr, gpointer value, gpointer value_vtable)
477 {
478         SGenProtocolGlobalRemset entry = { ptr, value, value_vtable };
479         protocol_entry (SGEN_PROTOCOL_GLOBAL_REMSET, &entry, sizeof (SGenProtocolGlobalRemset));
480 }
481
482 void
483 binary_protocol_ptr_update (gpointer ptr, gpointer old_value, gpointer new_value, gpointer vtable, int size)
484 {
485         SGenProtocolPtrUpdate entry = { ptr, old_value, new_value, vtable, size };
486         protocol_entry (SGEN_PROTOCOL_PTR_UPDATE, &entry, sizeof (SGenProtocolPtrUpdate));
487 }
488
489 void
490 binary_protocol_cleanup (gpointer ptr, gpointer vtable, int size)
491 {
492         SGenProtocolCleanup entry = { ptr, vtable, size };
493         protocol_entry (SGEN_PROTOCOL_CLEANUP, &entry, sizeof (SGenProtocolCleanup));
494 }
495
496 void
497 binary_protocol_empty (gpointer start, int size)
498 {
499         SGenProtocolEmpty entry = { start, size };
500         protocol_entry (SGEN_PROTOCOL_EMPTY, &entry, sizeof (SGenProtocolEmpty));
501 }
502
503 void
504 binary_protocol_card_scan (gpointer start, int size)
505 {
506         SGenProtocolCardScan entry = { start, size };
507         protocol_entry (SGEN_PROTOCOL_CARD_SCAN, &entry, sizeof (SGenProtocolCardScan));
508 }
509
510 void
511 binary_protocol_dislink_update (gpointer link, gpointer obj, int track, int staged)
512 {
513         SGenProtocolDislinkUpdate entry = { link, obj, track, staged };
514         protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE, &entry, sizeof (SGenProtocolDislinkUpdate));
515 }
516
517 void
518 binary_protocol_dislink_update_staged (gpointer link, gpointer obj, int track, int index)
519 {
520         SGenProtocolDislinkUpdateStaged entry = { link, obj, track, index };
521         protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE_STAGED, &entry, sizeof (SGenProtocolDislinkUpdateStaged));
522 }
523
524 void
525 binary_protocol_dislink_process_staged (gpointer link, gpointer obj, int index)
526 {
527         SGenProtocolDislinkProcessStaged entry = { link, obj, index };
528         protocol_entry (SGEN_PROTOCOL_DISLINK_PROCESS_STAGED, &entry, sizeof (SGenProtocolDislinkProcessStaged));
529 }
530
531 void
532 binary_protocol_gray_enqueue (gpointer queue, gpointer cursor, gpointer value)
533 {
534         SGenProtocolGrayQueue entry = { queue, cursor, value };
535         protocol_entry (SGEN_PROTOCOL_GRAY_ENQUEUE, &entry, sizeof (SGenProtocolGrayQueue));
536 }
537
538 void
539 binary_protocol_gray_dequeue (gpointer queue, gpointer cursor, gpointer value)
540 {
541         SGenProtocolGrayQueue entry = { queue, cursor, value };
542         protocol_entry (SGEN_PROTOCOL_GRAY_DEQUEUE, &entry, sizeof (SGenProtocolGrayQueue));
543 }
544 #endif
545
546 #endif /* HAVE_SGEN_GC */