!standard 11.4.1(13/2) 12-01-06 AC95-00219/00 !class confirmation 11-06-17 !status received no action 11-06-17 !status received 11-04-15 !subject Proposal Ada 2012 concerning Ada.Exceptions.Exception_Infomation !summary !appendix From: Dr. Joachim Schroerr Sent: Friday, April 15, 2011 4:18 AM Dears Ladies, Sirs I would replace 13/2 in 11.4.1 The Package Exceptions With a note requiring Exception_Information to return a readable call stack, e.g. 13/2 Exception_Information returns besides implementation-defined information about the exception occurrence a readable call stack with subprogram and source-file and line information. The returned string has lower bound 1. See e.g. Gnat.Traceback.Symbolic.Symbolic_Traceback. There are Ada95 implementations (Green Hills) and Gnat-Implementations of Ada 2005 around (Mac Os X) that do not support a call stack. You really miss it during development. This a in my opinion the single real advantage of Java to have a readable exception call stack required in the standard. *************************************************************** From: Georg Bauhaus Sent: Monday, April 18, 2011 12:18 PM ... > This a in my opinion the single real advantage of Java to have a readable > exception call stack required in the standard. I don't think Java's stacktrace includes calls from within JNI methods? *************************************************************** From: Florian Weimer Sent: Monday, April 18, 2011 2:37 PM Uhm, they do? *************************************************************** From: Florian Weimer Sent: Monday, April 18, 2011 2:44 PM > There are many processors (some of which our compiler targets) for > which a call stack can't even be unwound without a ton of extra > information being present in the program. Agreed. On the other hand, it might be useful to add implementation advice to C.7.3 to the effect that in the Unhandled_Exception case, the Termination_Handler should be called before the stack is unwound. This way, the programmer is free to use platform-specific stack unwinding information, or to trigger a crash dump which still reflects the original call stack. This would be similar to a subset to the functionality provided by C++'s std::set_unexpected. *************************************************************** From: Adam Beneschan Sent: Monday, April 18, 2011 3:42 PM I don't know what std::set_unexpected does. My knowledge of C++ is quite limited. I think your proposal would be a change in the semantics; thus, it couldn't be done simply by adding to Implementation Advice. C.7.3(8) says that the Termination_Handler is called when a task terminates. 9.3(5) says that a task terminates when the finalization of the body has been performed. Suppose task body A calls procedure B, and B calls C, and C raises an exception that is not handled anywhere within A or B. Your proposal seems to suggest that the Termination_Handler should be called while A, B, and C are still all on the stack. But suppose B declares a local variable V that requires a finalization routine to be performed; the code probably has to return from C before it can finalize V, especially if the Finalize routine needs to access some of B's local variables. Calling the Termination_Handler while C is still on the stack means that it's called before V is finalized; since the task termination occurs after V is finalized, this is a significant change in the dynamic semantics. I suppose it might be possible to cook up a scheme that would allow V (and any other variables in A or B) to be finalized while C is still on the stack, but it seems to me this could require a major change in implementations. There may be other ways to provide the ability to retrieve a stack for an exception occurrence, or for an unhandled exception occurrence, but I'd be opposed to *requiring* that the information be available since it could involve major changes to some Ada implementations. *************************************************************** From: Florian Weimer Sent: Monday, April 18, 2011 4:05 PM >> This would be similar to a subset to the functionality provided by >> C++'s std::set_unexpected. > > I don't know what std::set_unexpected does. My knowledge of C++ is > quite limited. It is called when an unhandled exception is encountered. The stack is not unwound, and destructors (finalizers) of on-stack objects are not invoked. > I think your proposal would be a change in the semantics; thus, it > couldn't be done simply by adding to Implementation Advice. C.7.3(8) > says that the Termination_Handler is called when a task terminates. > 9.3(5) says that a task terminates when the finalization of the body > has been performed. *grrr* Missed that. > There may be other ways to provide the ability to retrieve a stack for > an exception occurrence, or for an unhandled exception occurrence, but > I'd be opposed to *requiring* that the information be available since > it could involve major changes to some Ada implementations. Could you add another type of handler callback? Do implementations decide if a handler exists before beginning to unwind the stack in earnest? I think that in most cases, you want stack traces for unhandled exceptions only, or cases where you currently write a "when E : others" handler which logs and swallows the exception, without actually handling it in an exception-specific manner. Both could be converted to the new callback. These are supposed to be quite rare, so performance of generating the stack trace is not as important as it would be if stack traces were added to all exceptions. The question where to put the (possibly unbounded) stack trace data does not arise, either. *************************************************************** From: Randy Brukardt Sent: Monday, April 18, 2011 4:13 PM ... > There may be other ways to provide the ability to retrieve a stack for > an exception occurrence, or for an unhandled exception occurrence, but > I'd be opposed to *requiring* that the information be available since > it could involve major changes to some Ada implementations. I agree. IMHO, the only way to provide accurate stack information is for the implementation to keep a trace of it while running, and this is not free (both in time and space). Specifically, Janus/Ada doesn't try to unwind the stack, because the different calling conventions format the stack in different ways, and assembler code doesn't have to follow any format at all. So, in Janus/Ada, each task has a trace call stack that includes subprogram names and call line numbers. This is pushed/popped on entrance/exit to each subprogram. An exception occurrence contains a copied snapshot of the top of this this stack (an exception occurrence is quite large). Since there is substantial overhead to this information, there is a compiler switch to turn it off. That eliminates the overhead, but it also means that any routines compiled that way don't appear in any walkback information. Similarly, the runtime can be built without the support for these stacks for use on tiny embedded systems, in which case there is no support at all. We also use this same mechanism to gather subprogram-based profiling information. In any case, requiring implementations to go to these lengths seems wrong; what makes sense depends on the intended use (embedded vs. hosted), the characteristics of the target system, and customer needs. In any case, the Standard cannot reasonably mandate a "good" implementation; that's between customers and vendors. *************************************************************** From: Adam Beneschan Sent: Monday, April 18, 2011 4:22 PM > > There may be other ways to provide the ability to retrieve a stack > > for an exception occurrence, or for an unhandled exception > > occurrence, but I'd be opposed to *requiring* that the information > > be available since it could involve major changes to some Ada implementations. > > Could you add another type of handler callback? Do implementations > decide if a handler exists before beginning to unwind the stack in > earnest? I'm aware of both implementations that do, and implementations that don't. However, the implementations that *do* decide if a handler exists probably wouldn't do what you want if there's a subprogram on a stack that has a handler like: exception when others => Some_Cleanup_Routine; raise; And the ones I'm aware of would not be able to distinguish an explicit handler written in Ada from an implicit one generated by the compiler to finalize local variables. And what if an explicit handler like the above didn't always do a reraise, but did so based on a condition? I'd assume that in the above case, you'd still want to have the stack trace for the original exception that caused the exception occurrence. The stack trace for the subprogram that calls Some_Cleanup_Routine may not be all that useful. Or it might. Anyway, adding a second type of handler callback is one of the possible solutions I was envisioning. But even so, it seems challenging to define a feature in a way that is useful, can be feasibly implemented, and doesn't require overhead in programs that don't raise any exceptions in implementations that don't already have overhead. *************************************************************** From: Randy Brukardt Sent: Monday, April 18, 2011 5:05 PM ... > I'm aware of both implementations that do, and implementations that > don't. However, the implementations that *do* decide if a handler > exists probably wouldn't do what you want if there's a subprogram on a > stack that has a handler like: > > exception > when others => > Some_Cleanup_Routine; > raise; We have special handling for reraises specifically to deal with this problem; an exception occurrence includes copies of parts of both stacks (the original one, and the last reraise if any). None of this is cheap, and as such, it doesn't make sense for the Standard to require it. (And to reply to Florian, I think there are as many ways of identifying exception handlers as there are compilers, and possibly more than that. :-) There is little commonality in this area, so we can't really consider rules based on what some particular implementation can do.) *************************************************************** From: Jeff Cousins Sent: Tuesday, April 19, 2011 3:35 AM Note that if you raise an exception with a user-supplied string then you lose this implementation-defined information (which in GNAT's case is the useful package name and line number of the exception) - see RM 11.4.1 para 10.1/2 (Ada 2005) or 10.1/3 (Ada 2012). It would have been more useful for debugging to have allowed both user-supplied string and implementation-defined information. *************************************************************** From: Randy Brukardt Sent: Tuesday, April 19, 2011 3:33 PM I think you are confused. The user-defined string replaces the implementation-defined Exception_Message information. But it does not replace the implementation-defined exception occurrence information (that is, the information provided beyond the exception message and the exception name). This is the information discussed in 11.4.1(13/2) - this is not the same as the information disucssed in 11.4.1(10.1/2). Of course, there is no requirement that there be any such information. For Janus/Ada, the implementation-defined exception message is empty for most exceptions, but contains additional information for some of the language-defined exceptions. In particular, a Constraint_Error caused by a discrete value being out of range includes the out of range value. If you provide an user-specified message, this information is replaced and not available. OTOH, walkback information is part of Exception_Information, and is provided for all exception occurrence values (unless the value is streamed out; since it contains pointers to subprogram names that probably aren't the same in another program, it is stripped at that point). ***************************************************************