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: nob...@puremagic.com
          Reporter: thecybersha...@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).

--

Reply via email to