Skip to content

Commit 703efd5

Browse files
filipnavaraBrzVladakoeplinger
authored
[release/9.0] Fix edge cases in Tarjan GC bridge (Android) (#114391)
* Fix dump_processor_state debug code to compile and work on Android (#112970) Fix typo in GC bridge comparison message (SCCS -> XREFS) * [mono] Add a few bridge tests (#113703) * [mono][sgen] Fix DUMP_GRAPH debug option build for tarjan bridge * [mono][sgen] Don't create ScanData* during debug dumping of SCCs It serves no purpose and it would later crash the runtime since we didn't patch the lockword back in place. * [mono][sgen] Fix some null deref crashes in DUMP_GRAPH debug option * [mono][tests] Add bridge tests These are ported from some of the bridge tests we had on mono/mono. In order to test them we compare between the output of the new and the tarjan bridge. * Fix an edge case in the Tarjan GC bridge that leads to losing xref information (#112825) * Fix an edge case in the Tarjan SCC that lead to losing xref information In the Tarjan SCC bridge processing there's a color graph used to find out connections between SCCs. There was a rare case which only manifested when a cycle in the object graph points to another cycle that points to a bridge object. We only recognized direct bridge pointers but not pointers to other non-bridge SCCs that in turn point to bridges and where we already calculated the xrefs. These xrefs were then lost. * Add test case to sgen-bridge-pathologies and add an assert to catch the original bug * Add review --------- Co-authored-by: Vlad Brezae <[email protected]> * [SGen/Tarjan] Handle edge case with node heaviness changing due to deduplication (#113044) * [SGen/Tarjan] Handle edge case with node heaviness changing due to deduplication Do early deduplication Fix Windows build Add test cases to sgen-bridge-pathologies * Move test code * Remove old code * Add extra check (no change to functionality) * Disable test on wasm --------- Co-authored-by: Vlad Brezae <[email protected]> Co-authored-by: Alexander Köplinger <[email protected]>
1 parent 4d32f86 commit 703efd5

File tree

6 files changed

+550
-37
lines changed

6 files changed

+550
-37
lines changed

src/mono/mono/metadata/sgen-bridge.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -316,24 +316,24 @@ dump_processor_state (SgenBridgeProcessor *p)
316316
{
317317
int i;
318318
319-
printf ("------\n");
320-
printf ("SCCS %d\n", p->num_sccs);
319+
g_message ("------\n");
320+
g_message ("SCCS %d\n", p->num_sccs);
321321
for (i = 0; i < p->num_sccs; ++i) {
322322
int j;
323323
MonoGCBridgeSCC *scc = p->api_sccs [i];
324-
printf ("\tSCC %d:", i);
324+
g_message ("\tSCC %d:", i);
325325
for (j = 0; j < scc->num_objs; ++j) {
326326
MonoObject *obj = scc->objs [j];
327-
printf (" %p(%s)", obj, SGEN_LOAD_VTABLE (obj)->klass->name);
327+
g_message (" %p(%s)", obj, m_class_get_name (SGEN_LOAD_VTABLE (obj)->klass));
328328
}
329-
printf ("\n");
329+
g_message ("\n");
330330
}
331331
332-
printf ("XREFS %d\n", p->num_xrefs);
332+
g_message ("XREFS %d\n", p->num_xrefs);
333333
for (i = 0; i < p->num_xrefs; ++i)
334-
printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
334+
g_message ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
335335
336-
printf ("-------\n");
336+
g_message ("-------\n");
337337
}
338338
*/
339339

@@ -352,7 +352,7 @@ sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcess
352352
if (a->num_sccs != b->num_sccs)
353353
g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
354354
if (a->num_xrefs != b->num_xrefs)
355-
g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
355+
g_error ("XREFS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
356356

357357
/*
358358
* First we build a hash of each object in `a` to its respective SCC index within

src/mono/mono/metadata/sgen-tarjan-bridge.c

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -400,16 +400,7 @@ static const char*
400400
safe_name_bridge (GCObject *obj)
401401
{
402402
GCVTable vt = SGEN_LOAD_VTABLE (obj);
403-
return vt->klass->name;
404-
}
405-
406-
static ScanData*
407-
find_or_create_data (GCObject *obj)
408-
{
409-
ScanData *entry = find_data (obj);
410-
if (!entry)
411-
entry = create_data (obj);
412-
return entry;
403+
return m_class_get_name (vt->klass);
413404
}
414405
#endif
415406

@@ -566,10 +557,15 @@ find_in_cache (int *insert_index)
566557

567558
// Populate other_colors for a give color (other_colors represent the xrefs for this color)
568559
static void
569-
add_other_colors (ColorData *color, DynPtrArray *other_colors)
560+
add_other_colors (ColorData *color, DynPtrArray *other_colors, gboolean check_visited)
570561
{
571562
for (int i = 0; i < dyn_array_ptr_size (other_colors); ++i) {
572563
ColorData *points_to = (ColorData *)dyn_array_ptr_get (other_colors, i);
564+
if (check_visited) {
565+
if (points_to->visited)
566+
continue;
567+
points_to->visited = TRUE;
568+
}
573569
dyn_array_ptr_add (&color->other_colors, points_to);
574570
// Inform targets
575571
points_to->incoming_colors = MIN (points_to->incoming_colors + 1, INCOMING_COLORS_MAX);
@@ -593,7 +589,7 @@ new_color (gboolean has_bridges)
593589
cd = alloc_color_data ();
594590
cd->api_index = -1;
595591

596-
add_other_colors (cd, &color_merge_array);
592+
add_other_colors (cd, &color_merge_array, FALSE);
597593

598594
/* if cacheSlot >= 0, it means we prepared a given slot to receive the new color */
599595
if (cacheSlot >= 0)
@@ -700,11 +696,11 @@ compute_low_index (ScanData *data, GCObject *obj)
700696
obj = bridge_object_forward (obj);
701697
other = find_data (obj);
702698

703-
#if DUMP_GRAPH
704-
printf ("\tcompute low %p ->%p (%s) %p (%d / %d, color %p)\n", data->obj, obj, safe_name_bridge (obj), other, other ? other->index : -2, other ? other->low_index : -2, other->color);
705-
#endif
706699
if (!other)
707700
return;
701+
#if DUMP_GRAPH
702+
printf ("\tcompute low %p ->%p (%s) %p (%d / %d, color %p)\n", data->obj, obj, safe_name_bridge (obj), other, other ? other->index : -2, other->low_index, other->color);
703+
#endif
708704

709705
g_assert (other->state != INITIAL);
710706

@@ -777,24 +773,32 @@ create_scc (ScanData *data)
777773
gboolean found = FALSE;
778774
gboolean found_bridge = FALSE;
779775
ColorData *color_data = NULL;
776+
gboolean can_reduce_color = TRUE;
780777

781778
for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
782779
ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i);
783780
found_bridge |= other->is_bridge;
781+
if (dyn_array_ptr_size (&other->xrefs) > 0) {
782+
// This scc will have more xrefs than the ones from the color_merge_array,
783+
// we will need to create a new color to store this information.
784+
can_reduce_color = FALSE;
785+
}
784786
if (found_bridge || other == data)
785787
break;
786788
}
787789

788790
if (found_bridge) {
789791
color_data = new_color (TRUE);
790792
++num_colors_with_bridges;
791-
} else {
793+
} else if (can_reduce_color) {
792794
color_data = reduce_color ();
795+
} else {
796+
color_data = new_color (FALSE);
793797
}
794798
#if DUMP_GRAPH
795799
printf ("|SCC %p rooted in %s (%p) has bridge %d\n", color_data, safe_name_bridge (data->obj), data->obj, found_bridge);
796800
printf ("\tloop stack: ");
797-
for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
801+
for (i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
798802
ScanData *other = dyn_array_ptr_get (&loop_stack, i);
799803
printf ("(%d/%d)", other->index, other->low_index);
800804
}
@@ -824,10 +828,19 @@ create_scc (ScanData *data)
824828
dyn_array_ptr_add (&color_data->bridges, other->obj);
825829
}
826830

827-
// Maybe we should make sure we are not adding duplicates here. It is not really a problem
828-
// since we will get rid of duplicates before submitting the SCCs to the client in gather_xrefs
829-
if (color_data)
830-
add_other_colors (color_data, &other->xrefs);
831+
if (dyn_array_ptr_size (&other->xrefs) > 0) {
832+
g_assert (color_data != NULL);
833+
g_assert (can_reduce_color == FALSE);
834+
// We need to eliminate duplicates early otherwise the heaviness property
835+
// can change in gather_xrefs and it breaks down the loop that reports the
836+
// xrefs to the client.
837+
//
838+
// We reuse the visited flag to mark the objects that are already part of
839+
// the color_data array. The array was created above with the new_color call
840+
// and xrefs were populated from color_merge_array, which is already
841+
// deduplicated and every entry is marked as visited.
842+
add_other_colors (color_data, &other->xrefs, TRUE);
843+
}
831844
dyn_array_ptr_uninit (&other->xrefs);
832845

833846
if (other == data) {
@@ -837,11 +850,22 @@ create_scc (ScanData *data)
837850
}
838851
g_assert (found);
839852

853+
// Clear the visited flag on nodes that were added with add_other_colors in the loop above
854+
if (!can_reduce_color) {
855+
for (i = dyn_array_ptr_size (&color_merge_array); i < dyn_array_ptr_size (&color_data->other_colors); i++) {
856+
ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_data->other_colors, i);
857+
g_assert (cd->visited);
858+
cd->visited = FALSE;
859+
}
860+
}
861+
840862
#if DUMP_GRAPH
841-
printf ("\tpoints-to-colors: ");
842-
for (int i = 0; i < dyn_array_ptr_size (&color_data->other_colors); i++)
843-
printf ("%p ", dyn_array_ptr_get (&color_data->other_colors, i));
844-
printf ("\n");
863+
if (color_data) {
864+
printf ("\tpoints-to-colors: ");
865+
for (i = 0; i < dyn_array_ptr_size (&color_data->other_colors); i++)
866+
printf ("%p ", dyn_array_ptr_get (&color_data->other_colors, i));
867+
printf ("\n");
868+
}
845869
#endif
846870
}
847871

@@ -966,8 +990,11 @@ dump_color_table (const char *why, gboolean do_index)
966990
printf (" bridges: ");
967991
for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
968992
GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
969-
ScanData *data = find_or_create_data (obj);
970-
printf ("%d ", data->index);
993+
ScanData *data = find_data (obj);
994+
if (!data)
995+
printf ("%p ", obj);
996+
else
997+
printf ("%p(%d) ", obj, data->index);
971998
}
972999
}
9731000
printf ("\n");
@@ -1027,7 +1054,7 @@ processing_stw_step (void)
10271054
#if defined (DUMP_GRAPH)
10281055
printf ("----summary----\n");
10291056
printf ("bridges:\n");
1030-
for (int i = 0; i < bridge_count; ++i) {
1057+
for (i = 0; i < bridge_count; ++i) {
10311058
ScanData *sd = find_data (dyn_array_ptr_get (&registered_bridges, i));
10321059
printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
10331060
}
@@ -1141,6 +1168,7 @@ processing_build_callback_data (int generation)
11411168
gather_xrefs (cd);
11421169
reset_xrefs (cd);
11431170
dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
1171+
g_assert (color_visible_to_client (cd));
11441172
xref_count += dyn_array_ptr_size (&cd->other_colors);
11451173
}
11461174
}

0 commit comments

Comments
 (0)