lazarus/components/lazdebuggers/lazdebugtestbase/ttestdebuggerclasses.pas

556 lines
14 KiB
ObjectPascal

unit TTestDebuggerClasses;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DbgIntfDebuggerBase, IdeDebuggerBase, Debugger,
IdeDebuggerWatchResult, LazDebuggerIntf, LazDebuggerIntfBaseTypes,
LazDebuggerValueConverter, LazDebuggerTemplate;
type
{ TTestCallStack }
TTestCallStack = class(TCallStackBase)
private
FList: TList;
protected
procedure Clear; virtual;
function GetCount: Integer; override;
function GetEntryBase(AIndex: Integer): TCallStackEntry; override;
//function GetEntry(AIndex: Integer): TIdeCallStackEntry; virtual;
// procedure AddEntry(AnEntry: TIdeCallStackEntry); virtual; // must be added in correct order
// procedure AssignEntriesTo(AnOther: TTestCallStack); virtual;
// public
// procedure SetCountValidity({%H-}AValidity: TDebuggerDataState); override;
// procedure SetHasAtLeastCountInfo({%H-}AValidity: TDebuggerDataState; {%H-}AMinCount: Integer = - 1); override;
// procedure SetCurrentValidity({%H-}AValidity: TDebuggerDataState); override;
public
constructor Create;
destructor Destroy; override;
// procedure Assign(AnOther: TCallStackBase); override;
// procedure PrepareRange({%H-}AIndex, {%H-}ACount: Integer); override;
// procedure ChangeCurrentIndex(ANewIndex: Integer); virtual;
// function HasAtLeastCount(ARequiredMinCount: Integer): TNullableBool; virtual; // Can be faster than getting the full count
// function CountLimited(ALimit: Integer): Integer; override;
// property Entries[AIndex: Integer]: TIdeCallStackEntry read GetEntry;
end;
{ TTestCallStackList }
TTestCallStackList = class(TCallStackList)
protected
function NewEntryForThread(const AThreadId: Integer): TCallStackBase; override;
end;
{ TTestCallStackMonitor }
TTestCallStackMonitor = class(TCallStackMonitor)
protected
function CreateCallStackList: TCallStackList; override;
end;
TTestThreadsMonitor = class;
{ TTestThreads }
TTestThreads = class(TThreads)
private
FMonitor: TTestThreadsMonitor;
FDataValidity: TDebuggerDataState;
public
constructor Create;
function Count: Integer; override;
procedure Clear; override;
procedure SetValidity(AValidity: TDebuggerDataState); override;
property DataValidity: TDebuggerDataState read FDataValidity;
end;
{ TTestThreadsMonitor }
TTestThreadsMonitor = class(TThreadsMonitor)
protected
procedure DoStateEnterPause; override;
function CreateThreads: TThreads; override;
procedure RequestData;
end;
{ TTestWatchValue }
TTestWatchValue = class(specialize TDbgDataRequestTemplateBase<TWatchValue, IDbgWatchValueIntf>, IDbgWatchValueIntf)
private
FCurrentResData: TCurrentResData;
FUpdateCount: Integer;
protected
(* IDbgWatchValueIntf *)
procedure BeginUpdate; reintroduce;
procedure EndUpdate; reintroduce;
function ResData: IDbgWatchDataIntf;
function GetDbgValConverter: ILazDbgValueConvertSelectorIntf;
protected
procedure RequestData;
function GetTypeInfo: TDBGType; override;
function GetValidity: TDebuggerDataState; override;
function GetValue: String; override;
procedure SetValue(AValue: String); override;
public
constructor Create(AOwnerWatch: TWatch;
const AThreadId: Integer;
const AStackFrame: Integer
);
constructor Create(AOwnerWatch: TWatch); override;
destructor Destroy; override;
end;
{ TTestWatchValueList }
TTestWatchValueList = class(TWatchValueList)
protected
function CreateEntry(const {%H-}AThreadId: Integer; const {%H-}AStackFrame: Integer): TWatchValue; override;
end;
{ TTestWatch }
TTestWatch = class(TWatch)
function CreateValueList: TWatchValueList; override;
procedure RequestData(AWatchValue: TTestWatchValue);
public
end;
TTestWatchesMonitor = class;
{ TTestWatches }
TTestWatches = class(TWatches)
protected
FMonitor: TTestWatchesMonitor;
function WatchClass: TWatchClass; override;
procedure RequestData(AWatchValue: TTestWatchValue);
end;
{ TTestWatchesMonitor }
TTestWatchesMonitor = class(TWatchesMonitor)
private
FWatches: TWatches;
protected
procedure DoStateChangeEx(const AOldState, ANewState: TDBGState); override;
procedure RequestData(AWatchValue: TTestWatchValue);
function CreateWatches: TWatches;
public
constructor Create;
destructor Destroy; override;
property Watches: TWatches read FWatches;
end;
TTestRegistersMonitor = class;
{ TTestRegisters }
TTestRegisters = class(TRegisters)
private
FMonitor: TTestRegistersMonitor;
protected
procedure DoDataValidityChanged(AnOldValidity: TDebuggerDataState); override;
public
function Count: Integer; reintroduce; override;
end;
{ TTEstRegistersList }
TTestRegistersList = class(TRegistersList)
private
FMonitor: TTestRegistersMonitor;
protected
function CreateEntry(AThreadId, AStackFrame: Integer): TRegisters; override;
end;
{ TTestRegistersMonitor }
TTestRegistersMonitor = class(TRegistersMonitor)
protected
function CreateRegistersList: TRegistersList; override;
procedure RequestData(ARegisters: TRegisters);
procedure DoStateEnterPause; override;
procedure DoStateLeavePause; override;
end;
implementation
{ TTestCallStack }
procedure TTestCallStack.Clear;
var
i: Integer;
begin
for i := 0 to FList.Count - 1 do
TObject(FList[i]).Free;
FList.Clear;
end;
function TTestCallStack.GetCount: Integer;
begin
Result := FList.Count;
end;
function TTestCallStack.GetEntryBase(AIndex: Integer): TCallStackEntry;
begin
Result := TCallStackEntry(FList[AIndex]);
end;
constructor TTestCallStack.Create;
begin
FList := TList.Create;
inherited Create;
end;
destructor TTestCallStack.Destroy;
begin
inherited Destroy;
Clear;
FreeAndNil(FList);
end;
{ TTestThreads }
constructor TTestThreads.Create;
begin
inherited Create;
FDataValidity := ddsUnknown;
end;
function TTestThreads.Count: Integer;
begin
if (FDataValidity = ddsUnknown) then begin
FDataValidity := ddsRequested;
FMonitor.RequestData;
end;
Result := inherited Count;
end;
procedure TTestThreads.Clear;
begin
FDataValidity := ddsUnknown;
inherited Clear;
end;
procedure TTestThreads.SetValidity(AValidity: TDebuggerDataState);
begin
if FDataValidity = AValidity then exit;
FDataValidity := AValidity;
if FDataValidity = ddsUnknown then Clear;
end;
{ TTestThreadsMonitor }
procedure TTestThreadsMonitor.DoStateEnterPause;
begin
inherited DoStateEnterPause;
TTestThreads(Threads).SetValidity(ddsUnknown);
end;
function TTestThreadsMonitor.CreateThreads: TThreads;
begin
Result := TTestThreads.Create;
TTestThreads(Result).FMonitor := Self;
end;
procedure TTestThreadsMonitor.RequestData;
begin
if Supplier <> nil
then Supplier.RequestMasterData;
end;
{ TTestRegistersMonitor }
function TTestRegistersMonitor.CreateRegistersList: TRegistersList;
begin
Result := TTestRegistersList.Create;
TTestRegistersList(Result).FMonitor := Self;
end;
procedure TTestRegistersMonitor.RequestData(ARegisters: TRegisters);
begin
if Supplier <> nil
then Supplier.RequestData(ARegisters)
else ARegisters.DataValidity := ddsInvalid;
end;
procedure TTestRegistersMonitor.DoStateEnterPause;
begin
inherited DoStateEnterPause;
RegistersList.Clear;
end;
procedure TTestRegistersMonitor.DoStateLeavePause;
begin
inherited DoStateLeavePause;
RegistersList.Clear;
end;
{ TTEstRegistersList }
function TTestRegistersList.CreateEntry(AThreadId, AStackFrame: Integer): TRegisters;
begin
Result := TTestRegisters.Create(AThreadId, AStackFrame);
TTestRegisters(Result).FMonitor := FMonitor;
end;
{ TTestRegisters }
procedure TTestRegisters.DoDataValidityChanged(AnOldValidity: TDebuggerDataState);
begin
inherited DoDataValidityChanged(AnOldValidity);
end;
function TTestRegisters.Count: Integer;
begin
case DataValidity of
ddsUnknown: begin
AddReference;
try
Result := 0;
DataValidity := ddsRequested;
FMonitor.RequestData(Self); // Locals can be cleared, if debugger is "run" again
if DataValidity = ddsValid then Result := inherited Count();
finally
ReleaseReference;
end;
end;
ddsRequested, ddsEvaluating: Result := 0;
ddsValid: Result := inherited Count;
ddsInvalid, ddsError: Result := 0;
end;
end;
{ TTestWatches }
function TTestWatches.WatchClass: TWatchClass;
begin
Result := TTestWatch;
end;
procedure TTestWatches.RequestData(AWatchValue: TTestWatchValue);
begin
TTestWatchesMonitor(FMonitor).RequestData(AWatchValue);
end;
{ TTestWatchesMonitor }
procedure TTestWatchesMonitor.DoStateChangeEx(const AOldState, ANewState: TDBGState);
begin
inherited DoStateChangeEx(AOldState, ANewState);
if ANewState <> dsError then
Watches.ClearValues;
end;
procedure TTestWatchesMonitor.RequestData(AWatchValue: TTestWatchValue);
begin
if Supplier <> nil
then Supplier.RequestData(AWatchValue)
else AWatchValue.Validity := ddsInvalid;
end;
function TTestWatchesMonitor.CreateWatches: TWatches;
begin
Result := TTestWatches.Create;
TTestWatches(Result).FMonitor := Self;
end;
constructor TTestWatchesMonitor.Create;
begin
inherited Create;
FWatches := CreateWatches;
end;
destructor TTestWatchesMonitor.Destroy;
begin
inherited Destroy;
FreeAndNil(FWatches);
end;
{ TTestWatchValue }
procedure TTestWatchValue.BeginUpdate;
begin
AddReference;
inc(FUpdateCount);
end;
procedure TTestWatchValue.EndUpdate;
var
NewValid: TDebuggerDataState;
begin
//assert(Validity = ddsRequested, 'TCurrentWatchValue.EndUpdate: Validity = ddsRequested');
dec(FUpdateCount);
if (FUpdateCount = 0) then begin
NewValid := ddsValid;
FCurrentResData := FCurrentResData.RootResultData;
if (FCurrentResData <> nil) and (FCurrentResData.NewResultData <> nil) then begin
FCurrentResData.Done;
SetResultData(FCurrentResData.NewResultData);
if ResultData.ValueKind = rdkError then
NewValid := ddsError;
FreeAndNil(FCurrentResData);
end
else
NewValid := ddsInvalid;
if Validity = ddsRequested then
SetValidity(NewValid)
else
DoDataValidityChanged(ddsRequested);
end;
ReleaseReference; // Last statemnet, may call Destroy
end;
function TTestWatchValue.ResData: IDbgWatchDataIntf;
begin
if FCurrentResData = nil then
FCurrentResData := TCurrentResData.Create;
Result := FCurrentResData;
end;
function TTestWatchValue.GetDbgValConverter: ILazDbgValueConvertSelectorIntf;
begin
Result := nil;
end;
procedure TTestWatchValue.RequestData;
begin
TTestWatch(Watch).RequestData(self);
end;
function TTestWatchValue.GetTypeInfo: TDBGType;
var
i: Integer;
begin
Result := nil;
if not Watch.Enabled then
exit;
i := DbgStateChangeCounter; // workaround for state changes during TWatchValue.GetValue
if Validity = ddsUnknown then begin
Validity := ddsRequested;
RequestData;
if i <> DbgStateChangeCounter then exit;
end;
case Validity of
ddsRequested,
ddsEvaluating: Result := nil;
ddsValid: Result := inherited GetTypeInfo;
ddsInvalid,
ddsError: Result := nil;
end;
end;
function TTestWatchValue.GetValidity: TDebuggerDataState;
begin
if FUpdateCount > 0 then
Result := ddsRequested // prevent reading FValue
else
Result := inherited GetValidity;
end;
function TTestWatchValue.GetValue: String;
var
i: Integer;
begin
if not Watch.Enabled then begin
Result := '<disabled>';
exit;
end;
i := DbgStateChangeCounter; // workaround for state changes during TWatchValue.GetValue
if Validity = ddsUnknown then begin
Result := '<evaluating>';
Validity := ddsRequested;
RequestData;
if i <> DbgStateChangeCounter then exit; // in case the debugger did run.
// TODO: The watch can also be deleted by the user
end;
case Validity of
ddsRequested, ddsEvaluating: Result := '<evaluating>';
ddsValid: Result := inherited GetValue;
ddsInvalid: Result := '<invalid>';
ddsError: Result := '<Error: '+ (inherited GetValue) +'>';
end;
end;
procedure TTestWatchValue.SetValue(AValue: String);
begin
BeginUpdate;
ResData.CreatePrePrinted(AValue);
EndUpdate;
end;
constructor TTestWatchValue.Create(AOwnerWatch: TWatch; const AThreadId: Integer;
const AStackFrame: Integer);
begin
inherited Create(AOwnerWatch);
Validity := ddsUnknown;
FDisplayFormat := Watch.DisplayFormat;
FEvaluateFlags := Watch.EvaluateFlags;
FRepeatCount := Watch.RepeatCount;
FThreadId := AThreadId;
FStackFrame := AStackFrame;
end;
constructor TTestWatchValue.Create(AOwnerWatch: TWatch);
begin
inherited Create(AOwnerWatch);
Validity := ddsUnknown;
FDisplayFormat := Watch.DisplayFormat;
FEvaluateFlags := Watch.EvaluateFlags;
FRepeatCount := Watch.RepeatCount;
end;
destructor TTestWatchValue.Destroy;
begin
inherited Destroy;
DoDestroy;
end;
{ TTestWatchValueList }
function TTestWatchValueList.CreateEntry(const AThreadId: Integer;
const AStackFrame: Integer): TWatchValue;
begin
Result := TTestWatchValue.Create(Watch, AThreadId, AStackFrame);
Add(Result);
end;
{ TTestWatch }
function TTestWatch.CreateValueList: TWatchValueList;
begin
Result := TTestWatchValueList.Create(Self);
end;
procedure TTestWatch.RequestData(AWatchValue: TTestWatchValue);
begin
if Collection <> nil
then TTestWatches(Collection).RequestData(AWatchValue)
else AWatchValue.Validity := ddsInvalid;
end;
{ TTestCallStackMonitor }
function TTestCallStackMonitor.CreateCallStackList: TCallStackList;
begin
Result := TTestCallStackList.Create;
end;
{ TTestCallStackList }
function TTestCallStackList.NewEntryForThread(const AThreadId: Integer): TCallStackBase;
begin
Result := TTestCallStack.Create;
Result.ThreadId := AThreadId;
add(Result);
end;
end.