VM crash in the Squeak3D plugin

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

VM crash in the Squeak3D plugin

Stéphane Rollandin
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Nicolas Cellier
 
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Nicolas Cellier
 
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Nicolas Cellier
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
 
Hi Nicolas, Hi Stéphane,


On Dec 28, 2019, at 2:24 PM, Nicolas Cellier <[hidden email]> wrote:


I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

The intent of asserts is that they’re compiled out of the production (fully optimized) vm.  So the assert should stay.  If however, the plugin isn’t pulling in platforms/Cross/vm/sqAssert.h, it won’t be using the right definitions.  I’ll take a look soon.

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
In reply to this post by Stéphane Rollandin
 
Hi Stéphane,

> On Dec 28, 2019, at 10:20 AM, Stéphane Rollandin <[hidden email]> wrote:
>
> Hello all,
>
> For my first post on this list, I would like to submit the bug that prevents one of my game (this one: http://www.zogotounga.net/comp/guardians.htm) to work reliably.

Welcome!

> I spent quite some time on this, and I ended up with a nice and concise way to crash the Squeak3D plugin. The image is available at:
>
> http://www.zogotounga.net/swap/crashlab3.zip
>
> Instructions to crash the VM are detailed therein.
>
> The 3D scene is very simple, there are only three objects.

Thanks; this should help!

> The crash
> seems related to a race condition, because inserting a simple delay in
> the code prevents it - this is detailed in the image itself.

Interesting.  I shall see if it simulates.

> I have spent days studying the Smalltalk code, and could not see
> anything wrong with what is sent to the plugin. It looks to my
> uninformed eyes as a problem of shared memory, where one rasterizing
> operation messes up with another happening in parallel (although no
> processed is being forked on the Smalltalk side).

Right.  There is no parallelism here.  AIUI (which isn’t very well at all) the Squeak3D plugin is a pure output engine.  But I know very very little about rasterisers and the theory behind the code.  Those that do (Ronie? Craig?), please take a look.  This is one of Andreas’ great creations.  It would be lovely to have it working properly.

> Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
In reply to this post by Nicolas Cellier
 
Hi Nicolas, Hi Stéphane,

On Sat, Dec 28, 2019 at 2:24 PM Nicolas Cellier <[hidden email]> wrote:
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

I remember now.

The original code read

-                                      assert(rightEdge);

and this din't catch the crash.  So I added the #else arm:

+                                       if(!leftEdge || !rightEdge)
+                                               FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges

which caused the system to fail not being able to allocate edges.  So I tried an alternative:

+                                       if(!leftEdge || !rightEdge)
+                                               FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault

which caused segfaults.  So I added the stronger assert, and ifdefed out my two attempts at failing correctly in the #else arm.

+                                       assert(leftEdge && rightEdge);

So by all means strip the code back to the assert, but I think that the information that both of those attempts at remediation, FAIL_PAINTING(B3D_NO_MORE_EDGES); and FAIL_UPDATING(B3D_NO_MORE_EDGES);, failed and for different reasons might be useful to know.

The question is under what circumstances does the assert(leftEdge && rightEdge); fail?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Nicolas Cellier
 
Thanks to Stephane, we have a reproducible case and might understand why/how this happens...
Unfortunately, I have no idea what the algorithm does, it would deserve some more documentation.
Is this a classical algorithm that can be found in litterature?

Le dim. 29 déc. 2019 à 18:50, Eliot Miranda <[hidden email]> a écrit :
 
Hi Nicolas, Hi Stéphane,

On Sat, Dec 28, 2019 at 2:24 PM Nicolas Cellier <[hidden email]> wrote:
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

I remember now.

The original code read

-                                      assert(rightEdge);

and this din't catch the crash.  So I added the #else arm:

+                                       if(!leftEdge || !rightEdge)
+                                               FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges

which caused the system to fail not being able to allocate edges.  So I tried an alternative:

+                                       if(!leftEdge || !rightEdge)
+                                               FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault

which caused segfaults.  So I added the stronger assert, and ifdefed out my two attempts at failing correctly in the #else arm.

+                                       assert(leftEdge && rightEdge);

So by all means strip the code back to the assert, but I think that the information that both of those attempts at remediation, FAIL_PAINTING(B3D_NO_MORE_EDGES); and FAIL_UPDATING(B3D_NO_MORE_EDGES);, failed and for different reasons might be useful to know.

The question is under what circumstances does the assert(leftEdge && rightEdge); fail?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
In reply to this post by Nicolas Cellier
 
Hi Nicolas,

On Sat, Dec 28, 2019 at 2:24 PM Nicolas Cellier <[hidden email]> wrote:
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

So these lines (1390 to 1392 immediately above)

                        /* If the edge is not on top toggle its (back) fills */
                        b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                        rightEdge = NULL;

are the key lines.  What is the meaning of "If the edge is not on top toggle its (back) fills", and what is the continuation?  Clearly the continuation should not be to continue after the if because that continues into the code that indirects through the null pointer at line 1407:

                    if(fillList->firstFace) {
                        /* Note: We fill *including* leftX and rightX */
                        int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
>>                    int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
                        B3DPrimitiveFace *topFace = fillList->firstFace;

So, 3D graphics experts, what is going on here, and how should the algorithm continue when "the edge is not on top toggle its (back) fills"?


                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
In reply to this post by Nicolas Cellier
 
Hi Nicolas,

On Sun, Dec 29, 2019 at 10:01 AM Nicolas Cellier <[hidden email]> wrote:
 
Thanks to Stephane, we have a reproducible case and might understand why/how this happens...
Unfortunately, I have no idea what the algorithm does, it would deserve some more documentation.
Is this a classical algorithm that can be found in litterature?

I'm with you.  As I understand it this is a standard "break things into triangles and then render the scan lines along the edges of the triangles" 3D graphics algorithm, but at that point my "expertise" is revealed to be as vacuous as it is.  I think its straight-forward to fix for someone who understands the algorithm.  If "the edge is not on top toggle its (back) fills" the algorithm is nilling rightEdge (presumably to abort rendering that triangle?), and presumably this happens very rarely, rarely enough that Andreas never noticed he hadn't;'t programmed in the right continuation.  SO the question for people who can understand the algorithm is what the correct continuation is.

BTW, we're talking about the b3dMainLoop routine in platforms/Cross/plugins/Squeak3D/b3dMain.c


Another question is did Andreas ever prototype this in Smalltalk or did he write the C directly? (I suspect the latter).  On the off chance that he did prototype it, and one have any inkling where the Smalltalk code exists?
 

Le dim. 29 déc. 2019 à 18:50, Eliot Miranda <[hidden email]> a écrit :
 
Hi Nicolas, Hi Stéphane,

On Sat, Dec 28, 2019 at 2:24 PM Nicolas Cellier <[hidden email]> wrote:
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

I remember now.

The original code read

-                                      assert(rightEdge);

and this din't catch the crash.  So I added the #else arm:

+                                       if(!leftEdge || !rightEdge)
+                                               FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges

which caused the system to fail not being able to allocate edges.  So I tried an alternative:

+                                       if(!leftEdge || !rightEdge)
+                                               FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault

which caused segfaults.  So I added the stronger assert, and ifdefed out my two attempts at failing correctly in the #else arm.

+                                       assert(leftEdge && rightEdge);

So by all means strip the code back to the assert, but I think that the information that both of those attempts at remediation, FAIL_PAINTING(B3D_NO_MORE_EDGES); and FAIL_UPDATING(B3D_NO_MORE_EDGES);, failed and for different reasons might be useful to know.

The question is under what circumstances does the assert(leftEdge && rightEdge); fail?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef


--
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
In reply to this post by Eliot Miranda-2
 
Hi 

On Sun, Dec 29, 2019 at 10:03 AM Eliot Miranda <[hidden email]> wrote:
Hi Nicolas,

On Sat, Dec 28, 2019 at 2:24 PM Nicolas Cellier <[hidden email]> wrote:
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

So these lines (1390 to 1392 immediately above)

                        /* If the edge is not on top toggle its (back) fills */
                        b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                        rightEdge = NULL;

are the key lines.  What is the meaning of "If the edge is not on top toggle its (back) fills", and what is the continuation?  Clearly the continuation should not be to continue after the if because that continues into the code that indirects through the null pointer at line 1407:

                    if(fillList->firstFace) {
                        /* Note: We fill *including* leftX and rightX */
                        int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
>>                    int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
                        B3DPrimitiveFace *topFace = fillList->firstFace;

So, 3D graphics experts, what is going on here, and how should the algorithm continue when "the edge is not on top toggle its (back) fills"?



Changing my hacked in assert sequence to the following fixes the crash but I think causes the system to lock up (Stéphane using your WondwerlandPark game.  The old code crashes after 100 seconds of rotating the startup screen; this new code runs for about 5 minutes and then the VM appears to lock up)

#if 1
                    if (!rightEdge) {
                        b3dCleanupFill(fillList);
                        break;
                    }
#elif 1 /* This assert fails in rare cases; the fix is not understood. eem */
                    assert(leftEdge && rightEdge);
#else
                    if(!leftEdge || !rightEdge)
                        FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                        //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif


as it does if the continuation is simply
 
                    if (!rightEdge)
                        break;

So close, but no cigar...


                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef


--
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
 


On Sun, Dec 29, 2019 at 10:51 AM Eliot Miranda <[hidden email]> wrote:
Hi 

On Sun, Dec 29, 2019 at 10:03 AM Eliot Miranda <[hidden email]> wrote:
Hi Nicolas,

On Sat, Dec 28, 2019 at 2:24 PM Nicolas Cellier <[hidden email]> wrote:
 
I forgot to say that I had an assert failure report:

leftEdge && rightEdge 1398

Looking around line 1398:

                                        /*-- search for the next top edge which will be the right edge --*/
                                        assert(aetStart < aetSize);
                                        if(!fillList->firstFace)
                                                rightEdge = aetData[aetStart++]; /* If no current top fill just use the next edge */
                                        else while(aetStart < aetSize) { /* Search for the next top edge in the AET */
                                                rightEdge = aetData[aetStart];
                                                /* If we have an intersection use the intersection edge */
                                                if(nextIntersection->xValue <= rightEdge->xValue) {
                                                        rightEdge = nextIntersection;
                                                        break;
                                                }
                                                aetStart++;
                                                /* Check if this edge is on top */
                                                assert(fillList->firstFace);
                                                {
                                                        double xValue = rightEdge->xValue * B3D_FixedToFloat;
                                                        B3DPrimitiveFace *topFace = fillList->firstFace;
                                                        if( rightEdge->leftFace == topFace ||
                                                                rightEdge->rightFace == topFace ||
                                                                rightEdge->zValue < zValueAt(topFace, xValue, yValue))
                                                                break; /* rightEdge is on top */
                                                }
                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;
                                        }
                                        /*-- end of search for next top edge --*/

So these lines (1390 to 1392 immediately above)

                        /* If the edge is not on top toggle its (back) fills */
                        b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                        rightEdge = NULL;

are the key lines.  What is the meaning of "If the edge is not on top toggle its (back) fills", and what is the continuation?  Clearly the continuation should not be to continue after the if because that continues into the code that indirects through the null pointer at line 1407:

                    if(fillList->firstFace) {
                        /* Note: We fill *including* leftX and rightX */
                        int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
>>                    int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
                        B3DPrimitiveFace *topFace = fillList->firstFace;

So, 3D graphics experts, what is going on here, and how should the algorithm continue when "the edge is not on top toggle its (back) fills"?



Changing my hacked in assert sequence to the following fixes the crash but I think causes the system to lock up (Stéphane using your WondwerlandPark game.  The old code crashes after 100 seconds of rotating the startup screen; this new code runs for about 5 minutes and then the VM appears to lock up)

#if 1
                    if (!rightEdge) {
                        b3dCleanupFill(fillList);
                        break;
                    }
#elif 1 /* This assert fails in rare cases; the fix is not understood. eem */
                    assert(leftEdge && rightEdge);
#else
                    if(!leftEdge || !rightEdge)
                        FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                        //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif


as it does if the continuation is simply
 
                    if (!rightEdge)
                        break;

So close, but no cigar...

I think I have it.  An old case of Stéphane's is still running after 10 minutes without locking up.  The continuation is

                    if (!rightEdge) {
                        aet->size = 0;
                        b3dCleanupFill(fillList);
                        break;
                    }

The break causes exit from the loop at 1352.  And the set->size = 0 assignment then causes exit from the enclosing loop at 1227 (the BEGIN MAINLOOP loop).

I'm going to commit this.  Stéphane, sorry it has taken so long to get to this.  As you can see neither Nicolas nor I understand the code to any sufficient level.  We're going on very primitive understanding rather than familiarity with the algorithm.  Anyway, hopefully this fix will stand the test of time.
 


                                        /*-- Now do the drawing from leftEdge to rightEdge --*/
#if 1 /* This assert fails in rare cases; the fix is not understood. eem */
                                        assert(leftEdge && rightEdge);
#else
                                        if(!leftEdge || !rightEdge)
                                                FAIL_UPDATING(B3D_NO_MORE_EDGES); // another segfault
                                                //FAIL_PAINTING(B3D_NO_MORE_EDGES); // blow up in allocating edges
#endif
                                        if(fillList->firstFace) {
                                                /* Note: We fill *including* leftX and rightX */
                                                int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
                                                int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);

So I'm pretty sure that we went thru:

                                                /* If the edge is not on top toggle its (back) fills */
                                                b3dToggleBackFills(fillList, rightEdge, yValue, nextIntersection);
                                                rightEdge = NULL;

if intention of #if 1... assert(...) was probably to debug the case of failure...
But it was left in the release. Maybe it would be safer to replace with #ifdef DEBUG or something like that?
Eliot?

Le sam. 28 déc. 2019 à 23:04, Nicolas Cellier <[hidden email]> a écrit :
Running SqueakDebug.app in lldb, I get:

Process 61332 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
   1404 if(fillList->firstFace) {
   1405 /* Note: We fill *including* leftX and rightX */
   1406 int leftX = (leftEdge->xValue >> B3D_FixedToIntShift) + 1;
-> 1407 int rightX = (rightEdge->xValue >> B3D_FixedToIntShift);
   1408 B3DPrimitiveFace *topFace = fillList->firstFace;
   1409
   1410 if(leftX < 0) leftX = 0;
Target 0: (Squeak) stopped.
(lldb) print rightEdge
(B3DPrimitiveEdge *) $0 = 0x00000000
(lldb) print leftEdge
(B3DPrimitiveEdge *) $1 = 0x0617a49c
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x09f17c07 Squeak3D`b3dMainLoop(state=0x09f1928c, stopReason=0) at b3dMain.c:1407:32
    frame #1: 0x09f0cb23 Squeak3D`b3dStartRasterizer at Squeak3D.c:1704:12
    frame #2: 0x05d01398
    frame #3: 0x00002f52 Squeak`interpret at gcc3x-cointerp.c:2749:3

So why rightEdge is a null pointer?
If you already examined the code, you may have an idea...

Le sam. 28 déc. 2019 à 22:55, Nicolas Cellier <[hidden email]> a écrit :
Hi Stephane,
I confirm that I can reproduce the crash with an up-to-date VM (on OSX).

Le sam. 28 déc. 2019 à 19:20, Stéphane Rollandin <[hidden email]> a écrit :
 
Hello all,

For my first post on this list, I would like to submit the bug that
prevents one of my game (this one:
http://www.zogotounga.net/comp/guardians.htm) to work reliably.

I spent quite some time on this, and I ended up with a nice and concise
way to crash the Squeak3D plugin. The image is available at:

http://www.zogotounga.net/swap/crashlab3.zip

Instructions to crash the VM are detailed therein.

The 3D scene is very simple, there are only three objects. The crash
seems related to a race condition, because inserting a simple delay in
the code prevents it - this is detailed in the image itself.

I have spent days studying the Smalltalk code, and could not see
anything wrong with what is sent to the plugin. It looks to my
uninformed eyes as a problem of shared memory, where one rasterizing
operation messes up with another happening in parallel (although no
processed is being forked on the Smalltalk side).


Stef


--
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Stéphane Rollandin
 
> I'm going to commit this.  Stéphane, sorry it has taken so long to get
> to this.  As you can see neither Nicolas nor I understand the code to
> any sufficient level.  We're going on very primitive understanding
> rather than familiarity with the algorithm.  Anyway, hopefully this fix
> will stand the test of time.

Wow. You guys are amazing.

As soon as the fixed plugin is available, I will get back to my game and
finish it (I still have a lot of balancing to do there) - so of course
I'll keep you informed of how it goes.

Stef


Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Nicolas Cellier
 
Superficial fix, with just the right level of understanding, that's terribly efficient :)
kudos to Eliot!

BTW, Eliot, could you fix the double appveyor registration?
Or get the bintray keys from Fabio?
Appveyor appears to fail, but another instance succeed...

Le dim. 29 déc. 2019 à 20:26, Stéphane Rollandin <[hidden email]> a écrit :
 
> I'm going to commit this.  Stéphane, sorry it has taken so long to get
> to this.  As you can see neither Nicolas nor I understand the code to
> any sufficient level.  We're going on very primitive understanding
> rather than familiarity with the algorithm.  Anyway, hopefully this fix
> will stand the test of time.

Wow. You guys are amazing.

As soon as the fixed plugin is available, I will get back to my game and
finish it (I still have a lot of balancing to do there) - so of course
I'll keep you informed of how it goes.

Stef


Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
 


On Sun, Dec 29, 2019 at 1:20 PM Nicolas Cellier <[hidden email]> wrote:
 
Superficial fix, with just the right level of understanding, that's terribly efficient :)
kudos to Eliot!

BTW, Eliot, could you fix the double appveyor registration?
Or get the bintray keys from Fabio?
Appveyor appears to fail, but another instance succeed...

Ugh, I'll try. This is all Klingon to me :-)
 


Le dim. 29 déc. 2019 à 20:26, Stéphane Rollandin <[hidden email]> a écrit :
 
> I'm going to commit this.  Stéphane, sorry it has taken so long to get
> to this.  As you can see neither Nicolas nor I understand the code to
> any sufficient level.  We're going on very primitive understanding
> rather than familiarity with the algorithm.  Anyway, hopefully this fix
> will stand the test of time.

Wow. You guys are amazing.

As soon as the fixed plugin is available, I will get back to my game and
finish it (I still have a lot of balancing to do there) - so of course
I'll keep you informed of how it goes.

Stef




--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Jakob Reschke
 
Am So., 29. Dez. 2019 um 22:56 Uhr schrieb Eliot Miranda <[hidden email]>:
 
Ugh, I'll try. This is all Klingon to me :-)

Qapla'!


Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Stéphane Rollandin
In reply to this post by Eliot Miranda-2
 
> I'm going to commit this.  Stéphane, sorry it has taken so long to get
> to this.  As you can see neither Nicolas nor I understand the code to
> any sufficient level.  We're going on very primitive understanding
> rather than familiarity with the algorithm.  Anyway, hopefully this fix
> will stand the test of time.

I have now played a fair bit with my game and I have not had any crash
so far.

But, instead, there are rendering glitches at times. Of course I suppose
that these glitches occurs whenever the plugin would have crashed
previously.

It's no big deal, but it shows that indeed, as you stated, some
continuation is needed when "the edge is not on top" to "toggle its
(back) fills", whatever that means.

Best,

Stef
Reply | Threaded
Open this post in threaded view
|

Re: VM crash in the Squeak3D plugin

Eliot Miranda-2
 


On Wed, Jan 1, 2020 at 8:41 AM Stéphane Rollandin <[hidden email]> wrote:
 
> I'm going to commit this.  Stéphane, sorry it has taken so long to get
> to this.  As you can see neither Nicolas nor I understand the code to
> any sufficient level.  We're going on very primitive understanding
> rather than familiarity with the algorithm.  Anyway, hopefully this fix
> will stand the test of time.

I have now played a fair bit with my game and I have not had any crash
so far.

But, instead, there are rendering glitches at times. Of course I suppose
that these glitches occurs whenever the plugin would have crashed
previously.

It's no big deal, but it shows that indeed, as you stated, some
continuation is needed when "the edge is not on top" to "toggle its
(back) fills", whatever that means.


Happy New Year!
_,,,^..^,,,_
best, Eliot