Merge pull request #943 from ermshiperete/bug-novell-325669
[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 void
50 binary_protocol_init (const char *filename)
51 {
52         binary_protocol_file = fopen (filename, "w");
53 }
54
55 gboolean
56 binary_protocol_is_enabled (void)
57 {
58         return binary_protocol_file != NULL;
59 }
60
61 static gboolean
62 try_lock_exclusive (void)
63 {
64         do {
65                 if (binary_protocol_use_count)
66                         return FALSE;
67         } while (InterlockedCompareExchange (&binary_protocol_use_count, -1, 0) != 0);
68         mono_memory_barrier ();
69         return TRUE;
70 }
71
72 static void
73 unlock_exclusive (void)
74 {
75         mono_memory_barrier ();
76         SGEN_ASSERT (0, binary_protocol_use_count == -1, "Exclusively locked count must be -1");
77         if (InterlockedCompareExchange (&binary_protocol_use_count, 0, -1) != -1)
78                 SGEN_ASSERT (0, FALSE, "Somebody messed with the exclusive lock");
79 }
80
81 static void
82 lock_recursive (void)
83 {
84         int old_count;
85         do {
86         retry:
87                 old_count = binary_protocol_use_count;
88                 if (old_count < 0) {
89                         /* Exclusively locked - retry */
90                         /* FIXME: short back-off */
91                         goto retry;
92                 }
93         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count + 1, old_count) != old_count);
94         mono_memory_barrier ();
95 }
96
97 static void
98 unlock_recursive (void)
99 {
100         int old_count;
101         mono_memory_barrier ();
102         do {
103                 old_count = binary_protocol_use_count;
104                 SGEN_ASSERT (0, old_count > 0, "Locked use count must be at least 1");
105         } while (InterlockedCompareExchange (&binary_protocol_use_count, old_count - 1, old_count) != old_count);
106 }
107
108 static void
109 binary_protocol_flush_buffer (BinaryProtocolBuffer *buffer)
110 {
111         g_assert (buffer->index > 0);
112         fwrite (buffer->buffer, 1, buffer->index, binary_protocol_file);
113
114         sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
115 }
116
117 void
118 binary_protocol_flush_buffers (gboolean force)
119 {
120         int num_buffers = 0, i;
121         BinaryProtocolBuffer *buf;
122         BinaryProtocolBuffer **bufs;
123
124         if (!binary_protocol_file)
125                 return;
126
127         if (!force && !try_lock_exclusive ())
128                 return;
129
130         for (buf = binary_protocol_buffers; buf != NULL; buf = buf->next)
131                 ++num_buffers;
132         bufs = sgen_alloc_internal_dynamic (num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL, TRUE);
133         for (buf = binary_protocol_buffers, i = 0; buf != NULL; buf = buf->next, i++)
134                 bufs [i] = buf;
135         SGEN_ASSERT (0, i == num_buffers, "Binary protocol buffer count error");
136
137         binary_protocol_buffers = NULL;
138
139         for (i = num_buffers - 1; i >= 0; --i)
140                 binary_protocol_flush_buffer (bufs [i]);
141
142         sgen_free_internal_dynamic (buf, num_buffers * sizeof (BinaryProtocolBuffer*), INTERNAL_MEM_BINARY_PROTOCOL);
143
144         if (!force)
145                 unlock_exclusive ();
146
147         fflush (binary_protocol_file);
148 }
149
150 static BinaryProtocolBuffer*
151 binary_protocol_get_buffer (int length)
152 {
153         BinaryProtocolBuffer *buffer, *new_buffer;
154
155  retry:
156         buffer = binary_protocol_buffers;
157         if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
158                 return buffer;
159
160         new_buffer = sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging memory");
161         new_buffer->next = buffer;
162         new_buffer->index = 0;
163
164         if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
165                 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
166                 goto retry;
167         }
168
169         return new_buffer;
170 }
171
172
173 static void
174 protocol_entry (unsigned char type, gpointer data, int size)
175 {
176         int index;
177         BinaryProtocolBuffer *buffer;
178
179         if (!binary_protocol_file)
180                 return;
181
182         if (sgen_is_worker_thread (mono_native_thread_id_get ()))
183                 type |= 0x80;
184
185         lock_recursive ();
186
187  retry:
188         buffer = binary_protocol_get_buffer (size + 1);
189  retry_same_buffer:
190         index = buffer->index;
191         if (index + 1 + size > BINARY_PROTOCOL_BUFFER_SIZE)
192                 goto retry;
193
194         if (InterlockedCompareExchange (&buffer->index, index + 1 + size, index) != index)
195                 goto retry_same_buffer;
196
197         /* FIXME: if we're interrupted at this point, we have a buffer
198            entry that contains random data. */
199
200         buffer->buffer [index++] = type;
201         memcpy (buffer->buffer + index, data, size);
202         index += size;
203
204         g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
205
206         unlock_recursive ();
207 }
208
209 void
210 binary_protocol_collection_force (int generation)
211 {
212         SGenProtocolCollectionForce entry = { generation };
213         binary_protocol_flush_buffers (FALSE);
214         protocol_entry (SGEN_PROTOCOL_COLLECTION_FORCE, &entry, sizeof (SGenProtocolCollectionForce));
215 }
216
217 void
218 binary_protocol_collection_begin (int index, int generation)
219 {
220         SGenProtocolCollection entry = { index, generation };
221         binary_protocol_flush_buffers (FALSE);
222         protocol_entry (SGEN_PROTOCOL_COLLECTION_BEGIN, &entry, sizeof (SGenProtocolCollection));
223 }
224
225 void
226 binary_protocol_collection_end (int index, int generation)
227 {
228         SGenProtocolCollection entry = { index, generation };
229         binary_protocol_flush_buffers (FALSE);
230         protocol_entry (SGEN_PROTOCOL_COLLECTION_END, &entry, sizeof (SGenProtocolCollection));
231 }
232
233 void
234 binary_protocol_concurrent_start (void)
235 {
236         protocol_entry (SGEN_PROTOCOL_CONCURRENT_START, NULL, 0);
237 }
238
239 void
240 binary_protocol_concurrent_update_finish (void)
241 {
242         protocol_entry (SGEN_PROTOCOL_CONCURRENT_UPDATE_FINISH, NULL, 0);
243 }
244
245 void
246 binary_protocol_world_stopping (long long timestamp)
247 {
248         SGenProtocolWorldStopping entry = { timestamp };
249         protocol_entry (SGEN_PROTOCOL_WORLD_STOPPING, &entry, sizeof (SGenProtocolWorldStopping));
250 }
251
252 void
253 binary_protocol_world_stopped (long long timestamp, long long total_major_cards,
254                 long long marked_major_cards, long long total_los_cards, long long marked_los_cards)
255 {
256         SGenProtocolWorldStopped entry = { timestamp, total_major_cards, marked_major_cards, total_los_cards, marked_los_cards };
257         protocol_entry (SGEN_PROTOCOL_WORLD_STOPPED, &entry, sizeof (SGenProtocolWorldStopped));
258 }
259
260 void
261 binary_protocol_world_restarting (int generation, long long timestamp,
262                 long long total_major_cards, long long marked_major_cards, long long total_los_cards, long long marked_los_cards)
263 {
264         SGenProtocolWorldRestarting entry = { generation, timestamp, total_major_cards, marked_major_cards, total_los_cards, marked_los_cards };
265         protocol_entry (SGEN_PROTOCOL_WORLD_RESTARTING, &entry, sizeof (SGenProtocolWorldRestarting));
266 }
267
268 void
269 binary_protocol_world_restarted (int generation, long long timestamp)
270 {
271         SGenProtocolWorldRestarted entry = { generation, timestamp };
272         protocol_entry (SGEN_PROTOCOL_WORLD_RESTARTED, &entry, sizeof (SGenProtocolWorldRestarted));
273 }
274
275 void
276 binary_protocol_thread_suspend (gpointer thread, gpointer stopped_ip)
277 {
278         SGenProtocolThreadSuspend entry = { thread, stopped_ip };
279         protocol_entry (SGEN_PROTOCOL_THREAD_SUSPEND, &entry, sizeof (SGenProtocolThreadSuspend));
280 }
281
282 void
283 binary_protocol_thread_restart (gpointer thread)
284 {
285         SGenProtocolThreadRestart entry = { thread };
286         protocol_entry (SGEN_PROTOCOL_THREAD_RESTART, &entry, sizeof (SGenProtocolThreadRestart));
287 }
288
289 void
290 binary_protocol_thread_register (gpointer thread)
291 {
292         SGenProtocolThreadRegister entry = { thread };
293         protocol_entry (SGEN_PROTOCOL_THREAD_REGISTER, &entry, sizeof (SGenProtocolThreadRegister));
294
295 }
296
297 void
298 binary_protocol_thread_unregister (gpointer thread)
299 {
300         SGenProtocolThreadUnregister entry = { thread };
301         protocol_entry (SGEN_PROTOCOL_THREAD_UNREGISTER, &entry, sizeof (SGenProtocolThreadUnregister));
302
303 }
304
305 void
306 binary_protocol_missing_remset (gpointer obj, gpointer obj_vtable, int offset, gpointer value, gpointer value_vtable, int value_pinned)
307 {
308         SGenProtocolMissingRemset entry = { obj, obj_vtable, offset, value, value_vtable, value_pinned };
309         protocol_entry (SGEN_PROTOCOL_MISSING_REMSET, &entry, sizeof (SGenProtocolMissingRemset));
310
311 }
312
313 void
314 binary_protocol_cement (gpointer obj, gpointer vtable, int size)
315 {
316         SGenProtocolCement entry = { obj, vtable, size };
317         protocol_entry (SGEN_PROTOCOL_CEMENT, &entry, sizeof (SGenProtocolCement));
318 }
319
320 void
321 binary_protocol_cement_reset (void)
322 {
323         protocol_entry (SGEN_PROTOCOL_CEMENT_RESET, NULL, 0);
324 }
325
326 void
327 binary_protocol_domain_unload_begin (gpointer domain)
328 {
329         SGenProtocolDomainUnload entry = { domain };
330         protocol_entry (SGEN_PROTOCOL_DOMAIN_UNLOAD_BEGIN, &entry, sizeof (SGenProtocolDomainUnload));
331 }
332
333 void
334 binary_protocol_domain_unload_end (gpointer domain)
335 {
336         SGenProtocolDomainUnload entry = { domain };
337         protocol_entry (SGEN_PROTOCOL_DOMAIN_UNLOAD_END, &entry, sizeof (SGenProtocolDomainUnload));
338 }
339
340 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
341 void
342 binary_protocol_alloc (gpointer obj, gpointer vtable, int size)
343 {
344         SGenProtocolAlloc entry = { obj, vtable, size };
345         protocol_entry (SGEN_PROTOCOL_ALLOC, &entry, sizeof (SGenProtocolAlloc));
346 }
347
348 void
349 binary_protocol_alloc_pinned (gpointer obj, gpointer vtable, int size)
350 {
351         SGenProtocolAlloc entry = { obj, vtable, size };
352         protocol_entry (SGEN_PROTOCOL_ALLOC_PINNED, &entry, sizeof (SGenProtocolAlloc));
353 }
354
355 void
356 binary_protocol_alloc_degraded (gpointer obj, gpointer vtable, int size)
357 {
358         SGenProtocolAlloc entry = { obj, vtable, size };
359         protocol_entry (SGEN_PROTOCOL_ALLOC_DEGRADED, &entry, sizeof (SGenProtocolAlloc));
360 }
361
362 void
363 binary_protocol_copy (gpointer from, gpointer to, gpointer vtable, int size)
364 {
365         SGenProtocolCopy entry = { from, to, vtable, size };
366         protocol_entry (SGEN_PROTOCOL_COPY, &entry, sizeof (SGenProtocolCopy));
367 }
368
369 void
370 binary_protocol_pin (gpointer obj, gpointer vtable, int size)
371 {
372         SGenProtocolPin entry = { obj, vtable, size };
373         protocol_entry (SGEN_PROTOCOL_PIN, &entry, sizeof (SGenProtocolPin));
374 }
375
376 void
377 binary_protocol_mark (gpointer obj, gpointer vtable, int size)
378 {
379         SGenProtocolMark entry = { obj, vtable, size };
380         protocol_entry (SGEN_PROTOCOL_MARK, &entry, sizeof (SGenProtocolMark));
381 }
382
383 void
384 binary_protocol_scan_begin (gpointer obj, gpointer vtable, int size)
385 {
386         SGenProtocolScanBegin entry = { obj, vtable, size };
387         protocol_entry (SGEN_PROTOCOL_SCAN_BEGIN, &entry, sizeof (SGenProtocolScanBegin));
388 }
389
390 void
391 binary_protocol_scan_vtype_begin (gpointer obj, int size)
392 {
393         SGenProtocolScanVTypeBegin entry = { obj, size };
394         protocol_entry (SGEN_PROTOCOL_SCAN_VTYPE_BEGIN, &entry, sizeof (SGenProtocolScanVTypeBegin));
395 }
396
397 void
398 binary_protocol_wbarrier (gpointer ptr, gpointer value, gpointer value_vtable)
399 {
400         SGenProtocolWBarrier entry = { ptr, value, value_vtable };
401         protocol_entry (SGEN_PROTOCOL_WBARRIER, &entry, sizeof (SGenProtocolWBarrier));
402 }
403
404 void
405 binary_protocol_global_remset (gpointer ptr, gpointer value, gpointer value_vtable)
406 {
407         SGenProtocolGlobalRemset entry = { ptr, value, value_vtable };
408         protocol_entry (SGEN_PROTOCOL_GLOBAL_REMSET, &entry, sizeof (SGenProtocolGlobalRemset));
409 }
410
411 void
412 binary_protocol_ptr_update (gpointer ptr, gpointer old_value, gpointer new_value, gpointer vtable, int size)
413 {
414         SGenProtocolPtrUpdate entry = { ptr, old_value, new_value, vtable, size };
415         protocol_entry (SGEN_PROTOCOL_PTR_UPDATE, &entry, sizeof (SGenProtocolPtrUpdate));
416 }
417
418 void
419 binary_protocol_cleanup (gpointer ptr, gpointer vtable, int size)
420 {
421         SGenProtocolCleanup entry = { ptr, vtable, size };
422         protocol_entry (SGEN_PROTOCOL_CLEANUP, &entry, sizeof (SGenProtocolCleanup));
423 }
424
425 void
426 binary_protocol_empty (gpointer start, int size)
427 {
428         SGenProtocolEmpty entry = { start, size };
429         protocol_entry (SGEN_PROTOCOL_EMPTY, &entry, sizeof (SGenProtocolEmpty));
430 }
431
432 void
433 binary_protocol_card_scan (gpointer start, int size)
434 {
435         SGenProtocolCardScan entry = { start, size };
436         protocol_entry (SGEN_PROTOCOL_CARD_SCAN, &entry, sizeof (SGenProtocolCardScan));
437 }
438
439 void
440 binary_protocol_dislink_update (gpointer link, gpointer obj, int track, int staged)
441 {
442         SGenProtocolDislinkUpdate entry = { link, obj, track, staged };
443         protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE, &entry, sizeof (SGenProtocolDislinkUpdate));
444 }
445
446 void
447 binary_protocol_dislink_update_staged (gpointer link, gpointer obj, int track, int index)
448 {
449         SGenProtocolDislinkUpdateStaged entry = { link, obj, track, index };
450         protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE_STAGED, &entry, sizeof (SGenProtocolDislinkUpdateStaged));
451 }
452
453 void
454 binary_protocol_dislink_process_staged (gpointer link, gpointer obj, int index)
455 {
456         SGenProtocolDislinkProcessStaged entry = { link, obj, index };
457         protocol_entry (SGEN_PROTOCOL_DISLINK_PROCESS_STAGED, &entry, sizeof (SGenProtocolDislinkProcessStaged));
458 }
459 #endif
460
461 #endif /* HAVE_SGEN_GC */