mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-11 20:18:09 +02:00
FpDebug: refactor stack unwinding for AVR
This commit is contained in:
parent
53474705c4
commit
3b833564d5
@ -58,6 +58,8 @@ type
|
||||
FIsSteppingBreakPoint: boolean;
|
||||
FDidResetInstructionPointer: Boolean;
|
||||
FHasThreadState: boolean;
|
||||
FUnwinder: TDbgStackUnwinder;
|
||||
|
||||
function ReadDebugReg(ind: byte; out AVal: TDbgPtr): boolean;
|
||||
function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||
|
||||
@ -73,6 +75,7 @@ type
|
||||
function RequestInternalPause: Boolean;
|
||||
function CheckSignalForPostponing(AWaitedStatus: integer): Boolean;
|
||||
procedure ResetPauseStates;
|
||||
function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||
public
|
||||
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); override;
|
||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||
@ -85,8 +88,6 @@ type
|
||||
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
||||
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
||||
function GetStackPointerRegisterValue: TDbgPtr; override;
|
||||
|
||||
procedure PrepareCallStackEntryList(AFrameRequired: Integer = -1); override;
|
||||
end;
|
||||
|
||||
{ TDbgAvrProcess }
|
||||
@ -161,6 +162,7 @@ type
|
||||
property Count: integer read DataCount;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@ -363,6 +365,13 @@ begin
|
||||
FDidResetInstructionPointer := False;
|
||||
end;
|
||||
|
||||
function TDbgAvrThread.GetStackUnwinder: TDbgStackUnwinder;
|
||||
begin
|
||||
if FUnwinder = nil then
|
||||
FUnwinder := TDbgStackUnwinderAVR.Create(Process);
|
||||
Result := FUnwinder;
|
||||
end;
|
||||
|
||||
constructor TDbgAvrThread.Create(const AProcess: TDbgProcess;
|
||||
const AID: Integer; const AHandle: THandle);
|
||||
begin
|
||||
@ -526,128 +535,6 @@ begin
|
||||
ReadDebugReg(SPindex, result);
|
||||
end;
|
||||
|
||||
procedure TDbgAvrThread.PrepareCallStackEntryList(AFrameRequired: Integer);
|
||||
const
|
||||
MAX_FRAMES = 50000; // safety net
|
||||
// To read RAM, add data space offset to address
|
||||
DataOffset = $800000;
|
||||
Size = 2;
|
||||
var
|
||||
Address, FrameBase, LastFrameBase: TDBGPtr;
|
||||
CountNeeded, CodeReadErrCnt: integer;
|
||||
AnEntry: TDbgCallstackEntry;
|
||||
R: TDbgRegisterValue;
|
||||
NextIdx: LongInt;
|
||||
OutSideFrame: Boolean;
|
||||
StackPtr: TDBGPtr;
|
||||
startPC, endPC: TDBGPtr;
|
||||
returnAddrStackOffset: word;
|
||||
b: byte;
|
||||
begin
|
||||
// TODO: use AFrameRequired // check if already partly done
|
||||
if FCallStackEntryList = nil then
|
||||
FCallStackEntryList := TDbgCallstackEntryList.Create;
|
||||
if AFrameRequired = -2 then
|
||||
exit;
|
||||
|
||||
if (AFrameRequired >= 0) and (AFrameRequired < FCallStackEntryList.Count) then
|
||||
exit;
|
||||
|
||||
FCallStackEntryList.FreeObjects:=true;
|
||||
if FCallStackEntryList.Count > 0 then begin
|
||||
AnEntry := FCallStackEntryList[FCallStackEntryList.Count - 1];
|
||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(PCindex);
|
||||
if R = nil then exit;
|
||||
Address := R.NumValue;
|
||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(28);
|
||||
if R = nil then exit;
|
||||
FrameBase := R.NumValue;
|
||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(29);
|
||||
if R = nil then exit;
|
||||
FrameBase := FrameBase + (byte(R.NumValue) shl 8);
|
||||
|
||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(SPindex);
|
||||
if R = nil then exit;
|
||||
StackPtr := R.NumValue;
|
||||
end
|
||||
else begin
|
||||
Address := GetInstructionPointerRegisterValue;
|
||||
FrameBase := GetStackBasePointerRegisterValue;
|
||||
StackPtr := GetStackPointerRegisterValue;
|
||||
AnEntry := TDbgCallstackEntry.create(Self, 0, FrameBase, Address);
|
||||
// Top level could be without entry in registerlist / same as GetRegisterValueList / but some code tries to find it here ....
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PCindex);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SPindex);
|
||||
|
||||
// Y pointer register [r28:r29] is used as frame pointer for AVR
|
||||
// Store these to enable stack based parameters to be read correctly
|
||||
// as they are frame pointer relative and dwarf stores the frame pointer under r28
|
||||
//AnEntry.RegisterValueList.DbgRegisterAutoCreate[nFP].SetValue(FrameBase, IntToStr(FrameBase),Size, FPindex);
|
||||
b := byte(FrameBase);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate['r28'].SetValue(b, IntToStr(b),Size, 28);
|
||||
b := (FrameBase and $FF00) shr 8;
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate['r29'].SetValue(b, IntToStr(b),Size, 29);
|
||||
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
end;
|
||||
|
||||
NextIdx := FCallStackEntryList.Count;
|
||||
if AFrameRequired < 0 then
|
||||
AFrameRequired := MaxInt;
|
||||
CountNeeded := AFrameRequired - FCallStackEntryList.Count;
|
||||
LastFrameBase := 0;
|
||||
CodeReadErrCnt := 0;
|
||||
while (CountNeeded > 0) and (FrameBase <> 0) and (FrameBase > LastFrameBase) do
|
||||
begin
|
||||
// Get start/end PC of proc from debug info
|
||||
if not Self.Process.FindProcStartEndPC(Address, startPC, endPC) then
|
||||
begin
|
||||
// Give up for now, it is complicated to locate prologue/epilogue in general without proc limits
|
||||
// ToDo: Perhaps interpret .debug_frame info if available,
|
||||
// or scan forward from address until an epilogue is found.
|
||||
break;
|
||||
end;
|
||||
|
||||
if not TAvrAsmDecoder(Process.Disassembler).GetFunctionFrameReturnAddress(Address, startPC, endPC, returnAddrStackOffset, OutSideFrame) then
|
||||
begin
|
||||
OutSideFrame := False;
|
||||
end;
|
||||
LastFrameBase := FrameBase;
|
||||
|
||||
if OutSideFrame then begin
|
||||
// Before adjustment of frame pointer, or after restoration of frame pointer,
|
||||
// return PC should be located by offset from SP
|
||||
if not Process.ReadData(DataOffset or (StackPtr + returnAddrStackOffset), Size, Address) or (Address = 0) then Break;
|
||||
end
|
||||
else begin
|
||||
// Inside stack frame, return PC should be located by offset from FP
|
||||
if not Process.ReadData(DataOffset or (FrameBase + returnAddrStackOffset), Size, Address) or (Address = 0) then Break;
|
||||
end;
|
||||
// Convert return address from BE to LE, shl 1 to get byte address
|
||||
Address := BEtoN(word(Address)) shl 1;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
StackPtr := FrameBase + returnAddrStackOffset + Size - 1; // After popping return-addr from "StackPtr"
|
||||
FrameBase := StackPtr; // Estimate of previous FP
|
||||
LastFrameBase := LastFrameBase - 1; // Make the loop think that LastFrameBase was smaller
|
||||
{$POP}
|
||||
|
||||
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(Address, IntToStr(Address),Size, PCindex);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SPindex);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate['r28'].SetValue(byte(FrameBase), IntToStr(b),Size, 28);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate['r29'].SetValue((FrameBase and $FF00) shr 8, IntToStr(b),Size, 29);
|
||||
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
Dec(CountNeeded);
|
||||
inc(NextIdx);
|
||||
CodeReadErrCnt := 0;
|
||||
if (NextIdx > MAX_FRAMES) then
|
||||
break;
|
||||
end;
|
||||
if CountNeeded > 0 then // there was an error / not possible to read more frames
|
||||
FCallStackEntryList.SetHasReadAllAvailableFrames;
|
||||
end;
|
||||
|
||||
{ TDbgAvrProcess }
|
||||
|
||||
procedure TDbgAvrProcess.InitializeLoaders;
|
||||
|
@ -47,8 +47,6 @@ type
|
||||
|
||||
TAvrAsmDecoder = class;
|
||||
|
||||
{ TX86DisassemblerInstruction }
|
||||
|
||||
{ TAvrAsmInstruction }
|
||||
|
||||
TAvrAsmInstruction = class(TDbgAsmInstruction)
|
||||
@ -86,6 +84,7 @@ type
|
||||
returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||
function FParseEpilogue(AnAddress, AStartPC, AEndPC: TDBGPtr; out
|
||||
returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean;
|
||||
|
||||
protected
|
||||
function GetLastErrorWasMemReadErr: Boolean; override;
|
||||
function GetMaxInstrSize: integer; override;
|
||||
@ -94,10 +93,11 @@ type
|
||||
function ReadCodeAt(AnAddress: TDBGPtr; var ALen: Cardinal): Boolean; inline;
|
||||
|
||||
public
|
||||
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); override;
|
||||
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String; out AnInfo: TDbgInstInfo); override; overload;
|
||||
procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); override; overload;
|
||||
function GetInstructionInfo(AnAddress: TDBGPtr): TDbgAsmInstruction; override;
|
||||
|
||||
// Don't use, ot really suited to AVR ABI
|
||||
// Don't use, not really suited to AVR ABI
|
||||
function GetFunctionFrameInfo(AnAddress: TDBGPtr; out
|
||||
AnIsOutsideFrame: Boolean): Boolean; override;
|
||||
|
||||
@ -113,10 +113,36 @@ type
|
||||
end;
|
||||
|
||||
|
||||
{ TDbgStackUnwinderAVR }
|
||||
|
||||
TDbgStackUnwinderAVR = class(TDbgStackUnwinder)
|
||||
private
|
||||
FThread: TDbgThread;
|
||||
FProcess: TDbgProcess;
|
||||
FAddressSize: Integer;
|
||||
FLastFrameBaseIncreased: Boolean;
|
||||
FCodeReadErrCnt: integer;
|
||||
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
|
||||
StrUtils, LazClasses, Math;
|
||||
StrUtils, LazClasses, Math,
|
||||
FpDbgAvrClasses;
|
||||
|
||||
var
|
||||
DBG_WARNINGS: PLazLoggerLogGroup;
|
||||
@ -556,7 +582,7 @@ begin
|
||||
end;
|
||||
|
||||
procedure TAvrAsmDecoder.Disassemble(var AAddress: Pointer; out
|
||||
ACodeBytes: String; out ACode: String);
|
||||
ACodeBytes: String; out ACode: String; out AnInfo: TDbgInstInfo);
|
||||
var
|
||||
CodeIdx, r, d, k, q: byte;
|
||||
a: SmallInt;
|
||||
@ -565,6 +591,7 @@ var
|
||||
s1: string;
|
||||
_set: boolean;
|
||||
begin
|
||||
AnInfo := default(TDbgInstInfo);
|
||||
pcode := AAddress;
|
||||
CodeIdx := 0;
|
||||
code := pcode[CodeIdx];
|
||||
@ -1156,6 +1183,18 @@ begin
|
||||
ACodeBytes := ACodeBytes + HexStr(pcode[k], 2);
|
||||
|
||||
Inc(AAddress, CodeIdx);
|
||||
|
||||
// Todo: Indicate whether currrent instruction has a destination code address
|
||||
// call jump branch(?)
|
||||
// Return information in AnInfo
|
||||
end;
|
||||
|
||||
procedure TAvrAsmDecoder.Disassemble(var AAddress: Pointer; out
|
||||
ACodeBytes: String; out ACode: String);
|
||||
var
|
||||
AnInfo: TDbgInstInfo;
|
||||
begin
|
||||
Disassemble(AAddress, ACodeBytes, ACode, AnInfo);
|
||||
end;
|
||||
|
||||
function TAvrAsmDecoder.GetInstructionInfo(AnAddress: TDBGPtr
|
||||
@ -1222,6 +1261,132 @@ begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderAVR }
|
||||
|
||||
constructor TDbgStackUnwinderAVR.Create(AProcess: TDbgProcess);
|
||||
begin
|
||||
FProcess := AProcess;
|
||||
FAddressSize := 2;
|
||||
FLastFrameBaseIncreased := True;
|
||||
FCodeReadErrCnt := 0;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderAVR.InitForThread(AThread: TDbgThread);
|
||||
begin
|
||||
FThread := AThread;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderAVR.InitForFrame(ACurrentFrame: TDbgCallstackEntry;
|
||||
out CodePointer, StackPointer, FrameBasePointer: TDBGPtr);
|
||||
var
|
||||
R: TDbgRegisterValue;
|
||||
begin
|
||||
CodePointer := ACurrentFrame.AnAddress;
|
||||
|
||||
// Frame pointer is r29:r28 (if used)
|
||||
FrameBasePointer := ACurrentFrame.FrameAdress;
|
||||
|
||||
//Could update using GetStackBasePointerRegisterValue
|
||||
|
||||
//R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumBP);
|
||||
//if R <> nil then
|
||||
// FrameBasePointer := R.NumValue;
|
||||
|
||||
StackPointer := 0;
|
||||
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(SPindex);
|
||||
if R = nil then exit;
|
||||
StackPointer := R.NumValue;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderAVR.GetTopFrame(out CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr; out ANewFrame: TDbgCallstackEntry);
|
||||
var
|
||||
i: Integer;
|
||||
R: TDbgRegisterValue;
|
||||
begin
|
||||
CodePointer := Thread.GetInstructionPointerRegisterValue;
|
||||
StackPointer := Thread.GetStackPointerRegisterValue;
|
||||
FrameBasePointer := Thread.GetStackBasePointerRegisterValue;
|
||||
ANewFrame := TDbgCallstackEntry.create(Thread, 0, FrameBasePointer, CodePointer);
|
||||
|
||||
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;
|
||||
end;
|
||||
|
||||
function TDbgStackUnwinderAVR.Unwind(AFrameIndex: integer; var CodePointer,
|
||||
StackPointer, FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry;
|
||||
out ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult;
|
||||
const
|
||||
MAX_FRAMES = 50000; // safety net
|
||||
// To read RAM, add data space offset to address
|
||||
DataOffset = $800000;
|
||||
Size = 2;
|
||||
var
|
||||
NextIdx: LongInt;
|
||||
LastFrameBase: TDBGPtr;
|
||||
OutSideFrame: Boolean;
|
||||
StackPtr: TDBGPtr;
|
||||
startPC, endPC: TDBGPtr;
|
||||
returnAddrStackOffset: word;
|
||||
b: byte;
|
||||
begin
|
||||
ANewFrame := nil;
|
||||
Result := suFailed;
|
||||
|
||||
if StackPointer = 0 then
|
||||
exit;
|
||||
if not FLastFrameBaseIncreased then
|
||||
exit;
|
||||
|
||||
OutSideFrame := False;
|
||||
LastFrameBase := FrameBasePointer;
|
||||
|
||||
// Get start/end PC of proc from debug info
|
||||
if not Self.Process.FindProcStartEndPC(CodePointer, startPC, endPC) then
|
||||
begin
|
||||
// Give up for now, it is complicated to locate prologue/epilogue in general without proc limits
|
||||
// ToDo: Perhaps interpret .debug_frame info if available,
|
||||
// or scan forward from address until an epilogue is found.
|
||||
exit;
|
||||
end;
|
||||
|
||||
if not TAvrAsmDecoder(Process.Disassembler).GetFunctionFrameReturnAddress(CodePointer, startPC, endPC, returnAddrStackOffset, OutSideFrame) then
|
||||
begin
|
||||
OutSideFrame := False;
|
||||
end;
|
||||
|
||||
if OutSideFrame then begin
|
||||
// Before adjustment of frame pointer, or after restoration of frame pointer,
|
||||
// return PC should be located by offset from SP
|
||||
if not Process.ReadData(DataOffset or (StackPtr + returnAddrStackOffset), Size, CodePointer) or (CodePointer = 0) then exit;
|
||||
end
|
||||
else begin
|
||||
// Inside stack frame, return PC should be located by offset from FP
|
||||
if not Process.ReadData(DataOffset or (FrameBasePointer + returnAddrStackOffset), Size, CodePointer) or (CodePointer = 0) then exit;
|
||||
end;
|
||||
// Convert return address from BE to LE, shl 1 to get byte address
|
||||
CodePointer := BEtoN(word(CodePointer)) shl 1;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
StackPtr := FrameBasePointer + returnAddrStackOffset + Size - 1; // After popping return-addr from "StackPtr"
|
||||
FrameBasePointer := StackPtr; // Estimate of previous FP
|
||||
{$POP}
|
||||
|
||||
FLastFrameBaseIncreased := (FrameBasePointer <> 0) and (FrameBasePointer > LastFrameBase);
|
||||
|
||||
ANewFrame:= TDbgCallstackEntry.create(Thread, NextIdx, FrameBasePointer, CodePointer);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(CodePointer, IntToStr(CodePointer),Size, PCindex);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SPindex);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate['r28'].SetValue(byte(FrameBasePointer), IntToStr(b),Size, 28);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate['r29'].SetValue((FrameBasePointer and $FF00) shr 8, IntToStr(b),Size, 29);
|
||||
|
||||
FCodeReadErrCnt := 0;
|
||||
Result := suSuccess;
|
||||
end;
|
||||
|
||||
initialization
|
||||
DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user