mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-11 16:56:01 +02:00
Fpdebug: separate AVR specific code from generic RSP related debug code
This commit is contained in:
parent
9cae9d55d8
commit
5e4596e57a
@ -1,9 +1,7 @@
|
||||
unit FpDbgAvrClasses;
|
||||
|
||||
// Connects to gdbserver instance and communicate over gdb's remote serial protocol (RSP)
|
||||
// in principle possible to connect over any serial text capabile interface such as
|
||||
// tcp/ip, RS-232, pipes etc.
|
||||
// Support only tcp/ip connection for now.
|
||||
{ This unit supports AVR specific code for fpdebug.
|
||||
It communicates with a remote target via remote serial protocol }
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
{$packrecords c}
|
||||
@ -18,34 +16,20 @@ uses
|
||||
FpDbgLoader,
|
||||
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
||||
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, Maps,
|
||||
FpDbgRsp, FpDbgCommon, FpdMemoryTools,
|
||||
FpDbgRsp, FpDbgRspClasses, FpDbgCommon, FpdMemoryTools,
|
||||
FpErrorMessages;
|
||||
|
||||
const
|
||||
// RSP commands
|
||||
Rsp_Status = '?'; // Request break reason - returns either S or T
|
||||
lastCPURegIndex = 31; // After this are SREG, SP and PC
|
||||
// Use as dwarf register indexes
|
||||
SREGindex = 32; // 1 byte
|
||||
SPindex = 33; // 2 bytes
|
||||
PCindex = 34; // 4 bytes
|
||||
RegArrayLength = 35;
|
||||
// Special register names
|
||||
nSREG = 'SReg';
|
||||
nSP = 'SP';
|
||||
nPC = 'PC';
|
||||
|
||||
// Byte level register indexes
|
||||
SPLindex = 33;
|
||||
SPHindex = 34;
|
||||
PC0 = 35;
|
||||
PC1 = 36;
|
||||
PC2 = 37;
|
||||
PC3 = 38;
|
||||
RegArrayByteLength = 39;
|
||||
nSREG = 'SReg';
|
||||
|
||||
type
|
||||
|
||||
{ TAvrMemManager }
|
||||
|
||||
TAvrMemManager = class(TFpDbgMemManager)
|
||||
@ -54,42 +38,24 @@ type
|
||||
|
||||
{ TDbgAvrThread }
|
||||
|
||||
TDbgAvrThread = class(TDbgThread)
|
||||
TDbgAvrThread = class(TDbgRspThread)
|
||||
private
|
||||
FRegs: TInitializedRegisters;
|
||||
FRegsUpdated: boolean; // regs read from target
|
||||
//FRegsChanged: boolean; // write regs to target
|
||||
FExceptionSignal: integer;
|
||||
FIsPaused, FInternalPauseRequested, FIsInInternalPause: boolean;
|
||||
FIsSteppingBreakPoint: boolean;
|
||||
FDidResetInstructionPointer: Boolean;
|
||||
FHasThreadState: boolean;
|
||||
FUnwinder: TDbgStackUnwinder;
|
||||
const
|
||||
lastCPURegIndex = 31; // After this are SREG, SP and PC
|
||||
|
||||
function ReadDebugReg(ind: byte; out AVal: TDbgPtr): boolean;
|
||||
function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||
|
||||
// Cache registers if reported in event
|
||||
// Only cache if all reqisters are reported
|
||||
// if not, request registers from target
|
||||
procedure UpdateStatusFromEvent(event: TStatusEvent);
|
||||
procedure InvalidateRegisters;
|
||||
procedure RefreshRegisterCache;
|
||||
// Byte level register indexes
|
||||
SPLindex = 33;
|
||||
SPHindex = 34;
|
||||
PC0 = 35;
|
||||
PC1 = 36;
|
||||
PC2 = 37;
|
||||
PC3 = 38;
|
||||
RegArrayByteLength = 39;
|
||||
protected
|
||||
function ReadThreadState: boolean;
|
||||
|
||||
function RequestInternalPause: Boolean;
|
||||
function CheckSignalForPostponing(AWaitedStatus: integer): Boolean;
|
||||
procedure ResetPauseStates;
|
||||
procedure RefreshRegisterCache; override;
|
||||
function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||
public
|
||||
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); override;
|
||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
||||
function DetectHardwareWatchpoint: Pointer; override;
|
||||
procedure BeforeContinue; override;
|
||||
procedure LoadRegisterValues; override;
|
||||
|
||||
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
||||
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
||||
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
||||
@ -98,77 +64,18 @@ type
|
||||
|
||||
{ TDbgAvrProcess }
|
||||
|
||||
TDbgAvrProcess = class(TDbgProcess)
|
||||
private
|
||||
FStatus: integer;
|
||||
FProcessStarted: boolean;
|
||||
FIsTerminating: boolean;
|
||||
// RSP communication
|
||||
FConnection: TRspConnection;
|
||||
FRemoteConfig: TRemoteConfig;
|
||||
|
||||
procedure OnForkEvent(Sender : TObject);
|
||||
TDbgAvrProcess = class(TDbgRspProcess)
|
||||
private const
|
||||
FNumRegisters = 35; // r0..r31, SREG, SP, PC
|
||||
protected
|
||||
procedure InitializeLoaders; override;
|
||||
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
|
||||
function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; override;
|
||||
function CreateWatchPointData: TFpWatchPointData; override;
|
||||
public
|
||||
class function isSupported(target: TTargetDescriptor): boolean; override;
|
||||
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
|
||||
AMemManager: TFpDbgMemManager; AProcessConfig: TDbgProcessConfig); override;
|
||||
destructor Destroy; override;
|
||||
function StartInstance(AParams, AnEnvironment: TStrings;
|
||||
AWorkingDirectory, AConsoleTty: string; AFlags: TStartInstanceFlags;
|
||||
out AnError: TFpError): boolean; override;
|
||||
|
||||
// FOR AVR target AAddress could be program or data (SRAM) memory (or EEPROM)
|
||||
// Gnu tools masks data memory with $800000
|
||||
function ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean; override;
|
||||
function WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean; override;
|
||||
|
||||
procedure TerminateProcess; override;
|
||||
function Pause: boolean; override;
|
||||
function Detach(AProcess: TDbgProcess; AThread: TDbgThread): boolean; override;
|
||||
|
||||
function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; override;
|
||||
// Wait for -S or -T response from target, or if connection to target is lost
|
||||
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override;
|
||||
|
||||
// Insert/Delete break points on target
|
||||
// TODO: if target doesn't support break points or have limited break points
|
||||
// then debugger needs to manage insertion/deletion of break points in target memory
|
||||
function InsertBreakInstructionCode(const ALocation: TDBGPtr; out OrigValue: Byte; AMakeTempRemoved: Boolean): Boolean; override;
|
||||
function RemoveBreakInstructionCode(const ALocation: TDBGPtr; const OrigValue: Byte): Boolean; override;
|
||||
|
||||
property RspConfig: TRemoteConfig read FRemoteConfig;
|
||||
end;
|
||||
|
||||
// Lets stick with points 4 for now
|
||||
|
||||
{ TFpRspWatchPointData }
|
||||
|
||||
TRspBreakWatchPoint = record
|
||||
Owner: Pointer;
|
||||
Address: TDBGPtr;
|
||||
Size: Cardinal;
|
||||
Kind: TDBGWatchPointKind;
|
||||
end;
|
||||
|
||||
TFpRspWatchPointData = class(TFpWatchPointData)
|
||||
private
|
||||
FData: array of TRspBreakWatchPoint;
|
||||
function BreakWatchPoint(AnIndex: Integer): TRspBreakWatchPoint;
|
||||
function DataCount: integer;
|
||||
function FindOwner(AnAddr: TDBGPtr): Pointer;
|
||||
public
|
||||
function AddOwnedWatchpoint(AnOwner: Pointer; AnAddr: TDBGPtr; ASize: Cardinal; AReadWrite: TDBGWatchPointKind): boolean; override;
|
||||
function RemoveOwnedWatchpoint(AnOwner: Pointer): boolean; override;
|
||||
property Data[AnIndex: Integer]: TRspBreakWatchPoint read BreakWatchPoint;
|
||||
property Count: integer read DataCount;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@ -201,200 +108,27 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TFpRspWatchPointData }
|
||||
|
||||
function TFpRspWatchPointData.BreakWatchPoint(AnIndex: Integer
|
||||
): TRspBreakWatchPoint;
|
||||
begin
|
||||
if AnIndex < length(FData) then
|
||||
result := FData[AnIndex];
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.DataCount: integer;
|
||||
begin
|
||||
result := length(FData);
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.FindOwner(AnAddr: TDBGPtr): Pointer;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
i := 0;
|
||||
while (i < Count) and not ((AnAddr >= Data[i].Address) and (AnAddr < Data[i].Address + Data[i].Size)) do
|
||||
begin
|
||||
inc(i);
|
||||
end;
|
||||
if i < Count then
|
||||
Result := Data[i].Owner
|
||||
else
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.AddOwnedWatchpoint(AnOwner: Pointer;
|
||||
AnAddr: TDBGPtr; ASize: Cardinal; AReadWrite: TDBGWatchPointKind): boolean;
|
||||
var
|
||||
idx: integer;
|
||||
begin
|
||||
Result := false;
|
||||
idx := length(FData);
|
||||
SetLength(FData, idx+1);
|
||||
FData[idx].Address := AnAddr;
|
||||
FData[idx].Size := ASize;
|
||||
FData[idx].Kind := AReadWrite;
|
||||
FData[idx].Owner := AnOwner;
|
||||
Changed := true;
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.RemoveOwnedWatchpoint(AnOwner: Pointer): boolean;
|
||||
var
|
||||
i, j: integer;
|
||||
begin
|
||||
Result := False;
|
||||
i := 0;
|
||||
while (i < length(FData)) and (FData[i].Owner <> AnOwner) do
|
||||
inc(i);
|
||||
|
||||
if i < length(FData) then begin
|
||||
for j := i+1 to length(FData)-1 do begin
|
||||
FData[j-1] := FData[j];
|
||||
Changed := True;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
SetLength(FData, length(FData)-1);
|
||||
Changed := True;
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TDbgAvrThread }
|
||||
|
||||
procedure TDbgAvrProcess.OnForkEvent(Sender: TObject);
|
||||
begin
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.ReadDebugReg(ind: byte; out AVal: TDbgPtr): boolean;
|
||||
begin
|
||||
Result := false;
|
||||
if TDbgAvrProcess(Process).FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.GetDebugReg called while FIsTerminating is set.')
|
||||
else
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, ['TDbgRspThread.GetDebugReg requesting register: ',ind]);
|
||||
RefreshRegisterCache;
|
||||
if ind < length(FRegs) then
|
||||
begin
|
||||
AVal := FRegs[ind].Value;
|
||||
Result := true;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||
begin
|
||||
if TDbgAvrProcess(Process).FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.WriteDebugReg called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
end
|
||||
else
|
||||
result := TDbgAvrProcess(Process).FConnection.WriteDebugReg(ind, AVal);
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.UpdateStatusFromEvent(event: TStatusEvent);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i := 0 to high(FRegs) do
|
||||
begin
|
||||
FRegs[i].Initialized := event.registers[i].Initialized;
|
||||
if event.registers[i].Initialized then
|
||||
FRegs[i].Value := event.registers[i].Value;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.InvalidateRegisters;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
FRegsUpdated := false;
|
||||
for i := 0 to high(FRegs) do
|
||||
FRegs[i].Initialized := false;
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.RefreshRegisterCache;
|
||||
var
|
||||
regs: TBytes;
|
||||
i: integer;
|
||||
begin
|
||||
if not FRegsUpdated then
|
||||
if not FRegs.Initialized then
|
||||
begin
|
||||
SetLength(regs, RegArrayByteLength);
|
||||
FRegsUpdated := TDbgAvrProcess(Process).FConnection.ReadRegisters(regs[0], length(regs));
|
||||
FRegs.Initialized := TDbgRspProcess(Process).RspConnection.ReadRegisters(regs[0], length(regs));
|
||||
for i := 0 to lastCPURegIndex do
|
||||
begin
|
||||
FRegs[i].Initialized := true;
|
||||
FRegs[i].Value := regs[i];
|
||||
end;
|
||||
FRegs.regs[i] := regs[i];
|
||||
|
||||
FRegs.regs[SREGindex] := regs[SREGindex];
|
||||
// repack according to target endianness
|
||||
FRegs[SPindex].Value := regs[SPLindex] + (regs[SPHindex] shl 8);
|
||||
FRegs[SPHindex].Initialized := true;
|
||||
FRegs[PCindex].Value := regs[PC0] + (regs[PC1] shl 8) + (regs[PC2] shl 16) + (regs[PC3] shl 24);
|
||||
FRegs[PCindex].Initialized := true;
|
||||
FRegs.regs[SPindex] := regs[SPLindex] + (regs[SPHindex] shl 8);
|
||||
FRegs.regs[PCindex] := regs[PC0] + (regs[PC1] shl 8) + (regs[PC2] shl 16) + (regs[PC3] shl 24);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.ReadThreadState: boolean;
|
||||
begin
|
||||
// assert(FIsPaused, 'TDbgRspThread.ReadThreadState: FIsPaused');
|
||||
result := true;
|
||||
if FHasThreadState then
|
||||
exit;
|
||||
FRegisterValueListValid := false;
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.RequestInternalPause: Boolean;
|
||||
begin
|
||||
if TDbgAvrProcess(Process).FIsTerminating then
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.RequestInternalPause called while FIsTerminating is set.');
|
||||
|
||||
Result := False;
|
||||
if FInternalPauseRequested or FIsPaused or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
exit;
|
||||
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspThread.RequestInternalPause requesting Ctrl-C.');
|
||||
|
||||
FInternalPauseRequested := true;
|
||||
// Send SIGSTOP/break
|
||||
TDbgAvrProcess(Process).FConnection.Break();
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.CheckSignalForPostponing(AWaitedStatus: integer): Boolean;
|
||||
begin
|
||||
Assert(not FIsPaused, 'Got WaitStatus while already paused');
|
||||
assert(FExceptionSignal = 0, 'TDbgLinuxThread.CheckSignalForPostponing: FExceptionSignal = 0');
|
||||
Result := FIsPaused;
|
||||
DebugLn(DBG_VERBOSE and (Result), ['Warning: Thread already paused', ID]);
|
||||
|
||||
DebugLn(DBG_VERBOSE, ['TDbgRspThread.CheckSignalForPostponing called with ', AWaitedStatus]);
|
||||
|
||||
if Result then
|
||||
exit;
|
||||
|
||||
FIsPaused := True;
|
||||
FIsInInternalPause := False;
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.ResetPauseStates;
|
||||
begin
|
||||
FIsInInternalPause := False;
|
||||
FIsPaused := False;
|
||||
FExceptionSignal := 0;
|
||||
FHasThreadState := False;
|
||||
FDidResetInstructionPointer := False;
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.GetStackUnwinder: TDbgStackUnwinder;
|
||||
begin
|
||||
if FUnwinder = nil then
|
||||
@ -402,88 +136,11 @@ begin
|
||||
Result := FUnwinder;
|
||||
end;
|
||||
|
||||
constructor TDbgAvrThread.Create(const AProcess: TDbgProcess;
|
||||
const AID: Integer; const AHandle: THandle);
|
||||
begin
|
||||
inherited;
|
||||
SetLength(FRegs, RegArrayLength);
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
||||
begin
|
||||
if not ReadThreadState then
|
||||
exit(False);
|
||||
result := true;
|
||||
if FDidResetInstructionPointer then
|
||||
exit;
|
||||
FDidResetInstructionPointer := True;
|
||||
|
||||
// This is not required for gdbserver
|
||||
// since remote stub should ensure PC points to break address
|
||||
//Dec(FRegs.cpuRegs[PCindex]);
|
||||
//FRegsChanged:=true;
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.ApplyWatchPoints(AWatchPointData: TFpWatchPointData);
|
||||
var
|
||||
i: integer;
|
||||
addr: PtrUInt;
|
||||
watchData: TRspBreakWatchPoint;
|
||||
tmpData: TBytes;
|
||||
begin
|
||||
for i := 0 to TFpRspWatchPointData(AWatchPointData).Count-1 do
|
||||
begin
|
||||
watchData := TFpRspWatchPointData(AWatchPointData).Data[i];
|
||||
addr := watchData.Address;
|
||||
SetLength(tmpData, watchData.Size);
|
||||
if Process.ReadData(addr, watchData.Size, tmpData[0]) then
|
||||
begin
|
||||
if not TDbgAvrProcess(Process).FConnection.SetBreakWatchPoint(addr, watchData.Kind) then
|
||||
DebugLn(DBG_WARNINGS, 'Failed to set watch point.', []);
|
||||
end
|
||||
else
|
||||
DebugLn(DBG_WARNINGS, 'Failed to read memory.', []);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.DetectHardwareWatchpoint: Pointer;
|
||||
begin
|
||||
if TDbgAvrProcess(Process).FConnection.LastStatusEvent.stopReason in [srAnyWatchPoint, srReadWatchPoint, srWriteWatchPoint] then
|
||||
begin
|
||||
Result := TFpRspWatchPointData(TDbgAvrProcess(Process).WatchPointData).FindOwner(TDbgAvrProcess(Process).FConnection.LastStatusEvent.watchPointAddress);
|
||||
TDbgAvrProcess(Process).FConnection.ResetStatusEvent;
|
||||
end
|
||||
else
|
||||
result := nil;
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.BeforeContinue;
|
||||
//var
|
||||
// regs: TBytes;
|
||||
begin
|
||||
if not FIsPaused then
|
||||
exit;
|
||||
|
||||
inherited;
|
||||
InvalidateRegisters;
|
||||
|
||||
// TODO: currently nothing changes registers locally?
|
||||
|
||||
// Update registers if changed locally
|
||||
//if FRegsChanged then
|
||||
//begin
|
||||
// SetLength(regs, RegArrayByteLength);
|
||||
// for i := 0 to lastCPURegIndex do
|
||||
// regs[i] :=
|
||||
// FRegsChanged:=false;
|
||||
//end;
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.LoadRegisterValues;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
if TDbgAvrProcess(Process).FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
if TDbgRspProcess(Process).IsTerminating or (TDbgRspProcess(Process).Status = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.LoadRegisterValues called while FIsTerminating is set.');
|
||||
exit;
|
||||
@ -494,14 +151,14 @@ begin
|
||||
|
||||
RefreshRegisterCache;
|
||||
|
||||
if FRegsUpdated then
|
||||
if FRegs.Initialized then
|
||||
begin
|
||||
for i := 0 to lastCPURegIndex do
|
||||
FRegisterValueList.DbgRegisterAutoCreate['r'+IntToStr(i)].SetValue(FRegs[i].Value, IntToStr(FRegs[i].Value),1, i); // confirm dwarf index
|
||||
FRegisterValueList.DbgRegisterAutoCreate['r'+IntToStr(i)].SetValue(FRegs.regs[i], IntToStr(FRegs.regs[i]),1, i); // confirm dwarf index
|
||||
|
||||
FRegisterValueList.DbgRegisterAutoCreate[nSREG].SetValue(FRegs[SREGindex].Value, IntToStr(FRegs[SREGindex].Value),1,SREGindex);
|
||||
FRegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(FRegs[SPindex].Value, IntToStr(FRegs[SPindex].Value),2,SPindex);
|
||||
FRegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(FRegs[PCindex].Value, IntToStr(FRegs[PCindex].Value),4,PCindex);
|
||||
FRegisterValueList.DbgRegisterAutoCreate[nSREG].SetValue(FRegs.regs[SREGindex], IntToStr(FRegs.regs[SREGindex]),1,SREGindex);
|
||||
FRegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(FRegs.regs[SPindex], IntToStr(FRegs.regs[SPindex]),2,SPindex);
|
||||
FRegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(FRegs.regs[PCindex], IntToStr(FRegs.regs[PCindex]),4,PCindex);
|
||||
FRegisterValueListValid := true;
|
||||
end
|
||||
else
|
||||
@ -511,7 +168,7 @@ end;
|
||||
function TDbgAvrThread.GetInstructionPointerRegisterValue: TDbgPtr;
|
||||
begin
|
||||
Result := 0;
|
||||
if TDbgAvrProcess(Process).FIsTerminating then
|
||||
if TDbgRspProcess(Process).IsTerminating then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.GetInstructionPointerRegisterValue called while FIsTerminating is set.');
|
||||
exit;
|
||||
@ -529,7 +186,7 @@ var
|
||||
lval, hval: QWord;
|
||||
begin
|
||||
Result := 0;
|
||||
if TDbgAvrProcess(Process).FIsTerminating then
|
||||
if TDbgRspProcess(Process).IsTerminating then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgAvrThread.GetStackBasePointerRegisterValue called while FIsTerminating is set.');
|
||||
exit;
|
||||
@ -552,7 +209,7 @@ end;
|
||||
function TDbgAvrThread.GetStackPointerRegisterValue: TDbgPtr;
|
||||
begin
|
||||
Result := 0;
|
||||
if TDbgAvrProcess(Process).FIsTerminating then
|
||||
if TDbgRspProcess(Process).IsTerminating then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.GetStackPointerRegisterValue called while FIsTerminating is set.');
|
||||
exit;
|
||||
@ -567,12 +224,6 @@ end;
|
||||
|
||||
{ TDbgAvrProcess }
|
||||
|
||||
procedure TDbgAvrProcess.InitializeLoaders;
|
||||
begin
|
||||
if LoaderList.Count = 0 then
|
||||
TDbgImageLoader.Create(Name).AddToLoaderList(LoaderList);
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
|
||||
begin
|
||||
IsMainThread:=False;
|
||||
@ -585,449 +236,24 @@ begin
|
||||
result := nil;
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.CreateWatchPointData: TFpWatchPointData;
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.CreateWatchPointData called.');
|
||||
Result := TFpRspWatchPointData.Create;
|
||||
end;
|
||||
|
||||
constructor TDbgAvrProcess.Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
|
||||
AMemManager: TFpDbgMemManager; AProcessConfig: TDbgProcessConfig);
|
||||
begin
|
||||
if Assigned(AProcessConfig) and (AProcessConfig is TRemoteConfig) then
|
||||
begin
|
||||
FRemoteConfig := TRemoteConfig.Create;
|
||||
FRemoteConfig.Assign(AProcessConfig);
|
||||
end;
|
||||
|
||||
FRegArrayLength := FNumRegisters;
|
||||
inherited Create(AFileName, AnOsClasses, AMemManager, AProcessConfig);
|
||||
end;
|
||||
|
||||
destructor TDbgAvrProcess.Destroy;
|
||||
begin
|
||||
if Assigned(FConnection) then
|
||||
FreeAndNil(FConnection);
|
||||
if Assigned(FRemoteConfig) then
|
||||
FreeAndNil(FRemoteConfig);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.StartInstance(AParams, AnEnvironment: TStrings;
|
||||
AWorkingDirectory, AConsoleTty: string; AFlags: TStartInstanceFlags; out
|
||||
AnError: TFpError): boolean;
|
||||
var
|
||||
AnExecutabeFilename: string;
|
||||
begin
|
||||
Result := false;
|
||||
AnExecutabeFilename:=ExcludeTrailingPathDelimiter(Name);
|
||||
if DirectoryExists(AnExecutabeFilename) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'Can not debug %s, because it''s a directory',[AnExecutabeFilename]);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if not FileExists(Name) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'Can not find %s.',[AnExecutabeFilename]);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if not Assigned(FRemoteConfig) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgAvrProcess only supports remote debugging and requires a valid TRemoteConfig class');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
try
|
||||
FConnection := TRspConnection.Create(Name, self, self.FRemoteConfig);
|
||||
FConnection.Connect;
|
||||
try
|
||||
FConnection.RegisterCacheSize := RegArrayLength;
|
||||
FStatus := FConnection.Init;
|
||||
Result := true;
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, Format('Failed to init remote connection. Errormessage: "%s".', [E.Message]));
|
||||
end;
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, Format('Failed to start remote connection. Errormessage: "%s".', [E.Message]));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TDbgAvrProcess.isSupported(target: TTargetDescriptor): boolean;
|
||||
begin
|
||||
result := (target.OS = osEmbedded) and
|
||||
(target.machineType = mtAVR8);
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.ReadData(const AAdress: TDbgPtr;
|
||||
const ASize: Cardinal; out AData): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.ReadData called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
result := FConnection.ReadData(AAdress, ASize, AData);
|
||||
if Result then
|
||||
MaskBreakpointsInReadData(AAdress, ASize, AData);
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.WriteData(const AAdress: TDbgPtr;
|
||||
const ASize: Cardinal; const AData): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.WriteData called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
result := FConnection.WriteData(AAdress,AAdress, AData);
|
||||
end;
|
||||
|
||||
procedure TDbgAvrProcess.TerminateProcess;
|
||||
begin
|
||||
// Try to prevent access to the RSP socket after it has been closed
|
||||
if not (FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP)) then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, 'Removing all break points');
|
||||
RemoveAllBreakPoints;
|
||||
DebugLn(DBG_VERBOSE, 'Sending kill command from TDbgRspProcess.TerminateProcess');
|
||||
FConnection.Kill();
|
||||
FIsTerminating:=true;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.Pause: boolean;
|
||||
begin
|
||||
if FIsTerminating or (TDbgAvrProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.Pause called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// Target should automatically respond with T or S reply after processing the break
|
||||
result := true;
|
||||
if not PauseRequested then
|
||||
begin
|
||||
FConnection.Break();
|
||||
PauseRequested := true;
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.Pause called.');
|
||||
end
|
||||
else
|
||||
begin
|
||||
result := true;
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.Pause called while PauseRequested is set.');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.Detach(AProcess: TDbgProcess; AThread: TDbgThread): boolean;
|
||||
begin
|
||||
RemoveAllBreakPoints;
|
||||
DebugLn(DBG_VERBOSE, 'Sending detach command from TDbgRspProcess.Detach');
|
||||
Result := FConnection.Detach();
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean;
|
||||
var
|
||||
ThreadToContinue: TDbgAvrThread;
|
||||
PC: word;
|
||||
s: string;
|
||||
tempState: integer;
|
||||
initRegs: TInitializedRegisters;
|
||||
begin
|
||||
// Terminating process and all threads
|
||||
if FIsTerminating or (FStatus = SIGHUP) then
|
||||
begin
|
||||
AThread.BeforeContinue;
|
||||
TDbgAvrThread(AThread).InvalidateRegisters;
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.Continue called while terminating.');
|
||||
|
||||
// The kill command should have been issued earlier (if using fpd), calling SendKill again will lead to an exception since the connection should be terminated already.
|
||||
// FConnection.Kill();
|
||||
|
||||
TDbgAvrThread(AThread).ResetPauseStates;
|
||||
if not FThreadMap.HasId(AThread.ID) then
|
||||
AThread.Free;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if TDbgAvrThread(AThread).FIsPaused then // in case of deInternal, it may not be paused and can be ignored
|
||||
AThread.NextIsSingleStep:=SingleStep;
|
||||
|
||||
// check other threads if they need a singlestep
|
||||
for TDbgThread(ThreadToContinue) in FThreadMap do
|
||||
if (ThreadToContinue <> AThread) and ThreadToContinue.FIsPaused then
|
||||
begin
|
||||
PC := ThreadToContinue.GetInstructionPointerRegisterValue;
|
||||
if HasInsertedBreakInstructionAtLocation(PC) then
|
||||
begin
|
||||
TempRemoveBreakInstructionCode(PC);
|
||||
ThreadToContinue.BeforeContinue;
|
||||
|
||||
while (ThreadToContinue.GetInstructionPointerRegisterValue = PC) do
|
||||
begin
|
||||
result := FConnection.SingleStep();
|
||||
TDbgAvrThread(ThreadToContinue).ResetPauseStates; // So BeforeContinue will not run again
|
||||
ThreadToContinue.FIsPaused := True;
|
||||
if result then
|
||||
begin
|
||||
tempState := FConnection.WaitForSignal(s, initRegs); // TODO: Update registers cache for this thread
|
||||
if (tempState = SIGTRAP) then
|
||||
break; // if the command jumps back an itself....
|
||||
end
|
||||
else
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, ['Error single stepping other thread ', ThreadToContinue.ID]);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if TDbgAvrThread(AThread).FIsPaused and SingleStep then // in case of deInternal, it may not be paused and can be ignored
|
||||
if HasInsertedBreakInstructionAtLocation(AThread.GetInstructionPointerRegisterValue) then
|
||||
begin
|
||||
TempRemoveBreakInstructionCode(AThread.GetInstructionPointerRegisterValue);
|
||||
TDbgAvrThread(AThread).FIsSteppingBreakPoint := True;
|
||||
AThread.BeforeContinue;
|
||||
result := FConnection.SingleStep(); // TODO: pass thread ID once it is supported in FConnection - also signals not yet passed through
|
||||
TDbgAvrThread(AThread).ResetPauseStates;
|
||||
FStatus := 0; // need to call WaitForSignal to read state after single step
|
||||
exit;
|
||||
end;
|
||||
|
||||
RestoreTempBreakInstructionCodes;
|
||||
|
||||
ThreadsBeforeContinue;
|
||||
|
||||
// start all other threads
|
||||
for TDbgThread(ThreadToContinue) in FThreadMap do
|
||||
begin
|
||||
if (ThreadToContinue <> AThread) and (ThreadToContinue.FIsPaused) then
|
||||
begin
|
||||
FConnection.Continue();
|
||||
ThreadToContinue.ResetPauseStates;
|
||||
end;
|
||||
end;
|
||||
|
||||
if TDbgAvrThread(AThread).FIsPaused then // in case of deInternal, it may not be paused and can be ignored
|
||||
if not FIsTerminating then
|
||||
begin
|
||||
AThread.BeforeContinue;
|
||||
if SingleStep then
|
||||
result := FConnection.SingleStep()
|
||||
else
|
||||
result := FConnection.Continue();
|
||||
TDbgAvrThread(AThread).ResetPauseStates;
|
||||
FStatus := 0; // should update status by calling WaitForSignal
|
||||
end;
|
||||
|
||||
if not FThreadMap.HasId(AThread.ID) then
|
||||
AThread.Free;
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean;
|
||||
var
|
||||
s: string;
|
||||
initRegs: TInitializedRegisters;
|
||||
begin
|
||||
debugln(DBG_VERBOSE, ['Entering WaitForDebugEvent, FStatus = ', FStatus]);
|
||||
// Currently only single process/thread
|
||||
// TODO: Query and handle process/thread states of target
|
||||
ThreadIdentifier := self.ThreadID;
|
||||
ProcessIdentifier := Self.ProcessID;
|
||||
|
||||
if FIsTerminating then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.WaitForDebugEvent called while FIsTerminating is set.');
|
||||
FStatus := SIGKILL;
|
||||
end
|
||||
else
|
||||
// Wait for S or T response from target, or if connection to target is lost
|
||||
if FStatus = 0 then
|
||||
repeat
|
||||
try
|
||||
FStatus := FConnection.WaitForSignal(s, initRegs); // TODO: Update registers cache
|
||||
sleep(1);
|
||||
except
|
||||
FStatus := 0;
|
||||
end;
|
||||
until FStatus <> 0;
|
||||
|
||||
if FStatus in [SIGINT, SIGTRAP] then
|
||||
RestoreTempBreakInstructionCodes;
|
||||
|
||||
result := FStatus <> 0;
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.InsertBreakInstructionCode(const ALocation: TDBGPtr;
|
||||
out OrigValue: Byte; AMakeTempRemoved: Boolean): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (FStatus = SIGHUP) then
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.InsertBreakInstruction called while FIsTerminating is set.');
|
||||
|
||||
result := ReadData(ALocation, SizeOf(OrigValue), OrigValue);
|
||||
if result then
|
||||
begin
|
||||
// HW break...
|
||||
result := FConnection.SetBreakWatchPoint(ALocation, wkpExec);
|
||||
if not result then
|
||||
DebugLn(DBG_WARNINGS, 'Failed to set break point.', []);
|
||||
end
|
||||
else
|
||||
DebugLn(DBG_WARNINGS, 'Failed to read memory.', []);
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.RemoveBreakInstructionCode(const ALocation: TDBGPtr;
|
||||
const OrigValue: Byte): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.RemoveBreakInstructionCode called while FIsTerminating is set');
|
||||
result := false;
|
||||
end
|
||||
else
|
||||
result := FConnection.DeleteBreakWatchPoint(ALocation, wkpExec);
|
||||
end;
|
||||
|
||||
function TDbgAvrProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
|
||||
var
|
||||
ThreadToPause: TDbgAvrThread;
|
||||
begin
|
||||
debugln(DBG_VERBOSE, ['Entering TDbgRspProcess.AnalyseDebugEvent, FStatus = ', FStatus, ' PauseRequested = ', PauseRequested]);
|
||||
if FIsTerminating then begin
|
||||
result := deExitProcess;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if AThread = nil then begin // should not happen... / just assume the most likely safe failbacks
|
||||
result := deInternalContinue;
|
||||
exit;
|
||||
end;
|
||||
|
||||
TDbgAvrThread(AThread).FExceptionSignal:=0;
|
||||
TDbgAvrThread(AThread).FIsPaused := True;
|
||||
TDbgAvrThread(AThread).UpdateStatusFromEvent(FConnection.lastStatusEvent);
|
||||
|
||||
if FStatus in [SIGHUP, SIGKILL] then // not sure which signals is relevant here
|
||||
begin
|
||||
if AThread.ID=ProcessID then
|
||||
begin
|
||||
// Main thread stop -> application exited
|
||||
SetExitCode(FStatus);
|
||||
result := deExitProcess
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Thread stopped, just continue
|
||||
RemoveThread(AThread.Id);
|
||||
result := deInternalContinue;
|
||||
end;
|
||||
end
|
||||
else if FStatus <> 0 then
|
||||
begin
|
||||
TDbgAvrThread(AThread).ReadThreadState;
|
||||
|
||||
if (not FProcessStarted) and (FStatus <> SIGTRAP) then
|
||||
begin
|
||||
// attached, should be SigStop, but may be out of order
|
||||
debugln(DBG_VERBOSE, ['Attached ', FStatus]);
|
||||
result := deCreateProcess;
|
||||
FProcessStarted:=true;
|
||||
end
|
||||
else
|
||||
case FStatus of
|
||||
SIGTRAP:
|
||||
begin
|
||||
if not FProcessStarted then
|
||||
begin
|
||||
result := deCreateProcess;
|
||||
FProcessStarted:=true;
|
||||
DebugLn(DBG_VERBOSE, ['Creating process - SIGTRAP received for thread: ', AThread.ID]);
|
||||
end
|
||||
else if TDbgAvrThread(AThread).FInternalPauseRequested then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, ['???Received late SigTrap for thread ', AThread.ID]);
|
||||
result := deBreakpoint;
|
||||
end
|
||||
else
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, ['Received SigTrap for thread ', AThread.ID,
|
||||
' PauseRequest=', PauseRequested]);
|
||||
result := deBreakpoint;
|
||||
|
||||
if not TDbgAvrThread(AThread).FIsSteppingBreakPoint then
|
||||
AThread.CheckAndResetInstructionPointerAfterBreakpoint;
|
||||
end;
|
||||
end;
|
||||
SIGINT:
|
||||
begin
|
||||
if PauseRequested then
|
||||
result := deBreakpoint
|
||||
else
|
||||
begin
|
||||
ExceptionClass:='SIGINT';
|
||||
TDbgAvrThread(AThread).FExceptionSignal:=SIGINT;
|
||||
result := deException;
|
||||
end;
|
||||
end;
|
||||
SIGKILL:
|
||||
begin
|
||||
if FIsTerminating then
|
||||
result := deInternalContinue
|
||||
else
|
||||
begin
|
||||
ExceptionClass:='SIGKILL';
|
||||
TDbgAvrThread(AThread).FExceptionSignal:=SIGKILL;
|
||||
result := deException;
|
||||
end;
|
||||
end;
|
||||
SIGSTOP:
|
||||
begin
|
||||
// New thread (stopped within the new thread)
|
||||
result := deInternalContinue;
|
||||
end
|
||||
else
|
||||
begin
|
||||
ExceptionClass:='Unknown exception code ' + inttostr(FStatus);
|
||||
TDbgAvrThread(AThread).FExceptionSignal := FStatus;
|
||||
result := deException;
|
||||
end;
|
||||
end; {case}
|
||||
if result=deException then
|
||||
ExceptionClass:='External: '+ExceptionClass;
|
||||
end;
|
||||
|
||||
debugln(DBG_VERBOSE, ['Leaving AnalyseDebugEvent, result = ', result]);
|
||||
|
||||
TDbgAvrThread(AThread).FIsSteppingBreakPoint := False;
|
||||
|
||||
if Result in [deException, deBreakpoint, deFinishedStep] then // deFinishedStep will not be set here
|
||||
begin
|
||||
// Signal all other threads to pause
|
||||
for TDbgThread(ThreadToPause) in FThreadMap do
|
||||
begin
|
||||
if (ThreadToPause <> AThread) then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE and (ThreadToPause.FInternalPauseRequested), ['Re-Request Internal pause for ', ThreadToPause.ID]);
|
||||
ThreadToPause.FInternalPauseRequested:=false;
|
||||
if not ThreadToPause.RequestInternalPause then // will fail, if already paused
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
||||
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
||||
|
@ -69,12 +69,6 @@ type
|
||||
property AfterUploadMonitorCmds: TStringList read FAfterUploadMonitorCmds write FAfterUploadMonitorCmds;
|
||||
end;
|
||||
|
||||
TInitializedRegister = record
|
||||
Initialized: boolean;
|
||||
Value: qword; // sized to handle largest register, should truncate as required to smaller registers
|
||||
end;
|
||||
TInitializedRegisters = array of TInitializedRegister;
|
||||
|
||||
TStopReason = (srNone, srSWBreakPoint, srHWBreakPoint, srWriteWatchPoint, srReadWatchPoint, srAnyWatchPoint);
|
||||
|
||||
TStatusEvent = record
|
||||
@ -84,7 +78,6 @@ type
|
||||
threadID: integer;
|
||||
stopReason: TStopReason;
|
||||
watchPointAddress: qword; // contains address which triggered watch point
|
||||
registers: TInitializedRegisters;
|
||||
end;
|
||||
|
||||
{ TRspConnection }
|
||||
@ -99,7 +92,6 @@ type
|
||||
// Catch exceptions and store as socket errors
|
||||
FSockErr: boolean;
|
||||
FConfig: TRemoteConfig;
|
||||
procedure SetRegisterCacheSize(sz: cardinal);
|
||||
function WaitForData(timeout_ms: integer): integer; overload;
|
||||
|
||||
// Wrappers to catch exceptions and set SockErr
|
||||
@ -126,7 +118,7 @@ type
|
||||
constructor Create(AFileName: string; AOwner: TDbgProcess; AConfig: TRemoteConfig); Overload;
|
||||
destructor Destroy; override;
|
||||
// Wait for async signal - blocking
|
||||
function WaitForSignal(out msg: string; out registers: TInitializedRegisters): integer;
|
||||
function WaitForSignal(out msg: string): integer;
|
||||
procedure ResetStatusEvent;
|
||||
|
||||
procedure Break();
|
||||
@ -154,7 +146,6 @@ type
|
||||
function Init: integer;
|
||||
|
||||
property State: integer read FState;
|
||||
property RegisterCacheSize: cardinal write SetRegisterCacheSize;
|
||||
property lastStatusEvent: TStatusEvent read FStatusEvent;
|
||||
property SockErr: boolean read FSockErr;
|
||||
end;
|
||||
@ -208,11 +199,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRspConnection.SetRegisterCacheSize(sz: cardinal);
|
||||
begin
|
||||
SetLength(FStatusEvent.registers, sz);
|
||||
end;
|
||||
|
||||
procedure TRspConnection.ResetStatusEvent;
|
||||
var
|
||||
i: integer;
|
||||
@ -225,11 +211,6 @@ begin
|
||||
threadID := 0;
|
||||
stopReason := srNone;
|
||||
watchPointAddress := 0;
|
||||
for i := low(registers) to high(registers) do
|
||||
begin
|
||||
registers[i].Initialized := false;
|
||||
registers[i].Value := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -589,8 +570,7 @@ begin
|
||||
DoneCriticalSection(fCS);
|
||||
end;
|
||||
|
||||
function TRspConnection.WaitForSignal(out msg: string; out
|
||||
registers: TInitializedRegisters): integer;
|
||||
function TRspConnection.WaitForSignal(out msg: string): integer;
|
||||
var
|
||||
res: boolean;
|
||||
startIndex, colonIndex, semicolonIndex, i: integer;
|
||||
@ -599,7 +579,6 @@ var
|
||||
begin
|
||||
result := 0;
|
||||
res := false;
|
||||
SetLength(registers, 0);
|
||||
|
||||
EnterCriticalSection(fCS);
|
||||
try
|
||||
@ -719,23 +698,6 @@ begin
|
||||
else
|
||||
DebugLn(DBG_WARNINGS, 'Stop reason "thread" with no thread data');
|
||||
end;
|
||||
else // catch valid hex numbers - will be register info
|
||||
begin
|
||||
// check if part1 is a number, this should then be a register index
|
||||
if HexToIntLittleEndian(part1, tmp) and HexToIntLittleEndian(part2, tmp2) then
|
||||
begin
|
||||
if tmp < length(FStatusEvent.registers) then
|
||||
begin
|
||||
FStatusEvent.registers[tmp].Value := tmp2;
|
||||
FStatusEvent.registers[tmp].Initialized := true;
|
||||
end
|
||||
else
|
||||
DebugLn(DBG_WARNINGS, format('Register index exceeds total number of registers (%d > %d)',
|
||||
[tmp, length(FStatusEvent.registers)]));
|
||||
end
|
||||
else
|
||||
DebugLn(DBG_WARNINGS, format('Ignoring stop reply pair [%s:%s] ', [part1, part2]));
|
||||
end;
|
||||
end;
|
||||
startIndex := semicolonIndex + 1;
|
||||
until (semicolonIndex = 0) or (semicolonIndex = length(msg));
|
||||
@ -1029,7 +991,6 @@ end;
|
||||
function TRspConnection.Init: integer;
|
||||
var
|
||||
reply: string;
|
||||
intRegs: TInitializedRegisters;
|
||||
res: boolean;
|
||||
pSection: PDbgImageSection;
|
||||
dataStart: int64;
|
||||
@ -1065,7 +1026,6 @@ begin
|
||||
repeat
|
||||
inc(i);
|
||||
pSection := FOwner.LoaderList[0].SectionByID[i];
|
||||
|
||||
if (pSection <> nil) and (pSection^.Size > 0) and (pSection^.IsLoadable) then
|
||||
begin
|
||||
if Assigned(FConfig.SkipSectionsList) and
|
||||
@ -1122,7 +1082,7 @@ begin
|
||||
if res then
|
||||
begin
|
||||
// Already wrapped in critical section
|
||||
result := WaitForSignal(reply, intRegs);
|
||||
result := WaitForSignal(reply);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
834
components/fpdebug/fpdbgrspclasses.pas
Normal file
834
components/fpdebug/fpdbgrspclasses.pas
Normal file
@ -0,0 +1,834 @@
|
||||
unit FpDbgRspClasses;
|
||||
|
||||
{ Connects to gdbserver instance and communicate over gdb's remote serial protocol (RSP).
|
||||
Can in principle possible to connect over any serial text capabile interface
|
||||
such as tcp/ip, RS-232, pipes etc. Currently only tcp/ip connections are supported. }
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
{$packrecords c}
|
||||
{$modeswitch advancedrecords}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes,
|
||||
SysUtils,
|
||||
FpDbgClasses,
|
||||
FpDbgLoader,
|
||||
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
||||
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, Maps,
|
||||
FpDbgRsp, FpDbgCommon, FpdMemoryTools,
|
||||
FpErrorMessages;
|
||||
|
||||
type
|
||||
TInitializedRegisters = record
|
||||
Initialized: boolean;
|
||||
regs: array of qword; // sized to handle largest register, should truncate as required to smaller registers
|
||||
end;
|
||||
|
||||
{ TDbgRspThread }
|
||||
|
||||
TDbgRspThread = class(TDbgThread)
|
||||
private
|
||||
protected
|
||||
FRegs: TInitializedRegisters;
|
||||
FExceptionSignal: integer;
|
||||
FIsPaused, FInternalPauseRequested, FIsInInternalPause: boolean;
|
||||
FIsSteppingBreakPoint: boolean;
|
||||
FDidResetInstructionPointer: Boolean;
|
||||
FHasThreadState: boolean;
|
||||
FUnwinder: TDbgStackUnwinder;
|
||||
|
||||
procedure RefreshRegisterCache; virtual;
|
||||
procedure InvalidateRegisters;
|
||||
function ReadDebugReg(ind: byte; out AVal: TDbgPtr): boolean;
|
||||
function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||
function ReadThreadState: boolean;
|
||||
function RequestInternalPause: Boolean;
|
||||
function CheckSignalForPostponing(AWaitedStatus: integer): Boolean;
|
||||
procedure ResetPauseStates;
|
||||
public
|
||||
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); override;
|
||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
||||
function DetectHardwareWatchpoint: Pointer; override;
|
||||
procedure BeforeContinue; override;
|
||||
procedure SetRegisterValue(AName: string; AValue: QWord); override;
|
||||
procedure StoreRegisters; override;
|
||||
procedure RestoreRegisters; override;
|
||||
end;
|
||||
|
||||
{ TDbgRspProcess }
|
||||
|
||||
TDbgRspProcess = class(TDbgProcess)
|
||||
private
|
||||
protected
|
||||
FStatus: integer;
|
||||
FProcessStarted: boolean;
|
||||
FIsTerminating: boolean;
|
||||
FConnection: TRspConnection;
|
||||
FRemoteConfig: TRemoteConfig;
|
||||
// Initialize in target specific class
|
||||
FRegArrayLength: integer;
|
||||
|
||||
function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; override;
|
||||
function CreateWatchPointData: TFpWatchPointData; override;
|
||||
procedure InitializeLoaders; override;
|
||||
// Insert/Delete break points on target
|
||||
// TODO: if target doesn't support break points or have limited break points
|
||||
// then debugger needs to manage insertion/deletion of break points in target memory
|
||||
function InsertBreakInstructionCode(const ALocation: TDBGPtr; out OrigValue: Byte; AMakeTempRemoved: Boolean): Boolean; override;
|
||||
function RemoveBreakInstructionCode(const ALocation: TDBGPtr; const OrigValue: Byte): Boolean; override;
|
||||
public
|
||||
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
|
||||
AMemManager: TFpDbgMemManager; AProcessConfig: TDbgProcessConfig = nil); override;
|
||||
destructor Destroy; override;
|
||||
|
||||
function StartInstance(AParams, AnEnvironment: TStrings; AWorkingDirectory, AConsoleTty: string;
|
||||
AFlags: TStartInstanceFlags; out AnError: TFpError): boolean; override;
|
||||
function AttachToInstance(APid: Integer; out AnError: TFpError): boolean; override;
|
||||
|
||||
procedure CreateRspConnection(AFileName: string);
|
||||
|
||||
function ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean; override;
|
||||
function WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean; override;
|
||||
|
||||
procedure TerminateProcess; override;
|
||||
function Pause: boolean; override;
|
||||
function Detach(AProcess: TDbgProcess; AThread: TDbgThread): boolean; override;
|
||||
|
||||
function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; override;
|
||||
// Wait for -S or -T response from target, or if connection to target is lost
|
||||
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override;
|
||||
|
||||
property RspConnection: TRspConnection read FConnection;
|
||||
// Target specific length of register array
|
||||
property RegArrayLength: integer read FRegArrayLength;
|
||||
property IsTerminating: boolean read FIsTerminating;
|
||||
property Status: integer read FStatus;
|
||||
end;
|
||||
|
||||
{ TFpRspWatchPointData }
|
||||
|
||||
TRspBreakWatchPoint = record
|
||||
Owner: Pointer;
|
||||
Address: TDBGPtr;
|
||||
Size: Cardinal;
|
||||
Kind: TDBGWatchPointKind;
|
||||
end;
|
||||
|
||||
TFpRspWatchPointData = class(TFpWatchPointData)
|
||||
private
|
||||
FData: array of TRspBreakWatchPoint;
|
||||
function BreakWatchPoint(AnIndex: Integer): TRspBreakWatchPoint;
|
||||
function DataCount: integer;
|
||||
public
|
||||
function AddOwnedWatchpoint(AnOwner: Pointer; AnAddr: TDBGPtr; ASize: Cardinal; AReadWrite: TDBGWatchPointKind): boolean; override;
|
||||
function RemoveOwnedWatchpoint(AnOwner: Pointer): boolean; override;
|
||||
function FindOwner(AnAddr: TDBGPtr): Pointer;
|
||||
property Data[AnIndex: Integer]: TRspBreakWatchPoint read BreakWatchPoint;
|
||||
property Count: integer read DataCount;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
FpDbgDwarfDataClasses;
|
||||
|
||||
var
|
||||
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
|
||||
|
||||
{ TFpRspWatchPointData }
|
||||
|
||||
function TFpRspWatchPointData.BreakWatchPoint(AnIndex: Integer
|
||||
): TRspBreakWatchPoint;
|
||||
begin
|
||||
if AnIndex < length(FData) then
|
||||
result := FData[AnIndex];
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.DataCount: integer;
|
||||
begin
|
||||
result := length(FData);
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.FindOwner(AnAddr: TDBGPtr): Pointer;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
i := 0;
|
||||
while (i < Count) and not ((AnAddr >= Data[i].Address) and (AnAddr < Data[i].Address + Data[i].Size)) do
|
||||
begin
|
||||
inc(i);
|
||||
end;
|
||||
if i < Count then
|
||||
Result := Data[i].Owner
|
||||
else
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.AddOwnedWatchpoint(AnOwner: Pointer;
|
||||
AnAddr: TDBGPtr; ASize: Cardinal; AReadWrite: TDBGWatchPointKind): boolean;
|
||||
var
|
||||
idx: integer;
|
||||
begin
|
||||
Result := false;
|
||||
idx := length(FData);
|
||||
SetLength(FData, idx+1);
|
||||
FData[idx].Address := AnAddr;
|
||||
FData[idx].Size := ASize;
|
||||
FData[idx].Kind := AReadWrite;
|
||||
FData[idx].Owner := AnOwner;
|
||||
Changed := true;
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
function TFpRspWatchPointData.RemoveOwnedWatchpoint(AnOwner: Pointer): boolean;
|
||||
var
|
||||
i, j: integer;
|
||||
begin
|
||||
Result := False;
|
||||
i := 0;
|
||||
while (i < length(FData)) and (FData[i].Owner <> AnOwner) do
|
||||
inc(i);
|
||||
|
||||
if i < length(FData) then begin
|
||||
for j := i+1 to length(FData)-1 do begin
|
||||
FData[j-1] := FData[j];
|
||||
Changed := True;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
SetLength(FData, length(FData)-1);
|
||||
Changed := True;
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TDbgRspThread }
|
||||
|
||||
function TDbgRspThread.ReadDebugReg(ind: byte; out AVal: TDbgPtr): boolean;
|
||||
begin
|
||||
Result := false;
|
||||
if TDbgRspProcess(Process).FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.GetDebugReg called while FIsTerminating is set.')
|
||||
else
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, ['TDbgRspThread.GetDebugReg requesting register: ',ind]);
|
||||
RefreshRegisterCache;
|
||||
if ind < length(FRegs.regs) then
|
||||
begin
|
||||
AVal := FRegs.regs[ind];
|
||||
Result := true;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgRspThread.WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||
begin
|
||||
if TDbgRspProcess(Process).FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.WriteDebugReg called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
end
|
||||
else
|
||||
result := TDbgRspProcess(Process).RspConnection.WriteDebugReg(ind, AVal);
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.InvalidateRegisters;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
FRegs.Initialized := false;
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.RefreshRegisterCache;
|
||||
begin
|
||||
// Target specific
|
||||
end;
|
||||
|
||||
function TDbgRspThread.ReadThreadState: boolean;
|
||||
begin
|
||||
// assert(FIsPaused, 'TDbgRspThread.ReadThreadState: FIsPaused');
|
||||
result := true;
|
||||
if FHasThreadState then
|
||||
exit;
|
||||
FRegisterValueListValid := false;
|
||||
end;
|
||||
|
||||
function TDbgRspThread.RequestInternalPause: Boolean;
|
||||
begin
|
||||
if TDbgRspProcess(Process).FIsTerminating then
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspThread.RequestInternalPause called while FIsTerminating is set.');
|
||||
|
||||
Result := False;
|
||||
if FInternalPauseRequested or FIsPaused or (TDbgRspProcess(Process).FStatus = SIGHUP) then
|
||||
exit;
|
||||
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspThread.RequestInternalPause requesting Ctrl-C.');
|
||||
|
||||
FInternalPauseRequested := true;
|
||||
// Send SIGSTOP/break
|
||||
TDbgRspProcess(Process).RspConnection.Break();
|
||||
end;
|
||||
|
||||
function TDbgRspThread.CheckSignalForPostponing(AWaitedStatus: integer): Boolean;
|
||||
begin
|
||||
Assert(not FIsPaused, 'Got WaitStatus while already paused');
|
||||
assert(FExceptionSignal = 0, 'TDbgLinuxThread.CheckSignalForPostponing: FExceptionSignal = 0');
|
||||
Result := FIsPaused;
|
||||
DebugLn(DBG_VERBOSE and (Result), ['Warning: Thread already paused', ID]);
|
||||
|
||||
DebugLn(DBG_VERBOSE, ['TDbgRspThread.CheckSignalForPostponing called with ', AWaitedStatus]);
|
||||
|
||||
if Result then
|
||||
exit;
|
||||
|
||||
FIsPaused := True;
|
||||
FIsInInternalPause := False;
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.ResetPauseStates;
|
||||
begin
|
||||
FIsInInternalPause := False;
|
||||
FIsPaused := False;
|
||||
FExceptionSignal := 0;
|
||||
FHasThreadState := False;
|
||||
FDidResetInstructionPointer := False;
|
||||
end;
|
||||
|
||||
constructor TDbgRspThread.Create(const AProcess: TDbgProcess;
|
||||
const AID: Integer; const AHandle: THandle);
|
||||
begin
|
||||
inherited;
|
||||
FRegs.Initialized := false;
|
||||
SetLength(FRegs.regs, TDbgRspProcess(AProcess).RegArrayLength);
|
||||
end;
|
||||
|
||||
function TDbgRspThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
||||
begin
|
||||
if not ReadThreadState then
|
||||
exit(False);
|
||||
result := true;
|
||||
if FDidResetInstructionPointer then
|
||||
exit;
|
||||
FDidResetInstructionPointer := True;
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.ApplyWatchPoints(AWatchPointData: TFpWatchPointData);
|
||||
var
|
||||
i: integer;
|
||||
addr: PtrUInt;
|
||||
watchData: TRspBreakWatchPoint;
|
||||
tmpData: TBytes;
|
||||
begin
|
||||
for i := 0 to TFpRspWatchPointData(AWatchPointData).Count-1 do
|
||||
begin
|
||||
watchData := TFpRspWatchPointData(AWatchPointData).Data[i];
|
||||
addr := watchData.Address;
|
||||
SetLength(tmpData, watchData.Size);
|
||||
if Process.ReadData(addr, watchData.Size, tmpData[0]) then
|
||||
begin
|
||||
if not TDbgRspProcess(Process).RspConnection.SetBreakWatchPoint(addr, watchData.Kind) then
|
||||
DebugLn(DBG_WARNINGS, 'Failed to set watch point.', []);
|
||||
end
|
||||
else
|
||||
DebugLn(DBG_WARNINGS, 'Failed to read memory.', []);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgRspThread.DetectHardwareWatchpoint: Pointer;
|
||||
begin
|
||||
if TDbgRspProcess(Process).RspConnection.LastStatusEvent.stopReason in [srAnyWatchPoint, srReadWatchPoint, srWriteWatchPoint] then
|
||||
begin
|
||||
Result := TFpRspWatchPointData(TDbgRspProcess(Process).WatchPointData).FindOwner(TDbgRspProcess(Process).RspConnection.LastStatusEvent.watchPointAddress);
|
||||
TDbgRspProcess(Process).RspConnection.ResetStatusEvent;
|
||||
end
|
||||
else
|
||||
result := nil;
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.BeforeContinue;
|
||||
begin
|
||||
if not FIsPaused then
|
||||
exit;
|
||||
|
||||
inherited;
|
||||
InvalidateRegisters;
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.SetRegisterValue(AName: string; AValue: QWord);
|
||||
begin
|
||||
assert(true, 'Not implemented');
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.StoreRegisters;
|
||||
begin
|
||||
assert(true, 'Not implemented');
|
||||
end;
|
||||
|
||||
procedure TDbgRspThread.RestoreRegisters;
|
||||
begin
|
||||
assert(true, 'Not implemented');
|
||||
end;
|
||||
|
||||
{ TDbgRspProcess }
|
||||
|
||||
procedure TDbgRspProcess.InitializeLoaders;
|
||||
begin
|
||||
if LoaderList.Count = 0 then
|
||||
TDbgImageLoader.Create(Name).AddToLoaderList(LoaderList);
|
||||
end;
|
||||
|
||||
procedure TDbgRspProcess.CreateRspConnection(AFileName: string);
|
||||
begin
|
||||
self.FConnection := TRspConnection.Create(AFileName, self, FRemoteConfig);
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.CreateWatchPointData: TFpWatchPointData;
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.CreateWatchPointData called.');
|
||||
Result := TFpRspWatchPointData.Create;
|
||||
end;
|
||||
|
||||
constructor TDbgRspProcess.Create(const AFileName: string;
|
||||
AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager;
|
||||
AProcessConfig: TDbgProcessConfig);
|
||||
begin
|
||||
if Assigned(AProcessConfig) and (AProcessConfig is TRemoteConfig) then
|
||||
begin
|
||||
FRemoteConfig := TRemoteConfig.Create;
|
||||
FRemoteConfig.Assign(AProcessConfig);
|
||||
end;
|
||||
|
||||
inherited Create(AFileName, AnOsClasses, AMemManager);
|
||||
end;
|
||||
|
||||
destructor TDbgRspProcess.Destroy;
|
||||
begin
|
||||
if Assigned(FRemoteConfig) then
|
||||
FreeAndNil(FRemoteConfig);
|
||||
if Assigned(FConnection) then
|
||||
FreeAndNil(FConnection);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.AttachToInstance(APid: Integer; out AnError: TFpError
|
||||
): boolean;
|
||||
begin
|
||||
result := false;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.StartInstance(AParams, AnEnvironment: TStrings;
|
||||
AWorkingDirectory, AConsoleTty: string; AFlags: TStartInstanceFlags; out
|
||||
AnError: TFpError): boolean;
|
||||
var
|
||||
AnExecutabeFilename: string;
|
||||
begin
|
||||
Result := false;
|
||||
AnExecutabeFilename:=ExcludeTrailingPathDelimiter(Name);
|
||||
if DirectoryExists(AnExecutabeFilename) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'Can not debug %s, because it''s a directory',[AnExecutabeFilename]);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if not FileExists(Name) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'Can not find %s.',[AnExecutabeFilename]);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if not Assigned(FRemoteConfig) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgAvrProcess only supports remote debugging and requires a valid TRemoteConfig class');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
try
|
||||
FConnection := TRspConnection.Create(Name, self, self.FRemoteConfig);
|
||||
FConnection.Connect;
|
||||
try
|
||||
FStatus := FConnection.Init;
|
||||
Result := true;
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, Format('Failed to init remote connection. Errormessage: "%s".', [E.Message]));
|
||||
end;
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, Format('Failed to start remote connection. Errormessage: "%s".', [E.Message]));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.ReadData(const AAdress: TDbgPtr;
|
||||
const ASize: Cardinal; out AData): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.ReadData called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
result := RspConnection.ReadData(AAdress, ASize, AData);
|
||||
if Result then
|
||||
MaskBreakpointsInReadData(AAdress, ASize, AData);
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.WriteData(const AAdress: TDbgPtr;
|
||||
const ASize: Cardinal; const AData): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.WriteData called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
result := RspConnection.WriteData(AAdress, ASize, AData);
|
||||
end;
|
||||
|
||||
procedure TDbgRspProcess.TerminateProcess;
|
||||
begin
|
||||
// Try to prevent access to the RSP socket after it has been closed
|
||||
if not (FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP)) then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, 'Removing all break points');
|
||||
RemoveAllBreakPoints;
|
||||
DebugLn(DBG_VERBOSE, 'Sending kill command from TDbgRspProcess.TerminateProcess');
|
||||
RspConnection.Kill();
|
||||
FIsTerminating:=true;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.Pause: boolean;
|
||||
begin
|
||||
if FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.Pause called while FIsTerminating is set.');
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// Target should automatically respond with T or S reply after processing the break
|
||||
result := true;
|
||||
if not PauseRequested then
|
||||
begin
|
||||
RspConnection.Break();
|
||||
PauseRequested := true;
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.Pause called.');
|
||||
end
|
||||
else
|
||||
begin
|
||||
result := true;
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.Pause called while PauseRequested is set.');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.Detach(AProcess: TDbgProcess; AThread: TDbgThread): boolean;
|
||||
begin
|
||||
RemoveAllBreakPoints;
|
||||
DebugLn(DBG_VERBOSE, 'Sending detach command from TDbgRspProcess.Detach');
|
||||
Result := RspConnection.Detach();
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean;
|
||||
var
|
||||
ThreadToContinue: TDbgRspThread;
|
||||
PC: word;
|
||||
s: string;
|
||||
tempState: integer;
|
||||
initRegs: TInitializedRegisters;
|
||||
begin
|
||||
// Terminating process and all threads
|
||||
if FIsTerminating or (FStatus = SIGHUP) then
|
||||
begin
|
||||
AThread.BeforeContinue;
|
||||
TDbgRspThread(AThread).InvalidateRegisters;
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.Continue called while terminating.');
|
||||
|
||||
|
||||
|
||||
|
||||
TDbgRspThread(AThread).ResetPauseStates;
|
||||
if not FThreadMap.HasId(AThread.ID) then
|
||||
AThread.Free;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if TDbgRspThread(AThread).FIsPaused then // in case of deInternal, it may not be paused and can be ignored
|
||||
AThread.NextIsSingleStep:=SingleStep;
|
||||
|
||||
// check other threads if they need a singlestep
|
||||
for TDbgThread(ThreadToContinue) in FThreadMap do
|
||||
if (ThreadToContinue <> AThread) and ThreadToContinue.FIsPaused then
|
||||
begin
|
||||
PC := ThreadToContinue.GetInstructionPointerRegisterValue;
|
||||
if HasInsertedBreakInstructionAtLocation(PC) then
|
||||
begin
|
||||
TempRemoveBreakInstructionCode(PC);
|
||||
ThreadToContinue.BeforeContinue;
|
||||
|
||||
while (ThreadToContinue.GetInstructionPointerRegisterValue = PC) do
|
||||
begin
|
||||
result := RspConnection.SingleStep();
|
||||
TDbgRspThread(ThreadToContinue).ResetPauseStates; // So BeforeContinue will not run again
|
||||
ThreadToContinue.FIsPaused := True;
|
||||
if result then
|
||||
begin
|
||||
tempState := RspConnection.WaitForSignal(s); // TODO: Update registers cache for this thread
|
||||
if (tempState = SIGTRAP) then
|
||||
break; // if the command jumps back an itself....
|
||||
end
|
||||
else
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, ['Error single stepping other thread ', ThreadToContinue.ID]);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if TDbgRspThread(AThread).FIsPaused and SingleStep then // in case of deInternal, it may not be paused and can be ignored
|
||||
if HasInsertedBreakInstructionAtLocation(AThread.GetInstructionPointerRegisterValue) then
|
||||
begin
|
||||
TempRemoveBreakInstructionCode(AThread.GetInstructionPointerRegisterValue);
|
||||
TDbgRspThread(AThread).FIsSteppingBreakPoint := True;
|
||||
AThread.BeforeContinue;
|
||||
result := RspConnection.SingleStep(); // TODO: pass thread ID once it is supported in RspConnection - also signals not yet passed through
|
||||
TDbgRspThread(AThread).ResetPauseStates;
|
||||
FStatus := 0; // need to call WaitForSignal to read state after single step
|
||||
exit;
|
||||
end;
|
||||
|
||||
RestoreTempBreakInstructionCodes;
|
||||
|
||||
ThreadsBeforeContinue;
|
||||
|
||||
// start all other threads
|
||||
for TDbgThread(ThreadToContinue) in FThreadMap do
|
||||
begin
|
||||
if (ThreadToContinue <> AThread) and (ThreadToContinue.FIsPaused) then
|
||||
begin
|
||||
RspConnection.Continue();
|
||||
ThreadToContinue.ResetPauseStates;
|
||||
end;
|
||||
end;
|
||||
|
||||
if TDbgRspThread(AThread).FIsPaused then // in case of deInternal, it may not be paused and can be ignored
|
||||
if not FIsTerminating then
|
||||
begin
|
||||
AThread.BeforeContinue;
|
||||
if SingleStep then
|
||||
result := RspConnection.SingleStep()
|
||||
else
|
||||
result := RspConnection.Continue();
|
||||
TDbgRspThread(AThread).ResetPauseStates;
|
||||
FStatus := 0; // should update status by calling WaitForSignal
|
||||
end;
|
||||
|
||||
if not FThreadMap.HasId(AThread.ID) then
|
||||
AThread.Free;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean;
|
||||
var
|
||||
s: string;
|
||||
initRegs: TInitializedRegisters;
|
||||
begin
|
||||
debugln(DBG_VERBOSE, ['Entering WaitForDebugEvent, FStatus = ', FStatus]);
|
||||
// Currently only single process/thread
|
||||
// TODO: Query and handle process/thread states of target
|
||||
ThreadIdentifier := self.ThreadID;
|
||||
ProcessIdentifier := Self.ProcessID;
|
||||
|
||||
if FIsTerminating then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, 'TDbgRspProcess.WaitForDebugEvent called while FIsTerminating is set.');
|
||||
FStatus := SIGKILL;
|
||||
end
|
||||
else
|
||||
// Wait for S or T response from target, or if connection to target is lost
|
||||
if FStatus = 0 then
|
||||
repeat
|
||||
try
|
||||
FStatus := RspConnection.WaitForSignal(s); // TODO: Update registers cache
|
||||
sleep(1);
|
||||
except
|
||||
FStatus := 0;
|
||||
end;
|
||||
until FStatus <> 0;
|
||||
|
||||
if FStatus in [SIGINT, SIGTRAP] then
|
||||
RestoreTempBreakInstructionCodes;
|
||||
|
||||
result := FStatus <> 0;
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.InsertBreakInstructionCode(const ALocation: TDBGPtr;
|
||||
out OrigValue: Byte; AMakeTempRemoved: Boolean): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (FStatus = SIGHUP) then
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.InsertBreakInstruction called while FIsTerminating is set.');
|
||||
|
||||
// Todo: This does not respect a break instruction larger than 1 byte.
|
||||
// Fix: Use target specific break information in parent class.
|
||||
result := ReadData(ALocation, SizeOf(OrigValue), OrigValue);
|
||||
if AMakeTempRemoved then
|
||||
exit;
|
||||
|
||||
// Insert HW break...
|
||||
result := RspConnection.SetBreakWatchPoint(ALocation, wkpExec);
|
||||
if not result then
|
||||
DebugLn(DBG_WARNINGS, 'Failed to set break point.', []);
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.RemoveBreakInstructionCode(const ALocation: TDBGPtr;
|
||||
const OrigValue: Byte): Boolean;
|
||||
begin
|
||||
if FIsTerminating or (FStatus = SIGHUP) then
|
||||
begin
|
||||
DebugLn(DBG_WARNINGS, 'TDbgRspProcess.RemoveBreakInstructionCode called while FIsTerminating is set');
|
||||
result := false;
|
||||
end
|
||||
else
|
||||
result := RspConnection.DeleteBreakWatchPoint(ALocation, wkpExec);
|
||||
end;
|
||||
|
||||
function TDbgRspProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
|
||||
var
|
||||
ThreadToPause: TDbgRspThread;
|
||||
begin
|
||||
debugln(DBG_VERBOSE, ['Entering TDbgRspProcess.AnalyseDebugEvent, FStatus = ', FStatus, ' PauseRequested = ', PauseRequested]);
|
||||
if FIsTerminating then begin
|
||||
result := deExitProcess;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if AThread = nil then begin // should not happen... / just assume the most likely safe failbacks
|
||||
result := deInternalContinue;
|
||||
exit;
|
||||
end;
|
||||
|
||||
TDbgRspThread(AThread).FExceptionSignal:=0;
|
||||
TDbgRspThread(AThread).FIsPaused := True;
|
||||
|
||||
if FStatus in [SIGHUP, SIGKILL] then // not sure which signals is relevant here
|
||||
begin
|
||||
if AThread.ID=ProcessID then
|
||||
begin
|
||||
// Main thread stop -> application exited
|
||||
SetExitCode(FStatus);
|
||||
result := deExitProcess
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Thread stopped, just continue
|
||||
RemoveThread(AThread.Id);
|
||||
result := deInternalContinue;
|
||||
end;
|
||||
end
|
||||
else if FStatus <> 0 then
|
||||
begin
|
||||
TDbgRspThread(AThread).ReadThreadState;
|
||||
|
||||
if (not FProcessStarted) and (FStatus <> SIGTRAP) then
|
||||
begin
|
||||
// attached, should be SigStop, but may be out of order
|
||||
debugln(DBG_VERBOSE, ['Attached ', FStatus]);
|
||||
result := deCreateProcess;
|
||||
FProcessStarted:=true;
|
||||
end
|
||||
else
|
||||
case FStatus of
|
||||
SIGTRAP:
|
||||
begin
|
||||
if not FProcessStarted then
|
||||
begin
|
||||
result := deCreateProcess;
|
||||
FProcessStarted:=true;
|
||||
DebugLn(DBG_VERBOSE, ['Creating process - SIGTRAP received for thread: ', AThread.ID]);
|
||||
end
|
||||
else if TDbgRspThread(AThread).FInternalPauseRequested then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, ['???Received late SigTrap for thread ', AThread.ID]);
|
||||
result := deBreakpoint;
|
||||
end
|
||||
else
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE, ['Received SigTrap for thread ', AThread.ID,
|
||||
' PauseRequest=', PauseRequested]);
|
||||
result := deBreakpoint;
|
||||
|
||||
if not TDbgRspThread(AThread).FIsSteppingBreakPoint then
|
||||
AThread.CheckAndResetInstructionPointerAfterBreakpoint;
|
||||
end;
|
||||
end;
|
||||
SIGINT:
|
||||
begin
|
||||
if PauseRequested then
|
||||
result := deBreakpoint
|
||||
else
|
||||
begin
|
||||
ExceptionClass:='SIGINT';
|
||||
TDbgRspThread(AThread).FExceptionSignal:=SIGINT;
|
||||
result := deException;
|
||||
end;
|
||||
end;
|
||||
SIGKILL:
|
||||
begin
|
||||
if FIsTerminating then
|
||||
result := deInternalContinue
|
||||
else
|
||||
begin
|
||||
ExceptionClass:='SIGKILL';
|
||||
TDbgRspThread(AThread).FExceptionSignal:=SIGKILL;
|
||||
result := deException;
|
||||
end;
|
||||
end;
|
||||
SIGSTOP:
|
||||
begin
|
||||
// New thread (stopped within the new thread)
|
||||
result := deInternalContinue;
|
||||
end
|
||||
else
|
||||
begin
|
||||
ExceptionClass:='Unknown exception code ' + inttostr(FStatus);
|
||||
TDbgRspThread(AThread).FExceptionSignal := FStatus;
|
||||
result := deException;
|
||||
end;
|
||||
end; {case}
|
||||
if result=deException then
|
||||
ExceptionClass:='External: '+ExceptionClass;
|
||||
end;
|
||||
|
||||
debugln(DBG_VERBOSE, ['Leaving AnalyseDebugEvent, result = ', result]);
|
||||
|
||||
TDbgRspThread(AThread).FIsSteppingBreakPoint := False;
|
||||
|
||||
if Result in [deException, deBreakpoint, deFinishedStep] then // deFinishedStep will not be set here
|
||||
begin
|
||||
// Signal all other threads to pause
|
||||
for TDbgThread(ThreadToPause) in FThreadMap do
|
||||
begin
|
||||
if (ThreadToPause <> AThread) then
|
||||
begin
|
||||
DebugLn(DBG_VERBOSE and (ThreadToPause.FInternalPauseRequested), ['Re-Request Internal pause for ', ThreadToPause.ID]);
|
||||
ThreadToPause.FInternalPauseRequested:=false;
|
||||
if not ThreadToPause.RequestInternalPause then // will fail, if already paused
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
||||
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
||||
|
||||
end.
|
Loading…
Reference in New Issue
Block a user