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