[D-runtime] stacktraces on windows

Sean Kelly sean at invisibleduck.org
Mon May 9 16:29:26 PDT 2011


On May 9, 2011, at 4:01 PM, Rainer Schuetze wrote:

> Sean Kelly wrote:
>> On May 7, 2011, at 5:05 AM, Rainer Schuetze wrote:
>>> Hijacking this thread from the phobos list, I'd like to comment on stack traces under windows:
>>> 
>>> - you'll never get a symbolic stacktrace without converting the debug info with cv2pdb (well, it might work for very simple executables). stacktrace.d uses dbghelp.dll, and this usually dislikes the debug information generated by dmd/optlink.
>> This has worked for me in testing, but those tests were with very simple executables.  Are there any alternatives?
> 
> I've read on Benjamin Thaut's page about his stacktrace implementation, that it depends on the version of dbghelp.dll, but without any exact version given. Maybe I'm outdated here, but judging from reports on the newsgroup, others seem to have the same problem.

The only C compiler I have in my Windows install is DMC, so that's likely where I'm getting dbghelp.dll.  It would be interesting to see if the folks who couldn't get a good backtrace had Visual Studio as well.


>>> - if symbols are found, generating the stack trace message generates another exception inside std.demangle if the symbol cannot be demangled (non-D, compressed or SHA'd symbols).
>> If it helps, the exception thrown is a static exception, and is caught within demangle().  With the new chaining rules, this shouldn't adversely affect anything.
> 
> I've seen recursive exceptions in the debugger, but debugging only works after conversion of the debug info with cv2pdb, so it might be a little different. A recent change in dmd causes debug symbols to output demangled names, so using core.demangle should not be necessary anymore for printing the callstack.

Didn't know that.  Is this Windows-only?


>>> - walking the stack trace can be very time consuming, especially when loading debug symbols while doing so. This should not be done for every exception thrown.
>> What do you suggest?  I'd considered disabling tracing if -release was specified, though I haven't yet tested my idea for how to make this work.
> 
> As suggested for linux elsewhere, would it be possible to create the call stack only for unhandled exceptions or if requested by the exception handler? I remember doing this for SEH in C++, but don't have the details at the moment.

I think it would... it's just not intuitive to me.  And if the user passed the exception outside the catch block and then called the backtrace routine, chaos would ensue.


>>> - the stack trace often misses the source file location where the exception is thrown as reported here: http://d.puremagic.com/issues/show_bug.cgi?id=4809
>>> I'm not sure the patch is up to date, though.
>> Patch?  The code is already in druntime.  Or am I missing something?  Look at core.runtime and core.sys.windows.dbghelp.
> 
> The patch consists of adding option -S to the dmc command line when compiling deh.c, so it does not omit frame pointers. Otherwise the usual stack walk skips functions and has problems resyncing the frame pointer positions. See also http://www.dsource.org/projects/visuald/ticket/13 - I assume the VS debugger does the same as dbghelp.dll here.
> In the meantime the exception handling has been moved to a D implementation deh.d, but the problem seems to remain the same: the location that actually does the throw is often missing. I'll try to figure out if it is still the same cause.

Thanks.


>>> - a convenient way to inspect crashes is to fire up the debugger when the crash happens with the crash reporting by Windows. This functionality is currently blocked by dmain2.main() handling all Exceptions (which also lets a non-console application blow up without any message). Also, stopping on unhandled exceptions only inside a debugger is not possible.
>>> I understand that a dialog showing up is no good when running programs from the console (e.g. the autotester), so I suggest making this configurable:
>>> 
>>> a: catch all Throwables
>>> b: catch all Exceptions
>>> c: catch none
>>> 
>>> There's already a variable rt_trapExceptions available that does a or c, but you can't configure it before executing the program. It is also copied to a local value before any static initializer is called, so it cannot be modified programmatically. (The comment says it should be modified by the debugger, but I don't think this is a sensible approach.)
>> DDBG always worked this way.  In fact, I believe it was added on request from the developer.  I'd be happy to get rid of it if someone can suggest a better alternative though.
> 
> A first step would be to allow setting rt_trapExceptions from a static constructor, so you can configure how exceptions in main() are handled. This currently does not work because rt_trapExceptions is copied to a local variable trapExceptions before running the constructors.
> 
> I'm not sure how to access rt_trapExceptions earlier so it will also affect static constructors and unittests. Maybe it could be set from an environment variable.

The tricky part is that rt_trapExceptions determines whether the functional part of the app, including static ctors, is wrapped in a try block.  If you change this value later, there won't be any effect because execution has already entered/not entered the try block.  I think that re-throwing out of the default catch block will lose context info, so that isn't an option.  The alternative to rt_trapExceptions seems like it would require changes to the exception throwing code itself.


More information about the D-runtime mailing list