LazDebuggerFp (pure): Rewrote/refactored ResolveDebugEvent. As much as possible code moved from the os-specific classes to the general classes. Now TDbgProcess and TDbgThread only handle single-stepping and the handling of breakpoints and exceptions. Other commands (like step-line, step-into-line etc) are implemented as childs of TDbgControllerCmd. All specific handling is done in those classes.

git-svn-id: trunk@45590 -
This commit is contained in:
joost 2014-06-20 15:22:45 +00:00
parent 7d00cd341d
commit 843f23eafd
4 changed files with 561 additions and 292 deletions

View File

@ -37,9 +37,8 @@ unit FpDbgClasses;
interface interface
uses uses
Classes, SysUtils, Maps, FpDbgDwarf, FpDbgUtil, FpDbgWinExtra, FpDbgLoader, Classes, SysUtils, Maps, FpDbgDwarf, FpDbgUtil, FpDbgLoader,
FpDbgInfo, FpdMemoryTools, LazLoggerBase, LazClasses, DbgIntfBaseTypes, fgl, FpDbgInfo, FpdMemoryTools, LazLoggerBase, LazClasses, DbgIntfBaseTypes, fgl,
FpDbgDisasX86,
fpDbgSymTableContext, fpDbgSymTableContext,
FpDbgDwarfDataClasses; FpDbgDwarfDataClasses;
@ -130,11 +129,11 @@ type
TDbgThread = class(TObject) TDbgThread = class(TObject)
private private
FNextIsSingleStep: boolean;
FProcess: TDbgProcess; FProcess: TDbgProcess;
FID: Integer; FID: Integer;
FHandle: THandle; FHandle: THandle;
FSingleStepping: Boolean; FNeedIPDecrement: boolean;
FStepping: Boolean;
function GetRegisterValueList: TDbgRegisterValueList; function GetRegisterValueList: TDbgRegisterValueList;
protected protected
FCallStackEntryList: TDbgCallstackEntryList; FCallStackEntryList: TDbgCallstackEntryList;
@ -144,13 +143,6 @@ type
FStoreStepSrcLineNo: integer; FStoreStepSrcLineNo: integer;
FStoreStepStackFrame: TDBGPtr; FStoreStepStackFrame: TDBGPtr;
FStoreStepFuncAddr: TDBGPtr; FStoreStepFuncAddr: TDBGPtr;
FHiddenWatchpointInto: integer;
FHiddenWatchpointOut: integer;
FHiddenBreakpoint: TDbgBreakpoint;
FStepOut: boolean;
FInto: boolean;
FIntoDepth: boolean;
procedure StoreStepInfo;
procedure LoadRegisterValues; virtual; procedure LoadRegisterValues; virtual;
property Process: TDbgProcess read FProcess; property Process: TDbgProcess read FProcess;
public public
@ -161,22 +153,13 @@ type
function RemoveWatchpoint(AnId: integer): boolean; virtual; function RemoveWatchpoint(AnId: integer): boolean; virtual;
procedure PrepareCallStackEntryList(AFrameRequired: Integer = -1); virtual; procedure PrepareCallStackEntryList(AFrameRequired: Integer = -1); virtual;
procedure ClearCallStack; procedure ClearCallStack;
procedure AfterHitBreak;
procedure ClearHWBreakpoint;
destructor Destroy; override; destructor Destroy; override;
function SingleStep: Boolean; virtual;
function StepLine: Boolean; virtual;
function Next: Boolean; virtual;
function StepInto: Boolean; virtual;
function StepOut: Boolean; virtual;
function IntNext: Boolean; virtual;
function CompareStepInfo: boolean; function CompareStepInfo: boolean;
procedure StoreStepInfo;
property ID: Integer read FID; property ID: Integer read FID;
property Handle: THandle read FHandle; property Handle: THandle read FHandle;
property SingleStepping: boolean read FSingleStepping write FSingleStepping; property NextIsSingleStep: boolean read FNextIsSingleStep write FNextIsSingleStep;
property Stepping: boolean read FStepping;
property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList; property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList;
property HiddenBreakpoint: TDbgBreakpoint read FHiddenBreakpoint;
property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList; property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList;
end; end;
TDbgThreadClass = class of TDbgThread; TDbgThreadClass = class of TDbgThread;
@ -280,6 +263,8 @@ type
procedure MaskBreakpointsInReadData(const AAdress: TDbgPtr; const ASize: Cardinal; var AData); procedure MaskBreakpointsInReadData(const AAdress: TDbgPtr; const ASize: Cardinal; var AData);
// Should create a TDbgThread-instance for the given ThreadIdentifier. // Should create a TDbgThread-instance for the given ThreadIdentifier.
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; virtual; abstract; function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; virtual; abstract;
// Should analyse why the debugger has stopped.
function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; virtual; abstract;
public public
class function StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; virtual; class function StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; virtual;
constructor Create(const AName: string; const AProcessID, AThreadID: Integer; AOnLog: TOnLog); virtual; constructor Create(const AName: string; const AProcessID, AThreadID: Integer; AOnLog: TOnLog); virtual;
@ -290,6 +275,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;
function RemoveBreak(const ALocation: TDbgPtr): Boolean; function RemoveBreak(const ALocation: TDbgPtr): Boolean;
function HasBreak(const ALocation: TDbgPtr): Boolean;
procedure RemoveThread(const AID: DWord); procedure RemoveThread(const AID: DWord);
procedure Log(const AString: string; const ALogLevel: TFPDLogLevel = dllDebug); procedure Log(const AString: string; const ALogLevel: TFPDLogLevel = dllDebug);
procedure Log(const AString: string; const Options: array of const; const ALogLevel: TFPDLogLevel = dllDebug); procedure Log(const AString: string; const Options: array of const; const ALogLevel: TFPDLogLevel = dllDebug);
@ -302,9 +288,9 @@ type
function ReadString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: String): Boolean; virtual; function ReadString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: String): Boolean; virtual;
function ReadWString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: WideString): Boolean; virtual; function ReadWString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: WideString): Boolean; virtual;
function Continue(AProcess: TDbgProcess; AThread: TDbgThread): boolean; virtual; function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; virtual;
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; virtual; abstract; function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; virtual; abstract;
function ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; virtual; abstract; function ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; virtual;
function AddThread(AThreadIdentifier: THandle): TDbgThread; function AddThread(AThreadIdentifier: THandle): TDbgThread;
@ -821,11 +807,55 @@ begin
result := false; result := false;
end; end;
function TDbgProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread): boolean; function TDbgProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread;
SingleStep: boolean): boolean;
begin begin
result := false; result := false;
end; end;
function TDbgProcess.ResolveDebugEvent(AThread: TDbgThread): TFPDEvent;
var
CurrentAddr: TDBGPtr;
begin
result := AnalyseDebugEvent(AThread);
if result = deBreakpoint then
begin
if assigned(FCurrentBreakpoint) then
begin
// When a breakpoint has been hit, the debugger always continues with
// a single-step to jump over the breakpoint. Thereafter the breakpoint
// has to be set again.
FCurrentBreakpoint.SetBreak;
if not AThread.NextIsSingleStep then
// In this case the debugger has to continue. The debugger did only
// stop to be able to reset the breakpoint again. It was not a 'normal'
// singlestep.
result := deInternalContinue;
end;
// Determine the address where the execution has stopped
CurrentAddr:=GetInstructionPointerRegisterValue;
if not (FMainThread.NextIsSingleStep or assigned(FCurrentBreakpoint)) then
begin
// The debugger did not stop due to single-stepping, so a breakpoint has
// been hit. But breakpoints stop *after* they have been hit. So the
// decrement the CurrentAddr.
FMainThread.FNeedIPDecrement:=true;
dec(CurrentAddr);
end
else
FMainThread.FNeedIPDecrement:=false;
FCurrentBreakpoint:=nil;
AThread.NextIsSingleStep:=false;
// Whatever reason there was to change the result to deInternalContinue,
// if a breakpoint has been hit, always trigger it...
if DoBreak(CurrentAddr, FMainThread.ID) then
result := deBreakpoint;
end
end;
function TDbgProcess.AddThread(AThreadIdentifier: THandle): TDbgThread; function TDbgProcess.AddThread(AThreadIdentifier: THandle): TDbgThread;
var var
IsMainThread: boolean; IsMainThread: boolean;
@ -860,6 +890,14 @@ begin
end; end;
end; end;
function TDbgProcess.HasBreak(const ALocation: TDbgPtr): Boolean;
begin
if FBreakMap = nil then
Result := False
else
result := FBreakMap.HasId(ALocation);
end;
procedure TDbgProcess.RemoveThread(const AID: DWord); procedure TDbgProcess.RemoveThread(const AID: DWord);
begin begin
if FThreadMap = nil then Exit; if FThreadMap = nil then Exit;
@ -953,7 +991,6 @@ procedure TDbgProcess.MaskBreakpointsInReadData(const AAdress: TDbgPtr; const AS
var var
BreakLocation: TDBGPtr; BreakLocation: TDBGPtr;
Bp: TDbgBreakpoint; Bp: TDbgBreakpoint;
DataArr: PByteArray;
Iterator: TMapIterator; Iterator: TMapIterator;
begin begin
iterator := TMapIterator.Create(FBreakMap); iterator := TMapIterator.Create(FBreakMap);
@ -1002,7 +1039,7 @@ begin
sym := FProcess.FindSymbol(AnAddr); sym := FProcess.FindSymbol(AnAddr);
if assigned(sym) then if assigned(sym) then
begin begin
result := (((FStoreStepSrcFilename=sym.FileName) and (FStoreStepSrcLineNo=sym.Line)) or FStepOut) and result := (((FStoreStepSrcFilename=sym.FileName) and (FStoreStepSrcLineNo=sym.Line)) {or FStepOut}) and
(FStoreStepFuncAddr=sym.Address.Address); (FStoreStepFuncAddr=sym.Address.Address);
if not result and (FStoreStepFuncAddr<>sym.Address.Address) then if not result and (FStoreStepFuncAddr<>sym.Address.Address) then
begin begin
@ -1012,7 +1049,7 @@ begin
// This because when stepping out of a procedure, the first asm-instruction // This because when stepping out of a procedure, the first asm-instruction
// could still be part of the instruction-line that made the call to the // could still be part of the instruction-line that made the call to the
// procedure in the first place. // procedure in the first place.
if (sym is TDbgDwarfSymbolBase) and not FInto then if (sym is TDbgDwarfSymbolBase) {and not FInto} then
begin begin
CU := TDbgDwarfSymbolBase(sym).CompilationUnit; CU := TDbgDwarfSymbolBase(sym).CompilationUnit;
if cu.GetLineAddress(sym.FileName, sym.Line)<>AnAddr then if cu.GetLineAddress(sym.FileName, sym.Line)<>AnAddr then
@ -1047,21 +1084,12 @@ begin
// Do nothing // Do nothing
end; end;
function TDbgThread.IntNext: Boolean;
begin
result := StepLine;
FStepping:=result;
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;
FHandle := AHandle; FHandle := AHandle;
FProcess := AProcess; FProcess := AProcess;
FRegisterValueList:=TDbgRegisterValueList.Create; FRegisterValueList:=TDbgRegisterValueList.Create;
FHiddenWatchpointInto:=-1;
FHiddenWatchpointOut:=-1;
inherited Create; inherited Create;
end; end;
@ -1072,13 +1100,13 @@ end;
function TDbgThread.AddWatchpoint(AnAddr: TDBGPtr): integer; function TDbgThread.AddWatchpoint(AnAddr: TDBGPtr): integer;
begin begin
FProcess.log('Hardware watchpoints are nog available.'); FProcess.log('Hardware watchpoints are not available.');
result := -1; result := -1;
end; end;
function TDbgThread.RemoveWatchpoint(AnId: integer): boolean; function TDbgThread.RemoveWatchpoint(AnId: integer): boolean;
begin begin
FProcess.log('Hardware watchpoints are nog available.'); FProcess.log('Hardware watchpoints are not available: '+self.classname);
result := false; result := false;
end; end;
@ -1132,29 +1160,6 @@ begin
FCallStackEntryList.Clear; FCallStackEntryList.Clear;
end; end;
procedure TDbgThread.AfterHitBreak;
begin
FStepping:=false;
FInto:=false;
FIntoDepth:=false;
FStepOut:=false;
FreeAndNil(FHiddenBreakpoint);
end;
procedure TDbgThread.ClearHWBreakpoint;
begin
if FHiddenWatchpointOut>-1 then
begin
if RemoveWatchpoint(FHiddenWatchpointOut) then
FHiddenWatchpointOut:=-1;
end;
if FHiddenWatchpointInto>-1 then
begin
if RemoveWatchpoint(FHiddenWatchpointInto) then
FHiddenWatchpointInto:=-1;
end;
end;
destructor TDbgThread.Destroy; destructor TDbgThread.Destroy;
begin begin
FProcess.ThreadDestroyed(Self); FProcess.ThreadDestroyed(Self);
@ -1164,74 +1169,6 @@ begin
inherited; inherited;
end; end;
function TDbgThread.SingleStep: Boolean;
begin
FSingleStepping := True;
Result := true;
end;
function TDbgThread.StepLine: Boolean;
var
CodeBin: array[0..20] of byte;
p: pointer;
ADump,
AStatement: string;
CallInstr: boolean;
begin
if FInto and FIntoDepth then
begin
FHiddenWatchpointInto := AddWatchpoint(Process.GetStackPointerRegisterValue-4);
FHiddenWatchpointOut := AddWatchpoint(Process.GetStackBasePointerRegisterValue+4);
result := (FHiddenWatchpointInto<>-1) and (FHiddenWatchpointOut<>-1);
Exit;
end;
CallInstr:=false;
if FProcess.ReadData(FProcess.GetInstructionPointerRegisterValue,sizeof(CodeBin),CodeBin) then
begin
p := @CodeBin;
Disassemble(p, FProcess.Mode=dm64, ADump, AStatement);
if copy(AStatement,1,4)='call' then
CallInstr:=true;
end;
if CallInstr then
begin
FHiddenBreakpoint := TDbgBreakpoint.Create(FProcess, FProcess.GetInstructionPointerRegisterValue+(PtrUInt(p)-PtrUInt(@codebin)));
if FInto then
begin
FHiddenWatchpointInto := AddWatchpoint(RegisterValueList.FindRegisterByDwarfIndex(4).NumValue-4);
FIntoDepth:=true;
end;
end
else
SingleStep;
Result := True;
end;
function TDbgThread.Next: Boolean;
begin
StoreStepInfo;
result := IntNext;
end;
function TDbgThread.StepInto: Boolean;
begin
StoreStepInfo;
FInto:=true;
FIntoDepth:=false;
result := IntNext;
end;
function TDbgThread.StepOut: Boolean;
begin
result := next;
FStepOut := result;
end;
{ TDbgBreak } { TDbgBreak }
constructor TDbgBreakpoint.Create(const AProcess: TDbgProcess; const ALocation: TDbgPtr); constructor TDbgBreakpoint.Create(const AProcess: TDbgProcess; const ALocation: TDbgPtr);
@ -1259,7 +1196,10 @@ begin
if not Process.GetThread(AThreadId, Thread) then Exit; if not Process.GetThread(AThreadId, Thread) then Exit;
Result := Thread.ResetInstructionPointerAfterBreakpoint; if Thread.FNeedIPDecrement then
Result := Thread.ResetInstructionPointerAfterBreakpoint
else
Result := true;
end; end;
procedure TDbgBreakpoint.ResetBreak; procedure TDbgBreakpoint.ResetBreak;

View File

@ -8,8 +8,9 @@ uses
Classes, Classes,
SysUtils, SysUtils,
Maps, Maps,
FpDbgUtil,
LazLogger, LazLogger,
DbgIntfBaseTypes,
FpDbgDisasX86,
FpDbgClasses; FpDbgClasses;
type type
@ -19,6 +20,71 @@ type
TOnExceptionEvent = procedure(var continue: boolean; const ExceptionClass, ExceptionMessage: string) of object; TOnExceptionEvent = procedure(var continue: boolean; const ExceptionClass, ExceptionMessage: string) of object;
TOnProcessExitEvent = procedure(ExitCode: DWord) of object; TOnProcessExitEvent = procedure(ExitCode: DWord) of object;
TDbgController = class;
{ TDbgControllerCmd }
TDbgControllerCmd = class
protected
FController: TDbgController;
public
constructor Create(AController: TDbgController); virtual;
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); virtual; abstract;
procedure ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean); virtual; abstract;
end;
{ TDbgControllerContinueCmd }
TDbgControllerContinueCmd = class(TDbgControllerCmd)
public
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); override;
procedure ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean); override;
end;
{ TDbgControllerStepIntoInstructionCmd }
TDbgControllerStepIntoInstructionCmd = class(TDbgControllerCmd)
public
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); override;
procedure ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean); override;
end;
{ TDbgControllerStepOverInstructionCmd }
TDbgControllerStepOverInstructionCmd = class(TDbgControllerCmd)
private
FHiddenBreakpoint: TDbgBreakpoint;
FIsSet: boolean;
public
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); override;
procedure ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean); override;
end;
{ TDbgControllerStepOverLineCmd }
TDbgControllerStepOverLineCmd = class(TDbgControllerStepOverInstructionCmd)
private
FInfoStored: boolean;
public
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); override;
procedure ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean); override;
end;
{ TDbgControllerStepIntoLineCmd }
TDbgControllerStepIntoLineCmd = class(TDbgControllerCmd)
private
FInfoStored: boolean;
FStoredStackFrame: TDBGPtr;
FInto: boolean;
FHiddenWatchpointInto: integer;
FHiddenWatchpointOut: integer;
public
constructor Create(AController: TDbgController); override;
procedure DoContinue(AProcess: TDbgProcess; AThread: TDbgThread); override;
procedure ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean); override;
end;
{ TDbgController } { TDbgController }
TDbgController = class TDbgController = class
@ -45,12 +111,14 @@ type
FMainProcess: TDbgProcess; FMainProcess: TDbgProcess;
FCurrentProcess: TDbgProcess; FCurrentProcess: TDbgProcess;
FCurrentThread: TDbgThread; FCurrentThread: TDbgThread;
FCommand: TDbgControllerCmd;
procedure Log(const AString: string; const ALogLevel: TFPDLogLevel = dllDebug); procedure Log(const AString: string; const ALogLevel: TFPDLogLevel = dllDebug);
procedure Log(const AString: string; const Options: array of const; const ALogLevel: TFPDLogLevel = dllDebug); procedure Log(const AString: string; const Options: array of const; const ALogLevel: TFPDLogLevel = dllDebug);
function GetProcess(const AProcessIdentifier: THandle; out AProcess: TDbgProcess): Boolean; function GetProcess(const AProcessIdentifier: THandle; out AProcess: TDbgProcess): Boolean;
public public
constructor Create; virtual; constructor Create; virtual;
destructor Destroy; override; destructor Destroy; override;
procedure InitializeCommand(ACommand: TDbgControllerCmd);
function Run: boolean; function Run: boolean;
procedure Stop; procedure Stop;
procedure StepIntoInstr; procedure StepIntoInstr;
@ -65,6 +133,7 @@ type
property ExecutableFilename: string read FExecutableFilename write SetExecutableFilename; property ExecutableFilename: string read FExecutableFilename write SetExecutableFilename;
property OnLog: TOnLog read FOnLog write SetOnLog; property OnLog: TOnLog read FOnLog write SetOnLog;
property CurrentProcess: TDbgProcess read FCurrentProcess; property CurrentProcess: TDbgProcess read FCurrentProcess;
property CurrentThread: TDbgThread read FCurrentThread;
property MainProcess: TDbgProcess read FMainProcess; property MainProcess: TDbgProcess read FMainProcess;
property Params: TStringList read FParams write SetParams; property Params: TStringList read FParams write SetParams;
property Environment: TStrings read FEnvironment write SetEnvironment; property Environment: TStrings read FEnvironment write SetEnvironment;
@ -79,6 +148,177 @@ type
implementation implementation
{ TDbgControllerStepIntoLineCmd }
constructor TDbgControllerStepIntoLineCmd.Create(AController: TDbgController);
begin
inherited Create(AController);
FHiddenWatchpointInto:=-1;
FHiddenWatchpointOut:=-1;
end;
procedure TDbgControllerStepIntoLineCmd.DoContinue(AProcess: TDbgProcess; AThread: TDbgThread);
begin
if not FInfoStored then
begin
FInfoStored:=true;
FStoredStackFrame:=AProcess.GetStackBasePointerRegisterValue;
AThread.StoreStepInfo;
end;
AProcess.Continue(AProcess, AThread, not FInto);
end;
procedure TDbgControllerStepIntoLineCmd.ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean);
begin
if (FHiddenWatchpointOut<>-1) and FController.FCurrentThread.RemoveWatchpoint(FHiddenWatchpointOut) then
FHiddenWatchpointOut:=-1;
if (FHiddenWatchpointInto<>-1) and FController.FCurrentThread.RemoveWatchpoint(FHiddenWatchpointInto) then
FHiddenWatchpointInto:=-1;
Handled := false;
Finished := (AnEvent<>deInternalContinue);
if Finished then
begin
if FController.FCurrentThread.CompareStepInfo then
begin
AnEvent:=deInternalContinue;
if not FInto and (FStoredStackFrame<>FController.FCurrentProcess.GetStackBasePointerRegisterValue) then
begin
// A sub-procedure has been called, with no debug-information. Use hadrware-breakpoints instead of single-
// stepping for better performance.
FInto:=true;
end;
if FInto then
begin
FHiddenWatchpointInto := FController.FCurrentThread.AddWatchpoint(FController.FCurrentProcess.GetStackPointerRegisterValue-DBGPTRSIZE[FController.FCurrentProcess.Mode]);
FHiddenWatchpointOut := FController.FCurrentThread.AddWatchpoint(FController.FCurrentProcess.GetStackBasePointerRegisterValue+DBGPTRSIZE[FController.FCurrentProcess.Mode]);
assert((FHiddenWatchpointInto<>-1) and (FHiddenWatchpointOut<>-1));
end;
Finished:=false;
end;
end;
end;
{ TDbgControllerStepOverLineCmd }
procedure TDbgControllerStepOverLineCmd.DoContinue(AProcess: TDbgProcess; AThread: TDbgThread);
begin
if not FInfoStored then
begin
FInfoStored:=true;
AThread.StoreStepInfo;
end;
inherited DoContinue(AProcess, AThread);
end;
procedure TDbgControllerStepOverLineCmd.ResolveEvent(var AnEvent: TFPDEvent; out Handled, Finished: boolean);
begin
inherited ResolveEvent(AnEvent, Handled, Finished);
if Finished then
begin
if FController.FCurrentThread.CompareStepInfo then
begin
AnEvent:=deInternalContinue;
FHiddenBreakpoint:=nil;
FIsSet:=false;
Finished:=false;
end;
end;
end;
{ TDbgControllerStepOverInstructionCmd }
procedure TDbgControllerStepOverInstructionCmd.DoContinue(AProcess: TDbgProcess; AThread: TDbgThread);
var
CodeBin: array[0..20] of byte;
p: pointer;
ADump,
AStatement: string;
CallInstr: boolean;
ALocation: TDbgPtr;
begin
if FIsSet then
AProcess.Continue(AProcess, AThread, false)
else
begin
CallInstr:=false;
if AProcess.ReadData(aProcess.GetInstructionPointerRegisterValue,sizeof(CodeBin),CodeBin) then
begin
p := @CodeBin;
Disassemble(p, AProcess.Mode=dm64, ADump, AStatement);
if copy(AStatement,1,4)='call' then
CallInstr:=true;
end;
if CallInstr then
begin
ALocation := AProcess.GetInstructionPointerRegisterValue+(PtrUInt(p)-PtrUInt(@codebin));
if not AProcess.HasBreak(ALocation) then
FHiddenBreakpoint := AProcess.AddBreak(ALocation);
end;
FIsSet:=true;
AProcess.Continue(AProcess, AThread, not CallInstr);
end;
end;
procedure TDbgControllerStepOverInstructionCmd.ResolveEvent(
var AnEvent: TFPDEvent; out Handled, Finished: boolean);
begin
Handled := false;
Finished := (AnEvent<>deInternalContinue);
if Finished then
begin
if assigned(FHiddenBreakpoint) then
begin
FController.FCurrentProcess.RemoveBreak(FHiddenBreakpoint.Location);
FHiddenBreakpoint.Free;
end;
end;
end;
{ TDbgControllerStepIntoInstructionCmd }
procedure TDbgControllerStepIntoInstructionCmd.DoContinue(
AProcess: TDbgProcess; AThread: TDbgThread);
begin
AProcess.Continue(AProcess, AThread, True);
end;
procedure TDbgControllerStepIntoInstructionCmd.ResolveEvent(
var AnEvent: TFPDEvent; out Handled, Finished: boolean);
begin
Handled := false;
Finished := (AnEvent<>deInternalContinue);
end;
{ TDbgControllerContinueCmd }
procedure TDbgControllerContinueCmd.DoContinue(AProcess: TDbgProcess; AThread: TDbgThread);
begin
AProcess.Continue(AProcess, AThread, False);
end;
procedure TDbgControllerContinueCmd.ResolveEvent(var AnEvent: TFPDEvent; out
Handled, Finished: boolean);
begin
Handled := false;
Finished := (AnEvent<>deInternalContinue);
end;
{ TDbgControllerCmd }
constructor TDbgControllerCmd.Create(AController: TDbgController);
begin
FController := AController;
end;
{ TDbgController } { TDbgController }
procedure TDbgController.DoOnDebugInfoLoaded(Sender: TObject); procedure TDbgController.DoOnDebugInfoLoaded(Sender: TObject);
@ -119,6 +359,13 @@ begin
inherited Destroy; inherited Destroy;
end; end;
procedure TDbgController.InitializeCommand(ACommand: TDbgControllerCmd);
begin
if assigned(FCommand) then
raise exception.create('Prior command not finished yet.');
FCommand := ACommand;
end;
function TDbgController.Run: boolean; function TDbgController.Run: boolean;
begin begin
result := False; result := False;
@ -160,27 +407,27 @@ end;
procedure TDbgController.StepIntoInstr; procedure TDbgController.StepIntoInstr;
begin begin
FCurrentThread.SingleStep; InitializeCommand(TDbgControllerStepIntoInstructionCmd.Create(self));
end; end;
procedure TDbgController.StepOverInstr; procedure TDbgController.StepOverInstr;
begin begin
FCurrentThread.StepLine; InitializeCommand(TDbgControllerStepOverInstructionCmd.Create(self));
end; end;
procedure TDbgController.Next; procedure TDbgController.Next;
begin begin
FCurrentThread.Next; InitializeCommand(TDbgControllerStepOverLineCmd.Create(self));
end; end;
procedure TDbgController.Step; procedure TDbgController.Step;
begin begin
FCurrentThread.StepInto; InitializeCommand(TDbgControllerStepIntoLineCmd.Create(self));
end; end;
procedure TDbgController.StepOut; procedure TDbgController.StepOut;
begin begin
FCurrentThread.StepOut; //FCurrentThread.StepOut;
end; end;
procedure TDbgController.Pause; procedure TDbgController.Pause;
@ -194,6 +441,8 @@ var
AProcessIdentifier: THandle; AProcessIdentifier: THandle;
AThreadIdentifier: THandle; AThreadIdentifier: THandle;
AExit: boolean; AExit: boolean;
IsHandled: boolean;
IsFinished: boolean;
begin begin
AExit:=false; AExit:=false;
@ -201,8 +450,12 @@ begin
if assigned(FCurrentProcess) and not assigned(FMainProcess) then if assigned(FCurrentProcess) and not assigned(FMainProcess) then
FMainProcess:=FCurrentProcess FMainProcess:=FCurrentProcess
else else
FCurrentProcess.Continue(FCurrentProcess, FCurrentThread); begin
if not assigned(FCommand) then
FCurrentProcess.Continue(FCurrentProcess, FCurrentThread, False)
else
FCommand.DoContinue(FCurrentProcess, FCurrentThread);
end;
if not FCurrentProcess.WaitForDebugEvent(AProcessIdentifier, AThreadIdentifier) then Continue; if not FCurrentProcess.WaitForDebugEvent(AProcessIdentifier, AThreadIdentifier) then Continue;
FCurrentProcess := nil; FCurrentProcess := nil;
@ -223,21 +476,16 @@ begin
FCurrentThread := FCurrentProcess.AddThread(AThreadIdentifier); FCurrentThread := FCurrentProcess.AddThread(AThreadIdentifier);
FPDEvent:=FCurrentProcess.ResolveDebugEvent(FCurrentThread); FPDEvent:=FCurrentProcess.ResolveDebugEvent(FCurrentThread);
if (FPDEvent<>deInternalContinue) and assigned(FCurrentProcess.RunToBreakpoint) then begin if assigned(FCommand) then
FCurrentProcess.ClearRunToBreakpoint; FCommand.ResolveEvent(FPDEvent, IsHandled, IsFinished)
end; else
if assigned(FCurrentThread) then
begin begin
FCurrentThread.SingleStepping:=false; IsHandled:=false;
if not (FPDEvent in [deInternalContinue, deLoadLibrary]) then IsFinished:=false;
FCurrentThread.AfterHitBreak;
FCurrentThread.ClearHWBreakpoint;
end; end;
if not IsHandled then
begin
case FPDEvent of case FPDEvent of
deCreateProcess :
begin
// Do nothing
end;
deExitProcess : deExitProcess :
begin begin
if FCurrentProcess = FMainProcess then FMainProcess := nil; if FCurrentProcess = FMainProcess then FMainProcess := nil;
@ -260,17 +508,10 @@ begin
then GState := dsPause; then GState := dsPause;
end;} end;}
deBreakpoint :
begin
// Do nothing
end;
deInternalContinue,
deLoadLibrary:
begin
if assigned(FCurrentThread) and FCurrentThread.Stepping then
FCurrentThread.IntNext;
end;
end; {case} end; {case}
end;
if IsFinished then
FreeAndNil(FCommand);
AExit:=true; AExit:=true;
until AExit; until AExit;
end; end;

View File

@ -63,6 +63,34 @@ type
__gs: cuint64; __gs: cuint64;
end; end;
x86_debug_state32_t = record
__dr0: cuint32;
__dr1: cuint32;
__dr2: cuint32;
__dr3: cuint32;
__dr4: cuint32;
__dr5: cuint32;
__dr6: cuint32;
__dr7: cuint32;
end;
x86_debug_state64_t = record
__dr0: cuint64;
__dr1: cuint64;
__dr2: cuint64;
__dr3: cuint64;
__dr4: cuint64;
__dr5: cuint64;
__dr6: cuint64;
__dr7: cuint64;
end;
x86_debug_state = record
case a: byte of
1: (ds32: x86_debug_state32_t);
2: (ds64: x86_debug_state64_t);
end;
type type
{ TDbgDarwinThread } { TDbgDarwinThread }
@ -71,11 +99,18 @@ type
private private
FThreadState32: x86_thread_state32_t; FThreadState32: x86_thread_state32_t;
FThreadState64: x86_thread_state64_t; FThreadState64: x86_thread_state64_t;
FNeedIPDecrement: boolean; FDebugState32: x86_debug_state32_t;
FDebugState64: x86_debug_state64_t;
FDebugStateRead: boolean;
FDebugStateChanged: boolean;
protected protected
function ReadThreadState: boolean; function ReadThreadState: boolean;
function ReadDebugState: boolean;
public public
function ResetInstructionPointerAfterBreakpoint: boolean; override; function ResetInstructionPointerAfterBreakpoint: boolean; override;
function AddWatchpoint(AnAddr: TDBGPtr): integer; override;
function RemoveWatchpoint(AnId: integer): boolean; override;
procedure BeforeContinue; override;
procedure LoadRegisterValues; override; procedure LoadRegisterValues; override;
end; end;
@ -94,6 +129,7 @@ type
protected protected
function InitializeLoader: TDbgImageLoader; override; function InitializeLoader: TDbgImageLoader; override;
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override; function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; override;
public public
class function StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; override; class function StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; override;
constructor Create(const AName: string; const AProcessID, AThreadID: Integer; AOnLog: TOnLog); override; constructor Create(const AName: string; const AProcessID, AThreadID: Integer; AOnLog: TOnLog); override;
@ -107,9 +143,9 @@ type
function GetStackBasePointerRegisterValue: TDbgPtr; override; function GetStackBasePointerRegisterValue: TDbgPtr; override;
procedure TerminateProcess; override; procedure TerminateProcess; override;
function Continue(AProcess: TDbgProcess; AThread: TDbgThread): boolean; override; function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; override;
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override; function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override;
function ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; override; //function ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; override;
end; end;
procedure RegisterDbgClasses; procedure RegisterDbgClasses;
@ -144,7 +180,7 @@ const
x86_EXCEPTION_STATE = 9; x86_EXCEPTION_STATE = 9;
x86_DEBUG_STATE32 = 10; x86_DEBUG_STATE32 = 10;
x86_DEBUG_STATE64 = 11; x86_DEBUG_STATE64 = 11;
x86_DEBUG_STATE = 12; //x86_DEBUG_STATE = 12;
THREAD_STATE_NONE = 13; THREAD_STATE_NONE = 13;
x86_AVX_STATE32 = 16; x86_AVX_STATE32 = 16;
x86_AVX_STATE64 = 17; x86_AVX_STATE64 = 17;
@ -152,6 +188,8 @@ const
x86_THREAD_STATE32_COUNT: mach_msg_Type_number_t = sizeof(x86_thread_state32_t) div sizeof(cint); x86_THREAD_STATE32_COUNT: mach_msg_Type_number_t = sizeof(x86_thread_state32_t) div sizeof(cint);
x86_THREAD_STATE64_COUNT: mach_msg_Type_number_t = sizeof(x86_thread_state64_t) div sizeof(cint); x86_THREAD_STATE64_COUNT: mach_msg_Type_number_t = sizeof(x86_thread_state64_t) div sizeof(cint);
x86_DEBUG_STATE32_COUNT: mach_msg_Type_number_t = sizeof(x86_debug_state32_t) div sizeof(cint);
x86_DEBUG_STATE64_COUNT: mach_msg_Type_number_t = sizeof(x86_debug_state64_t) div sizeof(cint);
function task_for_pid(target_tport: mach_port_name_t; pid: integer; var t: mach_port_name_t): kern_return_t; cdecl external name 'task_for_pid'; function task_for_pid(target_tport: mach_port_name_t; pid: integer; var t: mach_port_name_t): kern_return_t; cdecl external name 'task_for_pid';
function mach_task_self: mach_port_name_t; cdecl external name 'mach_task_self'; function mach_task_self: mach_port_name_t; cdecl external name 'mach_task_self';
@ -167,6 +205,7 @@ function thread_set_state(target_act: thread_act_t; flavor: thread_state_flavor_
procedure RegisterDbgClasses; procedure RegisterDbgClasses;
begin begin
OSDbgClasses.DbgProcessClass:=TDbgDarwinProcess; OSDbgClasses.DbgProcessClass:=TDbgDarwinProcess;
OSDbgClasses.DbgThreadClass:=TDbgDarwinThread;
end; end;
Function WIFSTOPPED(Status: Integer): Boolean; Function WIFSTOPPED(Status: Integer): Boolean;
@ -196,23 +235,53 @@ begin
old_StateCnt:=x86_THREAD_STATE64_COUNT; old_StateCnt:=x86_THREAD_STATE64_COUNT;
aKernResult:=thread_get_state(Id,x86_THREAD_STATE64, @FThreadState64,old_StateCnt); aKernResult:=thread_get_state(Id,x86_THREAD_STATE64, @FThreadState64,old_StateCnt);
end; end;
if aKernResult <> KERN_SUCCESS then result := aKernResult = KERN_SUCCESS;
if not result then
begin begin
Log('Failed to call thread_get_state for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]); Log('Failed to call thread_get_state for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
end; end;
FRegisterValueListValid:=false; FRegisterValueListValid:=false;
end; end;
function TDbgDarwinThread.ReadDebugState: boolean;
var
aKernResult: kern_return_t;
old_StateCnt: mach_msg_Type_number_t;
begin
if FDebugStateRead then
begin
result := true;
exit;
end;
if Process.Mode=dm32 then
begin
old_StateCnt:=x86_DEBUG_STATE32_COUNT;
aKernResult:=thread_get_state(ID, x86_DEBUG_STATE32, @FDebugState32, old_StateCnt);
end
else
begin
old_StateCnt:=x86_DEBUG_STATE64_COUNT;
aKernResult:=thread_get_state(ID, x86_DEBUG_STATE64, @FDebugState64, old_StateCnt);
end;
if aKernResult <> KERN_SUCCESS then
begin
Log('Failed to call thread_get_state to ge debug-info for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
result := false;
end
else
begin
result := true;
FDebugStateRead:=true;
end;
end;
function TDbgDarwinThread.ResetInstructionPointerAfterBreakpoint: boolean; function TDbgDarwinThread.ResetInstructionPointerAfterBreakpoint: boolean;
var var
aKernResult: kern_return_t; aKernResult: kern_return_t;
new_StateCnt: mach_msg_Type_number_t; new_StateCnt: mach_msg_Type_number_t;
begin begin
result := true; result := true;
// If the breakpoint is reached by single-stepping, decrementing the
// instruction pointer is not necessary.
if not FNeedIPDecrement then
Exit;
if Process.Mode=dm32 then if Process.Mode=dm32 then
begin begin
@ -233,6 +302,96 @@ begin
end; end;
end; end;
function TDbgDarwinThread.AddWatchpoint(AnAddr: TDBGPtr): integer;
function SetBreakpoint(var dr: {$ifdef cpui386}DWORD{$else}DWORD64{$endif}; ind: byte): boolean;
begin
if (Dr=0) and ((FDebugState32.__dr7 and (1 shl ind))=0) then
begin
FDebugState32.__dr7 := FDebugState32.__dr7 or (1 shl (ind*2));
FDebugState32.__dr7 := FDebugState32.__dr7 or ($30000 shl (ind*4));
Dr:=AnAddr;
FDebugStateChanged:=true;
Result := True;
end
else
begin
result := False;
end;
end;
begin
result := -1;
if not ReadDebugState then
exit;
if SetBreakpoint(FDebugState32.__dr0, 0) then
result := 0
else if SetBreakpoint(FDebugState32.__dr1, 1) then
result := 1
else if SetBreakpoint(FDebugState32.__dr2, 2) then
result := 2
else if SetBreakpoint(FDebugState32.__dr3, 3) then
result := 3
else
Process.Log('No hardware breakpoint available.');
end;
function TDbgDarwinThread.RemoveWatchpoint(AnId: integer): boolean;
function RemoveBreakpoint(var dr: {$ifdef cpui386}DWORD{$else}DWORD64{$endif}; ind: byte): boolean;
begin
if (Dr<>0) and ((FDebugState32.__dr7 and (1 shl (ind*2)))<>0) then
begin
FDebugState32.__dr7 := FDebugState32.__dr7 xor (1 shl (ind*2));
FDebugState32.__dr7 := FDebugState32.__dr7 xor ($30000 shl (ind*4));
Dr:=0;
FDebugStateChanged:=true;
Result := True;
end
else
begin
result := False;
Process.Log('HW watchpoint %d is not set.',[ind]);
end;
end;
begin
result := false;
if not ReadDebugState then
exit;
case AnId of
0: result := RemoveBreakpoint(FDebugState32.__dr0, 0);
1: result := RemoveBreakpoint(FDebugState32.__dr1, 1);
2: result := RemoveBreakpoint(FDebugState32.__dr2, 2);
3: result := RemoveBreakpoint(FDebugState32.__dr3, 3);
end;
end;
procedure TDbgDarwinThread.BeforeContinue;
var
aKernResult: kern_return_t;
old_StateCnt: mach_msg_Type_number_t;
begin
if FDebugStateRead and FDebugStateChanged then
begin
if Process.Mode=dm32 then
begin
old_StateCnt:=x86_DEBUG_STATE32_COUNT;
aKernResult:=thread_set_state(Id, x86_DEBUG_STATE32, @FDebugState32, old_StateCnt);
end
else
begin
old_StateCnt:=x86_DEBUG_STATE64_COUNT;
aKernResult:=thread_set_state(Id, x86_DEBUG_STATE64, @FDebugState64, old_StateCnt);
end;
if aKernResult <> KERN_SUCCESS then
Log('Failed to call thread_set_state for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
end;
end;
procedure TDbgDarwinThread.LoadRegisterValues; procedure TDbgDarwinThread.LoadRegisterValues;
begin begin
if Process.Mode=dm32 then with FThreadState32 do if Process.Mode=dm32 then with FThreadState32 do
@ -324,7 +483,6 @@ end;
function TDbgDarwinProcess.InitializeLoader: TDbgImageLoader; function TDbgDarwinProcess.InitializeLoader: TDbgImageLoader;
var var
FObjFileName: string;
dSYMFilename: string; dSYMFilename: string;
begin begin
// JvdS: Mach-O binaries do not contain DWARF-debug info. Instead this info // JvdS: Mach-O binaries do not contain DWARF-debug info. Instead this info
@ -385,7 +543,6 @@ end;
class function TDbgDarwinProcess.StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; class function TDbgDarwinProcess.StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess;
var var
PID: TPid; PID: TPid;
stat: longint;
AProcess: TProcess; AProcess: TProcess;
AnExecutabeFilename: string; AnExecutabeFilename: string;
begin begin
@ -505,7 +662,7 @@ begin
end; end;
end; end;
function TDbgDarwinProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread): boolean; function TDbgDarwinProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean;
var var
e: integer; e: integer;
begin begin
@ -514,7 +671,9 @@ begin
fpPTrace(PTRACE_CONT, ProcessID, nil, nil); fpPTrace(PTRACE_CONT, ProcessID, nil, nil);
{$endif linux} {$endif linux}
{$ifdef darwin} {$ifdef darwin}
if (AThread.SingleStepping) or assigned(FCurrentBreakpoint) then AThread.NextIsSingleStep:=SingleStep;
AThread.BeforeContinue;
if SingleStep or assigned(FCurrentBreakpoint) then
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal)) fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal))
else if FIsTerminating then else if FIsTerminating then
fpPTrace(PTRACE_KILL, ProcessID, pointer(1), nil) fpPTrace(PTRACE_KILL, ProcessID, pointer(1), nil)
@ -524,7 +683,7 @@ begin
e := fpgeterrno; e := fpgeterrno;
if e <> 0 then if e <> 0 then
begin begin
writeln('Failed to continue process. Errcode: ',e); log('Failed to continue process. Errcode: '+inttostr(e));
result := false; result := false;
end end
else else
@ -557,17 +716,13 @@ begin
end end
end; end;
function TDbgDarwinProcess.ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; function TDbgDarwinProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
var
ExceptionAddr: TDBGPtr;
begin begin
FExceptionSignal:=0; FExceptionSignal:=0;
if wifexited(FStatus) or wifsignaled(FStatus) then if wifexited(FStatus) or wifsignaled(FStatus) then
begin begin
SetExitCode(wexitStatus(FStatus)); SetExitCode(wexitStatus(FStatus));
writeln('Exit');
// Clear all pending signals // Clear all pending signals
repeat repeat
until FpWaitPid(-1, FStatus, WNOHANG)<1; until FpWaitPid(-1, FStatus, WNOHANG)<1;
@ -576,7 +731,7 @@ begin
end end
else if WIFSTOPPED(FStatus) then else if WIFSTOPPED(FStatus) then
begin begin
writeln('Stopped ',FStatus, ' signal: ',wstopsig(FStatus)); //log('Stopped ',FStatus, ' signal: ',wstopsig(FStatus));
TDbgDarwinThread(AThread).ReadThreadState; TDbgDarwinThread(AThread).ReadThreadState;
case wstopsig(FStatus) of case wstopsig(FStatus) of
SIGTRAP: SIGTRAP:
@ -586,28 +741,7 @@ begin
result := deCreateProcess; result := deCreateProcess;
FProcessStarted:=true; FProcessStarted:=true;
end end
else if assigned(FCurrentBreakpoint) then
begin
FCurrentBreakpoint.SetBreak;
FCurrentBreakpoint:=nil;
if FMainThread.SingleStepping then
result := deBreakpoint
else else
result := deInternalContinue;
end
else
result := deBreakpoint;
// Handle the breakpoint also if it is reached by single-stepping.
ExceptionAddr:=GetInstructionPointerRegisterValue;
if not (FMainThread.SingleStepping or assigned(FCurrentBreakpoint)) then
begin
TDbgDarwinThread(FMainThread).FNeedIPDecrement:=true;
dec(ExceptionAddr);
end
else
TDbgDarwinThread(FMainThread).FNeedIPDecrement:=false;
if DoBreak(ExceptionAddr, FMainThread.ID) then
result := deBreakpoint; result := deBreakpoint;
end; end;
SIGBUS: SIGBUS:

View File

@ -47,7 +47,7 @@ uses
FpDbgWinExtra, FpDbgWinExtra,
strutils, strutils,
FpDbgInfo, FpDbgInfo,
FpDbgLoader, FpdMemoryTools, FpDbgLoader,
DbgIntfBaseTypes, DbgIntfBaseTypes,
LazLoggerBase; LazLoggerBase;
@ -85,6 +85,7 @@ type
FInfo: TCreateProcessDebugInfo; FInfo: TCreateProcessDebugInfo;
FPauseRequested: boolean; FPauseRequested: boolean;
FProcProcess: TProcess; FProcProcess: TProcess;
FJustStarted: boolean;
function GetFullProcessImageName(AProcessHandle: THandle): string; function GetFullProcessImageName(AProcessHandle: THandle): string;
function GetModuleFileName(AModuleHandle: THandle): string; function GetModuleFileName(AModuleHandle: THandle): string;
function GetProcFilename(AProcess: TDbgProcess; lpImageName: LPVOID; fUnicode: word; hFile: handle): string; function GetProcFilename(AProcess: TDbgProcess; lpImageName: LPVOID; fUnicode: word; hFile: handle): string;
@ -105,9 +106,9 @@ type
function HandleDebugEvent(const ADebugEvent: TDebugEvent): Boolean; function HandleDebugEvent(const ADebugEvent: TDebugEvent): Boolean;
class function StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; override; class function StartInstance(AFileName: string; AParams, AnEnvironment: TStrings; AWorkingDirectory: string; AOnLog: TOnLog): TDbgProcess; override;
function Continue(AProcess: TDbgProcess; AThread: TDbgThread): boolean; override; function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; override;
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override; function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override;
function ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; override; function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; override;
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override; function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
procedure StartProcess(const AThreadID: DWORD; const AInfo: TCreateProcessDebugInfo); procedure StartProcess(const AThreadID: DWORD; const AInfo: TCreateProcessDebugInfo);
@ -483,23 +484,23 @@ begin
end; end;
function TDbgWinProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread): boolean; function TDbgWinProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread;
SingleStep: boolean): boolean;
begin begin
case MDebugEvent.Exception.ExceptionRecord.ExceptionCode of if assigned(AThread) then
EXCEPTION_BREAKPOINT, begin
EXCEPTION_SINGLE_STEP: begin AThread.NextIsSingleStep:=SingleStep;
if assigned(AThread) then begin if SingleStep or assigned(FCurrentBreakpoint) then
// The thread is not assigned if the current process is not the main
// process. (Only the main process is being 'debugged')
if (AThread.SingleStepping) or assigned(FCurrentBreakpoint) then
TDbgWinThread(AThread).SetSingleStep; TDbgWinThread(AThread).SetSingleStep;
AThread.BeforeContinue; AThread.BeforeContinue;
end; end;
case MDebugEvent.Exception.ExceptionRecord.ExceptionCode of
EXCEPTION_BREAKPOINT,
EXCEPTION_SINGLE_STEP: begin
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE); Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
end end
else begin else begin
if assigned(AThread) then
AThread.BeforeContinue;
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
end; end;
end; end;
@ -513,7 +514,7 @@ begin
ThreadIdentifier:=MDebugEvent.dwThreadId; ThreadIdentifier:=MDebugEvent.dwThreadId;
end; end;
function TDbgWinProcess.ResolveDebugEvent(AThread: TDbgThread): TFPDEvent; function TDbgWinProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
procedure HandleException(const AEvent: TDebugEvent); procedure HandleException(const AEvent: TDebugEvent);
const const
@ -781,60 +782,15 @@ begin
//DumpEvent('EXCEPTION_DEBUG_EVENT'); //DumpEvent('EXCEPTION_DEBUG_EVENT');
case MDebugEvent.Exception.ExceptionRecord.ExceptionCode of case MDebugEvent.Exception.ExceptionRecord.ExceptionCode of
EXCEPTION_BREAKPOINT: begin EXCEPTION_BREAKPOINT: begin
if DoBreak(TDbgPtr(MDebugEvent.Exception.ExceptionRecord.ExceptionAddress), MDebugEvent.dwThreadId) if FJustStarted and (MDebugEvent.Exception.dwFirstChance <> 0) and (MDebugEvent.Exception.ExceptionRecord.ExceptionFlags = 0) then
then
result := deBreakpoint
else if assigned(AThread) and assigned(AThread.HiddenBreakpoint) then begin
AThread.HiddenBreakpoint.Hit(AThread.ID);
if AThread.Stepping and AThread.CompareStepInfo then
result := deInternalContinue
else
result := deBreakpoint;
end else if FPauseRequested
then begin
result := deBreakpoint;
FPauseRequested:=false;
end
else begin
// Unknown breakpoint.
if (MDebugEvent.Exception.dwFirstChance <> 0) and (MDebugEvent.Exception.ExceptionRecord.ExceptionFlags = 0)
then begin
// First chance and breakpoint is continuable -> silently ignore.
result := deInternalContinue
end else begin
// Or else, show an exception
result := deException;
end;
end;
end;
EXCEPTION_SINGLE_STEP: begin
if assigned(FCurrentBreakpoint) then
begin begin
FCurrentBreakpoint.SetBreak; FJustStarted:=false;
FCurrentBreakpoint:=nil;
if FMainThread.SingleStepping then
result := deBreakpoint
else
result := deInternalContinue; result := deInternalContinue;
end end
else else
result := deBreakpoint; result := deBreakpoint;
if AThread.Stepping then
begin
if AThread.CompareStepInfo then
result := deInternalContinue
else
result := deBreakpoint;
end; end;
EXCEPTION_SINGLE_STEP: begin
// If there is a breakpoint on this location, handle the breakpoint.
// Or else the int3-interrupt instruction won't be cleared and the
// breakpoint will be triggered again. (Notice that the location of
// the eip-register does not have to be decremented in this case,
// see TDbgWinThread.ResetInstructionPointerAfterBreakpoint)
if DoBreak(TDbgPtr(MDebugEvent.Exception.ExceptionRecord.ExceptionAddress), MDebugEvent.dwThreadId)
then
result := deBreakpoint; result := deBreakpoint;
end end
else begin else begin
@ -853,6 +809,7 @@ begin
CREATE_PROCESS_DEBUG_EVENT: begin CREATE_PROCESS_DEBUG_EVENT: begin
//DumpEvent('CREATE_PROCESS_DEBUG_EVENT'); //DumpEvent('CREATE_PROCESS_DEBUG_EVENT');
StartProcess(MDebugEvent.dwThreadId, MDebugEvent.CreateProcessInfo); StartProcess(MDebugEvent.dwThreadId, MDebugEvent.CreateProcessInfo);
FJustStarted := true;
result := deCreateProcess; result := deCreateProcess;
end; end;
EXIT_THREAD_DEBUG_EVENT: begin EXIT_THREAD_DEBUG_EVENT: begin
@ -1082,9 +1039,6 @@ begin
end; end;
function TDbgWinThread.AddWatchpoint(AnAddr: TDBGPtr): integer; function TDbgWinThread.AddWatchpoint(AnAddr: TDBGPtr): integer;
var
i: integer;
function SetBreakpoint(var dr: {$ifdef cpui386}DWORD{$else}DWORD64{$endif}; ind: byte): boolean; function SetBreakpoint(var dr: {$ifdef cpui386}DWORD{$else}DWORD64{$endif}; ind: byte): boolean;
begin begin
if (Dr=0) and ((GCurrentContext^.Dr7 and (1 shl ind))=0) then if (Dr=0) and ((GCurrentContext^.Dr7 and (1 shl ind))=0) then