PDA

View Full Version : [Cool Trick] Trapping "raise" for better debugging



masonwheeler
27-07-2008, 03:30 PM
My projects tend to make pretty heavy use of exception handling. Problem is, when an exception does get raised, it tends to break the stack or do other interesting things that make debugging more difficult than it should be. I generally end up finding where the exception was raised, then setting a breakpoint on the line that contains the raise command, then running everything again.

The raise command is basically just syntactic sugar. The compiler translates "raise Exception.Create('An error has occurred');" to "RaiseExceptionProc(Exception.Create('An error has occurred'));". RaiseExceptionProc is a defined in the System unit, and maps by default to an API function.

What this means is, you can actually redefine the RaiseExceptionProc and have a very handy debugging tool. It's a single simple unit, plus three other minor changes, but it can greatly simplify debugging for you.

unit special_debugging;

interface

procedure DebugExceptionHandler(value: pointer);

implementation

uses
sysUtils;

procedure DebugExceptionHandler(value: pointer);
var
E: Exception;
begin
e := Exception(value);
asm int 3 end; //manual breakpoint
E.Free;
end;

end.

The "asm int 3 end;" manually creates a debugger breakpoint in assembly. You can replace the call with the Windows API call DebugBreak if you want. (You'll have to add the Windows unit to your uses list in that case.)

What this gives you is a function that automatically breaks to the debugger, lets you examine the exception, and preserves the stack intact. Implementing it is simple:

Add "DEBUG" as a conditional define for your project. (Project -> Options... -> Directories/Conditionals -> Conditional Defines). Delphi 2007 does this for you automatically. You only want this handler going off in a debug build, as it has the side-effect of gutting real exception-handling functionality. Make sure to turn off the DEBUG define for release builds.
In your project's DPR file, in the USES list, add the following line: {$IFDEF DEBUG}special_debugging in '<wherever it's located>.pas',{$ENDIF}
Down below, in the code section, add "{$IFDEF DEBUG}RaiseExceptionProc := @special_debugging.DebugExceptionHandler;{$ENDIF}" somewhere before the call to Application.Run.

Feel free to use this in your projects if you find it useful. Modify it, play with it, whatever you'd like.

Mason

Brainer
27-07-2008, 03:41 PM
Looks hot! :D I'll give it a go when I have a chance. Nice doin' man, way to go! :D

masonwheeler
16-08-2008, 03:53 PM
I found an even better way to implement this. Delphi doesn't care too much for special conditionals in the DPR, and it might erase them and make trouble for you without warning. So instead of putting the condition there, just include the unit in your project unconditionally, and add the following to the bottom of the unit, just before the final end. line:


&#123;$IFDEF DEBUG&#125;
initialization
begin
RaiseExceptionProc &#58;= @DebugExceptionHandler;
end;
&#123;$ENDIF&#125;

This way you can make it work without needing to poke around in the DPR.