mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-15 07:42:10 +02:00
FpDebug: Fixes for watch-function-eval: allow function to recursively enter itself.
This commit is contained in:
parent
e61091c175
commit
db69b34e2a
@ -245,6 +245,7 @@ type
|
|||||||
function GetStackBasePointerRegisterValue: TDbgPtr; virtual; abstract;
|
function GetStackBasePointerRegisterValue: TDbgPtr; virtual; abstract;
|
||||||
function GetStackPointerRegisterValue: TDbgPtr; virtual; abstract;
|
function GetStackPointerRegisterValue: TDbgPtr; virtual; abstract;
|
||||||
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); virtual; abstract;
|
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); virtual; abstract;
|
||||||
|
procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); virtual; abstract;
|
||||||
function GetCurrentStackFrameInfo: TDbgStackFrameInfo;
|
function GetCurrentStackFrameInfo: TDbgStackFrameInfo;
|
||||||
|
|
||||||
function AllocStackMem(ASize: Integer): TDbgPtr; virtual;
|
function AllocStackMem(ASize: Integer): TDbgPtr; virtual;
|
||||||
|
@ -178,11 +178,11 @@ type
|
|||||||
// Calling the function is done in two steps:
|
// Calling the function is done in two steps:
|
||||||
// - first execute one instruction so that the debugee jumps into the function (sSingleStep)
|
// - first execute one instruction so that the debugee jumps into the function (sSingleStep)
|
||||||
// - then run until the function has been completed (sRunRoutine)
|
// - then run until the function has been completed (sRunRoutine)
|
||||||
type TStep = (sSingleStep, sRunRoutine);
|
type TStep = (sSingleStepInto, sRunRoutine, sSingleStepOver);
|
||||||
protected
|
protected
|
||||||
FOriginalCode: array of byte;
|
FOriginalCode: array of byte;
|
||||||
FOriginalInstructionPointer: TDBGPtr;
|
FOriginalInstructionPointer: TDBGPtr;
|
||||||
FReturnAdress: TDBGPtr;
|
FNewCodeAddress, FReturnAddress, FReturnStackPointer: TDBGPtr;
|
||||||
FRoutineAddress: TDBGPtr;
|
FRoutineAddress: TDBGPtr;
|
||||||
FStep: TStep;
|
FStep: TStep;
|
||||||
FHiddenBreakpoint: TFpInternalBreakpoint;
|
FHiddenBreakpoint: TFpInternalBreakpoint;
|
||||||
@ -465,7 +465,7 @@ begin
|
|||||||
if FController.FStoredDefaultContext <> nil then
|
if FController.FStoredDefaultContext <> nil then
|
||||||
FController.FStoredDefaultContext.AddReference;
|
FController.FStoredDefaultContext.AddReference;
|
||||||
|
|
||||||
FStep := sSingleStep;
|
FStep := sSingleStepInto;
|
||||||
StoreInstructionPointer;
|
StoreInstructionPointer;
|
||||||
|
|
||||||
if not FCallContext.WriteStack then begin
|
if not FCallContext.WriteStack then begin
|
||||||
@ -477,18 +477,31 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TDbgControllerCallRoutineCmd.InsertCallInstructionCode;
|
procedure TDbgControllerCallRoutineCmd.InsertCallInstructionCode;
|
||||||
|
const
|
||||||
|
TEMP_CODE_LEN = 5; // is the size of the instruction we are about to add.
|
||||||
var
|
var
|
||||||
CurrentIP : TDBGPtr;
|
InsertAddr : TDBGPtr;
|
||||||
Buf: array of Byte;
|
Buf: array [0..TEMP_CODE_LEN] of Byte;
|
||||||
RelAddr: Int32;
|
RelAddr: Int32;
|
||||||
DW: PInt32;
|
DW: PInt32;
|
||||||
begin
|
begin
|
||||||
// Get the address of the current instruction.
|
// Get the address of the current instruction.
|
||||||
CurrentIP := FController.CurrentThread.GetInstructionPointerRegisterValue;
|
InsertAddr := FController.CurrentThread.GetInstructionPointerRegisterValue;
|
||||||
|
FReturnStackPointer := FController.CurrentThread.GetStackPointerRegisterValue;
|
||||||
|
|
||||||
|
// Store the address where the debugee should return at after the function
|
||||||
|
// finished. It is used to determine if the call has been completed succesfully.
|
||||||
|
FReturnAddress := InsertAddr;
|
||||||
|
|
||||||
|
// Insert 5 bytes before
|
||||||
|
InsertAddr := InsertAddr - TEMP_CODE_LEN;
|
||||||
|
FNewCodeAddress := InsertAddr;
|
||||||
|
|
||||||
// Store the original code of the current instruction
|
// Store the original code of the current instruction
|
||||||
SetLength(FOriginalCode, 5);
|
(* TODO: if there is an error, try using: Current_IP + len_of_instr_at_IP - TEMP_CODE_LEN
|
||||||
if not FProcess.ReadData(CurrentIP, 5, FOriginalCode[0]) then begin
|
Ensure the breakpoint at FReturnAddress is at the start of an intruction *)
|
||||||
|
SetLength(FOriginalCode, TEMP_CODE_LEN);
|
||||||
|
if not FProcess.ReadData(InsertAddr, TEMP_CODE_LEN, FOriginalCode[0]) then begin
|
||||||
FCallContext.SetError('Failed to read code from mem');
|
FCallContext.SetError('Failed to read code from mem');
|
||||||
FInitError := True;
|
FInitError := True;
|
||||||
exit;
|
exit;
|
||||||
@ -497,29 +510,28 @@ begin
|
|||||||
|
|
||||||
// Calculate the relative offset between the address of the current instruction
|
// Calculate the relative offset between the address of the current instruction
|
||||||
// and the address of the function we want to call.
|
// and the address of the function we want to call.
|
||||||
if Abs(Int64(FRoutineAddress)-Int64(CurrentIP))>=MaxSIntValue then begin
|
{$PUSH}{$Q-}{$R-}
|
||||||
|
if Abs(Int64(FRoutineAddress-(InsertAddr+TEMP_CODE_LEN))) >= High(Int32) then begin
|
||||||
FCallContext.SetError('Calling this function is not supported. Offset to the function that is to be called is too high.');
|
FCallContext.SetError('Calling this function is not supported. Offset to the function that is to be called is too high.');
|
||||||
FInitError := True;
|
FInitError := True;
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
RelAddr := Int32(FRoutineAddress) - (Int32(CurrentIP) + 5); // 5 is the size of the instruction we are about to add.
|
RelAddr := Int64(FRoutineAddress-(InsertAddr+TEMP_CODE_LEN)); // TEMP_CODE_LEN is the size of the instruction we are about to add.
|
||||||
|
{$POP}
|
||||||
|
|
||||||
// Construct the code to call the function.
|
// Construct the code to call the function.
|
||||||
SetLength(Buf, 5);
|
|
||||||
Buf[0] := $e8; // CALL
|
Buf[0] := $e8; // CALL
|
||||||
DW := pointer(@Buf[1]);
|
DW := pointer(@Buf[1]);
|
||||||
DW^ := RelAddr;
|
DW^ := RelAddr;
|
||||||
|
|
||||||
// Overwrite the current code with the new code to call the function
|
// Overwrite the current code with the new code to call the function
|
||||||
if not FProcess.WriteInstructionCode(CurrentIP, 5, Buf[0]) then begin
|
if not FProcess.WriteInstructionCode(InsertAddr, TEMP_CODE_LEN, Buf[0]) then begin
|
||||||
FCallContext.SetError('Failed to write code to mem');
|
FCallContext.SetError('Failed to write code to mem');
|
||||||
FInitError := True;
|
FInitError := True;
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Store the address where the debugee should return at after the function
|
FController.CurrentThread.SetInstructionPointerRegisterValue(InsertAddr);
|
||||||
// finished. It is used to determine if the call has been completed succesfully.
|
|
||||||
FReturnAdress := CurrentIP + 5;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDbgControllerCallRoutineCmd.DoContinue(AProcess: TDbgProcess;
|
function TDbgControllerCallRoutineCmd.DoContinue(AProcess: TDbgProcess;
|
||||||
@ -533,7 +545,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
case FStep of
|
case FStep of
|
||||||
sSingleStep: AProcess.Continue(AProcess, AThread, True); // Single step into the function
|
sSingleStepInto, sSingleStepOver: AProcess.Continue(AProcess, AThread, True); // Single step into the function
|
||||||
sRunRoutine: AProcess.Continue(AProcess, AThread, False); // Continue running the function
|
sRunRoutine: AProcess.Continue(AProcess, AThread, False); // Continue running the function
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -553,14 +565,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
case FStep of
|
case FStep of
|
||||||
sSingleStep: begin
|
sSingleStepInto: begin
|
||||||
// The debugee is in the routine now. Restore the original code.
|
// The debugee is in the routine now. Restore the original code.
|
||||||
// (Remove the code that made the debugee jump into this routine)
|
// (Remove the code that made the debugee jump into this routine)
|
||||||
RestoreOriginalCode;
|
RestoreOriginalCode;
|
||||||
// Set a breakpoint at the return-adres, so the debugee stops when the
|
// Set a breakpoint at the return-adres, so the debugee stops when the
|
||||||
// routine has been completed.
|
// routine has been completed.
|
||||||
if AnEvent=deBreakpoint then begin
|
if AnEvent=deBreakpoint then begin
|
||||||
SetHiddenBreakpointAtReturnAddress(FReturnAdress);
|
SetHiddenBreakpointAtReturnAddress(FReturnAddress);
|
||||||
AnEvent := deInternalContinue;
|
AnEvent := deInternalContinue;
|
||||||
Finished := false;
|
Finished := false;
|
||||||
FStep := sRunRoutine;
|
FStep := sRunRoutine;
|
||||||
@ -572,6 +584,19 @@ begin
|
|||||||
Finished := True;
|
Finished := True;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
sSingleStepOver: begin
|
||||||
|
FStep := sRunRoutine;
|
||||||
|
if AnEvent=deBreakpoint then begin
|
||||||
|
AnEvent := deInternalContinue;
|
||||||
|
Finished := false;
|
||||||
|
end else begin
|
||||||
|
assert(False, 'TDbgControllerCallRoutineCmd.DoResolveEvent: False / failed single step, should never happen');
|
||||||
|
FCallContext.SetError('Failed to make call');
|
||||||
|
FThread.ClearExceptionSignal;
|
||||||
|
RestoreState;
|
||||||
|
Finished := True;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
sRunRoutine: begin
|
sRunRoutine: begin
|
||||||
// Now the debugee has stopped while running the routine.
|
// Now the debugee has stopped while running the routine.
|
||||||
|
|
||||||
@ -581,7 +606,6 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
if not (AnEvent in [deException, deBreakpoint, deHardCodedBreakpoint, deExitProcess]) then begin
|
if not (AnEvent in [deException, deBreakpoint, deHardCodedBreakpoint, deExitProcess]) then begin
|
||||||
//deCreateProcess, deFinishedStep
|
//deCreateProcess, deFinishedStep
|
||||||
// Bail out. It can be anything, even deExitProcess. Maybe that handling
|
// Bail out. It can be anything, even deExitProcess. Maybe that handling
|
||||||
@ -593,8 +617,10 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
CurrentIP := FController.CurrentThread.GetInstructionPointerRegisterValue;
|
CurrentIP := FController.CurrentThread.GetInstructionPointerRegisterValue;
|
||||||
if CurrentIP<>FReturnAdress then
|
|
||||||
begin
|
|
||||||
|
if CurrentIP<>FReturnAddress then
|
||||||
|
begin
|
||||||
// If we are not at the return-adres, the debugee has stopped due to some
|
// If we are not at the return-adres, the debugee has stopped due to some
|
||||||
// unforeseen reason. Skip setting up the call-context, but assign an
|
// unforeseen reason. Skip setting up the call-context, but assign an
|
||||||
// error instead.
|
// error instead.
|
||||||
@ -603,14 +629,24 @@ begin
|
|||||||
// at an actual breakpoint.
|
// at an actual breakpoint.
|
||||||
FCallContext.SetError('The function stopped unexpectedly. (Breakpoint, Exception, etc)')
|
FCallContext.SetError('The function stopped unexpectedly. (Breakpoint, Exception, etc)')
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
// Clear any (pending) signals that were sent to the application during
|
// Clear any (pending) signals that were sent to the application during
|
||||||
// the function-call.
|
// the function-call.
|
||||||
AnEventThread.ClearExceptionSignal;
|
AnEventThread.ClearExceptionSignal;
|
||||||
FCallContext.SetError('The function stopped due to an exception.')
|
FCallContext.SetError('The function stopped due to an exception.')
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
if (FThread.GetStackPointerRegisterValue < FReturnStackPointer)
|
||||||
|
then begin
|
||||||
|
// TODO: check for FCurrentProcess.CurrentBreakpoint ??
|
||||||
|
// in recursion
|
||||||
|
AnEvent := deInternalContinue;
|
||||||
|
Finished := false;
|
||||||
|
FStep := sSingleStepOver; // step over breakpoint
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
// We are at the return-adres. (Phew...)
|
// We are at the return-adres. (Phew...)
|
||||||
// Store the necessary data into the context to obtain the function-result
|
// Store the necessary data into the context to obtain the function-result
|
||||||
// later
|
// later
|
||||||
@ -636,7 +672,7 @@ begin
|
|||||||
if not FHasOrigCodeRead then
|
if not FHasOrigCodeRead then
|
||||||
exit;
|
exit;
|
||||||
FHasOrigCodeRead := False;
|
FHasOrigCodeRead := False;
|
||||||
if not FProcess.WriteInstructionCode(FOriginalInstructionPointer, Length(FOriginalCode), FOriginalCode[0]) then begin
|
if not FProcess.WriteInstructionCode(FNewCodeAddress, Length(FOriginalCode), FOriginalCode[0]) then begin
|
||||||
// There is no recovery from here. Attempt to exti somewhat graceful
|
// There is no recovery from here. Attempt to exti somewhat graceful
|
||||||
HandleUnrecoverable;
|
HandleUnrecoverable;
|
||||||
FCallContext.SetError('Failed to restore target app after call. Terminating');
|
FCallContext.SetError('Failed to restore target app after call. Terminating');
|
||||||
|
@ -123,6 +123,7 @@ type
|
|||||||
|
|
||||||
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
||||||
function GetStackPointerRegisterValue: TDbgPtr; override;
|
function GetStackPointerRegisterValue: TDbgPtr; override;
|
||||||
|
procedure SetSetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
|
||||||
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
||||||
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
||||||
end;
|
end;
|
||||||
@ -562,6 +563,10 @@ begin
|
|||||||
result := FThreadState64.__rsp;
|
result := FThreadState64.__rsp;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TDbgDarwinThread.SetSetInstructionPointerRegisterValue(AValue: TDbgPtr);
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TDbgDarwinThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
procedure TDbgDarwinThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
||||||
begin
|
begin
|
||||||
end;
|
end;
|
||||||
|
@ -297,6 +297,7 @@ type
|
|||||||
|
|
||||||
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
||||||
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
||||||
|
procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
|
||||||
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
||||||
function GetStackPointerRegisterValue: TDbgPtr; override;
|
function GetStackPointerRegisterValue: TDbgPtr; override;
|
||||||
end;
|
end;
|
||||||
@ -830,6 +831,16 @@ begin
|
|||||||
result := FUserRegs.regs64[rbp];
|
result := FUserRegs.regs64[rbp];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TDbgLinuxThread.SetInstructionPointerRegisterValue(AValue: TDbgPtr);
|
||||||
|
begin
|
||||||
|
if not FHasThreadState then
|
||||||
|
exit;
|
||||||
|
if Process.Mode=dm32 then
|
||||||
|
FUserRegs.regs32[eip] := AValue
|
||||||
|
else
|
||||||
|
FUserRegs.regs64[rip] := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TDbgLinuxThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
procedure TDbgLinuxThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
||||||
begin
|
begin
|
||||||
if not FHasThreadState then
|
if not FHasThreadState then
|
||||||
|
@ -169,6 +169,7 @@ type
|
|||||||
procedure RestoreRegisters; override;
|
procedure RestoreRegisters; override;
|
||||||
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
||||||
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
||||||
|
procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
|
||||||
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
||||||
function GetStackPointerRegisterValue: TDbgPtr; override;
|
function GetStackPointerRegisterValue: TDbgPtr; override;
|
||||||
property Process;
|
property Process;
|
||||||
@ -1978,6 +1979,21 @@ begin
|
|||||||
{$endif}
|
{$endif}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TDbgWinThread.SetInstructionPointerRegisterValue(AValue: TDbgPtr);
|
||||||
|
begin
|
||||||
|
if FCurrentContext = nil then
|
||||||
|
exit;
|
||||||
|
{$ifdef cpui386}
|
||||||
|
FCurrentContext^.def.Eip := AValue;
|
||||||
|
{$else}
|
||||||
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
||||||
|
FCurrentContext^.WOW.Eip := AValue
|
||||||
|
else
|
||||||
|
FCurrentContext^.def.Rip := AValue;
|
||||||
|
{$endif}
|
||||||
|
FThreadContextChanged:=True;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TDbgWinThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
procedure TDbgWinThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
||||||
begin
|
begin
|
||||||
if FCurrentContext = nil then
|
if FCurrentContext = nil then
|
||||||
|
Loading…
Reference in New Issue
Block a user