Thread overview
Catching excecptions in the MSVC debugger when running Win64
Apr 26, 2021
Lewis
Apr 26, 2021
Lewis
Apr 27, 2021
Lewis
Apr 27, 2021
Rainer Schuetze
April 26, 2021

I can't seem to catch a D exception on win64 in the Visual Studio debugger. I'm using:

  • DMD compiler (2.094.2)
  • VisualD 1.1.0
  • Visual Studio 2019
  • Building a Win64 executable
  • Debugger set to Visual Studio
  • All Win32 and D exceptions enabled in the Exception Settings window in VS

Test code:

import std.file : copy;
int main()
{
    copy("filethatdoesnotexist.bla", "othernonexistentfile.bla");
    return 0;
}

I've tried manually rebuilding phobos in debug so I can see inside it. I can step into the copy(), all the way until the point where it constructs the FileException object. But the debugger never seems to catch the exception.

Instead the debugger breaks in deh_win64_posix.d:493 on the call to terminate() at the end of _d_throwc(). But this just causes the debugger to catch 0xC0000096: Privileged instruction (presumably due to the hlt instruction inside terminate()), instead of actually understanding the D FileException that got thrown. Nothing is printed to the output window regarding the D exception

Is this the expected behaviour? If not, how to I get visual studio to properly catch D exceptions for a win64 executable?

April 26, 2021

Clearly I don't know how to spell exception :P

April 27, 2021

Okay, I was able to get the VS debugger to catch exceptions in win64 by adding a hack to deh_win64_posix.d so that it mimics deh_win32.d.

I added the following code above the definition of _d_throwc():

import core.sys.windows.windef : DWORD;
extern(Windows)
{
    void RaiseException(DWORD, DWORD, DWORD, void*);
}

...and I added the following to _d_throwc() itself, right after it calls _d_createTrace():

template MAKE_EXCEPTION_CODE(int severity, int facility, int exception)
{
   enum int MAKE_EXCEPTION_CODE = (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception));
}
enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1);
enum DWORD EXCEPTION_NONCONTINUABLE = 1;
RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION, EXCEPTION_NONCONTINUABLE, 1, cast(void*)&h);

With this, the debugger catches exceptions again.

This feels like a total hack though. Presumably I'm missing an obvious better solution?

April 27, 2021

On 27/04/2021 07:43, Lewis wrote:
> Okay, I was able to get the VS debugger to catch exceptions in win64 by adding a hack to deh_win64_posix.d so that it mimics deh_win32.d.
> 
> I added the following code above the definition of _d_throwc():
> 
> ```
> import core.sys.windows.windef : DWORD;
> extern(Windows)
> {
>    void RaiseException(DWORD, DWORD, DWORD, void*);
> }
> ```
> 
> ...and I added the following to _d_throwc() itself, right after it calls
> _d_createTrace():
> 
> ```
> template MAKE_EXCEPTION_CODE(int severity, int facility, int exception)
> {
>   enum int MAKE_EXCEPTION_CODE = (((severity) << 30) | (1 << 29) | (0 <<
> 28) | ((facility) << 16) | (exception));
> }
> enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1);
> enum DWORD EXCEPTION_NONCONTINUABLE = 1;
> RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION,
> EXCEPTION_NONCONTINUABLE, 1, cast(void*)&h);
> ```
> 
> With this, the debugger catches exceptions again.
> 
> This feels like a total hack though. Presumably I'm missing an obvious better solution?

Unfortunately, the dmd backend does not use the standard exception mechanism for win64, but some homegrown one adapted from the linux exception handling. That's why the debugger does not recognize the exceptions.

Your solution works for catching exceptions, but catch statements will not work with RaiseException. I guess the best option is to set a breakpoint in _d_throwc.

LDC uses C++ exceptions, so your debugger should behave as expected when building with LDC.