📄 jcldebug.pas
字号:
function MapByLevel(const Level: Integer; var File_, Module_, Proc_: string; var Line_: Integer): Boolean;
function FileOfAddr(const Addr: Pointer): string;
function ModuleOfAddr(const Addr: Pointer): string;
function ProcOfAddr(const Addr: Pointer): string;
function LineOfAddr(const Addr: Pointer): Integer;
function MapOfAddr(const Addr: Pointer; var File_, Module_, Proc_: string; var Line_: Integer): Boolean;
function ExtractClassName(const ProcedureName: string): string;
function ExtractMethodName(const ProcedureName: string): string;
// Original function names, deprecated will be removed in V2.0; do not use!
function __FILE__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __MODULE__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __PROC__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __LINE__(const Level: Integer = 0): Integer; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __MAP__(const Level: Integer; var _File, _Module, _Proc: string; var _Line: Integer): Boolean; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __FILE_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __MODULE_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __PROC_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __LINE_OF_ADDR__(const Addr: Pointer): Integer; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
function __MAP_OF_ADDR__(const Addr: Pointer; var _File, _Module, _Proc: string;
var _Line: Integer): Boolean; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
// Stack info routines base list
type
TJclStackBaseList = class(TObjectList)
private
FThreadID: DWORD;
FTimeStamp: TDateTime;
public
constructor Create;
property ThreadID: DWORD read FThreadID;
property TimeStamp: TDateTime read FTimeStamp;
end;
// Stack info routines
type
PDWORDArray = ^TDWORDArray;
TDWORDArray = array [0..(MaxInt - $F) div SizeOf(DWORD)] of DWORD;
PStackFrame = ^TStackFrame;
TStackFrame = record
CallersEBP: DWORD;
CallerAdr: DWORD;
end;
PStackInfo = ^TStackInfo;
TStackInfo = record
CallerAdr: DWORD;
Level: DWORD;
CallersEBP: DWORD;
DumpSize: DWORD;
ParamSize: DWORD;
ParamPtr: PDWORDArray;
case Integer of
0:
(StackFrame: PStackFrame);
1:
(DumpPtr: PByteArray);
end;
TJclStackInfoItem = class(TObject)
private
FStackInfo: TStackInfo;
function GetCallerAdr: Pointer;
function GetLogicalAddress: DWORD;
public
property CallerAdr: Pointer read GetCallerAdr;
property LogicalAddress: DWORD read GetLogicalAddress;
property StackInfo: TStackInfo read FStackInfo;
end;
TJclStackInfoList = class(TJclStackBaseList)
private
FIgnoreLevels: DWORD;
TopOfStack: Cardinal;
BaseOfStack: Cardinal;
FModuleInfoList: TJclModuleInfoList;
function GetItems(Index: Integer): TJclStackInfoItem;
function NextStackFrame(var StackFrame: PStackFrame; var StackInfo: TStackInfo): Boolean;
procedure StoreToList(const StackInfo: TStackInfo);
procedure TraceStackFrames;
procedure TraceStackRaw;
function ValidCallSite(CodeAddr: DWORD; var CallInstructionSize: Cardinal): Boolean;
function ValidStackAddr(StackAddr: DWORD): Boolean;
public
constructor Create(Raw: Boolean; AIgnoreLevels: DWORD; FirstCaller: Pointer);
destructor Destroy; override;
procedure AddToStrings(Strings: TStrings; IncludeModuleName: Boolean = False;
IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;
IncludeVAdress: Boolean = False);
property Items[Index: Integer]: TJclStackInfoItem read GetItems; default;
property IgnoreLevels: DWORD read FIgnoreLevels;
end;
function JclCreateStackList(Raw: Boolean; AIgnoreLevels: DWORD; FirstCaller: Pointer): TJclStackInfoList;
function JclLastExceptStackList: TJclStackInfoList;
function JclLastExceptStackListToStrings(Strings: TStrings; IncludeModuleName: Boolean = False;
IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;
IncludeVAdress: Boolean = False): Boolean;
// Exception frame info routines
type
PJmpInstruction = ^TJmpInstruction;
TJmpInstruction = packed record // from System.pas
OpCode: Byte;
Distance: Longint;
end;
TExcDescEntry = record // from System.pas
VTable: Pointer;
Handler: Pointer;
end;
PExcDesc = ^TExcDesc;
TExcDesc = packed record // from System.pas
JMP: TJmpInstruction;
case Integer of
0:
(Instructions: array [0..0] of Byte);
1:
(Cnt: Integer;
ExcTab: array [0..0] of TExcDescEntry);
end;
PExcFrame = ^TExcFrame;
TExcFrame = record // from System.pas
Next: PExcFrame;
Desc: PExcDesc;
HEBP: Pointer;
case Integer of
0:
();
1:
(ConstructedObject: Pointer);
2:
(SelfOfMethod: Pointer);
end;
PJmpTable = ^TJmpTable;
TJmpTable = packed record
OPCode: Word; // FF 25 = JMP DWORD PTR [$xxxxxxxx], encoded as $25FF
Ptr: Pointer;
end;
TExceptFrameKind =
(efkUnknown, efkFinally, efkAnyException, efkOnException, efkAutoException);
TJclExceptFrame = class(TObject)
private
FExcFrame: PExcFrame;
FFrameKind: TExceptFrameKind;
protected
procedure DoDetermineFrameKind;
public
constructor Create(AExcFrame: PExcFrame);
function Handles(ExceptObj: TObject): Boolean;
function HandlerInfo(ExceptObj: TObject; var HandlerAt: Pointer): Boolean;
function CodeLocation: Pointer;
property ExcFrame: PExcFrame read FExcFrame;
property FrameKind: TExceptFrameKind read FFrameKind;
end;
TJclExceptFrameList = class(TJclStackBaseList)
private
FIgnoreLevels: Integer;
function GetItems(Index: Integer): TJclExceptFrame;
protected
function AddFrame(AFrame: PExcFrame): TJclExceptFrame;
public
constructor Create(AIgnoreLevels: Integer);
procedure TraceExceptionFrames;
property Items[Index: Integer]: TJclExceptFrame read GetItems;
property IgnoreLevels: Integer read FIgnoreLevels write FIgnoreLevels;
end;
function JclCreateExceptFrameList(AIgnoreLevels: Integer): TJclExceptFrameList;
function JclLastExceptFrameList: TJclExceptFrameList;
// Global exceptional stack tracker enable routines and variables
type
TJclStackTrackingOption =
(stStack, stExceptFrame, stRawMode, stAllModules, stStaticModuleList);
TJclStackTrackingOptions = set of TJclStackTrackingOption;
var
JclStackTrackingOptions: TJclStackTrackingOptions = [stStack];
function JclStartExceptionTracking: Boolean;
function JclStopExceptionTracking: Boolean;
function JclExceptionTrackingActive: Boolean;
function JclTrackExceptionsFromLibraries: Boolean;
// Thread exception tracking support
type
TJclDebugThread = class(TThread)
private
FSyncException: Exception;
FThreadName: string;
procedure DoHandleException;
function GetThreadInfo: string;
protected
procedure DoNotify;
procedure DoSyncHandleException; dynamic;
procedure HandleException;
public
constructor Create(Suspended: Boolean; const AThreadName: string = '');
destructor Destroy; override;
property SyncException: Exception read FSyncException;
property ThreadInfo: string read GetThreadInfo;
property ThreadName: string read FThreadName;
end;
TJclDebugThreadNotifyEvent = procedure(Thread: TJclDebugThread) of object;
TJclThreadIDNotifyEvent = procedure(ThreadID: DWORD) of object;
TJclDebugThreadList = class(TObject)
private
FList: TStringList;
FLock: TJclCriticalSection;
FReadLock: TJclCriticalSection;
FRegSyncThreadID: DWORD;
FUnregSyncThreadID: DWORD;
FOnSyncException: TJclDebugThreadNotifyEvent;
FOnThreadRegistered: TJclThreadIDNotifyEvent;
FOnThreadUnregistered: TJclThreadIDNotifyEvent;
procedure DoSyncThreadRegistered;
procedure DoSyncThreadUnregistered;
function GetThreadIDs(Index: Integer): DWORD;
function GetThreadIDCount: Integer;
function GetThreadNames(ThreadID: DWORD; Index: Integer): string;
protected
procedure DoSyncException(Thread: TJclDebugThread);
procedure DoThreadRegistered(Thread: TThread);
procedure DoThreadUnregistered(Thread: TThread);
procedure InternalRegisterThread(Thread: TThread; const ThreadName: string);
procedure InternalUnregisterThread(Thread: TThread);
public
constructor Create;
destructor Destroy; override;
procedure RegisterThread(Thread: TThread; const ThreadName: string);
procedure UnregisterThread(Thread: TThread);
property Lock: TJclCriticalSection read FLock;
property ThreadClassNames[ThreadID: DWORD]: string index 1 read GetThreadNames;
property ThreadIDs[Index: Integer]: DWORD read GetThreadIDs;
property ThreadIDCount: Integer read GetThreadIDCount;
property ThreadInfos[ThreadID: DWORD]: string index 2 read GetThreadNames;
property ThreadNames[ThreadID: DWORD]: string index 0 read GetThreadNames;
property OnSyncException: TJclDebugThreadNotifyEvent read FOnSyncException write FOnSyncException;
property OnThreadRegistered: TJclThreadIDNotifyEvent read FOnThreadRegistered write FOnThreadRegistered;
property OnThreadUnregistered: TJclThreadIDNotifyEvent read FOnThreadUnregistered write FOnThreadUnregistered;
end;
function JclDebugThreadList: TJclDebugThreadList;
// Miscellanuous
{$IFDEF MSWINDOWS}
function EnableCrashOnCtrlScroll(const Enable: Boolean): Boolean;
function IsDebuggerAttached: Boolean;
function IsHandleValid(Handle: THandle): Boolean;
{$ENDIF MSWINDOWS}
{$IFDEF SUPPORTS_EXTSYM}
{$EXTERNALSYM __FILE__}
{$EXTERNALSYM __LINE__}
{$ENDIF SUPPORTS_EXTSYM}
implementation
uses
{$IFDEF MSWINDOWS}
JclRegistry,
{$ENDIF MSWINDOWS}
JclHookExcept, JclLogic, JclStrings, JclSysInfo, JclSysUtils;
//=== Helper assembler routines ==============================================
const
ModuleCodeOffset = $1000;
{$STACKFRAMES OFF}
function GetEBP: Pointer;
asm
MOV EAX, EBP
end;
function GetESP: Pointer;
asm
MOV EAX, ESP
end;
function GetFS: Pointer;
asm
XOR EAX, EAX
MOV EAX, FS:[EAX]
end;
// Reference: Matt Pietrek, MSJ, Under the hood, on TIBs:
// http://www.microsoft.com/MSJ/archive/S2CE.HTM
function GetStackTop: DWORD;
asm
MOV EAX, FS:[4]
end;
{$IFDEF STACKFRAMES_ON}
{$STACKFRAMES ON}
{$ENDIF STACKFRAMES_ON}
//=== Diagnostics ===========================================================
procedure AssertKindOf(const ClassName: string; const Obj: TObject);
var
C: TClass;
begin
if not Obj.ClassNameIs(ClassName) then
begin
C := Obj.ClassParent;
while (C <> nil) and (not C.ClassNameIs(ClassName)) do
C := C.ClassParent;
Assert(C <> nil);
end;
end;
procedure AssertKindOf(const ClassType: TClass; const Obj: TObject);
begin
Assert(Obj.InheritsFrom(ClassType));
end;
procedure Trace(const Msg: string);
begin
OutputDebugString(PChar(StrDoubleQuote(Msg)));
end;
procedure TraceFmt(const Fmt: string; const Args: array of const);
begin
OutputDebugString(PChar(Format(StrDoubleQuote(Fmt), Args)));
end;
procedure TraceLoc(const Msg: string);
begin
OutputDebugString(PChar(Format('%s:%u (%s) "%s"',
[FileByLevel(1), LineByLevel(1), ProcByLevel(1), Msg])));
end;
procedure TraceLocFmt(const Fmt: string; const Args: array of const);
var
S: string;
begin
S := Format('%s:%u (%s) ', [FileByLevel(1), LineByLevel(1), ProcByLevel(1)]) +
Format(StrDoubleQuote(Fmt), Args);
OutputDebugString(PChar(S));
end;
//=== { TJclModuleInfoList } =================================================
constructor TJclModuleInfoList.Create(ADynamicBuild, ASystemModulesOnly: Boolean);
begin
inherited Create(True);
FDynamicBuild := ADynamicBuild;
FSystemModulesOnly := ASystemModulesOnly;
if not FDynamicBuild then
BuildModulesList;
end;
function TJclModuleInfoList.AddModule(Module: HMODULE; SystemModule: Boolean): Boolean;
begin
Result := not IsValidModuleAddress(Pointer(Module)) and
(CreateItemForAddress(Pointer(Module), SystemModule) <> nil);
end;
procedure TJclModuleInfoList.BuildModulesList;
var
List: TStringList;
I: Integer;
CurModule: PLibModule;
begin
if FSystemModulesOnly then
begin
CurModule := LibModuleList;
while CurModule <> nil do
begin
CreateItemForAddress(Pointer(CurModule.Instance), True);
CurModule := CurModule.Next;
end;
end
else
begin
List := TStringList.Create;
try
LoadedModulesList(List, GetCurrentProcessId, True);
for I := 0 to List.Count - 1 do
CreateItemForAddress(List.Objects[I], False);
finally
List.Free;
end;
end;
end;
function TJclModuleInfoList.CreateItemForAddress(Addr: Pointer; SystemModule: Boolean): TJclModuleInfo;
var
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -