May 10, 2011
Sean Kelly wrote:
> 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.
> 

It's not from DMC, but in the system directory. On XP I have version
5.1.2600.5512, on Win7 6.1.7600.16385. The latter works. Also there is
version 6.11.1.404 in Microsofts "Debugging Tools for Windows (x86)"
which also works on XP.
I have not tested on complicated programs, but the stack traces work
fine with the updated dbghelp.dll.


> 
>>>> - 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?

Yes. But I tried it again, and the symbols shown are actually mangled, while in the mago debugger they are shown demangled! Dumping the debug info, there are now 2 different names for the same procedure, the mangled in section sstGlobalPub, the unmangled in sstAlignSym. Maybe this is no problem, I'm not sure. I stand corrected again.

> 
> 
>>>> - 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.

I agree, that would be a problem. I didn't thought about passing the exception along.

> 
> 
>>>> - 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.

The problem is still there in the debugger, but I don't see it in the printed stack trace. This might come from the different points where the call stack is snapshot. When throwing an exception, it is taken inside _d_throw before actually raising the exception, while the debugger pops up inside RaiseException, where unwinding seems a little more difficult.

The same happens with the stack trace if the exception is not thrown by the program, but is caused by an access violation, divide by zero or similar.

> 
> 
>>>> - 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.

There are two levels of trapping exceptions: one tryExec call that
includes the initialization, which itself calls tryExec on main.
I think it is no problem that it has no effect later, the documentation
just has to say so.
What do you think about the approach of setting rt_trapExceptions
through an environment variable? This way, it's up to the user or the
debugger or the process starting the debugger to decide how to deal with
unhandled exceptions.
1 2
Next ›   Last »