Debugger: prevent Destroy while executing inside debugger object. Issue #17815

git-svn-id: trunk@28128 -
This commit is contained in:
martin 2010-11-07 19:15:01 +00:00
parent 688d2bfc42
commit 82e943fc36
3 changed files with 192 additions and 91 deletions

View File

@ -78,7 +78,8 @@ type
dsPause, dsPause,
dsInit, dsInit,
dsRun, dsRun,
dsError dsError,
dsDestroying
); );
TDBGExceptionType = ( TDBGExceptionType = (
@ -117,6 +118,11 @@ type
dsError: dsError:
Something unforseen has happened. A shutdown of the debugger is in Something unforseen has happened. A shutdown of the debugger is in
most cases needed. most cases needed.
-dsDestroying
The debugger is about to be destroyed.
Should normally happen immediate on calling Release.
But the debugger may be in nested calls, and has to exit them first.
-------------------------------------------------------------------------- --------------------------------------------------------------------------
} }
@ -1323,6 +1329,7 @@ type
virtual; abstract; // True if succesful virtual; abstract; // True if succesful
procedure SetExitCode(const AValue: Integer); procedure SetExitCode(const AValue: Integer);
procedure SetState(const AValue: TDBGState); procedure SetState(const AValue: TDBGState);
procedure DoRelease; virtual;
public public
class function Caption: String; virtual; // The name of the debugger as shown in the debuggeroptions class function Caption: String; virtual; // The name of the debugger as shown in the debuggeroptions
class function ExePaths: String; virtual; // The default locations of the exe class function ExePaths: String; virtual; // The default locations of the exe
@ -1339,6 +1346,7 @@ type
procedure Init; virtual; // Initializes the debugger procedure Init; virtual; // Initializes the debugger
procedure Done; virtual; // Kills the debugger procedure Done; virtual; // Kills the debugger
procedure Release; // Free/Destroy self
procedure Run; // Starts / continues debugging procedure Run; // Starts / continues debugging
procedure Pause; // Stops running procedure Pause; // Stops running
procedure Stop; // quit debugging procedure Stop; // quit debugging
@ -1413,7 +1421,8 @@ const
'Pause', 'Pause',
'Init', 'Init',
'Run', 'Run',
'Error' 'Error',
'Destroying'
); );
DBGBreakPointActionNames: array[TIDEBreakPointAction] of string = ( DBGBreakPointActionNames: array[TIDEBreakPointAction] of string = (
@ -1446,7 +1455,8 @@ const
dcDisassemble], dcDisassemble],
{dsInit } [], {dsInit } [],
{dsRun } [dcPause, dcStop, dcBreak, dcWatch, dcEnvironment], {dsRun } [dcPause, dcStop, dcBreak, dcWatch, dcEnvironment],
{dsError} [dcStop] {dsError} [dcStop],
{dsDestroying} []
); );
var var
@ -1654,6 +1664,12 @@ begin
FCurEnvironment.Clear; FCurEnvironment.Clear;
end; end;
procedure TDebugger.Release;
begin
if Self <> nil
then Self.DoRelease;
end;
procedure TDebugger.DoCurrent(const ALocation: TDBGLocationRec); procedure TDebugger.DoCurrent(const ALocation: TDBGLocationRec);
begin begin
if Assigned(FOnCurrent) then FOnCurrent(Self, ALocation); if Assigned(FOnCurrent) then FOnCurrent(Self, ALocation);
@ -1903,6 +1919,17 @@ procedure TDebugger.SetState(const AValue: TDBGState);
var var
OldState: TDBGState; OldState: TDBGState;
begin begin
// dsDestroying is final, do not unset
if FState = dsDestroying
then exit;
// dsDestroying must be silent. The ide believes the debugger is gone already
if AValue = dsDestroying
then begin
FState := AValue;
exit;
end;
if AValue <> FState if AValue <> FState
then begin then begin
OldState := FState; OldState := FState;
@ -1917,6 +1944,11 @@ begin
end; end;
end; end;
procedure TDebugger.DoRelease;
begin
Self.Free;
end;
procedure TDebugger.StepInto; procedure TDebugger.StepInto;
begin begin
if ReqCmd(dcStepInto, []) then exit; if ReqCmd(dcStepInto, []) then exit;

View File

@ -132,6 +132,7 @@ type
procedure DoUnockQueueExecute; virtual; procedure DoUnockQueueExecute; virtual;
function DoExecute: Boolean; virtual; abstract; function DoExecute: Boolean; virtual; abstract;
procedure DoOnExecuted; procedure DoOnExecuted;
procedure DoCancel; virtual;
procedure DoOnCanceled; procedure DoOnCanceled;
// ExecuteCommand does execute direct. It does not use the queue // ExecuteCommand does execute direct. It does not use the queue
function ExecuteCommand(const ACommand: String): Boolean; overload; function ExecuteCommand(const ACommand: String): Boolean; overload;
@ -185,6 +186,7 @@ type
FAsmCache: TTypedMap; FAsmCache: TTypedMap;
FAsmCacheIter: TTypedMapIterator; FAsmCacheIter: TTypedMapIterator;
FSourceNames: TStringList; // Objects[] -> TMap[Integer|Integer] -> TDbgPtr FSourceNames: TStringList; // Objects[] -> TMap[Integer|Integer] -> TDbgPtr
FReleaseLock: Integer;
// GDB info (move to ?) // GDB info (move to ?)
FGDBVersion: String; FGDBVersion: String;
@ -216,6 +218,10 @@ type
out ADump, AStatement, AFile: String; out ALine: Integer): Boolean; out ADump, AStatement, AFile: String; out ALine: Integer): Boolean;
function GDBSourceAdress(const ASource: String; ALine, AColumn: Integer; out AAddr: TDbgPtr): Boolean; function GDBSourceAdress(const ASource: String; ALine, AColumn: Integer; out AAddr: TDbgPtr): Boolean;
// prevent destruction while nested in any call
procedure LockRelease;
procedure UnlockRelease;
procedure CallStackSetCurrent(AIndex: Integer); procedure CallStackSetCurrent(AIndex: Integer);
function ConvertPascalExpression(var AExpression: String): Boolean; function ConvertPascalExpression(var AExpression: String): Boolean;
// --- // ---
@ -250,6 +256,7 @@ type
function ExecuteCommandFull(const ACommand: String; const AValues: array of const; const AFlags: TGDBMICmdFlags; const ACallback: TGDBMICallback; const ATag: PtrInt; var AResult: TGDBMIExecResult): Boolean; overload; function ExecuteCommandFull(const ACommand: String; const AValues: array of const; const AFlags: TGDBMICmdFlags; const ACallback: TGDBMICallback; const ATag: PtrInt; var AResult: TGDBMIExecResult): Boolean; overload;
procedure QueueCommand(const ACommand: TGDBMIDebuggerCommand); procedure QueueCommand(const ACommand: TGDBMIDebuggerCommand);
procedure UnQueueCommand(const ACommand: TGDBMIDebuggerCommand); procedure UnQueueCommand(const ACommand: TGDBMIDebuggerCommand);
procedure CancelAllQueued;
function StartDebugging(const AContinueCommand: String): Boolean; function StartDebugging(const AContinueCommand: String): Boolean;
protected protected
procedure QueueExecuteLock; procedure QueueExecuteLock;
@ -277,6 +284,7 @@ type
property TargetFlags: TGDBMITargetFlags read FTargetFlags write FTargetFlags; property TargetFlags: TGDBMITargetFlags read FTargetFlags write FTargetFlags;
property PauseWaitState: TGDBMIPauseWaitState read FPauseWaitState; property PauseWaitState: TGDBMIPauseWaitState read FPauseWaitState;
property DebuggerFlags: TGDBMIDebuggerFlags read FDebuggerFlags; property DebuggerFlags: TGDBMIDebuggerFlags read FDebuggerFlags;
procedure DoRelease; override; // Destroy self (or schedule)
public public
class function CreateProperties: TDebuggerProperties; override; // Creates debuggerproperties class function CreateProperties: TDebuggerProperties; override; // Creates debuggerproperties
class function Caption: String; override; class function Caption: String; override;
@ -1594,6 +1602,7 @@ end;
constructor TGDBMIDebugger.Create(const AExternalDebugger: String); constructor TGDBMIDebugger.Create(const AExternalDebugger: String);
begin begin
FReleaseLock := 0;
FBreakErrorBreakID := -1; FBreakErrorBreakID := -1;
FRunErrorBreakID := -1; FRunErrorBreakID := -1;
FExceptionBreakID := -1; FExceptionBreakID := -1;
@ -1653,6 +1662,7 @@ end;
destructor TGDBMIDebugger.Destroy; destructor TGDBMIDebugger.Destroy;
begin begin
LockRelease;
inherited; inherited;
ClearCommandQueue; ClearCommandQueue;
FreeAndNil(FCommandQueue); FreeAndNil(FCommandQueue);
@ -1664,9 +1674,15 @@ end;
procedure TGDBMIDebugger.Done; procedure TGDBMIDebugger.Done;
begin begin
LockRelease;
try
CancelAllQueued;
if State = dsRun then GDBPause(True); if State = dsRun then GDBPause(True);
ExecuteCommand('-gdb-exit', []); ExecuteCommand('-gdb-exit', []);
inherited Done; inherited Done;
finally
UnlockRelease;
end;
end; end;
procedure TGDBMIDebugger.DoState(const OldState: TDBGState); procedure TGDBMIDebugger.DoState(const OldState: TDBGState);
@ -1681,6 +1697,15 @@ begin
inherited DoState(OldState); inherited DoState(OldState);
end; end;
procedure TGDBMIDebugger.DoRelease;
begin
SetState(dsDestroying);
if FReleaseLock > 0
then exit;
inherited DoRelease;
end;
function TGDBMIDebugger.ExecuteCommand(const ACommand: String; function TGDBMIDebugger.ExecuteCommand(const ACommand: String;
const AFlags: TGDBMICmdFlags): Boolean; const AFlags: TGDBMICmdFlags): Boolean;
var var
@ -1748,7 +1773,11 @@ procedure TGDBMIDebugger.QueueCommand(const ACommand: TGDBMIDebuggerCommand);
var var
R: Boolean; R: Boolean;
Cmd: TGDBMIDebuggerCommand; Cmd: TGDBMIDebuggerCommand;
SavedInExecuteCount: LongInt;
begin begin
SavedInExecuteCount := FInExecuteCount;
LockRelease;
try
FCommandQueue.Add(ACommand); FCommandQueue.Add(ACommand);
if (FCommandQueue.Count > 1) or (FCommandQueueExecLock > 0) if (FCommandQueue.Count > 1) or (FCommandQueueExecLock > 0)
then begin then begin
@ -1762,7 +1791,7 @@ begin
// If we are here we can process the command directly // If we are here we can process the command directly
repeat repeat
Inc(FInExecuteCount); Inc(FInExecuteCount);
try
Cmd := TGDBMIDebuggerCommand(FCommandQueue[0]); Cmd := TGDBMIDebuggerCommand(FCommandQueue[0]);
FCommandQueue.Delete(0); FCommandQueue.Delete(0);
{$IFDEF GDMI_QUEUE_DEBUG} {$IFDEF GDMI_QUEUE_DEBUG}
@ -1771,16 +1800,14 @@ begin
R := Cmd.Execute; R := Cmd.Execute;
Cmd.DoFinished; Cmd.DoFinished;
if State = dsError Dec(FInExecuteCount);
if State in [dsError, dsDestroying]
then begin then begin
//DebugLn('[WARNING] TGDBMIDebugger: ExecuteCommand "',Cmd,'" failed.'); //DebugLn('[WARNING] TGDBMIDebugger: ExecuteCommand "',Cmd,'" failed.');
Break; Break;
end; end;
finally
Dec(FInExecuteCount);
end;
if FCommandQueue.Count = 0 if FCommandQueue.Count = 0
then begin then begin
if (FInExecuteCount = 0) // not in Recursive call if (FInExecuteCount = 0) // not in Recursive call
@ -1803,6 +1830,10 @@ begin
{$IFDEF GDMI_QUEUE_DEBUG} {$IFDEF GDMI_QUEUE_DEBUG}
debugln(['Leaving Queue with count: ', FCommandQueue.Count, ' Recurse-Count=', FInExecuteCount,' State=',DBGStateNames[State]]); debugln(['Leaving Queue with count: ', FCommandQueue.Count, ' Recurse-Count=', FInExecuteCount,' State=',DBGStateNames[State]]);
{$ENDIF} {$ENDIF}
finally
UnlockRelease;
FInExecuteCount := SavedInExecuteCount;
end;
end; end;
procedure TGDBMIDebugger.UnQueueCommand(const ACommand: TGDBMIDebuggerCommand); procedure TGDBMIDebugger.UnQueueCommand(const ACommand: TGDBMIDebuggerCommand);
@ -1810,6 +1841,19 @@ begin
FCommandQueue.Remove(ACommand); FCommandQueue.Remove(ACommand);
end; end;
procedure TGDBMIDebugger.CancelAllQueued;
var
i: Integer;
begin
i := FCommandQueue.Count - 1;
while i >= 0 do begin
TGDBMIDebuggerCommand(FCommandQueue[i]).Cancel;
dec(i);
if i >= FCommandQueue.Count
then i := FCommandQueue.Count - 1;
end;
end;
class function TGDBMIDebugger.ExePaths: String; class function TGDBMIDebugger.ExePaths: String;
begin begin
Result := '/usr/bin/gdb;/usr/local/bin/gdb;/opt/fpc/gdb'; Result := '/usr/bin/gdb;/usr/local/bin/gdb;/opt/fpc/gdb';
@ -2482,6 +2526,18 @@ begin
LinesList.Free; LinesList.Free;
end; end;
procedure TGDBMIDebugger.LockRelease;
begin
inc(FReleaseLock);
end;
procedure TGDBMIDebugger.UnlockRelease;
begin
dec(FReleaseLock);
if (FReleaseLock = 0) and (State = dsDestroying)
then Release;
end;
function TGDBMIDebugger.GDBStepInto: Boolean; function TGDBMIDebugger.GDBStepInto: Boolean;
begin begin
Result := False; Result := False;
@ -2727,6 +2783,8 @@ procedure TGDBMIDebugger.Init;
var var
Options: String; Options: String;
begin begin
LockRelease;
try
FPauseWaitState := pwsNone; FPauseWaitState := pwsNone;
FInExecuteCount := 0; FInExecuteCount := 0;
@ -2759,6 +2817,9 @@ begin
else MessageDlg('Debugger', Format('Failed to create debug process: %s', [ReadLine]), mtError, [mbOK], 0); else MessageDlg('Debugger', Format('Failed to create debug process: %s', [ReadLine]), mtError, [mbOK], 0);
SetState(dsError); SetState(dsError);
end; end;
finally
UnlockRelease;
end;
end; end;
procedure TGDBMIDebugger.InterruptTarget; procedure TGDBMIDebugger.InterruptTarget;
@ -3244,6 +3305,8 @@ end;
function TGDBMIDebugger.RequestCommand(const ACommand: TDBGCommand; const AParams: array of const): Boolean; function TGDBMIDebugger.RequestCommand(const ACommand: TDBGCommand; const AParams: array of const): Boolean;
begin begin
LockRelease;
try
case ACommand of case ACommand of
dcRun: Result := GDBRun; dcRun: Result := GDBRun;
dcPause: Result := GDBPause(False); dcPause: Result := GDBPause(False);
@ -3260,6 +3323,9 @@ begin
String(AParams[3].VPointer^), String(AParams[4].VPointer^), String(AParams[3].VPointer^), String(AParams[4].VPointer^),
String(AParams[5].VPointer^), Integer(AParams[6].VPointer^)); String(AParams[5].VPointer^), Integer(AParams[6].VPointer^));
end; end;
finally
UnlockRelease;
end;
end; end;
procedure TGDBMIDebugger.ClearCommandQueue; procedure TGDBMIDebugger.ClearCommandQueue;
@ -5505,8 +5571,6 @@ begin
OldState := FState; OldState := FState;
FState := NewState; FState := NewState;
DoStateChanged(OldState); DoStateChanged(OldState);
if State = dcsCanceled
then DoOnCanceled;
if State in [dcsFinished, dcsCanceled] if State in [dcsFinished, dcsCanceled]
then DoFree; then DoFree;
end; end;
@ -5538,6 +5602,11 @@ begin
FOnExecuted(self); FOnExecuted(self);
end; end;
procedure TGDBMIDebuggerCommand.DoCancel;
begin
// empty
end;
procedure TGDBMIDebuggerCommand.DoOnCanceled; procedure TGDBMIDebuggerCommand.DoOnCanceled;
begin begin
if assigned(FOnCancel) then if assigned(FOnCancel) then
@ -5915,9 +5984,9 @@ end;
procedure TGDBMIDebuggerCommand.Cancel; procedure TGDBMIDebuggerCommand.Cancel;
begin begin
if State <> dcsQueued
then exit;
FTheDebugger.UnQueueCommand(Self); FTheDebugger.UnQueueCommand(Self);
DoCancel;
DoOnCanceled;
SetState(dcsCanceled); SetState(dcsCanceled);
end; end;

View File

@ -1503,8 +1503,8 @@ procedure TDebugManager.DebuggerChangeState(ADebugger: TDebugger;
const const
// dsNone, dsIdle, dsStop, dsPause, dsInit, dsRun, dsError // dsNone, dsIdle, dsStop, dsPause, dsInit, dsRun, dsError
TOOLSTATEMAP: array[TDBGState] of TIDEToolStatus = ( TOOLSTATEMAP: array[TDBGState] of TIDEToolStatus = (
// dsNone, dsIdle, dsStop, dsPause, dsInit, dsRun, dsError // dsNone, dsIdle, dsStop, dsPause, dsInit, dsRun, dsError, dsDestroying
itNone, itNone, itNone, itDebugger, itDebugger, itDebugger, itDebugger itNone, itNone, itNone, itDebugger, itDebugger, itDebugger, itDebugger, itNone
); );
//STATENAME: array[TDBGState] of string = ( //STATENAME: array[TDBGState] of string = (
// 'dsNone', 'dsIdle', 'dsStop', 'dsPause', 'dsInit', 'dsRun', 'dsError' // 'dsNone', 'dsIdle', 'dsStop', 'dsPause', 'dsInit', 'dsRun', 'dsError'
@ -2259,7 +2259,7 @@ var
begin begin
dbg := FDebugger; dbg := FDebugger;
SetDebugger(nil); SetDebugger(nil);
dbg.Free; dbg.Release;
FManagerStates := []; FManagerStates := [];
if MainIDE.ToolStatus = itDebugger if MainIDE.ToolStatus = itDebugger