mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-09 18:47:52 +02:00
1179 lines
37 KiB
ObjectPascal
1179 lines
37 KiB
ObjectPascal
unit GDBMIDebugInstructions;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, StrUtils, Math,
|
|
// LazUtils
|
|
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, LazClasses, LazStringUtils,
|
|
// LazDebuggerGdbmi
|
|
CmdLineDebugger, GDBMIMiscClasses;
|
|
|
|
type
|
|
|
|
{ TGDBMICmdLineDebugger }
|
|
|
|
TGDBMICmdLineDebugger = class(TCmdLineDebugger)
|
|
protected
|
|
FErrorHandlingFlags: set of (ehfDeferReadWriteError, ehfGotReadError, ehfGotWriteError);
|
|
procedure DoReadError; override;
|
|
procedure DoWriteError; override;
|
|
end;
|
|
|
|
{ TGDBInstruction }
|
|
|
|
TGDBInstructionFlag = (
|
|
ifRequiresThread,
|
|
ifRequiresStackFrame,
|
|
ifRequiresMemLimit,
|
|
ifRequiresArrayLimit
|
|
);
|
|
TGDBInstructionFlags = set of TGDBInstructionFlag;
|
|
|
|
TGDBInstructionResultFlag = (
|
|
ifrComleted,
|
|
ifrFailed
|
|
);
|
|
TGDBInstructionResultFlags = set of TGDBInstructionResultFlag;
|
|
|
|
TGDBInstructionErrorFlag = (
|
|
ifeContentError, // the imput from gdb was not in the expected format
|
|
ifeWriteError, // writing to gdb (pipe) failed
|
|
ifeReadError,
|
|
ifeGdbNotRunning,
|
|
ifeTimedOut,
|
|
ifeRecoveredTimedOut, // not an error
|
|
ifeInvalidStackFrame,
|
|
ifeInvalidThreadId,
|
|
ifeQueueContextError // The thread or stack command went ok, but something else interfered with setting th econtext
|
|
);
|
|
TGDBInstructionErrorFlags = set of TGDBInstructionErrorFlag;
|
|
|
|
TGDBInstructionQueue = class;
|
|
|
|
{ TGDBInstruction }
|
|
|
|
TGDBInstruction = class(TRefCountedObject)
|
|
private
|
|
FArrayLenLimit: Integer;
|
|
FCommand: String;
|
|
FFlags: TGDBInstructionFlags;
|
|
FMemLimit: Integer;
|
|
FStackFrame: Integer;
|
|
FThreadId: Integer;
|
|
protected
|
|
FResultFlags: TGDBInstructionResultFlags;
|
|
FErrorFlags: TGDBInstructionErrorFlags;
|
|
FTimeOut: Integer;
|
|
procedure SendCommandDataToGDB(AQueue: TGDBInstructionQueue); virtual;
|
|
function ProcessInputFromGdb(const AData: String): Boolean; virtual; abstract; // True if data was handled
|
|
|
|
function GetTimeOutVerifier: TGDBInstruction; virtual;
|
|
procedure Init; virtual;
|
|
procedure InternalCreate(ACommand: String;
|
|
AThread, AFrame: Integer; // ifRequiresThread, ifRequiresStackFrame will always be included
|
|
AFlags: TGDBInstructionFlags;
|
|
ATimeOut: Integer
|
|
);
|
|
public
|
|
constructor Create(ACommand: String;
|
|
AFlags: TGDBInstructionFlags = [];
|
|
ATimeOut: Integer = 0
|
|
);
|
|
constructor Create(ACommand: String;
|
|
AThread: Integer; // ifRequiresThread will always be included
|
|
AOtherFlags: TGDBInstructionFlags = [];
|
|
ATimeOut: Integer = 0
|
|
);
|
|
constructor Create(ACommand: String;
|
|
AThread, AFrame: Integer; // ifRequiresThread, ifRequiresStackFrame will always be included
|
|
AOtherFlags: TGDBInstructionFlags = [];
|
|
ATimeOut: Integer = 0
|
|
);
|
|
procedure ApplyArrayLenLimit(ALimit: Integer);
|
|
procedure ApplyMemLimit(ALimit: Integer);
|
|
function IsSuccess: Boolean;
|
|
function IsCompleted: boolean; virtual; // No more InputFromGdb required
|
|
|
|
procedure MarkAsSuccess;
|
|
procedure HandleWriteError({%H-}ASender: TGDBInstruction); virtual;
|
|
procedure HandleReadError; virtual;
|
|
procedure HandleTimeOut; virtual;
|
|
procedure HandleRecoveredTimeOut; virtual;
|
|
procedure HandleNoGdbRunning; virtual;
|
|
procedure HandleContentError; virtual;
|
|
procedure HandleError(AnError: TGDBInstructionErrorFlag; AMarkAsFailed: Boolean = True); virtual;
|
|
function DebugText: String;
|
|
|
|
property Command: String read FCommand;
|
|
property ThreadId: Integer read FThreadId;
|
|
property StackFrame: Integer read FStackFrame;
|
|
property ArrayLenLimit: Integer read FArrayLenLimit;
|
|
property MemLimit: Integer read FMemLimit;
|
|
property Flags: TGDBInstructionFlags read FFlags;
|
|
property ResultFlags: TGDBInstructionResultFlags read FResultFlags;
|
|
property ErrorFlags: TGDBInstructionErrorFlags read FErrorFlags;
|
|
property TimeOut: Integer read FTimeOut;
|
|
end;
|
|
|
|
{ TGDBInstructionVerifyTimeOut }
|
|
|
|
TGDBInstructionVerifyTimeOutState = (
|
|
vtSent, vtError,
|
|
vtGotPrompt,
|
|
vtGotPrompt7, vtGotPrompt7gdb, vtGotPrompt7and7, vtGotPrompt7and7gdb,
|
|
vtGotPrompt1, vtGotPrompt1gdb,
|
|
vtGot7, vtGot7gdb, vtGot7and7, vtGot7and7gdb, vtGot1, vtGot1gdb
|
|
);
|
|
|
|
TGDBInstructionVerifyTimeOut = class(TGDBInstruction)
|
|
private
|
|
FRunnigInstruction: TGDBInstruction;
|
|
FList: TGDBMINameValueList;
|
|
FPromptAfterErrorCount: Integer;
|
|
FVal7Data: String;
|
|
FState: TGDBInstructionVerifyTimeOutState;
|
|
protected
|
|
procedure SendCommandDataToGDB(AQueue: TGDBInstructionQueue); override;
|
|
function ProcessInputFromGdb(const AData: String): Boolean; override;
|
|
|
|
function GetTimeOutVerifier: TGDBInstruction; override;
|
|
function DebugText: String;
|
|
public
|
|
constructor Create(ARunnigInstruction: TGDBInstruction);
|
|
destructor Destroy; override;
|
|
|
|
procedure HandleWriteError(ASender: TGDBInstruction); override;
|
|
procedure HandleReadError; override;
|
|
procedure HandleTimeOut; override;
|
|
procedure HandleNoGdbRunning; override;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeMemLimit }
|
|
|
|
TGDBInstructionChangeMemLimit = class(TGDBInstruction)
|
|
private
|
|
FNewLimit: Integer;
|
|
FQueue: TGDBInstructionQueue;
|
|
//FDone: Boolean;
|
|
protected
|
|
procedure SendCommandDataToGDB(AQueue: TGDBInstructionQueue); override;
|
|
function ProcessInputFromGdb(const AData: String): Boolean; override;
|
|
function DebugText: String;
|
|
public
|
|
constructor Create(AQueue: TGDBInstructionQueue; ANewLimit: Integer);
|
|
// procedure HandleError(AnError: TGDBInstructionErrorFlag; AMarkAsFailed: Boolean = True); override;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeArrayLimit }
|
|
|
|
TGDBInstructionChangeArrayLimit = class(TGDBInstruction)
|
|
private
|
|
FNewLimit: Integer;
|
|
FQueue: TGDBInstructionQueue;
|
|
//FDone: Boolean;
|
|
protected
|
|
procedure SendCommandDataToGDB(AQueue: TGDBInstructionQueue); override;
|
|
function ProcessInputFromGdb(const AData: String): Boolean; override;
|
|
function DebugText: String;
|
|
public
|
|
constructor Create(AQueue: TGDBInstructionQueue; ANewLimit: Integer);
|
|
|
|
procedure HandleError(AnError: TGDBInstructionErrorFlag; AMarkAsFailed: Boolean = True);
|
|
override;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeThread }
|
|
|
|
TGDBInstructionChangeThread = class(TGDBInstruction)
|
|
private
|
|
FSelThreadId: Integer;
|
|
FQueue: TGDBInstructionQueue;
|
|
FDone: Boolean;
|
|
protected
|
|
procedure SendCommandDataToGDB(AQueue: TGDBInstructionQueue); override;
|
|
function ProcessInputFromGdb(const AData: String): Boolean; override;
|
|
function DebugText: String;
|
|
public
|
|
constructor Create(AQueue: TGDBInstructionQueue; AThreadId: Integer);
|
|
|
|
procedure HandleError(AnError: TGDBInstructionErrorFlag; AMarkAsFailed: Boolean = True);
|
|
override;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeStackFrame }
|
|
|
|
TGDBInstructionChangeStackFrame = class(TGDBInstruction)
|
|
private
|
|
FSelStackFrame: Integer;
|
|
FQueue: TGDBInstructionQueue;
|
|
FDone: Boolean;
|
|
protected
|
|
procedure SendCommandDataToGDB(AQueue: TGDBInstructionQueue); override;
|
|
function ProcessInputFromGdb(const AData: String): Boolean; override;
|
|
function DebugText: String;
|
|
public
|
|
constructor Create(AQueue: TGDBInstructionQueue; AFrame: Integer);
|
|
procedure HandleError(AnError: TGDBInstructionErrorFlag; AMarkAsFailed: Boolean = True);
|
|
override;
|
|
end;
|
|
|
|
{ TGDBInstructionQueue }
|
|
|
|
TGDBInstructionQueueFlag = (
|
|
ifqValidThread,
|
|
ifqValidStackFrame
|
|
);
|
|
TGDBInstructionQueueFlags = set of TGDBInstructionQueueFlag;
|
|
|
|
TGDBInstructionQueue = class
|
|
private
|
|
FCurrentInstruction: TGDBInstruction;
|
|
FCurrentStackFrame: Integer;
|
|
FCurrentThreadId: Integer;
|
|
FCurrentArrayLimit: Integer;
|
|
FCurrentMemLimit: Integer;
|
|
FExeCurInstructionStamp: Int64;
|
|
FDebugger: TGDBMICmdLineDebugger;
|
|
FFlags: TGDBInstructionQueueFlags;
|
|
|
|
procedure ExecuteCurrentInstruction;
|
|
procedure FinishCurrentInstruction;
|
|
procedure SetCurrentInstruction(AnInstruction: TGDBInstruction);
|
|
function HasCorrectThreadIdFor(AnInstruction: TGDBInstruction): Boolean;
|
|
function HasCorrectFrameFor(AnInstruction: TGDBInstruction): Boolean;
|
|
protected
|
|
function SendDataToGDB(ASender: TGDBInstruction; AData: String): Boolean;
|
|
function SendDataToGDB(ASender: TGDBInstruction; AData: String; const AValues: array of const): Boolean;
|
|
procedure HandleGdbDataBeforeInstruction(var {%H-}AData: String; var {%H-}SkipData: Boolean;
|
|
const {%H-}TheInstruction: TGDBInstruction); virtual;
|
|
procedure HandleGdbDataAfterInstruction(var {%H-}AData: String; const {%H-}Handled: Boolean;
|
|
const {%H-}TheInstruction: TGDBInstruction); virtual;
|
|
function GetSelectThreadInstruction(AThreadId: Integer): TGDBInstruction; virtual;
|
|
function GetSelectFrameInstruction(AFrame: Integer): TGDBInstruction; virtual;
|
|
|
|
property Debugger: TGDBMICmdLineDebugger read FDebugger;
|
|
public
|
|
constructor Create(ADebugger: TGDBMICmdLineDebugger);
|
|
procedure InvalidateThredAndFrame(AStackFrameOnly: Boolean = False);
|
|
procedure SetKnownThread(AThread: Integer);
|
|
procedure SetKnownThreadAndFrame(AThread, AFrame: Integer);
|
|
procedure RunInstruction(AnInstruction: TGDBInstruction); // Wait for instruction to be finished, not queuing
|
|
procedure ForceTimeOutAll(ATimeOut: Integer);
|
|
property CurrentThreadId: Integer read FCurrentThreadId;
|
|
property CurrentStackFrame: Integer read FCurrentStackFrame;
|
|
property Flags: TGDBInstructionQueueFlags read FFlags;
|
|
end;
|
|
|
|
function dbgs(AState: TGDBInstructionVerifyTimeOutState): String; overload;
|
|
function dbgs(AFlag: TGDBInstructionQueueFlag): String; overload;
|
|
function dbgs(AFlags: TGDBInstructionQueueFlags): String; overload;
|
|
function dbgs(AFlag: TGDBInstructionFlag): String; overload;
|
|
function dbgs(AFlags: TGDBInstructionFlags): String; overload;
|
|
|
|
implementation
|
|
|
|
var
|
|
DBGMI_TIMEOUT_DEBUG, DBG_THREAD_AND_FRAME, DBG_VERBOSE: PLazLoggerLogGroup;
|
|
|
|
const
|
|
TIMEOUT_AFTER_WRITE_ERROR = 50;
|
|
TIMEOUT_FOR_QUEUE_INSTR = 50; // select thread/frame
|
|
TIMEOUT_FOR_SYNC_AFTER_TIMEOUT = 2500; // extra timeout, while trying to recover from a suspected timeout
|
|
TIMEOUT_FOR_SYNC_AFTER_TIMEOUT_MAX = 3000; // upper limit, when using 2*original_timeout
|
|
|
|
function dbgs(AState: TGDBInstructionVerifyTimeOutState): String; overload;
|
|
begin
|
|
writestr(Result{%H-}, AState);
|
|
end;
|
|
|
|
function dbgs(AFlag: TGDBInstructionQueueFlag): String;
|
|
begin
|
|
writestr(Result{%H-}, AFlag);
|
|
end;
|
|
|
|
function dbgs(AFlags: TGDBInstructionQueueFlags): String;
|
|
var
|
|
i: TGDBInstructionQueueFlag;
|
|
begin
|
|
Result := '';
|
|
for i := low(TGDBInstructionQueueFlags) to high(TGDBInstructionQueueFlags) do
|
|
if i in AFlags then
|
|
if Result = '' then
|
|
Result := Result + dbgs(i)
|
|
else
|
|
Result := Result + ', ' +dbgs(i);
|
|
if Result <> '' then
|
|
Result := '[' + Result + ']';
|
|
end;
|
|
|
|
function dbgs(AFlag: TGDBInstructionFlag): String;
|
|
begin
|
|
writestr(Result{%H-}, AFlag);
|
|
end;
|
|
|
|
function dbgs(AFlags: TGDBInstructionFlags): String;
|
|
var
|
|
i: TGDBInstructionFlag;
|
|
begin
|
|
Result := '';
|
|
for i := low(TGDBInstructionFlags) to high(TGDBInstructionFlags) do
|
|
if i in AFlags then
|
|
if Result = '' then
|
|
Result := Result + dbgs(i)
|
|
else
|
|
Result := Result + ', ' +dbgs(i);
|
|
if Result <> '' then
|
|
Result := '[' + Result + ']';
|
|
end;
|
|
|
|
{ TGDBInstructionChangeMemLimit }
|
|
|
|
procedure TGDBInstructionChangeMemLimit.SendCommandDataToGDB(AQueue: TGDBInstructionQueue);
|
|
begin
|
|
if FNewLimit > 0 then
|
|
AQueue.SendDataToGDB(Self, 'set max-value-size %d', [FNewLimit])
|
|
else
|
|
if FNewLimit = 0 then
|
|
AQueue.SendDataToGDB(Self, 'set max-value-size unlimited');
|
|
end;
|
|
|
|
function TGDBInstructionChangeMemLimit.ProcessInputFromGdb(const AData: String): Boolean;
|
|
begin
|
|
Result := False;
|
|
if (AData = '(gdb) ') then begin
|
|
Result := True;
|
|
MarkAsSuccess;
|
|
FQueue.FCurrentMemLimit := FNewLimit;
|
|
end
|
|
|
|
else
|
|
begin
|
|
debugln(DBG_VERBOSE, ['GDBMI TGDBInstructionChangeArrayLimit ignoring: ', AData]);
|
|
end;
|
|
end;
|
|
|
|
function TGDBInstructionChangeMemLimit.DebugText: String;
|
|
begin
|
|
Result := ClassName;
|
|
end;
|
|
|
|
constructor TGDBInstructionChangeMemLimit.Create(AQueue: TGDBInstructionQueue;
|
|
ANewLimit: Integer);
|
|
begin
|
|
inherited Create('', [], TIMEOUT_FOR_QUEUE_INSTR);
|
|
FQueue := AQueue;
|
|
// FDone := False;
|
|
FNewLimit := ANewLimit;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeArrayLimit }
|
|
|
|
procedure TGDBInstructionChangeArrayLimit.SendCommandDataToGDB(AQueue: TGDBInstructionQueue);
|
|
begin
|
|
AQueue.SendDataToGDB(Self, 'set print elements %d', [FNewLimit]);
|
|
end;
|
|
|
|
function TGDBInstructionChangeArrayLimit.ProcessInputFromGdb(const AData: String): Boolean;
|
|
begin
|
|
Result := False;
|
|
if (AData = '(gdb) ') then begin
|
|
Result := True;
|
|
MarkAsSuccess;
|
|
FQueue.FCurrentArrayLimit := FNewLimit;
|
|
end
|
|
|
|
else
|
|
begin
|
|
debugln(DBG_VERBOSE, ['GDBMI TGDBInstructionChangeArrayLimit ignoring: ', AData]);
|
|
end;
|
|
end;
|
|
|
|
function TGDBInstructionChangeArrayLimit.DebugText: String;
|
|
begin
|
|
Result := ClassName;
|
|
end;
|
|
|
|
constructor TGDBInstructionChangeArrayLimit.Create(AQueue: TGDBInstructionQueue;
|
|
ANewLimit: Integer);
|
|
begin
|
|
inherited Create('', [], TIMEOUT_FOR_QUEUE_INSTR);
|
|
FQueue := AQueue;
|
|
// FDone := False;
|
|
FNewLimit := ANewLimit;
|
|
end;
|
|
|
|
procedure TGDBInstructionChangeArrayLimit.HandleError(AnError: TGDBInstructionErrorFlag;
|
|
AMarkAsFailed: Boolean);
|
|
begin
|
|
inherited HandleError(AnError, AMarkAsFailed);
|
|
// FQueue.FCurrentArrayLimit := -2;
|
|
FQueue.FCurrentArrayLimit := FNewLimit; // ignore error
|
|
end;
|
|
|
|
{ TGDBMICmdLineDebugger }
|
|
|
|
procedure TGDBMICmdLineDebugger.DoReadError;
|
|
begin
|
|
include(FErrorHandlingFlags, ehfGotReadError);
|
|
if not(ehfDeferReadWriteError in FErrorHandlingFlags)
|
|
then inherited DoReadError;
|
|
end;
|
|
|
|
procedure TGDBMICmdLineDebugger.DoWriteError;
|
|
begin
|
|
include(FErrorHandlingFlags, ehfGotWriteError);
|
|
if not(ehfDeferReadWriteError in FErrorHandlingFlags)
|
|
then inherited DoWriteError;
|
|
end;
|
|
|
|
{ TGDBInstruction }
|
|
|
|
procedure TGDBInstruction.SendCommandDataToGDB(AQueue: TGDBInstructionQueue);
|
|
begin
|
|
AQueue.SendDataToGDB(Self, FCommand);
|
|
end;
|
|
|
|
function TGDBInstruction.IsCompleted: boolean;
|
|
begin
|
|
Result := FResultFlags * [ifrComleted, ifrFailed] <> [];
|
|
end;
|
|
|
|
procedure TGDBInstruction.MarkAsSuccess;
|
|
begin
|
|
Include(FResultFlags, ifrComleted);
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleWriteError(ASender: TGDBInstruction);
|
|
begin
|
|
HandleError(ifeWriteError, False);
|
|
if (FTimeOut = 0) or (FTimeOut > TIMEOUT_AFTER_WRITE_ERROR) then
|
|
FTimeOut := TIMEOUT_AFTER_WRITE_ERROR;
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleReadError;
|
|
begin
|
|
HandleError(ifeReadError, True);
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleTimeOut;
|
|
begin
|
|
HandleError(ifeTimedOut, True);
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleRecoveredTimeOut;
|
|
begin
|
|
Include(FErrorFlags, ifeRecoveredTimedOut);
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleNoGdbRunning;
|
|
begin
|
|
HandleError(ifeGdbNotRunning, True);
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleContentError;
|
|
begin
|
|
HandleError(ifeContentError, True);
|
|
end;
|
|
|
|
procedure TGDBInstruction.HandleError(AnError: TGDBInstructionErrorFlag;
|
|
AMarkAsFailed: Boolean = True);
|
|
begin
|
|
if AMarkAsFailed then
|
|
Include(FResultFlags, ifrFailed);
|
|
Include(FErrorFlags, AnError);
|
|
end;
|
|
|
|
function TGDBInstruction.GetTimeOutVerifier: TGDBInstruction;
|
|
begin
|
|
if (ifeWriteError in ErrorFlags) then
|
|
Result := nil
|
|
else
|
|
Result := TGDBInstructionVerifyTimeOut.Create(Self);
|
|
end;
|
|
|
|
function TGDBInstruction.DebugText: String;
|
|
begin
|
|
Result := ClassName + ': "' + FCommand + '", ' + dbgs(FFlags);
|
|
if ifRequiresThread in FFlags then
|
|
Result := Result + ' Thr=' + IntToStr(FThreadId);
|
|
if ifRequiresStackFrame in FFlags then
|
|
Result := Result + ' Frm=' + IntToStr(FStackFrame);
|
|
end;
|
|
|
|
procedure TGDBInstruction.Init;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TGDBInstruction.InternalCreate(ACommand: String; AThread, AFrame: Integer;
|
|
AFlags: TGDBInstructionFlags; ATimeOut: Integer);
|
|
begin
|
|
FCommand := ACommand;
|
|
FThreadId := AThread;
|
|
FStackFrame := AFrame;
|
|
FFlags := AFlags;
|
|
FTimeOut := ATimeOut;
|
|
end;
|
|
|
|
constructor TGDBInstruction.Create(ACommand: String; AFlags: TGDBInstructionFlags;
|
|
ATimeOut: Integer = 0);
|
|
begin
|
|
inherited Create;
|
|
InternalCreate(ACommand, -1, -1, AFlags, ATimeOut);
|
|
Init;
|
|
end;
|
|
|
|
constructor TGDBInstruction.Create(ACommand: String; AThread: Integer;
|
|
AOtherFlags: TGDBInstructionFlags; ATimeOut: Integer = 0);
|
|
begin
|
|
inherited Create;
|
|
InternalCreate(ACommand, AThread, -1,
|
|
AOtherFlags + [ifRequiresThread], ATimeOut);
|
|
Init;
|
|
end;
|
|
|
|
constructor TGDBInstruction.Create(ACommand: String; AThread, AFrame: Integer;
|
|
AOtherFlags: TGDBInstructionFlags; ATimeOut: Integer = 0);
|
|
begin
|
|
inherited Create;
|
|
InternalCreate(ACommand, AThread, AFrame,
|
|
AOtherFlags + [ifRequiresThread, ifRequiresStackFrame], ATimeOut);
|
|
Init;
|
|
end;
|
|
|
|
procedure TGDBInstruction.ApplyArrayLenLimit(ALimit: Integer);
|
|
begin
|
|
FFlags := FFlags + [ifRequiresArrayLimit];
|
|
FArrayLenLimit := ALimit;
|
|
end;
|
|
|
|
procedure TGDBInstruction.ApplyMemLimit(ALimit: Integer);
|
|
begin
|
|
FFlags := FFlags + [ifRequiresMemLimit];
|
|
FMemLimit := ALimit;
|
|
end;
|
|
|
|
function TGDBInstruction.IsSuccess: Boolean;
|
|
begin
|
|
Result := ResultFlags * [ifrComleted, ifrFailed] = [ifrComleted]
|
|
end;
|
|
|
|
{ TGDBInstructionVerifyTimeOut }
|
|
|
|
procedure TGDBInstructionVerifyTimeOut.SendCommandDataToGDB(AQueue: TGDBInstructionQueue);
|
|
begin
|
|
AQueue.SendDataToGDB(Self, '-data-evaluate-expression 7');
|
|
AQueue.SendDataToGDB(Self, '-data-evaluate-expression 1');
|
|
FState := vtSent;
|
|
end;
|
|
|
|
function TGDBInstructionVerifyTimeOut.ProcessInputFromGdb(const AData: String): Boolean;
|
|
type
|
|
TLineDataTipe = (ldOther, ldGdb, ldValue7, ldValue1);
|
|
|
|
function CheckData(const ALineData: String): TLineDataTipe;
|
|
begin
|
|
Result := ldOther;
|
|
if ALineData= '(gdb) ' then begin
|
|
Result := ldGdb;
|
|
exit;
|
|
end;
|
|
if LazStartsStr('^done,', AData) or (AData = '^done') then begin
|
|
if FList = nil then
|
|
FList := TGDBMINameValueList.Create(ALineData)
|
|
else
|
|
FList.Init(ALineData);
|
|
if FList.Values['value'] = '7' then
|
|
Result := ldValue7
|
|
else
|
|
if FList.Values['value'] = '1' then
|
|
Result := ldValue1
|
|
end;
|
|
end;
|
|
|
|
procedure SetError(APromptCount: Integer);
|
|
begin
|
|
FState := vtError;
|
|
FPromptAfterErrorCount := APromptCount; // prompt for val7 and val1 needed
|
|
FRunnigInstruction.HandleTimeOut;
|
|
if FPromptAfterErrorCount <= 0 then
|
|
FTimeOut := 50; // wait for timeout
|
|
end;
|
|
|
|
begin
|
|
Result := True;
|
|
if FState = vtError then begin
|
|
dec(FPromptAfterErrorCount);
|
|
if FPromptAfterErrorCount <= 0 then
|
|
FTimeOut := 50; // wait for timeout
|
|
exit;
|
|
end;
|
|
|
|
case CheckData(AData) of
|
|
ldOther: begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Got other data']);
|
|
Result := FRunnigInstruction.ProcessInputFromGdb(AData);
|
|
end;
|
|
ldGdb:
|
|
case FState of
|
|
vtSent: begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Got prompt in order']);
|
|
FState := vtGotPrompt;
|
|
Result := FRunnigInstruction.ProcessInputFromGdb(AData);
|
|
if not FRunnigInstruction.IsCompleted then begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Prompt was not accepted']);
|
|
SetError(2); // prompt for val=7 and val=1 needed
|
|
end;
|
|
end;
|
|
vtGotPrompt7: FState := vtGotPrompt7gdb;
|
|
vtGotPrompt7and7: FState := vtGotPrompt7and7gdb;
|
|
vtGotPrompt1: FState := vtGotPrompt1gdb;
|
|
vtGot7: FState := vtGot7gdb;
|
|
vtGot7and7: FState := vtGot7and7gdb;
|
|
vtGot1: FState := vtGot1gdb;
|
|
else begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Extra Prompt ']);
|
|
if FState = vtGotPrompt
|
|
then SetError(1) // prompt val=1 needed
|
|
else SetError(0); // no more prompt needed
|
|
end;
|
|
end;
|
|
ldValue7:
|
|
case FState of
|
|
vtSent, vtGotPrompt: begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Got value 7']);
|
|
FVal7Data := AData;
|
|
if FState = vtSent
|
|
then FState := vtGot7
|
|
else FState := vtGotPrompt7;
|
|
end;
|
|
vtGotPrompt7gdb, vtGot7gdb: begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Got value 7 twice. Original Result?']);
|
|
Result := FRunnigInstruction.ProcessInputFromGdb(FVal7Data);
|
|
if FState = vtGotPrompt7gdb
|
|
then FState := vtGotPrompt7and7
|
|
else FState := vtGot7and7;
|
|
end;
|
|
else begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Extra VAlue 7']);
|
|
if FState in [vtGotPrompt7, vtGot7]
|
|
then SetError(1) // prompt val=1 needed
|
|
else SetError(0); // no more prompt needed
|
|
end;
|
|
end;
|
|
ldValue1:
|
|
case FState of
|
|
vtSent: begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Got other data']);
|
|
Result := FRunnigInstruction.ProcessInputFromGdb(AData);
|
|
end;
|
|
vtGotPrompt7gdb: FState := vtGotPrompt1;
|
|
vtGotPrompt7and7gdb: FState := vtGotPrompt1;
|
|
vtGot7gdb: FState := vtGot1;
|
|
vtGot7and7gdb: FState := vtGot1;
|
|
else begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Wrong Value 1']);
|
|
SetError(0);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if FState = vtGot1gdb then begin
|
|
// timeout, but recovored
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): Recovered']);
|
|
FRunnigInstruction.ProcessInputFromGdb('(gdb) '); // simulate prompt
|
|
FRunnigInstruction.HandleRecoveredTimeOut;
|
|
end;
|
|
if FState in [vtGot1gdb, vtGotPrompt1gdb] then begin
|
|
debugln(DBGMI_TIMEOUT_DEBUG, ['GDBMI TimeOut(', dbgs(FState), '): All done: original Instruction completed=',dbgs(FRunnigInstruction.IsCompleted)]);
|
|
Include(FResultFlags, ifrComleted);
|
|
if not FRunnigInstruction.IsCompleted then
|
|
FRunnigInstruction.HandleTimeOut;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TGDBInstructionVerifyTimeOut.HandleWriteError(ASender: TGDBInstruction);
|
|
begin
|
|
inherited HandleWriteError(ASender);
|
|
FRunnigInstruction.HandleWriteError(ASender);
|
|
end;
|
|
|
|
procedure TGDBInstructionVerifyTimeOut.HandleReadError;
|
|
begin
|
|
inherited HandleReadError;
|
|
FRunnigInstruction.HandleReadError;
|
|
end;
|
|
|
|
procedure TGDBInstructionVerifyTimeOut.HandleTimeOut;
|
|
begin
|
|
inherited HandleTimeOut;
|
|
FRunnigInstruction.HandleTimeOut;
|
|
end;
|
|
|
|
procedure TGDBInstructionVerifyTimeOut.HandleNoGdbRunning;
|
|
begin
|
|
inherited HandleNoGdbRunning;
|
|
FRunnigInstruction.HandleNoGdbRunning;
|
|
end;
|
|
|
|
function TGDBInstructionVerifyTimeOut.GetTimeOutVerifier: TGDBInstruction;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
function TGDBInstructionVerifyTimeOut.DebugText: String;
|
|
begin
|
|
Result := ClassName + ': for "' + FRunnigInstruction.DebugText;
|
|
end;
|
|
|
|
constructor TGDBInstructionVerifyTimeOut.Create(ARunnigInstruction: TGDBInstruction);
|
|
var
|
|
t: Integer;
|
|
begin
|
|
FRunnigInstruction := ARunnigInstruction;
|
|
FRunnigInstruction.AddReference;
|
|
t := FRunnigInstruction.TimeOut;
|
|
t := max(TIMEOUT_FOR_SYNC_AFTER_TIMEOUT, Min(TIMEOUT_FOR_SYNC_AFTER_TIMEOUT_MAX, t * 2));
|
|
inherited Create('', FRunnigInstruction.ThreadId, FRunnigInstruction.StackFrame,
|
|
FRunnigInstruction.Flags * [ifRequiresThread, ifRequiresStackFrame],
|
|
t);
|
|
end;
|
|
|
|
destructor TGDBInstructionVerifyTimeOut.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FList);
|
|
if (FRunnigInstruction <> nil) then
|
|
FRunnigInstruction.ReleaseReference;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeThread }
|
|
|
|
procedure TGDBInstructionChangeThread.SendCommandDataToGDB(AQueue: TGDBInstructionQueue);
|
|
begin
|
|
AQueue.SendDataToGDB(Self, '-thread-select %d', [FSelThreadId]);
|
|
end;
|
|
|
|
function TGDBInstructionChangeThread.ProcessInputFromGdb(const AData: String): Boolean;
|
|
begin
|
|
// "-thread-select 2"
|
|
// "^done,new-thread-id="2",frame={level="0",addr="0x7707878f",func="ntdll!DbgUiConvertStateChangeStructure",args=[],from="C:\\Windows\\system32\\ntdll.dll"}"
|
|
// "(gdb) "
|
|
|
|
Result := False;
|
|
if LazStartsStr('^done,', AData) or (AData = '^done') then begin
|
|
Result := True;
|
|
if FDone then
|
|
HandleContentError;
|
|
FDone := True;
|
|
end
|
|
|
|
else
|
|
if (AData = '(gdb) ') then begin
|
|
Result := True;
|
|
if not FDone then begin
|
|
HandleContentError;
|
|
end
|
|
else begin
|
|
MarkAsSuccess;
|
|
FQueue.FCurrentThreadId := FSelThreadId;
|
|
FQueue.FFlags := FQueue.FFlags + [ifqValidThread];
|
|
end;
|
|
end
|
|
|
|
else
|
|
begin
|
|
debugln(DBG_VERBOSE, ['GDBMI TGDBInstructionChangeThread ignoring: ', AData]);
|
|
end;
|
|
end;
|
|
|
|
procedure TGDBInstructionChangeThread.HandleError(AnError: TGDBInstructionErrorFlag;
|
|
AMarkAsFailed: Boolean);
|
|
begin
|
|
inherited HandleError(AnError, AMarkAsFailed);
|
|
FQueue.InvalidateThredAndFrame;
|
|
end;
|
|
|
|
function TGDBInstructionChangeThread.DebugText: String;
|
|
begin
|
|
Result := ClassName;
|
|
end;
|
|
|
|
constructor TGDBInstructionChangeThread.Create(AQueue: TGDBInstructionQueue;
|
|
AThreadId: Integer);
|
|
begin
|
|
inherited Create('', [], TIMEOUT_FOR_QUEUE_INSTR);
|
|
FQueue := AQueue;
|
|
FDone := False;
|
|
FSelThreadId := AThreadId;
|
|
end;
|
|
|
|
{ TGDBInstructionChangeStackFrame }
|
|
|
|
procedure TGDBInstructionChangeStackFrame.SendCommandDataToGDB(AQueue: TGDBInstructionQueue);
|
|
begin
|
|
AQueue.SendDataToGDB(Self, '-stack-select-frame %d', [FSelStackFrame]);
|
|
end;
|
|
|
|
function TGDBInstructionChangeStackFrame.ProcessInputFromGdb(const AData: String): Boolean;
|
|
begin
|
|
// "-stack-select-frame 0"
|
|
// "^done"
|
|
// "(gdb) "
|
|
//OR ^error => keep selected ?
|
|
|
|
Result := False;
|
|
if LazStartsStr('^done,', AData) or (AData = '^done') then begin
|
|
Result := True;
|
|
if FDone then
|
|
HandleContentError;
|
|
FDone := True;
|
|
end
|
|
|
|
else
|
|
if (AData = '(gdb) ') then begin
|
|
Result := True;
|
|
if not FDone then begin
|
|
HandleContentError;
|
|
end
|
|
else begin
|
|
MarkAsSuccess;
|
|
FQueue.FCurrentStackFrame := FSelStackFrame;
|
|
FQueue.FFlags := FQueue.FFlags + [ifqValidStackFrame];
|
|
end;
|
|
end
|
|
|
|
else
|
|
begin
|
|
debugln(DBG_VERBOSE, ['GDBMI TGDBInstructionChangeStackFrame ignoring: ', AData]);
|
|
end;
|
|
end;
|
|
|
|
procedure TGDBInstructionChangeStackFrame.HandleError(AnError: TGDBInstructionErrorFlag;
|
|
AMarkAsFailed: Boolean);
|
|
begin
|
|
inherited HandleError(AnError, AMarkAsFailed);
|
|
FQueue.InvalidateThredAndFrame(True);
|
|
end;
|
|
|
|
function TGDBInstructionChangeStackFrame.DebugText: String;
|
|
begin
|
|
Result := ClassName;
|
|
end;
|
|
|
|
constructor TGDBInstructionChangeStackFrame.Create(AQueue: TGDBInstructionQueue;
|
|
AFrame: Integer);
|
|
begin
|
|
inherited Create('', [], TIMEOUT_FOR_QUEUE_INSTR);
|
|
FQueue := AQueue;
|
|
FDone := False;
|
|
FSelStackFrame := AFrame;
|
|
end;
|
|
|
|
{ TGDBInstructionQueue }
|
|
|
|
procedure TGDBInstructionQueue.ExecuteCurrentInstruction;
|
|
function RunHelpInstruction(AnHelpInstr: TGDBInstruction): boolean;
|
|
begin
|
|
AnHelpInstr.AddReference;
|
|
try
|
|
FCurrentInstruction := AnHelpInstr;
|
|
FCurrentInstruction.SendCommandDataToGDB(Self);
|
|
FinishCurrentInstruction;
|
|
Result := AnHelpInstr.IsSuccess;
|
|
finally
|
|
AnHelpInstr.ReleaseReference;
|
|
end;
|
|
end;
|
|
var
|
|
ExeInstr, HelpInstr: TGDBInstruction;
|
|
CurStamp: Int64;
|
|
begin
|
|
if FCurrentInstruction = nil then
|
|
exit;
|
|
|
|
if FExeCurInstructionStamp = high(FExeCurInstructionStamp) then
|
|
FExeCurInstructionStamp := low(FExeCurInstructionStamp)
|
|
else
|
|
inc(FExeCurInstructionStamp);
|
|
|
|
ExeInstr := FCurrentInstruction;
|
|
ExeInstr.AddReference;
|
|
try
|
|
while true do begin
|
|
CurStamp := FExeCurInstructionStamp;
|
|
|
|
If (ifRequiresMemLimit in ExeInstr.Flags) and (FCurrentMemLimit <> ExeInstr.MemLimit) then begin
|
|
HelpInstr := TGDBInstructionChangeMemLimit.Create(Self, ExeInstr.MemLimit);
|
|
RunHelpInstruction(HelpInstr); // ignore result
|
|
end;
|
|
|
|
If (ifRequiresArrayLimit in ExeInstr.Flags) and (FCurrentArrayLimit <> ExeInstr.ArrayLenLimit) then begin
|
|
HelpInstr := TGDBInstructionChangeArrayLimit.Create(Self, ExeInstr.ArrayLenLimit);
|
|
RunHelpInstruction(HelpInstr); // ignore result
|
|
end;
|
|
|
|
if not HasCorrectThreadIdFor(ExeInstr) then begin
|
|
HelpInstr := GetSelectThreadInstruction(ExeInstr.ThreadId);
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Changing Thread from: ', FCurrentThreadId, ' - ', dbgs(FFlags),
|
|
' to ', ExeInstr.ThreadId, ' using [', HelpInstr.DebugText, '] for [', ExeInstr.DebugText, ']']);
|
|
if not RunHelpInstruction(HelpInstr) then begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Changing Thread FAILED']);
|
|
ExeInstr.HandleError(ifeInvalidThreadId);
|
|
exit;
|
|
end
|
|
end;
|
|
|
|
if not HasCorrectThreadIdFor(ExeInstr) then begin
|
|
if CurStamp = FExeCurInstructionStamp then begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Thread was interuppted, FAILING']);
|
|
ExeInstr.HandleError(ifeQueueContextError);
|
|
exit;
|
|
end;
|
|
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Thread was interuppted, repeating']);
|
|
continue;
|
|
end;
|
|
|
|
|
|
if not HasCorrectFrameFor(ExeInstr) then begin
|
|
HelpInstr := GetSelectFrameInstruction(ExeInstr.StackFrame);
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Changing Stack from: ', FCurrentStackFrame, ' - ', dbgs(FFlags),
|
|
' to ', ExeInstr.StackFrame, ' using [', HelpInstr.DebugText, '] for [', ExeInstr.DebugText, ']']);
|
|
if not RunHelpInstruction(HelpInstr) then begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Changing Stackframe FAILED']);
|
|
ExeInstr.HandleError(ifeInvalidStackFrame);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if not (HasCorrectThreadIdFor(ExeInstr) and HasCorrectFrameFor(ExeInstr)) then begin
|
|
if CurStamp = FExeCurInstructionStamp then begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Stack was interuppted, FAILING']);
|
|
ExeInstr.HandleError(ifeQueueContextError);
|
|
exit;
|
|
end;
|
|
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Stack was interuppted, repeating']);
|
|
continue;
|
|
end;
|
|
|
|
break;
|
|
end; // while true
|
|
finally
|
|
if (ExeInstr.RefCount > 1) and (not ExeInstr.IsCompleted) then
|
|
FCurrentInstruction := ExeInstr
|
|
else
|
|
FCurrentInstruction := nil;
|
|
ExeInstr.ReleaseReference;
|
|
end;
|
|
|
|
if FCurrentInstruction <> nil then
|
|
FCurrentInstruction.SendCommandDataToGDB(Self);
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.FinishCurrentInstruction;
|
|
var
|
|
S: String;
|
|
NewInstr, ExeInstr: TGDBInstruction;
|
|
Skip: Boolean;
|
|
Handled: Boolean;
|
|
begin
|
|
if FCurrentInstruction = nil then exit;
|
|
ExeInstr := FCurrentInstruction;
|
|
ExeInstr.AddReference;
|
|
try
|
|
while (FCurrentInstruction <> nil) and
|
|
(not FCurrentInstruction.IsCompleted)
|
|
do begin
|
|
if not FDebugger.DebugProcessRunning then begin
|
|
FCurrentInstruction.HandleNoGdbRunning;
|
|
break;
|
|
end;
|
|
|
|
S := FDebugger.ReadLine(FCurrentInstruction.TimeOut);
|
|
// Readline, may go into Application.ProcessMessages.
|
|
// If it does, it has not (yet) read any data.
|
|
// Therefore, if it does, another nested call to readline will work, and data will be returned in the correct order.
|
|
// If a nested readline reads all data, then the outer will have nothing to return.
|
|
|
|
if (FCurrentInstruction = nil) or (FCurrentInstruction.IsCompleted) then begin
|
|
if s <> '' then // Should not happen
|
|
DebugLn(DBG_VERBOSE, ['TGDB_IQ: Got Data, but command was finished. Cmd: ', ExeInstr.DebugText, ' Data: ', S]);
|
|
if not FDebugger.ReadLineWasAbortedByNested then
|
|
DebugLn(DBG_VERBOSE, ['TGDB_IQ: Missing instruction. Not flagged as nested. Cmd: ', ExeInstr.DebugText, ' Data: ', S]);
|
|
break;
|
|
end;
|
|
|
|
if FDebugger.ReadLineWasAbortedByNested and (S = '') then
|
|
Continue;
|
|
|
|
Skip := False;
|
|
HandleGdbDataBeforeInstruction(S, Skip, FCurrentInstruction);
|
|
// HandleGdbDataBeforeInstruction may execune other Instructions
|
|
if (FCurrentInstruction = nil) or (FCurrentInstruction.IsCompleted) then
|
|
break;
|
|
|
|
if (not Skip) and
|
|
( (not FDebugger.ReadLineTimedOut) or (S <> '') )
|
|
then
|
|
Handled := FCurrentInstruction.ProcessInputFromGdb(S);
|
|
|
|
HandleGdbDataAfterInstruction(S, Handled, FCurrentInstruction);
|
|
|
|
if (ehfGotReadError in FDebugger.FErrorHandlingFlags) then begin
|
|
FCurrentInstruction.HandleReadError;
|
|
break;
|
|
end;
|
|
if FDebugger.ReadLineTimedOut then begin
|
|
if FDebugger.IsInReset then
|
|
break;
|
|
NewInstr := FCurrentInstruction.GetTimeOutVerifier;
|
|
if NewInstr <> nil then begin
|
|
NewInstr.AddReference;
|
|
ExeInstr.ReleaseReference;
|
|
ExeInstr := NewInstr;
|
|
// TODO: Run NewInstr;
|
|
FCurrentInstruction := NewInstr;
|
|
FCurrentInstruction.SendCommandDataToGDB(Self); // ExecuteCurrentInstruction;
|
|
|
|
end
|
|
else begin
|
|
FCurrentInstruction.HandleTimeOut;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
end; // while
|
|
FCurrentInstruction := nil;
|
|
finally
|
|
ExeInstr.ReleaseReference;
|
|
end;
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.SetCurrentInstruction(AnInstruction: TGDBInstruction);
|
|
begin
|
|
FinishCurrentInstruction;
|
|
FCurrentInstruction := AnInstruction;
|
|
end;
|
|
|
|
function TGDBInstructionQueue.HasCorrectThreadIdFor(AnInstruction: TGDBInstruction): Boolean;
|
|
begin
|
|
Result := not(ifRequiresThread in AnInstruction.Flags);
|
|
if Result then
|
|
exit;
|
|
Result := (ifqValidThread in Flags) and (CurrentThreadId = AnInstruction.ThreadId);
|
|
end;
|
|
|
|
function TGDBInstructionQueue.HasCorrectFrameFor(AnInstruction: TGDBInstruction): Boolean;
|
|
begin
|
|
Result := not(ifRequiresStackFrame in AnInstruction.Flags);
|
|
if Result then
|
|
exit;
|
|
Result := (ifqValidStackFrame in Flags) and (CurrentStackFrame = AnInstruction.StackFrame);
|
|
end;
|
|
|
|
function TGDBInstructionQueue.SendDataToGDB(ASender: TGDBInstruction; AData: String): Boolean;
|
|
begin
|
|
Result := True;
|
|
FDebugger.FErrorHandlingFlags := FDebugger.FErrorHandlingFlags
|
|
+ [ehfDeferReadWriteError] - [ehfGotReadError, ehfGotWriteError];
|
|
|
|
FDebugger.SendCmdLn(AData);
|
|
|
|
if ehfGotWriteError in FDebugger.FErrorHandlingFlags then begin
|
|
Result := False;
|
|
// TODO try reading, but ensure timeout
|
|
if FCurrentInstruction <> nil then
|
|
FCurrentInstruction.HandleWriteError(ASender)
|
|
else
|
|
if ASender <> nil then
|
|
ASender.HandleWriteError(ASender);
|
|
end;
|
|
end;
|
|
|
|
function TGDBInstructionQueue.SendDataToGDB(ASender: TGDBInstruction; AData: String;
|
|
const AValues: array of const): Boolean;
|
|
begin
|
|
Result := SendDataToGDB(ASender, Format(AData, AValues));
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.HandleGdbDataBeforeInstruction(var AData: String;
|
|
var SkipData: Boolean; const TheInstruction: TGDBInstruction);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.HandleGdbDataAfterInstruction(var AData: String;
|
|
const Handled: Boolean; const TheInstruction: TGDBInstruction);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
function TGDBInstructionQueue.GetSelectThreadInstruction(AThreadId: Integer): TGDBInstruction;
|
|
begin
|
|
Result := TGDBInstructionChangeThread.Create(Self, AThreadId);
|
|
end;
|
|
|
|
function TGDBInstructionQueue.GetSelectFrameInstruction(AFrame: Integer): TGDBInstruction;
|
|
begin
|
|
Result := TGDBInstructionChangeStackFrame.Create(Self, AFrame);
|
|
end;
|
|
|
|
constructor TGDBInstructionQueue.Create(ADebugger: TGDBMICmdLineDebugger);
|
|
begin
|
|
FDebugger := ADebugger;
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.InvalidateThredAndFrame(AStackFrameOnly: Boolean = False);
|
|
begin
|
|
if AStackFrameOnly then begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Invalidating queue''s stack only. Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
|
|
FFlags := FFlags - [ifqValidStackFrame];
|
|
end
|
|
else begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Invalidating queue''s current thread and stack. Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
|
|
FFlags := FFlags - [ifqValidThread, ifqValidStackFrame];
|
|
end;
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.SetKnownThread(AThread: Integer);
|
|
begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Setting queue''s current thread and stack. New: Thr=', AThread, ' Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
|
|
FCurrentThreadId := AThread;
|
|
FFlags := FFlags + [ifqValidThread] - [ifqValidStackFrame];
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.SetKnownThreadAndFrame(AThread, AFrame: Integer);
|
|
begin
|
|
DebugLn(DBG_THREAD_AND_FRAME, ['TGDB_IQ: Setting queue''s current thread and stack. New: Thr=', AThread, ' Frm=', AFrame,' Was: ', dbgs(FFlags), ' Thr=', FCurrentThreadId, ' Frm=', FCurrentStackFrame]);
|
|
FCurrentThreadId := AThread;
|
|
FCurrentStackFrame := AFrame;
|
|
FFlags := FFlags + [ifqValidThread, ifqValidStackFrame];
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.RunInstruction(AnInstruction: TGDBInstruction);
|
|
begin
|
|
SetCurrentInstruction(AnInstruction);
|
|
ExecuteCurrentInstruction;
|
|
FinishCurrentInstruction;
|
|
end;
|
|
|
|
procedure TGDBInstructionQueue.ForceTimeOutAll(ATimeOut: Integer);
|
|
begin
|
|
if FCurrentInstruction <> nil then
|
|
FCurrentInstruction.FTimeOut := ATimeOut;
|
|
end;
|
|
|
|
initialization
|
|
DBGMI_TIMEOUT_DEBUG := DebugLogger.FindOrRegisterLogGroup('DBGMI_TIMEOUT_DEBUG' {$IFDEF DBGMI_TIMEOUT_DEBUG} , True {$ENDIF} );
|
|
DBG_THREAD_AND_FRAME := DebugLogger.FindOrRegisterLogGroup('DBG_THREAD_AND_FRAME' {$IFDEF DBG_THREAD_AND_FRAME} , True {$ENDIF} );
|
|
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
|
|
|
end.
|
|
|