mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-08 14:58:07 +02:00
533 lines
17 KiB
ObjectPascal
533 lines
17 KiB
ObjectPascal
unit FpDbgXtensaClasses;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$packrecords c}
|
|
{$modeswitch advancedrecords}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
SysUtils,
|
|
FpDbgClasses,
|
|
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
|
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif},
|
|
FpDbgRsp, FpDbgRspClasses, FpDbgCommon, FpdMemoryTools,
|
|
FpErrorMessages;
|
|
|
|
const
|
|
SPindexDwarf = 1; // Also known as a1, Index refers to active window
|
|
PCIndexDwarf = 100; // Dwarf index (assumed)
|
|
nPC = 'PC';
|
|
nReturnPC = 'a0';
|
|
nSP = 'a1';
|
|
ReturnPCIndexDwarf = 0; // Also known as a0, Index refers to active window
|
|
WindowBaseIndex = 65;
|
|
WindowStartIndex = 66;
|
|
PSIndex = 67;
|
|
nWindowBase = 'windowbase';
|
|
nWindowStart = 'windowstart';
|
|
nPS = 'ps';
|
|
|
|
type
|
|
{ TDbgXtensaThread }
|
|
|
|
TDbgXtensaThread = class(TDbgRspThread)
|
|
private
|
|
const
|
|
lastCPURegIndex = 64; // PC + 64 registers
|
|
// Offsets to load specific registers from register data
|
|
// These are byte offsets, to be used when reading from the raw byte register data
|
|
WindowBaseOffset = 276; // Dependent on Windowed option...
|
|
WindowStartOffset = 280; // "
|
|
PSOffset = 292; // "
|
|
RegArrayByteLength = 420; // Depends on qemu options, but this seems to be the smallest size to handle. Only show basic registers, so rest can be ignored for now.
|
|
// Ordered registers index
|
|
// PC, ar0..ar63, wb, ws, ps
|
|
PCindex = 0; // Offset into raw register array
|
|
|
|
protected
|
|
procedure LoadRegisterCache; override;
|
|
procedure SaveRegisterCache; override;
|
|
function GetReturnPC: TDbgPtr;
|
|
function GetStackUnwinder: TDbgStackUnwinder; override;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure LoadRegisterValues; override;
|
|
procedure SetRegisterValue(AName: string; AValue: QWord); override;
|
|
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
|
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
|
// procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
|
|
function GetStackPointerRegisterValue: TDbgPtr; override;
|
|
// procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
|
end;
|
|
|
|
{ TDbgXtensaProcess }
|
|
|
|
TDbgXtensaProcess = class(TDbgRspProcess)
|
|
private const
|
|
FNumRegisters = 68; // pc, ar0..ar63, WindowBase, WindowStart, PS
|
|
protected
|
|
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
|
|
function CreateBreakPointTargetHandler: TFpBreakPointTargetHandler; override;
|
|
public
|
|
class function isSupported(target: TTargetDescriptor): boolean; override;
|
|
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
|
|
AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel;
|
|
AProcessConfig: TDbgProcessConfig = nil); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TXtensaBreakInfo = object
|
|
const
|
|
_CODE: DWord = $F06D; // ILL.N -> this is a 16 bit narrow instruction
|
|
end;
|
|
|
|
TXtensaBreakPointTargetHandler = specialize TRspBreakPointTargetHandler<Word, TXtensaBreakInfo>;
|
|
|
|
|
|
{ TDbgStackUnwinderXtensa }
|
|
|
|
TDbgStackUnwinderXtensa = class(TDbgStackUnwinder)
|
|
private
|
|
FThread: TDbgThread;
|
|
FProcess: TDbgProcess;
|
|
FAddressSize: Integer;
|
|
FLastFrameBaseIncreased: Boolean;
|
|
FCodeReadErrCnt: integer;
|
|
|
|
FWindowBase: uint32;
|
|
FWindowStart: uint32;
|
|
protected
|
|
property Process: TDbgProcess read FProcess;
|
|
property Thread: TDbgThread read FThread;
|
|
property AddressSize: Integer read FAddressSize;
|
|
public
|
|
constructor Create(AProcess: TDbgProcess);
|
|
procedure InitForThread(AThread: TDbgThread); override;
|
|
procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry; out CodePointer,
|
|
StackPointer, FrameBasePointer: TDBGPtr); override;
|
|
procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
|
out ANewFrame: TDbgCallstackEntry); override;
|
|
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
|
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
|
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
FpDbgDisasXtensa, FpDbgDwarfDataClasses;
|
|
|
|
var
|
|
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
|
|
|
|
{ TDbgXtensaThread }
|
|
|
|
procedure TDbgXtensaThread.LoadRegisterCache;
|
|
var
|
|
regs: TBytes;
|
|
i: integer;
|
|
begin
|
|
if not FRegs.Initialized then
|
|
begin
|
|
SetLength(regs, RegArrayByteLength);
|
|
FRegs.Initialized := TDbgXtensaProcess(Process).RspConnection.ReadRegisters(regs[0], length(regs));
|
|
// 32 bit LE registers
|
|
for i := 0 to lastCPURegIndex do // PC, ar0..ar63
|
|
FRegs.regs[i] := regs[4*i] + (regs[4*i + 1] shl 8) + (regs[4*i + 2] shl 16) + (regs[4*i + 3] shl 24);
|
|
|
|
i := WindowBaseOffset;
|
|
FRegs.regs[WindowBaseIndex] := regs[i] + (regs[i + 1] shl 8) + (regs[i + 2] shl 16) + (regs[i + 3] shl 24);
|
|
i := WindowStartOffset;
|
|
FRegs.regs[WindowStartIndex] := regs[i] + (regs[i + 1] shl 8) + (regs[i + 2] shl 16) + (regs[i + 3] shl 24);
|
|
i := PSOffset;
|
|
FRegs.regs[PSIndex] := regs[i] + (regs[i + 1] shl 8) + (regs[i + 2] shl 16) + (regs[i + 3] shl 24);
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgXtensaThread.SaveRegisterCache;
|
|
procedure CopyDWordToByteArray( const val: DWord; regs: PByte);
|
|
begin
|
|
regs[0] := val;
|
|
regs[1] := val shr 8;
|
|
regs[2] := val shr 16;
|
|
regs[3] := val shr 24;
|
|
end;
|
|
|
|
var
|
|
regs: TBytes;
|
|
i: Integer;
|
|
begin
|
|
exit; // TODO: Need to determine which other registers will also change in case of a subroutine call on the debugger side.
|
|
|
|
if FRegsChanged then
|
|
begin
|
|
SetLength(regs, RegArrayByteLength);
|
|
for i := 0 to lastCPURegIndex do
|
|
CopyDWordToByteArray(FRegs.regs[i], @regs[4*i]);
|
|
|
|
CopyDWordToByteArray(FRegs.regs[WindowBaseIndex], @regs[WindowBaseOffset]);
|
|
CopyDWordToByteArray(FRegs.regs[WindowStartIndex], @regs[WindowStartOffset]);
|
|
CopyDWordToByteArray(FRegs.regs[PSIndex], @regs[PSOffset]);
|
|
end;
|
|
end;
|
|
|
|
function TDbgXtensaThread.GetReturnPC: TDbgPtr;
|
|
var
|
|
wb: TDBGPtr;
|
|
begin
|
|
Result := 0;
|
|
if TDbgXtensaProcess(Process).FIsTerminating then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetStackPointerRegisterValue called while FIsTerminating is set.');
|
|
exit;
|
|
end;
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetStackPointerRegisterValue requesting stack registers.');
|
|
// Windowed ABI: retPC in a0
|
|
ReadDebugReg(WindowBaseIndex, wb);
|
|
wb := wb * 4;
|
|
ReadDebugReg(wb+1, result);
|
|
end;
|
|
|
|
function TDbgXtensaThread.GetStackUnwinder: TDbgStackUnwinder;
|
|
begin
|
|
if FUnwinder = nil then
|
|
FUnwinder := TDbgStackUnwinderXtensa.Create(Process);
|
|
Result := FUnwinder;
|
|
end;
|
|
|
|
destructor TDbgXtensaThread.Destroy;
|
|
begin
|
|
if Assigned(FUnwinder) then
|
|
FreeAndNil(FUnwinder);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDbgXtensaThread.LoadRegisterValues;
|
|
var
|
|
i, j, wb: integer;
|
|
begin
|
|
if TDbgXtensaProcess(Process).FIsTerminating or (TDbgXtensaProcess(Process).FStatus = SIGHUP) then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.LoadRegisterValues called while FIsTerminating is set.');
|
|
exit;
|
|
end;
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
LoadRegisterCache;
|
|
|
|
if FRegs.Initialized then
|
|
begin
|
|
{ FRegs.regs start with PC, then the 64 core registers,
|
|
then a bunch of other system and optionally user registers.
|
|
Only load currently visible registers, PC and WindowBaseOffset for now.
|
|
The start of currently visible core registers is given by WindowBaseOffset }
|
|
|
|
// WindowBaseOffset is a 4 bit value
|
|
wb := 4 * (FRegs.regs[WindowBaseIndex] and $0F);
|
|
// Returned registers start with PC, followed by core register file, so offset index by 1
|
|
for i := 0 to 15 do
|
|
begin
|
|
// Index should wrap around to start of register file
|
|
j := ((wb+i) and 63) + 1;
|
|
FRegisterValueList.DbgRegisterAutoCreate['a'+IntToStr(i)].SetValue(FRegs.regs[j], IntToStr(FRegs.regs[j]), 4, i);
|
|
end;
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(FRegs.regs[0], IntToStr(FRegs.regs[0]), 4, 0);
|
|
FRegisterValueList.DbgRegisterAutoCreate[nWindowBase].SetValue(byte(FRegs.regs[WindowBaseIndex]), IntToStr(byte(FRegs.regs[WindowBaseIndex])),1 , WindowBaseIndex);
|
|
FRegisterValueList.DbgRegisterAutoCreate[nWindowStart].SetValue(word(FRegs.regs[WindowStartIndex]), IntToStr(word(FRegs.regs[WindowStartIndex])),2 , WindowStartIndex);
|
|
FRegisterValueList.DbgRegisterAutoCreate[nPS].SetValue(byte(FRegs.regs[PSIndex]), IntToStr(byte(FRegs.regs[PSIndex])),1 , 0);
|
|
FRegisterValueList.DbgRegisterAutoCreate[nPS].SetValue(byte(FRegs.regs[PSIndex]), IntToStr(byte(FRegs.regs[PSIndex])),1 , 0);
|
|
FRegisterValueListValid := true;
|
|
end
|
|
else
|
|
DebugLn(DBG_WARNINGS, 'Warning: Could not update registers');
|
|
end;
|
|
|
|
procedure TDbgXtensaThread.SetRegisterValue(AName: string; AValue: QWord);
|
|
var
|
|
i, err: integer;
|
|
res: boolean;
|
|
begin
|
|
if AName[1] = 'a' then
|
|
begin
|
|
val(copy(AName, 2, length(Aname)), i, err);
|
|
res := (err = 0) and (i <= 15);
|
|
if res then
|
|
res := TDbgRspProcess(Process).RspConnection.WriteDebugReg(i, byte(AValue));
|
|
end
|
|
else if AName = nPC then
|
|
res := TDbgRspProcess(Process).RspConnection.WriteDebugReg(PCindex, dword(AValue))
|
|
else
|
|
res := False;
|
|
|
|
if not res then
|
|
DebugLn(DBG_WARNINGS, 'Error setting register %s to %u', [AName, AValue]);
|
|
end;
|
|
|
|
function TDbgXtensaThread.GetInstructionPointerRegisterValue: TDbgPtr;
|
|
begin
|
|
Result := 0;
|
|
if TDbgXtensaProcess(Process).FIsTerminating then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetInstructionPointerRegisterValue called while FIsTerminating is set.');
|
|
exit;
|
|
end;
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetInstructionPointerRegisterValue requesting PC.');
|
|
ReadDebugReg(PCindex, Result);
|
|
end;
|
|
|
|
function TDbgXtensaThread.GetStackBasePointerRegisterValue: TDbgPtr;
|
|
begin
|
|
Result := 0;
|
|
if TDbgXtensaProcess(Process).FIsTerminating then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetStackBasePointerRegisterValue called while FIsTerminating is set.');
|
|
exit;
|
|
end;
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetStackBasePointerRegisterValue requesting base registers.');
|
|
// Todo: check FPC implementation of stack frame for ESP32
|
|
Result := GetStackPointerRegisterValue;
|
|
end;
|
|
|
|
function TDbgXtensaThread.GetStackPointerRegisterValue: TDbgPtr;
|
|
var
|
|
wb: TDBGPtr;
|
|
begin
|
|
Result := 0;
|
|
if TDbgXtensaProcess(Process).FIsTerminating then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'TDbgXtensaProcess.GetStackPointerRegisterValue called while FIsTerminating is set.');
|
|
exit;
|
|
end;
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
DebugLn(DBG_VERBOSE, 'TDbgXtensaProcess.GetStackPointerRegisterValue requesting stack registers.');
|
|
// Windowed ABI: SP in a1
|
|
if ReadDebugReg(WindowBaseIndex, wb) then
|
|
begin
|
|
wb := wb * 4;
|
|
ReadDebugReg(wb+2, result);
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
{ TDbgXtensaProcess }
|
|
|
|
function TDbgXtensaProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
|
|
begin
|
|
IsMainThread:=False;
|
|
if AthreadIdentifier<>feInvalidHandle then
|
|
begin
|
|
IsMainThread := AthreadIdentifier=ProcessID;
|
|
result := TDbgXtensaThread.Create(Self, AthreadIdentifier, AthreadIdentifier);
|
|
end
|
|
else
|
|
result := nil;
|
|
end;
|
|
|
|
function
|
|
TDbgXtensaProcess.CreateBreakPointTargetHandler: TFpBreakPointTargetHandler;
|
|
begin
|
|
Result := TXtensaBreakPointTargetHandler.Create(Self);
|
|
end;
|
|
|
|
constructor TDbgXtensaProcess.Create(const AFileName: string;
|
|
AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager;
|
|
AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig);
|
|
begin
|
|
FRegArrayLength := FNumRegisters;
|
|
inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel, AProcessConfig);
|
|
end;
|
|
|
|
destructor TDbgXtensaProcess.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
class function TDbgXtensaProcess.isSupported(target: TTargetDescriptor): boolean;
|
|
begin
|
|
result := (target.OS = osEmbedded) and (target.machineType = mtXTENSA);
|
|
end;
|
|
|
|
{ TDbgStackUnwinderXtensa }
|
|
|
|
constructor TDbgStackUnwinderXtensa.Create(AProcess: TDbgProcess);
|
|
begin
|
|
FProcess := AProcess;
|
|
FAddressSize := 4;
|
|
FCodeReadErrCnt := 0;
|
|
end;
|
|
|
|
procedure TDbgStackUnwinderXtensa.InitForThread(AThread: TDbgThread);
|
|
begin
|
|
FThread := AThread;
|
|
end;
|
|
|
|
procedure TDbgStackUnwinderXtensa.InitForFrame(
|
|
ACurrentFrame: TDbgCallstackEntry; out CodePointer, StackPointer,
|
|
FrameBasePointer: TDBGPtr);
|
|
var
|
|
R: TDbgRegisterValue;
|
|
begin
|
|
CodePointer := ACurrentFrame.AnAddress;
|
|
|
|
// Frame pointer is a7 (if used)
|
|
FrameBasePointer := ACurrentFrame.FrameAdress;
|
|
|
|
StackPointer := 0;
|
|
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(SPindexDwarf);
|
|
if R = nil then exit;
|
|
StackPointer := R.NumValue;
|
|
end;
|
|
|
|
procedure TDbgStackUnwinderXtensa.GetTopFrame(out CodePointer, StackPointer,
|
|
FrameBasePointer: TDBGPtr; out ANewFrame: TDbgCallstackEntry);
|
|
var
|
|
i: Integer;
|
|
R: TDbgRegisterValue;
|
|
begin
|
|
FLastFrameBaseIncreased := True;
|
|
CodePointer := Thread.GetInstructionPointerRegisterValue;
|
|
StackPointer := Thread.GetStackPointerRegisterValue;
|
|
FrameBasePointer := Thread.GetStackBasePointerRegisterValue;
|
|
ANewFrame := TDbgCallstackEntry.create(Thread, 0, FrameBasePointer, CodePointer);
|
|
|
|
// Frame pointer may not have been updated yet
|
|
if FrameBasePointer > StackPointer then
|
|
FrameBasePointer := StackPointer;
|
|
|
|
i := Thread.RegisterValueList.Count;
|
|
while i > 0 do begin
|
|
dec(i);
|
|
R := Thread.RegisterValueList[i];
|
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx);
|
|
end;
|
|
|
|
FWindowBase := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(WindowBaseIndex).NumValue and $F;
|
|
FWindowStart := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(WindowStartIndex).NumValue and $FFFF;
|
|
end;
|
|
|
|
function TDbgStackUnwinderXtensa.Unwind(AFrameIndex: integer; var CodePointer,
|
|
StackPointer, FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry;
|
|
out ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult;
|
|
const
|
|
MAX_FRAMES = 500; // safety net
|
|
Size = 4;
|
|
var
|
|
Address, returnAddress: TDBGPtr;
|
|
callSize: byte;
|
|
j, k, stackRegCount, numRegsRead: integer;
|
|
spilled: boolean;
|
|
stackRegs: array of TDBGPtr;
|
|
tmpReg: uint32;
|
|
LastFrameBase: TDBGPtr;
|
|
regName: string;
|
|
begin
|
|
ANewFrame := nil;
|
|
Result := suFailed;
|
|
|
|
if (StackPointer > $40000000) or (StackPointer < $3F000000) or
|
|
(CodePointer < $40000001) or not FLastFrameBaseIncreased then
|
|
exit;
|
|
|
|
LastFrameBase := FrameBasePointer;
|
|
|
|
ReturnAddress := ACurrentFrame.RegisterValueList.FindRegisterByName(nReturnPC).NumValue;
|
|
spilled := FWindowStart and (1 shl FWindowBase) = 0;
|
|
|
|
FCodeReadErrCnt := 0;
|
|
|
|
callSize := uint32(returnAddress) shr 30;
|
|
if callsize = 0 then
|
|
exit;
|
|
|
|
Address := (returnAddress and $3FFFFFFF) or $40000000;
|
|
// Check if start of this window frame is live or spilled
|
|
if not spilled then
|
|
begin
|
|
// Move window base to previous window frame
|
|
// Wraparound subtraction
|
|
FWindowBase := (FWindowBase + 16 - callsize) and $F;
|
|
spilled := FWindowStart and (1 shl FWindowBase) = 0;
|
|
end;
|
|
|
|
stackRegCount := 4*callSize;
|
|
SetLength(stackRegs, stackRegCount);
|
|
if not spilled then
|
|
begin
|
|
// Live registers, read from register file
|
|
j := ((4*FWindowBase) and 63);
|
|
numRegsRead := stackRegCount;
|
|
for k := 0 to stackRegCount-1 do
|
|
stackRegs[k] := TDbgRspThread(Thread).InternalRegs.regs[((j+k) and 63)+1]; // wraparound indexing
|
|
end
|
|
else
|
|
begin
|
|
// Registers spilled to stack, read from memory
|
|
numRegsRead := 0;
|
|
for j := 0 to callSize-1 do
|
|
begin
|
|
for k := 0 to 3 do
|
|
begin
|
|
if j = 0 then
|
|
begin
|
|
if not Process.ReadData(StackPointer-16+4*k, 4, tmpReg) then Break;
|
|
end
|
|
else
|
|
begin
|
|
if (stackRegs[1] < $3F000000) or
|
|
not Process.ReadData(stackRegs[1]-16+4*k, 4, tmpReg) then
|
|
Break;
|
|
end;
|
|
stackRegs[4*j + k] := NtoLE(tmpReg);
|
|
inc(numRegsRead);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
returnAddress := stackRegs[0];
|
|
StackPointer := stackRegs[1];
|
|
FrameBasePointer := StackPointer;
|
|
|
|
ANewFrame:= TDbgCallstackEntry.create(Thread, AFrameIndex, FrameBasePointer, Address);
|
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address), Size, PCIndexDwarf);
|
|
for j := 0 to numRegsRead-1 do
|
|
begin
|
|
regName := 'a' + IntToStr(j);
|
|
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[regName].SetValue(stackRegs[j],
|
|
IntToStr(stackRegs[j]), Size, j);
|
|
end;
|
|
|
|
FLastFrameBaseIncreased := (FrameBasePointer <> 0) and (FrameBasePointer > LastFrameBase);
|
|
Result := suSuccess;
|
|
end;
|
|
|
|
initialization
|
|
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
|
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
|
RegisterDbgOsClasses(TOSDbgClasses.Create(
|
|
TDbgXtensaProcess,
|
|
TDbgXtensaThread,
|
|
TXtensaAsmDecoder));
|
|
end.
|