[sgen-bridge] Move bridge debug option handling to sgen-bridge.c.
[mono.git] / mono / metadata / sgen-bridge.c
1 /*
2  * sgen-bridge.c: Simple generational GC.
3  *
4  * Copyright 2011 Novell, Inc (http://www.novell.com)
5  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
6  *
7  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
9  *
10  * Permission is hereby granted to use or copy this program
11  * for any purpose,  provided the above notices are retained on all copies.
12  * Permission to modify the code and to distribute modified code is granted,
13  * provided the above notices are retained, and a notice that the code was
14  * modified is included with the above copyright notice.
15  *
16  *
17  * Copyright 2001-2003 Ximian, Inc
18  * Copyright 2003-2010 Novell, Inc.
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include "config.h"
41
42 #ifdef HAVE_SGEN_GC
43
44 #include <stdlib.h>
45
46 #include "sgen-gc.h"
47 #include "sgen-bridge.h"
48 #include "sgen-hash-table.h"
49 #include "sgen-qsort.h"
50 #include "utils/mono-logger-internal.h"
51 #include "utils/mono-time.h"
52 #include "utils/mono-compiler.h"
53
54 MonoGCBridgeCallbacks bridge_callbacks;
55 static SgenBridgeProcessor bridge_processor;
56
57 gboolean bridge_processing_in_progress = FALSE;
58
59 void
60 mono_gc_wait_for_bridge_processing (void)
61 {
62         if (!bridge_processing_in_progress)
63                 return;
64
65         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE waiting for bridge processing to finish");
66
67         sgen_gc_lock ();
68         sgen_gc_unlock ();
69 }
70
71
72 void
73 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks)
74 {
75         if (callbacks->bridge_version != SGEN_BRIDGE_VERSION)
76                 g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION, callbacks->bridge_version);
77
78         bridge_callbacks = *callbacks;
79
80         if (!bridge_processor.reset_data)
81                 sgen_old_bridge_init (&bridge_processor);
82 }
83
84 void
85 sgen_set_bridge_implementation (const char *name)
86 {
87         if (!strcmp ("old", name)) {
88                 memset (&bridge_processor, 0, sizeof (SgenBridgeProcessor));
89                 sgen_old_bridge_init (&bridge_processor);
90         } else if (!strcmp ("new", name)) {
91                 memset (&bridge_processor, 0, sizeof (SgenBridgeProcessor));
92                 sgen_new_bridge_init (&bridge_processor);
93         } else {
94                 g_warning ("Invalid value for bridge implementation, valid values are: 'new' and 'old'.");
95         }
96 }
97
98 gboolean
99 sgen_is_bridge_object (MonoObject *obj)
100 {
101         if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OBJECT) != SGEN_GC_BIT_BRIDGE_OBJECT)
102                 return FALSE;
103         return bridge_callbacks.is_bridge_object (obj);
104 }
105
106 gboolean
107 sgen_need_bridge_processing (void)
108 {
109         return bridge_callbacks.cross_references != NULL;
110 }
111
112 /* Dispatch wrappers */
113 void
114 sgen_bridge_reset_data (void)
115 {
116         bridge_processor.reset_data ();
117 }
118
119 void
120 sgen_bridge_processing_stw_step (void)
121 {
122         /*
123          * bridge_processing_in_progress must be set with the world
124          * stopped.  If not there would be race conditions.
125          */
126         bridge_processing_in_progress = TRUE;
127
128         bridge_processor.processing_stw_step ();
129 }
130
131 static mono_bool
132 is_bridge_object_alive (MonoObject *obj, void *data)
133 {
134         SgenHashTable *table = data;
135         unsigned char *value = sgen_hash_table_lookup (table, obj);
136         if (!value)
137                 return TRUE;
138         return *value;
139 }
140
141 void
142 sgen_bridge_processing_finish (int generation)
143 {
144         int num_sccs, num_xrefs;
145         MonoGCBridgeSCC **api_sccs;
146         MonoGCBridgeXRef *api_xrefs;
147         int i, j;
148         SgenHashTable alive_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE, INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE_ENTRY, 1, mono_aligned_addr_hash, NULL);
149         unsigned long step_8;
150         SGEN_TV_DECLARE (atv);
151         SGEN_TV_DECLARE (btv);
152
153         bridge_processor.processing_build_callback_data (generation);
154
155         if (bridge_processor.num_sccs == 0) {
156                 g_assert (bridge_processor.num_xrefs == 0);
157                 goto after_callback;
158         }
159
160         num_sccs = bridge_processor.num_sccs;
161         num_xrefs = bridge_processor.num_xrefs;
162         api_sccs = bridge_processor.api_sccs;
163         api_xrefs = bridge_processor.api_xrefs;
164
165         bridge_callbacks.cross_references (bridge_processor.num_sccs, bridge_processor.api_sccs,
166                         bridge_processor.num_xrefs, bridge_processor.api_xrefs);
167
168         /* Release for finalization those objects we no longer care. */
169         SGEN_TV_GETTIME (btv);
170
171         for (i = 0; i < num_sccs; ++i) {
172                 unsigned char alive = api_sccs [i]->is_alive ? 1 : 0;
173                 for (j = 0; j < api_sccs [i]->num_objs; ++j) {
174                         /* Build hash table for nulling weak links. */
175                         sgen_hash_table_replace (&alive_hash, api_sccs [i]->objs [j], &alive, NULL);
176
177                         /* Release for finalization those objects we no longer care. */
178                         if (!api_sccs [i]->is_alive)
179                                 sgen_mark_bridge_object (api_sccs [i]->objs [j]);
180                 }
181         }
182
183         /* Null weak links to dead objects. */
184         sgen_null_links_with_predicate (GENERATION_NURSERY, is_bridge_object_alive, &alive_hash);
185         if (generation == GENERATION_OLD)
186                 sgen_null_links_with_predicate (GENERATION_OLD, is_bridge_object_alive, &alive_hash);
187
188         sgen_hash_table_clean (&alive_hash);
189
190         /* free callback data */
191
192         for (i = 0; i < num_sccs; ++i) {
193                 sgen_free_internal_dynamic (api_sccs [i],
194                                 sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * api_sccs [i]->num_objs,
195                                 INTERNAL_MEM_BRIDGE_DATA);
196         }
197         sgen_free_internal_dynamic (api_sccs, sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA);
198
199         sgen_free_internal_dynamic (api_xrefs, sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA);
200
201         bridge_processor.num_sccs = 0;
202         bridge_processor.api_sccs = NULL;
203         bridge_processor.num_xrefs = 0;
204         bridge_processor.api_xrefs = NULL;
205
206         SGEN_TV_GETTIME (atv);
207         step_8 = SGEN_TV_ELAPSED (btv, atv);
208
209  after_callback:
210         bridge_processor.processing_after_callback (generation);
211
212         bridge_processing_in_progress = FALSE;
213 }
214
215 MonoGCBridgeObjectKind
216 sgen_bridge_class_kind (MonoClass *class)
217 {
218         return bridge_processor.class_kind (class);
219 }
220
221 void
222 sgen_bridge_register_finalized_object (MonoObject *obj)
223 {
224         bridge_processor.register_finalized_object (obj);
225 }
226
227 void
228 sgen_bridge_describe_pointer (MonoObject *obj)
229 {
230         bridge_processor.describe_pointer (obj);
231 }
232
233 static void
234 set_dump_prefix (const char *prefix)
235 {
236         if (!bridge_processor.set_dump_prefix) {
237                 fprintf (stderr, "Warning: Bridge implementation does not support dumping - ignoring.\n");
238                 return;
239         }
240
241         bridge_processor.set_dump_prefix (prefix);
242 }
243
244 /* Test support code */
245 static const char *bridge_class;
246
247 static MonoGCBridgeObjectKind
248 bridge_test_bridge_class_kind (MonoClass *class)
249 {
250         if (!strcmp (bridge_class, class->name))
251                 return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS;
252         return GC_BRIDGE_TRANSPARENT_CLASS;
253 }
254
255 static gboolean
256 bridge_test_is_bridge_object (MonoObject *object)
257 {
258         return TRUE;
259 }
260
261 static void
262 bridge_test_cross_reference (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
263 {
264         int i;
265         for (i = 0; i < num_sccs; ++i) {
266                 int j;
267         //      g_print ("--- SCC %d\n", i);
268                 for (j = 0; j < sccs [i]->num_objs; ++j) {
269         //              g_print ("  %s\n", sgen_safe_name (sccs [i]->objs [j]));
270                         if (i & 1) /*retain half of the bridged objects */
271                                 sccs [i]->is_alive = TRUE;
272                 }
273         }
274         for (i = 0; i < num_xrefs; ++i) {
275                 g_assert (xrefs [i].src_scc_index >= 0 && xrefs [i].src_scc_index < num_sccs);
276                 g_assert (xrefs [i].dst_scc_index >= 0 && xrefs [i].dst_scc_index < num_sccs);
277         //      g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index);
278         }
279 }
280
281 static MonoClassField *mono_bridge_test_field;
282
283 enum {
284         BRIDGE_DEAD,
285         BRIDGE_ROOT,
286         BRIDGE_SAME_SCC,
287         BRIDGE_XREF,
288 };
289
290 static gboolean
291 test_scc (MonoGCBridgeSCC *scc, int i)
292 {
293         int status = BRIDGE_DEAD;
294         mono_field_get_value (scc->objs [i], mono_bridge_test_field, &status);
295         return status > 0;
296 }
297
298 static void
299 mark_scc (MonoGCBridgeSCC *scc, int value)
300 {
301         int i;
302         for (i = 0; i < scc->num_objs; ++i) {
303                 if (!test_scc (scc, i)) {
304                         int status = value;
305                         mono_field_set_value (scc->objs [i], mono_bridge_test_field, &status);
306                 }
307         }
308 }
309
310 static void
311 bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
312 {
313         int i;
314         gboolean modified;
315
316         if (!mono_bridge_test_field) {
317                 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
318                 g_assert (mono_bridge_test_field);
319         }
320
321         /*We mark all objects in a scc with live objects as reachable by scc*/
322         for (i = 0; i < num_sccs; ++i) {
323                 int j;
324                 gboolean live = FALSE;
325                 for (j = 0; j < sccs [i]->num_objs; ++j) {
326                         if (test_scc (sccs [i], j)) {
327                                 live = TRUE;
328                                 break;
329                         }
330                 }
331                 if (!live)
332                         continue;
333                 for (j = 0; j < sccs [i]->num_objs; ++j) {
334                         if (!test_scc (sccs [i], j)) {
335                                 int status = BRIDGE_SAME_SCC;
336                                 mono_field_set_value (sccs [i]->objs [j], mono_bridge_test_field, &status);
337                         }
338                 }
339         }
340
341         /*Now we mark the transitive closure of reachable objects from the xrefs*/
342         modified = TRUE;
343         while (modified) {
344                 modified = FALSE;
345                 /* Mark all objects that are brought to life due to xrefs*/
346                 for (i = 0; i < num_xrefs; ++i) {
347                         MonoGCBridgeXRef ref = xrefs [i];
348                         if (test_scc (sccs [ref.src_scc_index], 0) && !test_scc (sccs [ref.dst_scc_index], 0)) {
349                                 modified = TRUE;
350                                 mark_scc (sccs [ref.dst_scc_index], BRIDGE_XREF);
351                         }
352                 }
353         }
354
355         /* keep everything in memory, all we want to do is test persistence */
356         for (i = 0; i < num_sccs; ++i)
357                 sccs [i]->is_alive = TRUE;
358 }
359
360 static void
361 register_test_bridge_callbacks (const char *bridge_class_name)
362 {
363         MonoGCBridgeCallbacks callbacks;
364         callbacks.bridge_version = SGEN_BRIDGE_VERSION;
365         callbacks.bridge_class_kind = bridge_test_bridge_class_kind;
366         callbacks.is_bridge_object = bridge_test_is_bridge_object;
367         callbacks.cross_references = bridge_class_name[0] == '2' ? bridge_test_cross_reference2 : bridge_test_cross_reference;
368         mono_gc_register_bridge_callbacks (&callbacks);
369         bridge_class = bridge_class_name + (bridge_class_name[0] == '2' ? 1 : 0);
370 }
371
372 gboolean
373 sgen_bridge_handle_gc_debug (const char *opt)
374 {
375         if (g_str_has_prefix (opt, "bridge=")) {
376                 opt = strchr (opt, '=') + 1;
377                 register_test_bridge_callbacks (g_strdup (opt));
378         } else if (!strcmp (opt, "enable-bridge-accounting")) {
379                 bridge_processor.enable_accounting ();
380         } else if (g_str_has_prefix (opt, "bridge-dump=")) {
381                 char *prefix = strchr (opt, '=') + 1;
382                 set_dump_prefix (prefix);
383         } else {
384                 return FALSE;
385         }
386         return TRUE;
387 }
388
389 void
390 sgen_bridge_print_gc_debug_usage (void)
391 {
392         fprintf (stderr, "  bridge=<class-name>\n");
393         fprintf (stderr, "  enable-bridge-accounting\n");
394         fprintf (stderr, "  bridge-dump=<filename-prefix>\n");
395 }
396
397 #endif