{ $Id$ } { ---------------------------------------- DebuggerDlg.pp - Base class for all debugger related forms ---------------------------------------- @created(Wed Mar 16st WET 2001) @lastmod($Date$) @author(Marc Weustink ) This unit contains the base class for all debugger related dialogs. All common info needed for the IDE is found in this class *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** } unit DebuggerDlg; {$mode objfpc}{$H+} interface uses Classes, // LCL Forms, Controls, LCLProc, // LazUtils LazFileUtils, LazLoggerBase, // IdeIntf IDEImagesIntf, IDECommands, // DebuggerIntf DbgIntfDebuggerBase, DbgIntfMiscClasses, // IDE MainIntf, EditorOptions, BaseDebugManager, Debugger; type { TDebuggerDlg } TDebuggerDlg = class(TForm) private FUpdateCount: integer; protected procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure DoClose(var CloseAction: TCloseAction); override; procedure DoBeginUpdate; virtual; procedure DoEndUpdate; virtual; public procedure BeginUpdate; procedure EndUpdate; function UpdateCount: integer; function IsUpdating: Boolean; private (* provide some common properties *) FSnapshotManager: TSnapshotManager; FSnapshotNotification: TSnapshotNotification; FThreadsMonitor: TIdeThreadsMonitor; FThreadsNotification: TThreadsNotification; FCallStackMonitor: TIdeCallStackMonitor; FCallStackNotification: TCallStackNotification; FLocalsMonitor: TIdeLocalsMonitor; FLocalsNotification: TLocalsNotification; FWatchesMonitor: TIdeWatchesMonitor; FWatchesNotification: TWatchesNotification; FRegistersMonitor: TIdeRegistersMonitor; FRegistersNotification: TRegistersNotification; FBreakPoints: TIDEBreakPoints; FBreakpointsNotification: TIDEBreakPointsNotification; function GetSnapshotNotification: TSnapshotNotification; function GetThreadsNotification: TThreadsNotification; function GetCallStackNotification: TCallStackNotification; function GetLocalsNotification: TLocalsNotification; function GetWatchesNotification: TWatchesNotification; function GetRegistersNotification: TRegistersNotification; function GetBreakpointsNotification: TIDEBreakPointsNotification; procedure SetSnapshotManager(const AValue: TSnapshotManager); procedure SetThreadsMonitor(const AValue: TIdeThreadsMonitor); procedure SetCallStackMonitor(const AValue: TIdeCallStackMonitor); procedure SetLocalsMonitor(const AValue: TIdeLocalsMonitor); procedure SetWatchesMonitor(const AValue: TIdeWatchesMonitor); procedure SetRegistersMonitor(AValue: TIdeRegistersMonitor); procedure SetBreakPoints(const AValue: TIDEBreakPoints); protected procedure JumpToUnitSource(AnUnitInfo: TDebuggerUnitInfo; ALine: Integer); procedure DoWatchesChanged; virtual; // called if the WatchesMonitor object was changed procedure DoRegistersChanged; virtual; // called if the WatchesMonitor object was changed procedure DoBreakPointsChanged; virtual; // called if the BreakPoint(Monitor) object was changed function GetBreakPointImageIndex(ABreakPoint: TIDEBreakPoint; AIsCurLine: Boolean = False): Integer; property SnapshotNotification: TSnapshotNotification read GetSnapshotNotification; property ThreadsNotification: TThreadsNotification read GetThreadsNotification; property CallStackNotification: TCallStackNotification read GetCallStackNotification; property LocalsNotification: TLocalsNotification read GetLocalsNotification; property WatchesNotification: TWatchesNotification read GetWatchesNotification; property RegistersNotification: TRegistersNotification read GetRegistersNotification; property BreakpointsNotification: TIDEBreakPointsNotification read GetBreakpointsNotification; protected // publish as needed property SnapshotManager: TSnapshotManager read FSnapshotManager write SetSnapshotManager; property ThreadsMonitor: TIdeThreadsMonitor read FThreadsMonitor write SetThreadsMonitor; property CallStackMonitor: TIdeCallStackMonitor read FCallStackMonitor write SetCallStackMonitor; property LocalsMonitor: TIdeLocalsMonitor read FLocalsMonitor write SetLocalsMonitor; property WatchesMonitor: TIdeWatchesMonitor read FWatchesMonitor write SetWatchesMonitor; property RegistersMonitor: TIdeRegistersMonitor read FRegistersMonitor write SetRegistersMonitor; property BreakPoints: TIDEBreakPoints read FBreakPoints write SetBreakPoints; public destructor Destroy; override; end; TDebuggerDlgClass = class of TDebuggerDlg; var OnProcessCommand: procedure(Sender: TObject; Command: word; var Handled: boolean) of object; procedure CreateDebugDialog(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean); implementation var DBG_LOCATION_INFO: PLazLoggerLogGroup; BrkImgIdxInitialized: Boolean; ImgBreakPoints: Array [0..10] of Integer; procedure CreateDebugDialog(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean); begin DebugBoss.CreateDebugDialog(Sender, aFormName, AForm, DoDisableAutoSizing); end; { TDebuggerDlg } procedure TDebuggerDlg.BeginUpdate; begin Inc(FUpdateCount); if FUpdateCount = 1 then DoBeginUpdate; end; procedure TDebuggerDlg.EndUpdate; begin if FUpdateCount < 1 then RaiseGDBException('TDebuggerDlg.EndUpdate'); Dec(FUpdateCount); if FUpdateCount = 0 then DoEndUpdate; end; function TDebuggerDlg.UpdateCount: integer; begin Result := FUpdateCount; end; function TDebuggerDlg.IsUpdating: Boolean; begin Result := FUpdateCount > 0; end; function TDebuggerDlg.GetSnapshotNotification: TSnapshotNotification; begin If FSnapshotNotification = nil then begin FSnapshotNotification := TSnapshotNotification.Create; FSnapshotNotification.AddReference; if (FSnapshotManager <> nil) then FSnapshotManager.AddNotification(FSnapshotNotification); end; Result := FSnapshotNotification; end; function TDebuggerDlg.GetRegistersNotification: TRegistersNotification; begin If FRegistersNotification = nil then begin FRegistersNotification := TRegistersNotification.Create; FRegistersNotification.AddReference; if (FRegistersMonitor <> nil) then FRegistersMonitor.AddNotification(FRegistersNotification); end; Result := FRegistersNotification; end; function TDebuggerDlg.GetThreadsNotification: TThreadsNotification; begin if FThreadsNotification = nil then begin FThreadsNotification := TThreadsNotification.Create; FThreadsNotification.AddReference; if (FThreadsMonitor <> nil) then FThreadsMonitor.AddNotification(FThreadsNotification); end; Result := FThreadsNotification; end; function TDebuggerDlg.GetCallStackNotification: TCallStackNotification; begin if FCallStackNotification = nil then begin FCallStackNotification := TCallStackNotification.Create; FCallStackNotification.AddReference; if (FCallStackMonitor <> nil) then FCallStackMonitor.AddNotification(FCallStackNotification); end; Result := FCallStackNotification; end; function TDebuggerDlg.GetLocalsNotification: TLocalsNotification; begin If FLocalsNotification = nil then begin FLocalsNotification := TLocalsNotification.Create; FLocalsNotification.AddReference; if (FLocalsMonitor <> nil) then FLocalsMonitor.AddNotification(FLocalsNotification); end; Result := FLocalsNotification; end; function TDebuggerDlg.GetWatchesNotification: TWatchesNotification; begin If FWatchesNotification = nil then begin FWatchesNotification := TWatchesNotification.Create; FWatchesNotification.AddReference; if (FWatchesMonitor <> nil) then FWatchesMonitor.AddNotification(FWatchesNotification); end; Result := FWatchesNotification; end; function TDebuggerDlg.GetBreakpointsNotification: TIDEBreakPointsNotification; begin If FBreakpointsNotification = nil then begin FBreakpointsNotification := TIDEBreakPointsNotification.Create; FBreakpointsNotification.AddReference; if (FBreakPoints <> nil) then FBreakPoints.AddNotification(FBreakpointsNotification); end; Result := FBreakpointsNotification; end; procedure TDebuggerDlg.SetRegistersMonitor(AValue: TIdeRegistersMonitor); begin if FRegistersMonitor = AValue then exit; BeginUpdate; try if (FRegistersMonitor <> nil) and (FRegistersNotification <> nil) then FRegistersMonitor.RemoveNotification(FRegistersNotification); FRegistersMonitor := AValue; if (FRegistersMonitor <> nil) and (FRegistersNotification <> nil) then FRegistersMonitor.AddNotification(FRegistersNotification); DoRegistersChanged; finally EndUpdate; end; end; procedure TDebuggerDlg.SetSnapshotManager(const AValue: TSnapshotManager); begin if FSnapshotManager = AValue then exit; BeginUpdate; try if (FSnapshotManager <> nil) and (FSnapshotNotification <> nil) then FSnapshotManager.RemoveNotification(FSnapshotNotification); FSnapshotManager := AValue; if (FSnapshotManager <> nil) and (FSnapshotNotification <> nil) then FSnapshotManager.AddNotification(FSnapshotNotification); if assigned(FSnapshotNotification.OnChange) then FSnapshotNotification.OnChange(nil); if assigned(FSnapshotNotification.OnCurrent) then FSnapshotNotification.OnCurrent(nil); finally EndUpdate; end; end; procedure TDebuggerDlg.SetThreadsMonitor(const AValue: TIdeThreadsMonitor); begin if FThreadsMonitor = AValue then exit; BeginUpdate; try if (FThreadsMonitor <> nil) and (FThreadsNotification <> nil) then FThreadsMonitor.RemoveNotification(FThreadsNotification); FThreadsMonitor := AValue; if (FThreadsMonitor <> nil) and (FThreadsNotification <> nil) then FThreadsMonitor.AddNotification(FThreadsNotification); if assigned(FThreadsNotification.OnChange) then FThreadsNotification.OnChange(nil); if assigned(FThreadsNotification.OnCurrent) then FThreadsNotification.OnCurrent(nil); finally EndUpdate; end; end; procedure TDebuggerDlg.SetCallStackMonitor(const AValue: TIdeCallStackMonitor); begin if FCallStackMonitor = AValue then exit; BeginUpdate; try if (FCallStackMonitor <> nil) and (FCallStackNotification <> nil) then FCallStackMonitor.RemoveNotification(FCallStackNotification); FCallStackMonitor := AValue; if (FCallStackMonitor <> nil) and (FCallStackNotification <> nil) then FCallStackMonitor.AddNotification(FCallStackNotification); if assigned(FCallStackNotification.OnChange) then FCallStackNotification.OnChange(nil); if assigned(FCallStackNotification.OnCurrent) then FCallStackNotification.OnCurrent(nil); finally EndUpdate; end; end; procedure TDebuggerDlg.SetLocalsMonitor(const AValue: TIdeLocalsMonitor); begin if FLocalsMonitor = AValue then exit; BeginUpdate; try if (FLocalsMonitor <> nil) and (FLocalsNotification <> nil) then FLocalsMonitor.RemoveNotification(FLocalsNotification); FLocalsMonitor := AValue; if (FLocalsMonitor <> nil) and (FLocalsNotification <> nil) then FLocalsMonitor.AddNotification(FLocalsNotification); if assigned(FLocalsNotification.OnChange) then FLocalsNotification.OnChange(nil); finally EndUpdate; end; end; procedure TDebuggerDlg.SetWatchesMonitor(const AValue: TIdeWatchesMonitor); begin if FWatchesMonitor = AValue then exit; BeginUpdate; try if (FWatchesMonitor <> nil) and (FWatchesNotification <> nil) then FWatchesMonitor.RemoveNotification(FWatchesNotification); FWatchesMonitor := AValue; if (FWatchesMonitor <> nil) and (FWatchesNotification <> nil) then FWatchesMonitor.AddNotification(FWatchesNotification); DoWatchesChanged; finally EndUpdate; end; end; procedure TDebuggerDlg.SetBreakPoints(const AValue: TIDEBreakPoints); begin if FBreakPoints = AValue then exit; BeginUpdate; try if (FBreakPoints <> nil) and (FBreakpointsNotification <> nil) then FBreakPoints.RemoveNotification(FBreakpointsNotification); FBreakPoints := AValue; if (FBreakPoints <> nil) and (FBreakpointsNotification <> nil) then FBreakPoints.AddNotification(FBreakpointsNotification); DoBreakPointsChanged; finally EndUpdate; end; end; procedure TDebuggerDlg.JumpToUnitSource(AnUnitInfo: TDebuggerUnitInfo; ALine: Integer); const JmpFlags: TJumpToCodePosFlags = [jfAddJumpPoint, jfFocusEditor, jfMarkLine, jfMapLineFromDebug, jfSearchVirtualFullPath]; var Filename: String; ok: Boolean; begin if AnUnitInfo = nil then exit; debugln(DBG_LOCATION_INFO, ['JumpToUnitSource AnUnitInfo=', AnUnitInfo.DebugText ]); // avoid any process-messages, so this proc can not be re-entered (avoid opening one files many times) DebugBoss.LockCommandProcessing; try (* Maybe trim the filename here and use jfDoNotExpandFilename ExpandFilename works with the current IDE path, and may be wrong *) // TODO: better detection of unsaved project files if DebugBoss.GetFullFilename(AnUnitInfo, Filename, False) then begin ok := false; if ALine <= 0 then ALine := AnUnitInfo.SrcLine; if FilenameIsAbsolute(Filename) then ok := MainIDEInterface.DoJumpToSourcePosition(Filename, 0, ALine, 0, JmpFlags) = mrOK; if not ok then MainIDEInterface.DoJumpToSourcePosition(Filename, 0, ALine, 0, JmpFlags+[jfDoNotExpandFilename]); end; finally DebugBoss.UnLockCommandProcessing; end; end; procedure TDebuggerDlg.DoWatchesChanged; begin // end; procedure TDebuggerDlg.DoRegistersChanged; begin // end; procedure TDebuggerDlg.DoBreakPointsChanged; begin // end; function TDebuggerDlg.GetBreakPointImageIndex(ABreakPoint: TIDEBreakPoint; AIsCurLine: Boolean = False): Integer; var i: Integer; begin Result := -1; if not BrkImgIdxInitialized then begin ImgBreakPoints[0] := IDEImages.LoadImage('ActiveBreakPoint'); // red dot ImgBreakPoints[1] := IDEImages.LoadImage('InvalidBreakPoint'); // red dot "X" ImgBreakPoints[2] := IDEImages.LoadImage('UnknownBreakPoint'); // red dot "?" ImgBreakPoints[3] := IDEImages.LoadImage('PendingBreakPoint'); // red dot "||" ImgBreakPoints[4] := IDEImages.LoadImage('InactiveBreakPoint');// green dot ImgBreakPoints[5] := IDEImages.LoadImage('InvalidDisabledBreakPoint');// green dot "X" ImgBreakPoints[6] := IDEImages.LoadImage('UnknownDisabledBreakPoint');// green dot "?" ImgBreakPoints[7] := IDEImages.LoadImage('InactiveBreakPoint');// green dot ImgBreakPoints[8] := IDEImages.LoadImage('debugger_current_line'); ImgBreakPoints[9] := IDEImages.LoadImage('debugger_current_line_breakpoint'); ImgBreakPoints[10] := IDEImages.LoadImage('debugger_current_line_disabled_breakpoint'); BrkImgIdxInitialized := True; end; if AIsCurLine then begin if ABreakPoint = nil then Result := ImgBreakPoints[8] else if ABreakPoint.Enabled then Result := ImgBreakPoints[9] else Result := ImgBreakPoints[10]; end else if (ABreakPoint <> nil) then begin if ABreakPoint.Enabled then i := 0 else i := 4; case ABreakPoint.Valid of vsValid: i := i + 0; vsInvalid: i := i + 1; vsUnknown: i := i + 2; vsPending: i := i + 3; // TODO end; Result := ImgBreakPoints[i]; end; end; destructor TDebuggerDlg.Destroy; begin if FSnapshotNotification <> nil then begin; FSnapshotNotification.OnChange := nil; FSnapshotNotification.OnCurrent := nil; end; SetSnapshotManager(nil); ReleaseRefAndNil(FSnapshotNotification); if FThreadsNotification <> nil then begin; FThreadsNotification.OnChange := nil; FThreadsNotification.OnCurrent := nil; end; SetThreadsMonitor(nil); ReleaseRefAndNil(FThreadsNotification); if FCallStackNotification <> nil then begin; FCallStackNotification.OnChange := nil; FCallStackNotification.OnCurrent := nil; end; SetCallStackMonitor(nil); ReleaseRefAndNil(FCallStackNotification); if FLocalsNotification <> nil then begin; FLocalsNotification.OnChange := nil; end; SetLocalsMonitor(nil); ReleaseRefAndNil(FLocalsNotification); if FWatchesNotification <> nil then begin; FWatchesNotification.OnAdd := nil; FWatchesNotification.OnRemove := nil; FWatchesNotification.OnUpdate := nil; end; SetWatchesMonitor(nil); ReleaseRefAndNil(FWatchesNotification); if FRegistersNotification <> nil then begin; FRegistersNotification.OnChange := nil; end; SetRegistersMonitor(nil); ReleaseRefAndNil(FRegistersNotification); if FBreakpointsNotification <> nil then begin; FBreakpointsNotification.OnAdd := nil; FBreakpointsNotification.OnRemove := nil; FBreakpointsNotification.OnUpdate := nil; end; SetBreakPoints(nil); ReleaseRefAndNil(FBreakpointsNotification); inherited Destroy; end; procedure TDebuggerDlg.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var Command: Word; Handled: Boolean; begin Command := EditorOpts.KeyMap.TranslateKey(Key,Shift,TDebuggerDlg); if Assigned(OnProcessCommand) and (Command <> ecNone) and (Command <> ecContextHelp) and(Command <> ecEditContextHelp) then begin Handled:=false; OnProcessCommand(Self,Command,Handled); Key := 0; end; end; (* procedure TDebuggerDlg.SetDebugger(const ADebugger: TDebugger); begin FDebugger := ADebugger; end; *) procedure TDebuggerDlg.DoClose(var CloseAction: TCloseAction); begin CloseAction := caFree; // we default to free inherited DoClose(CloseAction); end; procedure TDebuggerDlg.DoBeginUpdate; begin end; procedure TDebuggerDlg.DoEndUpdate; begin end; initialization DBG_LOCATION_INFO := DebugLogger.FindOrRegisterLogGroup('DBG_LOCATION_INFO' {$IFDEF DBG_LOCATION_INFO} , True {$ENDIF} ); end.