lldb debugger: breakpoint handling (honour most brk-properties)

git-svn-id: trunk@58435 -
This commit is contained in:
martin 2018-07-03 14:57:35 +00:00
parent e23fd4d433
commit c7974af7b9
3 changed files with 493 additions and 71 deletions

View File

@ -92,6 +92,7 @@ type
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
@ -261,7 +262,7 @@ end;
{ TDBGInstruction }
function TDBGInstruction.GetCommandAsString: String;
function TDBGInstruction.GetCommandAsString(): String;
begin
Result := FCommand;
end;
@ -342,6 +343,11 @@ 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]);

View File

@ -283,23 +283,36 @@ type
TLldbBreakPoint = class(TDBGBreakPoint)
private
FBreakID: Integer;
procedure BreakInstructionFinished(Sender: TObject);
FCurrentInstruction: TLldbInstruction;
FState: (bpDone, bpNeedSet, bpNeedDel);
FUpdateState: set of (buEnabled, buCondition);
procedure InstructionDeleteBreakFinished(Sender: TObject);
procedure InstructionSetBreakFinished(Sender: TObject);
procedure InstructionUpdateBreakFinished(Sender: TObject);
procedure SetBreakPoint;
procedure ReleaseBreakPoint;
procedure UpdateProperties;
procedure DoCurrentInstructionFinished;
procedure CancelCurrentInstruction;
protected
procedure DoStateChange(const AOldState: TDBGState); override;
procedure DoEndUpdate; override;
procedure DoEnableChange; override;
procedure DoExpressionChange; override;
public
// constructor Create(ACollection: TCollection); override;
// destructor Destroy; override;
destructor Destroy; override;
// procedure DoLogExpression(const AnExpression: String); override;
procedure SetLocation(const ASource: String; const ALine: Integer); override;
// procedure SetWatch(const AData: String; const AScope: TDBGWatchPointScope;
// const AKind: TDBGWatchPointKind); override;
end;
{ TLldbBreakPoints }
TLldbBreakPoints = class(TDBGBreakPoints)
protected
// function FindById(AnId: Integer): TGDBMIBreakPoint;
function FindById(AnId: Integer): TLldbBreakPoint;
end;
{%endregion ^^^^^ BreakPoint ^^^^^ }
@ -595,42 +608,192 @@ procedure TLldbBreakPoint.SetBreakPoint;
var
i: Integer;
s: String;
Instr: TLldbInstructionBreakSet;
Instr: TLldbInstruction;
begin
debugln(['TLldbBreakPoint.SetBreakPoint ']);
i := LastPos(PathDelim, Source);
if i > 0 then
s := Copy(Source, i+1, Length(Source))
else
s := Source;
Instr := TLldbInstructionBreakSet.Create(s, Line);
Instr.OnFinish := @BreakInstructionFinished;
// TLldbDebugger(Debugger).QueueCommand();
if not (Debugger.State in [dsPause, dsInternalPause, dsRun]) then
exit;
debugln(['TLldbBreakPoint.SetBreakPoint ']);
if (FCurrentInstruction <> nil) then begin
if FCurrentInstruction.IsRunning then begin
FState := bpNeedSet;
exit;
end
else begin
CancelCurrentInstruction;
end;
end;
FState := bpDone;
if FBreakID <> 0 then begin
UpdateProperties;
exit;
end;
case Kind of
bpkSource: begin
i := LastPos(PathDelim, Source);
if i > 0 then
s := Copy(Source, i+1, Length(Source))
else
s := Source;
Instr := TLldbInstructionBreakSet.Create(s, Line, not Enabled, Expression);
end;
bpkAddress: begin
Instr := TLldbInstructionBreakSet.Create(Address, not Enabled, Expression);
end;
bpkData: begin
if not Enabled then // do not set, if not enabled
exit;
// TODO: scope
// TODO: apply , Expression, not Enabled
Instr := TLldbInstructionWatchSet.Create(WatchData, WatchKind);
end;
end;
Instr.OnFinish := @InstructionSetBreakFinished;
TLldbDebugger(Debugger).FDebugInstructionQueue.QueueInstruction(Instr);
Instr.ReleaseReference;
FCurrentInstruction := Instr;
end;
procedure TLldbBreakPoint.BreakInstructionFinished(Sender: TObject);
procedure TLldbBreakPoint.InstructionSetBreakFinished(Sender: TObject);
begin
if TLldbInstructionBreakSet(Sender).IsSuccess then begin
FBreakID := TLldbInstructionBreakSet(Sender).BreakId;
SetValid(TLldbInstructionBreakSet(Sender).State);
DoCurrentInstructionFinished;
if TLldbInstructionBreakOrWatchSet(Sender).IsSuccess then begin
FBreakID := TLldbInstructionBreakOrWatchSet(Sender).BreakId;
if FState <> bpNeedDel then
SetValid(TLldbInstructionBreakOrWatchSet(Sender).State);
end
else
SetValid(vsInvalid);
if FState = bpNeedDel then
ReleaseBreakPoint;
end;
procedure TLldbBreakPoint.InstructionUpdateBreakFinished(Sender: TObject);
begin
DoCurrentInstructionFinished;
if FState = bpNeedSet then
SetBreakPoint
else
if FState = bpNeedDel then
ReleaseBreakPoint;
end;
procedure TLldbBreakPoint.InstructionDeleteBreakFinished(Sender: TObject);
begin
DoCurrentInstructionFinished;
FBreakID := 0;
if FState = bpNeedSet then
SetBreakPoint;
end;
procedure TLldbBreakPoint.ReleaseBreakPoint;
var
Instr: TLldbInstructionBreakDelete;
Instr: TLldbInstruction;
begin
SetHitCount(0);
if FBreakID <= 0 then exit;
if not (Debugger.State in [dsPause, dsInternalPause, dsRun, dsStop]) then
exit;
if (FCurrentInstruction <> nil) then begin
if (FCurrentInstruction is TLldbInstructionBreakDelete) or
(FCurrentInstruction is TLldbInstructionWatchDelete)
then
exit;
if FCurrentInstruction.IsRunning then begin
FState := bpNeedDel;
exit;
end
else begin
CancelCurrentInstruction;
end;
end;
FState := bpDone;
if FBreakID <= 0 then exit;
SetHitCount(0);
case Kind of
bpkSource, bpkAddress:
Instr := TLldbInstructionBreakDelete.Create(FBreakID);
bpkData:
Instr := TLldbInstructionWatchDelete.Create(FBreakID);
end;
Instr := TLldbInstructionBreakDelete.Create(FBreakID);
// Instr.OnFinish := @BreakInstructionFinished;
TLldbDebugger(Debugger).FDebugInstructionQueue.QueueInstruction(Instr);
Instr.ReleaseReference;
Instr.OnFinish := @InstructionDeleteBreakFinished;
FCurrentInstruction := Instr;
end;
procedure TLldbBreakPoint.UpdateProperties;
var
Instr: TLldbInstruction;
begin
if not (Debugger.State in [dsPause, dsInternalPause, dsRun]) then
exit;
if IsUpdating or (FUpdateState = []) then
exit;
if (FCurrentInstruction <> nil) then begin
if (FCurrentInstruction is TLldbInstructionBreakDelete) or
(FCurrentInstruction is TLldbInstructionWatchDelete)
then
exit; // can not change deleted breakpoint
if FCurrentInstruction.IsRunning then begin
FState := bpNeedSet;
exit;
end
else begin
CancelCurrentInstruction;
end;
end;
FState := bpDone;
if FBreakID <= 0 then begin
SetBreakPoint;
exit;
end;
case Kind of
bpkSource, bpkAddress:
if buCondition in FUpdateState
then Instr := TLldbInstructionBreakModify.Create(FBreakID, not Enabled, Expression)
else Instr := TLldbInstructionBreakModify.Create(FBreakID, not Enabled);
bpkData:
if buCondition in FUpdateState then begin
if Enabled
then SetBreakPoint
else ReleaseBreakPoint;
exit;
end
else Instr := TLldbInstructionWatchModify.Create(FBreakID, Expression);
end;
TLldbDebugger(Debugger).FDebugInstructionQueue.QueueInstruction(Instr);
Instr.OnFinish := @InstructionUpdateBreakFinished;
FCurrentInstruction := Instr;
FUpdateState := [];
end;
procedure TLldbBreakPoint.DoCurrentInstructionFinished;
begin
if FCurrentInstruction <> nil then begin
FCurrentInstruction.OnFinish := nil;
ReleaseRefAndNil(FCurrentInstruction);
end;
end;
procedure TLldbBreakPoint.CancelCurrentInstruction;
begin
if FCurrentInstruction <> nil then begin
FCurrentInstruction.OnFinish := nil;
FCurrentInstruction.Cancel;
ReleaseRefAndNil(FCurrentInstruction);
end;
end;
procedure TLldbBreakPoint.DoStateChange(const AOldState: TDBGState);
@ -640,8 +803,7 @@ begin
dsRun: if AOldState = dsInit then begin
// Disabled data breakpoints: wait until enabled
// Disabled other breakpoints: Give to LLDB to see if they are valid
if (Kind <> bpkData) or Enabled then
SetBreakpoint;
SetBreakpoint;
end;
dsStop: begin
if FBreakID > 0
@ -650,6 +812,39 @@ begin
end;
end;
procedure TLldbBreakPoint.DoEndUpdate;
begin
inherited DoEndUpdate;
UpdateProperties;
end;
procedure TLldbBreakPoint.DoEnableChange;
begin
inherited DoEnableChange;
if Kind = bpkData then begin
if Enabled
then SetBreakPoint
else ReleaseBreakPoint;
exit;
end;
FUpdateState := FUpdateState + [buEnabled];
UpdateProperties;
end;
procedure TLldbBreakPoint.DoExpressionChange;
begin
inherited DoExpressionChange;
FUpdateState := FUpdateState + [buCondition];
UpdateProperties;
end;
destructor TLldbBreakPoint.Destroy;
begin
DoCurrentInstructionFinished;
inherited Destroy;
end;
procedure TLldbBreakPoint.SetLocation(const ASource: String;
const ALine: Integer);
begin
@ -658,6 +853,20 @@ begin
SetBreakPoint;
end;
{ TLldbBreakPoints }
function TLldbBreakPoints.FindById(AnId: Integer): TLldbBreakPoint;
var
i: Integer;
begin
for i := 0 to Count - 1 do begin
Result := TLldbBreakPoint(Items[i]);
if Result.FBreakID = AnId then
exit;
end;
Result := nil;
end;
{%region
*****
***** Register
@ -1051,6 +1260,74 @@ begin
end;
procedure TLldbDebugger.DoAfterLineReceived(var ALine: String);
procedure DoBreakPointHit(AReason: String);
var
i, BrkId: Integer;
BreakPoint: TLldbBreakPoint;
CanContinue: Boolean;
begin
i := pos('.', AReason);
if i = 0 then i := Length(AReason)+1;
BrkId := StrToIntDef(copy(AReason, 12, i-12), -1);
debugln(['DoBreakPointHit ', AReason, ' / ', BrkId]);
if (BrkId >= 0) then
BreakPoint := TLldbBreakPoints(BreakPoints).FindById(BrkId)
else
BreakPoint := nil;
if Assigned(EventLogHandler) then
EventLogHandler.LogEventBreakPointHit(Breakpoint, FCurrentLocation);
if BreakPoint <> nil then begin
if (BreakPoint.Valid = vsPending) then
BreakPoint.SetPendingToValid(vsValid);
try
BreakPoint.AddReference;
// Important: The Queue must be unlocked
// BreakPoint.Hit may evaluate stack and expressions
// SetDebuggerState may evaluate data for Snapshot
CanContinue := False;
BreakPoint.Hit(CanContinue);
if CanContinue
then begin
// Important trigger State => as snapshot is taken in TDebugManager.DebuggerChangeState
SetState(dsInternalPause);
// TODO: handle continue stepping
// TODO: wait for SetLocation / lldb sents the frame info in the next output line
LldbRun;
end
else begin
SetState(dsPause);
end;
finally
BreakPoint.ReleaseReference;
end;
end
else
if (State = dsRun)
then begin
debugln(['********** WARNING: breakpoint hit, but nothing known about it ABreakId=', BrkId]);
//case FTheDebugger.OnFeedback
// (self, Format(gdbmiWarningUnknowBreakPoint,
// [LineEnding, GDBMIBreakPointReasonNames[AReason]]),
// List.Text, ftWarning, [frOk, frStop]
// )
//of
// frOk: begin
SetState(dsPause);
// end;
// frStop: begin
// FTheDebugger.Stop;
// end;
//end;
end;
end;
var
Instr: TLldbInstructionTargetDelete;
found: TStringArray;
@ -1068,8 +1345,11 @@ begin
FCurrentStackFrame := 0;
FDebugInstructionQueue.SetKnownThreadAndFrame(FCurrentThreadId, 0);
Threads.CurrentThreads.CurrentThreadId := FCurrentThreadId;
SetState(dsPause);
ALine := '';
if StrStartsWith(found[1], 'breakpoint ') then
DoBreakPointHit(found[1])
else
SetState(dsPause);
end;
// Process 8888 exited with status = 0 (0x00000000)

View File

@ -102,21 +102,38 @@ type
constructor Create();
end;
{ TLldbInstructionBreakSet }
{ TLldbInstructionBreakOrWatchSet }
TLldbInstructionBreakSet = class(TLldbInstruction)
TLldbInstructionBreakOrWatchSet = class(TLldbInstruction)
private
FBreakId: Integer;
FState: TValidState;
protected
function ProcessInputFromDbg(const AData: String): Boolean; override;
public
constructor Create(AFileName: String; ALine: Integer);
constructor Create(AMethod: String);
property BreakId: Integer read FBreakId;
property State: TValidState read FState;
end;
{ TLldbInstructionBreakSet }
TLldbInstructionBreakSet = class(TLldbInstructionBreakOrWatchSet)
public
constructor Create(AFileName: String; ALine: Integer; ADisabled: Boolean = False; AConditon: String = '');
constructor Create(AMethod: String; ADisabled: Boolean = False; AConditon: String = '');
constructor Create(AnAddress: TDBGPtr; ADisabled: Boolean = False; AConditon: String = '');
end;
{ TLldbInstructionBreakModify }
TLldbInstructionBreakModify = class(TLldbInstruction)
protected
function ProcessInputFromDbg(const AData: String): Boolean; override;
public
constructor Create(AnId: Integer; ADisabled: Boolean);
constructor Create(AnId: Integer; ADisabled: Boolean; AConditon: String);
end;
{ TLldbInstructionBreakDelete }
TLldbInstructionBreakDelete = class(TLldbInstruction)
@ -126,6 +143,31 @@ type
constructor Create(AnId: Integer);
end;
{ TLldbInstructionWatchSet }
TLldbInstructionWatchSet = class(TLldbInstructionBreakOrWatchSet)
public
constructor Create(AWatch: String; AKind: TDBGWatchPointKind);
end;
{ TLldbInstructionWatchModify }
TLldbInstructionWatchModify = class(TLldbInstruction)
protected
function ProcessInputFromDbg(const AData: String): Boolean; override;
public
constructor Create(AnId: Integer; AConditon: String = '');
end;
{ TLldbInstructionWatchDelete }
TLldbInstructionWatchDelete = class(TLldbInstruction)
protected
function ProcessInputFromDbg(const AData: String): Boolean; override;
public
constructor Create(AnId: Integer);
end;
{ TLldbInstructionThreadSelect }
TLldbInstructionThreadSelect = class(TLldbInstruction)
@ -226,6 +268,43 @@ type
implementation
{ TLldbInstructionBreakOrWatchSet }
function TLldbInstructionBreakOrWatchSet.ProcessInputFromDbg(const AData: String
): Boolean;
var
i: Integer;
found, found2: TStringArray;
begin
Result := True;
if StrMatches(AData, ['Breakpoint ',': ', ''], found) then begin
i := StrToIntDef(found[0], -1);
if i = -1 then begin
MarkAsFailed;
exit;
end;
FBreakId:= i;
if StrContains(found[1], 'pending') then
FState := vsPending
else
if StrMatches(found[1], ['', ' locations'], found2) then begin
if StrToIntDef(found2[0], 0) > 0 then
FState := vsValid;
end
else
if StrStartsWith(found[1], 'where = ') then
FState := vsValid;
MarkAsSuccess;
end
//Breakpoint 41: where = lazarus.exe`CREATE + 2029 at synedit.pp:2123, address = 0x00764d2d
//Breakpoint 38: no locations (pending).
//Breakpoint 34: 3 locations.
else
Result := inherited;
end;
{ TLldbInstructionQueue }
function TLldbInstructionQueue.CheckForIgnoredError(const AData: String
@ -437,52 +516,61 @@ end;
{ TLldbInstructionBreakSet }
function TLldbInstructionBreakSet.ProcessInputFromDbg(const AData: String
): Boolean;
var
i: Integer;
found, found2: TStringArray;
begin
Result := True;
if StrMatches(AData, ['Breakpoint ',': ', ''], found) then begin
i := StrToIntDef(found[0], -1);
if i = -1 then begin
MarkAsFailed;
exit;
end;
FBreakId:= i;
if StrContains(found[1], 'pending') then
FState := vsPending
else
if StrMatches(found[1], ['', ' locations'], found2) then begin
if StrToIntDef(found2[0], 0) > 0 then
FState := vsValid;
end
else
if StrStartsWith(found[1], 'where = ') then
FState := vsValid;
MarkAsSuccess;
end
//Breakpoint 41: where = lazarus.exe`CREATE + 2029 at synedit.pp:2123, address = 0x00764d2d
//Breakpoint 38: no locations (pending).
//Breakpoint 34: 3 locations.
else
Result := inherited;
end;
constructor TLldbInstructionBreakSet.Create(AFileName: String; ALine: Integer);
constructor TLldbInstructionBreakSet.Create(AFileName: String; ALine: Integer;
ADisabled: Boolean; AConditon: String);
begin
FState := vsInvalid;
if AConditon <> '' then AConditon := ' --condition ''' + AConditon + '''';
if ADisabled then AConditon := AConditon + ' --disable';
if pos(' ', AFileName) > 0 then
AFileName := ''''+AFileName+'''';
inherited Create(Format('breakpoint set --file %s --line %d', [AFileName, ALine]));
inherited Create(Format('breakpoint set --file %s --line %d', [AFileName, ALine]) + AConditon);
end;
constructor TLldbInstructionBreakSet.Create(AMethod: String);
constructor TLldbInstructionBreakSet.Create(AMethod: String;
ADisabled: Boolean; AConditon: String);
begin
inherited Create(Format('breakpoint set --func %s', [AMethod]));
FState := vsInvalid;
if AConditon <> '' then AConditon := ' --condition ''' + AConditon + '''';
if ADisabled then AConditon := AConditon + ' --disable';
inherited Create(Format('breakpoint set --func %s', [AMethod]) + AConditon);
end;
constructor TLldbInstructionBreakSet.Create(AnAddress: TDBGPtr;
ADisabled: Boolean; AConditon: String);
begin
FState := vsInvalid;
if AConditon <> '' then AConditon := ' --condition ''' + AConditon + '''';
if ADisabled then AConditon := AConditon + ' --disable';
inherited Create(Format('breakpoint set --address %u', [AnAddress]) + AConditon);
end;
{ TLldbInstructionBreakModify }
function TLldbInstructionBreakModify.ProcessInputFromDbg(const AData: String
): Boolean;
begin
Result := inherited ProcessInputFromDbg(AData);
if not Result then
MarkAsSuccess;
end;
constructor TLldbInstructionBreakModify.Create(AnId: Integer; ADisabled: Boolean
);
begin
if ADisabled
then inherited Create(Format('breakpoint modify --disable %d', [AnId]))
else inherited Create(Format('breakpoint modify --enable %d', [AnId]));
end;
constructor TLldbInstructionBreakModify.Create(AnId: Integer;
ADisabled: Boolean; AConditon: String);
begin
AConditon := ' --condition ''' + AConditon + '''';
if ADisabled
then AConditon := ' --disable' + AConditon
else AConditon := ' --enable' + AConditon;
inherited Create(Format('breakpoint modify %s %d', [AConditon, AnId]));
end;
{ TLldbInstructionBreakDelete }
@ -505,6 +593,54 @@ begin
inherited Create(Format('breakpoint delete %d', [AnId]));
end;
{ TLldbInstructionWatchSet }
constructor TLldbInstructionWatchSet.Create(AWatch: String;
AKind: TDBGWatchPointKind);
begin
case AKind of
wpkWrite: inherited Create(Format('watchpoint set variable -w write %s', [AWatch]));
wpkRead: inherited Create(Format('watchpoint set variable -w read %s', [AWatch]));
wpkReadWrite: inherited Create(Format('watchpoint set variable -w read_write %s', [AWatch]));
end;
end;
{ TLldbInstructionWatchModify }
function TLldbInstructionWatchModify.ProcessInputFromDbg(const AData: String
): Boolean;
begin
Result := inherited ProcessInputFromDbg(AData);
if not Result then
MarkAsSuccess;
end;
constructor TLldbInstructionWatchModify.Create(AnId: Integer; AConditon: String
);
begin
inherited Create(Format('watchpoint modify --condition ''%s'' %d', [AConditon, AnId]));
end;
{ TLldbInstructionWatchDelete }
function TLldbInstructionWatchDelete.ProcessInputFromDbg(const AData: String
): Boolean;
begin
Result := inherited ProcessInputFromDbg(AData);
if not Result then // if Result=true then self is destroyed;
MarkAsSuccess;
Result := true;
//TODO: "error: No breakpoints exist to be deleted."
// prevent from failing other instruction
end;
constructor TLldbInstructionWatchDelete.Create(AnId: Integer);
begin
inherited Create(Format('watchpoint delete %d', [AnId]));
end;
{ TLldbInstructionThreadSelect }
function TLldbInstructionThreadSelect.ProcessInputFromDbg(const AData: String