lazarus/components/lazdebuggers/cmdlinedebuggerbase/debuginstructions.pas

679 lines
22 KiB
ObjectPascal

unit DebugInstructions;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LazClasses, LazLoggerBase, debugprocess;
type
{ TDBGInstruction }
TDBGInstructionFlag = (
ifRequiresThread,
ifRequiresStackFrame
);
TDBGInstructionFlags = set of TDBGInstructionFlag;
TDBGInstructionState = (
disNew,
disQueued,
disDataSent,
disContentReceived, //may still wait for closing token
disComleted,
disFailed
);
TDBGInstructionErrorFlag = (
ifeContentError, // the imput from debugger was not in the expected format
ifeWriteError, // writing to debugger (pipe) failed
ifeReadError,
ifeDbgNotRunning,
ifeTimedOut,
ifeRecoveredTimedOut, // not an error
ifeInvalidStackFrame,
ifeInvalidThreadId,
ifeQueueContextError, // The thread or stack command went ok, but something else interfered with setting th econtext
ifeCancelled
);
TDBGInstructionErrorFlags = set of TDBGInstructionErrorFlag;
TDBGInstructionQueue = class;
{ TDBGInstruction }
TDBGInstruction = class(TRefCountedObject)
private
FCommand: String;
FOnFailure: TNotifyEvent;
FOnFinish: TNotifyEvent;
FOnSuccess: TNotifyEvent;
FStackFrame: Integer;
FThreadId: Integer;
FFlags: TDBGInstructionFlags;
FState: TDBGInstructionState;
FErrorFlags: TDBGInstructionErrorFlags;
FTimeOut: Integer;
FQueue: TDBGInstructionQueue;
protected
FNextQueuedInstruction, FPrevQueuedInstruction: TDBGInstruction;
protected
function GetCommandAsString(): String; virtual;
procedure SendCommandDataToDbg(); virtual;
procedure SetStateRunning;
function ProcessInputFromDbg(const AData: String): Boolean; virtual; abstract; // True if data was handled
// function GetTimeOutVerifier: TDBGInstruction; virtual;
procedure Init; virtual;
procedure SetQueued(AQueue: TDBGInstructionQueue);
procedure SetContentReceieved;
procedure InternalCreate(ACommand: String;
AThread, AFrame: Integer; // ifRequiresThread, ifRequiresStackFrame will always be included
AFlags: TDBGInstructionFlags;
ATimeOut: Integer
);
property Queue: TDBGInstructionQueue read FQueue;
property NextInstruction: TDBGInstruction read FNextQueuedInstruction;
public
constructor Create(ACommand: String;
AFlags: TDBGInstructionFlags = [];
ATimeOut: Integer = 0
);
constructor Create(ACommand: String;
AThread: Integer; // ifRequiresThread will always be included
AOtherFlags: TDBGInstructionFlags = [];
ATimeOut: Integer = 0
);
constructor Create(ACommand: String;
AThread, AFrame: Integer; // ifRequiresThread, ifRequiresStackFrame will always be included
AOtherFlags: TDBGInstructionFlags = [];
ATimeOut: Integer = 0
);
procedure Cancel;
function IsSuccess: Boolean;
function IsCompleted: boolean;
function IsRunning: boolean;
procedure MarkAsSuccess; // calls DoInstructionFinished // releases the instruction
procedure MarkAsFailed; // calls DoInstructionFinished // releases the instruction
procedure HandleWriteError; virtual;
procedure HandleReadError; virtual;
procedure HandleTimeOut; virtual;
procedure HandleRecoveredTimeOut; virtual;
procedure HandleNoDbgRunning; virtual;
procedure HandleContentError; virtual;
procedure HandleError(AnError: TDBGInstructionErrorFlag; AMarkAsFailed: Boolean = True); virtual;
function DebugText: String;
property Command: String read GetCommandAsString;
property ThreadId: Integer read FThreadId;
property StackFrame: Integer read FStackFrame;
property Flags: TDBGInstructionFlags read FFlags;
property State: TDBGInstructionState read FState;
property ErrorFlags: TDBGInstructionErrorFlags read FErrorFlags;
property TimeOut: Integer read FTimeOut;
property OnFinish: TNotifyEvent read FOnFinish write FOnFinish;
property OnSuccess: TNotifyEvent read FOnSuccess write FOnSuccess;
property OnFailure: TNotifyEvent read FOnFailure write FOnFailure;
end;
{ TDBGInstructionQueue }
TDBGInstructionQueueFlag = (
iqfValidThread,
iqfValidStackFrame
);
TDBGInstructionQueueFlags = set of TDBGInstructionQueueFlag;
TLineReceivedNotification = procedure(var ALine: String) of object;
TDBGInstructionQueue = class
private
FDebugProcess: TDebugProcess;
FFirstQueuedInstruction, FLastQueuedInstruction: TDBGInstruction;
FCurrentInstruction, FRunningInstruction: TDBGInstruction;
FOnBeginLinesReceived: TNotifyEvent;
FOnEndLinesReceived: TNotifyEvent;
FOnDebuggerTerminated: TNotifyEvent;
FOnAfterHandleLineReceived: TLineReceivedNotification;
FOnBeforeHandleLineReceived: TLineReceivedNotification;
FFlags: TDBGInstructionQueueFlags;
FLockQueueRun: Integer;
FCurrentStackFrame: Integer;
FCurrentThreadId: Integer;
procedure DoDbgLineReceived(Sender: TObject; ALine: String);
procedure DoBeginLinesReceived(Sender: TObject);
procedure DoEndLinesReceived(Sender: TObject);
procedure DoDbgSendError(Sender: TObject; ALine: String);
procedure DoDbgTerminated(Sender: TObject);
procedure RunInstruction(AnInstruction: TDBGInstruction);
protected
procedure RunQueue;
function GetNextInstructionToRun: TDBGInstruction; virtual;
function GetChangeContextInstruction(AnInstruction: TDBGInstruction): TDBGInstruction; virtual;
function GetSelectThreadInstruction(AThreadId: Integer): TDBGInstruction; virtual; abstract;
function GetSelectFrameInstruction(AFrame: Integer): TDBGInstruction; virtual; abstract;
procedure DoBeforeHandleLineReceived(var ALine: String); virtual;
procedure DoAfterHandleLineReceived(var ALine: String); virtual;
procedure DoDebuggerTerminated; virtual;
procedure DoInstructionFinished(Sender: TDBGInstruction); virtual;
procedure SendDataToDBG(ASender: TDBGInstruction; AData: String);
procedure SendDataToDBG(ASender: TDBGInstruction; AData: String; const AValues: array of const);
procedure RemoveInstruction(AnInstruction: TDBGInstruction);
property FirstInstruction: TDBGInstruction read FFirstQueuedInstruction;
public
constructor Create(ADebugProcess: TDebugProcess);
destructor Destroy; override;
procedure LockQueueRun; // prevent next instruction from running, until unLockQueueRun
procedure UnLockQueueRun;
procedure InvalidateThredAndFrame(AStackFrameOnly: Boolean = False);
procedure SetKnownThread(AThread: Integer);
procedure SetKnownThreadAndFrame(AThread, AFrame: Integer);
procedure QueueInstruction(AnInstruction: TDBGInstruction); // Wait for instruction to be finished, not queuing
property CurrentThreadId: Integer read FCurrentThreadId;
property CurrentStackFrame: Integer read FCurrentStackFrame;
property Debugger: TDebugProcess read FDebugProcess;
property CurrentInstruction: TDBGInstruction read FCurrentInstruction;
property RunningInstruction: TDBGInstruction read FRunningInstruction;
public
property OnBeforeHandleLineReceived: TLineReceivedNotification
read FOnBeforeHandleLineReceived write FOnBeforeHandleLineReceived;
property OnAfterHandleLineReceived: TLineReceivedNotification
read FOnAfterHandleLineReceived write FOnAfterHandleLineReceived;
property OnDebuggerTerminated: TNotifyEvent read FOnDebuggerTerminated write FOnDebuggerTerminated;
property OnBeginLinesReceived: TNotifyEvent read FOnBeginLinesReceived write FOnBeginLinesReceived;
property OnEndLinesReceived: TNotifyEvent read FOnEndLinesReceived write FOnEndLinesReceived;
end;
function dbgs(AState: TDBGInstructionState): String; overload;
function dbgs(AFlag: TDBGInstructionQueueFlag): String; overload;
function dbgs(AFlags: TDBGInstructionQueueFlags): String; overload;
function dbgs(AFlag: TDBGInstructionFlag): String; overload;
function dbgs(AFlags: TDBGInstructionFlags): String; overload;
function dbgsInstr(AnInstr: TDBGInstruction): String;
implementation
var
DBGMI_TIMEOUT_DEBUG, DBG_THREAD_AND_FRAME, DBG_VERBOSE: PLazLoggerLogGroup;
function dbgs(AState: TDBGInstructionState): String;
begin
writestr(Result{%H-}, AState);
end;
function dbgs(AFlag: TDBGInstructionQueueFlag): String;
begin
writestr(Result{%H-}, AFlag);
end;
function dbgs(AFlags: TDBGInstructionQueueFlags): String;
var
i: TDBGInstructionQueueFlag;
begin
Result := '';
for i := low(TDBGInstructionQueueFlags) to high(TDBGInstructionQueueFlags) do
if i in AFlags then
if Result = '' then
Result := Result + dbgs(i)
else
Result := Result + ', ' +dbgs(i);
if Result <> '' then
Result := '[' + Result + ']';
end;
function dbgs(AFlag: TDBGInstructionFlag): String;
begin
writestr(Result{%H-}, AFlag);
end;
function dbgs(AFlags: TDBGInstructionFlags): String;
var
i: TDBGInstructionFlag;
begin
Result := '';
for i := low(TDBGInstructionFlags) to high(TDBGInstructionFlags) do
if i in AFlags then
if Result = '' then
Result := Result + dbgs(i)
else
Result := Result + ', ' +dbgs(i);
if Result <> '' then
Result := '[' + Result + ']';
end;
function dbgsInstr(AnInstr: TDBGInstruction): String;
begin
if AnInstr = nil then
Result := 'nil'
else
Result := AnInstr.DebugText;
end;
{ TDBGInstruction }
function TDBGInstruction.GetCommandAsString(): String;
begin
Result := FCommand;
end;
procedure TDBGInstruction.SendCommandDataToDbg();
begin
FQueue.SendDataToDBG(Self, GetCommandAsString);
FState := disDataSent;
end;
procedure TDBGInstruction.SetStateRunning;
begin
Assert(FState=disQueued, 'FState=disQueued');
FState := disDataSent;
end;
procedure TDBGInstruction.Init;
begin
//
end;
procedure TDBGInstruction.SetQueued(AQueue: TDBGInstructionQueue);
begin
FState := disQueued;
FQueue := AQueue;
end;
procedure TDBGInstruction.SetContentReceieved;
begin
FState := disContentReceived;
debugln('disContentReceived');
end;
procedure TDBGInstruction.InternalCreate(ACommand: String; AThread,
AFrame: Integer; AFlags: TDBGInstructionFlags; ATimeOut: Integer);
begin
inherited Create;
AddReference;
FState := disNew;
FCommand := ACommand;
FThreadId := AThread;
FStackFrame := AFrame;
FFlags := AFlags;
FTimeOut := ATimeOut;
Init;
end;
constructor TDBGInstruction.Create(ACommand: String;
AFlags: TDBGInstructionFlags; ATimeOut: Integer);
begin
InternalCreate(ACommand, -1, -1, AFlags, ATimeOut);
end;
constructor TDBGInstruction.Create(ACommand: String; AThread: Integer;
AOtherFlags: TDBGInstructionFlags; ATimeOut: Integer);
begin
InternalCreate(ACommand, AThread, -1,
AOtherFlags + [ifRequiresThread], ATimeOut);
end;
constructor TDBGInstruction.Create(ACommand: String; AThread, AFrame: Integer;
AOtherFlags: TDBGInstructionFlags; ATimeOut: Integer);
begin
InternalCreate(ACommand, AThread, AFrame,
AOtherFlags + [ifRequiresThread, ifRequiresStackFrame], ATimeOut);
end;
procedure TDBGInstruction.Cancel;
begin
debugln(['TDBGInstruction.Cancel ', Command]);
if FState = disQueued then
FQueue.RemoveInstruction(Self)
else
HandleError(ifeCancelled);
end;
function TDBGInstruction.IsSuccess: Boolean;
begin
Result := FState = disComleted;
end;
function TDBGInstruction.IsCompleted: boolean;
begin
Result := (FState = disComleted) or (FState = disFailed);
end;
function TDBGInstruction.IsRunning: boolean;
begin
Result := (FState = disDataSent) or (FState = disContentReceived);
end;
procedure TDBGInstruction.MarkAsSuccess;
begin
debugln(['TDBGInstruction.MarkAsSuccess SUCCESS ', Command]);
FState := disComleted;
if FOnSuccess <> nil then FOnSuccess(Self);
if FOnFinish <> nil then FOnFinish(Self);
If FQueue <> nil then
FQueue.DoInstructionFinished(Self);
end;
procedure TDBGInstruction.MarkAsFailed;
begin
debugln(['TDBGInstruction.MarkAsFailed FAILED ',Command]);
FState := disFailed;
if FOnFailure <> nil then FOnFailure(Self);
if FOnFinish <> nil then FOnFinish(Self);
If FQueue <> nil then
FQueue.DoInstructionFinished(Self);
end;
procedure TDBGInstruction.HandleWriteError;
begin
HandleError(ifeWriteError, False);
// if (FTimeOut = 0) or (FTimeOut > TIMEOUT_AFTER_WRITE_ERROR) then
// FTimeOut := TIMEOUT_AFTER_WRITE_ERROR;
end;
procedure TDBGInstruction.HandleReadError;
begin
HandleError(ifeReadError);
end;
procedure TDBGInstruction.HandleTimeOut;
begin
HandleError(ifeTimedOut);
end;
procedure TDBGInstruction.HandleRecoveredTimeOut;
begin
Include(FErrorFlags, ifeRecoveredTimedOut);
end;
procedure TDBGInstruction.HandleNoDbgRunning;
begin
HandleError(ifeDbgNotRunning);
end;
procedure TDBGInstruction.HandleContentError;
begin
HandleError(ifeContentError);
end;
procedure TDBGInstruction.HandleError(AnError: TDBGInstructionErrorFlag;
AMarkAsFailed: Boolean);
begin
Include(FErrorFlags, AnError);
if AMarkAsFailed then
MarkAsFailed;
end;
function TDBGInstruction.DebugText: String;
begin
Result := ClassName + ': "' + FCommand + '", ' + dbgs(FFlags) + ', ' + dbgs(FState)+' # refcnt '+dbgs(RefCount);
if ifRequiresThread in FFlags then
Result := Result + ' Thr=' + IntToStr(FThreadId);
if ifRequiresStackFrame in FFlags then
Result := Result + ' Frm=' + IntToStr(FStackFrame);
end;
{ TDBGInstructionQueue }
procedure TDBGInstructionQueue.RunQueue;
var
ContextInstr: TDBGInstruction;
begin
if FLockQueueRun > 0 then
exit;
if FRunningInstruction <> nil then
exit;
debugln(['TDBGInstructionQueue.RunQueue ', dbgsInstr(FCurrentInstruction), ' / ', dbgsInstr(FRunningInstruction)]);
if FCurrentInstruction = nil then begin
FCurrentInstruction := GetNextInstructionToRun;
if FCurrentInstruction = nil then
exit;
FCurrentInstruction.AddReference;
RemoveInstruction(FCurrentInstruction);
DebugLnEnter(['>> Current Instruction: ', FCurrentInstruction.Command]);
end;
// set FCurrentInstruction to a pre running state, while changing stack....
ContextInstr := GetChangeContextInstruction(FCurrentInstruction);
if ContextInstr <> nil then begin
ContextInstr.SetQueued(Self);
RunInstruction(ContextInstr);
ContextInstr.ReleaseReference;
exit; // run will be called again
end;
RunInstruction(FCurrentInstruction);
ReleaseRefAndNil(FCurrentInstruction);
end;
procedure TDBGInstructionQueue.DoDbgLineReceived(Sender: TObject; ALine: String
);
begin
// Lock DoInstructionFinished
DoBeforeHandleLineReceived(ALine);
if (FRunningInstruction <> nil) and (ALine <> '') then begin
if FRunningInstruction.ProcessInputFromDbg(ALine) then
ALine := '';
end;
if ALine <> '' then
DoAfterHandleLineReceived(ALine);
if (FRunningInstruction = nil) and (ALine <> '') then
DebugLn(DBG_VERBOSE, ['TDBGInstructionQueue: Got Data, but no command running: ', ALine]);
// unlock DoInstructionFinished
end;
procedure TDBGInstructionQueue.DoBeginLinesReceived(Sender: TObject);
begin
if FOnBeginLinesReceived <> nil then
FOnBeginLinesReceived(Self);
end;
procedure TDBGInstructionQueue.DoEndLinesReceived(Sender: TObject);
begin
if FOnEndLinesReceived <> nil then
FOnEndLinesReceived(Self);
end;
procedure TDBGInstructionQueue.DoDbgSendError(Sender: TObject; ALine: String);
begin
if FRunningInstruction <> nil then
FRunningInstruction.HandleWriteError;
end;
procedure TDBGInstructionQueue.DoDbgTerminated(Sender: TObject);
begin
if FRunningInstruction <> nil then
FRunningInstruction.HandleNoDbgRunning;
DoDebuggerTerminated;
end;
function TDBGInstructionQueue.GetNextInstructionToRun: TDBGInstruction;
begin
Result := FFirstQueuedInstruction;
end;
function TDBGInstructionQueue.GetChangeContextInstruction(
AnInstruction: TDBGInstruction): TDBGInstruction;
begin
Result := nil;
if (ifRequiresThread in AnInstruction.Flags) and
( (CurrentThreadId <> AnInstruction.ThreadId) or not (iqfValidThread in FFlags) )
then begin
Result := GetSelectThreadInstruction(AnInstruction.ThreadId);
exit;
end;
if (ifRequiresStackFrame in AnInstruction.Flags) and
( (CurrentStackFrame <> AnInstruction.StackFrame) or not (iqfValidStackFrame in FFlags) )
then begin
Result := GetSelectFrameInstruction(AnInstruction.StackFrame);
exit;
end;
end;
procedure TDBGInstructionQueue.DoBeforeHandleLineReceived(var ALine: String);
begin
if FOnBeforeHandleLineReceived <> nil then
FOnBeforeHandleLineReceived(ALine);
end;
procedure TDBGInstructionQueue.DoAfterHandleLineReceived(var ALine: String);
begin
if FOnAfterHandleLineReceived <> nil then
FOnAfterHandleLineReceived(ALine);
end;
procedure TDBGInstructionQueue.DoDebuggerTerminated;
begin
if FOnDebuggerTerminated <> nil then
FOnDebuggerTerminated(self);
end;
procedure TDBGInstructionQueue.DoInstructionFinished(Sender: TDBGInstruction);
begin
DebugLnExit(['<< Finished Instruction: ', FRunningInstruction.Command, ' // ', Sender=FRunningInstruction]);
if nil = FCurrentInstruction then DebugLnExit(['<< Current Instruction: ']);
ReleaseRefAndNil(FRunningInstruction);
RunQueue;
end;
procedure TDBGInstructionQueue.RunInstruction(AnInstruction: TDBGInstruction);
begin
DebugLnEnter(['>> Running Instruction: ', AnInstruction.Command]);
FRunningInstruction := AnInstruction;
FRunningInstruction.AddReference;
FRunningInstruction.SendCommandDataToDBG;
end;
procedure TDBGInstructionQueue.RemoveInstruction(AnInstruction: TDBGInstruction
);
begin
If AnInstruction.FPrevQueuedInstruction <> nil then
AnInstruction.FPrevQueuedInstruction.FNextQueuedInstruction := AnInstruction.FNextQueuedInstruction
else begin
assert(FFirstQueuedInstruction = AnInstruction, 'not on queue');
FFirstQueuedInstruction := AnInstruction.FNextQueuedInstruction;
end;
If AnInstruction.FNextQueuedInstruction <> nil then
AnInstruction.FNextQueuedInstruction.FPrevQueuedInstruction := AnInstruction.FPrevQueuedInstruction
else begin
assert(FLastQueuedInstruction = AnInstruction, 'not on queue');
FLastQueuedInstruction := AnInstruction.FPrevQueuedInstruction;
end;
AnInstruction.FPrevQueuedInstruction := nil;
AnInstruction.FNextQueuedInstruction := nil;
AnInstruction.ReleaseReference;
end;
procedure TDBGInstructionQueue.SendDataToDBG(ASender: TDBGInstruction;
AData: String);
begin
FDebugProcess.SendCmdLn(AData);
end;
procedure TDBGInstructionQueue.SendDataToDBG(ASender: TDBGInstruction;
AData: String; const AValues: array of const);
begin
SendDataToDBG(ASender, Format(AData, AValues));
end;
constructor TDBGInstructionQueue.Create(ADebugProcess: TDebugProcess);
begin
FDebugProcess := ADebugProcess;
FDebugProcess.OnLineReceived := @DoDbgLineReceived;
FDebugProcess.OnBeginLinesReceived := @DoBeginLinesReceived;
FDebugProcess.OnEndLinesReceived := @DoEndLinesReceived;
FDebugProcess.OnSendError := @DoDbgSendError;
FDebugProcess.OnTerminate := @DoDbgTerminated;
end;
destructor TDBGInstructionQueue.Destroy;
begin
while FFirstQueuedInstruction <> nil do
RemoveInstruction(FFirstQueuedInstruction);
if FRunningInstruction <> nil then
DoInstructionFinished(FRunningInstruction); // TODO: maybe cancel?
inherited Destroy;
end;
procedure TDBGInstructionQueue.LockQueueRun;
begin
inc(FLockQueueRun);
end;
procedure TDBGInstructionQueue.UnLockQueueRun;
begin
dec(FLockQueueRun);
if FLockQueueRun = 0 then
RunQueue;
end;
procedure TDBGInstructionQueue.InvalidateThredAndFrame(AStackFrameOnly: Boolean);
begin
if AStackFrameOnly then begin
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Invalidating queue''s stack only. Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
FFlags := FFlags - [iqfValidStackFrame];
end
else begin
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Invalidating queue''s current thread and stack. Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
FFlags := FFlags - [iqfValidThread, iqfValidStackFrame];
end;
end;
procedure TDBGInstructionQueue.SetKnownThread(AThread: Integer);
begin
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Setting queue''s current thread and stack. New: Thr=', AThread, ' Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
FCurrentThreadId := AThread;
FFlags := FFlags + [iqfValidThread] - [iqfValidStackFrame];
end;
procedure TDBGInstructionQueue.SetKnownThreadAndFrame(AThread, AFrame: Integer);
begin
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Setting queue''s current thread and stack. New: Thr=', AThread, ' Frm=', AFrame,' Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
FCurrentThreadId := AThread;
FCurrentStackFrame := AFrame;
FFlags := FFlags + [iqfValidThread, iqfValidStackFrame];
end;
procedure TDBGInstructionQueue.QueueInstruction(AnInstruction: TDBGInstruction);
begin
debugln(['TDBGInstructionQueue.QueueInstruction ', AnInstruction.DebugText]);
Assert(AnInstruction.State = disNew, 'queue only new instr');
AnInstruction.AddReference;
AnInstruction.FNextQueuedInstruction := nil;
AnInstruction.FPrevQueuedInstruction := FLastQueuedInstruction;
if FLastQueuedInstruction <> nil then
FLastQueuedInstruction.FNextQueuedInstruction := AnInstruction
else
FFirstQueuedInstruction := AnInstruction;
FLastQueuedInstruction := AnInstruction;
AnInstruction.SetQueued(Self);
RunQueue;
end;
initialization
DBGMI_TIMEOUT_DEBUG := DebugLogger.FindOrRegisterLogGroup('DBGMI_TIMEOUT_DEBUG' {$IFDEF DBGMI_TIMEOUT_DEBUG} , True {$ENDIF} );
DBG_THREAD_AND_FRAME := DebugLogger.FindOrRegisterLogGroup('DBG_THREAD_AND_FRAME' {$IFDEF DBG_THREAD_AND_FRAME} , True {$ENDIF} );
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
end.