mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-27 11:31:52 +02:00
6178 lines
190 KiB
ObjectPascal
6178 lines
190 KiB
ObjectPascal
{ $Id$ }
|
|
{ -------------------------------------------
|
|
DebuggerBase.pp - Debugger base classes
|
|
-------------------------------------------
|
|
|
|
@author(Marc Weustink <marc@@dommelstein.net>)
|
|
@author(Martin Friebe)
|
|
|
|
This unit contains the base class definitions of the debugger. These
|
|
classes are only definitions. Implemented debuggers should be
|
|
derived from these.
|
|
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit DbgIntfDebuggerBase;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
{$ifndef VER2}
|
|
{$define disassemblernestedproc}
|
|
{$endif VER2}
|
|
|
|
{$ifdef disassemblernestedproc}
|
|
{$modeswitch nestedprocvars}
|
|
{$endif disassemblernestedproc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, sysutils, math, contnrs,
|
|
// LCL
|
|
LCLProc,
|
|
// LazUtils
|
|
LazClasses, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, LazFileUtils, LazStringUtils, Maps, LazMethodList,
|
|
// DebuggerIntf
|
|
DbgIntfBaseTypes, DbgIntfMiscClasses, DbgIntfPseudoTerminal,
|
|
DbgIntfCommonStrings, LazDebuggerIntf, LazDebuggerTemplate,
|
|
LazDebuggerIntfBaseTypes;
|
|
|
|
const
|
|
DebuggerIntfVersion = 0;
|
|
|
|
type
|
|
EDebuggerException = class(Exception);
|
|
EDBGExceptions = class(EDebuggerException);
|
|
|
|
TDBGFeature = (
|
|
dfEvalFunctionCalls // The debugger supports calling functions in watches/expressions. defAllowFunctionCall in TWatcheEvaluateFlags
|
|
);
|
|
TDBGFeatures = set of TDBGFeature;
|
|
|
|
TDBGCommand = (
|
|
dcRun,
|
|
dcPause,
|
|
dcStop,
|
|
dcStepOver,
|
|
dcStepInto,
|
|
dcStepOut,
|
|
dcStepTo,
|
|
dcRunTo,
|
|
dcJumpto,
|
|
dcAttach,
|
|
dcDetach,
|
|
dcBreak,
|
|
dcWatch,
|
|
dcLocal,
|
|
dcEvaluate,
|
|
dcModify,
|
|
dcEnvironment,
|
|
dcSetStackFrame,
|
|
dcDisassemble,
|
|
dcStepOverInstr,
|
|
dcStepIntoInstr,
|
|
dcSendConsoleInput
|
|
//, dcSendSignal
|
|
);
|
|
TDBGCommands = set of TDBGCommand;
|
|
|
|
TDBGLocationRec = record
|
|
Address: TDBGPtr;
|
|
FuncName: String;
|
|
SrcFile: String;
|
|
SrcFullName: String;
|
|
SrcLine: Integer;
|
|
end;
|
|
|
|
TDBGExceptionType = (
|
|
deInternal,
|
|
deExternal,
|
|
deRunError
|
|
);
|
|
|
|
(* TValidState: State for breakpoints *)
|
|
TValidState = (vsUnknown, vsValid, vsInvalid, vsPending);
|
|
|
|
const
|
|
DebuggerDataStateStr : array[TDebuggerDataState] of string = (
|
|
'Unknown',
|
|
'Requested',
|
|
'Evaluating',
|
|
'Valid',
|
|
'Invalid',
|
|
'Error');
|
|
|
|
type
|
|
{ TRunningProcessInfo
|
|
Used to enumerate running processes.
|
|
}
|
|
|
|
TRunningProcessInfo = class
|
|
public
|
|
PID: Cardinal;
|
|
ImageName: string;
|
|
constructor Create(APID: Cardinal; const AImageName: string);
|
|
end;
|
|
|
|
TRunningProcessInfoList = TObjectList;
|
|
|
|
(* TDebuggerDataMonitor / TDebuggerDataSupplier
|
|
- TDebuggerDataMonitor
|
|
used by the IDE to receive/request updates on all data objects
|
|
- TDebuggerDataSupplier
|
|
used by the debugger to provide updates on all data objects
|
|
*)
|
|
|
|
TDebuggerIntf = class;
|
|
TDebuggerClass = class of TDebuggerIntf;
|
|
TDebuggerDataSupplier = class;
|
|
|
|
{ TDebuggerDataHandler }
|
|
|
|
TDebuggerDataHandler = class
|
|
private
|
|
FNotifiedState: TDBGState;
|
|
FOldState: TDBGState;
|
|
protected
|
|
procedure DoStateEnterPause; virtual;
|
|
procedure DoStateLeavePause; virtual;
|
|
procedure DoStateLeavePauseClean; virtual;
|
|
procedure DoStateChangeEx(const AOldState, ANewState: TDBGState); virtual;
|
|
property NotifiedState: TDBGState read FNotifiedState; // The last state seen by DoStateChange
|
|
property OldState: TDBGState read FOldState; // The state before last DoStateChange
|
|
public
|
|
end;
|
|
|
|
{ TDebuggerDataMonitor }
|
|
|
|
TDebuggerDataMonitor = class(TDebuggerDataHandler)
|
|
private
|
|
FUpdateCount: Integer;
|
|
FSupplier: TDebuggerDataSupplier;
|
|
procedure SetSupplier(const AValue: TDebuggerDataSupplier);
|
|
protected
|
|
procedure DoModified; virtual; // user-modified / xml-storable data modified
|
|
procedure DoNewSupplier; virtual;
|
|
procedure DoBeginUpdate; virtual;
|
|
procedure DoEndUpdate; virtual;
|
|
|
|
property Supplier: TDebuggerDataSupplier read FSupplier write SetSupplier;
|
|
public
|
|
destructor Destroy; override;
|
|
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
function IsUpdating: Boolean;
|
|
end;
|
|
|
|
|
|
{ TDebuggerDataSupplierBase }
|
|
|
|
TDebuggerDataSupplierBase = class(TDebuggerDataHandler)
|
|
private
|
|
FDebugger: TDebuggerIntf;
|
|
protected
|
|
procedure DoStateLeavePauseClean; override;
|
|
|
|
property Debugger: TDebuggerIntf read FDebugger write FDebugger;
|
|
public
|
|
constructor Create(const ADebugger: TDebuggerIntf);
|
|
end;
|
|
|
|
{ TDebuggerDataSupplier }
|
|
|
|
TDebuggerDataSupplier = class(TDebuggerDataSupplierBase)
|
|
private
|
|
FUpdateCount: Integer;
|
|
FMonitor: TDebuggerDataMonitor;
|
|
procedure SetMonitor(const AValue: TDebuggerDataMonitor);
|
|
property Monitor: TDebuggerDataMonitor read FMonitor write SetMonitor;
|
|
protected
|
|
procedure DoNewMonitor; virtual;
|
|
protected
|
|
|
|
procedure DoStateChange(const AOldState: TDBGState); virtual;
|
|
|
|
procedure DoBeginUpdate; virtual;
|
|
procedure DoEndUpdate; virtual;
|
|
public
|
|
destructor Destroy; override;
|
|
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
function IsUpdating: Boolean;
|
|
end;
|
|
|
|
{$region Breakpoints **********************************************************}
|
|
(******************************************************************************)
|
|
(** **)
|
|
(** B R E A K P O I N T S **)
|
|
(** **)
|
|
(** Note: This part of the interface may/will still change to the **)
|
|
(** monitor/supplier concept **)
|
|
(** **)
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
|
|
TDBGBreakPointKind = (
|
|
bpkSource, // source breakpoint
|
|
bpkAddress, // address breakpoint
|
|
bpkData // data/watchpoint
|
|
);
|
|
|
|
TDBGWatchPointScope = (
|
|
wpsLocal,
|
|
wpsGlobal
|
|
);
|
|
|
|
TDBGWatchPointKind = (
|
|
wpkWrite,
|
|
wpkRead,
|
|
wpkReadWrite,
|
|
wkpExec
|
|
);
|
|
|
|
{ TBaseBreakPoint }
|
|
|
|
TBaseBreakPoint = class(TRefCountedColectionItem)
|
|
private
|
|
FAddress: TDBGPtr;
|
|
FEnabled: Boolean;
|
|
FInitialEnabled: Boolean;
|
|
FExpression: String;
|
|
FHitCount: Integer; // Current counter
|
|
FBreakHitCount: Integer; // The user configurable value
|
|
FKind: TDBGBreakPointKind;
|
|
protected // TODO: private
|
|
FWatchData: String;
|
|
FWatchScope: TDBGWatchPointScope;
|
|
FWatchKind: TDBGWatchPointKind;
|
|
FSource: String;
|
|
FLine: Integer;
|
|
FValid: TValidState;
|
|
protected
|
|
type
|
|
(* ciCreated will be called, as soon as any other property is set the first time (or at EndUpdate)
|
|
ciLocation includes Address, WatchData,Watch....
|
|
*)
|
|
TDbgBpChangeIndicator = (ciCreated, ciDestroy, ciKind, ciLocation, ciEnabled, ciCondition, ciHitCount);
|
|
TDbgBpChangeIndicators = set of TDbgBpChangeIndicator;
|
|
// For the debugger backend to override
|
|
private
|
|
FPropertiesChanged: TDbgBpChangeIndicators;
|
|
FInPropertiesChanged: Boolean;
|
|
protected
|
|
procedure MarkPropertyChanged(AChanged: TDbgBpChangeIndicator);
|
|
procedure MarkPropertiesChanged(AChanged: TDbgBpChangeIndicators);
|
|
procedure DoPropertiesChanged(AChanged: TDbgBpChangeIndicators); virtual;
|
|
procedure DoExpressionChange; virtual;
|
|
procedure DoEnableChange; virtual;
|
|
// TODO: ClearPropertiesChanged, if needed inside DoPropertiesChanged
|
|
protected
|
|
procedure AssignLocationTo(Dest: TPersistent); virtual;
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure DoHit(const ACount: Integer; var {%H-}AContinue: Boolean); virtual;
|
|
procedure SetHitCount(const AValue: Integer);
|
|
procedure SetValid(const AValue: TValidState);
|
|
protected
|
|
// virtual properties
|
|
function GetAddress: TDBGPtr; virtual;
|
|
function GetBreakHitCount: Integer; virtual;
|
|
function GetEnabled: Boolean; virtual;
|
|
function GetExpression: String; virtual;
|
|
function GetHitCount: Integer; virtual;
|
|
function GetKind: TDBGBreakPointKind; virtual;
|
|
function GetLine: Integer; virtual;
|
|
function GetSource: String; virtual;
|
|
function GetWatchData: String; virtual;
|
|
function GetWatchScope: TDBGWatchPointScope; virtual;
|
|
function GetWatchKind: TDBGWatchPointKind; virtual;
|
|
function GetValid: TValidState; virtual;
|
|
procedure DoEndUpdate; override;
|
|
|
|
procedure SetBreakHitCount(const AValue: Integer); virtual;
|
|
procedure SetEnabled(const AValue: Boolean); virtual;
|
|
procedure SetExpression(const AValue: String); virtual;
|
|
procedure SetInitialEnabled(const AValue: Boolean); virtual;
|
|
procedure SetKind(const AValue: TDBGBreakPointKind);
|
|
public
|
|
constructor Create(ACollection: TCollection); override;
|
|
destructor Destroy; override;
|
|
procedure SetPendingToValid(const AValue: TValidState);
|
|
// PublicProtectedFix ide/debugmanager.pas(867,32) Error: identifier idents no member "SetLocation"
|
|
property BreakHitCount: Integer read GetBreakHitCount write SetBreakHitCount;
|
|
property Enabled: Boolean read GetEnabled write SetEnabled;
|
|
property Expression: String read GetExpression write SetExpression;
|
|
property HitCount: Integer read GetHitCount;
|
|
property InitialEnabled: Boolean read FInitialEnabled write SetInitialEnabled;
|
|
property Kind: TDBGBreakPointKind read GetKind;
|
|
property Valid: TValidState read GetValid;
|
|
public
|
|
procedure SetAddress(const AValue: TDBGPtr); virtual;
|
|
procedure SetLocation(const ASource: String; const ALine: Integer); virtual;
|
|
procedure SetWatch(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind); virtual;
|
|
// bpkAddress
|
|
property Address: TDBGPtr read GetAddress write SetAddress;
|
|
// bpkSource
|
|
// TDBGBreakPoint: Line is the line-number as stored in the debug info
|
|
// TIDEBreakPoint: Line is the location in the Source (potentially modified Source)
|
|
property Line: Integer read GetLine;
|
|
property Source: String read GetSource;
|
|
// bpkData
|
|
property WatchData: String read GetWatchData;
|
|
property WatchScope: TDBGWatchPointScope read GetWatchScope;
|
|
property WatchKind: TDBGWatchPointKind read GetWatchKind;
|
|
end;
|
|
TBaseBreakPointClass = class of TBaseBreakPoint;
|
|
|
|
{ TDBGBreakPoint }
|
|
|
|
TDBGBreakPoint = class(TBaseBreakPoint)
|
|
private
|
|
FSlave: TBaseBreakPoint;
|
|
function GetDebugger: TDebuggerIntf;
|
|
function GetDebuggerState: TDBGState;
|
|
procedure SetSlave(const ASlave : TBaseBreakPoint);
|
|
protected
|
|
procedure SetEnabled(const AValue: Boolean); override; // TODO: remove, currently used by WatchPoint, instead of vsInvalid
|
|
procedure DoChanged; override;
|
|
procedure DoStateChange(const AOldState: TDBGState); virtual;
|
|
property Debugger: TDebuggerIntf read GetDebugger;
|
|
property DebuggerState: TDBGState read GetDebuggerState;
|
|
public
|
|
constructor Create(ACollection: TCollection); override;
|
|
destructor Destroy; override;
|
|
procedure Hit(var ACanContinue: Boolean);
|
|
property Slave: TBaseBreakPoint read FSlave write SetSlave;
|
|
property Kind: TDBGBreakPointKind read GetKind write SetKind; // TODO: remove, used by TIDEBreakPoint.SetKind
|
|
|
|
procedure DoLogMessage(const AMessage: String); virtual;
|
|
procedure DoLogCallStack(const {%H-}Limit: Integer); virtual;
|
|
procedure DoLogExpression(const {%H-}AnExpression: String); virtual; // implemented in TGDBMIBreakpoint
|
|
end;
|
|
TDBGBreakPointClass = class of TDBGBreakPoint;
|
|
|
|
{ TIdeBreakPointBase }
|
|
|
|
TIdeBreakPointBase = class(TBaseBreakPoint)
|
|
private
|
|
FMaster: TDBGBreakPoint;
|
|
procedure SetMaster(AValue: TDBGBreakPoint);
|
|
protected
|
|
procedure DoEndUpdate; override;
|
|
procedure ReleaseMaster;
|
|
property Master: TDBGBreakPoint read FMaster write SetMaster;
|
|
// TODO: move TBaseBreakPoint properties from IDE te IDEBase
|
|
public
|
|
destructor Destroy; override;
|
|
procedure BeginUpdate; override;
|
|
end;
|
|
|
|
{ TBaseBreakPoints }
|
|
|
|
TBaseBreakPoints = class(TCollection)
|
|
private
|
|
protected
|
|
public
|
|
constructor Create(const ABreakPointClass: TBaseBreakPointClass);
|
|
destructor Destroy; override;
|
|
procedure Clear; reintroduce;
|
|
function Add(const ASource: String; const ALine: Integer; AnUpdating: Boolean = False): TBaseBreakPoint; overload;
|
|
function Add(const AAddress: TDBGPtr; AnUpdating: Boolean = False): TBaseBreakPoint; overload;
|
|
function Add(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind; AnUpdating: Boolean = False): TBaseBreakPoint; overload;
|
|
function Find(const ASource: String; const ALine: Integer): TBaseBreakPoint; overload;
|
|
function Find(const ASource: String; const ALine: Integer; const AIgnore: TBaseBreakPoint): TBaseBreakPoint; overload;
|
|
function Find(const AAddress: TDBGPtr): TBaseBreakPoint; overload;
|
|
function Find(const AAddress: TDBGPtr; const AIgnore: TBaseBreakPoint): TBaseBreakPoint; overload;
|
|
function Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind): TBaseBreakPoint; overload;
|
|
function Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind; const AIgnore: TBaseBreakPoint): TBaseBreakPoint; overload;
|
|
// no items property needed, it is "overridden" anyhow
|
|
end;
|
|
|
|
{ TDBGBreakPoints }
|
|
|
|
TDBGBreakPoints = class(TBaseBreakPoints)
|
|
private
|
|
FDebugger: TDebuggerIntf; // reference to our debugger
|
|
function GetItem(const AnIndex: Integer): TDBGBreakPoint;
|
|
procedure SetItem(const AnIndex: Integer; const AValue: TDBGBreakPoint);
|
|
protected
|
|
procedure DoStateChange(const AOldState: TDBGState); virtual;
|
|
property Debugger: TDebuggerIntf read FDebugger write FDebugger;
|
|
public
|
|
constructor Create(const ADebugger: TDebuggerIntf;
|
|
const ABreakPointClass: TDBGBreakPointClass);
|
|
function Add(const ASource: String; const ALine: Integer; AnUpdating: Boolean = False): TDBGBreakPoint; overload; reintroduce;
|
|
function Add(const AAddress: TDBGPtr; AnUpdating: Boolean = False): TDBGBreakPoint; overload; reintroduce;
|
|
function Add(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind; AnUpdating: Boolean = False): TDBGBreakPoint; overload; reintroduce;
|
|
function Find(const ASource: String; const ALine: Integer): TDBGBreakPoint; overload;
|
|
function Find(const ASource: String; const ALine: Integer; const AIgnore: TDBGBreakPoint): TDBGBreakPoint; overload;
|
|
function Find(const AAddress: TDBGPtr): TDBGBreakPoint; overload;
|
|
function Find(const AAddress: TDBGPtr; const {%H-}AIgnore: TDBGBreakPoint): TDBGBreakPoint; overload;
|
|
function Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind): TDBGBreakPoint; overload;
|
|
function Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind; const AIgnore: TDBGBreakPoint): TDBGBreakPoint; overload;
|
|
|
|
property Items[const AnIndex: Integer]: TDBGBreakPoint read GetItem write SetItem; default;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Breakpoints ^^^^^ }
|
|
|
|
{$region Debug Info ***********************************************************}
|
|
(******************************************************************************)
|
|
(** **)
|
|
(** D E B U G I N F O R M A T I O N **)
|
|
(** **)
|
|
(** Note: This part of the interface may/will still change. **)
|
|
(** **)
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
|
|
TDBGSymbolAttribute = (saRefParam, // var, const, constref passed by reference
|
|
saInternalPointer, // PointerToObject
|
|
saArray, saDynArray
|
|
);
|
|
TDBGSymbolAttributes = set of TDBGSymbolAttribute;
|
|
TDBGFieldLocation = (flPrivate, flProtected, flPublic, flPublished);
|
|
TDBGFieldFlag = (ffVirtual,ffConstructor,ffDestructor);
|
|
TDBGFieldFlags = set of TDBGFieldFlag;
|
|
|
|
TDBGType = class;
|
|
|
|
TDBGValue = record
|
|
AsString: ansistring;
|
|
case integer of
|
|
0: (As8Bits: BYTE);
|
|
1: (As16Bits: WORD);
|
|
2: (As32Bits: DWORD);
|
|
3: (As64Bits: QWORD);
|
|
4: (AsSingle: Single);
|
|
5: (AsDouble: Double);
|
|
6: (AsPointer: Pointer);
|
|
end;
|
|
|
|
{ TDBGField }
|
|
|
|
TDBGField = class(TObject)
|
|
private
|
|
FRefCount: Integer;
|
|
protected
|
|
FName: String;
|
|
FFlags: TDBGFieldFlags;
|
|
FLocation: TDBGFieldLocation;
|
|
FDBGType: TDBGType;
|
|
FClassName: String;
|
|
procedure IncRefCount;
|
|
procedure DecRefCount;
|
|
property RefCount: Integer read FRefCount;
|
|
public
|
|
constructor Create(const AName: String; ADBGType: TDBGType;
|
|
ALocation: TDBGFieldLocation; AFlags: TDBGFieldFlags = [];
|
|
AClassName: String = '');
|
|
destructor Destroy; override;
|
|
property Name: String read FName write FName;
|
|
property DBGType: TDBGType read FDBGType;
|
|
property Location: TDBGFieldLocation read FLocation;
|
|
property Flags: TDBGFieldFlags read FFlags;
|
|
property ClassName: String read FClassName write FClassName; // the class in which the field was declared
|
|
end;
|
|
|
|
{ TDBGFields }
|
|
|
|
TDBGFields = class(TObject)
|
|
private
|
|
FList: TList;
|
|
function GetField(const AIndex: Integer): TDBGField;
|
|
function GetCount: Integer;
|
|
protected
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
property Count: Integer read GetCount;
|
|
property Items[const AIndex: Integer]: TDBGField read GetField; default;
|
|
procedure Add(const AField: TDBGField);
|
|
end;
|
|
|
|
TDBGTypes = class(TObject)
|
|
private
|
|
function GetType(const AIndex: Integer): TDBGType;
|
|
function GetCount: Integer;
|
|
protected
|
|
FList: TList;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
property Count: Integer read GetCount;
|
|
property Items[const AIndex: Integer]: TDBGType read GetType; default;
|
|
end;
|
|
|
|
{ TDBGType }
|
|
|
|
TDBGType = class(TDBGTypeBase)
|
|
private
|
|
function GetFields: TDBGFields;
|
|
protected
|
|
FAncestor: String;
|
|
FResult: TDBGType;
|
|
FResultString: String;
|
|
FArguments: TDBGTypes;
|
|
FAttributes: TDBGSymbolAttributes;
|
|
FFields: TDBGFields;
|
|
FKind: TDBGSymbolKind;
|
|
FMembers: TStrings;
|
|
FTypeName: String;
|
|
FTypeDeclaration: String;
|
|
FDBGValue: TDBGValue;
|
|
FBoundHigh: Integer;
|
|
FBoundLow: Integer;
|
|
FLen: Integer;
|
|
procedure Init; virtual;
|
|
public
|
|
Value: TDBGValue;
|
|
constructor Create(AKind: TDBGSymbolKind; const ATypeName: String);
|
|
constructor Create(AKind: TDBGSymbolKind; const AArguments: TDBGTypes; AResult: TDBGType = nil);
|
|
destructor Destroy; override;
|
|
property Ancestor: String read FAncestor write FAncestor;
|
|
property Arguments: TDBGTypes read FArguments;
|
|
property Fields: TDBGFields read GetFields;
|
|
property Kind: TDBGSymbolKind read FKind;
|
|
property Attributes: TDBGSymbolAttributes read FAttributes;
|
|
property TypeName: String read FTypeName; // Name/Alias as in type section. One pascal token, or empty
|
|
property TypeDeclaration: String read FTypeDeclaration; // Declaration (for array, set, enum, ..)
|
|
property Members: TStrings read FMembers; // Set & ENUM
|
|
property Len: Integer read FLen; // Array
|
|
property BoundLow: Integer read FBoundLow; // Array
|
|
property BoundHigh: Integer read FBoundHigh; // Array
|
|
property Result: TDBGType read FResult;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Debug Info ^^^^^ }
|
|
|
|
{%region Watches **************************************************************
|
|
******************************************************************************
|
|
** **
|
|
** W A T C H E S **
|
|
** **
|
|
******************************************************************************
|
|
******************************************************************************}
|
|
|
|
{ TWatchesSupplier }
|
|
|
|
TWatchesSupplier = class(specialize TWatchesSupplierClassTemplate<TDebuggerDataSupplierBase>, TWatchesSupplierIntf)
|
|
protected
|
|
procedure DoStateChange(const AOldState: TDBGState); override;
|
|
procedure InternalRequestData(AWatchValue: TWatchValueIntf); virtual;
|
|
public
|
|
constructor Create(const ADebugger: TDebuggerIntf);
|
|
destructor Destroy; override;
|
|
procedure RequestData(AWatchValue: TWatchValueIntf); reintroduce;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Watches ^^^^^ }
|
|
|
|
{%region Locals ***************************************************************
|
|
******************************************************************************
|
|
** **
|
|
** L O C A L S **
|
|
** **
|
|
******************************************************************************
|
|
******************************************************************************}
|
|
|
|
// TODO: a more watch-like value object
|
|
TLocalsMonitor = class;
|
|
|
|
{ TLocalsValue }
|
|
|
|
TLocalsValue = class(TDbgEntityValue)
|
|
private
|
|
FName: String;
|
|
FValue: String;
|
|
protected
|
|
procedure DoAssign(AnOther: TDbgEntityValue); override;
|
|
public
|
|
property Name: String read FName;
|
|
property Value: String read FValue;
|
|
end;
|
|
|
|
{ TLocals }
|
|
|
|
TLocals = class(TDbgEntityValuesList)
|
|
private
|
|
function GetEntry(AnIndex: Integer): TLocalsValue;
|
|
function GetName(const AnIndex: Integer): String;
|
|
function GetValue(const AnIndex: Integer): String;
|
|
protected
|
|
function CreateEntry: TDbgEntityValue; override;
|
|
public
|
|
procedure Add(const AName, AValue: String);
|
|
procedure SetDataValidity({%H-}AValidity: TDebuggerDataState); virtual;
|
|
public
|
|
function Count: Integer;reintroduce; virtual;
|
|
property Entries[AnIndex: Integer]: TLocalsValue read GetEntry;
|
|
property Names[const AnIndex: Integer]: String read GetName;
|
|
property Values[const AnIndex: Integer]: String read GetValue;
|
|
end;
|
|
|
|
{ TLocalsList }
|
|
|
|
TLocalsList = class(TDbgEntitiesThreadStackList)
|
|
private
|
|
function GetEntry(AThreadId, AStackFrame: Integer): TLocals;
|
|
function GetEntryByIdx(AnIndex: Integer): TLocals;
|
|
protected
|
|
//function CreateEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList; override;
|
|
public
|
|
property EntriesByIdx[AnIndex: Integer]: TLocals read GetEntryByIdx;
|
|
property Entries[AThreadId, AStackFrame: Integer]: TLocals read GetEntry; default;
|
|
end;
|
|
|
|
{ TLocalsSupplier }
|
|
|
|
TLocalsSupplier = class(TDebuggerDataSupplier)
|
|
private
|
|
function GetMonitor: TLocalsMonitor;
|
|
procedure SetMonitor(AValue: TLocalsMonitor);
|
|
property Monitor: TLocalsMonitor read GetMonitor write SetMonitor;
|
|
public
|
|
procedure TriggerInvalidateLocals;
|
|
procedure RequestData(ALocals: TLocals); virtual;
|
|
end;
|
|
|
|
{ TLocalsMonitor }
|
|
|
|
TLocalsMonitor = class(TDebuggerDataMonitor)
|
|
private
|
|
function GetSupplier: TLocalsSupplier;
|
|
procedure SetSupplier(AValue: TLocalsSupplier);
|
|
protected
|
|
procedure InvalidateLocals; virtual;
|
|
public
|
|
property Supplier: TLocalsSupplier read GetSupplier write SetSupplier;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Locals ^^^^^ }
|
|
|
|
{%region Line Info ************************************************************
|
|
******************************************************************************
|
|
** **
|
|
** L I N E I N F O **
|
|
** **
|
|
******************************************************************************
|
|
******************************************************************************}
|
|
|
|
TIDELineInfoEvent = procedure(const ASender: TObject; const ASource: String) of object;
|
|
|
|
{ TBaseLineInfo }
|
|
|
|
TBaseLineInfo = class(TObject)
|
|
protected
|
|
function GetSource(const {%H-}AnIndex: integer): String; virtual;
|
|
public
|
|
constructor Create;
|
|
function Count: Integer; virtual;
|
|
function HasAddress(const AIndex: Integer; const ALine: Integer): Boolean; virtual;
|
|
function HasAddress(const ASource: String; const ALine: Integer): Boolean;
|
|
function GetInfo({%H-}AAddress: TDbgPtr; out {%H-}ASource, {%H-}ALine, {%H-}AOffset: Integer): Boolean; virtual;
|
|
function IndexOf(const {%H-}ASource: String): integer; virtual;
|
|
procedure Request(const {%H-}ASource: String); virtual;
|
|
procedure Cancel(const {%H-}ASource: String); virtual;
|
|
public
|
|
property Sources[const AnIndex: Integer]: String read GetSource;
|
|
end;
|
|
|
|
{ TDBGLineInfo }
|
|
|
|
TDBGLineInfo = class(TBaseLineInfo)
|
|
private
|
|
FDebugger: TDebuggerIntf; // reference to our debugger
|
|
FOnChange: TIDELineInfoEvent;
|
|
protected
|
|
procedure Changed(ASource: String); virtual;
|
|
procedure DoChange(ASource: String);
|
|
procedure DoStateChange(const {%H-}AOldState: TDBGState); virtual;
|
|
property Debugger: TDebuggerIntf read FDebugger write FDebugger;
|
|
public
|
|
constructor Create(const ADebugger: TDebuggerIntf);
|
|
property OnChange: TIDELineInfoEvent read FOnChange write FOnChange;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Line Info ^^^^^ }
|
|
|
|
{%region Register *************************************************************
|
|
******************************************************************************
|
|
** **
|
|
** R E G I S T E R S **
|
|
** **
|
|
******************************************************************************
|
|
******************************************************************************}
|
|
|
|
TRegisterDisplayFormat = (rdDefault, rdHex, rdBinary, rdOctal, rdDecimal, rdRaw);
|
|
TRegisterDisplayFormats = set of TRegisterDisplayFormat;
|
|
TRegistersMonitor = class;
|
|
|
|
{ TRegisterDisplayValue }
|
|
|
|
TRegisterDisplayValue = class // Only created if ddsValid
|
|
private
|
|
FStringValue: String; // default, rdRaw is always in FStringValue
|
|
FNumValue: QWord;
|
|
FSize: Integer; // 2, 4 or 8 bytes
|
|
FFlags: set of (rdvHasNum); // Calculate numeric values.
|
|
FSupportedDispFormats: TRegisterDisplayFormats;
|
|
function GetValue(ADispFormat: TRegisterDisplayFormat): String;
|
|
public
|
|
procedure Assign(AnOther: TRegisterDisplayValue);
|
|
procedure SetAsNum(AValue: QWord; ASize: Integer);
|
|
procedure SetAsText(AValue: String);
|
|
procedure AddFormats(AFormats: TRegisterDisplayFormats);
|
|
property SupportedDispFormats: TRegisterDisplayFormats read FSupportedDispFormats;
|
|
property Value[ADispFormat: TRegisterDisplayFormat]: String read GetValue;
|
|
end;
|
|
|
|
{ TRegisterValue }
|
|
|
|
TRegisterValue = class(TDbgEntityValue)
|
|
private
|
|
FDataValidity: TDebuggerDataState;
|
|
FDisplayFormat: TRegisterDisplayFormat;
|
|
FModified: Boolean;
|
|
FName: String;
|
|
FValues: Array of TRegisterDisplayValue;
|
|
function GetHasValue: Boolean;
|
|
function GetHasValueFormat(ADispFormat: TRegisterDisplayFormat): Boolean;
|
|
function GetValue: String;
|
|
function GetValueObj: TRegisterDisplayValue;
|
|
function GetValueObjFormat(ADispFormat: TRegisterDisplayFormat): TRegisterDisplayValue;
|
|
procedure SetDisplayFormat(AValue: TRegisterDisplayFormat);
|
|
procedure SetValue(AValue: String);
|
|
function GetValueObject(ACreateNew: Boolean = False): TRegisterDisplayValue;
|
|
function GetValueObject(ADispFormat: TRegisterDisplayFormat; ACreateNew: Boolean = False): TRegisterDisplayValue;
|
|
procedure SetDataValidity(AValidity: TDebuggerDataState);
|
|
procedure ClearDispValues;
|
|
protected
|
|
procedure DoAssign(AnOther: TDbgEntityValue); override;
|
|
procedure DoDataValidityChanged({%H-}AnOldValidity: TDebuggerDataState); virtual;
|
|
procedure DoDisplayFormatChanged({%H-}AnOldFormat: TRegisterDisplayFormat); virtual;
|
|
procedure DoValueNotEvaluated; virtual;
|
|
public
|
|
destructor Destroy; override;
|
|
property Name: String read FName;
|
|
property Value: String read GetValue write SetValue;
|
|
// TODO: DisplayFormat => does each thread/stackframe need to store a separet setting // InvalidateItems (soleved issue 62591) therefore must keep all of them
|
|
property DisplayFormat: TRegisterDisplayFormat read FDisplayFormat write SetDisplayFormat;
|
|
property Modified: Boolean read FModified write FModified;
|
|
property DataValidity: TDebuggerDataState read FDataValidity write SetDataValidity;
|
|
property ValueObj: TRegisterDisplayValue read GetValueObj; // Will create the object for current DispFormat. Only use for setting data.
|
|
property HasValue: Boolean read GetHasValue;
|
|
property ValueObjFormat[ADispFormat: TRegisterDisplayFormat]: TRegisterDisplayValue read GetValueObjFormat; // Will create the object for current DispFormat. Only use for setting data.
|
|
property HasValueFormat[ADispFormat: TRegisterDisplayFormat]: Boolean read GetHasValueFormat;
|
|
end;
|
|
|
|
{ TRegisters }
|
|
|
|
TRegisters = class(TDbgEntityValuesList)
|
|
private
|
|
FDataValidity: TDebuggerDataState;
|
|
function GetEntry(AnIndex: Integer): TRegisterValue;
|
|
function GetEntryByName(const AName: String): TRegisterValue;
|
|
// TODO: setting to requested will not affect TRegisterValue
|
|
// TODO: since TRegisterValue are kept, any registers that are no longer avail, must be removed at some point (i.e. when this is set valid, and some TRegisterValue are still unknown/requested)
|
|
procedure SetDataValidity(AValue: TDebuggerDataState);
|
|
protected
|
|
function CreateEntry: TDbgEntityValue; override;
|
|
procedure DoDataValidityChanged({%H-}AnOldValidity: TDebuggerDataState); virtual;
|
|
public
|
|
procedure InvalidateItems;
|
|
function Count: Integer; reintroduce; virtual;
|
|
property Entries[AnIndex: Integer]: TRegisterValue read GetEntry; default;
|
|
property EntriesByName[const AName: String]: TRegisterValue read GetEntryByName; // autocreate
|
|
property DataValidity: TDebuggerDataState read FDataValidity write SetDataValidity;
|
|
end;
|
|
|
|
{ TRegistersList }
|
|
|
|
TRegistersList = class(TDbgEntitiesThreadStackList)
|
|
private
|
|
function GetEntry(AThreadId, AStackFrame: Integer): TRegisters;
|
|
function GetEntryByIdx(AnIndex: Integer): TRegisters;
|
|
protected
|
|
public
|
|
procedure InvalidateItems;
|
|
property EntriesByIdx[AnIndex: Integer]: TRegisters read GetEntryByIdx;
|
|
property Entries[AThreadId, AStackFrame: Integer]: TRegisters read GetEntry; default;
|
|
end;
|
|
|
|
{ TRegisterSupplier }
|
|
|
|
TRegisterSupplier = class(TDebuggerDataSupplier)
|
|
private
|
|
function GetCurrentRegistersList: TRegistersList;
|
|
function GetMonitor: TRegistersMonitor;
|
|
procedure SetMonitor(AValue: TRegistersMonitor);
|
|
protected
|
|
public
|
|
procedure RequestData(ARegisters: TRegisters); virtual;
|
|
property CurrentRegistersList: TRegistersList read GetCurrentRegistersList;
|
|
property Monitor: TRegistersMonitor read GetMonitor write SetMonitor;
|
|
end;
|
|
|
|
{ TRegistersMonitor }
|
|
|
|
TRegistersMonitor = class(TDebuggerDataMonitor)
|
|
private
|
|
FRegistersList: TRegistersList;
|
|
function GetSupplier: TRegisterSupplier;
|
|
procedure SetSupplier(AValue: TRegisterSupplier);
|
|
protected
|
|
function CreateRegistersList: TRegistersList; virtual;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
property RegistersList: TRegistersList read FRegistersList;
|
|
property Supplier: TRegisterSupplier read GetSupplier write SetSupplier;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Register ^^^^^ }
|
|
|
|
{%region Callstack ************************************************************
|
|
******************************************************************************
|
|
** **
|
|
** C A L L S T A C K **
|
|
** **
|
|
******************************************************************************
|
|
******************************************************************************
|
|
* The entries for the callstack are created on demand. This way when the *
|
|
* first entry is needed, it isn't required to create the whole stack *
|
|
* *
|
|
* TCallStackEntry needs to stay a readonly object so its data can be shared *
|
|
******************************************************************************}
|
|
|
|
TCallStackMonitor = class;
|
|
|
|
{ TCallStackEntryBase }
|
|
|
|
TCallStackEntry = class(TFreeNotifyingObject)
|
|
private
|
|
FValidity: TDebuggerDataState;
|
|
FIndex: Integer;
|
|
FAddress: TDbgPtr;
|
|
FFunctionName: String;
|
|
FLine: Integer;
|
|
FArguments: TStrings;
|
|
protected
|
|
//// for use in TThreadEntry ONLY
|
|
//function GetThreadId: Integer; virtual; abstract;
|
|
//function GetThreadName: String; virtual; abstract;
|
|
//function GetThreadState: String; virtual; abstract;
|
|
//procedure SetThreadState(AValue: String); virtual; abstract;
|
|
function GetArgumentCount: Integer;
|
|
function GetArgumentName(const AnIndex: Integer): String;
|
|
function GetArgumentValue(const AnIndex: Integer): String;
|
|
protected
|
|
property Arguments: TStrings read FArguments;
|
|
function GetFunctionName: String; virtual;
|
|
function GetSource: String; virtual;
|
|
function GetValidity: TDebuggerDataState; virtual;
|
|
procedure SetValidity(AValue: TDebuggerDataState); virtual;
|
|
procedure InitFields(const AIndex:Integer; const AnAddress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String;
|
|
const ALine: Integer; AValidity: TDebuggerDataState);
|
|
public
|
|
constructor Create;
|
|
function CreateCopy: TCallStackEntry; virtual;
|
|
destructor Destroy; override;
|
|
procedure Assign(AnOther: TCallStackEntry); virtual;
|
|
procedure Init(const AnAddress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String;
|
|
const {%H-}AUnitName, {%H-}AClassName, {%H-}AProcName, {%H-}AFunctionArgs: String;
|
|
const ALine: Integer; AState: TDebuggerDataState = ddsValid); virtual;
|
|
procedure Init(const AnAddress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String;
|
|
const {%H-}FileName, {%H-}FullName: String;
|
|
const ALine: Integer; AState: TDebuggerDataState = ddsValid); virtual;
|
|
procedure ClearLocation; virtual; // TODO need a way to call Changed on TCallStack or TThreads // corrently done in SetThreadState
|
|
function GetFunctionWithArg: String;
|
|
//function IsCurrent: Boolean;
|
|
//procedure MakeCurrent;
|
|
property Address: TDbgPtr read FAddress;
|
|
property ArgumentCount: Integer read GetArgumentCount;
|
|
property ArgumentNames[const AnIndex: Integer]: String read GetArgumentName;
|
|
property ArgumentValues[const AnIndex: Integer]: String read GetArgumentValue;
|
|
property FunctionName: String read FFunctionName;
|
|
property Index: Integer read FIndex;
|
|
property Line: Integer read FLine;
|
|
property Source: String read GetSource;
|
|
property Validity: TDebuggerDataState read GetValidity write SetValidity;
|
|
public
|
|
//// for use in TThreadEntry ONLY
|
|
//property ThreadId: Integer read GetThreadId;
|
|
//property ThreadName: String read GetThreadName;
|
|
//property ThreadState: String read GetThreadState write SetThreadState;
|
|
end;
|
|
|
|
{ TCallStackBase }
|
|
|
|
TCallStackBase = class(TFreeNotifyingObject)
|
|
protected
|
|
FCurrent: Integer;
|
|
FThreadId: Integer;
|
|
function GetNewCurrentIndex: Integer; virtual;
|
|
function GetEntryBase(AIndex: Integer): TCallStackEntry; virtual; abstract;
|
|
function GetCount: Integer; virtual;
|
|
procedure SetCount(AValue: Integer); virtual; abstract;
|
|
function GetCurrent: Integer; virtual;
|
|
procedure SetCurrent(AValue: Integer); virtual;
|
|
function GetHighestUnknown: Integer; virtual;
|
|
function GetLowestUnknown: Integer; virtual;
|
|
function GetRawEntries: TMap; virtual; abstract;
|
|
public
|
|
constructor Create;
|
|
function CreateCopy: TCallStackBase; virtual;
|
|
procedure Assign(AnOther: TCallStackBase); virtual;
|
|
|
|
procedure PrepareRange({%H-}AIndex, {%H-}ACount: Integer); virtual; abstract;
|
|
procedure DoEntriesCreated; virtual; abstract;
|
|
procedure DoEntriesUpdated; virtual; abstract;
|
|
procedure SetCountValidity({%H-}AValidity: TDebuggerDataState); virtual;
|
|
procedure SetHasAtLeastCountInfo({%H-}AValidity: TDebuggerDataState; {%H-}AMinCount: Integer = -1); virtual;
|
|
procedure SetCurrentValidity({%H-}AValidity: TDebuggerDataState); virtual;
|
|
function CountLimited(ALimit: Integer): Integer; virtual; abstract;
|
|
property Count: Integer read GetCount write SetCount;
|
|
property CurrentIndex: Integer read GetCurrent write SetCurrent;
|
|
property Entries[AIndex: Integer]: TCallStackEntry read GetEntryBase;
|
|
property ThreadId: Integer read FThreadId write FThreadId;
|
|
property NewCurrentIndex: Integer read GetNewCurrentIndex;
|
|
|
|
property RawEntries: TMap read GetRawEntries;
|
|
property LowestUnknown: Integer read GetLowestUnknown;
|
|
property HighestUnknown: Integer read GetHighestUnknown;
|
|
end;
|
|
|
|
{ TCallStackListBase }
|
|
|
|
TCallStackList = class
|
|
private
|
|
FList: TList;
|
|
function GetEntry(const AIndex: Integer): TCallStackBase;
|
|
function GetEntryForThread(const AThreadId: Integer): TCallStackBase;
|
|
protected
|
|
function NewEntryForThread(const {%H-}AThreadId: Integer): TCallStackBase; virtual;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Assign(AnOther: TCallStackList); virtual;
|
|
procedure Add(ACallStack: TCallStackBase);
|
|
procedure Clear; virtual;
|
|
function Count: Integer; virtual; // Count of already requested CallStacks (via ThreadId)
|
|
property Entries[const AIndex: Integer]: TCallStackBase read GetEntry; default;
|
|
property EntriesForThreads[const AThreadId: Integer]: TCallStackBase read GetEntryForThread;
|
|
end;
|
|
|
|
{ TCallStackSupplier }
|
|
|
|
TCallStackSupplier = class(TDebuggerDataSupplier)
|
|
private
|
|
function GetCurrentCallStackList: TCallStackList;
|
|
function GetMonitor: TCallStackMonitor;
|
|
procedure SetMonitor(AValue: TCallStackMonitor);
|
|
property Monitor: TCallStackMonitor read GetMonitor write SetMonitor;
|
|
protected
|
|
//procedure CurrentChanged;
|
|
procedure Changed;
|
|
public
|
|
procedure RequestCount(ACallstack: TCallStackBase); virtual;
|
|
procedure RequestAtLeastCount(ACallstack: TCallStackBase; {%H-}ARequiredMinCount: Integer); virtual;
|
|
procedure RequestCurrent(ACallstack: TCallStackBase); virtual;
|
|
procedure RequestEntries(ACallstack: TCallStackBase); virtual;
|
|
procedure UpdateCurrentIndex; virtual;
|
|
property CurrentCallStackList: TCallStackList read GetCurrentCallStackList;
|
|
end;
|
|
|
|
{ TCallStackMonitor }
|
|
|
|
TCallStackMonitor = class(TDebuggerDataMonitor)
|
|
private
|
|
FCallStackList: TCallStackList;
|
|
function GetSupplier: TCallStackSupplier;
|
|
procedure SetSupplier(AValue: TCallStackSupplier);
|
|
protected
|
|
function CreateCallStackList: TCallStackList; virtual;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
property CallStackList: TCallStackList read FCallStackList;
|
|
property Supplier: TCallStackSupplier read GetSupplier write SetSupplier;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Callstack ^^^^^ }
|
|
|
|
{%region ***** Disassembler ***** }
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
(** **)
|
|
(** D I S A S S E M B L E R **)
|
|
(** **)
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
|
|
(* Some values to calculate how many bytes to disassemble for a given amount of lines
|
|
Those values are only guesses *)
|
|
const
|
|
// DAssBytesPerCommandAvg: Average len: Used for LinesBefore/LinesAfter.
|
|
// (should rather be to big than to small)
|
|
DAssBytesPerCommandAvg = 8;
|
|
// If we have a range with more then DAssRangeOverFuncTreshold * DAssBytesPerCommandAvg
|
|
// then prefer the Range-end as start, rather than the known func start
|
|
// (otherwhise re-dissassemble the whole function, including the part already known)
|
|
// The assumption is, that no single *source* statement starting before this range,
|
|
// will ever reach into the next statement (where the next statement already started / mixed addresses)
|
|
DAssRangeOverFuncTreshold = 15;
|
|
// Never dis-assemble more bytes in a single go (actually, max-offset before requested addr)
|
|
DAssMaxRangeSize = 4096;
|
|
|
|
type
|
|
PDisassemblerEntry = ^TDisassemblerEntry;
|
|
TDisassemblerEntry = record
|
|
Addr: TDbgPtr; // Address
|
|
Dump: String; // Raw Data
|
|
Statement: String; // Asm
|
|
FuncName: String; // Function, if avail
|
|
Offset: Integer; // Byte-Offest in Fonction
|
|
SrcFileName: String; // SrcFile if avail
|
|
SrcFileLine: Integer; // Line in SrcFile
|
|
SrcStatementIndex: SmallInt; // Index of Statement, within list of Stmnt of the same SrcLine
|
|
SrcStatementCount: SmallInt; // Count of Statements for this SrcLine
|
|
end;
|
|
|
|
TDisassemblerAddressValidity =
|
|
(avFoundFunction, avFoundRange, avFoundStatement, // known address
|
|
avGuessed, // guessed
|
|
avExternRequest, // As requested by external caller
|
|
avPadded // Padded, because address was not known for sure
|
|
);
|
|
TDisassemblerAddress = record
|
|
Value, GuessedValue: TDBGPtr;
|
|
Offset: Integer;
|
|
Validity: TDisassemblerAddressValidity;
|
|
end;
|
|
|
|
|
|
{ TBaseDisassembler }
|
|
|
|
TBaseDisassembler = class(TObject)
|
|
private
|
|
FBaseAddr: TDbgPtr;
|
|
FCountAfter: Integer;
|
|
FCountBefore: Integer;
|
|
FChangedLockCount: Integer;
|
|
FIsChanged: Boolean;
|
|
function GetEntryPtr(AIndex: Integer): PDisassemblerEntry;
|
|
procedure IndexError(AIndex: Integer);
|
|
function GetEntry(AIndex: Integer): TDisassemblerEntry;
|
|
protected
|
|
function InternalGetEntry({%H-}AIndex: Integer): TDisassemblerEntry; virtual;
|
|
function InternalGetEntryPtr({%H-}AIndex: Integer): PDisassemblerEntry; virtual;
|
|
procedure DoChanged; virtual;
|
|
procedure Changed;
|
|
procedure LockChanged;
|
|
procedure UnlockChanged;
|
|
procedure InternalIncreaseCountBefore(ACount: Integer);
|
|
procedure InternalIncreaseCountAfter(ACount: Integer);
|
|
procedure SetCountBefore(ACount: Integer);
|
|
procedure SetCountAfter(ACount: Integer);
|
|
procedure SetBaseAddr(AnAddr: TDbgPtr);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear; virtual;
|
|
// Returns "True", if the range is valid, if not a ChangeNotification will be triggered later
|
|
function PrepareRange({%H-}AnAddr: TDbgPtr; {%H-}ALinesBefore, {%H-}ALinesAfter: Integer): Boolean; virtual;
|
|
property BaseAddr: TDbgPtr read FBaseAddr;
|
|
property CountAfter: Integer read FCountAfter; // Includes the line at BaseAddr, as set by PrepareRange(AnAddr, ...)
|
|
property CountBefore: Integer read FCountBefore;
|
|
property Entries[AIndex: Integer]: TDisassemblerEntry read GetEntry;
|
|
property EntriesPtr[Index: Integer]: PDisassemblerEntry read GetEntryPtr;
|
|
end;
|
|
|
|
{ TDBGDisassemblerEntryRange }
|
|
|
|
TDBGDisassemblerEntryRange = class
|
|
private
|
|
FCount: Integer;
|
|
FEntries: array of TDisassemblerEntry;
|
|
FLastEntryEndAddr: TDBGPtr;
|
|
FRangeEndAddr: TDBGPtr;
|
|
FRangeStartAddr: TDBGPtr;
|
|
function GetCapacity: Integer;
|
|
function GetEntry(Index: Integer): TDisassemblerEntry;
|
|
function GetEntryPtr(Index: Integer): PDisassemblerEntry;
|
|
procedure SetCapacity(const AValue: Integer);
|
|
procedure SetCount(const AValue: Integer);
|
|
public
|
|
procedure Clear;
|
|
function Append(const AnEntryPtr: PDisassemblerEntry): Integer;
|
|
procedure Merge(const AnotherRange: TDBGDisassemblerEntryRange);
|
|
// Actual addresses on the ranges
|
|
function FirstAddr: TDbgPtr;
|
|
function LastAddr: TDbgPtr;
|
|
function ContainsAddr(const AnAddr: TDbgPtr; IncludeNextAddr: Boolean = False): Boolean;
|
|
function IndexOfAddr(const AnAddr: TDbgPtr): Integer;
|
|
function IndexOfAddrWithOffs(const AnAddr: TDbgPtr): Integer;
|
|
function IndexOfAddrWithOffs(const AnAddr: TDbgPtr; out AOffs: Integer): Integer;
|
|
property Count: Integer read FCount write SetCount;
|
|
property Capacity: Integer read GetCapacity write SetCapacity;
|
|
property Entries[Index: Integer]: TDisassemblerEntry read GetEntry;
|
|
property EntriesPtr[Index: Integer]: PDisassemblerEntry read GetEntryPtr;
|
|
// The first address behind last entry
|
|
property LastEntryEndAddr: TDBGPtr read FLastEntryEndAddr write FLastEntryEndAddr;
|
|
// The addresses for which the range was requested
|
|
// The range may bo more, than the entries, if there a gaps that cannot be retrieved.
|
|
property RangeStartAddr: TDBGPtr read FRangeStartAddr write FRangeStartAddr;
|
|
property RangeEndAddr: TDBGPtr read FRangeEndAddr write FRangeEndAddr;
|
|
end;
|
|
|
|
{ TDBGDisassemblerEntryMap }
|
|
|
|
TDBGDisassemblerEntryMapMergeEvent
|
|
= procedure(MergeReceiver, MergeGiver: TDBGDisassemblerEntryRange) of object;
|
|
|
|
{ TDBGDisassemblerEntryMapIterator }
|
|
TDBGDisassemblerEntryMap = class;
|
|
|
|
TDBGDisassemblerEntryMapIterator = class(TMapIterator)
|
|
public
|
|
function GetRangeForAddr(AnAddr: TDbgPtr; IncludeNextAddr: Boolean = False): TDBGDisassemblerEntryRange;
|
|
function NextRange: TDBGDisassemblerEntryRange;
|
|
function PreviousRange: TDBGDisassemblerEntryRange;
|
|
end;
|
|
|
|
TDBGDisassemblerEntryMap = class(TMap)
|
|
private
|
|
FIterator: TDBGDisassemblerEntryMapIterator;
|
|
FOnDelete: TNotifyEvent;
|
|
FOnMerge: TDBGDisassemblerEntryMapMergeEvent;
|
|
FFreeItemLock: Boolean;
|
|
protected
|
|
procedure ReleaseData(ADataPtr: Pointer); override;
|
|
public
|
|
constructor Create(AIdType: TMapIdType; ADataSize: Cardinal);
|
|
destructor Destroy; override;
|
|
// AddRange, may destroy the object
|
|
procedure AddRange(const ARange: TDBGDisassemblerEntryRange); // Arange may be freed
|
|
function GetRangeForAddr(AnAddr: TDbgPtr; IncludeNextAddr: Boolean = False): TDBGDisassemblerEntryRange;
|
|
property OnDelete: TNotifyEvent read FOnDelete write FOnDelete;
|
|
property OnMerge: TDBGDisassemblerEntryMapMergeEvent
|
|
read FOnMerge write FOnMerge;
|
|
end;
|
|
|
|
{ TDBGDisassemblerRangeExtender }
|
|
|
|
TDoDisassembleRangeProc = function(AnEntryRanges: TDBGDisassemblerEntryMap; AFirstAddr, ALastAddr: TDisassemblerAddress; StopAfterAddress: TDBGPtr; StopAfterNumLines: Integer): Boolean {$ifdef disassemblernestedproc} is nested {$else} of object{$endif};
|
|
TDisassembleCancelProc = function(): Boolean {$ifdef disassemblernestedproc} is nested {$else} of object {$endif};
|
|
TDisassembleAdjustToKnowFunctionStart = function (var AStartAddr: TDisassemblerAddress): Boolean {$ifdef disassemblernestedproc} is nested {$else} of object {$endif};
|
|
|
|
TDBGDisassemblerRangeExtender = class
|
|
private
|
|
FOnAdjustToKnowFunctionStart: TDisassembleAdjustToKnowFunctionStart;
|
|
FOnCheckCancel: TDisassembleCancelProc;
|
|
FOnDoDisassembleRange: TDoDisassembleRangeProc;
|
|
|
|
FEntryRangeMap: TDBGDisassemblerEntryMap;
|
|
FRangeIterator: TDBGDisassemblerEntryMapIterator;
|
|
function CheckIfCancelled: boolean;
|
|
function AdjustToRangeOrKnowFunctionStart(var AStartAddr: TDisassemblerAddress;
|
|
ARangeBefore: TDBGDisassemblerEntryRange): Boolean;
|
|
function InitAddress(AValue: TDBGPtr; AValidity: TDisassemblerAddressValidity;
|
|
AnOffset: Integer = -1): TDisassemblerAddress;
|
|
public
|
|
constructor Create(AnEntryRangeMap: TDBGDisassemblerEntryMap);
|
|
destructor Destroy; override;
|
|
function DisassembleRange(ALinesBefore,
|
|
ALinesAfter: integer; AStartAddr: TDBGPtr; AnEndAddr: TDBGPtr): boolean;
|
|
property OnDoDisassembleRange: TDoDisassembleRangeProc read FOnDoDisassembleRange write FOnDoDisassembleRange;
|
|
property OnCheckCancel: TDisassembleCancelProc read FOnCheckCancel write FOnCheckCancel;
|
|
property OnAdjustToKnowFunctionStart: TDisassembleAdjustToKnowFunctionStart read FOnAdjustToKnowFunctionStart write FOnAdjustToKnowFunctionStart;
|
|
end;
|
|
|
|
{ TDBGDisassembler }
|
|
|
|
TDBGDisassembler = class(TBaseDisassembler)
|
|
private
|
|
FDebugger: TDebuggerIntf;
|
|
FOnChange: TNotifyEvent;
|
|
|
|
FEntryRanges: TDBGDisassemblerEntryMap;
|
|
FCurrentRange: TDBGDisassemblerEntryRange;
|
|
FPreparingBefore, FPreparingAfter: Integer;
|
|
FPreparingAddr: TDbgPtr;
|
|
procedure EntryRangesOnDelete(Sender: TObject);
|
|
procedure EntryRangesOnMerge(MergeReceiver, MergeGiver: TDBGDisassemblerEntryRange);
|
|
function FindRange(AnAddr: TDbgPtr; ALinesBefore, ALinesAfter: Integer): Boolean;
|
|
protected
|
|
procedure DoChanged; override;
|
|
procedure DoStateChange(const AOldState: TDBGState); virtual;
|
|
function InternalGetEntry(AIndex: Integer): TDisassemblerEntry; override;
|
|
function InternalGetEntryPtr(AIndex: Integer): PDisassemblerEntry; override;
|
|
// PrepareEntries returns True, if it already added some entries
|
|
function PrepareEntries({%H-}AnAddr: TDbgPtr; {%H-}ALinesBefore, {%H-}ALinesAfter: Integer): boolean; virtual;
|
|
function HandleRangeWithInvalidAddr(ARange: TDBGDisassemblerEntryRange;{%H-}AnAddr:
|
|
TDbgPtr; var {%H-}ALinesBefore, {%H-}ALinesAfter: Integer): boolean; virtual;
|
|
property Debugger: TDebuggerIntf read FDebugger write FDebugger;
|
|
property EntryRanges: TDBGDisassemblerEntryMap read FEntryRanges;
|
|
public
|
|
constructor Create(const ADebugger: TDebuggerIntf);
|
|
destructor Destroy; override;
|
|
procedure Clear; override;
|
|
function PrepareRange(AnAddr: TDbgPtr; ALinesBefore, ALinesAfter: Integer): Boolean; override;
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Disassembler ^^^^^ }
|
|
|
|
{%region Threads **************************************************************
|
|
******************************************************************************
|
|
** **
|
|
** T H R E A D S **
|
|
** **
|
|
******************************************************************************
|
|
******************************************************************************}
|
|
|
|
TThreadsMonitor = class;
|
|
|
|
{ TThreadEntry }
|
|
|
|
TThreadEntry = class(TObject)
|
|
private
|
|
FTopFrame: TCallStackEntry;
|
|
protected
|
|
FThreadId: Integer;
|
|
FThreadName: String;
|
|
FThreadState: String;
|
|
procedure SetThreadState(AValue: String); virtual;
|
|
function CreateStackEntry: TCallStackEntry; virtual;
|
|
public
|
|
constructor Create;
|
|
constructor Create(const AnAdress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String;
|
|
const FileName, FullName: String;
|
|
const ALine: Integer;
|
|
const AThreadId: Integer; const AThreadName: String;
|
|
const AThreadState: String;
|
|
AState: TDebuggerDataState = ddsValid);
|
|
procedure Init(const AnAdress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String;
|
|
const FileName, FullName: String;
|
|
const ALine: Integer;
|
|
const AThreadId: Integer; const AThreadName: String;
|
|
const AThreadState: String;
|
|
AState: TDebuggerDataState = ddsValid);
|
|
function CreateCopy: TThreadEntry; virtual;
|
|
destructor Destroy; override;
|
|
procedure Assign(AnOther: TThreadEntry); virtual;
|
|
published
|
|
property ThreadId: Integer read FThreadId;
|
|
property ThreadName: String read FThreadName;
|
|
property ThreadState: String read FThreadState write SetThreadState;
|
|
property TopFrame: TCallStackEntry read FTopFrame;
|
|
end;
|
|
|
|
{ TThreadsBase }
|
|
|
|
TThreads = class(TObject)
|
|
private
|
|
FCurrentThreadId: Integer;
|
|
FList: TList;
|
|
function GetEntry(const AnIndex: Integer): TThreadEntry;
|
|
function GetEntryById(const AnID: Integer): TThreadEntry;
|
|
protected
|
|
procedure SetCurrentThreadId(AValue: Integer); virtual;
|
|
property List: TList read FList;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Assign(AnOther: TThreads); virtual;
|
|
function Count: Integer; virtual;
|
|
procedure Clear; virtual;
|
|
procedure Add(AThread: TThreadEntry); virtual;
|
|
procedure Remove(AThread: TThreadEntry); virtual;
|
|
function CreateEntry(const AnAdress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String;
|
|
const FileName, FullName: String;
|
|
const ALine: Integer;
|
|
const AThreadId: Integer; const AThreadName: String;
|
|
const AThreadState: String;
|
|
AState: TDebuggerDataState = ddsValid): TThreadEntry; virtual;
|
|
procedure SetValidity({%H-}AValidity: TDebuggerDataState); virtual;
|
|
property Entries[const AnIndex: Integer]: TThreadEntry read GetEntry; default;
|
|
property EntryById[const AnID: Integer]: TThreadEntry read GetEntryById;
|
|
property CurrentThreadId: Integer read FCurrentThreadId write SetCurrentThreadId;
|
|
end;
|
|
|
|
{ TThreadsSupplier }
|
|
|
|
TThreadsSupplier = class(TDebuggerDataSupplier)
|
|
private
|
|
function GetCurrentThreads: TThreads;
|
|
function GetMonitor: TThreadsMonitor;
|
|
procedure SetMonitor(AValue: TThreadsMonitor);
|
|
property Monitor: TThreadsMonitor read GetMonitor write SetMonitor;
|
|
protected
|
|
procedure DoStateChange(const AOldState: TDBGState); override;
|
|
procedure DoStateLeavePauseClean; override;
|
|
procedure DoCleanAfterPause; virtual;
|
|
public
|
|
procedure RequestMasterData; virtual;
|
|
procedure ChangeCurrentThread({%H-}ANewId: Integer); virtual;
|
|
procedure Changed; // TODO: needed because entries can not notify the monitor
|
|
property CurrentThreads: TThreads read GetCurrentThreads;
|
|
end;
|
|
|
|
{ TThreadsMonitor }
|
|
|
|
TThreadsMonitor = class(TDebuggerDataMonitor)
|
|
private
|
|
FThreads: TThreads;
|
|
function GetSupplier: TThreadsSupplier;
|
|
procedure SetSupplier(AValue: TThreadsSupplier);
|
|
protected
|
|
function CreateThreads: TThreads; virtual;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
property Threads: TThreads read FThreads;
|
|
property Supplier: TThreadsSupplier read GetSupplier write SetSupplier;
|
|
end;
|
|
|
|
{%endregion ^^^^^ Threads ^^^^^ }
|
|
|
|
{%region Signals / Exceptions *************************************************}
|
|
(******************************************************************************)
|
|
(** **)
|
|
(** S I G N A L S and E X C E P T I O N S **)
|
|
(** **)
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
|
|
{ TBaseSignal }
|
|
|
|
TBaseSignal = class(TDelayedUdateItem)
|
|
private
|
|
FHandledByDebugger: Boolean;
|
|
FID: Integer;
|
|
FName: String;
|
|
FResumeHandled: Boolean;
|
|
protected
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure SetHandledByDebugger(const AValue: Boolean); virtual;
|
|
procedure SetID(const AValue: Integer); virtual;
|
|
procedure SetName(const AValue: String); virtual;
|
|
procedure SetResumeHandled(const AValue: Boolean); virtual;
|
|
public
|
|
constructor Create(ACollection: TCollection); override;
|
|
property ID: Integer read FID write SetID;
|
|
property Name: String read FName write SetName;
|
|
property HandledByDebugger: Boolean read FHandledByDebugger write SetHandledByDebugger;
|
|
property ResumeHandled: Boolean read FResumeHandled write SetResumeHandled;
|
|
end;
|
|
TBaseSignalClass = class of TBaseSignal;
|
|
|
|
{ TDBGSignal }
|
|
|
|
TDBGSignal = class(TBaseSignal)
|
|
private
|
|
function GetDebugger: TDebuggerIntf;
|
|
protected
|
|
property Debugger: TDebuggerIntf read GetDebugger;
|
|
public
|
|
end;
|
|
TDBGSignalClass = class of TDBGSignal;
|
|
|
|
{ TBaseSignals }
|
|
TBaseSignals = class(TCollection)
|
|
private
|
|
protected
|
|
public
|
|
constructor Create(const AItemClass: TBaseSignalClass);
|
|
procedure Reset; virtual;
|
|
function Add(const AName: String; AID: Integer): TBaseSignal;
|
|
function Find(const AName: String): TBaseSignal;
|
|
end;
|
|
|
|
{ TDBGSignals }
|
|
|
|
TDBGSignals = class(TBaseSignals)
|
|
private
|
|
FDebugger: TDebuggerIntf; // reference to our debugger
|
|
function GetItem(const AIndex: Integer): TDBGSignal;
|
|
procedure SetItem(const AIndex: Integer; const AValue: TDBGSignal);
|
|
protected
|
|
public
|
|
constructor Create(const ADebugger: TDebuggerIntf;
|
|
const ASignalClass: TDBGSignalClass);
|
|
function Add(const AName: String; AID: Integer): TDBGSignal;
|
|
function Find(const AName: String): TDBGSignal;
|
|
public
|
|
property Items[const AIndex: Integer]: TDBGSignal read GetItem
|
|
write SetItem; default;
|
|
end;
|
|
|
|
|
|
|
|
{ TBaseException }
|
|
TBaseException = class(TDelayedUdateItem)
|
|
private
|
|
procedure SetEnabled(AValue: Boolean);
|
|
protected
|
|
FEnabled: Boolean;
|
|
FName: String;
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure SetName(const AValue: String); virtual;
|
|
public
|
|
constructor Create(ACollection: TCollection); override;
|
|
public
|
|
property Name: String read FName write SetName;
|
|
property Enabled: Boolean read FEnabled write SetEnabled; // ignored if enabled
|
|
end;
|
|
TBaseExceptionClass = class of TBaseException;
|
|
|
|
{ TDBGException }
|
|
TDBGException = class(TBaseException)
|
|
private
|
|
protected
|
|
public
|
|
end;
|
|
TDBGExceptionClass = class of TDBGException;
|
|
|
|
{ TBaseExceptions }
|
|
TBaseExceptions = class(TCollection)
|
|
private
|
|
function GetItem(const AIndex: Integer): TBaseException;
|
|
procedure SetItem(const AIndex: Integer; AValue: TBaseException);
|
|
protected
|
|
FIgnoreAll: Boolean;
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure ClearExceptions; virtual;
|
|
procedure SetIgnoreAll(const AValue: Boolean); virtual;
|
|
public
|
|
constructor Create(const AItemClass: TBaseExceptionClass);
|
|
destructor Destroy; override;
|
|
procedure Reset; virtual;
|
|
function Add(const AName: String): TBaseException;
|
|
function Find(const AName: String): TBaseException;
|
|
property IgnoreAll: Boolean read FIgnoreAll write SetIgnoreAll;
|
|
property Items[const AIndex: Integer]: TBaseException read GetItem
|
|
write SetItem; default;
|
|
end;
|
|
|
|
|
|
{%endregion ^^^^^ Signals / Exceptions ^^^^^ }
|
|
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
(** **)
|
|
(** D E B U G G E R **)
|
|
(** **)
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
|
|
TDBGEventCategory = (
|
|
ecBreakpoint, // Breakpoint hit
|
|
ecProcess, // Process start, process stop
|
|
ecThread, // Thread creation, destruction, start, etc.
|
|
ecModule, // Library load and unload
|
|
ecOutput, // DebugOutput calls
|
|
ecWindows, // Windows events
|
|
ecDebugger); // debugger errors and warnings
|
|
TDBGEventCategories = set of TDBGEventCategory;
|
|
|
|
TDBGEventType = (
|
|
etDefault,
|
|
// ecBreakpoint category
|
|
etBreakpointEvaluation,
|
|
etBreakpointHit,
|
|
etBreakpointMessage,
|
|
etBreakpointStackDump,
|
|
etExceptionRaised,
|
|
// ecModule category
|
|
etModuleLoad,
|
|
etModuleUnload,
|
|
// ecOutput category
|
|
etOutputDebugString,
|
|
// ecProcess category
|
|
etProcessExit,
|
|
etProcessStart,
|
|
// ecThread category
|
|
etThreadExit,
|
|
etThreadStart,
|
|
// ecWindows category
|
|
etWindowsMessagePosted,
|
|
etWindowsMessageSent
|
|
);
|
|
|
|
TDebugCompilerRequirement = (dcrNoExternalDbgInfo, dcrExternalDbgInfoOnly, dcrDwarfOnly);
|
|
TDebugCompilerRequirements = set of TDebugCompilerRequirement;
|
|
|
|
TDBGFeedbackType = (ftInformation, ftWarning, ftError);
|
|
TDBGFeedbackResult = (frOk, frStop);
|
|
TDBGFeedbackResults = set of TDBGFeedbackResult;
|
|
|
|
TDBGEventNotify = procedure(Sender: TObject;
|
|
const ACategory: TDBGEventCategory;
|
|
const AEventType: TDBGEventType;
|
|
const AText: String) of object;
|
|
|
|
TDebuggerStateChangedEvent = procedure(ADebugger: TDebuggerIntf;
|
|
AOldState: TDBGState) of object;
|
|
TDebuggerBreakPointHitEvent = procedure(ADebugger: TDebuggerIntf; ABreakPoint: TBaseBreakPoint;
|
|
var ACanContinue: Boolean) of object;
|
|
TDBGOutputEvent = procedure(Sender: TObject; const AText: String) of object;
|
|
TDBGCurrentLineEvent = procedure(Sender: TObject;
|
|
const ALocation: TDBGLocationRec) of object;
|
|
TDBGExceptionEvent = procedure(Sender: TObject; const AExceptionType: TDBGExceptionType;
|
|
const AExceptionClass: String;
|
|
const AExceptionLocation: TDBGLocationRec;
|
|
const AExceptionText: String;
|
|
out AContinue: Boolean) of object;
|
|
|
|
TDBGFeedbackEvent = function(Sender: TObject; const AText, AInfo: String;
|
|
AType: TDBGFeedbackType; AButtons: TDBGFeedbackResults
|
|
): TDBGFeedbackResult of object;
|
|
|
|
TDBGEvaluateResultCallback = procedure(Sender: TObject; ASuccess: Boolean; ResultText: String;
|
|
ResultDBGType: TDBGType) of object;
|
|
|
|
TDebuggerNotifyReason = (dnrDestroy);
|
|
|
|
{ TDebuggerProperties }
|
|
|
|
TDebuggerProperties = class(TPersistent)
|
|
private
|
|
public
|
|
constructor Create; virtual;
|
|
procedure Assign({%H-}Source: TPersistent); override;
|
|
published
|
|
end;
|
|
TDebuggerPropertiesClass= class of TDebuggerProperties;
|
|
|
|
{ TCommonDebuggerProperties
|
|
properties that all debuggers should/could implement
|
|
}
|
|
|
|
TInternalExceptionBreakPoint = (ieRaiseBreakPoint, ieRunErrorBreakPoint, ieBreakErrorBreakPoint);
|
|
TInternalExceptionBreakPoints = set of TInternalExceptionBreakPoint;
|
|
|
|
TCommonDebuggerProperties = class(TDebuggerProperties)
|
|
private
|
|
FInternalExceptionBreakPoints: TInternalExceptionBreakPoints;
|
|
protected const
|
|
INTERNALEXCEPTIONBREAKPOINTS_DEFAULT = [ieRaiseBreakPoint, ieRunErrorBreakPoint, ieBreakErrorBreakPoint];
|
|
protected
|
|
property InternalExceptionBreakPoints: TInternalExceptionBreakPoints
|
|
read FInternalExceptionBreakPoints write FInternalExceptionBreakPoints default INTERNALEXCEPTIONBREAKPOINTS_DEFAULT;
|
|
public
|
|
constructor Create; override;
|
|
procedure Assign({%H-}Source: TPersistent); override;
|
|
end;
|
|
|
|
{$INTERFACES CORBA} // no ref counting needed
|
|
|
|
{ TDebuggerEventLogInterface
|
|
Methods for the EventLogger that a debugger may call
|
|
}
|
|
//TODO: remove TDebuggerIntf.OnEvent
|
|
|
|
TDebuggerEventLogInterface = interface
|
|
procedure LogCustomEvent(const ACategory: TDBGEventCategory;
|
|
const AEventType: TDBGEventType; const AText: String);
|
|
procedure LogEventBreakPointHit(const ABreakpoint: TDBGBreakPoint; const ALocation: TDBGLocationRec);
|
|
procedure LogEventWatchPointTriggered(const ABreakpoint: TDBGBreakPoint;
|
|
const ALocation: TDBGLocationRec; const AOldWatchedVal, ANewWatchedVal: String);
|
|
procedure LogEventWatchPointScope(const ABreakpoint: TDBGBreakPoint;
|
|
const ALocation: TDBGLocationRec);
|
|
end;
|
|
|
|
//TDebuggerActionInterface = interface
|
|
// // prompt user
|
|
//end;
|
|
|
|
{ TDebuggerIntf }
|
|
|
|
TDebuggerIntf = class
|
|
private
|
|
FArguments: String;
|
|
FBreakPoints: TDBGBreakPoints;
|
|
FDebuggerEnvironment: TStrings;
|
|
FCurEnvironment: TStrings;
|
|
FDisassembler: TDBGDisassembler;
|
|
FEnabledFeatures: TDBGFeatures;
|
|
FEnvironment: TStrings;
|
|
FErrorStateInfo: String;
|
|
FErrorStateMessage: String;
|
|
FExceptions: TBaseExceptions;
|
|
FExitCode: Integer;
|
|
FExternalDebugger: String;
|
|
FFileName: String;
|
|
FIsInReset: Boolean;
|
|
FLocals: TLocalsSupplier;
|
|
FLineInfo: TDBGLineInfo;
|
|
//FUnitInfoProvider, FInternalUnitInfoProvider: TDebuggerUnitInfoProvider;
|
|
FOnBeforeState: TDebuggerStateChangedEvent;
|
|
FOnConsoleOutput: TDBGOutputEvent;
|
|
FOnFeedback: TDBGFeedbackEvent;
|
|
FOnIdle: TNotifyEvent;
|
|
FRegisters: TRegisterSupplier;
|
|
FShowConsole: Boolean;
|
|
FSignals: TDBGSignals;
|
|
FSkipStopMessage: Boolean;
|
|
FState: TDBGState;
|
|
FCallStack: TCallStackSupplier;
|
|
FWatches: TWatchesSupplier;
|
|
FThreads: TThreadsSupplier;
|
|
FEventLogHandler: TDebuggerEventLogInterface;
|
|
FOnCurrent: TDBGCurrentLineEvent;
|
|
FOnException: TDBGExceptionEvent;
|
|
FOnOutput: TDBGOutputEvent;
|
|
FOnDbgOutput: TDBGOutputEvent;
|
|
FOnDbgEvent: TDBGEventNotify;
|
|
FOnState: TDebuggerStateChangedEvent;
|
|
FOnBreakPointHit: TDebuggerBreakPointHitEvent;
|
|
FWorkingDir: String;
|
|
FDestroyNotificationList: array [TDebuggerNotifyReason] of TMethodList;
|
|
FReleaseLock: Integer;
|
|
procedure DebuggerEnvironmentChanged(Sender: TObject);
|
|
procedure EnvironmentChanged(Sender: TObject);
|
|
function GetRunErrorText(ARunError: Integer): string;
|
|
//function GetUnitInfoProvider: TDebuggerUnitInfoProvider;
|
|
function GetState: TDBGState;
|
|
function GetWatches: TWatchesSupplierIntf;
|
|
function ReqCmd(const ACommand: TDBGCommand;
|
|
const AParams: array of const): Boolean; overload;
|
|
function ReqCmd(const ACommand: TDBGCommand;
|
|
const AParams: array of const;
|
|
const ACallback: TMethod): Boolean;
|
|
procedure SetDebuggerEnvironment (const AValue: TStrings ); overload;
|
|
procedure SetEnabledFeatures(AValue: TDBGFeatures);
|
|
procedure SetEnvironment(const AValue: TStrings);
|
|
procedure SetFileName(const AValue: String);
|
|
protected
|
|
procedure ResetStateToIdle; virtual;
|
|
function CreateBreakPoints: TDBGBreakPoints; virtual;
|
|
function CreateLocals: TLocalsSupplier; virtual;
|
|
function CreateLineInfo: TDBGLineInfo; virtual;
|
|
function CreateRegisters: TRegisterSupplier; virtual;
|
|
function CreateCallStack: TCallStackSupplier; virtual;
|
|
function CreateDisassembler: TDBGDisassembler; virtual;
|
|
function CreateWatches: TWatchesSupplier; virtual;
|
|
function CreateThreads: TThreadsSupplier; virtual;
|
|
function CreateSignals: TDBGSignals; virtual;
|
|
procedure DoCurrent(const ALocation: TDBGLocationRec);
|
|
procedure DoDbgOutput(const AText: String);
|
|
procedure DoDbgEvent(const ACategory: TDBGEventCategory; const AEventType: TDBGEventType; const AText: String);
|
|
deprecated 'switch to EventLogHandler';
|
|
procedure DoException(const AExceptionType: TDBGExceptionType;
|
|
const AExceptionClass: String;
|
|
const AExceptionLocation: TDBGLocationRec;
|
|
const AExceptionText: String;
|
|
out AContinue: Boolean);
|
|
procedure DoOutput(const AText: String);
|
|
procedure DoBreakpointHit(const ABreakPoint: TBaseBreakPoint; var ACanContinue: Boolean);
|
|
procedure DoBeforeState(const OldState: TDBGState); virtual;
|
|
procedure DoState(const OldState: TDBGState); virtual;
|
|
function ChangeFileName: Boolean; virtual;
|
|
function GetCommands: TDBGCommands; virtual;
|
|
class function GetSupportedCommands: TDBGCommands; virtual;
|
|
function GetTargetWidth: Byte; virtual;
|
|
function GetWaiting: Boolean; virtual;
|
|
function GetIsIdle: Boolean; virtual;
|
|
function RequestCommand(const ACommand: TDBGCommand;
|
|
const AParams: array of const;
|
|
const ACallback: TMethod): Boolean;
|
|
virtual; abstract; // True if succesful
|
|
procedure SetExitCode(const AValue: Integer);
|
|
procedure SetState(const AValue: TDBGState);
|
|
procedure SetErrorState(const AMsg: String; const AInfo: String = '');
|
|
procedure DoRelease; virtual;
|
|
// prevent destruction while nested in any call
|
|
procedure LockRelease; virtual;
|
|
procedure UnlockRelease; virtual;
|
|
function GetPseudoTerminal: TPseudoTerminal; virtual;
|
|
property InternalFilename: string read FFileName write FFileName; //experimental
|
|
public
|
|
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 ExePathsMruGroup: TDebuggerClass; virtual; // The default locations of the exe
|
|
class function HasExePath: boolean; virtual; deprecated; // use NeedsExePath instead
|
|
class function NeedsExePath: boolean; virtual; // If the debugger needs to have an exe path
|
|
class function RequiredCompilerOpts(ATargetCPU, ATargetOS: String): TDebugCompilerRequirements; virtual;
|
|
class function SupportedFeatures: TDBGFeatures; virtual;
|
|
|
|
// debugger properties
|
|
class function CreateProperties: TDebuggerProperties; virtual; // Creates debuggerproperties
|
|
class function GetProperties: TDebuggerProperties; // Get the current properties
|
|
//class procedure SetProperties(const AProperties: TDebuggerProperties); // Set the current properties
|
|
|
|
(* TODO:
|
|
This method is a workaround for http://bugs.freepascal.org/view.php?id=21834
|
|
See main.pp 12188 function TMainIDE.DoInitProjectRun: TModalResult;
|
|
See debugmanager function TDebugManager.InitDebugger: Boolean;
|
|
Checks could be performed in SetFileName, invalidating debuggerstate
|
|
Errors should also be reported by debugger
|
|
*)
|
|
class function RequiresLocalExecutable: Boolean; virtual;
|
|
procedure TestCmd(const ACommand: String); virtual;// For internal debugging purposes
|
|
public
|
|
constructor Create(const AExternalDebugger: String); virtual;
|
|
destructor Destroy; override;
|
|
|
|
procedure Init; virtual; // Initializes the debugger
|
|
procedure Done; virtual; // Kills the debugger
|
|
procedure Release; // Free/Destroy self
|
|
procedure Run; // Starts / continues debugging
|
|
procedure Pause; // Stops running
|
|
procedure Stop; // quit debugging
|
|
procedure StepOver;
|
|
procedure StepInto;
|
|
procedure StepOverInstr;
|
|
procedure StepIntoInstr;
|
|
procedure StepOut;
|
|
procedure StepTo(const ASource: String; const ALine: Integer); // Executes til a certain point
|
|
procedure RunTo(const ASource: String; const ALine: Integer); // Executes til a certain point
|
|
procedure JumpTo(const ASource: String; const ALine: Integer); // No execute, only set exec point
|
|
procedure Attach(AProcessID: String);
|
|
procedure Detach;
|
|
procedure SendConsoleInput(AText: String);
|
|
function Evaluate(const AExpression: String; ACallback: TDBGEvaluateResultCallback;
|
|
EvalFlags: TWatcheEvaluateFlags = []): Boolean; // Evaluates the given expression, returns true if valid
|
|
function GetProcessList({%H-}AList: TRunningProcessInfoList): boolean; virtual;
|
|
function Modify(const AExpression, AValue: String): Boolean; // Modifies the given expression, returns true if valid
|
|
function Disassemble(AAddr: TDbgPtr; ABackward: Boolean; out ANextAddr: TDbgPtr;
|
|
out ADump, AStatement, AFile: String; out ALine: Integer): Boolean; deprecated;
|
|
function GetLocation: TDBGLocationRec; virtual;
|
|
procedure LockCommandProcessing; virtual;
|
|
procedure UnLockCommandProcessing; virtual;
|
|
procedure BeginReset; virtual;
|
|
function NeedReset: Boolean; virtual;
|
|
property IsInReset: Boolean read FIsInReset;
|
|
procedure AddNotifyEvent(AReason: TDebuggerNotifyReason; AnEvent: TNotifyEvent);
|
|
procedure RemoveNotifyEvent(AReason: TDebuggerNotifyReason; AnEvent: TNotifyEvent);
|
|
procedure SetSkipStopMessage;
|
|
public
|
|
property Arguments: String read FArguments write FArguments; // Arguments feed to the program
|
|
property BreakPoints: TDBGBreakPoints read FBreakPoints; // list of all breakpoints
|
|
property CallStack: TCallStackSupplier read FCallStack;
|
|
property Disassembler: TDBGDisassembler read FDisassembler;
|
|
property Commands: TDBGCommands read GetCommands; // All current available commands of the debugger
|
|
property DebuggerEnvironment: TStrings read FDebuggerEnvironment
|
|
write SetDebuggerEnvironment; // The environment passed to the debugger process
|
|
property Environment: TStrings read FEnvironment write SetEnvironment; // The environment passed to the debuggee
|
|
property Exceptions: TBaseExceptions read FExceptions write FExceptions; // A list of exceptions we should ignore
|
|
property RunErrorText [ARunError: Integer]: string read GetRunErrorText;
|
|
property ExitCode: Integer read FExitCode;
|
|
property ExternalDebugger: String read FExternalDebugger; // The name of the debugger executable
|
|
property FileName: String read FFileName write SetFileName; // The name of the exe to be debugged
|
|
property Locals: TLocalsSupplier read FLocals; // list of all localvars etc
|
|
property LineInfo: TDBGLineInfo read FLineInfo; // list of all source LineInfo
|
|
property Registers: TRegisterSupplier read FRegisters; // list of all registers
|
|
property Signals: TDBGSignals read FSignals; // A list of actions for signals we know
|
|
property ShowConsole: Boolean read FShowConsole write FShowConsole; // Indicates if the debugger should create a console for the debuggee
|
|
property PseudoTerminal: TPseudoTerminal read GetPseudoTerminal; experimental; // 'may be replaced with a more general API';
|
|
property State: TDBGState read FState; // The current state of the debugger
|
|
property SupportedCommands: TDBGCommands read GetSupportedCommands; // All available commands of the debugger
|
|
class function SupportedCommandsFor(AState: TDBGState): TDBGCommands; virtual;
|
|
property TargetWidth: Byte read GetTargetWidth; // Currently only 32 or 64
|
|
//property Waiting: Boolean read GetWaiting; // Set when the debugger is wating for a command to complete
|
|
property WatchSupplier: TWatchesSupplierIntf read GetWatches; // list of all watches etc
|
|
property Watches: TWatchesSupplier read FWatches; // list of all watches etc
|
|
property Threads: TThreadsSupplier read FThreads;
|
|
property WorkingDir: String read FWorkingDir write FWorkingDir; // The working dir of the exe being debugged
|
|
property IsIdle: Boolean read GetIsIdle; // Nothing queued
|
|
property ErrorStateMessage: String read FErrorStateMessage;
|
|
property ErrorStateInfo: String read FErrorStateInfo;
|
|
property SkipStopMessage: Boolean read FSkipStopMessage;
|
|
property EnabledFeatures: TDBGFeatures read FEnabledFeatures write SetEnabledFeatures;
|
|
//property UnitInfoProvider: TDebuggerUnitInfoProvider // Provided by DebugBoss, to map files to packages or project
|
|
// read GetUnitInfoProvider write FUnitInfoProvider;
|
|
// Events
|
|
property EventLogHandler: TDebuggerEventLogInterface read FEventLogHandler write FEventLogHandler;
|
|
property OnCurrent: TDBGCurrentLineEvent read FOnCurrent write FOnCurrent; // Passes info about the current line being debugged
|
|
property OnDbgOutput: TDBGOutputEvent read FOnDbgOutput write FOnDbgOutput; // Passes all debuggeroutput
|
|
property OnDbgEvent: TDBGEventNotify read FOnDbgEvent write FOnDbgEvent; // Passes recognized debugger events, like library load or unload
|
|
deprecated 'switch to EventLogHandler';
|
|
property OnException: TDBGExceptionEvent read FOnException write FOnException; // Fires when the debugger received an ecxeption
|
|
property OnOutput: TDBGOutputEvent read FOnOutput write FOnOutput; // Passes all output of the debugged target
|
|
property OnBeforeState: TDebuggerStateChangedEvent read FOnBeforeState write FOnBeforeState; // Fires when the current state of the debugger changes
|
|
property OnState: TDebuggerStateChangedEvent read FOnState write FOnState; // Fires when the current state of the debugger changes
|
|
property OnBreakPointHit: TDebuggerBreakPointHitEvent read FOnBreakPointHit write FOnBreakPointHit; // Fires when the program is paused at a breakpoint
|
|
property OnConsoleOutput: TDBGOutputEvent read FOnConsoleOutput write FOnConsoleOutput; // Passes Application Console Output
|
|
property OnFeedback: TDBGFeedbackEvent read FOnFeedback write FOnFeedback;
|
|
property OnIdle: TNotifyEvent read FOnIdle write FOnIdle; // Called if all outstanding requests are processed (queue empty)
|
|
end;
|
|
|
|
{ TBaseDebugManagerIntf }
|
|
|
|
TBaseDebugManagerIntf = class(TComponent)
|
|
public type
|
|
TStringFunction = function(const aValue: string): string;
|
|
private
|
|
FValueFormatterList: TStringList;
|
|
|
|
function ValueFormatterKey(const aSymbolKind: TDBGSymbolKind;
|
|
const aTypeName: string): string;
|
|
protected
|
|
class function GetDebuggerClass(const AIndex: Integer): TDebuggerClass;static;
|
|
class function GetDebuggerClassByName(const AIndex: String): TDebuggerClass; static;
|
|
function FindDebuggerClass(const Astring: String): TDebuggerClass;
|
|
public
|
|
class function DebuggerCount: Integer;
|
|
|
|
procedure RegisterValueFormatter(const aSymbolKind: TDBGSymbolKind;
|
|
const aTypeName: string; const aFunc: TStringFunction);
|
|
function FormatValue(const aSymbolKind: TDBGSymbolKind;
|
|
const aTypeName, aValue: string): string;
|
|
function FormatValue(const aDBGType: TDBGType;
|
|
const aValue: string): string;
|
|
class property Debuggers[const AIndex: Integer]: TDebuggerClass read GetDebuggerClass;
|
|
class property DebuggersByClassName[const AIndex: String]: TDebuggerClass read GetDebuggerClassByName;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
procedure DoBackendConverterChanged; virtual; abstract;
|
|
end;
|
|
|
|
procedure RegisterDebugger(const ADebuggerClass: TDebuggerClass);
|
|
function MinDbgPtr(a, b: TDBGPtr): TDBGPtr;inline; overload;
|
|
|
|
function dbgs(AState: TDBGState): String; overload;
|
|
function dbgs(ADataState: TDebuggerDataState): String; overload;
|
|
function dbgs(AKind: TDBGSymbolKind): String; overload;
|
|
function dbgs(AnAttribute: TDBGSymbolAttribute): String; overload;
|
|
function dbgs(AnAttributes: TDBGSymbolAttributes): String; overload;
|
|
function dbgs(ADisassRange: TDBGDisassemblerEntryRange): String; overload;
|
|
function dbgs(const AnAddr: TDisassemblerAddress): string; overload;
|
|
function dbgs(ACategory: TDBGEventCategory): String; overload;
|
|
function dbgs(AFlag: TWatcheEvaluateFlag): String; overload;
|
|
function dbgs(AFlags: TWatcheEvaluateFlags): String; overload;
|
|
function dbgs(AName: TDBGCommand): String; overload;
|
|
|
|
var
|
|
DbgStateChangeCounter: Integer = 0; // workaround for state changes during TWatchValue.GetValue
|
|
DebugBossManager: TBaseDebugManagerIntf;
|
|
|
|
implementation
|
|
|
|
var
|
|
DBG_STATE, DBG_EVENTS, DBG_STATE_EVENT, DBG_DATA_MONITORS,
|
|
DBG_VERBOSE, DBG_WARNINGS, DBG_DISASSEMBLER: PLazLoggerLogGroup;
|
|
|
|
const
|
|
COMMANDMAP: array[TDBGState] of TDBGCommands = (
|
|
{dsNone } [],
|
|
{dsIdle } [dcRun, dcStepOver, dcStepInto, dcStepOverInstr, dcStepIntoInstr, dcRunTo,
|
|
dcAttach, dcBreak, dcWatch, {dcEvaluate,} dcEnvironment],
|
|
{dsStop } [dcRun, dcStepOver, dcStepInto, dcStepOverInstr, dcStepIntoInstr, dcRunTo,
|
|
dcAttach, dcBreak, dcWatch, {dcEvaluate,} dcEnvironment,
|
|
dcSendConsoleInput],
|
|
{dsPause} [dcRun, dcStop, dcStepOver, dcStepInto, dcStepOverInstr, dcStepIntoInstr,
|
|
dcStepOut, dcStepTo, dcRunTo, dcJumpto, dcDetach, dcBreak, dcWatch, dcLocal, dcEvaluate, dcModify,
|
|
dcEnvironment, dcSetStackFrame, dcDisassemble, dcSendConsoleInput {, dcSendSignal}],
|
|
{dsInternalPause} // same as run, so not really used
|
|
[dcStop, dcBreak, dcWatch, dcEnvironment, dcSendConsoleInput{, dcSendSignal}],
|
|
{dsInit } [],
|
|
{dsRun } [dcPause, dcStop, dcDetach, dcBreak, dcWatch, dcEnvironment, dcSendConsoleInput{, dcSendSignal}],
|
|
{dsError} [dcStop],
|
|
{dsDestroying} []
|
|
);
|
|
|
|
var
|
|
MDebuggerPropertiesList: TStringlist = nil;
|
|
MDebuggerClasses: TStringList;
|
|
|
|
procedure RegisterDebugger(const ADebuggerClass: TDebuggerClass);
|
|
begin
|
|
if MDebuggerClasses.IndexOfObject(TObject(Pointer(ADebuggerClass))) < 0 then
|
|
MDebuggerClasses.AddObject(ADebuggerClass.ClassName, TObject(Pointer(ADebuggerClass)));
|
|
end;
|
|
|
|
function MinDbgPtr(a, b: TDBGPtr): TDBGPtr;
|
|
begin
|
|
if a < b then
|
|
Result := a
|
|
else
|
|
Result := b;
|
|
end;
|
|
|
|
procedure DoFinalization;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
if MDebuggerPropertiesList <> nil
|
|
then begin
|
|
for n := 0 to MDebuggerPropertiesList.Count - 1 do
|
|
MDebuggerPropertiesList.Objects[n].Free;
|
|
FreeAndNil(MDebuggerPropertiesList);
|
|
end;
|
|
end;
|
|
|
|
function dbgs(AState: TDBGState): String; overload;
|
|
begin
|
|
Result := '';
|
|
WriteStr(Result, AState);
|
|
end;
|
|
|
|
function dbgs(ADataState: TDebuggerDataState): String;
|
|
begin
|
|
writestr(Result{%H-}, ADataState);
|
|
end;
|
|
|
|
function dbgs(AKind: TDBGSymbolKind): String;
|
|
begin
|
|
writestr(Result{%H-}, AKind);
|
|
end;
|
|
|
|
function dbgs(AnAttribute: TDBGSymbolAttribute): String;
|
|
begin
|
|
writestr(Result{%H-}, AnAttribute);
|
|
end;
|
|
|
|
function dbgs(AnAttributes: TDBGSymbolAttributes): String;
|
|
var
|
|
i: TDBGSymbolAttribute;
|
|
begin
|
|
Result:='';
|
|
for i := low(TDBGSymbolAttributes) to high(TDBGSymbolAttributes) do
|
|
if i in AnAttributes then begin
|
|
if Result <> '' then Result := Result + ', ';
|
|
Result := Result + dbgs(i);
|
|
end;
|
|
if Result <> '' then Result := '[' + Result + ']';
|
|
end;
|
|
|
|
function dbgs(ACategory: TDBGEventCategory): String;
|
|
begin
|
|
writestr(Result{%H-}, ACategory);
|
|
end;
|
|
|
|
function dbgs(AFlag: TWatcheEvaluateFlag): String;
|
|
begin
|
|
Result := '';
|
|
WriteStr(Result, AFlag);
|
|
end;
|
|
|
|
function dbgs(AFlags: TWatcheEvaluateFlags): String;
|
|
var
|
|
i: TWatcheEvaluateFlag;
|
|
begin
|
|
Result:='';
|
|
for i := low(TWatcheEvaluateFlag) to high(TWatcheEvaluateFlag) do
|
|
if i in AFlags then begin
|
|
if Result <> '' then Result := Result + ', ';
|
|
Result := Result + dbgs(i);
|
|
end;
|
|
Result := '[' + Result + ']';
|
|
end;
|
|
|
|
function dbgs(AName: TDBGCommand): String;
|
|
begin
|
|
Result := '';
|
|
WriteStr(Result, AName);
|
|
end;
|
|
|
|
function dbgs(ADisassRange: TDBGDisassemblerEntryRange): String; overload;
|
|
var
|
|
fo: Integer;
|
|
begin
|
|
if (ADisassRange = nil)
|
|
then begin
|
|
Result := 'Range(nil)'
|
|
end
|
|
else begin
|
|
if (ADisassRange.Count > 0)
|
|
then fo := ADisassRange.EntriesPtr[0]^.Offset
|
|
else fo := 0;
|
|
{$PUSH}{$RANGECHECKS OFF}
|
|
with ADisassRange do
|
|
Result := Format('Range(%u)=[[ Cnt=%d, Capac=%d, [0].Addr=%u, RFirst=%u, [Cnt].Addr=%u, RLast=%u, REnd=%u, FirstOfs=%d ]]',
|
|
[PtrUInt(ADisassRange), Count, Capacity, FirstAddr, RangeStartAddr, LastAddr, RangeEndAddr, LastEntryEndAddr, fo]);
|
|
{$POP}
|
|
end;
|
|
end;
|
|
|
|
function Dbgs(const AnAddr: TDisassemblerAddress): string;
|
|
const
|
|
ValidityName: array [TDisassemblerAddressValidity] of string =
|
|
('FoundFunction', 'FoundRange', 'FoundStatemnet', 'Guessed', 'ExternRequest', 'Padded');
|
|
begin
|
|
Result := Format('[[ Value=%u, Guessed=%u, Offset=%d, Validity=%s ]]',
|
|
[AnAddr.Value, AnAddr.GuessedValue, AnAddr.Offset, ValidityName[AnAddr.Validity]]);
|
|
end;
|
|
|
|
{ TCommonDebuggerProperties }
|
|
|
|
constructor TCommonDebuggerProperties.Create;
|
|
begin
|
|
FInternalExceptionBreakPoints := INTERNALEXCEPTIONBREAKPOINTS_DEFAULT;
|
|
end;
|
|
|
|
procedure TCommonDebuggerProperties.Assign(Source: TPersistent);
|
|
begin
|
|
inherited Assign(Source);
|
|
if Source is TCommonDebuggerProperties then begin
|
|
FInternalExceptionBreakPoints := TCommonDebuggerProperties(Source).FInternalExceptionBreakPoints;
|
|
end;
|
|
end;
|
|
|
|
{ TDBGDisassemblerRangeExtender }
|
|
|
|
function TDBGDisassemblerRangeExtender.InitAddress(AValue: TDBGPtr;
|
|
AValidity: TDisassemblerAddressValidity; AnOffset: Integer): TDisassemblerAddress;
|
|
begin
|
|
Result.Value := AValue;
|
|
Result.GuessedValue := AValue;;
|
|
Result.Offset := AnOffset;
|
|
Result.Validity := AValidity;
|
|
end;
|
|
|
|
constructor TDBGDisassemblerRangeExtender.Create(AnEntryRangeMap: TDBGDisassemblerEntryMap);
|
|
begin
|
|
FEntryRangeMap := AnEntryRangeMap;
|
|
FRangeIterator:= TDBGDisassemblerEntryMapIterator.Create(FEntryRangeMap);
|
|
end;
|
|
|
|
destructor TDBGDisassemblerRangeExtender.Destroy;
|
|
begin
|
|
FRangeIterator.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function TDBGDisassemblerRangeExtender.CheckIfCancelled: boolean;
|
|
begin
|
|
result := assigned(FOnCheckCancel) and FOnCheckCancel();
|
|
end;
|
|
|
|
// Set Value, based on GuessedValue
|
|
function TDBGDisassemblerRangeExtender.AdjustToRangeOrKnowFunctionStart(var AStartAddr: TDisassemblerAddress;
|
|
ARangeBefore: TDBGDisassemblerEntryRange): Boolean;
|
|
begin
|
|
Result := False;
|
|
AStartAddr.Offset := -1;
|
|
AStartAddr.Validity := avGuessed;
|
|
if OnAdjustToKnowFunctionStart(AStartAddr)
|
|
then begin
|
|
// funtion found, check for range
|
|
if (ARangeBefore <> nil) and (ARangeBefore.LastAddr > AStartAddr.Value)
|
|
and (ARangeBefore.Count > DAssRangeOverFuncTreshold)
|
|
and (ARangeBefore.EntriesPtr[ARangeBefore.Count - 1]^.Offset > DAssRangeOverFuncTreshold * DAssBytesPerCommandAvg)
|
|
then begin
|
|
// got a big overlap, don't redo the whole function
|
|
debugln(DBG_DISASSEMBLER, ['INFO: Restarting inside previous range for known function-start=', Dbgs(AStartAddr),' and ARangeBefore=', dbgs(ARangeBefore)]);
|
|
// redo one statement
|
|
{$PUSH}{$IFnDEF DBGMI_WITH_DISASS_OVERFLOW}{$Q-}{$R-}{$ENDIF} // Overflow is allowed to occur
|
|
AStartAddr.Value := ARangeBefore.EntriesPtr[ARangeBefore.Count - 1]^.Addr;
|
|
AStartAddr.Offset := ARangeBefore.EntriesPtr[ARangeBefore.Count - 1]^.Offset;
|
|
AStartAddr.Validity := avFoundRange;
|
|
//AStartAddr - ARangeBefore.EntriesPtr[ARangeBefore.Count - DAssRangeOverFuncTreshold]^.Addr ;
|
|
{$POP}
|
|
end
|
|
end
|
|
else begin
|
|
debugln(DBG_DISASSEMBLER, ['INFO: No known function-start for ', Dbgs(AStartAddr),' ARangeBefore=', dbgs(ARangeBefore)]);
|
|
// no function found // check distance to previous range
|
|
// The distance of range before has been checked by the caller
|
|
if (ARangeBefore <> nil)
|
|
then begin
|
|
{$PUSH}{$IFnDEF DBGMI_WITH_DISASS_OVERFLOW}{$Q-}{$R-}{$ENDIF} // Overflow is allowed to occur
|
|
AStartAddr.Value := ARangeBefore.EntriesPtr[ARangeBefore.Count - 1]^.Addr;
|
|
AStartAddr.Offset := ARangeBefore.EntriesPtr[ARangeBefore.Count - 1]^.Offset;
|
|
AStartAddr.Validity := avFoundRange;
|
|
{$POP}
|
|
end
|
|
else begin
|
|
AStartAddr.Value := AStartAddr.GuessedValue;
|
|
AStartAddr.Offset := -1;
|
|
AStartAddr.Validity := avGuessed;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDBGDisassemblerRangeExtender.DisassembleRange(ALinesBefore,
|
|
ALinesAfter: integer; AStartAddr: TDBGPtr; AnEndAddr: TDBGPtr): boolean;
|
|
var
|
|
TryStartAt, TryEndAt: TDisassemblerAddress;
|
|
TmpAddr, TmpOffset: TDBGPtr;
|
|
GotCnt, LastGotCnt: Integer;
|
|
RngBefore, RngAfter: TDBGDisassemblerEntryRange;
|
|
begin
|
|
result := true;
|
|
(* Try to find the boundaries for the unknown range containing FStartAddr
|
|
If FStartAddr already has known disassembler data, then this will return
|
|
the boundaries of the 1ast unknown section after FStartAddr
|
|
*)
|
|
// Guess the maximum Addr-Range which needs to be disassembled
|
|
TryStartAt := InitAddress(AStartAddr, avExternRequest, -1);
|
|
// Find the begin of the function at TryStartAt
|
|
// or the rng before (if not to far back)
|
|
|
|
RngBefore := FRangeIterator.GetRangeForAddr(AStartAddr, True);
|
|
{$PUSH}{$IFnDEF DBGMI_WITH_DISASS_OVERFLOW}{$Q-}{$R-}{$ENDIF} // Overflow is allowed to occur
|
|
if (RngBefore <> nil)
|
|
and (TryStartAt.Value > RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr)
|
|
and (TryStartAt.Value - RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr > ALinesBefore * DAssBytesPerCommandAvg)
|
|
then RngBefore := nil;
|
|
{$POP}
|
|
TmpOffset := Min(ALinesBefore * DAssBytesPerCommandAvg, DAssMaxRangeSize);
|
|
if TmpOffset > AStartAddr then
|
|
TmpAddr := 0
|
|
else
|
|
TmpAddr := AStartAddr - TmpOffset;
|
|
TryStartAt.GuessedValue := TmpAddr;
|
|
AdjustToRangeOrKnowFunctionStart(TryStartAt, RngBefore);
|
|
// check max size
|
|
if (TryStartAt.Value < AStartAddr - MinDbgPtr(AStartAddr, DAssMaxRangeSize))
|
|
then begin
|
|
DebugLn(DBG_DISASSEMBLER, ['INFO: Limit Range for Disass: FStartAddr=', AStartAddr, ' TryStartAt.Value=', TryStartAt.Value ]);
|
|
TryStartAt := InitAddress(TmpAddr, avGuessed);
|
|
end;
|
|
|
|
// Guess Maximum, will adjust later
|
|
if TryStartAt.Value > AnEndAddr then begin
|
|
if (RngBefore <> nil) then begin
|
|
GotCnt := RngBefore.IndexOfAddr(AnEndAddr);
|
|
LastGotCnt := RngBefore.IndexOfAddr(TryStartAt.Value);
|
|
if (GotCnt >= 0) and (LastGotCnt >= 0) and (LastGotCnt > GotCnt) then
|
|
ALinesAfter := Max(ALinesAfter - (LastGotCnt - GotCnt), 1);
|
|
end;
|
|
AnEndAddr := TryStartAt.Value; // WARNING: modifying FEndAddr
|
|
end;
|
|
|
|
TryEndAt := InitAddress(AnEndAddr + ALinesAfter * DAssBytesPerCommandAvg, avGuessed);
|
|
|
|
// Read as many unknown ranges, until LinesAfter is met
|
|
GotCnt := -1;
|
|
while(True)
|
|
do begin
|
|
// check if we need any LinesAfter
|
|
if CheckIfCancelled then break;
|
|
LastGotCnt:= GotCnt;
|
|
GotCnt := 0;
|
|
TmpAddr := AnEndAddr;
|
|
if TryStartAt.Value > AnEndAddr
|
|
then
|
|
TmpAddr := TryStartAt.Value;
|
|
if RngBefore <> nil
|
|
then begin
|
|
TmpAddr := RngBefore.RangeEndAddr;
|
|
if RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr > TmpAddr
|
|
then TmpAddr := RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr;
|
|
GotCnt := RngBefore.IndexOfAddrWithOffs(AnEndAddr);
|
|
if GotCnt >= 0 then begin
|
|
GotCnt := RngBefore.Count - 1 - GotCnt; // the amount of LinesAfter, that are already known
|
|
if (GotCnt >= ALinesAfter)
|
|
then break;
|
|
// adjust end address
|
|
TryEndAt := InitAddress(RngBefore.RangeEndAddr + (ALinesAfter-GotCnt) * DAssBytesPerCommandAvg, avGuessed);
|
|
end
|
|
else GotCnt := 0;
|
|
end;
|
|
if LastGotCnt >= GotCnt
|
|
then begin
|
|
debugln(['Disassembler: *** Failure to get any more lines while scanning forward LastGotCnt=',LastGotCnt, ' now GotCnt=',GotCnt, ' Requested=',ALinesAfter]);
|
|
break;
|
|
end;
|
|
|
|
if CheckIfCancelled then break;
|
|
RngAfter := FRangeIterator.NextRange;
|
|
// adjust TryEndAt
|
|
if (RngAfter <> nil) and (TryEndAt.Value >= RngAfter.RangeStartAddr)
|
|
then begin
|
|
TryEndAt.Value := RngAfter.RangeStartAddr;
|
|
TryEndAt.Validity := avFoundRange;
|
|
end;
|
|
|
|
if CheckIfCancelled then break;
|
|
// Try to disassemble the range
|
|
if not OnDoDisassembleRange(FEntryRangeMap, TryStartAt, TryEndAt, TmpAddr, ALinesAfter-GotCnt)
|
|
then begin
|
|
// disassemble failed
|
|
debugln(['ERROR: Failed to disassemble from ', Dbgs(TryStartAt),' to ', Dbgs(TryEndAt)]);
|
|
break;
|
|
end;
|
|
|
|
// prepare the next range
|
|
RngBefore := FRangeIterator.GetRangeForAddr(AStartAddr, True);
|
|
if (RngBefore = nil)
|
|
then begin
|
|
debugln(['INTERNAL ERROR: (linesafter) Missing the data, that was just disassembled: from ', Dbgs(TryStartAt),' to ', Dbgs(TryEndAt)]);
|
|
break;
|
|
end;
|
|
|
|
TryStartAt.Value := RngBefore.RangeEndAddr;
|
|
TryStartAt.Validity := avFoundRange;
|
|
TryEndAt := InitAddress(AnEndAddr + ALinesAfter * DAssBytesPerCommandAvg, avGuessed);
|
|
end;
|
|
|
|
// Find LinesBefore
|
|
RngAfter := FRangeIterator.GetRangeForAddr(AStartAddr, True);
|
|
GotCnt := -1;
|
|
while(True)
|
|
do begin
|
|
if CheckIfCancelled then break;
|
|
LastGotCnt:= GotCnt;
|
|
if (RngAfter = nil)
|
|
then begin
|
|
debugln(['INTERNAL ERROR: (linesbefore) Missing the data, that was disassembled: from ', Dbgs(TryStartAt),' to ', Dbgs(TryEndAt)]);
|
|
break;
|
|
end;
|
|
|
|
GotCnt := RngAfter.IndexOfAddrWithOffs(AStartAddr); // already known before
|
|
if GotCnt >= ALinesBefore
|
|
then break;
|
|
if LastGotCnt >= GotCnt
|
|
then begin
|
|
debugln(['Disassembler: *** Failure to get any more lines while scanning backward LastGotCnt=',LastGotCnt, ' now GotCnt=',GotCnt, ' Requested=',ALinesBefore]);
|
|
break;
|
|
end;
|
|
|
|
TryEndAt := InitAddress(RngAfter.RangeStartAddr, avFoundRange);
|
|
TmpOffset := Min((ALinesBefore - GotCnt) * DAssBytesPerCommandAvg, DAssMaxRangeSize);
|
|
if TryEndAt.Value > TmpOffset then
|
|
TmpAddr := TryEndAt.Value - TmpOffset
|
|
else
|
|
TmpAddr := 0;
|
|
TryStartAt := InitAddress(TryEndAt.Value - 1, avGuessed);
|
|
TryStartAt.GuessedValue := TmpAddr;
|
|
// and adjust
|
|
RngBefore := FRangeIterator.PreviousRange;
|
|
{$PUSH}{$IFnDEF DBGMI_WITH_DISASS_OVERFLOW}{$Q-}{$R-}{$ENDIF} // Overflow is allowed to occur
|
|
if (RngBefore <> nil)
|
|
and (TryStartAt.Value > RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr)
|
|
and (TryStartAt.Value - RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr > (ALinesBefore - GotCnt) * DAssBytesPerCommandAvg)
|
|
then RngBefore := nil;
|
|
{$POP}
|
|
AdjustToRangeOrKnowFunctionStart(TryStartAt, RngBefore);
|
|
if (TryStartAt.Value < TryEndAt.Value - MinDbgPtr(TryEndAt.Value, DAssMaxRangeSize))
|
|
then begin
|
|
DebugLn(DBG_DISASSEMBLER, ['INFO: Limit Range for Disass: TryEndAt.Value=', TryEndAt.Value, ' TryStartAt.Value=', TryStartAt.Value ]);
|
|
TryStartAt := InitAddress(TmpAddr, avGuessed);
|
|
end;
|
|
|
|
if CheckIfCancelled then break;
|
|
// Try to disassemble the range
|
|
if not OnDoDisassembleRange(FEntryRangeMap, TryStartAt, TryEndAt, 0, -1)
|
|
then begin
|
|
// disassemble failed
|
|
debugln(['ERROR: Failed to disassemble from ', Dbgs(TryStartAt),' to ', Dbgs(TryEndAt)]);
|
|
break;
|
|
end;
|
|
|
|
RngAfter := FRangeIterator.GetRangeForAddr(AStartAddr, True);
|
|
end;
|
|
end;
|
|
|
|
{ TThreadEntry }
|
|
|
|
procedure TThreadEntry.SetThreadState(AValue: String);
|
|
begin
|
|
if FThreadState = AValue then Exit;
|
|
FThreadState := AValue;
|
|
end;
|
|
|
|
function TThreadEntry.CreateStackEntry: TCallStackEntry;
|
|
begin
|
|
Result := TCallStackEntry.Create;
|
|
end;
|
|
|
|
constructor TThreadEntry.Create;
|
|
begin
|
|
FTopFrame := CreateStackEntry;
|
|
inherited Create;
|
|
end;
|
|
|
|
constructor TThreadEntry.Create(const AnAdress: TDbgPtr; const AnArguments: TStrings;
|
|
const AFunctionName: String; const FileName, FullName: String; const ALine: Integer;
|
|
const AThreadId: Integer; const AThreadName: String; const AThreadState: String;
|
|
AState: TDebuggerDataState);
|
|
begin
|
|
Create;
|
|
TopFrame.Init(AnAdress, AnArguments, AFunctionName, FileName, FullName, ALine, AState);
|
|
FThreadId := AThreadId;
|
|
FThreadName := AThreadName;
|
|
FThreadState := AThreadState;
|
|
end;
|
|
|
|
procedure TThreadEntry.Init(const AnAdress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String; const FileName,
|
|
FullName: String; const ALine: Integer; const AThreadId: Integer;
|
|
const AThreadName: String; const AThreadState: String;
|
|
AState: TDebuggerDataState);
|
|
begin
|
|
TopFrame.Init(AnAdress, AnArguments, AFunctionName, FileName, FullName, ALine, AState);
|
|
FThreadId := AThreadId;
|
|
FThreadName := AThreadName;
|
|
FThreadState := AThreadState;
|
|
end;
|
|
|
|
function TThreadEntry.CreateCopy: TThreadEntry;
|
|
begin
|
|
Result := TThreadEntry.Create;
|
|
Result.Assign(Self);
|
|
end;
|
|
|
|
destructor TThreadEntry.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FTopFrame);
|
|
end;
|
|
|
|
procedure TThreadEntry.Assign(AnOther: TThreadEntry);
|
|
begin
|
|
FTopFrame.Free;
|
|
FTopFrame := CreateStackEntry; // .CreateCopy;
|
|
FTopFrame.Assign(AnOther.TopFrame);
|
|
FThreadId := AnOther.FThreadId;
|
|
FThreadName := AnOther.FThreadName;
|
|
FThreadState := AnOther.FThreadState;
|
|
end;
|
|
|
|
{ TThreads }
|
|
|
|
function TThreads.GetEntry(const AnIndex: Integer): TThreadEntry;
|
|
begin
|
|
if (AnIndex < 0) or (AnIndex >= Count) then exit(nil);
|
|
Result := TThreadEntry(FList[AnIndex]);
|
|
end;
|
|
|
|
function TThreads.GetEntryById(const AnID: Integer): TThreadEntry;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := Count - 1;
|
|
while i >= 0 do begin
|
|
Result := Entries[i];
|
|
if Result.ThreadId = AnID then
|
|
exit;
|
|
dec(i);
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TThreads.SetCurrentThreadId(AValue: Integer);
|
|
begin
|
|
if FCurrentThreadId = AValue then exit;
|
|
FCurrentThreadId := AValue;
|
|
end;
|
|
|
|
constructor TThreads.Create;
|
|
begin
|
|
FList := TList.Create;
|
|
end;
|
|
|
|
destructor TThreads.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FList);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TThreads.Assign(AnOther: TThreads);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Clear;
|
|
FCurrentThreadId := AnOther.FCurrentThreadId;
|
|
for i := 0 to AnOther.FList.Count-1 do
|
|
FList.Add(TThreadEntry(AnOther.FList[i]).CreateCopy);
|
|
end;
|
|
|
|
function TThreads.Count: Integer;
|
|
begin
|
|
Result := FList.Count;
|
|
end;
|
|
|
|
procedure TThreads.Clear;
|
|
begin
|
|
while FList.Count > 0 do begin
|
|
TThreadEntry(Flist[0]).Free;
|
|
FList.Delete(0);
|
|
end;
|
|
end;
|
|
|
|
procedure TThreads.Add(AThread: TThreadEntry);
|
|
begin
|
|
FList.Add(AThread.CreateCopy);
|
|
if FList.Count = 1 then
|
|
FCurrentThreadId := AThread.ThreadId; // TODO: this should never be needed?
|
|
end;
|
|
|
|
procedure TThreads.Remove(AThread: TThreadEntry);
|
|
begin
|
|
FList.Remove(AThread);
|
|
if FCurrentThreadId = AThread.ThreadId then begin
|
|
if FList.Count > 0 then
|
|
FCurrentThreadId := Entries[0].ThreadId
|
|
else
|
|
FCurrentThreadId := 0;
|
|
end;
|
|
AThread.Free;
|
|
end;
|
|
|
|
function TThreads.CreateEntry(const AnAdress: TDbgPtr; const AnArguments: TStrings;
|
|
const AFunctionName: String; const FileName, FullName: String; const ALine: Integer;
|
|
const AThreadId: Integer; const AThreadName: String; const AThreadState: String;
|
|
AState: TDebuggerDataState): TThreadEntry;
|
|
begin
|
|
Result := TThreadEntry.Create(AnAdress, AnArguments, AFunctionName, FileName,
|
|
FullName, ALine, AThreadId, AThreadName, AThreadState, AState);
|
|
end;
|
|
|
|
procedure TThreads.SetValidity(AValidity: TDebuggerDataState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{ TThreadsMonitor }
|
|
|
|
function TThreadsMonitor.GetSupplier: TThreadsSupplier;
|
|
begin
|
|
Result := TThreadsSupplier(inherited Supplier);
|
|
end;
|
|
|
|
procedure TThreadsMonitor.SetSupplier(AValue: TThreadsSupplier);
|
|
begin
|
|
inherited Supplier := AValue;
|
|
end;
|
|
|
|
function TThreadsMonitor.CreateThreads: TThreads;
|
|
begin
|
|
Result := TThreads.Create;
|
|
end;
|
|
|
|
constructor TThreadsMonitor.Create;
|
|
begin
|
|
FThreads := CreateThreads;
|
|
inherited Create;
|
|
end;
|
|
|
|
destructor TThreadsMonitor.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FThreads);
|
|
end;
|
|
|
|
{ TRegistersMonitor }
|
|
|
|
function TRegistersMonitor.GetSupplier: TRegisterSupplier;
|
|
begin
|
|
Result := TRegisterSupplier(inherited Supplier);
|
|
end;
|
|
|
|
procedure TRegistersMonitor.SetSupplier(AValue: TRegisterSupplier);
|
|
begin
|
|
inherited Supplier := AValue;
|
|
end;
|
|
|
|
function TRegistersMonitor.CreateRegistersList: TRegistersList;
|
|
begin
|
|
Result := TRegistersList.Create;
|
|
end;
|
|
|
|
constructor TRegistersMonitor.Create;
|
|
begin
|
|
inherited Create;
|
|
FRegistersList := CreateRegistersList;
|
|
FRegistersList.AddReference;
|
|
end;
|
|
|
|
destructor TRegistersMonitor.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
ReleaseRefAndNil(FRegistersList);
|
|
end;
|
|
|
|
{ TDebuggerDataHandler }
|
|
|
|
procedure TDebuggerDataHandler.DoStateEnterPause;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataHandler.DoStateLeavePause;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataHandler.DoStateLeavePauseClean;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataHandler.DoStateChangeEx(const AOldState, ANewState: TDBGState);
|
|
begin
|
|
FNotifiedState := ANewState;
|
|
FOldState := AOldState;
|
|
DebugLnEnter(DBG_DATA_MONITORS, [ClassName, ': >>ENTER: ', ClassName, '.DoStateChange New-State=', dbgs(FNotifiedState)]);
|
|
|
|
if FNotifiedState in [dsPause, dsInternalPause]
|
|
then begin
|
|
// typical: Clear and reload data
|
|
if not(AOldState in [dsPause, dsInternalPause] )
|
|
then DoStateEnterPause;
|
|
end
|
|
else
|
|
if (AOldState in [dsPause, dsInternalPause, dsNone] )
|
|
then begin
|
|
// dsIdle happens after dsStop
|
|
if (FNotifiedState in [dsRun, dsInit, dsIdle]) or (AOldState = dsNone)
|
|
then begin
|
|
// typical: finalize snapshot and clear data.
|
|
DoStateLeavePauseClean;
|
|
end
|
|
else begin
|
|
// typical: finalize snapshot
|
|
// Do *not* clear data. Objects may be in use (e.g. dsError)
|
|
DoStateLeavePause;
|
|
end;
|
|
end
|
|
else
|
|
if (AOldState in [dsStop]) and (FNotifiedState = dsIdle)
|
|
then begin
|
|
// stopped // typical: finalize snapshot and clear data.
|
|
DoStateLeavePauseClean;
|
|
end;
|
|
DebugLnExit(DBG_DATA_MONITORS, [ClassName, ': <<EXIT: ', ClassName, '.DoStateChange']);
|
|
end;
|
|
|
|
{ TRegisterSupplier }
|
|
|
|
function TRegisterSupplier.GetCurrentRegistersList: TRegistersList;
|
|
begin
|
|
Result := nil;
|
|
if Monitor <> nil then
|
|
Result := Monitor.RegistersList;
|
|
end;
|
|
|
|
function TRegisterSupplier.GetMonitor: TRegistersMonitor;
|
|
begin
|
|
Result := TRegistersMonitor(inherited Monitor);
|
|
end;
|
|
|
|
procedure TRegisterSupplier.SetMonitor(AValue: TRegistersMonitor);
|
|
begin
|
|
inherited Monitor := AValue;
|
|
end;
|
|
|
|
procedure TRegisterSupplier.RequestData(ARegisters: TRegisters);
|
|
begin
|
|
ARegisters.SetDataValidity(ddsInvalid);
|
|
end;
|
|
|
|
{ TLocalsValue }
|
|
|
|
procedure TLocalsValue.DoAssign(AnOther: TDbgEntityValue);
|
|
begin
|
|
inherited DoAssign(AnOther);
|
|
FName := TLocalsValue(AnOther).FName;
|
|
FValue := TLocalsValue(AnOther).FValue;
|
|
end;
|
|
|
|
{ TLocalsListBase }
|
|
|
|
function TLocalsList.GetEntry(AThreadId, AStackFrame: Integer): TLocals;
|
|
begin
|
|
Result := TLocals(inherited Entries[AThreadId, AStackFrame]);
|
|
end;
|
|
|
|
function TLocalsList.GetEntryByIdx(AnIndex: Integer): TLocals;
|
|
begin
|
|
Result := TLocals(inherited EntriesByIdx[AnIndex]);
|
|
end;
|
|
|
|
{ TLocalsBase }
|
|
|
|
function TLocals.GetEntry(AnIndex: Integer): TLocalsValue;
|
|
begin
|
|
Result := TLocalsValue(inherited Entries[AnIndex]);
|
|
end;
|
|
|
|
function TLocals.GetName(const AnIndex: Integer): String;
|
|
begin
|
|
Result := Entries[AnIndex].Name;
|
|
end;
|
|
|
|
function TLocals.GetValue(const AnIndex: Integer): String;
|
|
begin
|
|
Result := Entries[AnIndex].Value;
|
|
end;
|
|
|
|
function TLocals.CreateEntry: TDbgEntityValue;
|
|
begin
|
|
Result := TLocalsValue.Create;
|
|
end;
|
|
|
|
procedure TLocals.Add(const AName, AValue: String);
|
|
var
|
|
v: TLocalsValue;
|
|
begin
|
|
assert(not Immutable, 'TLocalsBase.Add Immutable');
|
|
v := TLocalsValue(CreateEntry);
|
|
v.FName := AName;
|
|
v.FValue := AValue;
|
|
inherited Add(v);
|
|
end;
|
|
|
|
procedure TLocals.SetDataValidity(AValidity: TDebuggerDataState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
function TLocals.Count: Integer;
|
|
begin
|
|
Result := inherited Count;
|
|
end;
|
|
|
|
{ TRegisterDisplayValue }
|
|
|
|
function TRegisterDisplayValue.GetValue(ADispFormat: TRegisterDisplayFormat): String;
|
|
const Digits = '01234567';
|
|
function IntToBase(Val, Base: QWord): String;
|
|
var
|
|
M: Integer;
|
|
begin
|
|
Result := '';
|
|
case Base of
|
|
2: M := 1;
|
|
8: M := 7;
|
|
end;
|
|
while Val > 0 do begin
|
|
Result := Digits[1 + (Val and m)] + Result;
|
|
Val := Val div Base;
|
|
end;
|
|
end;
|
|
begin
|
|
Result := '';
|
|
if not(ADispFormat in FSupportedDispFormats) then exit;
|
|
if (ADispFormat in [rdDefault, rdRaw]) or not (rdvHasNum in FFlags) then begin
|
|
Result := FStringValue;
|
|
exit;
|
|
end;
|
|
case ADispFormat of
|
|
rdHex: Result := IntToHex(FNumValue, FSize * 2);
|
|
rdBinary: Result := IntToBase(FNumValue, 2);
|
|
rdOctal: Result := IntToBase(FNumValue, 8);
|
|
rdDecimal: Result := IntToStr(FNumValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TRegisterDisplayValue.Assign(AnOther: TRegisterDisplayValue);
|
|
begin
|
|
FStringValue := AnOther.FStringValue;
|
|
FNumValue := AnOther.FNumValue;
|
|
FFlags := AnOther.FFlags;
|
|
FSize := AnOther.FSize;
|
|
FSupportedDispFormats := AnOther.FSupportedDispFormats;
|
|
end;
|
|
|
|
procedure TRegisterDisplayValue.SetAsNum(AValue: QWord; ASize: Integer);
|
|
begin
|
|
if FNumValue = AValue then Exit;
|
|
FNumValue := AValue;
|
|
FSize := ASize;
|
|
Include(FFlags, rdvHasNum);
|
|
end;
|
|
|
|
procedure TRegisterDisplayValue.SetAsText(AValue: String);
|
|
begin
|
|
FStringValue := AValue;
|
|
end;
|
|
|
|
procedure TRegisterDisplayValue.AddFormats(AFormats: TRegisterDisplayFormats);
|
|
begin
|
|
FSupportedDispFormats := FSupportedDispFormats + AFormats;
|
|
end;
|
|
|
|
{ TRegisterValue }
|
|
|
|
function TRegisterValue.GetValue: String;
|
|
var
|
|
v: TRegisterDisplayValue;
|
|
begin
|
|
v := GetValueObject();
|
|
if v <> nil then begin
|
|
Result := v.Value[FDisplayFormat];
|
|
exit;
|
|
end;
|
|
|
|
Result := '';
|
|
DoValueNotEvaluated;
|
|
end;
|
|
|
|
function TRegisterValue.GetHasValue: Boolean;
|
|
begin
|
|
Result := GetValueObject <> nil;
|
|
end;
|
|
|
|
function TRegisterValue.GetHasValueFormat(ADispFormat: TRegisterDisplayFormat): Boolean;
|
|
begin
|
|
Result := GetValueObject(ADispFormat) <> nil;
|
|
end;
|
|
|
|
function TRegisterValue.GetValueObj: TRegisterDisplayValue;
|
|
begin
|
|
Result := GetValueObject(True);
|
|
end;
|
|
|
|
function TRegisterValue.GetValueObjFormat(ADispFormat: TRegisterDisplayFormat): TRegisterDisplayValue;
|
|
begin
|
|
Result := GetValueObject(ADispFormat, True);
|
|
end;
|
|
|
|
procedure TRegisterValue.SetDisplayFormat(AValue: TRegisterDisplayFormat);
|
|
var
|
|
Old: TRegisterDisplayFormat;
|
|
begin
|
|
assert(not Immutable, 'TRegisterValue.SetDisplayFormat: not Immutable');
|
|
if FDisplayFormat = AValue then Exit;
|
|
Old := FDisplayFormat;
|
|
FDisplayFormat := AValue;
|
|
DoDisplayFormatChanged(Old);
|
|
end;
|
|
|
|
procedure TRegisterValue.SetValue(AValue: String);
|
|
var
|
|
v: TRegisterDisplayValue;
|
|
begin
|
|
assert(not Immutable, 'TRegisterValue.SetValue: not Immutable');
|
|
v := GetValueObject(True);
|
|
v.FStringValue := AValue;
|
|
end;
|
|
|
|
function TRegisterValue.GetValueObject(ACreateNew: Boolean): TRegisterDisplayValue;
|
|
begin
|
|
Result := GetValueObject(FDisplayFormat, ACreateNew);
|
|
end;
|
|
|
|
function TRegisterValue.GetValueObject(ADispFormat: TRegisterDisplayFormat;
|
|
ACreateNew: Boolean): TRegisterDisplayValue;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to length(FValues) - 1 do
|
|
if ADispFormat in FValues[i].SupportedDispFormats then begin
|
|
Result := FValues[i];
|
|
exit;
|
|
end;
|
|
|
|
if not ACreateNew then begin
|
|
Result := nil;
|
|
exit;
|
|
end;
|
|
|
|
assert(not Immutable, 'TRegisterValue.GetValueObject: not Immutable');
|
|
Result := TRegisterDisplayValue.Create;
|
|
Result.FSupportedDispFormats := [ADispFormat];
|
|
i := length(FValues);
|
|
SetLength(FValues, i + 1);
|
|
FValues[i] := Result;
|
|
end;
|
|
|
|
procedure TRegisterValue.SetDataValidity(AValidity: TDebuggerDataState);
|
|
var
|
|
Old: TDebuggerDataState;
|
|
begin
|
|
assert(not Immutable, 'TRegisterValue.SetDataValidity: not Immutable');
|
|
if FDataValidity = AValidity then exit;
|
|
Old := FDataValidity;
|
|
FDataValidity := AValidity;
|
|
if AValidity = ddsUnknown then
|
|
ClearDispValues;
|
|
DoDataValidityChanged(Old);
|
|
end;
|
|
|
|
procedure TRegisterValue.ClearDispValues;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to Length(FValues) - 1 do
|
|
FValues[i].Free;
|
|
FValues := nil;
|
|
end;
|
|
|
|
procedure TRegisterValue.DoAssign(AnOther: TDbgEntityValue);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
inherited DoAssign(AnOther);
|
|
FDataValidity := TRegisterValue(AnOther).FDataValidity;
|
|
FDisplayFormat := TRegisterValue(AnOther).FDisplayFormat;
|
|
FName := TRegisterValue(AnOther).FName;
|
|
SetLength(FValues, length(TRegisterValue(AnOther).FValues));
|
|
for i := 0 to length(TRegisterValue(AnOther).FValues) - 1 do begin
|
|
FValues[i] := TRegisterDisplayValue.Create;
|
|
FValues[i].Assign(TRegisterValue(AnOther).FValues[i]);
|
|
end;
|
|
end;
|
|
|
|
procedure TRegisterValue.DoDataValidityChanged(AnOldValidity: TDebuggerDataState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TRegisterValue.DoDisplayFormatChanged(AnOldFormat: TRegisterDisplayFormat);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TRegisterValue.DoValueNotEvaluated;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
destructor TRegisterValue.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
ClearDispValues;
|
|
end;
|
|
|
|
{ TRegisters }
|
|
|
|
function TRegisters.GetEntry(AnIndex: Integer): TRegisterValue;
|
|
begin
|
|
Result := TRegisterValue(inherited Entries[AnIndex]);
|
|
end;
|
|
|
|
function TRegisters.GetEntryByName(const AName: String): TRegisterValue;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to Count - 1 do begin
|
|
Result := Entries[i];
|
|
if Result.Name = AName then
|
|
exit;
|
|
end;
|
|
|
|
assert(not Immutable, 'TRegisters.GetEntryByName: not Immutable');
|
|
Result := TRegisterValue(CreateEntry);
|
|
Result.FName := AName;
|
|
Add(Result);
|
|
end;
|
|
|
|
procedure TRegisters.SetDataValidity(AValue: TDebuggerDataState);
|
|
var
|
|
Old: TDebuggerDataState;
|
|
begin
|
|
assert(not Immutable, 'TRegisters.SetDataValidity: not Immutable');
|
|
if FDataValidity = AValue then Exit;
|
|
Old := FDataValidity;
|
|
FDataValidity := AValue;
|
|
DoDataValidityChanged(Old);
|
|
end;
|
|
|
|
function TRegisters.CreateEntry: TDbgEntityValue;
|
|
begin
|
|
assert(not Immutable, 'TRegisters.CreateEntry: not Immutable');
|
|
Result := TRegisterValue.Create;
|
|
end;
|
|
|
|
procedure TRegisters.DoDataValidityChanged(AnOldValidity: TDebuggerDataState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TRegisters.InvalidateItems;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
DataValidity := ddsUnknown;
|
|
for i := 0 to (inherited Count) - 1 do // The current count, without triggering request.
|
|
Entries[i].DataValidity := ddsUnknown;
|
|
end;
|
|
|
|
function TRegisters.Count: Integer;
|
|
begin
|
|
if FDataValidity = ddsValid then
|
|
Result := inherited Count
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
{ TRegistersList }
|
|
|
|
function TRegistersList.GetEntry(AThreadId, AStackFrame: Integer): TRegisters;
|
|
begin
|
|
Result := TRegisters(inherited Entries[AThreadId, AStackFrame]);
|
|
end;
|
|
|
|
function TRegistersList.GetEntryByIdx(AnIndex: Integer): TRegisters;
|
|
begin
|
|
Result := TRegisters(inherited EntriesByIdx[AnIndex]);
|
|
end;
|
|
|
|
procedure TRegistersList.InvalidateItems;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Assert(not Immutable, 'TRegisterList.InvalidateItems Immutable');
|
|
if Count = 0 then
|
|
exit;
|
|
for i := 0 to Count-1 do begin
|
|
EntriesByIdx[i].InvalidateItems;
|
|
end;
|
|
DoCleared;
|
|
end;
|
|
|
|
{ TCallStackBase }
|
|
|
|
function TCallStackBase.GetNewCurrentIndex: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TCallStackBase.GetCount: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TCallStackBase.GetCurrent: Integer;
|
|
begin
|
|
Result := FCurrent;
|
|
end;
|
|
|
|
procedure TCallStackBase.SetCurrent(AValue: Integer);
|
|
begin
|
|
FCurrent := AValue;
|
|
end;
|
|
|
|
function TCallStackBase.GetHighestUnknown: Integer;
|
|
begin
|
|
Result := -1;
|
|
end;
|
|
|
|
function TCallStackBase.GetLowestUnknown: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
constructor TCallStackBase.Create;
|
|
begin
|
|
FThreadId := -1;
|
|
FCurrent := -1;
|
|
inherited;
|
|
end;
|
|
|
|
function TCallStackBase.CreateCopy: TCallStackBase;
|
|
begin
|
|
Result := TCallStackBase.Create;
|
|
Result.Assign(Self);
|
|
end;
|
|
|
|
procedure TCallStackBase.Assign(AnOther: TCallStackBase);
|
|
begin
|
|
ThreadId := AnOther.ThreadId;
|
|
FCurrent := AnOther.FCurrent;
|
|
end;
|
|
|
|
procedure TCallStackBase.SetCountValidity(AValidity: TDebuggerDataState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TCallStackBase.SetHasAtLeastCountInfo(AValidity: TDebuggerDataState;
|
|
AMinCount: Integer);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TCallStackBase.SetCurrentValidity(AValidity: TDebuggerDataState);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{ TRunningProcessInfo }
|
|
|
|
constructor TRunningProcessInfo.Create(APID: Cardinal; const AImageName: string);
|
|
begin
|
|
self.PID := APID;
|
|
self.ImageName := AImageName;
|
|
end;
|
|
|
|
{ TDebuggerDataMonitor }
|
|
|
|
procedure TDebuggerDataMonitor.SetSupplier(const AValue: TDebuggerDataSupplier);
|
|
begin
|
|
if FSupplier = AValue then exit;
|
|
Assert((FSupplier=nil) or (AValue=nil), 'TDebuggerDataMonitor.Supplier already set');
|
|
if FSupplier <> nil then FSupplier.Monitor := nil;
|
|
FSupplier := AValue;
|
|
if FSupplier <> nil then FSupplier.Monitor:= self;
|
|
|
|
DoNewSupplier;
|
|
end;
|
|
|
|
procedure TDebuggerDataMonitor.DoModified;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataMonitor.DoNewSupplier;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
destructor TDebuggerDataMonitor.Destroy;
|
|
begin
|
|
Supplier := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDebuggerDataMonitor.DoBeginUpdate;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataMonitor.DoEndUpdate;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataMonitor.BeginUpdate;
|
|
begin
|
|
inc(FUpdateCount);
|
|
if FUpdateCount = 1 then
|
|
DoBeginUpdate;
|
|
end;
|
|
|
|
procedure TDebuggerDataMonitor.EndUpdate;
|
|
begin
|
|
assert(FUpdateCount > 0, 'TDebuggerDataMonitor.EndUpdate: FUpdateCount > 0');
|
|
dec(FUpdateCount);
|
|
if FUpdateCount = 0 then
|
|
DoEndUpdate;
|
|
end;
|
|
|
|
function TDebuggerDataMonitor.IsUpdating: Boolean;
|
|
begin
|
|
Result := FUpdateCount > 0;
|
|
end;
|
|
|
|
{ TDebuggerDataSupplierBase }
|
|
|
|
procedure TDebuggerDataSupplierBase.DoStateLeavePauseClean;
|
|
begin
|
|
DoStateLeavePause;
|
|
end;
|
|
|
|
constructor TDebuggerDataSupplierBase.Create(const ADebugger: TDebuggerIntf);
|
|
begin
|
|
FDebugger := ADebugger;
|
|
inherited Create;
|
|
end;
|
|
|
|
{ TDebuggerDataSupplier }
|
|
|
|
procedure TDebuggerDataSupplier.SetMonitor(const AValue: TDebuggerDataMonitor);
|
|
begin
|
|
if FMonitor = AValue then exit;
|
|
Assert((FMonitor=nil) or (AValue=nil), 'TDebuggerDataSupplier.Monitor already set');
|
|
FMonitor := AValue;
|
|
DoNewMonitor;
|
|
end;
|
|
|
|
procedure TDebuggerDataSupplier.DoNewMonitor;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerDataSupplier.DoStateChange(const AOldState: TDBGState);
|
|
begin
|
|
if (Debugger = nil) then Exit;
|
|
DoStateChangeEx(AOldState, Debugger.State);
|
|
if Monitor <> nil then
|
|
Monitor.DoStateChangeEx(AOldState, FDebugger.State);
|
|
end;
|
|
|
|
destructor TDebuggerDataSupplier.Destroy;
|
|
begin
|
|
if FMonitor <> nil then FMonitor.Supplier := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDebuggerDataSupplier.BeginUpdate;
|
|
begin
|
|
inc(FUpdateCount);
|
|
if FUpdateCount = 1 then
|
|
DoBeginUpdate;
|
|
end;
|
|
|
|
procedure TDebuggerDataSupplier.EndUpdate;
|
|
begin
|
|
assert(FUpdateCount > 0, 'TDebuggerDataSupplier.EndUpdate: FUpdateCount > 0');
|
|
dec(FUpdateCount);
|
|
if FUpdateCount = 0 then
|
|
DoEndUpdate;
|
|
end;
|
|
|
|
function TDebuggerDataSupplier.IsUpdating: Boolean;
|
|
begin
|
|
Result := FUpdateCount > 0;
|
|
end;
|
|
|
|
procedure TDebuggerDataSupplier.DoBeginUpdate;
|
|
begin
|
|
FMonitor.BeginUpdate;
|
|
end;
|
|
|
|
procedure TDebuggerDataSupplier.DoEndUpdate;
|
|
begin
|
|
FMonitor.EndUpdate;
|
|
end;
|
|
|
|
{ ===========================================================================
|
|
TBaseBreakPoint
|
|
=========================================================================== }
|
|
|
|
function TBaseBreakPoint.GetAddress: TDBGPtr;
|
|
begin
|
|
Result := FAddress;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetKind: TDBGBreakPointKind;
|
|
begin
|
|
Result := FKind;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetKind(const AValue: TDBGBreakPointKind);
|
|
begin
|
|
if FKind <> AValue
|
|
then begin
|
|
FKind := AValue;
|
|
Changed;
|
|
MarkPropertyChanged(ciKind);
|
|
end;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetAddress(const AValue: TDBGPtr);
|
|
begin
|
|
if FAddress <> AValue then
|
|
begin
|
|
FAddress := AValue;
|
|
Changed;
|
|
MarkPropertyChanged(ciLocation);
|
|
end;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetWatchData: String;
|
|
begin
|
|
Result := FWatchData;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetWatchScope: TDBGWatchPointScope;
|
|
begin
|
|
Result := FWatchScope;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetWatchKind: TDBGWatchPointKind;
|
|
begin
|
|
Result := FWatchKind;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.AssignLocationTo(Dest: TPersistent);
|
|
var
|
|
DestBreakPoint: TBaseBreakPoint absolute Dest;
|
|
begin
|
|
DestBreakPoint.SetLocation(FSource, FLine);
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.AssignTo(Dest: TPersistent);
|
|
var
|
|
DestBreakPoint: TBaseBreakPoint absolute Dest;
|
|
begin
|
|
// updatelock is set in source.assignto
|
|
if Dest is TBaseBreakPoint
|
|
then begin
|
|
DestBreakPoint.SetKind(FKind);
|
|
DestBreakPoint.SetWatch(FWatchData, FWatchScope, FWatchKind);
|
|
DestBreakPoint.SetAddress(FAddress);
|
|
AssignLocationTo(DestBreakPoint);
|
|
DestBreakPoint.SetBreakHitCount(FBreakHitCount);
|
|
DestBreakPoint.SetExpression(FExpression);
|
|
DestBreakPoint.SetEnabled(FEnabled);
|
|
DestBreakPoint.InitialEnabled := FInitialEnabled;
|
|
end
|
|
else inherited;
|
|
end;
|
|
|
|
constructor TBaseBreakPoint.Create(ACollection: TCollection);
|
|
begin
|
|
FPropertiesChanged := [ciCreated];
|
|
FAddress := 0;
|
|
FSource := '';
|
|
FLine := -1;
|
|
FValid := vsUnknown;
|
|
FEnabled := False;
|
|
FHitCount := 0;
|
|
FBreakHitCount := 0;
|
|
FExpression := '';
|
|
FInitialEnabled := False;
|
|
FKind := bpkSource;
|
|
inherited Create(ACollection);
|
|
AddReference;
|
|
end;
|
|
|
|
destructor TBaseBreakPoint.Destroy;
|
|
begin
|
|
FPropertiesChanged := []; // Do not sent old changes
|
|
if not IsUpdating then
|
|
MarkPropertyChanged(ciDestroy);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetPendingToValid(const AValue: TValidState);
|
|
begin
|
|
assert(Valid in [vsPending, vsUnknown], 'Can only change state if pending');
|
|
SetValid(AValue);
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.MarkPropertyChanged(AChanged: TDbgBpChangeIndicator);
|
|
begin
|
|
MarkPropertiesChanged([AChanged]);
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.MarkPropertiesChanged(AChanged: TDbgBpChangeIndicators
|
|
);
|
|
var
|
|
c: TDbgBpChangeIndicators;
|
|
begin
|
|
FPropertiesChanged := FPropertiesChanged + AChanged;
|
|
if IsUpdating or FInPropertiesChanged then
|
|
exit;
|
|
FInPropertiesChanged := True;
|
|
try
|
|
while FPropertiesChanged <> [] do begin
|
|
c := FPropertiesChanged;
|
|
FPropertiesChanged := [];
|
|
DoPropertiesChanged(c);
|
|
end;
|
|
finally
|
|
FInPropertiesChanged := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.DoPropertiesChanged(AChanged: TDbgBpChangeIndicators);
|
|
begin
|
|
if ciEnabled in AChanged then
|
|
DoEnableChange;
|
|
if ciCondition in AChanged then
|
|
DoExpressionChange;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.DoEnableChange;
|
|
begin
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.DoExpressionChange;
|
|
begin
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.DoHit(const ACount: Integer; var AContinue: Boolean );
|
|
begin
|
|
SetHitCount(ACount);
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetBreakHitCount: Integer;
|
|
begin
|
|
Result := FBreakHitCount;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetEnabled: Boolean;
|
|
begin
|
|
Result := FEnabled;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetExpression: String;
|
|
begin
|
|
Result := FExpression;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetHitCount: Integer;
|
|
begin
|
|
Result := FHitCount;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetLine: Integer;
|
|
begin
|
|
Result := FLine;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetSource: String;
|
|
begin
|
|
Result := FSource;
|
|
end;
|
|
|
|
function TBaseBreakPoint.GetValid: TValidState;
|
|
begin
|
|
Result := FValid;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.DoEndUpdate;
|
|
begin
|
|
inherited DoEndUpdate;
|
|
MarkPropertiesChanged([]);
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetBreakHitCount(const AValue: Integer);
|
|
begin
|
|
if FBreakHitCount <> AValue
|
|
then begin
|
|
FBreakHitCount := AValue;
|
|
Changed;
|
|
MarkPropertyChanged(ciHitCount);
|
|
end;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetEnabled (const AValue: Boolean );
|
|
begin
|
|
if FEnabled <> AValue
|
|
then begin
|
|
FEnabled := AValue;
|
|
MarkPropertyChanged(ciEnabled);
|
|
end;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetExpression (const AValue: String );
|
|
begin
|
|
if FExpression <> AValue
|
|
then begin
|
|
FExpression := AValue;
|
|
MarkPropertyChanged(ciCondition);
|
|
end;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetHitCount (const AValue: Integer );
|
|
begin
|
|
if FHitCount <> AValue
|
|
then begin
|
|
FHitCount := AValue;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetInitialEnabled(const AValue: Boolean);
|
|
begin
|
|
if FInitialEnabled=AValue then exit;
|
|
FInitialEnabled:=AValue;
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetLocation (const ASource: String; const ALine: Integer );
|
|
begin
|
|
if (FSource = ASource) and (FLine = ALine) then exit;
|
|
FSource := ASource;
|
|
FLine := ALine;
|
|
Changed;
|
|
MarkPropertyChanged(ciLocation);
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetWatch(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind);
|
|
begin
|
|
if (AData = FWatchData) and (AScope = FWatchScope) and (AKind = FWatchKind) then exit;
|
|
FWatchData := AData;
|
|
FWatchScope := AScope;
|
|
FWatchKind := AKind;
|
|
Changed;
|
|
MarkPropertyChanged(ciLocation);
|
|
end;
|
|
|
|
procedure TBaseBreakPoint.SetValid(const AValue: TValidState );
|
|
begin
|
|
if FValid <> AValue
|
|
then begin
|
|
FValid := AValue;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TDBGBreakPoint }
|
|
{ =========================================================================== }
|
|
|
|
constructor TDBGBreakPoint.Create (ACollection: TCollection );
|
|
begin
|
|
FSlave := nil;
|
|
inherited Create(ACollection);
|
|
end;
|
|
|
|
destructor TDBGBreakPoint.Destroy;
|
|
var
|
|
SBP: TBaseBreakPoint;
|
|
begin
|
|
SBP := FSlave;
|
|
FSlave := nil;
|
|
if SBP <> nil
|
|
then SBP.DoChanged; // In case UpdateCount 0
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.Hit(var ACanContinue: Boolean);
|
|
var
|
|
cnt: Integer;
|
|
begin
|
|
cnt := HitCount + 1;
|
|
if BreakHitcount > 0
|
|
then ACanContinue := cnt < BreakHitcount;
|
|
DoHit(cnt, ACanContinue);
|
|
if Assigned(FSlave)
|
|
then FSlave.DoHit(cnt, ACanContinue);
|
|
Debugger.DoBreakpointHit(Self, ACanContinue)
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.DoChanged;
|
|
begin
|
|
inherited DoChanged;
|
|
if FSlave <> nil
|
|
then FSlave.Changed;
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.DoStateChange(const AOldState: TDBGState);
|
|
begin
|
|
if Debugger.State <> dsStop then Exit;
|
|
SetValid(vsUnknown);
|
|
|
|
if not (AOldState in [dsIdle, dsNone]) then Exit;
|
|
|
|
BeginUpdate;
|
|
try
|
|
SetLocation(FSource, Line);
|
|
Enabled := InitialEnabled;
|
|
SetHitCount(0);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.DoLogMessage(const AMessage: String);
|
|
begin
|
|
Debugger.DoDbgEvent(ecBreakpoint, etBreakpointMessage, 'Breakpoint Message: ' + AMessage);
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.DoLogCallStack(const Limit: Integer);
|
|
const
|
|
Spacing = ' ';
|
|
var
|
|
CallStack: TCallStackBase;
|
|
I, Count: Integer;
|
|
Entry: TCallStackEntry;
|
|
StackString: String;
|
|
begin
|
|
Debugger.SetState(dsInternalPause);
|
|
CallStack := Debugger.CallStack.CurrentCallStackList.EntriesForThreads[Debugger.Threads.CurrentThreads.CurrentThreadId];
|
|
if Limit = 0 then
|
|
begin
|
|
Debugger.DoDbgEvent(ecBreakpoint, etBreakpointMessage, 'Breakpoint Call Stack: Log all stack frames');
|
|
Count := CallStack.Count;
|
|
CallStack.PrepareRange(0, Count);
|
|
end
|
|
else
|
|
begin
|
|
Debugger.DoDbgEvent(ecBreakpoint, etBreakpointMessage, Format('Breakpoint Call Stack: Log %d stack frames', [Limit]));
|
|
Count := CallStack.CountLimited(Limit);
|
|
CallStack.PrepareRange(0, Count);
|
|
end;
|
|
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
Entry := CallStack.Entries[I];
|
|
StackString := Spacing + Entry.Source;
|
|
if Entry.Source = '' then // we do not have a source file => just show an adress
|
|
StackString := Spacing + ':' + IntToHex(Entry.Address, 8);
|
|
StackString := StackString + ' ' + Entry.GetFunctionWithArg;
|
|
if line > 0 then
|
|
StackString := StackString + ' line ' + IntToStr(Entry.Line);
|
|
|
|
Debugger.DoDbgEvent(ecBreakpoint, etBreakpointStackDump, StackString);
|
|
end;
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.DoLogExpression(const AnExpression: String);
|
|
begin
|
|
// will be called while Debgger.State = dsRun => can not call Evaluate
|
|
end;
|
|
|
|
function TDBGBreakPoint.GetDebugger: TDebuggerIntf;
|
|
begin
|
|
Result := TDBGBreakPoints(Collection).FDebugger;
|
|
end;
|
|
|
|
function TDBGBreakPoint.GetDebuggerState: TDBGState;
|
|
begin
|
|
if Debugger <> nil then
|
|
Result := Debugger.State
|
|
else
|
|
Result := dsNone;
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.SetSlave(const ASlave : TBaseBreakPoint);
|
|
begin
|
|
Assert((FSlave = nil) or (ASlave = nil), 'TDBGBreakPoint.SetSlave already has a slave');
|
|
FSlave := ASlave;
|
|
end;
|
|
|
|
procedure TDBGBreakPoint.SetEnabled(const AValue: Boolean);
|
|
begin
|
|
if Enabled = AValue then exit;
|
|
inherited SetEnabled(AValue);
|
|
// feedback to IDEBreakPoint
|
|
if FSlave <> nil then FSlave.Enabled := AValue;
|
|
end;
|
|
|
|
{ TIdeBreakPointBase }
|
|
|
|
procedure TIdeBreakPointBase.SetMaster(AValue: TDBGBreakPoint);
|
|
begin
|
|
if FMaster = AValue then Exit;
|
|
if (FMaster <> nil) and IsUpdating then FMaster.EndUpdate;
|
|
FMaster := AValue;
|
|
if (FMaster <> nil) and IsUpdating then FMaster.BeginUpdate;
|
|
end;
|
|
|
|
procedure TIdeBreakPointBase.BeginUpdate;
|
|
begin
|
|
if (not IsUpdating) and (FMaster <> nil) then FMaster.BeginUpdate;
|
|
inherited BeginUpdate;
|
|
end;
|
|
|
|
procedure TIdeBreakPointBase.DoEndUpdate;
|
|
begin
|
|
inherited DoEndUpdate;
|
|
if FMaster <> nil then FMaster.EndUpdate;
|
|
end;
|
|
|
|
procedure TIdeBreakPointBase.ReleaseMaster;
|
|
begin
|
|
if FMaster <> nil
|
|
then begin
|
|
FMaster.Slave := nil;
|
|
ReleaseRefAndNil(FMaster);
|
|
end;
|
|
end;
|
|
|
|
destructor TIdeBreakPointBase.Destroy;
|
|
begin
|
|
ReleaseMaster;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TBaseBreakPoints }
|
|
{ =========================================================================== }
|
|
|
|
function TBaseBreakPoints.Add(const ASource: String; const ALine: Integer;
|
|
AnUpdating: Boolean): TBaseBreakPoint;
|
|
begin
|
|
Result := TBaseBreakPoint(inherited Add);
|
|
Result.BeginUpdate;
|
|
Result.SetKind(bpkSource);
|
|
Result.SetLocation(ASource, ALine);
|
|
if not AnUpdating then
|
|
Result.EndUpdate;
|
|
end;
|
|
|
|
function TBaseBreakPoints.Add(const AAddress: TDBGPtr; AnUpdating: Boolean
|
|
): TBaseBreakPoint;
|
|
begin
|
|
Result := TBaseBreakPoint(inherited Add);
|
|
Result.BeginUpdate;
|
|
Result.SetKind(bpkAddress);
|
|
Result.SetAddress(AAddress);
|
|
if not AnUpdating then
|
|
Result.EndUpdate;
|
|
end;
|
|
|
|
function TBaseBreakPoints.Add(const AData: String;
|
|
const AScope: TDBGWatchPointScope; const AKind: TDBGWatchPointKind;
|
|
AnUpdating: Boolean): TBaseBreakPoint;
|
|
begin
|
|
Result := TBaseBreakPoint(inherited Add);
|
|
Result.BeginUpdate;
|
|
Result.SetKind(bpkData);
|
|
Result.SetWatch(AData, AScope, AKind);
|
|
if not AnUpdating then
|
|
Result.EndUpdate;
|
|
end;
|
|
|
|
constructor TBaseBreakPoints.Create(const ABreakPointClass: TBaseBreakPointClass);
|
|
begin
|
|
inherited Create(ABreakPointClass);
|
|
end;
|
|
|
|
destructor TBaseBreakPoints.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TBaseBreakPoints.Clear;
|
|
begin
|
|
while Count > 0 do TBaseBreakPoint(GetItem(0)).ReleaseReference;
|
|
end;
|
|
|
|
function TBaseBreakPoints.Find(const ASource: String; const ALine: Integer): TBaseBreakPoint;
|
|
begin
|
|
Result := Find(ASource, ALine, nil);
|
|
end;
|
|
|
|
function TBaseBreakPoints.Find(const ASource: String; const ALine: Integer; const AIgnore: TBaseBreakPoint): TBaseBreakPoint;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
begin
|
|
Result := TBaseBreakPoint(GetItem(n));
|
|
if (Result.Kind = bpkSource)
|
|
and (Result.Line = ALine)
|
|
and (AIgnore <> Result)
|
|
and (CompareFilenames(Result.Source, ASource) = 0)
|
|
then Exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
function TBaseBreakPoints.Find(const AAddress: TDBGPtr): TBaseBreakPoint;
|
|
begin
|
|
Result := Find(AAddress, nil);
|
|
end;
|
|
|
|
function TBaseBreakPoints.Find(const AAddress: TDBGPtr; const AIgnore: TBaseBreakPoint): TBaseBreakPoint;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
begin
|
|
Result := TBaseBreakPoint(GetItem(n));
|
|
if (Result.Kind = bpkAddress)
|
|
and (Result.Address = AAddress)
|
|
and (AIgnore <> Result)
|
|
then Exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
function TBaseBreakPoints.Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind): TBaseBreakPoint;
|
|
begin
|
|
Result := Find(AData, AScope, AKind, nil);
|
|
end;
|
|
|
|
function TBaseBreakPoints.Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind; const AIgnore: TBaseBreakPoint): TBaseBreakPoint;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
begin
|
|
Result := TBaseBreakPoint(GetItem(n));
|
|
if (Result.Kind = bpkData)
|
|
and (Result.WatchData = AData)
|
|
and (Result.WatchScope = AScope)
|
|
and (Result.WatchKind = AKind)
|
|
and (AIgnore <> Result)
|
|
then Exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TDBGBreakPoints }
|
|
{ =========================================================================== }
|
|
|
|
function TDBGBreakPoints.Add(const ASource: String; const ALine: Integer;
|
|
AnUpdating: Boolean): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Add(ASource, ALine, AnUpdating));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Add(const AAddress: TDBGPtr; AnUpdating: Boolean
|
|
): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Add(AAddress, AnUpdating));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Add(const AData: String;
|
|
const AScope: TDBGWatchPointScope; const AKind: TDBGWatchPointKind;
|
|
AnUpdating: Boolean): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Add(AData, AScope, AKind, AnUpdating));
|
|
end;
|
|
|
|
constructor TDBGBreakPoints.Create(const ADebugger: TDebuggerIntf;
|
|
const ABreakPointClass: TDBGBreakPointClass);
|
|
begin
|
|
FDebugger := ADebugger;
|
|
inherited Create(ABreakPointClass);
|
|
end;
|
|
|
|
procedure TDBGBreakPoints.DoStateChange(const AOldState: TDBGState);
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
GetItem(n).DoStateChange(AOldState);
|
|
end;
|
|
|
|
function TDBGBreakPoints.Find(const ASource: String; const ALine: Integer): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Find(Asource, ALine, nil));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Find (const ASource: String; const ALine: Integer; const AIgnore: TDBGBreakPoint ): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Find(ASource, ALine, AIgnore));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Find(const AAddress: TDBGPtr): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Find(AAddress));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Find(const AAddress: TDBGPtr; const AIgnore: TDBGBreakPoint): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Find(AAddress, nil));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Find(AData, AScope, AKind, nil));
|
|
end;
|
|
|
|
function TDBGBreakPoints.Find(const AData: String; const AScope: TDBGWatchPointScope;
|
|
const AKind: TDBGWatchPointKind; const AIgnore: TDBGBreakPoint): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited Find(AData, AScope, AKind, AIgnore));
|
|
end;
|
|
|
|
function TDBGBreakPoints.GetItem (const AnIndex: Integer ): TDBGBreakPoint;
|
|
begin
|
|
Result := TDBGBreakPoint(inherited GetItem(AnIndex));
|
|
end;
|
|
|
|
procedure TDBGBreakPoints.SetItem (const AnIndex: Integer; const AValue: TDBGBreakPoint );
|
|
begin
|
|
inherited SetItem(AnIndex, AValue);
|
|
end;
|
|
|
|
{ TDBGField }
|
|
|
|
procedure TDBGField.IncRefCount;
|
|
begin
|
|
inc(FRefCount);
|
|
end;
|
|
|
|
procedure TDBGField.DecRefCount;
|
|
begin
|
|
dec(FRefCount);
|
|
if FRefCount <= 0
|
|
then Self.Free;
|
|
end;
|
|
|
|
constructor TDBGField.Create(const AName: String; ADBGType: TDBGType;
|
|
ALocation: TDBGFieldLocation; AFlags: TDBGFieldFlags; AClassName: String = '');
|
|
begin
|
|
inherited Create;
|
|
FName := AName;
|
|
FLocation := ALocation;
|
|
FDBGType := ADBGType;
|
|
FFlags := AFlags;
|
|
FRefCount := 0;
|
|
FClassName := AClassName;
|
|
end;
|
|
|
|
destructor TDBGField.Destroy;
|
|
begin
|
|
FreeAndNil(FDBGType);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TDBGFields }
|
|
|
|
constructor TDBGFields.Create;
|
|
begin
|
|
FList := TList.Create;
|
|
inherited;
|
|
end;
|
|
|
|
destructor TDBGFields.Destroy;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
Items[n].DecRefCount;
|
|
|
|
FreeAndNil(FList);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TDBGFields.Add(const AField: TDBGField);
|
|
begin
|
|
AField.IncRefCount;
|
|
FList.Add(AField);
|
|
end;
|
|
|
|
function TDBGFields.GetCount: Integer;
|
|
begin
|
|
Result := FList.Count;
|
|
end;
|
|
|
|
function TDBGFields.GetField(const AIndex: Integer): TDBGField;
|
|
begin
|
|
Result := TDBGField(FList[AIndex]);
|
|
end;
|
|
|
|
{ TDBGPTypes }
|
|
|
|
constructor TDBGTypes.Create;
|
|
begin
|
|
FList := TList.Create;
|
|
inherited;
|
|
end;
|
|
|
|
destructor TDBGTypes.Destroy;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
Items[n].Free;
|
|
|
|
FreeAndNil(FList);
|
|
inherited;
|
|
end;
|
|
|
|
function TDBGTypes.GetCount: Integer;
|
|
begin
|
|
Result := Flist.Count;
|
|
end;
|
|
|
|
function TDBGTypes.GetType(const AIndex: Integer): TDBGType;
|
|
begin
|
|
Result := TDBGType(FList[AIndex]);
|
|
end;
|
|
|
|
{ TDBGPType }
|
|
|
|
function TDBGType.GetFields: TDBGFields;
|
|
begin
|
|
if FFields = nil then
|
|
FFields := TDBGFields.Create;
|
|
Result := FFields;
|
|
end;
|
|
|
|
procedure TDBGType.Init;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
constructor TDBGType.Create(AKind: TDBGSymbolKind; const ATypeName: String);
|
|
begin
|
|
FKind := AKind;
|
|
FTypeName := ATypeName;
|
|
Init;
|
|
inherited Create;
|
|
end;
|
|
|
|
constructor TDBGType.Create(AKind: TDBGSymbolKind; const AArguments: TDBGTypes; AResult: TDBGType);
|
|
begin
|
|
FKind := AKind;
|
|
FArguments := AArguments;
|
|
FResult := AResult;
|
|
Init;
|
|
inherited Create;
|
|
end;
|
|
|
|
destructor TDBGType.Destroy;
|
|
begin
|
|
FreeAndNil(FResult);
|
|
FreeAndNil(FArguments);
|
|
FreeAndNil(FFields);
|
|
FreeAndNil(FMembers);
|
|
inherited;
|
|
end;
|
|
|
|
{ TWatchesSupplier }
|
|
|
|
procedure TWatchesSupplier.RequestData(AWatchValue: TWatchValueIntf);
|
|
begin
|
|
if FNotifiedState in [dsPause, dsInternalPause]
|
|
then InternalRequestData(AWatchValue)
|
|
else AWatchValue.SetValidity(ddsInvalid);
|
|
end;
|
|
|
|
procedure TWatchesSupplier.DoStateChange(const AOldState: TDBGState);
|
|
begin
|
|
// workaround for state changes during TWatchValue.GetValue
|
|
inc(DbgStateChangeCounter);
|
|
if DbgStateChangeCounter = high(DbgStateChangeCounter) then DbgStateChangeCounter := 0;
|
|
|
|
if (Debugger = nil) then Exit;
|
|
|
|
DoStateChangeEx(AOldState, Debugger.State);
|
|
if Monitor <> nil then
|
|
Monitor.DoStateChange(AOldState, Debugger.State);
|
|
end;
|
|
|
|
procedure TWatchesSupplier.InternalRequestData(AWatchValue: TWatchValueIntf);
|
|
begin
|
|
AWatchValue.SetValidity(ddsInvalid);
|
|
end;
|
|
|
|
constructor TWatchesSupplier.Create(const ADebugger: TDebuggerIntf);
|
|
begin
|
|
inherited Create(ADebugger);
|
|
FNotifiedState := dsNone;
|
|
end;
|
|
|
|
destructor TWatchesSupplier.Destroy;
|
|
begin
|
|
DoDestroy;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TLocalsSupplier }
|
|
|
|
function TLocalsSupplier.GetMonitor: TLocalsMonitor;
|
|
begin
|
|
Result := TLocalsMonitor(inherited Monitor);
|
|
end;
|
|
|
|
procedure TLocalsSupplier.SetMonitor(AValue: TLocalsMonitor);
|
|
begin
|
|
inherited Monitor := AValue;
|
|
end;
|
|
|
|
procedure TLocalsSupplier.TriggerInvalidateLocals;
|
|
begin
|
|
if Monitor <> nil then
|
|
Monitor.InvalidateLocals;
|
|
end;
|
|
|
|
procedure TLocalsSupplier.RequestData(ALocals: TLocals);
|
|
begin
|
|
ALocals.SetDataValidity(ddsInvalid)
|
|
end;
|
|
|
|
{ TLocalsMonitor }
|
|
|
|
function TLocalsMonitor.GetSupplier: TLocalsSupplier;
|
|
begin
|
|
Result := TLocalsSupplier(inherited Supplier);
|
|
end;
|
|
|
|
procedure TLocalsMonitor.SetSupplier(AValue: TLocalsSupplier);
|
|
begin
|
|
inherited Supplier := AValue;
|
|
end;
|
|
|
|
procedure TLocalsMonitor.InvalidateLocals;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{ TBaseLineInfo }
|
|
|
|
function TBaseLineInfo.GetSource(const AnIndex: integer): String;
|
|
begin
|
|
Result := '';
|
|
end;
|
|
|
|
function TBaseLineInfo.IndexOf(const ASource: String): integer;
|
|
begin
|
|
Result := -1;
|
|
end;
|
|
|
|
constructor TBaseLineInfo.Create;
|
|
begin
|
|
inherited Create;
|
|
end;
|
|
|
|
function TBaseLineInfo.HasAddress(const ASource: String; const ALine: Integer
|
|
): Boolean;
|
|
var
|
|
idx: Integer;
|
|
begin
|
|
idx := IndexOf(ASource);
|
|
if idx = -1
|
|
then Result := False
|
|
else Result := HasAddress(idx, ALine);
|
|
end;
|
|
|
|
function TBaseLineInfo.GetInfo(AAddress: TDbgPtr; out ASource, ALine, AOffset: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TBaseLineInfo.Request(const ASource: String);
|
|
begin
|
|
end;
|
|
|
|
procedure TBaseLineInfo.Cancel(const ASource: String);
|
|
begin
|
|
|
|
end;
|
|
|
|
function TBaseLineInfo.Count: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TBaseLineInfo.HasAddress(const AIndex: Integer; const ALine: Integer
|
|
): Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
{ TDBGLineInfo }
|
|
|
|
procedure TDBGLineInfo.Changed(ASource: String);
|
|
begin
|
|
DoChange(ASource);
|
|
end;
|
|
|
|
procedure TDBGLineInfo.DoChange(ASource: String);
|
|
begin
|
|
if Assigned(FOnChange) then FOnChange(Self, ASource);
|
|
end;
|
|
|
|
procedure TDBGLineInfo.DoStateChange(const AOldState: TDBGState);
|
|
begin
|
|
end;
|
|
|
|
constructor TDBGLineInfo.Create(const ADebugger: TDebuggerIntf);
|
|
begin
|
|
inherited Create;
|
|
FDebugger := ADebugger;
|
|
end;
|
|
|
|
{ TCallStackEntry }
|
|
|
|
function TCallStackEntry.GetArgumentCount: Integer;
|
|
begin
|
|
Result := FArguments.Count;
|
|
end;
|
|
|
|
function TCallStackEntry.GetArgumentName(const AnIndex: Integer): String;
|
|
begin
|
|
Result := FArguments.Names[AnIndex];
|
|
end;
|
|
|
|
function TCallStackEntry.GetArgumentValue(const AnIndex: Integer): String;
|
|
begin
|
|
Result := FArguments[AnIndex];
|
|
Result := GetPart('=', '', Result);
|
|
end;
|
|
|
|
function TCallStackEntry.GetFunctionName: String;
|
|
begin
|
|
Result := FFunctionName;
|
|
end;
|
|
|
|
function TCallStackEntry.GetSource: String;
|
|
begin
|
|
Result := '';
|
|
end;
|
|
|
|
function TCallStackEntry.GetValidity: TDebuggerDataState;
|
|
begin
|
|
Result := FValidity;
|
|
end;
|
|
|
|
procedure TCallStackEntry.SetValidity(AValue: TDebuggerDataState);
|
|
begin
|
|
FValidity := AValue;
|
|
end;
|
|
|
|
procedure TCallStackEntry.ClearLocation;
|
|
begin
|
|
InitFields(0, 0, nil, '', 0, Validity);
|
|
if Arguments <> nil then
|
|
Arguments.Clear;
|
|
end;
|
|
|
|
procedure TCallStackEntry.InitFields(const AIndex: Integer; const AnAddress: TDbgPtr;
|
|
const AnArguments: TStrings; const AFunctionName: String; const ALine: Integer;
|
|
AValidity: TDebuggerDataState);
|
|
begin
|
|
FIndex := AIndex;
|
|
FAddress := AnAddress;
|
|
if AnArguments <> nil
|
|
then FArguments.Assign(AnArguments);
|
|
FFunctionName := AFunctionName;
|
|
FLine := ALine;
|
|
FValidity := AValidity;
|
|
end;
|
|
|
|
constructor TCallStackEntry.Create;
|
|
begin
|
|
inherited Create;
|
|
FArguments := TStringlist.Create;
|
|
end;
|
|
|
|
function TCallStackEntry.CreateCopy: TCallStackEntry;
|
|
begin
|
|
Result := TCallStackEntry.Create;
|
|
Result.Assign(Self);
|
|
end;
|
|
|
|
destructor TCallStackEntry.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FArguments);
|
|
end;
|
|
|
|
procedure TCallStackEntry.Assign(AnOther: TCallStackEntry);
|
|
begin
|
|
FValidity := AnOther.FValidity;
|
|
FIndex := AnOther.FIndex;
|
|
FAddress := AnOther.FAddress;
|
|
FFunctionName := AnOther.FFunctionName;
|
|
FLine := AnOther.FLine;
|
|
FArguments.Assign(AnOther.FArguments);
|
|
end;
|
|
|
|
procedure TCallStackEntry.Init(const AnAddress: TDbgPtr; const AnArguments: TStrings;
|
|
const AFunctionName: String; const AUnitName, AClassName, AProcName, AFunctionArgs: String;
|
|
const ALine: Integer; AState: TDebuggerDataState);
|
|
begin
|
|
InitFields(FIndex, AnAddress, AnArguments, AFunctionName, ALine, AState);
|
|
end;
|
|
|
|
procedure TCallStackEntry.Init(const AnAddress: TDbgPtr; const AnArguments: TStrings;
|
|
const AFunctionName: String; const FileName, FullName: String; const ALine: Integer;
|
|
AState: TDebuggerDataState);
|
|
begin
|
|
InitFields(FIndex, AnAddress, AnArguments, AFunctionName, ALine, AState);
|
|
end;
|
|
|
|
function TCallStackEntry.GetFunctionWithArg: String;
|
|
var
|
|
S: String;
|
|
m: Integer;
|
|
begin
|
|
S := '';
|
|
for m := 0 to ArgumentCount - 1 do
|
|
begin
|
|
if S <> '' then
|
|
S := S + ', ';
|
|
S := S + ArgumentValues[m];
|
|
end;
|
|
if S <> '' then
|
|
S := '(' + S + ')';
|
|
Result := FunctionName + S;
|
|
end;
|
|
|
|
{ TCallStackList }
|
|
|
|
function TCallStackList.GetEntry(const AIndex: Integer): TCallStackBase;
|
|
begin
|
|
Result := TCallStackBase(FList[AIndex]);
|
|
end;
|
|
|
|
function TCallStackList.GetEntryForThread(const AThreadId: Integer): TCallStackBase;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := Count - 1;
|
|
while (i >= 0) and (TCallStackBase(FList[i]).ThreadId <> AThreadId) do dec(i);
|
|
if i >= 0
|
|
then Result := TCallStackBase(FList[i])
|
|
else Result := NewEntryForThread(AThreadId);
|
|
end;
|
|
|
|
function TCallStackList.NewEntryForThread(const AThreadId: Integer): TCallStackBase;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
constructor TCallStackList.Create;
|
|
begin
|
|
FList := TList.Create;
|
|
end;
|
|
|
|
destructor TCallStackList.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
Clear;
|
|
FreeAndNil(FList);
|
|
end;
|
|
|
|
procedure TCallStackList.Assign(AnOther: TCallStackList);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Clear;
|
|
for i := 0 to AnOther.FList.Count-1 do
|
|
FList.Add(TCallStackBase(AnOther.FList[i]).CreateCopy);
|
|
end;
|
|
|
|
procedure TCallStackList.Add(ACallStack: TCallStackBase);
|
|
begin
|
|
FList.Add(ACallStack);
|
|
end;
|
|
|
|
procedure TCallStackList.Clear;
|
|
begin
|
|
while FList.Count > 0 do begin
|
|
TObject(FList[0]).Free;
|
|
FList.Delete(0);
|
|
end;
|
|
end;
|
|
|
|
function TCallStackList.Count: Integer;
|
|
begin
|
|
Result := FList.Count;
|
|
end;
|
|
|
|
{ TCallStackSupplier }
|
|
|
|
procedure TCallStackSupplier.Changed;
|
|
begin
|
|
DebugLn(DBG_DATA_MONITORS, ['DebugDataMonitor: TCallStackSupplier.Changed']);
|
|
Monitor.DoModified;
|
|
end;
|
|
|
|
function TCallStackSupplier.GetCurrentCallStackList: TCallStackList;
|
|
begin
|
|
Result := nil;
|
|
if Monitor <> nil then
|
|
Result := Monitor.CallStackList;
|
|
end;
|
|
|
|
function TCallStackSupplier.GetMonitor: TCallStackMonitor;
|
|
begin
|
|
Result := TCallStackMonitor(inherited Monitor);
|
|
end;
|
|
|
|
procedure TCallStackSupplier.SetMonitor(AValue: TCallStackMonitor);
|
|
begin
|
|
inherited Monitor := AValue;
|
|
end;
|
|
|
|
procedure TCallStackSupplier.RequestCount(ACallstack: TCallStackBase);
|
|
begin
|
|
ACallstack.SetCountValidity(ddsInvalid);
|
|
end;
|
|
|
|
procedure TCallStackSupplier.RequestAtLeastCount(ACallstack: TCallStackBase;
|
|
ARequiredMinCount: Integer);
|
|
begin
|
|
RequestCount(ACallstack);
|
|
end;
|
|
|
|
procedure TCallStackSupplier.RequestCurrent(ACallstack: TCallStackBase);
|
|
begin
|
|
ACallstack.SetCurrentValidity(ddsInvalid);
|
|
end;
|
|
|
|
procedure TCallStackSupplier.RequestEntries(ACallstack: TCallStackBase);
|
|
var
|
|
e: TCallStackEntry;
|
|
It: TMapIterator;
|
|
begin
|
|
DebugLn(DBG_DATA_MONITORS, ['DebugDataMonitor: TCallStackSupplier.RequestEntries']);
|
|
It := TMapIterator.Create(ACallstack.RawEntries);
|
|
|
|
if not It.Locate(ACallstack.LowestUnknown )
|
|
then if not It.EOM
|
|
then It.Next;
|
|
|
|
while (not IT.EOM) and (TCallStackEntry(It.DataPtr^).Index < ACallstack.HighestUnknown)
|
|
do begin
|
|
e := TCallStackEntry(It.DataPtr^);
|
|
if e.Validity = ddsRequested then e.Validity := ddsInvalid;
|
|
It.Next;
|
|
end;
|
|
It.Free;
|
|
|
|
if Monitor <> nil then
|
|
ACallstack.DoEntriesUpdated; // calls Monitor.DoModified;
|
|
//Monitor.DoModified;
|
|
end;
|
|
|
|
//procedure TCallStackSupplier.CurrentChanged;
|
|
//begin
|
|
// DebugLn(DBG_DATA_MONITORS, ['DebugDataMonitor: TCallStackSupplier.CurrentChanged']);
|
|
// if Monitor <> nil
|
|
// then Monitor.NotifyCurrent;
|
|
//end;
|
|
|
|
procedure TCallStackSupplier.UpdateCurrentIndex;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{ TCallStackMonitor }
|
|
|
|
function TCallStackMonitor.GetSupplier: TCallStackSupplier;
|
|
begin
|
|
Result := TCallStackSupplier(inherited Supplier);
|
|
end;
|
|
|
|
procedure TCallStackMonitor.SetSupplier(AValue: TCallStackSupplier);
|
|
begin
|
|
inherited Supplier := AValue;
|
|
end;
|
|
|
|
function TCallStackMonitor.CreateCallStackList: TCallStackList;
|
|
begin
|
|
Result := TCallStackList.Create;
|
|
end;
|
|
|
|
constructor TCallStackMonitor.Create;
|
|
begin
|
|
FCallStackList := CreateCallStackList;
|
|
inherited Create;
|
|
end;
|
|
|
|
destructor TCallStackMonitor.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FCallStackList);
|
|
end;
|
|
|
|
{ TThreadsSupplier }
|
|
|
|
procedure TThreadsSupplier.Changed;
|
|
begin
|
|
if Monitor <> nil
|
|
then Monitor.DoModified;
|
|
end;
|
|
|
|
function TThreadsSupplier.GetCurrentThreads: TThreads;
|
|
begin
|
|
Result := nil;
|
|
if Monitor <> nil then
|
|
Result := Monitor.Threads;
|
|
end;
|
|
|
|
function TThreadsSupplier.GetMonitor: TThreadsMonitor;
|
|
begin
|
|
Result := TThreadsMonitor(inherited Monitor);
|
|
end;
|
|
|
|
procedure TThreadsSupplier.SetMonitor(AValue: TThreadsMonitor);
|
|
begin
|
|
inherited Monitor := AValue;
|
|
end;
|
|
|
|
procedure TThreadsSupplier.ChangeCurrentThread(ANewId: Integer);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TThreadsSupplier.RequestMasterData;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TThreadsSupplier.DoStateChange(const AOldState: TDBGState);
|
|
begin
|
|
if (Debugger.State = dsStop) and (CurrentThreads <> nil) then
|
|
CurrentThreads.Clear;
|
|
inherited DoStateChange(AOldState);
|
|
end;
|
|
|
|
procedure TThreadsSupplier.DoStateLeavePauseClean;
|
|
begin
|
|
DoCleanAfterPause;
|
|
end;
|
|
|
|
procedure TThreadsSupplier.DoCleanAfterPause;
|
|
begin
|
|
if CurrentThreads <> nil then
|
|
CurrentThreads.Clear;
|
|
if Monitor <> nil then
|
|
Monitor.DoModified;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TBaseSignal }
|
|
{ =========================================================================== }
|
|
|
|
procedure TBaseSignal.AssignTo(Dest: TPersistent);
|
|
begin
|
|
if Dest is TBaseSignal
|
|
then begin
|
|
TBaseSignal(Dest).Name := FName;
|
|
TBaseSignal(Dest).ID := FID;
|
|
TBaseSignal(Dest).HandledByDebugger := FHandledByDebugger;
|
|
TBaseSignal(Dest).ResumeHandled := FResumeHandled;
|
|
end
|
|
else inherited AssignTo(Dest);
|
|
end;
|
|
|
|
constructor TBaseSignal.Create(ACollection: TCollection);
|
|
begin
|
|
FID := 0;
|
|
FHandledByDebugger := False;
|
|
FResumeHandled := True;
|
|
inherited Create(ACollection);
|
|
end;
|
|
|
|
procedure TBaseSignal.SetHandledByDebugger(const AValue: Boolean);
|
|
begin
|
|
if AValue = FHandledByDebugger then Exit;
|
|
FHandledByDebugger := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseSignal.SetID (const AValue: Integer );
|
|
begin
|
|
if FID = AValue then Exit;
|
|
FID := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseSignal.SetName (const AValue: String );
|
|
begin
|
|
if FName = AValue then Exit;
|
|
FName := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseSignal.SetResumeHandled(const AValue: Boolean);
|
|
begin
|
|
if FResumeHandled = AValue then Exit;
|
|
FResumeHandled := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TDBGSignal }
|
|
{ =========================================================================== }
|
|
|
|
function TDBGSignal.GetDebugger: TDebuggerIntf;
|
|
begin
|
|
Result := TDBGSignals(Collection).FDebugger;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TBaseSignals }
|
|
{ =========================================================================== }
|
|
|
|
function TBaseSignals.Add (const AName: String; AID: Integer ): TBaseSignal;
|
|
begin
|
|
Result := TBaseSignal(inherited Add);
|
|
Result.BeginUpdate;
|
|
try
|
|
Result.Name := AName;
|
|
Result.ID := AID;
|
|
finally
|
|
Result.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
constructor TBaseSignals.Create (const AItemClass: TBaseSignalClass );
|
|
begin
|
|
inherited Create(AItemClass);
|
|
end;
|
|
|
|
procedure TBaseSignals.Reset;
|
|
begin
|
|
Clear;
|
|
end;
|
|
|
|
function TBaseSignals.Find(const AName: String): TBaseSignal;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
begin
|
|
Result := TBaseSignal(GetItem(n));
|
|
if CompareText(Result.Name, AName) = 0 then Exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TDBGSignals }
|
|
{ =========================================================================== }
|
|
|
|
function TDBGSignals.Add(const AName: String; AID: Integer): TDBGSignal;
|
|
begin
|
|
Result := TDBGSignal(inherited Add(AName, AID));
|
|
end;
|
|
|
|
constructor TDBGSignals.Create(const ADebugger: TDebuggerIntf;
|
|
const ASignalClass: TDBGSignalClass);
|
|
begin
|
|
FDebugger := ADebugger;
|
|
inherited Create(ASignalClass);
|
|
end;
|
|
|
|
function TDBGSignals.Find(const AName: String): TDBGSignal;
|
|
begin
|
|
Result := TDBGSignal(inherited Find(ANAme));
|
|
end;
|
|
|
|
function TDBGSignals.GetItem(const AIndex: Integer): TDBGSignal;
|
|
begin
|
|
Result := TDBGSignal(inherited GetItem(AIndex));
|
|
end;
|
|
|
|
procedure TDBGSignals.SetItem(const AIndex: Integer; const AValue: TDBGSignal);
|
|
begin
|
|
inherited SetItem(AIndex, AValue);
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TBaseException }
|
|
{ =========================================================================== }
|
|
|
|
procedure TBaseException.SetEnabled(AValue: Boolean);
|
|
begin
|
|
if FEnabled = AValue then Exit;
|
|
FEnabled := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseException.AssignTo(Dest: TPersistent);
|
|
begin
|
|
if Dest is TBaseException
|
|
then begin
|
|
TBaseException(Dest).Name := FName;
|
|
end
|
|
else inherited AssignTo(Dest);
|
|
end;
|
|
|
|
constructor TBaseException.Create(ACollection: TCollection);
|
|
begin
|
|
inherited Create(ACollection);
|
|
end;
|
|
|
|
procedure TBaseException.SetName(const AValue: String);
|
|
begin
|
|
if FName = AValue then exit;
|
|
|
|
if TBaseExceptions(GetOwner).Find(AValue) <> nil
|
|
then raise EDBGExceptions.Create('Duplicate name: ' + AValue);
|
|
|
|
FName := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TBaseExceptions }
|
|
{ =========================================================================== }
|
|
|
|
function TBaseExceptions.Add(const AName: String): TBaseException;
|
|
begin
|
|
Result := TBaseException(inherited Add);
|
|
Result.Name := AName;
|
|
end;
|
|
|
|
constructor TBaseExceptions.Create(const AItemClass: TBaseExceptionClass);
|
|
begin
|
|
inherited Create(AItemClass);
|
|
FIgnoreAll := False;
|
|
end;
|
|
|
|
destructor TBaseExceptions.Destroy;
|
|
begin
|
|
ClearExceptions;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TBaseExceptions.Reset;
|
|
begin
|
|
ClearExceptions;
|
|
FIgnoreAll := False;
|
|
end;
|
|
|
|
function TBaseExceptions.Find(const AName: String): TBaseException;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
for n := 0 to Count - 1 do
|
|
begin
|
|
Result := TBaseException(GetItem(n));
|
|
if CompareText(Result.Name, AName) = 0 then Exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
function TBaseExceptions.GetItem(const AIndex: Integer): TBaseException;
|
|
begin
|
|
Result := TBaseException(inherited GetItem(AIndex));
|
|
end;
|
|
|
|
procedure TBaseExceptions.SetItem(const AIndex: Integer; AValue: TBaseException);
|
|
begin
|
|
inherited SetItem(AIndex, AValue);
|
|
end;
|
|
|
|
procedure TBaseExceptions.ClearExceptions;
|
|
begin
|
|
while Count>0 do
|
|
TBaseException(GetItem(Count-1)).Free;
|
|
end;
|
|
|
|
procedure TBaseExceptions.SetIgnoreAll(const AValue: Boolean);
|
|
begin
|
|
if FIgnoreAll = AValue then exit;
|
|
FIgnoreAll := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseExceptions.AssignTo(Dest: TPersistent);
|
|
begin
|
|
if Dest is TBaseExceptions
|
|
then begin
|
|
TBaseExceptions(Dest).IgnoreAll := IgnoreAll;
|
|
end
|
|
else inherited AssignTo(Dest);
|
|
end;
|
|
|
|
{ TBaseDisassembler }
|
|
|
|
procedure TBaseDisassembler.IndexError(AIndex: Integer);
|
|
begin
|
|
raise EInvalidOperation.CreateFmt('Index out of range (%d)', [AIndex]);
|
|
end;
|
|
|
|
function TBaseDisassembler.GetEntryPtr(AIndex: Integer): PDisassemblerEntry;
|
|
begin
|
|
if (AIndex < -FCountBefore)
|
|
or (AIndex >= FCountAfter) then IndexError(Aindex);
|
|
|
|
Result := InternalGetEntryPtr(AIndex);
|
|
end;
|
|
|
|
function TBaseDisassembler.GetEntry(AIndex: Integer): TDisassemblerEntry;
|
|
begin
|
|
if (AIndex < -FCountBefore)
|
|
or (AIndex >= FCountAfter) then IndexError(Aindex);
|
|
|
|
Result := InternalGetEntry(AIndex);
|
|
end;
|
|
|
|
function TBaseDisassembler.InternalGetEntry(AIndex: Integer): TDisassemblerEntry;
|
|
begin
|
|
Result.Addr := 0;
|
|
Result.Offset := 0;
|
|
Result.SrcFileLine := 0;
|
|
Result.SrcStatementIndex := 0;
|
|
Result.SrcStatementCount := 0;
|
|
end;
|
|
|
|
function TBaseDisassembler.InternalGetEntryPtr(AIndex: Integer): PDisassemblerEntry;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.DoChanged;
|
|
begin
|
|
// nothing
|
|
end;
|
|
|
|
procedure TBaseDisassembler.Changed;
|
|
begin
|
|
if FChangedLockCount > 0
|
|
then begin
|
|
FIsChanged := True;
|
|
exit;
|
|
end;
|
|
FIsChanged := False;
|
|
DoChanged;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.LockChanged;
|
|
begin
|
|
inc(FChangedLockCount);
|
|
end;
|
|
|
|
procedure TBaseDisassembler.UnlockChanged;
|
|
begin
|
|
dec(FChangedLockCount);
|
|
if FIsChanged and (FChangedLockCount = 0)
|
|
then Changed;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.InternalIncreaseCountBefore(ACount: Integer);
|
|
begin
|
|
// increase count withou change notification
|
|
if ACount < FCountBefore
|
|
then begin
|
|
debugln(DBG_DISASSEMBLER, ['WARNING: TBaseDisassembler.InternalIncreaseCountBefore will decrease was ', FCountBefore , ' new=',ACount]);
|
|
SetCountBefore(ACount);
|
|
end
|
|
else FCountBefore := ACount;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.InternalIncreaseCountAfter(ACount: Integer);
|
|
begin
|
|
// increase count withou change notification
|
|
if ACount < FCountAfter
|
|
then begin
|
|
debugln(DBG_DISASSEMBLER, ['WARNING: TBaseDisassembler.InternalIncreaseCountAfter will decrease was ', FCountAfter , ' new=',ACount]);
|
|
SetCountAfter(ACount)
|
|
end
|
|
else FCountAfter := ACount;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.SetCountBefore(ACount: Integer);
|
|
begin
|
|
if FCountBefore = ACount
|
|
then exit;
|
|
FCountBefore := ACount;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.SetCountAfter(ACount: Integer);
|
|
begin
|
|
if FCountAfter = ACount
|
|
then exit;
|
|
FCountAfter := ACount;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.SetBaseAddr(AnAddr: TDbgPtr);
|
|
begin
|
|
if FBaseAddr = AnAddr
|
|
then exit;
|
|
FBaseAddr := AnAddr;
|
|
Changed;
|
|
end;
|
|
|
|
constructor TBaseDisassembler.Create;
|
|
begin
|
|
Clear;
|
|
FChangedLockCount := 0;
|
|
end;
|
|
|
|
destructor TBaseDisassembler.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
Clear;
|
|
end;
|
|
|
|
procedure TBaseDisassembler.Clear;
|
|
begin
|
|
FCountAfter := 0;
|
|
FCountBefore := 0;
|
|
FBaseAddr := 0;
|
|
end;
|
|
|
|
function TBaseDisassembler.PrepareRange(AnAddr: TDbgPtr; ALinesBefore,
|
|
ALinesAfter: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
{ TDBGDisassemblerEntryRange }
|
|
|
|
function TDBGDisassemblerEntryRange.GetEntry(Index: Integer): TDisassemblerEntry;
|
|
begin
|
|
if (Index < 0) or (Index >= FCount)
|
|
then raise Exception.Create('Illegal Index');
|
|
Result := FEntries[Index];
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.GetCapacity: Integer;
|
|
begin
|
|
Result := length(FEntries);
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.GetEntryPtr(Index: Integer): PDisassemblerEntry;
|
|
begin
|
|
if (Index < 0) or (Index >= FCount)
|
|
then raise Exception.Create('Illegal Index');
|
|
Result := @FEntries[Index];
|
|
end;
|
|
|
|
procedure TDBGDisassemblerEntryRange.SetCapacity(const AValue: Integer);
|
|
begin
|
|
SetLength(FEntries, AValue);
|
|
if FCount >= AValue
|
|
then FCount := AValue - 1;
|
|
end;
|
|
|
|
procedure TDBGDisassemblerEntryRange.SetCount(const AValue: Integer);
|
|
begin
|
|
if FCount = AValue then exit;
|
|
if AValue >= Capacity
|
|
then Capacity := AValue + Max(20, AValue div 4);
|
|
|
|
FCount := AValue;
|
|
end;
|
|
|
|
procedure TDBGDisassemblerEntryRange.Clear;
|
|
begin
|
|
SetCapacity(0);
|
|
FCount := 0;
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.Append(const AnEntryPtr: PDisassemblerEntry): Integer;
|
|
begin
|
|
if FCount >= Capacity
|
|
then Capacity := FCount + Max(20,FCount div 4);
|
|
|
|
FEntries[FCount] := AnEntryPtr^;
|
|
Result := FCount;
|
|
inc(FCount);
|
|
end;
|
|
|
|
procedure TDBGDisassemblerEntryRange.Merge(const AnotherRange: TDBGDisassemblerEntryRange);
|
|
var
|
|
i, j: Integer;
|
|
a: TDBGPtr;
|
|
begin
|
|
if AnotherRange.RangeStartAddr < RangeStartAddr then
|
|
begin
|
|
// merge before
|
|
i := AnotherRange.Count - 1;
|
|
a := FirstAddr;
|
|
while (i >= 0) and (AnotherRange.EntriesPtr[i]^.Addr >= a)
|
|
do dec(i);
|
|
inc(i);
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassemblerEntryRange.Merge: Merged to START: Other=', dbgs(AnotherRange), ' To other index=', i, ' INTO self=', dbgs(self) ]);
|
|
if Capacity < Count + i
|
|
then Capacity := Count + i;
|
|
for j := Count-1 downto 0 do
|
|
FEntries[j+i] := FEntries[j];
|
|
for j := 0 to i - 1 do
|
|
FEntries[j] := AnotherRange.FEntries[j];
|
|
FCount := FCount + i;
|
|
FRangeStartAddr := AnotherRange.FRangeStartAddr;
|
|
end
|
|
else begin
|
|
// merge after
|
|
a:= LastAddr;
|
|
i := 0;
|
|
while (i < AnotherRange.Count) and (AnotherRange.EntriesPtr[i]^.Addr <= a)
|
|
do inc(i);
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassemblerEntryRange.Merge to END: Other=', dbgs(AnotherRange), ' From other index=', i, ' INTO self=', dbgs(self) ]);
|
|
if Capacity < Count + AnotherRange.Count - i
|
|
then Capacity := Count + AnotherRange.Count - i;
|
|
for j := 0 to AnotherRange.Count - i - 1 do
|
|
FEntries[Count + j] := AnotherRange.FEntries[i + j];
|
|
FCount := FCount + AnotherRange.Count - i;
|
|
FRangeEndAddr := AnotherRange.FRangeEndAddr;
|
|
FLastEntryEndAddr := AnotherRange.FLastEntryEndAddr;
|
|
if FRangeStartAddr = 0 then
|
|
FRangeStartAddr := AnotherRange.FRangeStartAddr;
|
|
end;
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassemblerEntryRange.Merge AFTER MERGE: ', dbgs(self) ]);
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.FirstAddr: TDbgPtr;
|
|
begin
|
|
if FCount = 0
|
|
then exit(0);
|
|
Result := FEntries[0].Addr;
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.LastAddr: TDbgPtr;
|
|
begin
|
|
if FCount = 0
|
|
then exit(0);
|
|
Result := FEntries[FCount-1].Addr;
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.ContainsAddr(const AnAddr: TDbgPtr;
|
|
IncludeNextAddr: Boolean = False): Boolean;
|
|
begin
|
|
if IncludeNextAddr
|
|
then Result := (AnAddr >= RangeStartAddr) and (AnAddr <= RangeEndAddr)
|
|
else Result := (AnAddr >= RangeStartAddr) and (AnAddr < RangeEndAddr);
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.IndexOfAddr(const AnAddr: TDbgPtr): Integer;
|
|
begin
|
|
Result := FCount - 1;
|
|
while Result >= 0 do begin
|
|
if FEntries[Result].Addr = AnAddr
|
|
then exit;
|
|
dec(Result);
|
|
end;
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.IndexOfAddrWithOffs(const AnAddr: TDbgPtr): Integer;
|
|
var
|
|
O: Integer;
|
|
begin
|
|
Result := IndexOfAddrWithOffs(AnAddr, O);
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryRange.IndexOfAddrWithOffs(const AnAddr: TDbgPtr; out
|
|
AOffs: Integer): Integer;
|
|
begin
|
|
Result := FCount - 1;
|
|
while Result >= 0 do begin
|
|
if FEntries[Result].Addr <= AnAddr
|
|
then break;
|
|
dec(Result);
|
|
end;
|
|
If Result < 0
|
|
then AOffs := 0
|
|
else AOffs := AnAddr - FEntries[Result].Addr;
|
|
end;
|
|
|
|
{ TDBGDisassemblerEntryMapIterator }
|
|
|
|
function TDBGDisassemblerEntryMapIterator.GetRangeForAddr(AnAddr: TDbgPtr;
|
|
IncludeNextAddr: Boolean): TDBGDisassemblerEntryRange;
|
|
begin
|
|
Result := nil;
|
|
if not Locate(AnAddr)
|
|
then if not BOM
|
|
then Previous;
|
|
|
|
if BOM
|
|
then exit;
|
|
|
|
GetData(Result);
|
|
if not Result.ContainsAddr(AnAddr, IncludeNextAddr)
|
|
then Result := nil;
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryMapIterator.NextRange: TDBGDisassemblerEntryRange;
|
|
begin
|
|
Result := nil;
|
|
if EOM
|
|
then exit;
|
|
|
|
Next;
|
|
if not EOM
|
|
then GetData(Result);
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryMapIterator.PreviousRange: TDBGDisassemblerEntryRange;
|
|
begin
|
|
Result := nil;
|
|
if BOM
|
|
then exit;
|
|
|
|
Previous;
|
|
if not BOM
|
|
then GetData(Result);
|
|
end;
|
|
|
|
{ TDBGDisassemblerEntryMap }
|
|
|
|
procedure TDBGDisassemblerEntryMap.ReleaseData(ADataPtr: Pointer);
|
|
type
|
|
PDBGDisassemblerEntryRange = ^TDBGDisassemblerEntryRange;
|
|
begin
|
|
if FFreeItemLock
|
|
then exit;
|
|
if Assigned(FOnDelete)
|
|
then FOnDelete(PDBGDisassemblerEntryRange(ADataPtr)^);
|
|
PDBGDisassemblerEntryRange(ADataPtr)^.Free;
|
|
end;
|
|
|
|
constructor TDBGDisassemblerEntryMap.Create(AIdType: TMapIdType; ADataSize: Cardinal);
|
|
begin
|
|
inherited;
|
|
FIterator := TDBGDisassemblerEntryMapIterator.Create(Self);
|
|
end;
|
|
|
|
destructor TDBGDisassemblerEntryMap.Destroy;
|
|
begin
|
|
FreeAndNil(FIterator);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDBGDisassemblerEntryMap.AddRange(const ARange: TDBGDisassemblerEntryRange);
|
|
var
|
|
MergeRng, MergeRng2: TDBGDisassemblerEntryRange;
|
|
OldId: TDBGPtr;
|
|
begin
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassemblerEntryMap.AddRange ', dbgs(ARange), ' to map with count=', Count ]);
|
|
if ARange.Count = 0 then begin
|
|
ARange.Free;
|
|
exit;
|
|
end;
|
|
|
|
MergeRng := GetRangeForAddr(ARange.RangeStartAddr, True);
|
|
if MergeRng <> nil then begin
|
|
// merge to end ( ARange.RangeStartAddr >= MergeRng.RangeStartAddr )
|
|
// MergeRng keeps it's ID;
|
|
MergeRng.Merge(ARange);
|
|
if assigned(FOnMerge)
|
|
then FOnMerge(MergeRng, ARange);
|
|
ARange.Free;
|
|
|
|
MergeRng2 := GetRangeForAddr(MergeRng.RangeEndAddr, True);
|
|
if (MergeRng2 <> nil) and (MergeRng2 <> MergeRng) then begin
|
|
// MergeRng is located before MergeRng2
|
|
// MergeRng2 merges to end of MergeRng ( No ID changes )
|
|
MergeRng.Merge(MergeRng2);
|
|
if assigned(FOnMerge)
|
|
then FOnMerge(MergeRng, MergeRng2);
|
|
Delete(MergeRng2.RangeStartAddr);
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
MergeRng := GetRangeForAddr(ARange.RangeEndAddr, True);
|
|
if MergeRng <> nil then begin
|
|
// merge to start ( ARange.RangeEndAddr is in MergeRng )
|
|
if MergeRng.ContainsAddr(ARange.RangeStartAddr)
|
|
then begin
|
|
debugln(['ERROR: New Range is completely inside existing ', dbgs(MergeRng)]);
|
|
ARange.Free;
|
|
exit;
|
|
end;
|
|
// MergeRng changes ID
|
|
OldId := MergeRng.RangeStartAddr;
|
|
MergeRng.Merge(ARange);
|
|
if assigned(FOnMerge)
|
|
then FOnMerge(ARange, MergeRng);
|
|
FFreeItemLock := True; // prevent destruction of MergeRng
|
|
Delete(OldId);
|
|
FFreeItemLock := False;
|
|
Add(MergeRng.RangeStartAddr, MergeRng);
|
|
ARange.Free;
|
|
exit;
|
|
end;
|
|
|
|
Add(ARange.RangeStartAddr, ARange);
|
|
end;
|
|
|
|
function TDBGDisassemblerEntryMap.GetRangeForAddr(AnAddr: TDbgPtr;
|
|
IncludeNextAddr: Boolean = False): TDBGDisassemblerEntryRange;
|
|
begin
|
|
Result := FIterator.GetRangeForAddr(AnAddr, IncludeNextAddr);
|
|
end;
|
|
|
|
{ TDBGDisassembler }
|
|
|
|
procedure TDBGDisassembler.EntryRangesOnDelete(Sender: TObject);
|
|
begin
|
|
if FCurrentRange <> Sender
|
|
then exit;
|
|
LockChanged;
|
|
FCurrentRange := nil;
|
|
SetBaseAddr(0);
|
|
SetCountBefore(0);
|
|
SetCountAfter(0);
|
|
UnlockChanged;
|
|
end;
|
|
|
|
procedure TDBGDisassembler.EntryRangesOnMerge(MergeReceiver,
|
|
MergeGiver: TDBGDisassemblerEntryRange);
|
|
var
|
|
i: LongInt;
|
|
lb, la: Integer;
|
|
begin
|
|
// no need to call changed, will be done by whoever triggered this
|
|
if FCurrentRange = MergeGiver
|
|
then FCurrentRange := MergeReceiver;
|
|
|
|
if FCurrentRange = MergeReceiver
|
|
then begin
|
|
i := FCurrentRange.IndexOfAddrWithOffs(BaseAddr);
|
|
if i >= 0
|
|
then begin
|
|
InternalIncreaseCountBefore(i);
|
|
InternalIncreaseCountAfter(FCurrentRange.Count - 1 - i);
|
|
exit;
|
|
end
|
|
else if FCurrentRange.ContainsAddr(BaseAddr)
|
|
then begin
|
|
debugln(DBG_DISASSEMBLER, ['WARNING: TDBGDisassembler.OnMerge: Address at odd offset ',BaseAddr, ' before=',CountBefore, ' after=', CountAfter]);
|
|
lb := CountBefore;
|
|
la := CountAfter;
|
|
if HandleRangeWithInvalidAddr(FCurrentRange, BaseAddr, lb, la)
|
|
then begin
|
|
InternalIncreaseCountBefore(lb);
|
|
InternalIncreaseCountAfter(la);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
LockChanged;
|
|
SetBaseAddr(0);
|
|
SetCountBefore(0);
|
|
SetCountAfter(0);
|
|
UnlockChanged;
|
|
end;
|
|
end;
|
|
|
|
function TDBGDisassembler.FindRange(AnAddr: TDbgPtr; ALinesBefore,
|
|
ALinesAfter: Integer): Boolean;
|
|
var
|
|
i: LongInt;
|
|
NewRange: TDBGDisassemblerEntryRange;
|
|
begin
|
|
LockChanged;
|
|
try
|
|
Result := False;
|
|
NewRange := FEntryRanges.GetRangeForAddr(AnAddr);
|
|
|
|
if (NewRange <> nil)
|
|
and ( (NewRange.RangeStartAddr > AnAddr) or (NewRange.RangeEndAddr < AnAddr) )
|
|
then
|
|
NewRange := nil;
|
|
|
|
if NewRange = nil
|
|
then begin
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassembler.FindRange: Address not found ', AnAddr, ' wanted-before=',ALinesBefore,' wanted-after=',ALinesAfter,' in map with count=', FEntryRanges.Count ]);
|
|
exit;
|
|
end;
|
|
|
|
i := NewRange.IndexOfAddr(AnAddr);
|
|
if i < 0
|
|
then begin
|
|
// address at incorrect offset
|
|
Result := HandleRangeWithInvalidAddr(NewRange, AnAddr, ALinesBefore, ALinesAfter);
|
|
debugln(DBG_DISASSEMBLER, ['WARNING: TDBGDisassembler.FindRange: Address at odd offset ',AnAddr,' Result=', dbgs(result), ' before=',CountBefore, ' after=', CountAfter, ' wanted-before=',ALinesBefore,' wanted-after=',ALinesAfter,' in map with count=', FEntryRanges.Count]);
|
|
if Result
|
|
then begin
|
|
FCurrentRange := NewRange;
|
|
SetBaseAddr(AnAddr);
|
|
SetCountBefore(ALinesBefore);
|
|
SetCountAfter(ALinesAfter);
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
FCurrentRange := NewRange;
|
|
SetBaseAddr(AnAddr);
|
|
SetCountBefore(i);
|
|
SetCountAfter(NewRange.Count - i);
|
|
Result := (i >= ALinesBefore) and (CountAfter >= ALinesAfter);
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassembler.FindRange: Address found ',AnAddr,' Result=', dbgs(result), ' before=',CountBefore, ' after=', CountAfter, ' wanted-before=',ALinesBefore,' wanted-after=',ALinesAfter,' in map with count=', FEntryRanges.Count]);
|
|
finally
|
|
UnlockChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TDBGDisassembler.DoChanged;
|
|
begin
|
|
inherited DoChanged;
|
|
if assigned(FOnChange)
|
|
then FOnChange(Self);
|
|
end;
|
|
|
|
procedure TDBGDisassembler.Clear;
|
|
begin
|
|
debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassembler.Clear: map had count=', FEntryRanges.Count ]);
|
|
FCurrentRange := nil;
|
|
FEntryRanges.Clear;
|
|
inherited Clear;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TDBGDisassembler.DoStateChange(const AOldState: TDBGState);
|
|
begin
|
|
if FDebugger.State = dsPause
|
|
then begin
|
|
Changed;
|
|
end
|
|
else begin
|
|
if (AOldState = dsPause) or (AOldState = dsNone) { Force clear on initialisation }
|
|
then Clear;
|
|
end;
|
|
end;
|
|
|
|
function TDBGDisassembler.InternalGetEntry(AIndex: Integer): TDisassemblerEntry;
|
|
begin
|
|
Result := FCurrentRange.Entries[AIndex + CountBefore];
|
|
end;
|
|
|
|
function TDBGDisassembler.InternalGetEntryPtr(AIndex: Integer): PDisassemblerEntry;
|
|
begin
|
|
Result := FCurrentRange.EntriesPtr[AIndex + CountBefore];
|
|
end;
|
|
|
|
function TDBGDisassembler.PrepareEntries(AnAddr: TDbgPtr; ALinesBefore,
|
|
ALinesAfter: Integer): boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
function TDBGDisassembler.HandleRangeWithInvalidAddr(ARange: TDBGDisassemblerEntryRange;
|
|
AnAddr: TDbgPtr; var ALinesBefore, ALinesAfter: Integer): boolean;
|
|
begin
|
|
Result := False;
|
|
if ARange <> nil then
|
|
FEntryRanges.Delete(ARange.RangeStartAddr);
|
|
end;
|
|
|
|
constructor TDBGDisassembler.Create(const ADebugger: TDebuggerIntf);
|
|
begin
|
|
FDebugger := ADebugger;
|
|
FEntryRanges := TDBGDisassemblerEntryMap.Create(itu8, SizeOf(TDBGDisassemblerEntryRange));
|
|
FEntryRanges.OnDelete := @EntryRangesOnDelete;
|
|
FEntryRanges.OnMerge := @EntryRangesOnMerge;
|
|
inherited Create;
|
|
end;
|
|
|
|
destructor TDBGDisassembler.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FEntryRanges.OnDelete := nil;
|
|
Clear;
|
|
FreeAndNil(FEntryRanges);
|
|
end;
|
|
|
|
function TDBGDisassembler.PrepareRange(AnAddr: TDbgPtr; ALinesBefore,
|
|
ALinesAfter: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
if (Debugger = nil) or (Debugger.State <> dsPause) or (AnAddr = 0)
|
|
then exit;
|
|
if (ALinesBefore < 0) or (ALinesAfter < 0)
|
|
then raise Exception.Create('invalid PrepareRange request');
|
|
|
|
if (FPreparingBefore > 0) or (FPreparingAfter > 0) then begin
|
|
if AnAddr <> FPreparingAddr then begin
|
|
FPreparingBefore := ALinesBefore;
|
|
FPreparingAfter := ALinesAfter;
|
|
FPreparingAddr := AnAddr;
|
|
end
|
|
else begin
|
|
FPreparingBefore := Max(FPreparingBefore, ALinesBefore);
|
|
FPreparingAfter := Max(FPreparingAfter, ALinesAfter);
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
FPreparingBefore := ALinesBefore;
|
|
FPreparingAfter := ALinesAfter;
|
|
FPreparingAddr := AnAddr;
|
|
try
|
|
repeat
|
|
ALinesBefore := FPreparingBefore;
|
|
ALinesAfter := FPreparingAfter;
|
|
AnAddr := FPreparingAddr;
|
|
// Do not LockChange, if FindRange changes something, then notification must be send to syncronize counts on IDE-object
|
|
Result:= FindRange(AnAddr, ALinesBefore, ALinesAfter);
|
|
if result then debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassembler.PrepareRange found existing data Addr=', AnAddr,' before=', ALinesBefore, ' After=', ALinesAfter ]);
|
|
if Result
|
|
then exit;
|
|
|
|
if result then debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassembler.PrepareRange calling PrepareEntries Addr=', AnAddr,' before=', ALinesBefore, ' After=', ALinesAfter ]);
|
|
if PrepareEntries(AnAddr, ALinesBefore, ALinesAfter)
|
|
then Result:= FindRange(AnAddr, ALinesBefore, ALinesAfter);
|
|
if result then debugln(DBG_DISASSEMBLER, ['INFO: TDBGDisassembler.PrepareRange found data AFTER PrepareEntries Addr=', AnAddr,' before=', ALinesBefore, ' After=', ALinesAfter ]);
|
|
until (FPreparingBefore = ALinesBefore) and (FPreparingAfter = ALinesAfter) and (FPreparingAddr = AnAddr);
|
|
finally
|
|
FPreparingBefore := 0;
|
|
FPreparingAfter := 0;
|
|
end;
|
|
end;
|
|
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
(** **)
|
|
(** D E B U G G E R **)
|
|
(** **)
|
|
(******************************************************************************)
|
|
(******************************************************************************)
|
|
|
|
{ TDebuggerProperties }
|
|
|
|
constructor TDebuggerProperties.Create;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerProperties.Assign(Source: TPersistent);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{ =========================================================================== }
|
|
{ TDebuggerIntf }
|
|
{ =========================================================================== }
|
|
|
|
class function TDebuggerIntf.Caption: String;
|
|
begin
|
|
Result := 'No caption set';
|
|
end;
|
|
|
|
function TDebuggerIntf.ChangeFileName: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
constructor TDebuggerIntf.Create(const AExternalDebugger: String);
|
|
var
|
|
list: TStringList;
|
|
nr: TDebuggerNotifyReason;
|
|
begin
|
|
inherited Create;
|
|
FEnabledFeatures := SupportedFeatures;
|
|
|
|
for nr := low(TDebuggerNotifyReason) to high(TDebuggerNotifyReason) do
|
|
FDestroyNotificationList[nr] := TMethodList.Create;
|
|
FOnState := nil;
|
|
FOnCurrent := nil;
|
|
FOnOutput := nil;
|
|
FOnDbgOutput := nil;
|
|
FState := dsNone;
|
|
FArguments := '';
|
|
FFilename := '';
|
|
FExternalDebugger := AExternalDebugger;
|
|
|
|
list := TStringList.Create;
|
|
list.OnChange := @DebuggerEnvironmentChanged;
|
|
FDebuggerEnvironment := list;
|
|
|
|
list := TStringList.Create;
|
|
list.OnChange := @EnvironmentChanged;
|
|
FEnvironment := list;
|
|
FCurEnvironment := TStringList.Create;
|
|
//FInternalUnitInfoProvider := TDebuggerUnitInfoProvider.Create;
|
|
|
|
FBreakPoints := CreateBreakPoints;
|
|
FLocals := CreateLocals;
|
|
FLineInfo := CreateLineInfo;
|
|
FRegisters := CreateRegisters;
|
|
FCallStack := CreateCallStack;
|
|
FDisassembler := CreateDisassembler;
|
|
FWatches := CreateWatches;
|
|
FThreads := CreateThreads;
|
|
FSignals := CreateSignals;
|
|
FExitCode := 0;
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateBreakPoints: TDBGBreakPoints;
|
|
begin
|
|
Result := TDBGBreakPoints.Create(Self, TDBGBreakPoint);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateCallStack: TCallStackSupplier;
|
|
begin
|
|
Result := TCallStackSupplier.Create(Self);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateDisassembler: TDBGDisassembler;
|
|
begin
|
|
Result := TDBGDisassembler.Create(Self);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateLocals: TLocalsSupplier;
|
|
begin
|
|
Result := TLocalsSupplier.Create(Self);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateLineInfo: TDBGLineInfo;
|
|
begin
|
|
Result := TDBGLineInfo.Create(Self);
|
|
end;
|
|
|
|
class function TDebuggerIntf.CreateProperties: TDebuggerProperties;
|
|
begin
|
|
Result := TDebuggerProperties.Create;
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateRegisters: TRegisterSupplier;
|
|
begin
|
|
Result := TRegisterSupplier.Create(Self);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateSignals: TDBGSignals;
|
|
begin
|
|
Result := TDBGSignals.Create(Self, TDBGSignal);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateWatches: TWatchesSupplier;
|
|
begin
|
|
Result := TWatchesSupplier.Create(Self);
|
|
end;
|
|
|
|
function TDebuggerIntf.CreateThreads: TThreadsSupplier;
|
|
begin
|
|
Result := TThreadsSupplier.Create(Self);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DebuggerEnvironmentChanged (Sender: TObject );
|
|
begin
|
|
end;
|
|
|
|
destructor TDebuggerIntf.Destroy;
|
|
var
|
|
nr: TDebuggerNotifyReason;
|
|
begin
|
|
FDestroyNotificationList[dnrDestroy].CallNotifyEvents(Self);
|
|
for nr := low(TDebuggerNotifyReason) to high(TDebuggerNotifyReason) do
|
|
FreeAndNil(FDestroyNotificationList[nr]);
|
|
// don't call events
|
|
FOnState := nil;
|
|
FOnCurrent := nil;
|
|
FOnOutput := nil;
|
|
FOnDbgOutput := nil;
|
|
|
|
if FState <> dsNone
|
|
then Done;
|
|
|
|
FBreakPoints.Debugger := nil;
|
|
FLocals.Debugger := nil;
|
|
FLineInfo.Debugger := nil;
|
|
FRegisters.Debugger := nil;
|
|
FCallStack.Debugger := nil;
|
|
FDisassembler.Debugger := nil;
|
|
FWatches.Debugger := nil;
|
|
FThreads.Debugger := nil;
|
|
|
|
//FreeAndNil(FInternalUnitInfoProvider);
|
|
FreeAndNil(FBreakPoints);
|
|
FreeAndNil(FLocals);
|
|
FreeAndNil(FLineInfo);
|
|
FreeAndNil(FRegisters);
|
|
FreeAndNil(FCallStack);
|
|
FreeAndNil(FDisassembler);
|
|
FreeAndNil(FWatches);
|
|
FreeAndNil(FThreads);
|
|
FreeAndNil(FDebuggerEnvironment);
|
|
FreeAndNil(FEnvironment);
|
|
FreeAndNil(FCurEnvironment);
|
|
FreeAndNil(FSignals);
|
|
inherited;
|
|
end;
|
|
|
|
function TDebuggerIntf.Disassemble(AAddr: TDbgPtr; ABackward: Boolean; out ANextAddr: TDbgPtr; out ADump, AStatement, AFile: String; out ALine: Integer): Boolean;
|
|
begin
|
|
Result := ReqCmd(dcDisassemble, [AAddr, ABackward, @ANextAddr, @ADump, @AStatement, @AFile, @ALine]);
|
|
end;
|
|
|
|
function TDebuggerIntf.GetLocation: TDBGLocationRec;
|
|
begin
|
|
Result.Address := 0;
|
|
Result.SrcLine := 0;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.LockCommandProcessing;
|
|
begin
|
|
// nothing
|
|
end;
|
|
|
|
procedure TDebuggerIntf.UnLockCommandProcessing;
|
|
begin
|
|
// nothing
|
|
end;
|
|
|
|
procedure TDebuggerIntf.BeginReset;
|
|
begin
|
|
FIsInReset := True;
|
|
end;
|
|
|
|
function TDebuggerIntf.NeedReset: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.AddNotifyEvent(AReason: TDebuggerNotifyReason; AnEvent: TNotifyEvent);
|
|
begin
|
|
FDestroyNotificationList[AReason].Add(TMethod(AnEvent));
|
|
end;
|
|
|
|
procedure TDebuggerIntf.RemoveNotifyEvent(AReason: TDebuggerNotifyReason; AnEvent: TNotifyEvent);
|
|
begin
|
|
FDestroyNotificationList[AReason].Remove(TMethod(AnEvent));
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetSkipStopMessage;
|
|
begin
|
|
FSkipStopMessage := True;
|
|
end;
|
|
|
|
class function TDebuggerIntf.SupportedCommandsFor(AState: TDBGState): TDBGCommands;
|
|
begin
|
|
Result := COMMANDMAP[AState] * GetSupportedCommands;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Done;
|
|
begin
|
|
SetState(dsNone);
|
|
FEnvironment.Clear;
|
|
FCurEnvironment.Clear;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Release;
|
|
begin
|
|
if Self <> nil
|
|
then Self.DoRelease;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoCurrent(const ALocation: TDBGLocationRec);
|
|
begin
|
|
DebugLnEnter(DBG_EVENTS, ['DebugEvent: Enter >> DoCurrent (Location) >> State=', dbgs(FState)]);
|
|
if Assigned(FOnCurrent) then FOnCurrent(Self, ALocation);
|
|
DebugLnExit(DBG_EVENTS, ['DebugEvent: Exit << DoCurrent (Location) <<']);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoDbgOutput(const AText: String);
|
|
begin
|
|
// WriteLN(' [TDebuggerIntf] ', AText);
|
|
if Assigned(FOnDbgOutput) then FOnDbgOutput(Self, AText);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoDbgEvent(const ACategory: TDBGEventCategory; const AEventType: TDBGEventType; const AText: String);
|
|
begin
|
|
DebugLnEnter(DBG_EVENTS, ['DebugEvent: Enter >> DoDbgEvent >> State=', dbgs(FState), ' Category=', dbgs(ACategory)]);
|
|
if Assigned(FEventLogHandler) then FEventLogHandler.LogCustomEvent(ACategory, AEventType, AText);
|
|
DebugLnExit(DBG_EVENTS, ['DebugEvent: Exit << DoDbgEvent <<']);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoException(const AExceptionType: TDBGExceptionType;
|
|
const AExceptionClass: String; const AExceptionLocation: TDBGLocationRec; const AExceptionText: String; out AContinue: Boolean);
|
|
begin
|
|
DebugLnEnter(DBG_EVENTS, ['DebugEvent: Enter >> DoException >> State=', dbgs(FState)]);
|
|
if AExceptionType = deInternal then
|
|
DoDbgEvent(ecDebugger, etExceptionRaised,
|
|
Format('Exception class "%s" at $%.' + IntToStr(TargetWidth div 4) + 'x with message "%s"',
|
|
[AExceptionClass, AExceptionLocation.Address, AExceptionText]));
|
|
if Assigned(FOnException) then
|
|
FOnException(Self, AExceptionType, AExceptionClass, AExceptionLocation, AExceptionText, AContinue)
|
|
else
|
|
AContinue := True;
|
|
DebugLnExit(DBG_EVENTS, ['DebugEvent: Exit << DoException <<']);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoOutput(const AText: String);
|
|
begin
|
|
if Assigned(FOnOutput) then FOnOutput(Self, AText);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoBreakpointHit(const ABreakPoint: TBaseBreakPoint; var ACanContinue: Boolean);
|
|
begin
|
|
DebugLnEnter(DBG_EVENTS, ['DebugEvent: Enter >> DoBreakpointHit << State=', dbgs(FState)]);
|
|
if Assigned(FOnBreakpointHit)
|
|
then FOnBreakpointHit(Self, ABreakPoint, ACanContinue);
|
|
DebugLnExit(DBG_EVENTS, ['DebugEvent: Exit >> DoBreakpointHit <<']);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoBeforeState(const OldState: TDBGState);
|
|
begin
|
|
DebugLnEnter(DBG_STATE_EVENT, ['DebugEvent: Enter >> DoBeforeState << State=', dbgs(FState)]);
|
|
if Assigned(FOnBeforeState) then FOnBeforeState(Self, OldState);
|
|
DebugLnExit(DBG_STATE_EVENT, ['DebugEvent: Exit >> DoBeforeState <<']);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoState(const OldState: TDBGState);
|
|
begin
|
|
DebugLnEnter(DBG_STATE_EVENT, ['DebugEvent: Enter >> DoState << State=', dbgs(FState)]);
|
|
if Assigned(FOnState) then FOnState(Self, OldState);
|
|
DebugLnExit(DBG_STATE_EVENT, ['DebugEvent: Exit >> DoState <<']);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.EnvironmentChanged(Sender: TObject);
|
|
var
|
|
n, idx: integer;
|
|
S: String;
|
|
Env: TStringList;
|
|
begin
|
|
// Createe local copy
|
|
if FState <> dsNone then
|
|
begin
|
|
Env := TStringList.Create;
|
|
try
|
|
Env.Assign(Environment);
|
|
|
|
// Check for nonexisting and unchanged vars
|
|
for n := 0 to FCurEnvironment.Count - 1 do
|
|
begin
|
|
S := FCurEnvironment[n];
|
|
idx := Env.IndexOfName(GetPart([], ['='], S, False, False));
|
|
if idx = -1
|
|
then ReqCmd(dcEnvironment, [S, False])
|
|
else begin
|
|
if Env[idx] = S
|
|
then Env.Delete(idx);
|
|
end;
|
|
end;
|
|
|
|
// Set the remaining
|
|
for n := 0 to Env.Count - 1 do
|
|
begin
|
|
S := Env[n];
|
|
//Skip functions etc.
|
|
if Pos('=()', S) <> 0 then Continue;
|
|
ReqCmd(dcEnvironment, [S, True]);
|
|
end;
|
|
finally
|
|
Env.Free;
|
|
end;
|
|
end;
|
|
FCurEnvironment.Assign(FEnvironment);
|
|
end;
|
|
|
|
function TDebuggerIntf.GetRunErrorText(ARunError: Integer): string;
|
|
begin
|
|
Result := '';
|
|
case ARunError of
|
|
1: Result := rsRunErrorInvalidFunctionNumber;
|
|
2: Result := rsRunErrorFileNotFound;
|
|
3: Result := rsRunErrorPathNotFound;
|
|
4: Result := rsRunErrorTooManyOpenFiles;
|
|
5: Result := rsRunErrorFileAccessDenied;
|
|
6: Result := rsRunErrorInvalidFileHandle;
|
|
12: Result := rsRunErrorInvalidFileAccessCode;
|
|
15: Result := rsRunErrorInvalidDriveNumber;
|
|
16: Result := rsRunErrorCannotRemoveCurrentDirect;
|
|
17: Result := rsRunErrorCannotRenameAcrossDrives;
|
|
100: Result := rsRunErrorDiskReadError;
|
|
101: Result := rsRunErrorDiskWriteError;
|
|
102: Result := rsRunErrorFileNotAssigned;
|
|
103: Result := rsRunErrorFileNotOpen;
|
|
104: Result := rsRunErrorFileNotOpenForInput;
|
|
105: Result := rsRunErrorFileNotOpenForOutput;
|
|
106: Result := rsRunErrorInvalidNumericFormat;
|
|
107: Result := rsRunErrorInvalidEnumeration;
|
|
150: Result := rsRunErrorDiskIsWriteProtected;
|
|
151: Result := rsRunErrorBadDriveRequestStructLeng;
|
|
152: Result := rsRunErrorDriveNotReady;
|
|
154: Result := rsRunErrorCRCErrorInData;
|
|
156: Result := rsRunErrorDiskSeekError;
|
|
157: Result := rsRunErrorUnknownMediaType;
|
|
158: Result := rsRunErrorSectorNotFound;
|
|
159: Result := rsRunErrorPrinterOutOfPaper;
|
|
160: Result := rsRunErrorDeviceWriteFault;
|
|
161: Result := rsRunErrorDeviceReadFault;
|
|
162: Result := rsRunErrorHardwareFailure;
|
|
200: Result := rsRunErrorDivisionByZero;
|
|
201: Result := rsRunErrorRangeCheckError;
|
|
202: Result := rsRunErrorStackOverflowError;
|
|
203: Result := rsRunErrorHeapOverflowError;
|
|
204: Result := rsRunErrorInvalidPointerOperation;
|
|
205: Result := rsRunErrorFloatingPointOverflow;
|
|
206: Result := rsRunErrorFloatingPointUnderflow;
|
|
207: Result := rsRunErrorInvalidFloatingPointOpera;
|
|
210: Result := rsRunErrorObjectNotInitialized;
|
|
211: Result := rsRunErrorCallToAbstractMethod;
|
|
212: Result := rsRunErrorStreamRegistrationError;
|
|
213: Result := rsRunErrorCollectionIndexOutOfRange;
|
|
214: Result := rsRunErrorCollectionOverflowError;
|
|
215: Result := rsRunErrorArithmeticOverflowError;
|
|
216: Result := rsRunErrorGeneralProtectionFault;
|
|
217: Result := rsRunErrorUnhandledExceptionOccurre;
|
|
218: Result := rsRunErrorInvalidValueSpecified;
|
|
219: Result := rsRunErrorInvalidTypecast;
|
|
222: Result := rsRunErrorVariantDispatchError;
|
|
223: Result := rsRunErrorVariantArrayCreate;
|
|
224: Result := rsRunErrorVariantIsNotAnArray;
|
|
225: Result := rsRunErrorVarArrayBoundsCheckError;
|
|
227: Result := rsRunErrorAssertionFailedError;
|
|
229: Result := rsRunErrorSafecallErrorCheck;
|
|
231: Result := rsRunErrorExceptionStackCorrupted;
|
|
232: Result := rsRunErrorThreadsNotSupported;
|
|
end;
|
|
end;
|
|
|
|
function TDebuggerIntf.GetPseudoTerminal: TPseudoTerminal;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
//function TDebuggerIntf.GetUnitInfoProvider: TDebuggerUnitInfoProvider;
|
|
//begin
|
|
// Result := FUnitInfoProvider;
|
|
// if Result = nil then
|
|
// Result := FInternalUnitInfoProvider;
|
|
//end;
|
|
|
|
function TDebuggerIntf.GetIsIdle: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
function TDebuggerIntf.Evaluate(const AExpression: String;
|
|
ACallback: TDBGEvaluateResultCallback; EvalFlags: TWatcheEvaluateFlags): Boolean;
|
|
begin
|
|
Result := ReqCmd(dcEvaluate, [AExpression, Integer(EvalFlags)], TMethod(ACallback));
|
|
end;
|
|
|
|
function TDebuggerIntf.GetProcessList(AList: TRunningProcessInfoList): boolean;
|
|
begin
|
|
result := false;
|
|
end;
|
|
|
|
class function TDebuggerIntf.ExePaths: String;
|
|
begin
|
|
Result := '';
|
|
end;
|
|
|
|
class function TDebuggerIntf.ExePathsMruGroup: TDebuggerClass;
|
|
begin
|
|
Result := Self;
|
|
end;
|
|
|
|
class function TDebuggerIntf.HasExePath: boolean;
|
|
begin
|
|
Result := NeedsExePath;
|
|
end;
|
|
|
|
class function TDebuggerIntf.NeedsExePath: boolean;
|
|
begin
|
|
Result := true; // most debugger are external and have an exe path
|
|
end;
|
|
|
|
class function TDebuggerIntf.RequiredCompilerOpts(ATargetCPU, ATargetOS: String): TDebugCompilerRequirements;
|
|
begin
|
|
Result := [];
|
|
end;
|
|
|
|
class function TDebuggerIntf.SupportedFeatures: TDBGFeatures;
|
|
begin
|
|
Result := [];
|
|
end;
|
|
|
|
function TDebuggerIntf.GetCommands: TDBGCommands;
|
|
begin
|
|
Result := COMMANDMAP[State] * GetSupportedCommands;
|
|
end;
|
|
|
|
class function TDebuggerIntf.GetProperties: TDebuggerProperties;
|
|
var
|
|
idx: Integer;
|
|
begin
|
|
if MDebuggerPropertiesList = nil
|
|
then MDebuggerPropertiesList := TStringList.Create;
|
|
idx := MDebuggerPropertiesList.IndexOf(ClassName);
|
|
if idx = -1
|
|
then begin
|
|
Result := CreateProperties;
|
|
MDebuggerPropertiesList.AddObject(ClassName, Result)
|
|
end
|
|
else begin
|
|
Result := TDebuggerProperties(MDebuggerPropertiesList.Objects[idx]);
|
|
end;
|
|
end;
|
|
|
|
function TDebuggerIntf.GetState: TDBGState;
|
|
begin
|
|
Result := FState;
|
|
end;
|
|
|
|
function TDebuggerIntf.GetWatches: TWatchesSupplierIntf;
|
|
begin
|
|
Result := FWatches;
|
|
end;
|
|
|
|
function TDebuggerIntf.ReqCmd(const ACommand: TDBGCommand;
|
|
const AParams: array of const): Boolean;
|
|
var
|
|
dummy: TMethod;
|
|
begin
|
|
dummy.Code := nil;
|
|
dummy.Data := nil;
|
|
Result := ReqCmd(ACommand, AParams, dummy);
|
|
end;
|
|
|
|
class function TDebuggerIntf.GetSupportedCommands: TDBGCommands;
|
|
begin
|
|
Result := [];
|
|
end;
|
|
|
|
function TDebuggerIntf.GetTargetWidth: Byte;
|
|
begin
|
|
Result := SizeOf(PtrInt)*8;
|
|
end;
|
|
|
|
function TDebuggerIntf.GetWaiting: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Init;
|
|
begin
|
|
FExitCode := 0;
|
|
FErrorStateMessage := '';
|
|
FErrorStateInfo := '';
|
|
SetState(dsIdle);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.JumpTo(const ASource: String; const ALine: Integer);
|
|
begin
|
|
ReqCmd(dcJumpTo, [ASource, ALine]);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Attach(AProcessID: String);
|
|
begin
|
|
if State = dsIdle then SetState(dsStop); // Needed, because no filename was set
|
|
ReqCmd(dcAttach, [AProcessID]);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Detach;
|
|
begin
|
|
ReqCmd(dcDetach, []);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SendConsoleInput(AText: String);
|
|
begin
|
|
ReqCmd(dcSendConsoleInput, [AText]);
|
|
end;
|
|
|
|
function TDebuggerIntf.Modify(const AExpression, AValue: String): Boolean;
|
|
begin
|
|
Result := ReqCmd(dcModify, [AExpression, AValue]);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Pause;
|
|
begin
|
|
ReqCmd(dcPause, []);
|
|
end;
|
|
|
|
function TDebuggerIntf.ReqCmd(const ACommand: TDBGCommand;
|
|
const AParams: array of const; const ACallback: TMethod): Boolean;
|
|
begin
|
|
if FState = dsNone then Init;
|
|
if ACommand in Commands
|
|
then begin
|
|
Result := RequestCommand(ACommand, AParams, ACallback);
|
|
if not Result then begin
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.ReqCmd failed: ',dbgs(ACommand));
|
|
end;
|
|
end
|
|
else begin
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.ReqCmd Command not supported: ',
|
|
dbgs(ACommand),' ClassName=',ClassName);
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Run;
|
|
begin
|
|
ReqCmd(dcRun, []);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.StepTo(const ASource: String; const ALine: Integer);
|
|
begin
|
|
ReqCmd(dcStepTo, [ASource, ALine]);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.RunTo(const ASource: String; const ALine: Integer);
|
|
begin
|
|
ReqCmd(dcRunTo, [ASource, ALine]);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetDebuggerEnvironment (const AValue: TStrings );
|
|
begin
|
|
FDebuggerEnvironment.Assign(AValue);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetEnabledFeatures(AValue: TDBGFeatures);
|
|
begin
|
|
AValue := AValue * SupportedFeatures;
|
|
if FEnabledFeatures = AValue then Exit;
|
|
FEnabledFeatures := AValue;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetEnvironment(const AValue: TStrings);
|
|
begin
|
|
FEnvironment.Assign(AValue);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetExitCode(const AValue: Integer);
|
|
begin
|
|
FExitCode := AValue;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetFileName(const AValue: String);
|
|
begin
|
|
if FFileName <> AValue
|
|
then begin
|
|
DebugLn(DBG_VERBOSE, '[TDebuggerIntf.SetFileName] "', AValue, '"');
|
|
if FState in [dsRun, dsPause]
|
|
then begin
|
|
Stop;
|
|
// check if stopped
|
|
if FState <> dsStop
|
|
then SetState(dsError);
|
|
end;
|
|
|
|
if FState = dsStop
|
|
then begin
|
|
// Reset state
|
|
FFileName := '';
|
|
ResetStateToIdle;
|
|
ChangeFileName;
|
|
end;
|
|
|
|
FFileName := AValue;
|
|
// TODO: Why?
|
|
if (FFilename <> '') and (FState = dsIdle) and ChangeFileName
|
|
then SetState(dsStop);
|
|
end
|
|
else
|
|
if FileName = '' then
|
|
ResetStateToIdle;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.ResetStateToIdle;
|
|
begin
|
|
FExitCode := 0;
|
|
SetState(dsIdle);
|
|
end;
|
|
|
|
//class procedure TDebuggerIntf.SetProperties(const AProperties: TDebuggerProperties);
|
|
//var
|
|
// Props: TDebuggerProperties;
|
|
//begin
|
|
// if AProperties = nil then Exit;
|
|
// Props := GetProperties;
|
|
// if Props = AProperties then Exit;
|
|
//
|
|
// if Props = nil then Exit; // they weren't created ?
|
|
// Props.Assign(AProperties);
|
|
//end;
|
|
|
|
class function TDebuggerIntf.RequiresLocalExecutable: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.TestCmd(const ACommand: String);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetState(const AValue: TDBGState);
|
|
var
|
|
OldState: TDBGState;
|
|
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
|
|
then begin
|
|
if AValue <> dsStop then
|
|
FSkipStopMessage := False;
|
|
DebugLnEnter(DBG_STATE, ['DebuggerState: Setting to ', dbgs(AValue),', from ', dbgs(FState)]);
|
|
OldState := FState;
|
|
FState := AValue;
|
|
LockCommandProcessing;
|
|
try
|
|
DoBeforeState(OldState);
|
|
try
|
|
FThreads.DoStateChange(OldState);
|
|
FCallStack.DoStateChange(OldState);
|
|
FBreakpoints.DoStateChange(OldState);
|
|
FLocals.DoStateChange(OldState);
|
|
FLineInfo.DoStateChange(OldState);
|
|
FRegisters.DoStateChange(OldState);
|
|
FDisassembler.DoStateChange(OldState);
|
|
FWatches.DoStateChange(OldState);
|
|
finally
|
|
DoState(OldState);
|
|
end;
|
|
finally
|
|
UnLockCommandProcessing;
|
|
DebugLnExit(DBG_STATE, ['DebuggerState: Finished ', dbgs(AValue)]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.SetErrorState(const AMsg: String; const AInfo: String = '');
|
|
begin
|
|
if FErrorStateMessage = ''
|
|
then FErrorStateMessage := AMsg;
|
|
if FErrorStateInfo = ''
|
|
then FErrorStateInfo := AInfo;
|
|
SetState(dsError);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.DoRelease;
|
|
begin
|
|
SetState(dsDestroying);
|
|
if FReleaseLock > 0
|
|
then exit;
|
|
|
|
FReleaseLock := -1;
|
|
Self.Free;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.LockRelease;
|
|
begin
|
|
inc(FReleaseLock);
|
|
DebugLnEnter(DBG_VERBOSE and (FReleaseLock >= 0), ['> TDebuggerIntf.LockRelease ',FReleaseLock]);
|
|
end;
|
|
|
|
procedure TDebuggerIntf.UnlockRelease;
|
|
begin
|
|
DebugLnExit(DBG_VERBOSE and (FReleaseLock >= 0), ['< TDebuggerIntf.UnlockRelease ',FReleaseLock]);
|
|
dec(FReleaseLock);
|
|
if (FReleaseLock = 0) and (State = dsDestroying)
|
|
then Release;
|
|
end;
|
|
|
|
procedure TDebuggerIntf.StepInto;
|
|
begin
|
|
if ReqCmd(dcStepInto, []) then exit;
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.StepInto Class=',ClassName,' failed.');
|
|
end;
|
|
|
|
procedure TDebuggerIntf.StepOverInstr;
|
|
begin
|
|
if ReqCmd(dcStepOverInstr, []) then exit;
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.StepOverInstr Class=',ClassName,' failed.');
|
|
end;
|
|
|
|
procedure TDebuggerIntf.StepIntoInstr;
|
|
begin
|
|
if ReqCmd(dcStepIntoInstr, []) then exit;
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.StepIntoInstr Class=',ClassName,' failed.');
|
|
end;
|
|
|
|
procedure TDebuggerIntf.StepOut;
|
|
begin
|
|
if ReqCmd(dcStepOut, []) then exit;
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.StepOut Class=', ClassName, ' failed.');
|
|
end;
|
|
|
|
procedure TDebuggerIntf.StepOver;
|
|
begin
|
|
if ReqCmd(dcStepOver, []) then exit;
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.StepOver Class=',ClassName,' failed.');
|
|
end;
|
|
|
|
procedure TDebuggerIntf.Stop;
|
|
begin
|
|
if ReqCmd(dcStop,[]) then exit;
|
|
DebugLn(DBG_WARNINGS, 'TDebuggerIntf.Stop Class=',ClassName,' failed.');
|
|
end;
|
|
|
|
constructor TBaseDebugManagerIntf.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
|
|
FValueFormatterList := TStringList.Create;
|
|
FValueFormatterList.Sorted := True;
|
|
FValueFormatterList.Duplicates := dupError;
|
|
end;
|
|
|
|
class function TBaseDebugManagerIntf.DebuggerCount: Integer;
|
|
begin
|
|
Result := MDebuggerClasses.Count;
|
|
end;
|
|
|
|
destructor TBaseDebugManagerIntf.Destroy;
|
|
begin
|
|
FValueFormatterList.Free;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TBaseDebugManagerIntf.FindDebuggerClass(const Astring: String
|
|
): TDebuggerClass;
|
|
var
|
|
idx: Integer;
|
|
begin
|
|
idx := MDebuggerClasses.IndexOf(AString);
|
|
if idx = -1
|
|
then Result := nil
|
|
else Result := TDebuggerClass(MDebuggerClasses.Objects[idx]);
|
|
end;
|
|
|
|
function TBaseDebugManagerIntf.FormatValue(const aSymbolKind: TDBGSymbolKind;
|
|
const aTypeName, aValue: string): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := FValueFormatterList.IndexOf(ValueFormatterKey(aSymbolKind, aTypeName));
|
|
if I>=0 then
|
|
Result := TStringFunction(FValueFormatterList.Objects[I])(aValue)
|
|
else
|
|
Result := aValue;
|
|
end;
|
|
|
|
function TBaseDebugManagerIntf.FormatValue(const aDBGType: TDBGType;
|
|
const aValue: string): string;
|
|
begin
|
|
if aDBGType=nil then
|
|
Result := aValue
|
|
else
|
|
Result := FormatValue(aDBGType.Kind, aDBGType.TypeName, aValue);
|
|
end;
|
|
|
|
class function TBaseDebugManagerIntf.GetDebuggerClass(const AIndex: Integer): TDebuggerClass;
|
|
begin
|
|
Result := TDebuggerClass(MDebuggerClasses.Objects[AIndex]);
|
|
end;
|
|
|
|
class function TBaseDebugManagerIntf.GetDebuggerClassByName(const AIndex: String
|
|
): TDebuggerClass;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := MDebuggerClasses.Count - 1;
|
|
while i >= 0 do begin
|
|
Result := TDebuggerClass(MDebuggerClasses.Objects[i]);
|
|
if CompareText(Result.ClassName, AIndex) = 0 then
|
|
exit;
|
|
dec(i);
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TBaseDebugManagerIntf.RegisterValueFormatter(
|
|
const aSymbolKind: TDBGSymbolKind; const aTypeName: string;
|
|
const aFunc: TStringFunction);
|
|
begin
|
|
FValueFormatterList.AddObject(ValueFormatterKey(aSymbolKind, aTypeName), TObject(aFunc));
|
|
end;
|
|
|
|
function TBaseDebugManagerIntf.ValueFormatterKey(
|
|
const aSymbolKind: TDBGSymbolKind; const aTypeName: string): string;
|
|
begin
|
|
Result := UpperCase(IntToStr(Ord(aSymbolKind))+':'+aTypeName);
|
|
end;
|
|
|
|
|
|
initialization
|
|
MDebuggerPropertiesList := nil;
|
|
{$IFDEF DBG_STATE} {$DEFINE DBG_STATE_EVENT} {$ENDIF}
|
|
{$IFDEF DBG_EVENTS} {$DEFINE DBG_STATE_EVENT} {$ENDIF}
|
|
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
|
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
|
DBG_STATE := DebugLogger.FindOrRegisterLogGroup('DBG_STATE' {$IFDEF DBG_STATE} , True {$ENDIF} );
|
|
DBG_EVENTS := DebugLogger.FindOrRegisterLogGroup('DBG_EVENTS' {$IFDEF DBG_EVENTS} , True {$ENDIF} );
|
|
DBG_STATE_EVENT := DebugLogger.FindOrRegisterLogGroup('DBG_STATE_EVENT' {$IFDEF DBG_STATE_EVENT} , True {$ENDIF} );
|
|
DBG_DATA_MONITORS := DebugLogger.FindOrRegisterLogGroup('DBG_DATA_MONITORS' {$IFDEF DBG_DATA_MONITORS} , True {$ENDIF} );
|
|
DBG_DISASSEMBLER := DebugLogger.FindOrRegisterLogGroup('DBG_DISASSEMBLER' {$IFDEF DBG_DISASSEMBLER} , True {$ENDIF} );
|
|
|
|
MDebuggerClasses := TStringList.Create;
|
|
MDebuggerClasses.Sorted := True;
|
|
MDebuggerClasses.Duplicates := dupError;
|
|
|
|
finalization
|
|
DoFinalization;
|
|
FreeAndNil(MDebuggerClasses);
|
|
|
|
end.
|