2 * sgen-bridge.c: Simple generational GC.
4 * Copyright 2011 Novell, Inc (http://www.novell.com)
5 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
6 * Copyright 2001-2003 Ximian, Inc
7 * Copyright 2003-2010 Novell, Inc.
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 #include "sgen/sgen-gc.h"
19 #include "sgen-bridge-internals.h"
20 #include "sgen/sgen-hash-table.h"
21 #include "sgen/sgen-qsort.h"
22 #include "utils/mono-logger-internals.h"
25 BRIDGE_PROCESSOR_INVALID
,
28 BRIDGE_PROCESSOR_TARJAN
,
29 BRIDGE_PROCESSOR_DEFAULT
= BRIDGE_PROCESSOR_TARJAN
30 } BridgeProcessorSelection
;
32 // Bridge processor type pending / in use
33 static BridgeProcessorSelection bridge_processor_selection
= BRIDGE_PROCESSOR_DEFAULT
;
34 // Most recently requested callbacks
35 static MonoGCBridgeCallbacks pending_bridge_callbacks
;
36 // Configuration to be passed to bridge processor after init
37 static SgenBridgeProcessorConfig bridge_processor_config
;
38 // Currently-in-use callbacks
39 MonoGCBridgeCallbacks bridge_callbacks
;
41 // Bridge processor state
42 static SgenBridgeProcessor bridge_processor
;
43 // This is used for a special debug feature
44 static SgenBridgeProcessor compare_to_bridge_processor
;
46 volatile gboolean bridge_processing_in_progress
= FALSE
;
48 // FIXME: The current usage pattern for this function is unsafe. Bridge processing could start immediately after unlock
50 mono_gc_wait_for_bridge_processing (void)
52 if (!bridge_processing_in_progress
)
55 mono_trace (G_LOG_LEVEL_INFO
, MONO_TRACE_GC
, "GC_BRIDGE waiting for bridge processing to finish");
62 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks
*callbacks
)
64 if (callbacks
->bridge_version
!= SGEN_BRIDGE_VERSION
)
65 g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION
, callbacks
->bridge_version
);
67 // Defer assigning to bridge_callbacks until we have the gc lock.
68 // Note: This line is unsafe if we are on a separate thread from the one the runtime was initialized on.
69 pending_bridge_callbacks
= *callbacks
;
71 // If sgen has started, will assign bridge callbacks and init bridge
75 static BridgeProcessorSelection
76 bridge_processor_name (const char *name
)
78 if (!strcmp ("old", name
)) {
79 return BRIDGE_PROCESSOR_OLD
;
80 } else if (!strcmp ("new", name
)) {
81 return BRIDGE_PROCESSOR_NEW
;
82 } else if (!strcmp ("tarjan", name
)) {
83 return BRIDGE_PROCESSOR_TARJAN
;
85 return BRIDGE_PROCESSOR_INVALID
;
90 bridge_processor_started ()
92 return bridge_processor
.reset_data
!= NULL
;
95 // Initialize a single bridge processor
97 init_bridge_processor (SgenBridgeProcessor
*processor
, BridgeProcessorSelection selection
)
99 memset (processor
, 0, sizeof (SgenBridgeProcessor
));
102 case BRIDGE_PROCESSOR_OLD
:
103 sgen_old_bridge_init (processor
);
105 case BRIDGE_PROCESSOR_NEW
:
106 sgen_new_bridge_init (processor
);
108 case BRIDGE_PROCESSOR_TARJAN
:
109 sgen_tarjan_bridge_init (processor
);
112 g_assert_not_reached ();
117 * Initializing the sgen bridge consists of setting the bridge callbacks,
118 * and initializing the bridge processor. Init should follow these rules:
120 * - Init happens only after sgen is initialized (because we don't
121 * know which bridge processor to initialize until then, and also
122 * to allow bridge processor init to interact with sgen if it wants)
124 * - Init happens only after mono_gc_register_bridge_callbacks is called
126 * - Init should not happen concurrently with a GC (because a GC will
127 * call sgen_need_bridge_processing at various times)
129 * - Initializing the bridge processor should happen only once
131 * We call sgen_init_bridge when the callbacks are set, and also when sgen
132 * is done initing. Actual initialization then only occurs if it is ready.
137 if (sgen_gc_initialized ()) {
138 // This lock is not initialized until the GC is
141 bridge_callbacks
= pending_bridge_callbacks
;
143 // If a bridge was registered but there is no bridge processor yet
144 if (bridge_callbacks
.cross_references
&& !bridge_processor_started ()) {
145 init_bridge_processor (&bridge_processor
, bridge_processor_selection
);
147 if (bridge_processor
.set_config
)
148 bridge_processor
.set_config (&bridge_processor_config
);
150 // Config is no longer needed so free its memory
151 free (bridge_processor_config
.dump_prefix
);
152 bridge_processor_config
.dump_prefix
= NULL
;
160 sgen_set_bridge_implementation (const char *name
)
162 BridgeProcessorSelection selection
= bridge_processor_name (name
);
164 if (selection
== BRIDGE_PROCESSOR_INVALID
)
165 g_warning ("Invalid value for bridge processor implementation, valid values are: 'new', 'old' and 'tarjan'.");
166 else if (bridge_processor_started ())
167 g_warning ("Cannot set bridge processor implementation once bridge has already started");
169 bridge_processor_selection
= selection
;
173 sgen_is_bridge_object (GCObject
*obj
)
175 if ((obj
->vtable
->gc_bits
& SGEN_GC_BIT_BRIDGE_OBJECT
) != SGEN_GC_BIT_BRIDGE_OBJECT
)
177 return bridge_callbacks
.is_bridge_object (obj
);
181 sgen_need_bridge_processing (void)
183 return bridge_callbacks
.cross_references
!= NULL
;
187 compare_bridge_processors (void)
189 return compare_to_bridge_processor
.reset_data
!= NULL
;
192 /* Dispatch wrappers */
194 sgen_bridge_reset_data (void)
196 bridge_processor
.reset_data ();
197 if (compare_bridge_processors ())
198 compare_to_bridge_processor
.reset_data ();
202 sgen_bridge_processing_stw_step (void)
205 * bridge_processing_in_progress must be set with the world
206 * stopped. If not there would be race conditions.
208 bridge_processing_in_progress
= TRUE
;
210 bridge_processor
.processing_stw_step ();
211 if (compare_bridge_processors ())
212 compare_to_bridge_processor
.processing_stw_step ();
216 is_bridge_object_dead (GCObject
*obj
, void *data
)
218 SgenHashTable
*table
= (SgenHashTable
*)data
;
219 unsigned char *value
= (unsigned char *)sgen_hash_table_lookup (table
, obj
);
226 null_weak_links_to_dead_objects (SgenBridgeProcessor
*processor
, int generation
)
229 int num_sccs
= processor
->num_sccs
;
230 MonoGCBridgeSCC
**api_sccs
= processor
->api_sccs
;
231 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
);
233 for (i
= 0; i
< num_sccs
; ++i
) {
234 unsigned char alive
= api_sccs
[i
]->is_alive
? 1 : 0;
235 for (j
= 0; j
< api_sccs
[i
]->num_objs
; ++j
) {
236 /* Build hash table for nulling weak links. */
237 sgen_hash_table_replace (&alive_hash
, api_sccs
[i
]->objs
[j
], &alive
, NULL
);
239 /* Release for finalization those objects we no longer care. */
240 if (!api_sccs
[i
]->is_alive
)
241 sgen_mark_bridge_object (api_sccs
[i
]->objs
[j
]);
245 /* Null weak links to dead objects. */
246 sgen_null_links_if (is_bridge_object_dead
, &alive_hash
, GENERATION_NURSERY
, FALSE
);
247 sgen_null_links_if (is_bridge_object_dead
, &alive_hash
, GENERATION_NURSERY
, TRUE
);
248 if (generation
== GENERATION_OLD
) {
249 sgen_null_links_if (is_bridge_object_dead
, &alive_hash
, GENERATION_OLD
, FALSE
);
250 sgen_null_links_if (is_bridge_object_dead
, &alive_hash
, GENERATION_OLD
, TRUE
);
253 sgen_hash_table_clean (&alive_hash
);
257 free_callback_data (SgenBridgeProcessor
*processor
)
260 int num_sccs
= processor
->num_sccs
;
261 int num_xrefs
= processor
->num_xrefs
;
262 MonoGCBridgeSCC
**api_sccs
= processor
->api_sccs
;
263 MonoGCBridgeXRef
*api_xrefs
= processor
->api_xrefs
;
265 for (i
= 0; i
< num_sccs
; ++i
) {
266 sgen_free_internal_dynamic (api_sccs
[i
],
267 sizeof (MonoGCBridgeSCC
) + sizeof (MonoObject
*) * api_sccs
[i
]->num_objs
,
268 INTERNAL_MEM_BRIDGE_DATA
);
270 sgen_free_internal_dynamic (api_sccs
, sizeof (MonoGCBridgeSCC
*) * num_sccs
, INTERNAL_MEM_BRIDGE_DATA
);
272 sgen_free_internal_dynamic (api_xrefs
, sizeof (MonoGCBridgeXRef
) * num_xrefs
, INTERNAL_MEM_BRIDGE_DATA
);
274 processor
->num_sccs
= 0;
275 processor
->api_sccs
= NULL
;
276 processor
->num_xrefs
= 0;
277 processor
->api_xrefs
= NULL
;
281 compare_xrefs (const void *a_ptr
, const void *b_ptr
)
283 const MonoGCBridgeXRef
*a
= (const MonoGCBridgeXRef
*)a_ptr
;
284 const MonoGCBridgeXRef
*b
= (const MonoGCBridgeXRef
*)b_ptr
;
286 if (a
->src_scc_index
< b
->src_scc_index
)
288 if (a
->src_scc_index
> b
->src_scc_index
)
291 if (a
->dst_scc_index
< b
->dst_scc_index
)
293 if (a
->dst_scc_index
> b
->dst_scc_index
)
301 dump_processor_state (SgenBridgeProcessor *p)
306 printf ("SCCS %d\n", p->num_sccs);
307 for (i = 0; i < p->num_sccs; ++i) {
309 MonoGCBridgeSCC *scc = p->api_sccs [i];
310 printf ("\tSCC %d:", i);
311 for (j = 0; j < scc->num_objs; ++j) {
312 MonoObject *obj = scc->objs [j];
318 printf ("XREFS %d\n", p->num_xrefs);
319 for (i = 0; i < p->num_xrefs; ++i)
320 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
322 printf ("-------\n");
327 sgen_compare_bridge_processor_results (SgenBridgeProcessor
*a
, SgenBridgeProcessor
*b
)
330 SgenHashTable obj_to_a_scc
= SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG
, INTERNAL_MEM_BRIDGE_DEBUG
, sizeof (int), mono_aligned_addr_hash
, NULL
);
331 SgenHashTable b_scc_to_a_scc
= SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG
, INTERNAL_MEM_BRIDGE_DEBUG
, sizeof (int), g_direct_hash
, NULL
);
332 MonoGCBridgeXRef
*a_xrefs
, *b_xrefs
;
333 size_t xrefs_alloc_size
;
335 // dump_processor_state (a);
336 // dump_processor_state (b);
338 if (a
->num_sccs
!= b
->num_sccs
)
339 g_error ("SCCS count expected %d but got %d", a
->num_sccs
, b
->num_sccs
);
340 if (a
->num_xrefs
!= b
->num_xrefs
)
341 g_error ("SCCS count expected %d but got %d", a
->num_xrefs
, b
->num_xrefs
);
344 * First we build a hash of each object in `a` to its respective SCC index within
345 * `a`. Along the way we also assert that no object is more than one SCC.
347 for (i
= 0; i
< a
->num_sccs
; ++i
) {
349 MonoGCBridgeSCC
*scc
= a
->api_sccs
[i
];
351 g_assert (scc
->num_objs
> 0);
353 for (j
= 0; j
< scc
->num_objs
; ++j
) {
354 GCObject
*obj
= scc
->objs
[j
];
355 gboolean new_entry
= sgen_hash_table_replace (&obj_to_a_scc
, obj
, &i
, NULL
);
356 g_assert (new_entry
);
361 * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
362 * of `b` contain the same sets of objects as those of `a`.
364 * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
367 for (i
= 0; i
< b
->num_sccs
; ++i
) {
368 MonoGCBridgeSCC
*scc
= b
->api_sccs
[i
];
369 MonoGCBridgeSCC
*a_scc
;
370 int *a_scc_index_ptr
;
375 g_assert (scc
->num_objs
> 0);
376 a_scc_index_ptr
= (int *)sgen_hash_table_lookup (&obj_to_a_scc
, scc
->objs
[0]);
377 g_assert (a_scc_index_ptr
);
378 a_scc_index
= *a_scc_index_ptr
;
380 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
382 a_scc
= a
->api_sccs
[a_scc_index
];
383 g_assert (a_scc
->num_objs
== scc
->num_objs
);
385 for (j
= 1; j
< scc
->num_objs
; ++j
) {
386 a_scc_index_ptr
= (int *)sgen_hash_table_lookup (&obj_to_a_scc
, scc
->objs
[j
]);
387 g_assert (a_scc_index_ptr
);
388 g_assert (*a_scc_index_ptr
== a_scc_index
);
391 new_entry
= sgen_hash_table_replace (&b_scc_to_a_scc
, GINT_TO_POINTER (i
), &a_scc_index
, NULL
);
392 g_assert (new_entry
);
396 * Finally, check that we have the same xrefs. We do this by making copies of both
397 * xref arrays, and replacing the SCC indexes in the copy for `b` with the
398 * corresponding indexes in `a`. Then we sort both arrays and assert that they're
401 * At the same time, check that no xref is self-referential and that there are no
405 xrefs_alloc_size
= a
->num_xrefs
* sizeof (MonoGCBridgeXRef
);
406 a_xrefs
= (MonoGCBridgeXRef
*)sgen_alloc_internal_dynamic (xrefs_alloc_size
, INTERNAL_MEM_BRIDGE_DEBUG
, TRUE
);
407 b_xrefs
= (MonoGCBridgeXRef
*)sgen_alloc_internal_dynamic (xrefs_alloc_size
, INTERNAL_MEM_BRIDGE_DEBUG
, TRUE
);
409 memcpy (a_xrefs
, a
->api_xrefs
, xrefs_alloc_size
);
410 for (i
= 0; i
< b
->num_xrefs
; ++i
) {
411 MonoGCBridgeXRef
*xref
= &b
->api_xrefs
[i
];
414 g_assert (xref
->src_scc_index
!= xref
->dst_scc_index
);
416 scc_index_ptr
= (int *)sgen_hash_table_lookup (&b_scc_to_a_scc
, GINT_TO_POINTER (xref
->src_scc_index
));
417 g_assert (scc_index_ptr
);
418 b_xrefs
[i
].src_scc_index
= *scc_index_ptr
;
420 scc_index_ptr
= (int *)sgen_hash_table_lookup (&b_scc_to_a_scc
, GINT_TO_POINTER (xref
->dst_scc_index
));
421 g_assert (scc_index_ptr
);
422 b_xrefs
[i
].dst_scc_index
= *scc_index_ptr
;
425 qsort (a_xrefs
, a
->num_xrefs
, sizeof (MonoGCBridgeXRef
), compare_xrefs
);
426 qsort (b_xrefs
, a
->num_xrefs
, sizeof (MonoGCBridgeXRef
), compare_xrefs
);
428 for (i
= 0; i
< a
->num_xrefs
; ++i
) {
429 g_assert (a_xrefs
[i
].src_scc_index
== b_xrefs
[i
].src_scc_index
);
430 g_assert (a_xrefs
[i
].dst_scc_index
== b_xrefs
[i
].dst_scc_index
);
433 sgen_hash_table_clean (&obj_to_a_scc
);
434 sgen_hash_table_clean (&b_scc_to_a_scc
);
435 sgen_free_internal_dynamic (a_xrefs
, xrefs_alloc_size
, INTERNAL_MEM_BRIDGE_DEBUG
);
436 sgen_free_internal_dynamic (b_xrefs
, xrefs_alloc_size
, INTERNAL_MEM_BRIDGE_DEBUG
);
442 sgen_bridge_processing_finish (int generation
)
444 bridge_processor
.processing_build_callback_data (generation
);
445 if (compare_bridge_processors ())
446 compare_to_bridge_processor
.processing_build_callback_data (generation
);
448 if (bridge_processor
.num_sccs
== 0) {
449 g_assert (bridge_processor
.num_xrefs
== 0);
453 bridge_callbacks
.cross_references (bridge_processor
.num_sccs
, bridge_processor
.api_sccs
,
454 bridge_processor
.num_xrefs
, bridge_processor
.api_xrefs
);
456 if (compare_bridge_processors ())
457 sgen_compare_bridge_processor_results (&bridge_processor
, &compare_to_bridge_processor
);
459 null_weak_links_to_dead_objects (&bridge_processor
, generation
);
461 free_callback_data (&bridge_processor
);
462 if (compare_bridge_processors ())
463 free_callback_data (&compare_to_bridge_processor
);
466 bridge_processor
.processing_after_callback (generation
);
467 if (compare_bridge_processors ())
468 compare_to_bridge_processor
.processing_after_callback (generation
);
470 mono_trace (G_LOG_LEVEL_INFO
, MONO_TRACE_GC
, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f
);
472 bridge_processing_in_progress
= FALSE
;
475 MonoGCBridgeObjectKind
476 sgen_bridge_class_kind (MonoClass
*klass
)
478 return bridge_processor
.class_kind (klass
);
482 sgen_bridge_register_finalized_object (GCObject
*obj
)
484 bridge_processor
.register_finalized_object (obj
);
485 if (compare_bridge_processors ())
486 compare_to_bridge_processor
.register_finalized_object (obj
);
490 sgen_bridge_describe_pointer (GCObject
*obj
)
492 if (bridge_processor
.describe_pointer
)
493 bridge_processor
.describe_pointer (obj
);
497 set_dump_prefix (const char *prefix
)
499 if (bridge_processor_config
.dump_prefix
)
500 free (bridge_processor_config
.dump_prefix
);
501 bridge_processor_config
.dump_prefix
= strdup (prefix
);
504 /* Test support code */
505 static const char *bridge_class
;
507 static MonoGCBridgeObjectKind
508 bridge_test_bridge_class_kind (MonoClass
*klass
)
510 if (!strcmp (bridge_class
, klass
->name
))
511 return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS
;
512 return GC_BRIDGE_TRANSPARENT_CLASS
;
516 bridge_test_is_bridge_object (MonoObject
*object
)
522 bridge_test_cross_reference (int num_sccs
, MonoGCBridgeSCC
**sccs
, int num_xrefs
, MonoGCBridgeXRef
*xrefs
)
525 for (i
= 0; i
< num_sccs
; ++i
) {
527 // g_print ("--- SCC %d\n", i);
528 for (j
= 0; j
< sccs
[i
]->num_objs
; ++j
) {
529 // g_print (" %s\n", sgen_safe_name (sccs [i]->objs [j]));
530 if (i
& 1) /*retain half of the bridged objects */
531 sccs
[i
]->is_alive
= TRUE
;
534 for (i
= 0; i
< num_xrefs
; ++i
) {
535 g_assert (xrefs
[i
].src_scc_index
>= 0 && xrefs
[i
].src_scc_index
< num_sccs
);
536 g_assert (xrefs
[i
].dst_scc_index
>= 0 && xrefs
[i
].dst_scc_index
< num_sccs
);
537 // g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index);
541 static MonoClassField
*mono_bridge_test_field
;
551 test_scc (MonoGCBridgeSCC
*scc
, int i
)
553 int status
= BRIDGE_DEAD
;
554 mono_field_get_value (scc
->objs
[i
], mono_bridge_test_field
, &status
);
559 mark_scc (MonoGCBridgeSCC
*scc
, int value
)
562 for (i
= 0; i
< scc
->num_objs
; ++i
) {
563 if (!test_scc (scc
, i
)) {
565 mono_field_set_value (scc
->objs
[i
], mono_bridge_test_field
, &status
);
571 bridge_test_cross_reference2 (int num_sccs
, MonoGCBridgeSCC
**sccs
, int num_xrefs
, MonoGCBridgeXRef
*xrefs
)
576 if (!mono_bridge_test_field
) {
577 mono_bridge_test_field
= mono_class_get_field_from_name (mono_object_get_class (sccs
[0]->objs
[0]), "__test");
578 g_assert (mono_bridge_test_field
);
581 /*We mark all objects in a scc with live objects as reachable by scc*/
582 for (i
= 0; i
< num_sccs
; ++i
) {
584 gboolean live
= FALSE
;
585 for (j
= 0; j
< sccs
[i
]->num_objs
; ++j
) {
586 if (test_scc (sccs
[i
], j
)) {
593 for (j
= 0; j
< sccs
[i
]->num_objs
; ++j
) {
594 if (!test_scc (sccs
[i
], j
)) {
595 int status
= BRIDGE_SAME_SCC
;
596 mono_field_set_value (sccs
[i
]->objs
[j
], mono_bridge_test_field
, &status
);
601 /*Now we mark the transitive closure of reachable objects from the xrefs*/
605 /* Mark all objects that are brought to life due to xrefs*/
606 for (i
= 0; i
< num_xrefs
; ++i
) {
607 MonoGCBridgeXRef ref
= xrefs
[i
];
608 if (test_scc (sccs
[ref
.src_scc_index
], 0) && !test_scc (sccs
[ref
.dst_scc_index
], 0)) {
610 mark_scc (sccs
[ref
.dst_scc_index
], BRIDGE_XREF
);
615 /* keep everything in memory, all we want to do is test persistence */
616 for (i
= 0; i
< num_sccs
; ++i
)
617 sccs
[i
]->is_alive
= TRUE
;
620 /* This bridge keeps all peers with __test > 0 */
622 bridge_test_positive_status (int num_sccs
, MonoGCBridgeSCC
**sccs
, int num_xrefs
, MonoGCBridgeXRef
*xrefs
)
626 if (!mono_bridge_test_field
) {
627 mono_bridge_test_field
= mono_class_get_field_from_name (mono_object_get_class (sccs
[0]->objs
[0]), "__test");
628 g_assert (mono_bridge_test_field
);
631 /*We mark all objects in a scc with live objects as reachable by scc*/
632 for (i
= 0; i
< num_sccs
; ++i
) {
634 for (j
= 0; j
< sccs
[i
]->num_objs
; ++j
) {
635 if (test_scc (sccs
[i
], j
)) {
636 sccs
[i
]->is_alive
= TRUE
;
645 register_test_bridge_callbacks (const char *bridge_class_name
)
647 MonoGCBridgeCallbacks callbacks
;
648 callbacks
.bridge_version
= SGEN_BRIDGE_VERSION
;
649 callbacks
.bridge_class_kind
= bridge_test_bridge_class_kind
;
650 callbacks
.is_bridge_object
= bridge_test_is_bridge_object
;
652 switch (bridge_class_name
[0]) {
654 bridge_class
= bridge_class_name
+ 1;
655 callbacks
.cross_references
= bridge_test_cross_reference2
;
658 bridge_class
= bridge_class_name
+ 1;
659 callbacks
.cross_references
= bridge_test_positive_status
;
662 bridge_class
= bridge_class_name
;
663 callbacks
.cross_references
= bridge_test_cross_reference
;
665 mono_gc_register_bridge_callbacks (&callbacks
);
669 sgen_bridge_handle_gc_param (const char *opt
)
671 g_assert (!bridge_processor_started ());
673 if (!strcmp (opt
, "bridge-require-precise-merge")) {
674 bridge_processor_config
.scc_precise_merge
= TRUE
;
683 sgen_bridge_handle_gc_debug (const char *opt
)
685 g_assert (!bridge_processor_started ());
687 if (g_str_has_prefix (opt
, "bridge=")) {
688 opt
= strchr (opt
, '=') + 1;
689 register_test_bridge_callbacks (g_strdup (opt
));
690 } else if (!strcmp (opt
, "enable-bridge-accounting")) {
691 bridge_processor_config
.accounting
= TRUE
;
692 } else if (g_str_has_prefix (opt
, "bridge-dump=")) {
693 char *prefix
= strchr (opt
, '=') + 1;
694 set_dump_prefix(prefix
);
695 } else if (g_str_has_prefix (opt
, "bridge-compare-to=")) {
696 const char *name
= strchr (opt
, '=') + 1;
697 BridgeProcessorSelection selection
= bridge_processor_name (name
);
699 if (selection
!= BRIDGE_PROCESSOR_INVALID
) {
700 // Compare processor doesn't get config
701 init_bridge_processor (&compare_to_bridge_processor
, selection
);
703 g_warning ("Invalid bridge implementation to compare against - ignoring.");
712 sgen_bridge_print_gc_debug_usage (void)
714 fprintf (stderr
, " bridge=<class-name>\n");
715 fprintf (stderr
, " enable-bridge-accounting\n");
716 fprintf (stderr
, " bridge-dump=<filename-prefix>\n");
717 fprintf (stderr
, " bridge-compare-to=<implementation>\n");