www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - proposal(+working code): catch block callback before stack unwinds to

reply Timothee Cour <thelastmammoth gmail.com> writes:
currently, there's no way to execute some function attached to a specific
'catch block' right before unwinding the stack, after a thrown
exception/error.
The only thing we can do is set a custom Runtime.traceHandler, but this
cannot depend on the catch block (and doing so for every catch block would
hurt performance)

This prevents attaching a debugger right before stack unwinding at the root
of the program in the following scenario:

----
void fun(){
try{...}catch{...}
// code that may throw and is usually caught in normal setting
// occasionally some unusual exceptions/errors won't be caught
}
void call_debugger(){
 //raise signal so we can attach a debugger
}
void main(){
try{
  fun();
}
catch(Throwable t){
  // we want to call call_debugger after an uncaught exception lands here
but before stack unwinds, because most information is lost after stack
unwinds
  // the other catch blocks should be unaffected, in particular
call_debugger should only be called before landing on this specific catch
block
}
}
----


I've modified rt/deh2.d to allow it, it works great, but my implementation
is hacky, and I'd like to poke the newsgroup for a better design:

class ThrowableSpecialUnwinding : Throwable {...}
void main(){
try{...}
catch(ThrowableSpecialUnwinding t){
//callbackBeforeUnwinding is called right before unwinding stack and
entering this block
// it doesn't affect other catch blocks nor assumes anything about the
caught exception which could be any Throwable.
// I've cheated here so casting back
auto real_t=cast(Throwable)cast(void*)t;
}
}

extern(C)
void callbackBeforeUnwinding(Throwable t){
call_debugger; //this could be calling a function pointer modifyable at
runtime
}

The hacky part is here:
in rt/deh2.d, I changed:

if (_d_isbaseof(ci, pcb.type) )

to :

bool isSpecial = pcb.type== ThrowableSpecialUnwinding.classinfo &&
_d_isbaseof(ci, Throwable.classinfo);
 if(isSpecial)
  callbackBeforeUnwinding(cast(Throwable) *h);

if (_d_isbaseof(ci, pcb.type) || isSpecial) {/*will stack unwind here*/}
Oct 27 2013
parent reply "David Nadlinger" <code klickverbot.at> writes:
On Sunday, 27 October 2013 at 13:09:21 UTC, Timothee Cour wrote:
 The hacky part is here:
 in rt/deh2.d, I changed:
Haven't looked at the proposal in any detail yet, but keep in mind that rt.deh2 is only used by DMD. GDC and LDC use GCC's libunwind for exception handling on most platforms. If the semantics of a feature are reasonable, it's probably possible to implement it on top of libunwind as well, but it's definitely something to keep in mind. David
Oct 27 2013
next sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
ideal syntax would be something along those lines:

try{...}
catch(SomeThrowableType e, &callback_before_unwinding){...}
with callback_before_unwinding of type 'void function(Throwable t)'

The semantics of which is:
if 'e' is about to be caught in this particular catch block, call
callback_before_unwinding(e) _before_ unwinding stack and entering catch
block.

Advantages are:
* it allows one to, for example, attach a debugger before stack unwinds and
entering this catch block
* the debugger is only attached at that point, hence not making the regular
program slower
* it doesn't affect other catch blocks nor does it affect how
exceptions/errors are thrown
* it could even allow one to make speed up exception handling, by computing
backtrace _only_ inside this callback_before_unwinding, so that usual
exceptions (std.conv conversions etc) can be caught without any backtrace
inside the main program while unusual unintended exceptions would be caught
at the root and show proper stack trace.

This saves a lot of time when debugging issues, at _zero_ cost in
performance.

Does libunwind allow that? Otherwise could it be adapted to do that?



On Sun, Oct 27, 2013 at 6:27 AM, David Nadlinger <code klickverbot.at>wrote:

 On Sunday, 27 October 2013 at 13:09:21 UTC, Timothee Cour wrote:

 The hacky part is here:
 in rt/deh2.d, I changed:
Haven't looked at the proposal in any detail yet, but keep in mind that rt.deh2 is only used by DMD. GDC and LDC use GCC's libunwind for exception handling on most platforms. If the semantics of a feature are reasonable, it's probably possible to implement it on top of libunwind as well, but it's definitely something to keep in mind. David
Oct 27 2013
prev sibling next sibling parent Iain Buclaw <ibuclaw ubuntu.com> writes:
On 27 October 2013 23:43, Timothee Cour <thelastmammoth gmail.com> wrote:
 ideal syntax would be something along those lines:

 try{...}
 catch(SomeThrowableType e, &callback_before_unwinding){...}
 with callback_before_unwinding of type 'void function(Throwable t)'

 The semantics of which is:
 if 'e' is about to be caught in this particular catch block, call
 callback_before_unwinding(e) _before_ unwinding stack and entering catch
 block.

 Advantages are:
 * it allows one to, for example, attach a debugger before stack unwinds and
 entering this catch block
 * the debugger is only attached at that point, hence not making the regular
 program slower
 * it doesn't affect other catch blocks nor does it affect how
 exceptions/errors are thrown
 * it could even allow one to make speed up exception handling, by computing
 backtrace _only_ inside this callback_before_unwinding, so that usual
 exceptions (std.conv conversions etc) can be caught without any backtrace
 inside the main program while unusual unintended exceptions would be caught
 at the root and show proper stack trace.

 This saves a lot of time when debugging issues, at _zero_ cost in
 performance.

 Does libunwind allow that? Otherwise could it be adapted to do that?
GDB has 'catch throw' and 'catch catch' as convenient C++ breakpoint placeholders in the debugger. Can certainly implement them for D too. http://www.delorie.com/gnu/docs/gdb/gdb_31.html -- Iain Buclaw *(p < e ? p++ : p) = (c & 0x0f) + '0';
Oct 28 2013
prev sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
On Mon, Oct 28, 2013 at 12:08 AM, Iain Buclaw <ibuclaw ubuntu.com> wrote:

 On 27 October 2013 23:43, Timothee Cour <thelastmammoth gmail.com> wrote:
 ideal syntax would be something along those lines:

 try{...}
 catch(SomeThrowableType e, &callback_before_unwinding){...}
 with callback_before_unwinding of type 'void function(Throwable t)'

 The semantics of which is:
 if 'e' is about to be caught in this particular catch block, call
 callback_before_unwinding(e) _before_ unwinding stack and entering catch
 block.

 Advantages are:
 * it allows one to, for example, attach a debugger before stack unwinds
and
 entering this catch block
 * the debugger is only attached at that point, hence not making the
regular
 program slower
 * it doesn't affect other catch blocks nor does it affect how
 exceptions/errors are thrown
 * it could even allow one to make speed up exception handling, by
computing
 backtrace _only_ inside this callback_before_unwinding, so that usual
 exceptions (std.conv conversions etc) can be caught without any backtrace
 inside the main program while unusual unintended exceptions would be
caught
 at the root and show proper stack trace.

 This saves a lot of time when debugging issues, at _zero_ cost in
 performance.

 Does libunwind allow that? Otherwise could it be adapted to do that?
GDB has 'catch throw' and 'catch catch' as convenient C++ breakpoint placeholders in the debugger. Can certainly implement them for D too. http://www.delorie.com/gnu/docs/gdb/gdb_31.html
I'm worried about that from performance standpoint: * that implies running the normal program execution while attached to a debugger, which would be slower * catch throw will stop at every thrown exception as opposed to the ones that are caught at a certain 'root' point. In what I suggested (and implemented for dmd), it has 0 performance penalty and only triggers when a thrown exception is caught in a particular location; debugger isn't attached during normal program execution.
 --
 Iain Buclaw

 *(p < e ? p++ : p) = (c & 0x0f) + '0';
Oct 28 2013