diff --git a/components/fpdebug/fpdbgavrclasses.pas b/components/fpdebug/fpdbgavrclasses.pas index a97777f34d..9e97ab7723 100644 --- a/components/fpdebug/fpdbgavrclasses.pas +++ b/components/fpdebug/fpdbgavrclasses.pas @@ -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; diff --git a/components/fpdebug/fpdbgdisasavr.pp b/components/fpdebug/fpdbgdisasavr.pp index 730ff544a3..17fc81d9c1 100644 --- a/components/fpdebug/fpdbgdisasavr.pp +++ b/components/fpdebug/fpdbgdisasavr.pp @@ -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} );