mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-03 00:43:39 +02:00
621 lines
18 KiB
ObjectPascal
621 lines
18 KiB
ObjectPascal
unit DebugThreadCommand;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
FPDbgController,
|
|
FpDbgClasses,
|
|
FpDbgUtil,
|
|
FpDbgInfo,
|
|
FpPascalParser,
|
|
FpPascalBuilder,
|
|
FpErrorMessages,
|
|
DbgIntfDebuggerBase,
|
|
DbgIntfBaseTypes,
|
|
strutils,
|
|
debugthread,
|
|
CustApp,
|
|
SysUtils;
|
|
|
|
type
|
|
|
|
{ TFpDebugThreadCommandList }
|
|
|
|
TFpDebugThreadCommandList = class(TFPList)
|
|
public
|
|
class function instance: TFpDebugThreadCommandList;
|
|
function GetCommandByName(ATextName: string): TFpDebugThreadCommandClass;
|
|
end;
|
|
|
|
{ TFpDebugThreadQuitDebugServerCommand }
|
|
|
|
TFpDebugThreadQuitDebugServerCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function PreExecute(AController: TDbgController; out DoQueueCommand: boolean): boolean; override;
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadSetFilenameCommand }
|
|
|
|
TFpDebugThreadSetFilenameCommand = class(TFpDebugThreadCommand)
|
|
private
|
|
FFileName: string;
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
published
|
|
property Filename: string read FFileName write FFileName;
|
|
end;
|
|
|
|
{ TFpDebugThreadRunCommand }
|
|
|
|
TFpDebugThreadRunCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadContinueCommand }
|
|
|
|
TFpDebugThreadContinueCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadNextCommand }
|
|
|
|
TFpDebugThreadNextCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadStepCommand }
|
|
|
|
TFpDebugThreadStepCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadStepOutCommand }
|
|
|
|
TFpDebugThreadStepOutCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadStepIntoInstrCommand }
|
|
|
|
TFpDebugThreadStepIntoInstrCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadStepOverInstrCommand }
|
|
|
|
TFpDebugThreadStepOverInstrCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadStopCommand }
|
|
|
|
TFpDebugThreadStopCommand = class(TFpDebugThreadCommand)
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
end;
|
|
|
|
{ TFpDebugThreadAddBreakpointCommand }
|
|
|
|
TFpDebugThreadAddBreakpointCommand = class(TFpDebugThreadCommand)
|
|
private
|
|
FFileName: string;
|
|
FLine: integer;
|
|
FBreakPoint: FpDbgClasses.TDbgBreakpoint;
|
|
public
|
|
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
published
|
|
property Filename: string read FFileName write FFileName;
|
|
property Line: integer read FLine write FLine;
|
|
end;
|
|
|
|
{ TFpDebugThreadRemoveBreakpointCommand }
|
|
|
|
TFpDebugThreadRemoveBreakpointCommand = class(TFpDebugThreadCommand)
|
|
private
|
|
FLocationValue: TDBGPtr;
|
|
function GetLocation: string;
|
|
procedure SetLocation(AValue: string);
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
published
|
|
property Location: string read GetLocation write SetLocation;
|
|
end;
|
|
|
|
{ TFpDebugThreadGetLocationInfoCommand }
|
|
|
|
TFpDebugThreadGetLocationInfoCommand = class(TFpDebugThreadCommand)
|
|
private
|
|
FLocationRec: TDBGLocationRec;
|
|
FAddressValue: TDBGPtr;
|
|
function GetAddress: string;
|
|
procedure SetAddress(AValue: string);
|
|
protected
|
|
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
published
|
|
property Address: string read GetAddress write SetAddress;
|
|
end;
|
|
|
|
{ TFpDebugThreadEvaluateCommand }
|
|
|
|
TFpDebugThreadEvaluateCommand = class(TFpDebugThreadCommand)
|
|
private
|
|
FExpression: string;
|
|
FResText: string;
|
|
FValidity: TDebuggerDataState;
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
|
|
published
|
|
property Expression: string read FExpression write FExpression;
|
|
end;
|
|
|
|
{ TFpDebugThreadStackTraceCommand }
|
|
|
|
TFpDebugThreadStackTraceCommand = class(TFpDebugThreadCommand)
|
|
private
|
|
FStackEntryArray: TFpDebugEventCallStackEntryArray;
|
|
public
|
|
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
|
|
class function TextName: string; override;
|
|
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TFpDebugThreadCommandList }
|
|
|
|
var
|
|
GFpDebugThreadCommandList: TFpDebugThreadCommandList = nil;
|
|
|
|
{ TFpDebugThreadStackTraceCommand }
|
|
|
|
function TFpDebugThreadStackTraceCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
var
|
|
ThreadCallStack: TDbgCallstackEntryList;
|
|
i: integer;
|
|
begin
|
|
AController.CurrentProcess.MainThread.PrepareCallStackEntryList;
|
|
ThreadCallStack := AController.CurrentProcess.MainThread.CallStackEntryList;
|
|
SetLength(FStackEntryArray, ThreadCallStack.Count);
|
|
for i := 0 to ThreadCallStack.Count-1 do
|
|
begin
|
|
FStackEntryArray[i].AnAddress:=ThreadCallStack[i].AnAddress;
|
|
FStackEntryArray[i].FrameAdress:=ThreadCallStack[i].FrameAdress;
|
|
FStackEntryArray[i].FunctionName:=ThreadCallStack[i].FunctionName+ThreadCallStack[i].GetParamsAsString;
|
|
FStackEntryArray[i].Line:=ThreadCallStack[i].Line;
|
|
FStackEntryArray[i].SourceFile:=ThreadCallStack[i].SourceFile;
|
|
end;
|
|
// Clear the callstack immediately. Doing this each time the process continous is
|
|
// cumbersome. And the chances that this command is called twice, so that
|
|
// caching the result is usefull, are slim.
|
|
AController.CurrentProcess.MainThread.ClearCallStack;
|
|
DoProcessLoop:=false;
|
|
result := true;
|
|
end;
|
|
|
|
|
|
class function TFpDebugThreadStackTraceCommand.TextName: string;
|
|
begin
|
|
result := 'stacktrace';
|
|
end;
|
|
|
|
procedure TFpDebugThreadStackTraceCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
|
|
begin
|
|
inherited ComposeSuccessEvent(AnEvent);
|
|
AnEvent.StackEntryArray:=FStackEntryArray;
|
|
end;
|
|
|
|
{ TFpDebugThreadEvaluateCommand }
|
|
|
|
procedure TFpDebugThreadEvaluateCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
|
|
begin
|
|
inherited ComposeSuccessEvent(AnEvent);
|
|
AnEvent.Message:=FResText;
|
|
AnEvent.Validity:=FValidity;
|
|
end;
|
|
|
|
function TFpDebugThreadEvaluateCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
var
|
|
AContext: TFpDbgInfoContext;
|
|
APasExpr: TFpPascalExpression;
|
|
ADbgInfo: TDbgInfo;
|
|
Res: Boolean;
|
|
APrettyPrinter: TFpPascalPrettyPrinter;
|
|
ATypeInfo: TDBGType;
|
|
|
|
begin
|
|
Result := False;
|
|
DoProcessLoop:=false;
|
|
ADbgInfo := AController.CurrentProcess.DbgInfo;
|
|
AContext := ADbgInfo.FindContext(AController.CurrentThread.ID, 0, AController.CurrentProcess.GetInstructionPointerRegisterValue);
|
|
if AContext = nil then
|
|
begin
|
|
FValidity:=ddsInvalid;
|
|
exit;
|
|
end;
|
|
|
|
Result := True;
|
|
AContext.MemManager.DefaultContext := AContext;
|
|
APasExpr := TFpPascalExpression.Create(FExpression, AContext);
|
|
try
|
|
APasExpr.ResultValue; // trigger full validation
|
|
if not APasExpr.Valid then
|
|
begin
|
|
FResText := ErrorHandler.ErrorAsString(APasExpr.Error);
|
|
FValidity := ddsError;
|
|
end
|
|
else
|
|
begin
|
|
APrettyPrinter := TFpPascalPrettyPrinter.Create(sizeof(pointer));
|
|
try
|
|
APrettyPrinter.AddressSize:=AContext.SizeOfAddress;
|
|
APrettyPrinter.MemManager := AContext.MemManager;
|
|
Res := APrettyPrinter.PrintValue(FResText, ATypeInfo, APasExpr.ResultValue);
|
|
if Res then
|
|
begin
|
|
FValidity:=ddsValid;
|
|
end
|
|
else
|
|
begin
|
|
FResText := 'Error';
|
|
FValidity:=ddsValid;
|
|
end;
|
|
finally
|
|
APrettyPrinter.Free;
|
|
end;
|
|
end;
|
|
finally
|
|
APasExpr.Free;
|
|
AContext.ReleaseReference;
|
|
end;
|
|
end;
|
|
|
|
class function TFpDebugThreadEvaluateCommand.TextName: string;
|
|
begin
|
|
result := 'evaluate';
|
|
end;
|
|
|
|
{ TFpDebugThreadQuitDebugServerCommand }
|
|
|
|
function TFpDebugThreadQuitDebugServerCommand.PreExecute(AController: TDbgController; out DoQueueCommand: boolean): boolean;
|
|
begin
|
|
DoQueueCommand:=false;
|
|
CustomApplication.Terminate;
|
|
result := true;
|
|
end;
|
|
|
|
function TFpDebugThreadQuitDebugServerCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
begin
|
|
result := true;
|
|
DoProcessLoop := false;
|
|
end;
|
|
|
|
class function TFpDebugThreadQuitDebugServerCommand.TextName: string;
|
|
begin
|
|
result := 'quitdebugserver';
|
|
end;
|
|
|
|
{ TFpDebugThreadRemoveBreakpointCommand }
|
|
|
|
function TFpDebugThreadRemoveBreakpointCommand.GetLocation: string;
|
|
begin
|
|
result := FormatAddress(FLocationValue);
|
|
end;
|
|
|
|
procedure TFpDebugThreadRemoveBreakpointCommand.SetLocation(AValue: string);
|
|
begin
|
|
FLocationValue := Hex2Dec(AValue);
|
|
end;
|
|
|
|
function TFpDebugThreadRemoveBreakpointCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
begin
|
|
result := false;
|
|
DoProcessLoop:=false;
|
|
if not assigned(AController.CurrentProcess) then
|
|
begin
|
|
log('Failed to remove breakpoint: No process', dllInfo);
|
|
exit;
|
|
end;
|
|
if (FLocationValue<>0) then
|
|
result := AController.CurrentProcess.RemoveBreak(FLocationValue)
|
|
else
|
|
log('Failed to remove breakpoint: No location given', dllInfo);
|
|
end;
|
|
|
|
class function TFpDebugThreadRemoveBreakpointCommand.TextName: string;
|
|
begin
|
|
result := 'removebreakpoint';
|
|
end;
|
|
|
|
{ TFpDebugThreadStopCommand }
|
|
|
|
function TFpDebugThreadStopCommand.Execute(AController: TDbgController; out
|
|
DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.Stop;
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadStopCommand.TextName: string;
|
|
begin
|
|
result := 'stop';
|
|
end;
|
|
|
|
{ TFpDebugThreadStepOutCommand }
|
|
|
|
function TFpDebugThreadStepOutCommand.Execute(AController: TDbgController; out
|
|
DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.StepOut;
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadStepOutCommand.TextName: string;
|
|
begin
|
|
result := 'stepout';
|
|
end;
|
|
|
|
{ TFpDebugThreadStepOverInstrCommand }
|
|
|
|
function TFpDebugThreadStepOverInstrCommand.Execute(
|
|
AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.StepOverInstr;
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadStepOverInstrCommand.TextName: string;
|
|
begin
|
|
result := 'stepoverinstr';
|
|
end;
|
|
|
|
{ TFpDebugThreadStepIntoInstrCommand }
|
|
|
|
function TFpDebugThreadStepIntoInstrCommand.Execute(
|
|
AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.StepIntoInstr;
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadStepIntoInstrCommand.TextName: string;
|
|
begin
|
|
result := 'stepintoinstr';
|
|
end;
|
|
|
|
{ TFpDebugThreadStepCommand }
|
|
|
|
function TFpDebugThreadStepCommand.Execute(AController: TDbgController; out
|
|
DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.Step;
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadStepCommand.TextName: string;
|
|
begin
|
|
result := 'step';
|
|
end;
|
|
|
|
{ TFpDebugThreadNextCommand }
|
|
|
|
function TFpDebugThreadNextCommand.Execute(AController: TDbgController; out
|
|
DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.Next;
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadNextCommand.TextName: string;
|
|
begin
|
|
result := 'next';
|
|
end;
|
|
|
|
{ TFpDebugThreadGetLocationInfoCommand }
|
|
|
|
function TFpDebugThreadGetLocationInfoCommand.GetAddress: string;
|
|
begin
|
|
result := FormatAddress(FAddressValue);
|
|
end;
|
|
|
|
procedure TFpDebugThreadGetLocationInfoCommand.SetAddress(AValue: string);
|
|
begin
|
|
FAddressValue := Hex2Dec(AValue);
|
|
end;
|
|
|
|
procedure TFpDebugThreadGetLocationInfoCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
|
|
begin
|
|
inherited ComposeSuccessEvent(AnEvent);
|
|
AnEvent.LocationRec:=FLocationRec;
|
|
end;
|
|
|
|
function TFpDebugThreadGetLocationInfoCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
var
|
|
sym, symproc: TFpDbgSymbol;
|
|
begin
|
|
DoProcessLoop:=false;
|
|
result := false;
|
|
if Assigned(AController.CurrentProcess) then
|
|
begin
|
|
FLocationRec.FuncName:='';
|
|
FLocationRec.SrcFile:='';
|
|
FLocationRec.SrcFullName:='';
|
|
FLocationRec.SrcLine:=0;
|
|
|
|
if FAddressValue=0 then
|
|
FLocationRec.Address := AController.CurrentProcess.GetInstructionPointerRegisterValue
|
|
else
|
|
FLocationRec.Address := FAddressValue;
|
|
|
|
sym := AController.CurrentProcess.FindSymbol(FLocationRec.Address);
|
|
if sym = nil then
|
|
Exit;
|
|
|
|
FLocationRec.SrcFile := ExtractFileName(sym.FileName);
|
|
FLocationRec.SrcLine := sym.Line;
|
|
FLocationRec.SrcFullName := sym.FileName;
|
|
|
|
symproc := sym;
|
|
while not (symproc.kind in [skProcedure, skFunction]) do
|
|
symproc := symproc.Parent;
|
|
|
|
if assigned(symproc) then
|
|
FLocationRec.FuncName:=symproc.Name;
|
|
sym.free;
|
|
result := true;
|
|
end;
|
|
end;
|
|
|
|
class function TFpDebugThreadGetLocationInfoCommand.TextName: string;
|
|
begin
|
|
result := 'getlocationinfo'
|
|
end;
|
|
|
|
{ TFpDebugThreadAddBreakpointCommand }
|
|
|
|
procedure TFpDebugThreadAddBreakpointCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
|
|
begin
|
|
inherited ComposeSuccessEvent(AnEvent);
|
|
AnEvent.BreakpointAddr:=FBreakPoint.Location;
|
|
end;
|
|
|
|
function TFpDebugThreadAddBreakpointCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
|
|
begin
|
|
result := false;
|
|
DoProcessLoop:=false;
|
|
if not assigned(AController.CurrentProcess) then
|
|
begin
|
|
log('Failed to add breakpoint: No process', dllInfo);
|
|
exit;
|
|
end;
|
|
if (Filename<>'') and (line>-1) then
|
|
begin
|
|
FBreakPoint := AController.CurrentProcess.AddBreak(FileName, Line);
|
|
result := assigned(FBreakPoint);
|
|
end
|
|
else
|
|
log('Failed to add breakpoint: No filename and line-number given', dllInfo);
|
|
end;
|
|
|
|
class function TFpDebugThreadAddBreakpointCommand.TextName: string;
|
|
begin
|
|
result := 'addbreakpoint';
|
|
end;
|
|
|
|
class function TFpDebugThreadCommandList.instance: TFpDebugThreadCommandList;
|
|
begin
|
|
if not assigned(GFpDebugThreadCommandList) then
|
|
GFpDebugThreadCommandList := TFpDebugThreadCommandList.Create;
|
|
result := GFpDebugThreadCommandList;
|
|
end;
|
|
|
|
function TFpDebugThreadCommandList.GetCommandByName(ATextName: string): TFpDebugThreadCommandClass;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
result := nil;
|
|
for i := 0 to count -1 do
|
|
begin
|
|
if TFpDebugThreadCommandClass(Items[i]).TextName=ATextName then
|
|
result := TFpDebugThreadCommandClass(Items[i]);
|
|
end;
|
|
end;
|
|
|
|
{ TFpDebugThreadContinueCommand }
|
|
|
|
function TFpDebugThreadContinueCommand.Execute(AController: TDbgController; out
|
|
DoProcessLoop: boolean): boolean;
|
|
begin
|
|
DoProcessLoop:=true;
|
|
result := true;
|
|
end;
|
|
|
|
class function TFpDebugThreadContinueCommand.TextName: string;
|
|
begin
|
|
result := 'continue';
|
|
end;
|
|
|
|
{ TFpDebugThreadRunCommand }
|
|
|
|
function TFpDebugThreadRunCommand.Execute(AController: TDbgController; out
|
|
DoProcessLoop: boolean): boolean;
|
|
begin
|
|
DoProcessLoop := AController.Run;
|
|
result := DoProcessLoop;
|
|
end;
|
|
|
|
class function TFpDebugThreadRunCommand.TextName: string;
|
|
begin
|
|
result := 'run';
|
|
end;
|
|
|
|
{ TFpDebugThreadSetFilenameCommand }
|
|
|
|
function TFpDebugThreadSetFilenameCommand.Execute(AController: TDbgController;
|
|
out DoProcessLoop: boolean): boolean;
|
|
begin
|
|
AController.ExecutableFilename:=FFileName;
|
|
DoProcessLoop:=false;
|
|
result:=true;
|
|
end;
|
|
|
|
class function TFpDebugThreadSetFilenameCommand.TextName: string;
|
|
begin
|
|
result := 'filename'
|
|
end;
|
|
|
|
initialization
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadQuitDebugServerCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadSetFilenameCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadRunCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadContinueCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepOverInstrCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepIntoInstrCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadNextCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepOutCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStopCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadAddBreakpointCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadRemoveBreakpointCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadGetLocationInfoCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadEvaluateCommand);
|
|
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStackTraceCommand);
|
|
finalization
|
|
GFpDebugThreadCommandList.Free;
|
|
end.
|
|
|