November 13, 2014
https://issues.dlang.org/show_bug.cgi?id=13726

          Issue ID: 13726
           Summary: Build Phobos and Druntime with stack frames enabled
                    (-gs)
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: Phobos
          Assignee: nobody@puremagic.com
          Reporter: thecybershadow@gmail.com

Background: enabling stack frames causes the compiler to emit a few extra prologue/epilogue instructions for each function which save the stack pointer right near the function return address. This creates a linked list, which, when traversed, provides information for each function's address in the call stack (and where within the function it invokes the next function), and the size of its stack frame.

Stack frames are a good debugging aid and have a minimal impact on performance. I suggest to build Phobos and Druntime with these enabled.

An example use case where stack frames are useful is debugging InvalidMemoryOperation errors. These errors are currently difficult to debug. Consider the following program:

//////////// test.d ////////////
import core.memory;

class C
{
    ~this() { new ubyte[1024]; }
}

void faulty()
{
    foreach (n; 0..1000)
        new C();
    GC.collect();
}

void main()
{
    faulty();
}
////////////////////////////////

This program allocates in a destructor, which is not allowed. When ran, it only prints core.exception.InvalidMemoryOperationError@(0), because a stack trace is purposefully not allocated for memory operations (and even if it was, it would still be incorrect, as seen below).

When the program is launched from a debugger, on Windows, the runtime will disable its own exception handler, and the program will break on the point where the InvalidMemoryOperationError is thrown. However, the stack trace will look as follows:

>	KernelBase.dll!_RaiseException@16()  + 0x58 bytes
     test.exe!_D2rt3deh9throwImplFC6ObjectZv()  + 0x1a bytes
     test.exe!_onInvalidMemoryOperationError()  + 0xc bytes
     test.exe!_gc_malloc()  + 0x1e bytes
     test.exe!_D2gc3gcx3Gcx11fullcollectMFZk()  + 0x617 bytes
     test.exe!_D2gc3gcx2GC11fullCollectMFZk()  + 0x45 bytes

Because _gc_malloc does not create a stack frame, the stack trace is corrupt - it incorrectly shows that Gcx.fullcollect invokes gc_malloc. The stack trace ends abruptly at GC.fullCollect, and does not contain any useful information on why the problem occurred.

If Phobos and Druntime are rebuilt with -gs, we see the following stack trace instead:

     KernelBase.dll!_RaiseException@16()  + 0x58 bytes
     test.exe!_D2rt9deh_win329throwImplFC6ObjectZv()  + 0x23 bytes
     test.exe!__d_throwc()  + 0xc bytes
     test.exe!_D2gc2gc2GC12mallocNoSyncMFNbkkKkxC8TypeInfoZPv()  + 0x1f bytes
     test.exe!_D2gc2gc2GC6mallocMFNbkkPkxC8TypeInfoZPv()  + 0x51 bytes
     test.exe!_gc_malloc()  + 0x21 bytes
     test.exe!__d_newclass()  + 0x66 bytes
     test.exe!_rt_finalize2()  + 0xee bytes
     test.exe!_D2gc2gc3Gcx11fullcollectMFNbZk()  + 0x8c8 bytes
     test.exe!_D2gc2gc2GC11fullCollectMFNbZk()  + 0x1f bytes
     test.exe!_gc_collect()  + 0x16 bytes
     test.exe!_D4core6memory2GC7collectFNbZv()  + 0x8 bytes
>	test.exe!test.faulty()  Line 13	C
     test.exe!D main()  Line 17 + 0x5 bytes    C
(rest omitted)

We don't see the destructor in the stack trace because of issue 13723. With issue 13725 fixed, onInvalidMemoryOperationError can be breakpointed instead (this approach also requires stack frames for a useful stack trace).

--