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; FRegsChanged: boolean; FStoredRegs: TInitializedRegisters; FExceptionSignal: integer; FIsPaused, FInternalPauseRequested, FIsInInternalPause: boolean; FIsSteppingBreakPoint: boolean; FDidResetInstructionPointer: Boolean; FHasThreadState: boolean; FUnwinder: TDbgStackUnwinder; procedure LoadRegisterCache; virtual; procedure SaveRegisterCache; virtual; procedure InvalidateRegisters; function ReadDebugReg(ind: byte; out AVal: TDbgPtr): boolean; function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean; function ReadThreadState: boolean; function RequestInternalPause: 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; // Read access to internal registers property InternalRegs: TInitializedRegisters read FRegs; 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; public constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel; 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; generic TRspBreakPointTargetHandler<_BRK_STORE, _BREAK> = class(specialize TGenericBreakPointTargetHandler<_BRK_STORE, _BREAK>) // Defer break handling to remote server. function DoInsertBreakInstructionCode(const ALocation: TDBGPtr; out OrigValue: _BRK_STORE; AMakeTempRemoved: Boolean): Boolean; override; function DoRemoveBreakInstructionCode(const ALocation: TDBGPtr; const OrigValue: _BRK_STORE): Boolean; override; 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; { TRspBreakPointTargetHandler } function TRspBreakPointTargetHandler.DoInsertBreakInstructionCode(const ALocation: TDBGPtr; out OrigValue: _BRK_STORE; AMakeTempRemoved: Boolean): Boolean; begin if TDbgRspProcess(Process).FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then DebugLn(DBG__WARNINGS, 'TDbgRspProcess.InsertBreakInstruction called while FIsTerminating is set.'); result := process.ReadData(ALocation, SizeOf(_BRK_STORE), OrigValue); if AMakeTempRemoved then exit; // Insert HW break... result := TDbgRspProcess(Process).RspConnection.SetBreakWatchPoint(ALocation, wkpExec, SizeOf(_BRK_STORE), true); if not result then DebugLn(DBG__WARNINGS, 'Failed to set break point.', []); end; function TRspBreakPointTargetHandler.DoRemoveBreakInstructionCode(const ALocation: TDBGPtr; const OrigValue: _BRK_STORE): Boolean; begin if TDbgRspProcess(Process).FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP) then begin DebugLn(DBG__WARNINGS, 'TDbgRspProcess.RemoveBreakInstructionCode called while FIsTerminating is set'); result := false; end else result := TDbgRspProcess(Process).RspConnection.DeleteBreakWatchPoint(ALocation, wkpExec); 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]); LoadRegisterCache; 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; begin FRegs.Initialized := false; end; procedure TDbgRspThread.LoadRegisterCache; begin // Target specific end; procedure TDbgRspThread.SaveRegisterCache; 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; 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, watchData.Size) 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; if FRegsChanged then SaveRegisterCache; InvalidateRegisters; end; procedure TDbgRspThread.SetRegisterValue(AName: string; AValue: QWord); begin assert(true, 'TDbgRspThread.SetRegisterValue not implemented'); end; procedure TDbgRspThread.StoreRegisters; begin FStoredRegs.Initialized := FRegs.Initialized; FStoredRegs.regs := copy(FRegs.regs); end; procedure TDbgRspThread.RestoreRegisters; begin FRegs.Initialized := FStoredRegs.Initialized; FRegs.regs := copy(FStoredRegs.regs); FRegsChanged := true; end; { TDbgRspProcess } procedure TDbgRspProcess.InitializeLoaders; var Loader: TDbgImageLoader; begin if LoaderList.Count = 0 then begin Loader := TDbgImageLoader.Create(Name); if Loader.IsValid then Loader.AddToLoaderList(LoaderList) else Loader.Free; end; end; procedure TDbgRspProcess.CreateRspConnection(AFileName: string); begin self.FConnection := TRspConnection.Create(AFileName, self, FRemoteConfig); end; constructor TDbgRspProcess.Create(const AFileName: string; AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig); begin if Assigned(AProcessConfig) and (AProcessConfig is TRemoteConfig) then begin FRemoteConfig := TRemoteConfig.Create; FRemoteConfig.Assign(AProcessConfig); end; inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel); 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 := FStatus <> SIGHUP; 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; var s: string; begin // Try to prevent access to the RSP socket after it has been closed if not (FIsTerminating or (TDbgRspProcess(Process).FStatus = SIGHUP)) then begin // Qemu only accepts a kill command in the paused state if not (TDbgRspProcess(Process).FStatus in [SIGINT, SIGTRAP]) then begin TDbgRspThread(Process.MainThread).RequestInternalPause; TDbgRspProcess(Process).FStatus := FConnection.WaitForSignal(s); end; 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; function DoLocalStep(TheThread: TDbgThread): Integer; var res: boolean; s: string; begin res := RspConnection.SingleStep(); TDbgRspThread(TheThread).ResetPauseStates; // So BeforeContinue will not run again TDbgRspThread(TheThread).FIsPaused := True; if res then Result := RspConnection.WaitForSignal(s) else begin DebugLn(DBG_WARNINGS, ['Error local single stepping thread ', TheThread.ID]); Result := -1; end; end; var ThreadToContinue: TDbgRspThread; PC: word; 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; if (ThreadToContinue.GetInstructionPointerRegisterValue = PC) then repeat FStatus := DoLocalStep(ThreadToContinue); until (FStatus = -1) or (FStatus = SIGTRAP); 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; // In qemu, needs to step over breakpoint for continue command to work FStatus := DoLocalStep(AThread); if not SingleStep then begin result := RspConnection.Continue(); TDbgRspThread(AThread).ResetPauseStates; FStatus := 0; // should update status by calling WaitForSignal end; end; if not FThreadMap.HasId(AThread.ID) then AThread.Free; end; function TDbgRspProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; var s: string; 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 := SIGHUP; Exit(True); 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.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; function TDbgRspProcess.CreateWatchPointData: TFpWatchPointData; begin Result := TFpRspWatchPointData.Create; 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.