mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-06 08:32:35 +02:00
310 lines
7.3 KiB
ObjectPascal
310 lines
7.3 KiB
ObjectPascal
unit FpDebugDebugger;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
SysUtils,
|
|
Forms,
|
|
LazLogger,
|
|
FpDbgClasses,
|
|
FpDbgInfo,
|
|
DbgIntfBaseTypes,
|
|
DbgIntfDebuggerBase,
|
|
FPDbgController;
|
|
|
|
type
|
|
|
|
{ TFpDebugThread }
|
|
TFpDebugDebugger = class;
|
|
TFpDebugThread = class(TThread)
|
|
private
|
|
FFpDebugDebugger: TFpDebugDebugger;
|
|
procedure DoDebugLoopFinishedASync({%H-}Data: PtrInt);
|
|
public
|
|
constructor Create(AFpDebugDebugger: TFpDebugDebugger);
|
|
destructor Destroy; override;
|
|
procedure Execute; override;
|
|
end;
|
|
|
|
{ TFpDebugDebugger }
|
|
|
|
TFpDebugDebugger = class(TDebuggerIntf)
|
|
private
|
|
FDbgController: TDbgController;
|
|
FFpDebugThread: TFpDebugThread;
|
|
FDebugLoopRunning: boolean;
|
|
procedure FDbgControllerHitBreakpointEvent(var continue: boolean);
|
|
procedure FDbgControllerCreateProcessEvent(var continue: boolean);
|
|
procedure FDbgControllerProcessExitEvent(AExitCode: DWord);
|
|
procedure FDbgControllerExceptionEvent(var continue: boolean);
|
|
protected
|
|
function CreateWatches: TWatchesSupplier; override;
|
|
function RequestCommand(const ACommand: TDBGCommand;
|
|
const AParams: array of const): Boolean; override;
|
|
function ChangeFileName: Boolean; override;
|
|
|
|
procedure OnLog(AString: String);
|
|
procedure StartDebugLoop;
|
|
procedure DebugLoopFinished;
|
|
public
|
|
constructor Create(const AExternalDebugger: String); override;
|
|
destructor Destroy; override;
|
|
function GetLocation: TDBGLocationRec; override;
|
|
class function Caption: String; override;
|
|
class function HasExePath: boolean; override;
|
|
function GetSupportedCommands: TDBGCommands; override;
|
|
end;
|
|
|
|
{ TFPWatches }
|
|
|
|
TFPWatches = class(TWatchesSupplier)
|
|
private
|
|
protected
|
|
function FpDebugger: TFpDebugDebugger;
|
|
//procedure DoStateChange(const AOldState: TDBGState); override;
|
|
procedure InternalRequestData(AWatchValue: TWatchValue); override;
|
|
public
|
|
end;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterDebugger(TFpDebugDebugger);
|
|
end;
|
|
|
|
{ TFPWatches }
|
|
|
|
function TFPWatches.FpDebugger: TFpDebugDebugger;
|
|
begin
|
|
Result := TFpDebugDebugger(Debugger);
|
|
end;
|
|
|
|
procedure TFPWatches.InternalRequestData(AWatchValue: TWatchValue);
|
|
begin
|
|
AWatchValue.Validity := ddsInvalid;
|
|
end;
|
|
|
|
{ TFpDebugThread }
|
|
|
|
procedure TFpDebugThread.DoDebugLoopFinishedASync(Data: PtrInt);
|
|
begin
|
|
FFpDebugDebugger.DebugLoopFinished;
|
|
end;
|
|
|
|
constructor TFpDebugThread.Create(AFpDebugDebugger: TFpDebugDebugger);
|
|
begin
|
|
FFpDebugDebugger := AFpDebugDebugger;
|
|
inherited Create(false);
|
|
end;
|
|
|
|
destructor TFpDebugThread.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TFpDebugThread.Execute;
|
|
begin
|
|
FFpDebugDebugger.FDbgController.ProcessLoop;
|
|
Application.QueueAsyncCall(@DoDebugLoopFinishedASync, 0);
|
|
end;
|
|
|
|
{ TFpDebugDebugger }
|
|
|
|
procedure TFpDebugDebugger.FDbgControllerProcessExitEvent(AExitCode: DWord);
|
|
begin
|
|
SetExitCode(AExitCode);
|
|
DoDbgEvent(ecProcess, etProcessExit, Format('Process exited with exit-code %d',[AExitCode]));
|
|
SetState(dsStop);
|
|
end;
|
|
|
|
procedure TFpDebugDebugger.FDbgControllerExceptionEvent(var continue: boolean);
|
|
begin
|
|
DoException(deInternal, 'unknown', GetLocation, 'Unknown exception', continue);
|
|
if not continue then
|
|
begin
|
|
SetState(dsPause);
|
|
DoCurrent(GetLocation);
|
|
end;
|
|
end;
|
|
|
|
function TFpDebugDebugger.CreateWatches: TWatchesSupplier;
|
|
begin
|
|
Result := TFPWatches.Create(Self);
|
|
end;
|
|
|
|
procedure TFpDebugDebugger.FDbgControllerHitBreakpointEvent(var continue: boolean);
|
|
begin
|
|
BreakPoints[0].Hit(continue);
|
|
SetState(dsPause);
|
|
DoCurrent(GetLocation);
|
|
end;
|
|
|
|
procedure TFpDebugDebugger.FDbgControllerCreateProcessEvent(var continue: boolean);
|
|
var
|
|
i: integer;
|
|
bp: TDBGBreakPoint;
|
|
ibp: FpDbgClasses.TDbgBreakpoint;
|
|
begin
|
|
SetState(dsInit);
|
|
for i := 0 to BreakPoints.Count-1 do
|
|
begin
|
|
bp := BreakPoints.Items[i];
|
|
if bp.Enabled then
|
|
begin
|
|
case bp.Kind of
|
|
bpkAddress: ibp := FDbgController.CurrentProcess.AddBreak(bp.Address);
|
|
bpkSource: ibp := TDbgInstance(FDbgController.CurrentProcess).AddBreak(bp.Source, cardinal(bp.Line));
|
|
else
|
|
Raise Exception.Create('Breakpoints of this kind are not suported.');
|
|
end;
|
|
if not assigned(ibp) then
|
|
begin
|
|
DoDbgOutput('Failed to set breakpoint '+inttostr(bp.ID));
|
|
DoOutput('Failed to set breakpoint '+inttostr(bp.ID));
|
|
//bp.Valid:=vsInvalid;
|
|
end
|
|
//else
|
|
//bp.Valid:=vsValid;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TFpDebugDebugger.RequestCommand(const ACommand: TDBGCommand;
|
|
const AParams: array of const): Boolean;
|
|
begin
|
|
result := False;
|
|
case ACommand of
|
|
dcRun:
|
|
begin
|
|
if not assigned(FDbgController.MainProcess) then
|
|
begin
|
|
FDbgController.ExecutableFilename:=FileName;
|
|
Result := FDbgController.Run;
|
|
if not Result then
|
|
Exit;
|
|
SetState(dsInit);
|
|
end
|
|
else
|
|
begin
|
|
SetState(dsRun);
|
|
end;
|
|
StartDebugLoop;
|
|
end;
|
|
dcStop:
|
|
begin
|
|
FDbgController.Stop;
|
|
result := true;
|
|
end;
|
|
end; {case}
|
|
end;
|
|
|
|
function TFpDebugDebugger.ChangeFileName: Boolean;
|
|
begin
|
|
result := true;
|
|
end;
|
|
|
|
procedure TFpDebugDebugger.OnLog(AString: String);
|
|
begin
|
|
DebugLn(AString);
|
|
end;
|
|
|
|
procedure TFpDebugDebugger.StartDebugLoop;
|
|
begin
|
|
DebugLn('StartDebugLoop');
|
|
FDebugLoopRunning:=true;
|
|
FFpDebugThread := TFpDebugThread.Create(Self);
|
|
end;
|
|
|
|
procedure TFpDebugDebugger.DebugLoopFinished;
|
|
var
|
|
Cont: boolean;
|
|
begin
|
|
FFpDebugThread.WaitFor;
|
|
FFpDebugThread.Free;
|
|
FDebugLoopRunning:=false;
|
|
DebugLn('DebugLoopFinished');
|
|
|
|
FDbgController.SendEvents(Cont);
|
|
|
|
if Cont then
|
|
begin
|
|
SetState(dsRun);
|
|
StartDebugLoop;
|
|
end
|
|
end;
|
|
|
|
constructor TFpDebugDebugger.Create(const AExternalDebugger: String);
|
|
begin
|
|
inherited Create(AExternalDebugger);
|
|
FDbgController := TDbgController.Create;
|
|
FDbgController.OnLog:=@OnLog;
|
|
FDbgController.OnCreateProcessEvent:=@FDbgControllerCreateProcessEvent;
|
|
FDbgController.OnHitBreakpointEvent:=@FDbgControllerHitBreakpointEvent;
|
|
FDbgController.OnProcessExitEvent:=@FDbgControllerProcessExitEvent;
|
|
FDbgController.OnExceptionEvent:=@FDbgControllerExceptionEvent;
|
|
end;
|
|
|
|
destructor TFpDebugDebugger.Destroy;
|
|
begin
|
|
FDbgController.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TFpDebugDebugger.GetLocation: TDBGLocationRec;
|
|
var
|
|
sym, symproc: TFpDbgSymbol;
|
|
begin
|
|
if Assigned(FDbgController.CurrentProcess) then
|
|
begin
|
|
result.FuncName:='';
|
|
result.SrcFile:='';
|
|
result.SrcFullName:='';
|
|
result.SrcLine:=0;
|
|
|
|
result.Address := FDbgController.CurrentProcess.GetInstructionPointerRegisterValue;
|
|
|
|
sym := FDbgController.CurrentProcess.FindSymbol(result.Address);
|
|
if sym = nil then
|
|
Exit;
|
|
|
|
result.SrcFile := sym.FileName;
|
|
result.SrcLine := sym.Line;
|
|
result.SrcFullName := sym.FileName;
|
|
|
|
debugln('Locatie: '+sym.FileName+':'+sym.Name+':'+inttostr(sym.Line));
|
|
|
|
symproc := sym;
|
|
while not (symproc.kind in [skProcedure, skFunction]) do
|
|
symproc := symproc.Parent;
|
|
|
|
if assigned(symproc) then
|
|
result.FuncName:=symproc.Name;
|
|
end
|
|
else
|
|
result := inherited;
|
|
end;
|
|
|
|
class function TFpDebugDebugger.Caption: String;
|
|
begin
|
|
Result:='FpDebug internal Dwarf-debugger (alfa)';
|
|
end;
|
|
|
|
class function TFpDebugDebugger.HasExePath: boolean;
|
|
begin
|
|
Result:=False;
|
|
end;
|
|
|
|
function TFpDebugDebugger.GetSupportedCommands: TDBGCommands;
|
|
begin
|
|
Result:=[dcRun, dcStop];
|
|
end;
|
|
|
|
end.
|
|
|