I would like to examine the exception in the debugger.
When I have
except on e:exception do
This is trivial, I can just examine the e variable
But, many exception handlers do not have an on e:exception clause.
Is there a special variable such as $exception that can be inspected, or some other method to inspect the exception that does not require changing the source.
I remember doing this (though memory can be faulty), but have not been able find a way to do this.
In the System unit there is a function named ExceptObject which returns the exception object that is currently active, or nil if no exception is active. The debugger is able to evaluate this function and so give you the information you need.
Related
I do not quite understand and I could not find the answer to the question bothering me. Can the try..except block catch and pass the sub-procedure exception?
Let's say that i have code:
try
ProcedureA;
except
on E : Exception do
...
end;
and code for ProcedureA
procedure ProcedureA;
begin
SubProcedureA;
SubProcedureB;
SubProcedureC;
...
end;
If SubProcedureB raises exception, will the exception be handled at the main ProcedureA level? Will SubProcedureC be performed? Will the exception be forwarded to procedure A unchanged? Or maybe there is a restriction on sub-procedures, for example, Sub-sub-sub-procedure will no longer pass an exception to the higher-level procedure?
Thank you for the information and I apologize if this is a beginner question (which I am). :)
If SubProcedureB raises exception, will the exception be handled at the main ProcedureA level?
Yes. When an exception is raised, it propagates up the call stack until a matching handler catches it. If no handler catches it, then the process will usually terminate.
Will SubProcedureC be performed?
Usually no, however on Windows at least, it is possible (but not with Delphi's except syntax) for an exception handler to instruct the system to return back to the original call site that raised the exception. This is useful in rare cases where an exception handler can actually fix the condition that caused the exception to be raised in the first place, allowing execution to continue from where it left off. But again, this is very rare.
Will the exception be forwarded to procedure A unchanged?
Usually yes. There is only 1 Exception object in memory, and it is passed to each exception handler on the call stack until a matching handler is found. That being said, it is possible for an exception handler to catch an exception, modify it (it is just an object in memory, after all), and then re-raise it to continue the search up the call stack for another handler. That is not the case in your example, but it is allowed.
Or maybe there is a restriction on sub-procedures, for example, Sub-sub-sub-procedure will no longer pass an exception to the higher-level procedure?
There is no such restriction.
Try except block catches the exception at any level. The exception is thrown up until it is processed.
Top level is Application.OnException event.
I'm trying to figure out how to obtain a stack trace after an exception is thrown in Delphi. However, when I try to read the stack in the Application.OnException event using the function below, the stack already seems to be flushed and replaced by the throwing procedures.
function GetStackReport: AnsiString;
var
retaddr, walker: ^pointer;
begin
// ...
// History of stack, ignore esp frame
asm
mov walker, ebp
end;
// assume return address is present above ebp
while Cardinal(walker^) <> 0 do begin
retaddr := walker;
Inc(retaddr);
result := result + AddressInfo(Cardinal(retaddr^));
walker := walker^;
end;
end;
Here's what kind of results I'm getting:
001A63E3: TApplication.HandleException (Forms)
00129072: StdWndProc (Classes)
001A60B0: TApplication.ProcessMessage (Forms)
That's obviously not what I'm looking for, although it's correct. I'd like to retrieve the stack as it was just before the exception was thrown, or in other words the contents before (after would do too) the OnException call.
Is there any way to do that?
I am aware that I'm reinventing the wheel, because the folks over at madExcept/Eurekalog/jclDebug have already done this, but I'd like to know how it's done.
It is not possible to manually obtain a viable stack trace from inside the OnException event. As you have already noticed, the stack at the time of the error is already gone by the time that event is triggered. What you are looking for requires obtaining the stack trace at the time the exception is raised. Third-party exception loggers, like MadExcept, EurekaLog, etc handle those details for you by hooking into key functions and core exception handlers inside of the RTL itself.
In recent Delphi versions, the SysUtils.Exception class does have public StackTrace and StackInfo properties now, which would be useful in the OnException event except for the fact that Embarcadero has chosen NOT to implement those properties natively for unknown reasons. It requires third-party exception loggers to assign handlers to various callbacks exposed by the Exception class to generate stack trace data for the properties. But if you have JclDebug installed, for instance, then you could provide your own callback handlers in your own code that use JCL's stack tracing functions to generate the stack data for the properties.
I'd like to retrieve the stack as it was just before the
exception was thrown, or in other words the contents before
(after would do too) the OnException call.
Actually, you don't want the stack before the OnException call. That's what you've already got. You want the stack at the point at which the exception was raised. And that requires the stack tracing to happen ASAP after the raise. It's too late in the OnException call because the exception has propagated all the way to the top-level handler.
madExcept works by hooking all the RTL functions that handle exceptions. And it hooks the lowest level functions. This takes some serious effort to bring about. With these routines hooked the code can capture stack traces and so on. Note that the hooking is version specific and requires reverse engineering of the RTL.
What's more the stack walking is very much more advanced than your basic code. I don't mean that in a derogatory way, it's just that stack walking on x86 is a tricky business and the madExcept code is very well honed.
That's the basic idea. If you want to learn more then you can obtain the source code of JclDebug for free. Or buy madExcept and get its source.
I have an exception which its raise command causes stack overflow. I read this article in order to know what should I do: http://www.debuggingexperts.com/modeling-exception-handling
What I understood is the exception 0xc0000025 means attempt to catch an exception which is forbidden to be caught (EXCEPTION_NONCONTINUABLE_EXCEPTION). Am I right?
If so, I wish to know what cause the exception to be defined as non-continuable. The exception is defined in Pascal and derived from Exception object.
In addition, I failed to found where this exception is handled, and added by myself a try-catch block. The exception caught successfully. Why?
EDIT
I want to explain the specific situation I need help:
There is a C++ code which calls Pascal code, which has the exception definition, and raise command happens in it.
Before I put the try-catch block in the C++ code, the raise in Pascal causes 1000 times exception of EXCEPTION_NONCONTINUABLE_EXCEPTION until stack overflowed.
After I added the try-catch block in the C++ code, the raise in Pascal code returned to the catch block in the C++ code.
Now I have 2 questions:
Why process didn't stop on the first NONCONTINUABLE exception?
Why the catch block in C++ code didn't cause this exception?
You are correct that EXCEPTION_NONCONTINUABLE_EXCEPTION means the program attempted to continue from an exception that isn't continuable. However, it's not possible to define such an exception in Delphi, so the source of your problem is elsewhere.
Consider debugging the creation, raising, catching, and destruction of your custom exception type. If there are external libraries involved in your program, particularly any written in something other than Delphi, make sure they either know what to do with external exceptions, or are shielded entirely from exceptions.
When introducing new exception types I am always a but unsure how to do this correctly. Is there a common convention? How do you do it?
I am interested in the scope you organize them (Keep them in the unit they are used in? Have a unit on component level? Package level? Application?)
This also influences naming. How much context do you include? Is it better to make them very specific (like EPersonIDNotFoundError) or try to make them reusable (like ENotFoundError)?
And what about the suffix "Error" - when should I add it and when leave it? I cannot see the logic e.g. in Classes.pas:
EWriteError = class(EFilerError);
EClassNotFound = class(EFilerError);
EResNotFound = class(Exception);
The only real convention I know of, is to prefix them with E.
I haven't really given it much thought in the past, but now I think of it, it seems to me that both Error and Exception are commonly used as a postfix. If you mix them, I'd say that Exception relates to something that goes wrong unexpectedly, like a connection that is broken, or a file that turns out to be unreadable, while an error relates more to wrong input, for instance a number was expected, but someone typed text.
The VCL seems to follow certain conventions too, but it seems to add a postfix only if it wouldn't be clearly and error without it, for instance
EConvertError, EMathError, EVariantError
vs
EAccessViolation, EInvalidPointer, EZeroDivide
The latter describe the error itself, where the first list need the postfix to indicate an error in a certain process or entity.
These examples are found in SysUtils, maybe you can take a look there, because it contains many exception classes as well as base classes for an even larger amount of exception. Very few of those end in Exception, except for some very specific errors that indeed you hope to never come across, like EHeapException and ESafecallException.
When creating a new exception I make it application wide. I start from the most detailed error to the most general, like class(Exception) and I name them accordingly.
So, in your example, I would use EPersonIDNotFoundError, ENotFoundError, Exception.
It really depends on how much detail you want from your error messages and what you include in your log (if you keep a log of errors)
Normally for simple applications you can get away with Exception.Create('ErrorMessage'). Where exceptions get powerful is being able to detect the kind of response required by looking at the class. Delphi already does this by the Abort procedure, which raises EAbort. EAbort is 'special' in that it does not trigger an 'error' as such, i.e. it is a kind of 'silent' exception. You can use this class-specific action to examine an exception and do different things. You could create a EMyWarning, EMyVeryNastyError etc, each descended from the basic Exception type.
Further, you can define a new exception class to carry more information out to the point where the exception is trapped. For example with the code (not checked):
EMyException = class( Exception )
constructor Create( const AErrorMessage : string; AErrorCode : integer ); reintroduce;
PUBLIC
ErrorCode : integer
end;
constructor EMyException.Create( const AErrorMessage : string; AErrorCode : integer );
begin
inherited Create( AErrorMessage );
ErrorCode := AErrorCode;
end;
You now have the possibility to set 'ErrorCode' when you raise the exception and you have it available when the exception is caught. Exceptions are pretty powerful.
Which scope to organize them in?
Use one unit for the whole application where you try to fit in the most general exceptions. Everything else goes into the unit where the exception is thrown. If you need these exceptions in other units then move them to a common unit used by the subsystem you are working on.
How about naming?
Try to make one or two levels of "general" exceptions like ENotFoundError. Put these in the application global file. Don't try too hard to generalize because you can't know what exception will come later requiring you to change everything. Create specialized exceptions on unit level inheriting from the global ones.
What about "Error" postfix?
Stop thinking about it. Add it. Unless it sounds stupid.
There is a post by Raymond Chen, where he tells how bad IsBadXxxPtr function is by eating guard page exception.
I don't quite understand how it is applied to Delphi. Who and how should normally (i.e. without call to IsBadXxxPtr) process this exception?
I do know that Delphi inserts a code, which (for example) access a memory for large static arrays - exactly for this reason: to expand stack.
But if guard page exception is raised: who will handle it in a Delphi application? Can't I accidentally mess with it by using try/except in inappropriate way? Will Delphi's debugger notify me about these exceptions?
Windows structured exception handling (SEH) is has a two-phase structure. When an exception occurs, Windows first looks for a handler for the exception by following the registered exception handler chain (the head of which is stored in fs:[0] on x86, i.e. the first dword in the segment pointed to by the FS segment register - all that ugly 16-bit segment-offset logic didn't go away in 32-bit, it just became less relevant).
The search is done by calling a function with a particular flag, a pointer to which is stored in each exception frame on the stack. fs:[0] points to the topmost frame. Each frame points to the previous frame. Ultimately, the last frame on the list is one that has been provided by the OS (this handler will pop up a app-crash dialog if an unhandled exception reaches it).
These functions normally check the type of the exception, and return a code to indicate what to do. One of the codes that can be returned is basically, "ignore this exception and continue". If Windows sees this, it will reset the instruction pointer to the point of the exception and resume execution. Another code indicates that this exception frame should handle the given exception. A third code is "I'm not going to catch this exception, keep searching". Windows keeps on calling these exception filter functions until it finds one that handles the exception one way or the other.
If Windows finds one that handles the exception by catching it, then it will proceed to unwind the stack back to that handler, which consists of calling all the functions again, only passing in a different flag. It's at this point that the functions execute the finally logic, up until the handler which executes the except logic.
However, with the stack page guard exception, the process is different. None of the language's exception handlers will elect to handle this exception, because otherwise the stack growth mechanism would break. Instead, the filter search filters all the way through to the base exception handler provided by the OS, which grows the stack allocation by committing the appropriate memory, and then returns the appropriate return code to indicate that the OS should continue where it left off, rather than unwind the stack.
The tool and debugging infrastructure are designed to let these particular exceptions play out correctly, so you don't need to worry about handling them.
You can read more about SEH in Matt Pietrek's excellent article in MSJ from over a decade ago.
From looking at the comments, it looks to me like the "guard page exception" mess takes place entirely within the kernel, and is not something that you need to be worrying about from user space.
You've gotta remember that this article was written for C++, which is nowhere near as advanced as Delphi on the memory management front. The uninitialized pointers issue is a lot less of a mess in Delphi than in C/C++ for two reasons:
Delphi checks for uninitialized variables at compile time, which (for whatever reason) a lot of C compilers tend to have trouble with.
Delphi initializes all of its dynamic memory to 0, so you don't have random heap garbage to deal with that might look like a good pointer when it's really not. This means that most bad pointers give you access violations, which are easy to debug, instead of silently failing and corrupting memory.