You've already forked lazarus-ccr
362 lines
8.9 KiB
ObjectPascal
362 lines
8.9 KiB
ObjectPascal
![]() |
(* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is TurboPower SysTools
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* TurboPower Software
|
||
|
*
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** *)
|
||
|
|
||
|
{*********************************************************}
|
||
|
{* SysTools: StExpLog.pas 4.03 *}
|
||
|
{*********************************************************}
|
||
|
{* SysTools: Exception Logging *}
|
||
|
{*********************************************************}
|
||
|
|
||
|
{$IFDEF FPC}
|
||
|
{$mode DELPHI}
|
||
|
{$ENDIF}
|
||
|
|
||
|
{$I StDefine.inc}
|
||
|
|
||
|
unit StExpEng;
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
Windows, SysUtils, Classes, StBase, StExpLog;
|
||
|
|
||
|
const
|
||
|
OnHookInstaller : procedure = nil;
|
||
|
|
||
|
procedure DumpException;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
Forms;
|
||
|
|
||
|
const
|
||
|
MaxStackSize = 48;
|
||
|
|
||
|
type
|
||
|
TStExceptionHandler = class
|
||
|
private
|
||
|
OldOnException : TExceptionEvent;
|
||
|
protected
|
||
|
procedure OnException(Sender : TObject; E : Exception);
|
||
|
end;
|
||
|
|
||
|
TStExceptionTrace = record
|
||
|
Count : Integer;
|
||
|
Trace : array[0..pred(MaxStackSize)] of DWORD;
|
||
|
end;
|
||
|
|
||
|
const
|
||
|
EH : TStExceptionHandler = nil;
|
||
|
WroteInfo : Boolean = False;
|
||
|
HandlerInstalled : Boolean = False;
|
||
|
cDelphiException = DWORD($0EEDFADE);
|
||
|
cCppException = DWORD($0EEFFACE); { used by BCB }
|
||
|
|
||
|
var
|
||
|
RA2 : procedure (dwExceptionCode, dwExceptionFlags, nNumberOfArguments : DWORD;
|
||
|
const lpArguments : DWORD); stdcall;
|
||
|
BaseOfCode, TopOfCode : DWORD;
|
||
|
|
||
|
{ Writes exception to log file }
|
||
|
procedure WriteException(E : Exception);
|
||
|
var
|
||
|
p1 : Integer;
|
||
|
RipFileName, S : string;
|
||
|
FS : TFileStream;
|
||
|
Buffer : array[0..255] of AnsiChar;
|
||
|
begin
|
||
|
if Assigned(ExpLog) then
|
||
|
RipFileName := ExpLog.FileName;
|
||
|
|
||
|
if RipFileName = '' then begin
|
||
|
GetModuleFileName(HInstance, Buffer, SizeOf(Buffer));
|
||
|
RipFileName := ChangeFileExt(PChar(@Buffer),'.RIP');
|
||
|
end;
|
||
|
|
||
|
{ Open file stream }
|
||
|
if FileExists(RipFileName) then begin
|
||
|
FS := TFileStream.Create(RipFileName, fmOpenReadWrite or fmShareDenyWrite);
|
||
|
FS.Seek(0, soFromEnd);
|
||
|
S := #13#10#13#10;
|
||
|
FS.Write(S[1], Length(S));
|
||
|
end else begin
|
||
|
FS := TFileStream.Create(RipFileName, fmCreate or fmShareDenyWrite);
|
||
|
end;
|
||
|
|
||
|
try
|
||
|
{ Write info if necessary }
|
||
|
if not WroteInfo and Assigned(ExpLog) then begin
|
||
|
if (ExpLog.RipInfo <> '') then begin
|
||
|
S := ExpLog.RipInfo + #13#10#13#10;
|
||
|
FS.Write(S[1], Length(S));
|
||
|
end;
|
||
|
WroteInfo := True;
|
||
|
end;
|
||
|
|
||
|
{ Write dump info from E.Message }
|
||
|
p1 := Pos(#0, E.Message);
|
||
|
S := Copy(E.Message, p1+1, MaxInt) + #13#10;
|
||
|
FS.Write(S[1], Length(S));
|
||
|
|
||
|
{ Restore E.Message }
|
||
|
S := E.Message;
|
||
|
SetLength(S, P1-1);
|
||
|
E.Message := S;
|
||
|
|
||
|
finally
|
||
|
FS.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ Dumps Exception }
|
||
|
procedure DumpException;
|
||
|
var
|
||
|
PutInLog : Boolean;
|
||
|
begin
|
||
|
PutInLog := True;
|
||
|
if Assigned(ExpLog) then
|
||
|
ExpLog.DoExceptionFilter(Exception(ExceptObject),PutInLog);
|
||
|
if PutInLog then
|
||
|
WriteException(Exception(ExceptObject));
|
||
|
end;
|
||
|
|
||
|
{ TStExceptionHandler }
|
||
|
|
||
|
procedure TStExceptionHandler.OnException(Sender : TObject; E : Exception);
|
||
|
begin
|
||
|
DumpException;
|
||
|
if Assigned(OldOnException) then
|
||
|
OldOnException(Sender, E)
|
||
|
else
|
||
|
Application.ShowException(Exception(ExceptObject));
|
||
|
end;
|
||
|
|
||
|
var
|
||
|
SaveGetExceptionObject : function(P : PExceptionRecord) : Exception;
|
||
|
|
||
|
procedure HookInstaller;
|
||
|
begin
|
||
|
EH := TStExceptionHandler.Create;
|
||
|
EH.OldOnException := Application.OnException;
|
||
|
Application.OnException := EH.OnException;
|
||
|
end;
|
||
|
|
||
|
procedure StackDump(E : Exception; Root : DWORD);
|
||
|
var
|
||
|
P : PDWORD;
|
||
|
C, D, StackTop, N, Prev : DWORD;
|
||
|
Trace : TStExceptionTrace;
|
||
|
I : Integer;
|
||
|
Store : Boolean;
|
||
|
MsgPtr : PChar;
|
||
|
MsgLen : Integer;
|
||
|
begin
|
||
|
if not HandlerInstalled then begin
|
||
|
if Assigned(OnHookInstaller) then
|
||
|
OnHookInstaller;
|
||
|
HandlerInstalled := True;
|
||
|
end;
|
||
|
|
||
|
if Root = 0 then
|
||
|
Trace.Count := 0
|
||
|
else begin
|
||
|
Trace.Count := 1;
|
||
|
Trace.Trace[0] := Root;
|
||
|
end;
|
||
|
|
||
|
asm
|
||
|
mov P,ebp
|
||
|
mov eax,fs:[4]
|
||
|
mov [StackTop],eax
|
||
|
end;
|
||
|
|
||
|
Prev := 0;
|
||
|
C := 0;
|
||
|
|
||
|
while DWORD(P) < DWORD(StackTop) do begin
|
||
|
D := P^;
|
||
|
N := 0;
|
||
|
if (D >= BaseOfCode) and (D < TopOfCode) then
|
||
|
if (PByte(D-5)^ = $E8)
|
||
|
or ((PByte(D-6)^ = $FF) and (((PByte(D-5)^ and $38) = $10)))
|
||
|
or ((PByte(D-4)^ = $FF) and (((PByte(D-3)^ and $38) = $10)))
|
||
|
or ((PByte(D-3)^ = $FF) and (((PByte(D-2)^ and $38) = $10)))
|
||
|
or ((PByte(D-2)^ = $FF) and (((PByte(D-1)^ and $38) = $10))) then
|
||
|
N := D-BaseOfCode;
|
||
|
if (N <> 0) and (N <> Prev) then begin
|
||
|
if (Root = 0) then
|
||
|
Store := C > 0
|
||
|
else
|
||
|
Store := C > 1;
|
||
|
if Store then
|
||
|
begin
|
||
|
Trace.Trace[Trace.Count] := N;
|
||
|
Inc(Trace.Count);
|
||
|
end;
|
||
|
Inc(C);
|
||
|
if C > MaxStackSize then Break;
|
||
|
Prev := N;
|
||
|
end;
|
||
|
Inc(P);
|
||
|
end;
|
||
|
|
||
|
if C > 0 then begin
|
||
|
MsgPtr := PChar(E.Message);
|
||
|
MsgLen := StrLen(MsgPtr);
|
||
|
if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then
|
||
|
E.Message := E.Message + '.';
|
||
|
E.Message := E.Message + #0 + Format('Fault : %s'#13#10'Date/time : %s %s'#13#10'Stack dump'#13#10+
|
||
|
'----------'#13#10,[E.Message,DateToStr(Now),TimeToStr(Now)]);
|
||
|
for i := 0 to pred(Trace.Count) do
|
||
|
E.Message := E.Message + Format('%8.8x'#13#10,[Trace.Trace[i]]);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure LRE(dwExceptionCode, dwExceptionFlags, nNumberOfArguments : DWORD;
|
||
|
const lpArguments : DWORD); stdcall;
|
||
|
var
|
||
|
E : Exception;
|
||
|
begin
|
||
|
if (dwExceptionCode = cDelphiException) or (dwExceptionCode = cCppException) then begin
|
||
|
asm
|
||
|
push ebx
|
||
|
mov ebx,lpArguments
|
||
|
mov eax,ss:[ebx+4]
|
||
|
mov E,eax
|
||
|
pop ebx
|
||
|
end;
|
||
|
if assigned(E) then
|
||
|
StackDump(E, 0);
|
||
|
end;
|
||
|
if Assigned(RA2) then
|
||
|
RA2(dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
|
||
|
end;
|
||
|
|
||
|
function HookGetExceptionObject(P : PExceptionRecord) : Exception;
|
||
|
begin
|
||
|
Result := SaveGetExceptionObject(P);
|
||
|
StackDump(Result, DWORD(P^.ExceptionAddress)-BaseOfCode);
|
||
|
end;
|
||
|
|
||
|
procedure InitializeEng;
|
||
|
const
|
||
|
ImageNumberofDirectoryEntries = 16;
|
||
|
ImageDirectoryEntryImport = 1;
|
||
|
|
||
|
type
|
||
|
|
||
|
PImageImportByName = ^TImageImportByName;
|
||
|
TImageImportByName = packed record
|
||
|
Hint : WORD;
|
||
|
Name : array[0..255] of char;
|
||
|
end;
|
||
|
|
||
|
PImageThunkData = ^TImageThunkData;
|
||
|
TImageThunkData = packed record
|
||
|
case Integer of
|
||
|
1 : (Funct : ^DWORD);
|
||
|
2 : (Ordinal : DWORD);
|
||
|
3 : (AddressOfData : PImageImportByName);
|
||
|
end;
|
||
|
|
||
|
PImageImportDescriptor = ^TImageImportDescriptor;
|
||
|
TImageImportDescriptor = packed record
|
||
|
Characteristics : DWORD;
|
||
|
TimeDateStamp : DWORD;
|
||
|
ForwarderChain : DWORD;
|
||
|
Name : DWORD;
|
||
|
FirstThunk : PImageThunkData;
|
||
|
end;
|
||
|
|
||
|
PImageDosHeader = ^TImageDosHeader;
|
||
|
TImageDosHeader = packed record
|
||
|
e_magic : WORD;
|
||
|
e_cblp : WORD;
|
||
|
e_cp : WORD;
|
||
|
e_crlc : WORD;
|
||
|
e_cparhdr : WORD;
|
||
|
e_minalloc : WORD;
|
||
|
e_maxalloc : WORD;
|
||
|
e_ss : WORD;
|
||
|
e_sp : WORD;
|
||
|
e_csum : WORD;
|
||
|
e_ip : WORD;
|
||
|
e_cs : WORD;
|
||
|
e_lfarlc : WORD;
|
||
|
e_ovno : WORD;
|
||
|
e_res : array [0..3] of WORD;
|
||
|
e_oemid : WORD;
|
||
|
e_oeminfo : WORD;
|
||
|
e_res2 : array [0..9] of WORD;
|
||
|
e_lfanew : DWORD;
|
||
|
end;
|
||
|
|
||
|
var
|
||
|
OriginalProc : Pointer;
|
||
|
NTHeader : PImageNTHeaders;
|
||
|
ImportDesc : PImageImportDescriptor;
|
||
|
Thunk : PImageThunkData;
|
||
|
|
||
|
begin
|
||
|
RA2 := nil;
|
||
|
OriginalProc := GetProcAddress(GetModuleHandle('kernel32.dll'), 'RaiseException');
|
||
|
|
||
|
if OriginalProc <> nil then begin
|
||
|
NTHeader := PImageNTHeaders(DWORD(hInstance) + PImageDosHeader(hInstance).e_lfanew);
|
||
|
ImportDesc := PImageImportDescriptor(DWORD(hInstance) +
|
||
|
NTHeader.OptionalHeader.DataDirectory[ImageDirectoryEntryImport].VirtualAddress);
|
||
|
|
||
|
BaseOfCode := DWORD(hInstance) + NTHeader.OptionalHeader.BaseOfCode;
|
||
|
TopOfCode := BaseOfCode + NTHeader.OptionalHeader.SizeOfCode;
|
||
|
|
||
|
while ImportDesc.Name <> 0 do begin
|
||
|
if StriComp(PChar(DWORD(hInstance) + ImportDesc.Name), 'kernel32.dll') = 0 then begin
|
||
|
Thunk := PImageThunkData(DWORD(hInstance) + DWORD(ImportDesc.FirstThunk));
|
||
|
while Thunk.Funct <> nil do begin
|
||
|
if Thunk.Funct = OriginalProc then
|
||
|
Thunk.Funct := @LRE;
|
||
|
Inc(Thunk);
|
||
|
end;
|
||
|
end;
|
||
|
Inc(ImportDesc);
|
||
|
end;
|
||
|
RA2 := OriginalProc;
|
||
|
end;
|
||
|
SaveGetExceptionObject := ExceptObjProc;
|
||
|
ExceptObjProc := @HookGetExceptionObject;
|
||
|
end;
|
||
|
|
||
|
initialization
|
||
|
OnHookInstaller := HookInstaller;
|
||
|
{$WARNINGS OFF} { Yeah, we know DebugHook is platform specific }
|
||
|
if DebugHook = 0 then InitializeEng;
|
||
|
{$WARNINGS ON}
|
||
|
|
||
|
finalization
|
||
|
EH.Free;
|
||
|
|
||
|
end.
|