Re: [MLton] Crash in GC

Matthew Fluet <[email protected]> Sun, 6 Nov 2022 15:49:31 -0500
Newsgroups gmane.comp.lang.ml.mlton.devel
Message-ID <CAMrhFL5AR-b1iTjDM3CPYzfzY+u3zPu1oGbRx2aBgvfo+bH9Vw@mail.gmail.com>
--===============8416496296062392358==
Content-Type: multipart/alternative; boundary="0000000000004aa05605ecd37222"

--0000000000004aa05605ecd37222
Content-Type: text/plain; charset="UTF-8"

On Sun, Nov 6, 2022 at 2:04 PM Chris Cannam <[email protected]>
wrote:

>
> On Sun, 6 Nov 2022, at 15:52, Matthew Fluet wrote:
> > If the address of the bad intergenerational pointer is consistent
> > between runs
>
> It is.
>
> > set a hardware watchpoint on the address
> > that will be written with the pointer into the nursery and then look at
> > the C/assembly that immediately follows the write; that should mark a
> > corresponding card.
>
> According to gdb, and using the C-codegen output, the last two writes to
> that address before the failing GC are:
>
>  * initialisation to 1 from GC_sequenceAllocate:87, then
>

Makes sense.  GC_sequenceAllocate is allocating an array and needs to
initialize all pointer fields with a pointer-sized non-pointer (1);
this is so that a GC that occurs before the actual initialization of the
array elements won't see uninitialized pointers and try to follow them.



>  * updating to another value (3436048) in line 33191 of this code:
>
>
Just before this, there must be a
  T(Q, n) = CPointer_add (Frontier, (Word64)(0x8ull));
  T(P, 1) = (Objptr)T(Q, n);


>  33184          Frontier = CPointer_add (Frontier, (Word64)(0x28ull));
>  33185          O(Word64, T(P, 1), 0) = S(Word64, 128);
>  33186          O(Real64, T(P, 1), 8) = S(Real64, 136);
>  33187          O(Real64, T(P, 1), 16) = S(Real64, 144);
>  33188          O(Word32, T(P, 1), 24) = T(W32, 0);
>

This has allocated and initialized a new object.


>  33189          T(W64, 0) = WordU64_rshift ((Word64)S(Objptr, 112),
> (Word32)(0x8ull));
>  33190          X(Word8, O(CPointer, GCState, 936), T(W64, 0), 1, 0) =
> (Word8)(0x1ull);
>

This is the card marking.  GCState+936 corresponds to the base of the
`cardMapAbsolute` field within the `struct GC_generationalMaps` within the
`struct GC_state`.
This looks correct.  The `S(Objptr, 112)` is the stack variable holding the
base address of the object about to be updated.  The `rshift` performs the
division by 256 and the `X(...)` computes the array element of the card map
to be updated to 0x1ull (true).


>  33191          X(Objptr, S(Objptr, 112), S(Word64, 88), 8, 0) = T(P, 1);
>

This is the actual write that creates the intergenerational pointer.


>  33192          T(W64, 1) = Word64_add (S(Word64, 88), (Word64)(0x1ull));
>  33193          S(Word64, 88) = T(W64, 1);
>  33194          goto loop_277;
>  33195
>  33196  loop_277:
>  33197          T(W32, 0) = WordS64_lt (S(Word64, 88), S(Word64, 104));
>  33198          if (T(W32, 0)) goto L_9521; else goto L_9522;
>  33199
>  33200  L_9522:
>  33201          O(Word64, S(Objptr, 112), -8) = (Word64)(0x1Dull);
>  33202          T(W64, 0) = WordU64_rshift ((Word64)S(Objptr, 0),
> (Word32)(0x8ull));
>  33203          X(Word8, O(CPointer, GCState, 936), T(W64, 0), 1, 0) =
> (Word8)(0x1ull);
>  33204          X(Objptr, S(Objptr, 0), S(Word64, 40), 8, 0) =
> (Objptr)S(Objptr, 112);
>

This is another card mark and array update.


>  33205          T(W64, 1) = Word64_add (S(Word64, 40), (Word64)(0x1ull));
>  33206          S(Word64, 40) = T(W64, 1);
>  33207          goto loop_278;
>
>

> Is it possible to get any more annotation in the C output, about which bit
> of the source something in the output might correspond to?
>

Compiling with `-native-commented 2` will insert a bunch of comments in the
generated C file with the corresponding Machine IR.

It occurs to me that the comment in
`assertIsObjptrInFromSpaceOrImmutableMutableOrRootStaticHeap` about false
positives with stacks also applies to arrays.  The card of the address of
the beginning of the array is marked, but, if the array is larger than 256
bytes, then an intergenerational pointer can occur at an address that
corresponds to a card after the card of the beginning of the array.

I looked briefly at the code that walks the card map to find
intergenerational roots (
https://github.com/MLton/mlton/blob/master/runtime/gc/forward.c#L191) and
don't see anything obviously amiss.  But, it must be something rather
subtle, because this hasn't been a pervasive problem with the generational
collector.

-Matthew

--0000000000004aa05605ecd37222
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div dir=3D"ltr"><div class=3D"gmail_default" style=3D"fon=
t-family:arial,sans-serif;font-size:large">On Sun, Nov 6, 2022 at 2:04 PM C=
hris Cannam &lt;<a href=3D"mailto:[email protected]">cannam@all-=
day-breakfast.com</a>&gt; wrote:<br></div></div><div class=3D"gmail_quote">=
<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-=
left:1px solid rgb(204,204,204);padding-left:1ex"><br>
On Sun, 6 Nov 2022, at 15:52, Matthew Fluet wrote:<br>
&gt; If the address of the bad intergenerational pointer is consistent <br>
&gt; between runs<br>
<br>
It is.<br>
<br>
&gt; set a hardware watchpoint on the address <br>
&gt; that will be written with the pointer into the nursery and then look a=
t <br>
&gt; the C/assembly that immediately follows the write; that should mark a =
<br>
&gt; corresponding card.<br>
<br>
According to gdb, and using the C-codegen output, the last two writes to th=
at address before the failing GC are:<br>
<br>
=C2=A0* initialisation to 1 from GC_sequenceAllocate:87, then<br></blockquo=
te><div><br></div><div><div style=3D"font-family:arial,sans-serif;font-size=
:large" class=3D"gmail_default">Makes sense.=C2=A0 GC_sequenceAllocate is a=
llocating an array and needs to initialize all pointer fields with a pointe=
r-sized non-pointer (1);</div><div style=3D"font-family:arial,sans-serif;fo=
nt-size:large" class=3D"gmail_default">this is so that a GC that occurs bef=
ore the actual initialization of the array elements won&#39;t see uninitial=
ized pointers and try to follow them.</div><br></div><div>=C2=A0</div><bloc=
kquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:=
1px solid rgb(204,204,204);padding-left:1ex">
=C2=A0* updating to another value (3436048) in line 33191 of this code:<br>
<br></blockquote><div><br></div><div><div style=3D"font-family:arial,sans-s=
erif;font-size:large" class=3D"gmail_default">Just before this, there must =
be a</div><div style=3D"font-family:arial,sans-serif;font-size:large" class=
=3D"gmail_default">=C2=A0 T(Q, n) =3D CPointer_add (Frontier, (Word64)(0x8u=
ll));</div><div style=3D"font-family:arial,sans-serif;font-size:large" clas=
s=3D"gmail_default">=C2=A0 T(P, 1) =3D (Objptr)T(Q, n);<br></div></div><div=
>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px =
0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
=C2=A033184=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Frontier =3D CPointer_add (Fr=
ontier, (Word64)(0x28ull));<br>
=C2=A033185=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 O(Word64, T(P, 1), 0) =3D S(W=
ord64, 128);<br>
=C2=A033186=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 O(Real64, T(P, 1), 8) =3D S(R=
eal64, 136);<br>
=C2=A033187=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 O(Real64, T(P, 1), 16) =3D S(=
Real64, 144);<br>
=C2=A033188=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 O(Word32, T(P, 1), 24) =3D T(=
W32, 0);<br></blockquote><div><br></div><div><div style=3D"font-family:aria=
l,sans-serif;font-size:large" class=3D"gmail_default">This has allocated an=
d initialized a new object.<br></div></div><div>=C2=A0</div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid =
rgb(204,204,204);padding-left:1ex">
=C2=A033189=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 T(W64, 0) =3D WordU64_rshift =
((Word64)S(Objptr, 112), (Word32)(0x8ull));<br>
=C2=A033190=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 X(Word8, O(CPointer, GCState,=
 936), T(W64, 0), 1, 0) =3D (Word8)(0x1ull);<br></blockquote><div><br></div=
><div><div style=3D"font-family:arial,sans-serif;font-size:large" class=3D"=
gmail_default">This is the card marking.=C2=A0 GCState+936 corresponds to t=
he base of the `cardMapAbsolute` field within the `struct GC_generationalMa=
ps` within the `struct GC_state`.</div><div style=3D"font-family:arial,sans=
-serif;font-size:large" class=3D"gmail_default">This looks correct.=C2=A0 T=
he `S(Objptr, 112)` is the stack variable holding the base address of the o=
bject about to be updated.=C2=A0 The `rshift` performs the division by 256 =
and the `X(...)` computes the array element of the card map to be updated t=
o 0x1ull (true).<br></div></div><div>=C2=A0</div><blockquote class=3D"gmail=
_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204=
,204);padding-left:1ex">
=C2=A033191=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 X(Objptr, S(Objptr, 112), S(W=
ord64, 88), 8, 0) =3D T(P, 1);<br></blockquote><div><br></div><div><div sty=
le=3D"font-family:arial,sans-serif;font-size:large" class=3D"gmail_default"=
>This is the actual write that creates the intergenerational pointer.</div>=
</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0p=
x 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
=C2=A033192=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 T(W64, 1) =3D Word64_add (S(W=
ord64, 88), (Word64)(0x1ull));<br>
=C2=A033193=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 S(Word64, 88) =3D T(W64, 1);<=
br>
=C2=A033194=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto loop_277;<br>
=C2=A033195=C2=A0 <br>
=C2=A033196=C2=A0 loop_277:<br>
=C2=A033197=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 T(W32, 0) =3D WordS64_lt (S(W=
ord64, 88), S(Word64, 104));<br>
=C2=A033198=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (T(W32, 0)) goto L_9521; e=
lse goto L_9522;<br>
=C2=A033199=C2=A0 <br>
=C2=A033200=C2=A0 L_9522:<br>
=C2=A033201=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 O(Word64, S(Objptr, 112), -8)=
 =3D (Word64)(0x1Dull);<br>
=C2=A033202=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 T(W64, 0) =3D WordU64_rshift =
((Word64)S(Objptr, 0), (Word32)(0x8ull));<br>
=C2=A033203=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 X(Word8, O(CPointer, GCState,=
 936), T(W64, 0), 1, 0) =3D (Word8)(0x1ull);<br>
=C2=A033204=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 X(Objptr, S(Objptr, 0), S(Wor=
d64, 40), 8, 0) =3D (Objptr)S(Objptr, 112);<br></blockquote><div><br></div>=
<div><div style=3D"font-family:arial,sans-serif;font-size:large" class=3D"g=
mail_default">This is another card mark and array update.</div></div><div>=
=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0=
.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
=C2=A033205=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 T(W64, 1) =3D Word64_add (S(W=
ord64, 40), (Word64)(0x1ull));<br>
=C2=A033206=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 S(Word64, 40) =3D T(W64, 1);<=
br>
=C2=A033207=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto loop_278;<br>
<br></blockquote><div><span class=3D"gmail_default" style=3D"font-family:ar=
ial,sans-serif;font-size:large"></span>=C2=A0</div><blockquote class=3D"gma=
il_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,2=
04,204);padding-left:1ex">
Is it possible to get any more annotation in the C output, about which bit =
of the source something in the output might correspond to?<br></blockquote>=
<div><br></div><div style=3D"font-family:arial,sans-serif;font-size:large" =
class=3D"gmail_default">Compiling with `-native-commented 2` will insert a =
bunch of comments in the generated C file with the corresponding Machine IR=
.</div><div><br></div><div><div style=3D"font-family:arial,sans-serif;font-=
size:large" class=3D"gmail_default">It occurs to me that the comment in `as=
sertIsObjptrInFromSpaceOrImmutableMutableOrRootStaticHeap` about false posi=
tives with stacks also applies to arrays.=C2=A0 The card of the address of =
the beginning of the array is marked, but, if the array is larger than 256 =
bytes, then an intergenerational pointer can occur at an address that corre=
sponds to a card after the card of the beginning of the array.<br></div></d=
iv></div><div class=3D"gmail_quote"><br></div><div class=3D"gmail_quote"><d=
iv style=3D"font-family:arial,sans-serif;font-size:large" class=3D"gmail_de=
fault">I looked briefly at the code that walks the card map to find interge=
nerational roots (<a href=3D"https://github.com/MLton/mlton/blob/master/run=
time/gc/forward.c#L191">https://github.com/MLton/mlton/blob/master/runtime/=
gc/forward.c#L191</a>) and don&#39;t see anything obviously amiss.=C2=A0 Bu=
t, it must be something rather subtle, because this hasn&#39;t been a perva=
sive problem with the generational collector.</div><div style=3D"font-famil=
y:arial,sans-serif;font-size:large" class=3D"gmail_default"><br></div><div =
style=3D"font-family:arial,sans-serif;font-size:large" class=3D"gmail_defau=
lt">-Matthew</div><br></div></div>

--0000000000004aa05605ecd37222--


--===============8416496296062392358==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--===============8416496296062392358==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
MLton-devel mailing list
[email protected]; [email protected]
https://lists.sourceforge.net/lists/listinfo/mlton-devel

--===============8416496296062392358==--