lazarus/debugger/breakpropertydlg.pas
2021-01-29 19:59:04 +00:00

469 lines
14 KiB
ObjectPascal

unit BreakPropertyDlg;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
// LCL
Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, ButtonPanel, EditBtn, Spin,
// LazUtils
LazUTF8,
// IdeIntf
IDEHelpIntf,
// DebuggerIntf
DbgIntfDebuggerBase,
// IDE
BreakPropertyDlgGroups, DebuggerDlg, Debugger,
BaseDebugManager, LazarusIDEStrConsts, InputHistory, IDEProcs, EnvironmentOpts;
type
{ TBreakPropertyDlg }
TBreakPropertyDlg = class(TDebuggerDlg)
ButtonPanel: TButtonPanel;
chkTakeSnap: TCheckBox;
chkLogCallStack: TCheckBox;
chkEnableGroups: TCheckBox;
chkDisableGroups: TCheckBox;
chkEvalExpression: TCheckBox;
chkLogMessage: TCheckBox;
chkActionBreak: TCheckBox;
cmbGroup: TComboBox;
edtCondition: TComboBox;
edtEvalExpression: TEdit;
edtLine: TSpinEdit;
edtLogMessage: TEdit;
edtEnableGroups: TEditButton;
edtDisableGroups: TEditButton;
edtAutocontinueMS: TEdit;
edtCounter: TEdit;
edtFilename: TEdit;
gbActions: TGroupBox;
Label1: TLabel;
lblWatchKind: TLabel;
lblWatchScope: TLabel;
lblLogCallStackLimit: TLabel;
lblMS: TLabel;
lblFileName: TLabel;
lblLine: TLabel;
lblCondition: TLabel;
lblHitCount: TLabel;
lblGroup: TLabel;
lblAutoContinue: TLabel;
edtLogCallStack: TSpinEdit;
rbWrite: TRadioButton;
rbRead: TRadioButton;
rbReadWrite: TRadioButton;
rbGlobal: TRadioButton;
rbLocal: TRadioButton;
rgWatchKind: TPanel;
rgWatchScope: TPanel;
procedure btnHelpClick(Sender: TObject);
procedure btnOKClick(Sender: TObject);
procedure BreakPointRemove(const {%H-}ASender: TIDEBreakPoints;
const ABreakpoint: TIDEBreakPoint);
procedure BreakPointUpdate(const {%H-}ASender: TIDEBreakPoints;
const {%H-}ABreakpoint: TIDEBreakPoint);
procedure chkDisableGroupsChange(Sender: TObject);
procedure chkEnableGroupsChange(Sender: TObject);
procedure chkEvalExpressionChange(Sender: TObject);
procedure chkLogCallStackChange(Sender: TObject);
procedure chkLogMessageChange(Sender: TObject);
procedure cmbGroupKeyPress(Sender: TObject; var Key: char);
procedure edtDisableGroupsButtonClick(Sender: TObject);
procedure edtEnableGroupsButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FBreakpointsNotification : TIDEBreakPointsNotification;
FBreakpoint: TIDEBreakPoint;
FUpdatingInfo: Boolean;
protected
procedure DoEndUpdate; override;
procedure UpdateInfo;
public
constructor Create(AOwner: TComponent; ABreakPoint: TIDEBreakPoint);overload;
destructor Destroy; override;
end;
implementation
{$R *.lfm}
{ TBreakPropertyDlg }
procedure TBreakPropertyDlg.BreakPointUpdate(
const ASender: TIDEBreakPoints; const ABreakpoint: TIDEBreakPoint);
begin
UpdateInfo;
end;
procedure TBreakPropertyDlg.chkDisableGroupsChange(Sender: TObject);
begin
edtDisableGroups.Enabled := chkDisableGroups.Checked;
end;
procedure TBreakPropertyDlg.chkEnableGroupsChange(Sender: TObject);
begin
edtEnableGroups.Enabled := chkEnableGroups.Checked;
end;
procedure TBreakPropertyDlg.chkEvalExpressionChange(Sender: TObject);
begin
edtEvalExpression.Enabled := chkEvalExpression.Checked;
end;
procedure TBreakPropertyDlg.chkLogCallStackChange(Sender: TObject);
begin
edtLogCallStack.Enabled := chkLogCallStack.Checked;
end;
procedure TBreakPropertyDlg.chkLogMessageChange(Sender: TObject);
begin
edtLogMessage.Enabled := chkLogMessage.Checked;
end;
procedure TBreakPropertyDlg.cmbGroupKeyPress(Sender: TObject; var Key: char);
begin
if Key = ';' then Key := #0;
end;
procedure TBreakPropertyDlg.edtDisableGroupsButtonClick(Sender: TObject);
var
s: TCaption;
begin
if FBreakpoint = nil then Exit;
s := edtDisableGroups.Text;
if ExecuteBreakPointGroupDlg(FBreakpoint, s, DebugBoss.BreakPointGroups, bgaDisable) = mrok
then edtDisableGroups.Text := s;
end;
procedure TBreakPropertyDlg.edtEnableGroupsButtonClick(Sender: TObject);
var
s: TCaption;
begin
if FBreakpoint = nil then Exit;
s := edtEnableGroups.Text;
if ExecuteBreakPointGroupDlg(FBreakpoint, s, DebugBoss.BreakPointGroups, bgaEnable) = mrok
then edtEnableGroups.Text := s;
end;
procedure TBreakPropertyDlg.FormCreate(Sender: TObject);
begin
edtCondition.DropDownCount := EnvironmentOptions.DropDownCount;
cmbGroup.DropDownCount := EnvironmentOptions.DropDownCount;
end;
procedure TBreakPropertyDlg.btnHelpClick(Sender: TObject);
begin
LazarusHelp.ShowHelpForIDEControl(Self);
end;
procedure TBreakPropertyDlg.BreakPointRemove(
const ASender: TIDEBreakPoints; const ABreakpoint: TIDEBreakPoint);
begin
if ABreakpoint = FBreakpoint
then ModalResult := mrCancel;
end;
procedure TBreakPropertyDlg.btnOKClick(Sender: TObject);
function CheckGroupList(Alist: TStringList): Boolean;
var
i: Integer;
r: TModalResult;
s: String;
NewGroup: TIDEBreakPointGroup;
begin
Result := True;
s := '';
for i := 0 to Alist.Count-1 do begin
if Alist[i] = '' then continue;
if DebugBoss.BreakPointGroups.GetGroupByName(Alist[i]) = nil then
s := s + ', ' + Alist[i];
end;
if s = '' then exit;
delete(s, 1, 2);
r := MessageDlg(Format(dbgBreakPropertyGroupNotFound, [LineEnding, s]),
mtConfirmation, [mbYes, mbIgnore, mbCancel], 0);
if r = mrCancel then exit(False);
if r = mrYes then begin
for i := 0 to Alist.Count-1 do begin
if Alist[i] = '' then continue;
if DebugBoss.BreakPointGroups.GetGroupByName(Alist[i]) = nil then begin
NewGroup := TIDEBreakPointGroup(DebugBoss.BreakPointGroups.Add);
NewGroup.Name := Alist[i];
end;
end;
end;
end;
var
Actions: TIDEBreakPointActions;
GroupName: String;
NewGroup: TIDEBreakPointGroup;
ws: TDBGWatchPointScope;
wk: TDBGWatchPointKind;
i: SizeInt;
EnableGroupList, DisableGroupList: TStringListUTF8Fast;
begin
if FBreakpoint = nil then Exit;
EnableGroupList := TStringListUTF8Fast.Create;
DisableGroupList := TStringListUTF8Fast.Create;
try
EnableGroupList.Delimiter := ';';
DisableGroupList.Delimiter := ';';
EnableGroupList.DelimitedText := edtEnableGroups.Text+';'+edtDisableGroups.Text;
if not CheckGroupList(EnableGroupList) then begin
ModalResult := mrNone;
exit;
end;
EnableGroupList.DelimitedText := edtEnableGroups.Text;
DisableGroupList.DelimitedText := edtDisableGroups.Text;
FBreakpointsNotification.OnUpdate := nil;
case FBreakpoint.Kind of
bpkSource:
begin
// filename + line
FBreakpoint.SetLocation(edtFilename.Text, edtLine.Value);
end;
bpkAddress:
begin
FBreakpoint.SetAddress(StrToQWordDef(edtFilename.Text, 0));
end;
bpkData:
begin
if rbGlobal.Checked
then ws := wpsGlobal
else ws := wpsLocal;
wk := wpkWrite;
if rbRead.Checked
then wk := wpkRead;
if rbReadWrite.Checked
then wk := wpkReadWrite;
FBreakpoint.SetWatch(edtFilename.Text, ws, wk);
end;
end;
// expression
FBreakpoint.Expression := edtCondition.Text;
// hitcount
FBreakpoint.BreakHitCount := StrToIntDef(edtCounter.Text, FBreakpoint.HitCount);
//auto continue
FBreakpoint.AutoContinueTime := StrToIntDef(edtAutocontinueMS.Text, FBreakpoint.AutoContinueTime);
// group
GroupName := cmbGroup.Text;
NewGroup := DebugBoss.BreakPointGroups.GetGroupByName(GroupName);
if not Assigned(NewGroup) and (GroupName <> '') then
begin
NewGroup := TIDEBreakPointGroup(DebugBoss.BreakPointGroups.Add);
NewGroup.Name := GroupName;
end;
FBreakpoint.Group := NewGroup;
// enable groups
for i := 0 to DebugBoss.BreakPointGroups.Count-1 do begin
NewGroup := DebugBoss.BreakPointGroups[i];
if EnableGroupList.IndexOf(NewGroup.Name) >= 0
then FBreakpoint.EnableGroupList.Add(NewGroup)
else FBreakpoint.EnableGroupList.Remove(NewGroup);
end;
// disable groups
for i := 0 to DebugBoss.BreakPointGroups.Count-1 do begin
NewGroup := DebugBoss.BreakPointGroups[i];
if DisableGroupList.IndexOf(NewGroup.Name) >= 0
then FBreakpoint.DisableGroupList.Add(NewGroup)
else FBreakpoint.DisableGroupList.Remove(NewGroup);
end;
// actions
Actions := [];
if chkActionBreak.Checked then Include(Actions, bpaStop);
if chkDisableGroups.Checked then Include(Actions, bpaDisableGroup);
if chkEnableGroups.Checked then Include(Actions, bpaEnableGroup);
if chkEvalExpression.Checked then Include(Actions, bpaEValExpression);
if chkLogMessage.Checked then Include(Actions, bpaLogMessage);
if chkLogCallStack.Checked then Include(Actions, bpaLogCallStack);
if chkTakeSnap.Checked then include(Actions, bpaTakeSnapshot);
FBreakpoint.Actions := Actions;
FBreakpoint.LogEvalExpression := edtEvalExpression.Text;
FBreakpoint.LogMessage := edtLogMessage.Text;
FBreakpoint.LogCallStackLimit := edtLogCallStack.Value;
if edtCondition.Text <> '' then
with InputHistories.HistoryLists.GetList('BreakPointExpression',
True, rltCaseSensitive) do
begin
i := IndexOf(edtCondition.Text);
if i <> -1 then Delete(i);
Insert(0, edtCondition.Text);
end;
finally
EnableGroupList.Free;
DisableGroupList.Free;
end;
end;
procedure TBreakPropertyDlg.DoEndUpdate;
begin
inherited DoEndUpdate;
UpdateInfo;
end;
procedure TBreakPropertyDlg.UpdateInfo;
var
Actions: TIDEBreakPointActions;
I: Integer;
s: String;
begin
FUpdatingInfo := True;
if FBreakpoint = nil then Exit;
case FBreakpoint.Kind of
bpkSource:
begin
// filename
edtFilename.Text := FBreakpoint.Source;
// line
if FBreakpoint.Line > 0
then edtLine.Value := FBreakpoint.Line
else edtLine.Value := 0;
end;
bpkAddress:
begin
edtFilename.Text := '$' + IntToHex(FBreakpoint.Address, 8); // todo: 8/16 depends on platform
end;
bpkData:
begin
edtFilename.Text := FBreakpoint.WatchData;
rbGlobal.Checked := FBreakpoint.WatchScope = wpsGlobal;
rbLocal.Checked := FBreakpoint.WatchScope = wpsLocal;
rbWrite.Checked := FBreakpoint.WatchKind = wpkWrite;
rbRead.Checked := FBreakpoint.WatchKind = wpkRead;
rbReadWrite.Checked := FBreakpoint.WatchKind = wpkReadWrite;
end;
end;
// expression
edtCondition.Text := FBreakpoint.Expression;
// hitcount
edtCounter.Text := IntToStr(FBreakpoint.BreakHitCount);
// auto continue
edtAutocontinueMS.Text := IntToStr(FBreakpoint.AutoContinueTime);
// group
for I := 0 to DebugBoss.BreakPointGroups.Count - 1 do
cmbGroup.Items.Add(DebugBoss.BreakPointGroups[I].Name);
if FBreakpoint.Group = nil
then cmbGroup.Text := ''
else cmbGroup.Text := FBreakpoint.Group.Name;
// enable groups
s := '';
for i := 0 to FBreakpoint.EnableGroupList.Count - 1 do begin
if s <> '' then s := s + ';';
s := s + FBreakpoint.EnableGroupList[i].Name;
end;
edtEnableGroups.Text := s;
// disable groups
s := '';
for i := 0 to FBreakpoint.DisableGroupList.Count - 1 do begin
if s <> '' then s := s + ';';
s := s + FBreakpoint.DisableGroupList[i].Name;
end;
edtDisableGroups.Text := s;
// actions
Actions := FBreakpoint.Actions;
chkActionBreak.Checked := bpaStop in Actions;
chkDisableGroups.Checked := bpaDisableGroup in Actions;
chkEnableGroups.Checked := bpaEnableGroup in Actions;
chkEvalExpression.Checked := bpaEValExpression in Actions;
chkLogMessage.Checked := bpaLogMessage in Actions;
edtLogMessage.Text := FBreakpoint.LogMessage;
edtEvalExpression.Text := FBreakpoint.LogEvalExpression;
chkLogCallStack.Checked := bpaLogCallStack in Actions;
edtLogCallStack.Value := FBreakpoint.LogCallStackLimit;
chkTakeSnap.Checked := bpaTakeSnapshot in Actions;
FUpdatingInfo := False;
end;
constructor TBreakPropertyDlg.Create(AOwner: TComponent; ABreakPoint: TIDEBreakPoint);
begin
inherited Create(AOwner);
Caption := lisBreakPointProperties;
case ABreakPoint.Kind of
bpkSource:
begin
lblFileName.Caption := lisPEFilename;
lblLine.Caption := lisLine;
end;
bpkAddress:
begin
lblFileName.Caption := lisAddress;
lblLine.Visible := False;
edtLine.Visible := False;
edtFilename.ReadOnly := False;
edtFilename.Color := clDefault;
end;
bpkData:
begin
lblFileName.Caption := lisWatchData;
lblLine.Visible := False;
edtLine.Visible := False;
edtFilename.ReadOnly := False;
edtFilename.Color := clDefault;
lblWatchKind.Visible := True;
lblWatchScope.Visible := True;
rgWatchKind.Visible := True;
rgWatchScope.Visible := True;
lblWatchScope.Caption := lisWatchScope;
lblWatchKind.Caption := lisWatchKind;
rbGlobal.Caption := lisWatchScopeGlobal;
rbLocal.Caption := lisWatchScopeLocal;
rbWrite.Caption := lisWatchKindWrite;
rbRead.Caption := lisWatchKindRead;
rbReadWrite.Caption := lisWatchKindReadWrite;
end;
end;
lblCondition.Caption := lisCondition + ':';
lblHitCount.Caption := lisHitCount + ':';
lblAutoContinue.Caption := lisAutoContinueAfter;
lblMS.Caption := lisMS;
lblGroup.Caption := lisGroup + ':';
gbActions.Caption := lisActions;
chkActionBreak.Caption := lisBreak;
chkEnableGroups.Caption := lisEnableGroups;
chkDisableGroups.Caption := lisDisableGroups;
chkEvalExpression.Caption := lisEvalExpression;
chkLogMessage.Caption := lisLogMessage;
chkLogCallStack.Caption := lisLogCallStack;
lblLogCallStackLimit.Caption := lisLogCallStackLimit;
chkTakeSnap.Caption := lisTakeSnapshot;
edtCondition.Items.Assign(InputHistories.HistoryLists.GetList(
'BreakPointExpression', True,rltCaseSensitive));
FBreakpoint := ABreakPoint;
FBreakpointsNotification := TIDEBreakPointsNotification.Create;
FBreakpointsNotification.AddReference;
FBreakpointsNotification.OnUpdate := @BreakPointUpdate;
FBreakpointsNotification.OnRemove := @BreakPointRemove;
UpdateInfo;
ButtonPanel.OKButton.Caption:=lisMenuOk;
ButtonPanel.HelpButton.Caption:=lisMenuHelp;
ButtonPanel.CancelButton.Caption:=lisCancel;
end;
destructor TBreakPropertyDlg.Destroy;
begin
FBreakpointsNotification.OnUpdate := nil;
FBreakpointsNotification.OnRemove := nil;
FBreakpointsNotification.ReleaseReference;
FBreakpointsNotification := nil;
inherited Destroy;
end;
end.