Skip to content

Commit 38ce4ee

Browse files
committed
Add RECYCLE/VERBOSE to Debug build
This is an experimental basic way of getting some information about the nodes which are being garbage collected. Using a manually allocated array from before the recycle is run, it gathers the nodes expected to be freed. The nodes are then PROBE'd one at a time, and then the actual recycling process is performed. The code for filling the "sweeplist" array is separated from the actual sweeping code, to avoid slowing it down with debug tests. (Includes a minor fix to VARARGS! on expired frames, an added test in ASSERT_CONTEXT(), and removal of the recursion-causing PROBE() when panic'ing on a series)
1 parent 2e13afe commit 38ce4ee

File tree

6 files changed

+128
-11
lines changed

6 files changed

+128
-11
lines changed

src/core/b-init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1602,7 +1602,7 @@ void Shutdown_Core(void)
16021602
&= (~NODE_FLAG_ROOT);
16031603
AS_SERIES(CTX_VARLIST(TG_Task_Context))->header.bits
16041604
&= (~NODE_FLAG_ROOT);
1605-
Recycle_Core(TRUE);
1605+
Recycle_Core(TRUE, NULL);
16061606

16071607
Shutdown_Ports();
16081608
Shutdown_Event_Scheme();

src/core/c-context.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,17 @@ void Assert_Context_Core(REBCTX *c)
13801380
//
13811381
if (!IS_FRAME(rootvar))
13821382
panic (rootvar);
1383+
1384+
REBFRM *f = CTX_FRAME_IF_ON_STACK(c);
1385+
if (f != NULL) {
1386+
REBFUN *rootkey_fun = VAL_FUNC(rootkey);
1387+
REBFUN *frame_fun = f->underlying; // should match
1388+
1389+
if (rootkey_fun != frame_fun) {
1390+
printf("FRAME! context function doesn't match its REBFRM");
1391+
panic (frame_fun);
1392+
}
1393+
}
13831394
}
13841395
else if (IS_BLANK(rootkey)) {
13851396
//

src/core/d-crash.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,15 @@ ATTRIBUTE_NO_RETURN void Panic_Core(
199199
case GUESSED_AS_SERIES: {
200200
REBSER *s = m_cast(REBSER*, cast(const REBSER*, p)); // don't mutate
201201
#if !defined(NDEBUG)
202-
PROBE(s);
202+
#if 0
203+
//
204+
// It can sometimes be useful to probe here if the series is
205+
// valid, but if it's not valid then that could result in a
206+
// recursive call to panic and a stack overflow.
207+
//
208+
PROBE(s);
209+
#endif
210+
203211
Panic_Series_Debug(cast(REBSER*, s));
204212
#else
205213
strncat(buf, "valid series", PANIC_BUF_SIZE - strlen(buf));

src/core/m-gc.c

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,12 +1299,69 @@ static REBCNT Sweep_Series(void)
12991299
}
13001300

13011301

1302+
#if !defined(NDEBUG)
1303+
1304+
//
1305+
// Fill_Sweeplist: C
1306+
//
1307+
REBCNT Fill_Sweeplist(REBSER *sweeplist)
1308+
{
1309+
assert(SER_WIDE(sweeplist) == sizeof(REBNOD*));
1310+
assert(SER_LEN(sweeplist) == 0);
1311+
1312+
REBCNT count = 0;
1313+
1314+
REBSEG *seg;
1315+
for (seg = Mem_Pools[SER_POOL].segs; seg != NULL; seg = seg->next) {
1316+
REBSER *s = cast(REBSER*, seg + 1);
1317+
REBCNT n;
1318+
for (n = Mem_Pools[SER_POOL].units; n > 0; --n, ++s) {
1319+
switch (LEFT_N_BITS(s->header.bits, 4)) {
1320+
case 9: // 0x8 + 0x1
1321+
assert(IS_SERIES_MANAGED(s));
1322+
if (Is_Rebser_Marked(s))
1323+
Unmark_Rebser(s);
1324+
else {
1325+
EXPAND_SERIES_TAIL(sweeplist, 1);
1326+
*SER_AT(REBNOD*, sweeplist, count) = cast(REBNOD*, s);
1327+
++count;
1328+
}
1329+
break;
1330+
1331+
case 11: // 0x8 + 0x2 + 0x1
1332+
//
1333+
// It's a cell which is managed where the key is not an END.
1334+
// This is a managed pairing, so mark bit should be heeded.
1335+
//
1336+
// !!! It is a REBNOD, but *not* a "series".
1337+
//
1338+
assert(IS_SERIES_MANAGED(s));
1339+
if (Is_Rebser_Marked(s))
1340+
Unmark_Rebser(s);
1341+
else {
1342+
EXPAND_SERIES_TAIL(sweeplist, 1);
1343+
*SER_AT(REBNOD*, sweeplist, count) = cast(REBNOD*, s);
1344+
++count;
1345+
}
1346+
break;
1347+
}
1348+
}
1349+
}
1350+
1351+
return count;
1352+
}
1353+
1354+
#endif
1355+
1356+
13021357
//
13031358
// Recycle_Core: C
13041359
//
1305-
// Recycle memory no longer needed.
1360+
// Recycle memory no longer needed. If sweeplist is not NULL, then it needs
1361+
// to be a series whose width is sizeof(REBSER*), and it will be filled with
1362+
// the list of series that *would* be recycled.
13061363
//
1307-
REBCNT Recycle_Core(REBOOL shutdown)
1364+
REBCNT Recycle_Core(REBOOL shutdown, REBSER *sweeplist)
13081365
{
13091366
// Ordinarily, it should not be possible to spawn a recycle during a
13101367
// recycle. But when debug code is added into the recycling code, it
@@ -1384,12 +1441,27 @@ REBCNT Recycle_Core(REBOOL shutdown)
13841441

13851442
REBCNT count = 0;
13861443

1387-
count += Sweep_Series();
1388-
count += Sweep_Gobs();
1444+
if (sweeplist != NULL) {
1445+
#if defined(NDEBUG)
1446+
panic (sweeplist);
1447+
#else
1448+
count += Fill_Sweeplist(sweeplist);
1449+
#endif
1450+
}
1451+
else
1452+
count += Sweep_Series();
1453+
1454+
// !!! The intent is for GOB! to be unified in the REBNOD pattern, the
1455+
// way that the FFI structures were. So they are not included in the
1456+
// count, in order to help make the numbers returned consistent between
1457+
// when the sweeplist is used and not.
1458+
//
1459+
Sweep_Gobs();
13891460

13901461
#if !defined(NDEBUG)
13911462
// Compute new stats:
1392-
PG_Reb_Stats->Recycle_Series = Mem_Pools[SER_POOL].free - PG_Reb_Stats->Recycle_Series;
1463+
PG_Reb_Stats->Recycle_Series
1464+
= Mem_Pools[SER_POOL].free - PG_Reb_Stats->Recycle_Series;
13931465
PG_Reb_Stats->Recycle_Series_Total += PG_Reb_Stats->Recycle_Series;
13941466
PG_Reb_Stats->Recycle_Prior_Eval = Eval_Cycles;
13951467
#endif
@@ -1448,7 +1520,7 @@ REBCNT Recycle(void)
14481520
{
14491521
// Default to not passing the `shutdown` flag.
14501522
//
1451-
REBCNT n = Recycle_Core(FALSE);
1523+
REBCNT n = Recycle_Core(FALSE, NULL);
14521524

14531525
#ifdef DOUBLE_RECYCLE_TEST
14541526
//
@@ -1457,7 +1529,7 @@ REBCNT Recycle(void)
14571529
// shouldn't crash.) This is an expensive check, but helpful to try if
14581530
// it seems a GC left things in a bad state that crashed a later GC.
14591531
//
1460-
REBCNT n2 = Recycle_Core(FALSE);
1532+
REBCNT n2 = Recycle_Core(FALSE, NULL);
14611533
assert(n2 == 0);
14621534
#endif
14631535

src/core/n-system.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ REBNATIVE(exit_rebol)
124124
// size [integer!]
125125
// /torture
126126
// "Constant recycle (for internal debugging)"
127+
// /verbose
128+
// "Dump out information about series being recycled"
127129
// ]
128130
//
129131
REBNATIVE(recycle)
@@ -153,7 +155,32 @@ REBNATIVE(recycle)
153155
if (GC_Disabled)
154156
return R_VOID; // don't give back misleading "0", since no recycle ran
155157

156-
REBCNT count = Recycle();
158+
REBCNT count;
159+
160+
if (REF(verbose)) {
161+
#if defined(NDEBUG)
162+
fail (Error(RE_DEBUG_ONLY));
163+
#else
164+
REBSER *sweeplist = Make_Series(100, sizeof(REBNOD*), MKS_NONE);
165+
count = Recycle_Core(FALSE, sweeplist);
166+
assert(count == SER_LEN(sweeplist));
167+
168+
REBCNT index = 0;
169+
for (index = 0; index < count; ++index) {
170+
REBNOD *node = *SER_AT(REBNOD*, sweeplist, index);
171+
PROBE(node);
172+
}
173+
174+
Free_Series(sweeplist);
175+
176+
REBCNT recount = Recycle_Core(FALSE, NULL);
177+
assert(recount == count);
178+
#endif
179+
}
180+
else {
181+
count = Recycle();
182+
}
183+
157184
SET_INTEGER(D_OUT, count);
158185
return R_OUT;
159186
}

src/core/t-varargs.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,6 @@ void Mold_Varargs(const REBVAL *v, REB_MOLD *mold) {
557557
REBFRM *f = CTX_FRAME_IF_ON_STACK(context);
558558

559559
if (f == NULL) {
560-
assert(GET_SER_FLAG(CTX_VARLIST(context), CONTEXT_FLAG_STACK));
561560
Append_Unencoded(mold->series, "**unavailable: call ended **");
562561
}
563562
else {

0 commit comments

Comments
 (0)