Merge pull request #601 from knocte/sock_improvements
[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_buffers_rec (BinaryProtocolBuffer *buffer)
111 {
112         if (!buffer)
113                 return;
114
115         binary_protocol_flush_buffers_rec (buffer->next);
116
117         g_assert (buffer->index > 0);
118         fwrite (buffer->buffer, 1, buffer->index, binary_protocol_file);
119
120         sgen_free_os_memory (buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
121 }
122
123 void
124 binary_protocol_flush_buffers (gboolean force)
125 {
126         if (!binary_protocol_file)
127                 return;
128
129         if (!force && !try_lock_exclusive ())
130                 return;
131
132         binary_protocol_flush_buffers_rec (binary_protocol_buffers);
133         binary_protocol_buffers = NULL;
134
135         if (!force)
136                 unlock_exclusive ();
137
138         fflush (binary_protocol_file);
139 }
140
141 static BinaryProtocolBuffer*
142 binary_protocol_get_buffer (int length)
143 {
144         BinaryProtocolBuffer *buffer, *new_buffer;
145
146  retry:
147         buffer = binary_protocol_buffers;
148         if (buffer && buffer->index + length <= BINARY_PROTOCOL_BUFFER_SIZE)
149                 return buffer;
150
151         new_buffer = sgen_alloc_os_memory (sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging memory");
152         new_buffer->next = buffer;
153         new_buffer->index = 0;
154
155         if (InterlockedCompareExchangePointer ((void**)&binary_protocol_buffers, new_buffer, buffer) != buffer) {
156                 sgen_free_os_memory (new_buffer, sizeof (BinaryProtocolBuffer), SGEN_ALLOC_INTERNAL);
157                 goto retry;
158         }
159
160         return new_buffer;
161 }
162
163
164 static void
165 protocol_entry (unsigned char type, gpointer data, int size)
166 {
167         int index;
168         BinaryProtocolBuffer *buffer;
169
170         if (!binary_protocol_file)
171                 return;
172
173         lock_recursive ();
174
175  retry:
176         buffer = binary_protocol_get_buffer (size + 1);
177  retry_same_buffer:
178         index = buffer->index;
179         if (index + 1 + size > BINARY_PROTOCOL_BUFFER_SIZE)
180                 goto retry;
181
182         if (InterlockedCompareExchange (&buffer->index, index + 1 + size, index) != index)
183                 goto retry_same_buffer;
184
185         /* FIXME: if we're interrupted at this point, we have a buffer
186            entry that contains random data. */
187
188         buffer->buffer [index++] = type;
189         memcpy (buffer->buffer + index, data, size);
190         index += size;
191
192         g_assert (index <= BINARY_PROTOCOL_BUFFER_SIZE);
193
194         unlock_recursive ();
195 }
196
197 void
198 binary_protocol_collection_force (int generation)
199 {
200         SGenProtocolCollectionForce entry = { generation };
201         binary_protocol_flush_buffers (FALSE);
202         protocol_entry (SGEN_PROTOCOL_COLLECTION_FORCE, &entry, sizeof (SGenProtocolCollectionForce));
203 }
204
205 void
206 binary_protocol_collection_begin (int index, int generation)
207 {
208         SGenProtocolCollection entry = { index, generation };
209         binary_protocol_flush_buffers (FALSE);
210         protocol_entry (SGEN_PROTOCOL_COLLECTION_BEGIN, &entry, sizeof (SGenProtocolCollection));
211 }
212
213 void
214 binary_protocol_collection_end (int index, int generation)
215 {
216         SGenProtocolCollection entry = { index, generation };
217         binary_protocol_flush_buffers (FALSE);
218         protocol_entry (SGEN_PROTOCOL_COLLECTION_END, &entry, sizeof (SGenProtocolCollection));
219 }
220
221 void
222 binary_protocol_alloc (gpointer obj, gpointer vtable, int size)
223 {
224         SGenProtocolAlloc entry = { obj, vtable, size };
225         protocol_entry (SGEN_PROTOCOL_ALLOC, &entry, sizeof (SGenProtocolAlloc));
226 }
227
228 void
229 binary_protocol_alloc_pinned (gpointer obj, gpointer vtable, int size)
230 {
231         SGenProtocolAlloc entry = { obj, vtable, size };
232         protocol_entry (SGEN_PROTOCOL_ALLOC_PINNED, &entry, sizeof (SGenProtocolAlloc));
233 }
234
235 void
236 binary_protocol_alloc_degraded (gpointer obj, gpointer vtable, int size)
237 {
238         SGenProtocolAlloc entry = { obj, vtable, size };
239         protocol_entry (SGEN_PROTOCOL_ALLOC_DEGRADED, &entry, sizeof (SGenProtocolAlloc));
240 }
241
242 void
243 binary_protocol_copy (gpointer from, gpointer to, gpointer vtable, int size)
244 {
245         SGenProtocolCopy entry = { from, to, vtable, size };
246         protocol_entry (SGEN_PROTOCOL_COPY, &entry, sizeof (SGenProtocolCopy));
247 }
248
249 void
250 binary_protocol_pin (gpointer obj, gpointer vtable, int size)
251 {
252         SGenProtocolPin entry = { obj, vtable, size };
253         protocol_entry (SGEN_PROTOCOL_PIN, &entry, sizeof (SGenProtocolPin));
254 }
255
256 void
257 binary_protocol_mark (gpointer obj, gpointer vtable, int size)
258 {
259         SGenProtocolMark entry = { obj, vtable, size };
260         protocol_entry (SGEN_PROTOCOL_MARK, &entry, sizeof (SGenProtocolMark));
261 }
262
263 void
264 binary_protocol_scan_begin (gpointer obj, gpointer vtable, int size)
265 {
266         SGenProtocolScanBegin entry = { obj, vtable, size };
267         protocol_entry (SGEN_PROTOCOL_SCAN_BEGIN, &entry, sizeof (SGenProtocolScanBegin));
268 }
269
270 void
271 binary_protocol_scan_vtype_begin (gpointer obj, int size)
272 {
273         SGenProtocolScanVTypeBegin entry = { obj, size };
274         protocol_entry (SGEN_PROTOCOL_SCAN_VTYPE_BEGIN, &entry, sizeof (SGenProtocolScanVTypeBegin));
275 }
276
277 void
278 binary_protocol_wbarrier (gpointer ptr, gpointer value, gpointer value_vtable)
279 {
280         SGenProtocolWBarrier entry = { ptr, value, value_vtable };
281         protocol_entry (SGEN_PROTOCOL_WBARRIER, &entry, sizeof (SGenProtocolWBarrier));
282 }
283
284 void
285 binary_protocol_global_remset (gpointer ptr, gpointer value, gpointer value_vtable)
286 {
287         SGenProtocolGlobalRemset entry = { ptr, value, value_vtable };
288         protocol_entry (SGEN_PROTOCOL_GLOBAL_REMSET, &entry, sizeof (SGenProtocolGlobalRemset));
289 }
290
291 void
292 binary_protocol_ptr_update (gpointer ptr, gpointer old_value, gpointer new_value, gpointer vtable, int size)
293 {
294         SGenProtocolPtrUpdate entry = { ptr, old_value, new_value, vtable, size };
295         protocol_entry (SGEN_PROTOCOL_PTR_UPDATE, &entry, sizeof (SGenProtocolPtrUpdate));
296 }
297
298 void
299 binary_protocol_cleanup (gpointer ptr, gpointer vtable, int size)
300 {
301         SGenProtocolCleanup entry = { ptr, vtable, size };
302         protocol_entry (SGEN_PROTOCOL_CLEANUP, &entry, sizeof (SGenProtocolCleanup));
303 }
304
305 void
306 binary_protocol_empty (gpointer start, int size)
307 {
308         SGenProtocolEmpty entry = { start, size };
309         protocol_entry (SGEN_PROTOCOL_EMPTY, &entry, sizeof (SGenProtocolEmpty));
310 }
311
312 void
313 binary_protocol_thread_suspend (gpointer thread, gpointer stopped_ip)
314 {
315         SGenProtocolThreadSuspend entry = { thread, stopped_ip };
316         protocol_entry (SGEN_PROTOCOL_THREAD_SUSPEND, &entry, sizeof (SGenProtocolThreadSuspend));
317 }
318
319 void
320 binary_protocol_thread_restart (gpointer thread)
321 {
322         SGenProtocolThreadRestart entry = { thread };
323         protocol_entry (SGEN_PROTOCOL_THREAD_RESTART, &entry, sizeof (SGenProtocolThreadRestart));
324 }
325
326 void
327 binary_protocol_thread_register (gpointer thread)
328 {
329         SGenProtocolThreadRegister entry = { thread };
330         protocol_entry (SGEN_PROTOCOL_THREAD_REGISTER, &entry, sizeof (SGenProtocolThreadRegister));
331
332 }
333
334 void
335 binary_protocol_thread_unregister (gpointer thread)
336 {
337         SGenProtocolThreadUnregister entry = { thread };
338         protocol_entry (SGEN_PROTOCOL_THREAD_UNREGISTER, &entry, sizeof (SGenProtocolThreadUnregister));
339
340 }
341
342 void
343 binary_protocol_missing_remset (gpointer obj, gpointer obj_vtable, int offset, gpointer value, gpointer value_vtable, int value_pinned)
344 {
345         SGenProtocolMissingRemset entry = { obj, obj_vtable, offset, value, value_vtable, value_pinned };
346         protocol_entry (SGEN_PROTOCOL_MISSING_REMSET, &entry, sizeof (SGenProtocolMissingRemset));
347
348 }
349
350 void
351 binary_protocol_card_scan (gpointer start, int size)
352 {
353         SGenProtocolCardScan entry = { start, size };
354         protocol_entry (SGEN_PROTOCOL_CARD_SCAN, &entry, sizeof (SGenProtocolCardScan));
355 }
356
357 void
358 binary_protocol_cement (gpointer obj, gpointer vtable, int size)
359 {
360         SGenProtocolCement entry = { obj, vtable, size };
361         protocol_entry (SGEN_PROTOCOL_CEMENT, &entry, sizeof (SGenProtocolCement));
362 }
363
364 void
365 binary_protocol_cement_reset (void)
366 {
367         protocol_entry (SGEN_PROTOCOL_CEMENT_RESET, NULL, 0);
368 }
369
370 void
371 binary_protocol_dislink_update (gpointer link, gpointer obj, int track)
372 {
373         SGenProtocolDislinkUpdate entry = { link, obj, track };
374         protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE, &entry, sizeof (SGenProtocolDislinkUpdate));
375 }
376
377 #endif
378
379 #endif /* HAVE_SGEN_GC */