mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-03 00:00:39 +02:00
FpDebug: Handle hitting breakpoints, that have been removed while the event was waiting. Still need to re-execute the asm instruction that was hidden by int3
git-svn-id: trunk@61839 -
This commit is contained in:
parent
2f812db331
commit
876682fc9d
@ -139,7 +139,10 @@ type
|
|||||||
FProcess: TDbgProcess;
|
FProcess: TDbgProcess;
|
||||||
FID: Integer;
|
FID: Integer;
|
||||||
FHandle: THandle;
|
FHandle: THandle;
|
||||||
FIsAtBreakInstruction: Boolean;
|
|
||||||
|
FPausedAtRemovedBreakPointState: (rbUnknown, rbNone, rbFound{, rbFoundAndDec});
|
||||||
|
FPausedAtRemovedBreakPointAddress: TDBGPtr;
|
||||||
|
|
||||||
function GetRegisterValueList: TDbgRegisterValueList;
|
function GetRegisterValueList: TDbgRegisterValueList;
|
||||||
protected
|
protected
|
||||||
FCallStackEntryList: TDbgCallstackEntryList;
|
FCallStackEntryList: TDbgCallstackEntryList;
|
||||||
@ -152,8 +155,11 @@ type
|
|||||||
procedure LoadRegisterValues; virtual;
|
procedure LoadRegisterValues; virtual;
|
||||||
property Process: TDbgProcess read FProcess;
|
property Process: TDbgProcess read FProcess;
|
||||||
function ResetInstructionPointerAfterBreakpoint: boolean; virtual; abstract;
|
function ResetInstructionPointerAfterBreakpoint: boolean; virtual; abstract;
|
||||||
|
procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change
|
||||||
|
procedure ValidateRemovedBreakPointInfo;
|
||||||
public
|
public
|
||||||
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual;
|
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual;
|
||||||
|
function HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean; // include removed breakpoints that (may have) already triggered
|
||||||
procedure CheckAndResetInstructionPointerAfterBreakpoint;
|
procedure CheckAndResetInstructionPointerAfterBreakpoint;
|
||||||
procedure BeforeContinue; virtual;
|
procedure BeforeContinue; virtual;
|
||||||
function AddWatchpoint(AnAddr: TDBGPtr): integer; virtual;
|
function AddWatchpoint(AnAddr: TDBGPtr): integer; virtual;
|
||||||
@ -175,7 +181,6 @@ type
|
|||||||
property NextIsSingleStep: boolean read FNextIsSingleStep write FNextIsSingleStep;
|
property NextIsSingleStep: boolean read FNextIsSingleStep write FNextIsSingleStep;
|
||||||
property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList;
|
property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList;
|
||||||
property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList;
|
property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList;
|
||||||
property IsAtBreakInstruction: boolean read FIsAtBreakInstruction; // Is stopped at Int3 code. Needs to exec orig instruction instead
|
|
||||||
end;
|
end;
|
||||||
TDbgThreadClass = class of TDbgThread;
|
TDbgThreadClass = class of TDbgThread;
|
||||||
|
|
||||||
@ -241,7 +246,7 @@ type
|
|||||||
procedure RemoveLocotion(const ALocation: TDBGPtr; const AInternalBreak: TFpInternalBreakpoint);
|
procedure RemoveLocotion(const ALocation: TDBGPtr; const AInternalBreak: TFpInternalBreakpoint);
|
||||||
function GetInternalBreaksAtLocation(const ALocation: TDBGPtr): TFpInternalBreakpointArray;
|
function GetInternalBreaksAtLocation(const ALocation: TDBGPtr): TFpInternalBreakpointArray;
|
||||||
function GetOrigValueAtLocation(const ALocation: TDBGPtr): Byte; // returns Int3, if there is no break at this location
|
function GetOrigValueAtLocation(const ALocation: TDBGPtr): Byte; // returns Int3, if there is no break at this location
|
||||||
function HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean; // returns Int3, if there is no break at this location
|
function HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean;
|
||||||
function GetEnumerator: TBreakLocationMapEnumerator;
|
function GetEnumerator: TBreakLocationMapEnumerator;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -376,6 +381,7 @@ type
|
|||||||
function GetLib(const AHandle: THandle; out ALib: TDbgLibrary): Boolean;
|
function GetLib(const AHandle: THandle; out ALib: TDbgLibrary): Boolean;
|
||||||
function GetThread(const AID: Integer; out AThread: TDbgThread): Boolean;
|
function GetThread(const AID: Integer; out AThread: TDbgThread): Boolean;
|
||||||
procedure RemoveBreak(const ABreakPoint: TFpInternalBreakpoint);
|
procedure RemoveBreak(const ABreakPoint: TFpInternalBreakpoint);
|
||||||
|
procedure DoBeforeBreakLocationMapChange;
|
||||||
function HasBreak(const ALocation: TDbgPtr): Boolean; // TODO: remove, once an address can have many breakpoints
|
function HasBreak(const ALocation: TDbgPtr): Boolean; // TODO: remove, once an address can have many breakpoints
|
||||||
procedure RemoveThread(const AID: DWord);
|
procedure RemoveThread(const AID: DWord);
|
||||||
function FormatAddress(const AAddress): String;
|
function FormatAddress(const AAddress): String;
|
||||||
@ -617,6 +623,7 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
FProcess.DoBeforeBreakLocationMapChange; // Only if a new breakpoint is set => memory changed
|
||||||
new(LocData);
|
new(LocData);
|
||||||
LocData^.ErrorSetting := not FProcess.InsertBreakInstructionCode(ALocation, LocData^.OrigValue);
|
LocData^.ErrorSetting := not FProcess.InsertBreakInstructionCode(ALocation, LocData^.OrigValue);
|
||||||
LocData^.IsBreakList := False;
|
LocData^.IsBreakList := False;
|
||||||
@ -662,6 +669,7 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
FProcess.DoBeforeBreakLocationMapChange; // Only if a breakpoint is removed => memory changed
|
||||||
if not LocData^.ErrorSetting then
|
if not LocData^.ErrorSetting then
|
||||||
FProcess.RemoveBreakInstructionCode(ALocation, LocData^.OrigValue);
|
FProcess.RemoveBreakInstructionCode(ALocation, LocData^.OrigValue);
|
||||||
Delete(ALocation);
|
Delete(ALocation);
|
||||||
@ -1237,6 +1245,7 @@ function TDbgProcess.ResolveDebugEvent(AThread: TDbgThread): TFPDEvent;
|
|||||||
var
|
var
|
||||||
CurrentAddr: TDBGPtr;
|
CurrentAddr: TDBGPtr;
|
||||||
begin
|
begin
|
||||||
|
AThread.ValidateRemovedBreakPointInfo;
|
||||||
result := AnalyseDebugEvent(AThread);
|
result := AnalyseDebugEvent(AThread);
|
||||||
|
|
||||||
if result = deBreakpoint then
|
if result = deBreakpoint then
|
||||||
@ -1365,6 +1374,14 @@ begin
|
|||||||
FCurrentBreakpoint := nil;
|
FCurrentBreakpoint := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TDbgProcess.DoBeforeBreakLocationMapChange;
|
||||||
|
var
|
||||||
|
t: TDbgThread;
|
||||||
|
begin
|
||||||
|
for t in FThreadMap do
|
||||||
|
t.DoBeforeBreakLocationMapChange;
|
||||||
|
end;
|
||||||
|
|
||||||
function TDbgProcess.HasBreak(const ALocation: TDbgPtr): Boolean;
|
function TDbgProcess.HasBreak(const ALocation: TDbgPtr): Boolean;
|
||||||
begin
|
begin
|
||||||
if FBreakMap = nil then
|
if FBreakMap = nil then
|
||||||
@ -1654,6 +1671,41 @@ begin
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TDbgThread.DoBeforeBreakLocationMapChange;
|
||||||
|
var
|
||||||
|
t: TDBGPtr;
|
||||||
|
begin
|
||||||
|
debugln(FPausedAtRemovedBreakPointState <> rbUnknown, ['@@@@@@@ MAP tid ', ID, ' ', ord(FPausedAtRemovedBreakPointState), ' ',dbghex(FPausedAtRemovedBreakPointAddress)]);
|
||||||
|
if (FPausedAtRemovedBreakPointState <> rbUnknown) and
|
||||||
|
(FPausedAtRemovedBreakPointAddress = GetInstructionPointerRegisterValue) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
t := GetInstructionPointerRegisterValue;
|
||||||
|
if Process.HasInsertedBreakInstructionAtLocation(t - 1) then begin
|
||||||
|
(* There is a chance, that the code jumped to this Addr, instead of executing the breakpoint.
|
||||||
|
But if the next signal for this thread is a breakpoint at this address, then
|
||||||
|
it must be handled (even if the breakpoint has been removed since)
|
||||||
|
*)
|
||||||
|
FPausedAtRemovedBreakPointAddress := t;
|
||||||
|
FPausedAtRemovedBreakPointState := rbFound;
|
||||||
|
debugln(['####### STORE ',dbghex( t), ' for id ', ID]);
|
||||||
|
// Most likely the debugger should see the previous address (unless we got here
|
||||||
|
// by jump.
|
||||||
|
// Call something like ResetInstructionPointerAfterBreakpointForPendingSignal; virtual;
|
||||||
|
////ResetInstructionPointerAfterBreakpoint;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
FPausedAtRemovedBreakPointState := rbNone;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDbgThread.ValidateRemovedBreakPointInfo;
|
||||||
|
begin
|
||||||
|
if (FPausedAtRemovedBreakPointState <> rbUnknown) and
|
||||||
|
(FPausedAtRemovedBreakPointAddress <> GetInstructionPointerRegisterValue)
|
||||||
|
then
|
||||||
|
FPausedAtRemovedBreakPointState := rbUnknown;
|
||||||
|
end;
|
||||||
|
|
||||||
constructor TDbgThread.Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle);
|
constructor TDbgThread.Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle);
|
||||||
begin
|
begin
|
||||||
FID := AID;
|
FID := AID;
|
||||||
@ -1663,18 +1715,32 @@ begin
|
|||||||
inherited Create;
|
inherited Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDbgThread.HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean;
|
||||||
|
var
|
||||||
|
t: TDBGPtr;
|
||||||
|
begin
|
||||||
|
t := GetInstructionPointerRegisterValue;
|
||||||
|
Result := ( (FPausedAtRemovedBreakPointState = rbFound) and
|
||||||
|
(FPausedAtRemovedBreakPointAddress = t) ) or
|
||||||
|
Process.HasInsertedBreakInstructionAtLocation(t - 1);
|
||||||
|
debugln(['####### CHECK ',result, ' for id ', ID, ' stored ', FPausedAtRemovedBreakPointState=rbFound, ' ',FPausedAtRemovedBreakPointAddress=t, ' ',dbghex(t), ' ', dbghex(FPausedAtRemovedBreakPointAddress)]);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TDbgThread.CheckAndResetInstructionPointerAfterBreakpoint;
|
procedure TDbgThread.CheckAndResetInstructionPointerAfterBreakpoint;
|
||||||
begin
|
begin
|
||||||
// todo: check that the breakpoint is NOT in the temp removed list
|
// todo: check that the breakpoint is NOT in the temp removed list
|
||||||
if not Process.HasInsertedBreakInstructionAtLocation(GetInstructionPointerRegisterValue - 1) then
|
if HasInsertedBreakInstructionAtLocation(GetInstructionPointerRegisterValue - 1)
|
||||||
exit;
|
then begin
|
||||||
FIsAtBreakInstruction := True;
|
FPausedAtRemovedBreakPointState := rbFound;
|
||||||
ResetInstructionPointerAfterBreakpoint;
|
ResetInstructionPointerAfterBreakpoint;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TDbgThread.BeforeContinue;
|
procedure TDbgThread.BeforeContinue;
|
||||||
begin
|
begin
|
||||||
// Do nothing
|
// On Windows this is only called, if this was the signalled thread
|
||||||
|
FPausedAtRemovedBreakPointState := rbUnknown;
|
||||||
|
FPausedAtRemovedBreakPointAddress := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDbgThread.AddWatchpoint(AnAddr: TDBGPtr): integer;
|
function TDbgThread.AddWatchpoint(AnAddr: TDBGPtr): integer;
|
||||||
|
@ -525,6 +525,7 @@ var
|
|||||||
aKernResult: kern_return_t;
|
aKernResult: kern_return_t;
|
||||||
old_StateCnt: mach_msg_Type_number_t;
|
old_StateCnt: mach_msg_Type_number_t;
|
||||||
begin
|
begin
|
||||||
|
inherited;
|
||||||
if Process.CurrentWatchpoint>-1 then
|
if Process.CurrentWatchpoint>-1 then
|
||||||
begin
|
begin
|
||||||
if Process.Mode=dm32 then
|
if Process.Mode=dm32 then
|
||||||
@ -868,7 +869,7 @@ begin
|
|||||||
{$endif linux}
|
{$endif linux}
|
||||||
{$ifdef darwin}
|
{$ifdef darwin}
|
||||||
AThread.NextIsSingleStep:=SingleStep;
|
AThread.NextIsSingleStep:=SingleStep;
|
||||||
AThread.BeforeContinue;
|
AThread.BeforeContinue; // TODO: All threads
|
||||||
if HasInsertedBreakInstructionAtLocation(AThread.GetInstructionPointerRegisterValue) then begin
|
if HasInsertedBreakInstructionAtLocation(AThread.GetInstructionPointerRegisterValue) then begin
|
||||||
TempRemoveBreakInstructionCode(AThread.GetInstructionPointerRegisterValue);
|
TempRemoveBreakInstructionCode(AThread.GetInstructionPointerRegisterValue);
|
||||||
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal));
|
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal));
|
||||||
|
@ -631,6 +631,7 @@ begin
|
|||||||
if not FIsPaused then
|
if not FIsPaused then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
inherited;
|
||||||
if Process.CurrentWatchpoint>-1 then
|
if Process.CurrentWatchpoint>-1 then
|
||||||
WriteDebugReg(6, 0);
|
WriteDebugReg(6, 0);
|
||||||
|
|
||||||
|
@ -1376,9 +1376,12 @@ end;
|
|||||||
|
|
||||||
procedure TDbgWinThread.BeforeContinue;
|
procedure TDbgWinThread.BeforeContinue;
|
||||||
begin
|
begin
|
||||||
if Process.ProcessID <> MDebugEvent.dwProcessId then
|
if ID <> MDebugEvent.dwThreadId then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
|
||||||
|
inherited;
|
||||||
|
|
||||||
if (FCurrentContext <> nil) and
|
if (FCurrentContext <> nil) and
|
||||||
(FCurrentContext^.Dr6 <> $ffff0ff0) then
|
(FCurrentContext^.Dr6 <> $ffff0ff0) then
|
||||||
begin
|
begin
|
||||||
@ -1427,6 +1430,7 @@ begin
|
|||||||
{$else}
|
{$else}
|
||||||
Dec(Context^.Rip);
|
Dec(Context^.Rip);
|
||||||
dec(FCurrentContext^.Rip);
|
dec(FCurrentContext^.Rip);
|
||||||
|
debugln(['TDbgWinThread.ResetInstructionPointerAfterBreakpoint ',ID, ' before ', dbghex(FCurrentContext^.Rip), ' / ',Context^.Rip]);
|
||||||
{$endif}
|
{$endif}
|
||||||
|
|
||||||
if not SetThreadContext(Handle, Context^)
|
if not SetThreadContext(Handle, Context^)
|
||||||
|
Loading…
Reference in New Issue
Block a user