diff --git a/.gitattributes b/.gitattributes index c92dbdd9da..b67a697316 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2765,6 +2765,7 @@ debugger/test/Gdbmi/rungdbmiform.lfm svneol=native#text/plain debugger/test/Gdbmi/rungdbmiform.pas svneol=native#text/pascal debugger/test/Gdbmi/testbase.pas svneol=native#text/pascal debugger/test/Gdbmi/testbreakpoint.pas svneol=native#text/pascal +debugger/test/Gdbmi/testdisass.pas svneol=native#text/pascal debugger/test/Gdbmi/testexception.pas svneol=native#text/pascal debugger/test/Gdbmi/testgdbmicontrol.lfm svneol=native#text/plain debugger/test/Gdbmi/testgdbmicontrol.pas svneol=native#text/pascal diff --git a/debugger/cmdlinedebugger.pp b/debugger/cmdlinedebugger.pp index cfe70f1024..01b6d804f3 100644 --- a/debugger/cmdlinedebugger.pp +++ b/debugger/cmdlinedebugger.pp @@ -54,12 +54,12 @@ type FFlushAfterRead: Boolean;// Set if we should flush after finished reading FPeekOffset: Integer; // Count the number of lines we have peeked FReadLineTimedOut: Boolean; - function GetDebugProcessRunning: Boolean; function WaitForHandles(const AHandles: array of Integer; var ATimeOut: Integer): Integer; overload; function WaitForHandles(const AHandles: array of Integer): Integer; overload; protected + function GetDebugProcessRunning: Boolean; virtual; procedure ProcessWhileWaitForHandles; virtual; - function CreateDebugProcess(const AOptions: String): Boolean; + function CreateDebugProcess(const AOptions: String): Boolean; virtual; procedure Flush; // Flushes output buffer function GetWaiting: Boolean; override; function ReadLine(ATimeOut: Integer = -1): String; overload; diff --git a/debugger/debugger.pp b/debugger/debugger.pp index b75fbebdac..dab9006c5e 100644 --- a/debugger/debugger.pp +++ b/debugger/debugger.pp @@ -1328,6 +1328,7 @@ type 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; @@ -1339,7 +1340,7 @@ type 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; + 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; @@ -2056,7 +2057,7 @@ begin then fo := ADisassRange.EntriesPtr[0]^.Offset else fo := 0; with ADisassRange do - Result := Format('Range(%u)=[[ Cnt=%d, Capac=%d, First=%u, RFirst=%u, Last=%u, RLast=%u, REnd=%u, FirstOfs=%d ]]', + 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]); end; end; @@ -6512,6 +6513,9 @@ destructor TIDEDisassembler.Destroy; var n: Integer; begin + if FMaster <> nil + then FMaster.OnChange := nil; + FMaster := nil; for n := FNotificationList.Count - 1 downto 0 do TDebuggerNotification(FNotificationList[n]).ReleaseReference; @@ -6577,6 +6581,15 @@ begin 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); @@ -6602,7 +6615,8 @@ begin begin // merge before i := AnotherRange.Count - 1; - while (i >= 0) and (AnotherRange.EntriesPtr[i]^.Addr >= RangeStartAddr) + a := FirstAddr; + while (i >= 0) and (AnotherRange.EntriesPtr[i]^.Addr >= a) do dec(i); inc(i); {$IFDEF DBG_VERBOSE} @@ -6619,9 +6633,7 @@ begin end else begin // merge after - a:= RangeEndAddr; - if LastAddr > a - then a := LastAddr; + a:= LastAddr; i := 0; while (i < AnotherRange.Count) and (AnotherRange.EntriesPtr[i]^.Addr <= a) do inc(i); @@ -6689,7 +6701,9 @@ begin then break; dec(Result); end; - AOffs := AnAddr - FEntries[Result].Addr; + If Result < 0 + then AOffs := 0 + else AOffs := AnAddr - FEntries[Result].Addr; end; { TDBGDisassemblerEntryMapIterator } @@ -6765,6 +6779,8 @@ begin {$IFDEF DBG_VERBOSE} debugln(['INFO: TDBGDisassemblerEntryMap.AddRange ', dbgs(ARange), ' to map with count=', Count ]); {$ENDIF} + if ARange.Count = 0 then exit; + MergeRng := GetRangeForAddr(ARange.RangeStartAddr, True); if MergeRng <> nil then begin // merge to end ( ARange.RangeStartAddr >= MergeRng.RangeStartAddr ) diff --git a/debugger/gdbmidebugger.pp b/debugger/gdbmidebugger.pp index ee7f31da68..6fa1a76558 100644 --- a/debugger/gdbmidebugger.pp +++ b/debugger/gdbmidebugger.pp @@ -994,6 +994,8 @@ const // 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 { TGDBMIDisassembleResultList } @@ -1053,12 +1055,12 @@ type // Current SubList function IsFirstSubList: Boolean; - function CurrentFixedAddr: TDBGPtr; // Addr[0] - Offs[0] + function CurrentFixedAddr(AOffsLimit: Integer): TDBGPtr; // Addr[0] - Offs[0] // About the next SubList function NextStartAddr: TDBGPtr; function NextStartOffs: Integer; // Overall - function CountLinesAfterCounterAddr: Integer; // count up to Startof Current SubList + function CountLinesAfterCounterAddr: Integer; // count up to Start of Current SubList property CurrentIndex: Integer read FCurIdx; property NextIndex: Integer read FStartIdx; @@ -1986,8 +1988,9 @@ begin // Does the next address look good? // And is AStartAddrHit ok - Result := ((NextIdx > FMaxIdx) or (FList.Item[NextIdx]^.Offset = 0)) - and ( (not HasLocate) or ((FIndexOfLocateAddress < 0) or (FOffsetOfLocateAddress = 0)) ); + //Result := ((NextIdx > FMaxIdx) or (FList.Item[NextIdx]^.Offset = 0)) + // and + Result := ( (not HasLocate) or ((FIndexOfLocateAddress < 0) or (FOffsetOfLocateAddress = 0)) ); end; function TGDBMIDisassembleResultFunctionIterator.IsFirstSubList: Boolean; @@ -2002,9 +2005,9 @@ begin Result := CurrentIndex - IndexOfCounterAddress - 1; end; -function TGDBMIDisassembleResultFunctionIterator.CurrentFixedAddr: TDBGPtr; +function TGDBMIDisassembleResultFunctionIterator.CurrentFixedAddr(AOffsLimit: Integer): TDBGPtr; begin - Result := FList.Item[CurrentIndex]^.Addr - FList.Item[CurrentIndex]^.Offset; + Result := FList.Item[CurrentIndex]^.Addr - Min(FList.Item[CurrentIndex]^.Offset, AOffsLimit); end; function TGDBMIDisassembleResultFunctionIterator.NextStartAddr: TDBGPtr; @@ -2034,8 +2037,13 @@ begin end; function TGDBMIMemoryDumpResultList.GetItemTxt(Index: Integer): string; +var + itm: PGDBMINameValue; begin - Result := PCLenToString(FNameValueList.Items[Index]^.Name, True); + itm := FNameValueList.Items[Index]; + if itm <> nil + then Result := PCLenToString(itm^.Name, True) + else Result := ''; end; procedure TGDBMIMemoryDumpResultList.PreParse; @@ -2203,21 +2211,21 @@ begin end; function TGDBMIDebuggerCommandDisassembe.DoExecute: Boolean; -type - TAddressValidity = - (avFoundFunction, avFoundRange, avFoundStatemnet, // known address - avGuessed, // guessed - avExternRequest, // As requested by external caller - avPadded // Padded, because address was not known for sure - ); - TAddress = record - Value, GuessedValue: TDBGPtr; - Offset: Integer; - Validity: TAddressValidity; - end; + type + TAddressValidity = + (avFoundFunction, avFoundRange, avFoundStatement, // known address + avGuessed, // guessed + avExternRequest, // As requested by external caller + avPadded // Padded, because address was not known for sure + ); + TAddress = record + Value, GuessedValue: TDBGPtr; + Offset: Integer; + Validity: TAddressValidity; + end; -const - TrustedValidity = [avFoundFunction, avFoundRange, avFoundStatemnet]; + const + TrustedValidity = [avFoundFunction, avFoundRange, avFoundStatement]; function InitAddress(AValue: TDBGPtr; AValidity: TAddressValidity; AnOffset: Integer = -1): TAddress; @@ -2228,6 +2236,15 @@ const Result.Validity := AValidity; end; + procedure PadAddress(var AnAddr: TAddress; APad: Integer); + begin + {$PUSH}{$Q-}{$R-}// APad can be negative, but will be expanded to TDbgPtr (QWord) + AnAddr.Value := AnAddr.Value + APad; + {$POP} + AnAddr.Validity := avPadded; + AnAddr.Offset := -1; + end; + function DbgsAddr(const AnAddr: TAddress): string; const ValidityName: array [TAddressValidity] of string = @@ -2247,12 +2264,10 @@ const WS := 0; if WithSrc then WS := 1;; + Result := AResultList; ExecuteCommand('-data-disassemble -s %u -e %u -- %d', [AStartAddr, AnEndAddr, WS], R); - if AResultList <> nil - then begin - Result := AResultList; - Result.Init(R); - end + if Result <> nil + then Result.Init(R) else Result := TGDBMIDisassembleResultList.Create(R); if ACutBeforeEndAddr and Result.HasSourceInfo then Result.SortByAddress; @@ -2265,12 +2280,10 @@ const var R: TGDBMIExecResult; begin + Result := AResultList; ExecuteCommand('-data-read-memory %u x 1 1 %u', [AStartAddr, ACount], R); - if AResultList <> nil - then begin - Result := AResultList; - Result.Init(R); - end + if Result <> nil + then Result.Init(R) else Result := TGDBMIMemoryDumpResultList.Create(R); end; @@ -2344,13 +2357,71 @@ const end; end; + procedure AdjustLastEntryEndAddr(const ARange: TDBGDisassemblerEntryRange; + const ADisAssList: TGDBMIDisassembleResultList); + var + i: Integer; + TmpAddr: TDBGPtr; + begin + if ARange.Count = 0 then exit; + TmpAddr := ARange.LastAddr; + i := 0; + while (i < ADisAssList.Count) and (ADisAssList.Item[i]^.Addr <= TmpAddr) do inc(i); + if i < ADisAssList.Count + then ARange.LastEntryEndAddr := ADisAssList.Item[i]^.Addr + else if ARange.LastEntryEndAddr <= ARange.RangeEndAddr + then ARange.LastEntryEndAddr := ARange.RangeEndAddr + 1; + end; + procedure CopyToRange(const ADisAssList: TGDBMIDisassembleResultList; const ADestRange: TDBGDisassemblerEntryRange; AFromIndex, ACount: Integer; - const ASrcInfoDisAssList: TGDBMIDisassembleResultList = nil); + ASrcInfoDisAssList: TGDBMIDisassembleResultList = nil); var i, j, MinInSrc, MaxInSrc: Integer; ItmPtr, ItmPtr2, LastItem: PDisassemblerEntry; begin + if ASrcInfoDisAssList = ADisAssList + then ASrcInfoDisAssList := nil; + // Clean end of range + ItmPtr := ADisAssList.Item[AFromIndex]; + i := ADestRange.Count; + while (i > 0) and (ADestRange.EntriesPtr[i-1]^.Addr >= ItmPtr^.Addr) do dec(i); + {$IFDEF DBG_VERBOSE} + if ADestRange.Count <> i then + debugln(['NOTICE, CopyToRange: Removing ',i,' entries from the end of Range. AFromIndex=',AFromIndex, ' ACount=', ACount, ' Range=',dbgs(ADestRange)]); + {$ENDIF} + ADestRange.Count := i; + if i > 0 then begin + ItmPtr2 := ADestRange.EntriesPtr[i-1]; + if ItmPtr2^.Dump <> '' then begin + {$PUSH}{$IFnDEF DBGMI_WITH_DISASS_OVERFLOW}{$Q-}{$R-}{$ENDIF} // Overflow is allowed to occur + j := (ItmPtr^.Addr - ItmPtr2^.Addr) * 2; + {$POP} + {$IFDEF DBG_VERBOSE} + if length(ItmPtr2^.Dump) > j then + debugln(['NOTICE, CopyToRange: Shortening Dump at the end of Range. AFromIndex=',AFromIndex, ' ACount=', ACount, ' Range=',dbgs(ADestRange)]); + {$ENDIF} + if length(ItmPtr2^.Dump) > j then ItmPtr2^.Dump := copy(ItmPtr2^.Dump, 1, j); + end; + end; + + if ADestRange.Count = 0 + then ADestRange.RangeStartAddr := ADisAssList.Item[AFromIndex]^.Addr; + + if ADestRange.RangeEndAddr < ADisAssList.Item[AFromIndex+ACount-1]^.Addr + then ADestRange.RangeEndAddr := ADisAssList.Item[AFromIndex+ACount-1]^.Addr; + + if ADisAssList.Count > AFromIndex + ACount + then begin + if ADestRange.LastEntryEndAddr < ADisAssList.Item[AFromIndex+ACount]^.Addr + then ADestRange.LastEntryEndAddr := ADisAssList.Item[AFromIndex+ACount]^.Addr; + end + else + if ADestRange.LastEntryEndAddr <= ADestRange.RangeEndAddr + then ADestRange.LastEntryEndAddr := ADestRange.RangeEndAddr + 1; + + + // Append new items LastItem := nil; MinInSrc := 0; if ASrcInfoDisAssList <> nil @@ -2415,7 +2486,9 @@ const s: String; begin Cnt := ARange.Count; - FromIndex := ARange.IndexOfAddrWithOffs(AFirstAddr)-1; + if ARange.FirstAddr > AFirstAddr + then FromIndex := -1 + else FromIndex := ARange.IndexOfAddrWithOffs(AFirstAddr)-1; if FromIndex < -1 then exit; @@ -2441,7 +2514,7 @@ const if (Offs < 0) or (Offs >= AMemDump.Count) then Continue; - if NextItm <> nil + if (NextItm <> nil) //and (NextItm^.Addr > Addr) then Len := NextItm^.Addr - Addr else Len := AMemDump.Count - 1 - Offs; if Offs + Len >= AMemDump.Count @@ -2514,7 +2587,7 @@ const debugln(['WARNING: Sublist not at offset 0 (filling gap in/before Src-Info): FromIdx=', DisAssIterator.CurrentIndex, ' NextIdx=', DisAssIterator.NextIndex, ' SequenceNo=', DisAssIterator.SublistNumber, ' StartIdx=', DisAssIterator.IndexOfLocateAddress, ' StartOffs=', DisAssIterator.OffsetOfLocateAddress]); {$ENDIF} - DisAssListCurrentSub := ExecDisassmble(DisAssIterator.CurrentFixedAddr, + DisAssListCurrentSub := ExecDisassmble(DisAssIterator.CurrentFixedAddr(DAssMaxRangeSize), DisAssIterator.NextStartAddr, False, DisAssListCurrentSub, True); end; @@ -2534,8 +2607,7 @@ const NewRange: TDBGDisassemblerEntryRange; OrigLastAddress, OrigFirstAddress: TAddress; TmpAddr: TDBGPtr; - BlockOk, GotFullDisAss, SkipDisAssInFirstLoop: Boolean; - s: String; + BlockOk, SkipDisAssInFirstLoop, ContinueAfterSource: Boolean; Itm: TDisassemblerEntry; begin Result := False; @@ -2545,31 +2617,29 @@ const DisAssIterator := nil; OrigFirstAddress := AFirstAddr; OrigLastAddress := ALastAddr; + SkipDisAssInFirstLoop := False; NewRange := TDBGDisassemblerEntryRange.Create; + // set some values, wil be adjusted later (in CopyToRange + NewRange.RangeStartAddr := AFirstAddr.Value; + NewRange.RangeEndAddr := ALastAddr.Value; + NewRange.LastEntryEndAddr := ALastAddr.Value; // No nice startingpoint found, just start to disassemble aprox 5 instructions before it // and hope that when we started in the middle of an instruction it get sorted out. // If so, the 4st for lines from the result must be discarded if not (AFirstAddr.Validity in TrustedValidity) - then begin - AFirstAddr.Value := AFirstAddr.Value - 5 * DAssBytesPerCommandMax; - AFirstAddr.Validity := avPadded; - AFirstAddr.Offset := -1; - end; + then PadAddress(AFirstAddr, - 5 * DAssBytesPerCommandMax); // Adjust ALastAddr - if ALastAddr.Value < AFirstAddr.Value + if ALastAddr.Value <= AFirstAddr.Value then begin - ALastAddr.Value := AFirstAddr.Value + 2 * DAssBytesPerCommandMax; - ALastAddr.Validity := avPadded; + ALastAddr.Value := AFirstAddr.Value; + PadAddress(ALastAddr, 2 * DAssBytesPerCommandMax); end else if not (ALastAddr.Validity in TrustedValidity) - then begin - ALastAddr.Value := ALastAddr.Value + 2 * DAssBytesPerCommandMax; - ALastAddr.Validity := avPadded; - end; + then PadAddress(ALastAddr, 2 * DAssBytesPerCommandMax); {$IFDEF DBG_VERBOSE} DebugLnEnter(['INFO: DoDisassembleRange for AFirstAddr =', DbgsAddr(AFirstAddr), @@ -2580,14 +2650,13 @@ const // check if we have an overall source-info // we can only do that, if we know the offset of firstaddr (limit to DAssRangeOverFuncTreshold avg lines, should be enough) + // TODO: limit offset ONLY, if previous range known (already have disass) if (AFirstAddr.Offset >= 0) then DisAssListWithSrc := ExecDisassmble (AFirstAddr.Value - Min(AFirstAddr.Offset, DAssRangeOverFuncTreshold * DAssBytesPerCommandAvg), ALastAddr.Value, True); - GotFullDisAss := (DisAssListWithSrc <> nil) and (DisAssListWithSrc.Count > 0) and DisAssListWithSrc.HasSourceInfo; - SkipDisAssInFirstLoop := False; - if GotFullDisAss + if (DisAssListWithSrc <> nil) and (DisAssListWithSrc.Count > 0) and DisAssListWithSrc.HasSourceInfo then begin (* *** *** Add the full source info @@ -2602,10 +2671,12 @@ const end; // Find out what comes after the disassembled source (need at least one statemnet, to determine end-add of last src-stmnt) - TmpAddr := DisAssListWithSrc.LastItem^.Addr + 2 * DAssBytesPerCommandAlign; - if ALastAddr.Value > TmpAddr + TmpAddr := DisAssListWithSrc.LastItem^.Addr; + ContinueAfterSource := OrigLastAddress.Value > TmpAddr; + if ContinueAfterSource then TmpAddr := ALastAddr.Value; - DisAssList := ExecDisassmble(DisAssListWithSrc.LastItem^.Addr, TmpAddr, False); + DisAssList := ExecDisassmble(DisAssListWithSrc.LastItem^.Addr, + TmpAddr + 2 * DAssBytesPerCommandAlign, False); // Add the known source list if DisAssList.Count < 2 @@ -2617,6 +2688,7 @@ const NewRange.Capacity := Max(NewRange.Capacity, NewRange.Count + DisAssListWithSrc.Count); while not DisAssIterator.EOL do begin + if (dcsCanceled in SeenStates) then break; DisAssIterator.NextSubList(DisAssListCurrentSub); CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count); // Do not add the Sourcelist as last param, or it will get re-sorted @@ -2630,31 +2702,28 @@ const DoDisassembleSourceless(DisAssListCurrentSub.LastItem^.Addr, DisAssIterator.NextStartAddr, NewRange, True); end; end; + i := DisAssIterator.CountLinesAfterCounterAddr; FreeAndNil(DisAssIterator); FreeAndNil(DisAssListWithSrc); + FreeAndNil(DisAssListCurrentSub); // Source Completly Added - // DisAssList contains data after source (from index = 1) - if DisAssList.Count < 2 + if not ContinueAfterSource then begin - // nothing to continue - NewRange.RangeStartAddr := AFirstAddr.Value; - NewRange.RangeEndAddr := OrigLastAddress.Value; // we have/will have tried our best to get everything to this value. If we didn't then neither will we later - NewRange.LastEntryEndAddr := ALastAddr.Value; + AdjustLastEntryEndAddr(NewRange, DisAssList); FKnownRanges.AddRange(NewRange); // NewRange is now owned by FKnownRanges NewRange := nil; FreeAndNil(DisAssList); exit; end; - // continue with the DisAsslist for the remainder - GotFullDisAss := False; AFirstAddr.Validity := avFoundFunction; // if we got source, then start is ok (original start is kept) DisAssStartIdx := 1; SkipDisAssInFirstLoop := True; - StopAfterNumLines := StopAfterNumLines - NewRange.Count; + if i > 0 + then StopAfterNumLines := StopAfterNumLines - i; (* *** *** Finished adding the full source info *** @@ -2680,8 +2749,8 @@ const // create a dummy range, so we will not retry NewRange.Capacity := 1; NewRange.RangeStartAddr := AFirstAddr.Value; - NewRange.RangeEndAddr := OrigLastAddress.Value; - NewRange.LastEntryEndAddr := OrigLastAddress.Value; + NewRange.RangeEndAddr := Max(OrigLastAddress.Value, AFirstAddr.Value+1); + NewRange.LastEntryEndAddr := AFirstAddr.Value+1; Itm.Addr := AFirstAddr.Value; Itm.Dump := ' '; Itm.SrcFileLine := 0; @@ -2702,7 +2771,7 @@ const if (ALastAddr.Validity = avPadded) or (DisAssList.LastItem^.Addr >= ALastAddr.Value) then begin ALastAddr.Value := DisAssList.LastItem^.Addr; - ALastAddr.Validity := avFoundStatemnet; + ALastAddr.Validity := avFoundStatement; dec(Cnt); DisAssList.Count := Cnt; end; @@ -2714,28 +2783,28 @@ const while (DisAssStartIdx < 4) and (DisAssStartIdx + 1 < Cnt) and (DisAssList.Item[DisAssStartIdx+1]^.Addr <= OrigFirstAddress.Value) do inc(DisAssStartIdx); AFirstAddr.Value := DisAssList.Item[DisAssStartIdx]^.Addr; - AFirstAddr.Validity := avFoundStatemnet; + AFirstAddr.Validity := avFoundStatement; end; NewRange.Capacity := Max(NewRange.Capacity, NewRange.Count + Cnt); - NewRange.RangeStartAddr := AFirstAddr.Value; - NewRange.RangeEndAddr := OrigLastAddress.Value; // we have/will have tried our best to get everything to this value. If we didn't then neither will we later - NewRange.LastEntryEndAddr := ALastAddr.Value; DisAssIterator := TGDBMIDisassembleResultFunctionIterator.Create (DisAssList, DisAssStartIdx, ALastAddr.Value, FStartAddr, StopAfterAddress); while not DisAssIterator.EOL do begin + if (dcsCanceled in SeenStates) then break; BlockOk := DisAssIterator.NextSubList(DisAssListCurrentSub); - if (DisAssIterator.CountLinesAfterCounterAddr > StopAfterNumLines) // got enough lines - // only if the next block is good to go - and not ( (not BlockOk) or (DisAssListCurrentSub.Item[0]^.Offset <> 0) or (not GotFullDisAss) ) + // Do we have enough lines (without the current block)? + if (DisAssIterator.CountLinesAfterCounterAddr > StopAfterNumLines) then begin + {$IFDEF DBG_VERBOSE} + DebugLnExit(['INFO: Got enough line in Iteration: CurrentIndex=', DisAssIterator.CurrentIndex]); + {$ENDIF} NewRange.LastEntryEndAddr := DisAssIterator.NextStartAddr; - NewRange.RangeEndAddr := DisAssIterator.NextStartAddr; + //AdjustLastEntryEndAddr(NewRange, DisAssList); break; end; @@ -2748,61 +2817,32 @@ const {$ENDIF} // Current block starts with offset. Adjust and disassemble again // Try with source first, in case it returns dat without source - if GotFullDisAss + DisAssListWithSrc := ExecDisassmble(DisAssIterator.CurrentFixedAddr(DAssMaxRangeSize), + DisAssIterator.NextStartAddr, True, DisAssListWithSrc, True); + if (DisAssListWithSrc.Count > 0) then begin - //get the source-less code as reference - DisAssListCurrentSub := ExecDisassmble(DisAssIterator.CurrentFixedAddr, - DisAssIterator.NextStartAddr, False, DisAssListCurrentSub, True); - CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count, DisAssListWithSrc); - Result := Result or (DisAssListCurrentSub.Count > 0); - continue; - end - else begin - // Try source first - DisAssListWithSrc := ExecDisassmble(DisAssIterator.CurrentFixedAddr, - DisAssIterator.NextStartAddr, True, DisAssListWithSrc, True); - if (DisAssListWithSrc.Count > 0) + if DisAssListWithSrc.HasSourceInfo + then DisAssListWithSrc.SortByAddress; + if (not DisAssListWithSrc.HasSourceInfo) + or (DisAssListWithSrc.LastItem^.Addr > DisAssIterator.NextStartAddr - DAssBytesPerCommandAlign) then begin - if DisAssListWithSrc.HasSourceInfo - then DisAssListWithSrc.SortByAddress; - if (not DisAssListWithSrc.HasSourceInfo) - or (DisAssListWithSrc.LastItem^.Addr > DisAssIterator.NextStartAddr - DAssBytesPerCommandAlign) - then begin - // no source avail, but got data - // OR source and no gap - CopyToRange(DisAssListWithSrc, NewRange, 0, DisAssListWithSrc.Count); - Result := True; - continue; - end; + // no source avail, but got data + // OR source and no gap + CopyToRange(DisAssListWithSrc, NewRange, 0, DisAssListWithSrc.Count); + Result := True; + continue; end; - - //get the source-less code as reference - DisAssListCurrentSub := ExecDisassmble(DisAssIterator.CurrentFixedAddr, - DisAssIterator.NextStartAddr, False, DisAssListCurrentSub, True); - CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count, DisAssListWithSrc); - Result := Result or (DisAssListCurrentSub.Count > 0); - continue; end; + + //get the source-less code as reference + DisAssListCurrentSub := ExecDisassmble(DisAssIterator.CurrentFixedAddr(DAssMaxRangeSize), + DisAssIterator.NextStartAddr, False, DisAssListCurrentSub, True); + CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count, DisAssListWithSrc); + Result := Result or (DisAssListCurrentSub.Count > 0); + continue; end; // Todo: Check for wrong start stmnt offset - if not BlockOk - and (DisAssIterator.NextStartOffs <> 0) - then begin - // overlap into next proc - {$IFDEF DBG_VERBOSE} - debugln(['WARNING: FindProcEnd found an overlap (',DisAssIterator.NextStartOffs,') at block end: FromIdx=', DisAssIterator.CurrentIndex, ' NextIdx=', DisAssIterator.NextIndex, - ' SequenceNo=', DisAssIterator.SublistNumber, ' StartIdx=', DisAssIterator.IndexOfLocateAddress, ' StartOffs=', DisAssIterator.OffsetOfLocateAddress]); - {$ENDIF} - s := DisAssListCurrentSub.LastItem^.Dump; - s := copy(s, 1, Max(0, length(s) - DisAssIterator.NextStartOffs * 2)); - if s = '' - then s := ' '; - DisAssListCurrentSub.LastItem^.Dump := s; - DisAssListCurrentSub.LastItem^.Statement := ''; - BlockOk := True; - end; - if BlockOk then begin // Got a good block @@ -2811,17 +2851,17 @@ const // Try to get source-info (up to DisAssIterator.NextStartAddr) // Subtract offset from StartAddress, in case this is the first block // (we may continue existing data, but src info must be retrieved in full, or may be incomplete) - // If we are in IsFirstSubList, we already tried - if (not GotFullDisAss) - and not(DisAssIterator.IsFirstSubList and SkipDisAssInFirstLoop) - then DisAssListWithSrc := ExecDisassmble(DisAssIterator.CurrentFixedAddr, - DisAssIterator.NextStartAddr, True, DisAssListWithSrc, True); - // We may have less lines with source, as we stripped padding at the end - if (DisAssListWithSrc <> nil) and DisAssListWithSrc.HasSourceInfo + if not( DisAssIterator.IsFirstSubList and SkipDisAssInFirstLoop ) then begin - CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count, DisAssListWithSrc); - Result := Result or (DisAssListCurrentSub.Count > 0); - continue; + DisAssListWithSrc := ExecDisassmble(DisAssIterator.CurrentFixedAddr(DAssMaxRangeSize), + DisAssIterator.NextStartAddr, True, DisAssListWithSrc, True); + // We may have less lines with source, as we stripped padding at the end + if (DisAssListWithSrc <> nil) and DisAssListWithSrc.HasSourceInfo + then begin + CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count, DisAssListWithSrc); + Result := Result or (DisAssListCurrentSub.Count > 0); + continue; + end; end; end; CopyToRange(DisAssListCurrentSub, NewRange, 0, DisAssListCurrentSub.Count); @@ -2899,17 +2939,21 @@ const Rng: TDBGDisassemblerEntryRange; FirstAddr: TDBGPtr; begin - MemDump := nil;; + MemDump := nil; for i := 0 to length(FMemDumpsNeeded) - 1 do begin + if (dcsCanceled in SeenStates) then break; FirstAddr := FMemDumpsNeeded[i].FirstAddr; Rng := FRangeIterator.GetRangeForAddr(FirstAddr, True); if rng <> nil then MemDump := ExecMemDump(FirstAddr, FMemDumpsNeeded[i].LastAddr - FirstAddr, MemDump); - while (Rng <> nil) and (Rng.FirstAddr <= FMemDumpsNeeded[i].LastAddr) do - begin - AddMemDumpToRange(Rng, MemDump, FMemDumpsNeeded[i].FirstAddr, FMemDumpsNeeded[i].LastAddr); - Rng := FRangeIterator.NextRange; + if DebuggerState <> dsError + then begin + while (Rng <> nil) and (Rng.FirstAddr <= FMemDumpsNeeded[i].LastAddr) do + begin + AddMemDumpToRange(Rng, MemDump, FMemDumpsNeeded[i].FirstAddr, FMemDumpsNeeded[i].LastAddr); + Rng := FRangeIterator.NextRange; + end; end; end; FreeAndNil(MemDump); @@ -2942,10 +2986,29 @@ begin and (TryStartAt.Value - RngBefore.EntriesPtr[RngBefore.Count - 1]^.Addr > FLinesBefore * DAssBytesPerCommandAvg) then RngBefore := nil; {$POP} - TryStartAt.GuessedValue := FStartAddr - FLinesBefore * DAssBytesPerCommandAvg; + TmpAddr := FStartAddr - Min(FLinesBefore * DAssBytesPerCommandAvg, DAssMaxRangeSize); + TryStartAt.GuessedValue := TmpAddr; AdjustToRangeOrKnowFunctionStart(TryStartAt, RngBefore); + // check max size + if (TryStartAt.Value < FStartAddr - Min(FStartAddr, DAssMaxRangeSize)) + then begin + {$IFDEF DBG_VERBOSE} + DebugLn(['INFO: Limit Range for Disass: FStartAddr=', FStartAddr, ' TryStartAt.Value=', TryStartAt.Value ]); + {$ENDIF} + TryStartAt := InitAddress(TmpAddr, avGuessed); + end; // Guess Maximum, will adjust later + if TryStartAt.Value > FEndAddr then begin + if (RngBefore <> nil) then begin + GotCnt := RngBefore.IndexOfAddr(FEndAddr); + LastGotCnt := RngBefore.IndexOfAddr(TryStartAt.Value); + if (GotCnt >= 0) and (LastGotCnt >= 0) and (LastGotCnt > GotCnt) then + FLinesAfter := Max(FLinesAfter - (LastGotCnt - GotCnt), 1); + end; + FEndAddr := TryStartAt.Value; // WARNING: modifying FEndAddr + end; + TryEndAt := InitAddress(FEndAddr + FLinesAfter * DAssBytesPerCommandAvg, avGuessed); // Read as many unknown ranges, until LinesAfter is met @@ -2953,9 +3016,13 @@ begin while(True) do begin // check if we need any LinesAfter + if (dcsCanceled in SeenStates) then break; LastGotCnt:= GotCnt; GotCnt := 0; TmpAddr := FEndAddr; + if TryStartAt.Value > FEndAddr + then + TmpAddr := TryStartAt.Value; if RngBefore <> nil then begin TmpAddr := RngBefore.RangeEndAddr; @@ -2973,10 +3040,11 @@ begin end; if LastGotCnt >= GotCnt then begin - debugln(['Disassembler: *** Failure to get any mor lines while scanning forward LastGotCnt=',LastGotCnt, ' now GotCnt=',GotCnt, ' Requested=',FLinesAfter]); + debugln(['Disassembler: *** Failure to get any more lines while scanning forward LastGotCnt=',LastGotCnt, ' now GotCnt=',GotCnt, ' Requested=',FLinesAfter]); break; end; + if (dcsCanceled in SeenStates) then break; RngAfter := FRangeIterator.NextRange; // adjust TryEndAt if (RngAfter <> nil) and (TryEndAt.Value >= RngAfter.RangeStartAddr) @@ -2985,6 +3053,7 @@ begin TryEndAt.Validity := avFoundRange; end; + if (dcsCanceled in SeenStates) then break; // Try to disassemble the range if not DoDisassembleRange(TryStartAt, TryEndAt, TmpAddr, FLinesAfter-GotCnt) then begin @@ -3011,6 +3080,7 @@ begin GotCnt := -1; while(True) do begin + if (dcsCanceled in SeenStates) then break; LastGotCnt:= GotCnt; if (RngAfter = nil) then begin @@ -3018,18 +3088,19 @@ begin break; end; - GotCnt := RngAfter.IndexOfAddrWithOffs(FEndAddr); // already known before + GotCnt := RngAfter.IndexOfAddrWithOffs(FStartAddr); // already known before if GotCnt >= FLinesBefore then break; if LastGotCnt >= GotCnt then begin - debugln(['Disassembler: *** Failure to get any mor lines while scanning backward LastGotCnt=',LastGotCnt, ' now GotCnt=',GotCnt, ' Requested=',FLinesBefore]); + debugln(['Disassembler: *** Failure to get any more lines while scanning backward LastGotCnt=',LastGotCnt, ' now GotCnt=',GotCnt, ' Requested=',FLinesBefore]); break; end; TryEndAt := InitAddress(RngAfter.RangeStartAddr, avFoundRange); + TmpAddr := TryEndAt.Value - Min((FLinesBefore - GotCnt) * DAssBytesPerCommandAvg, DAssMaxRangeSize); TryStartAt := InitAddress(TryEndAt.Value - 1, avGuessed); - TryStartAt.GuessedValue := TryEndAt.Value - (FLinesBefore - GotCnt) * DAssBytesPerCommandAvg; + TryStartAt.GuessedValue := TmpAddr; // and adjust RngBefore := FRangeIterator.PreviousRange; {$PUSH}{$IFnDEF DBGMI_WITH_DISASS_OVERFLOW}{$Q-}{$R-}{$ENDIF} // Overflow is allowed to occur @@ -3039,7 +3110,15 @@ begin then RngBefore := nil; {$POP} AdjustToRangeOrKnowFunctionStart(TryStartAt, RngBefore); + if (TryStartAt.Value < TryEndAt.Value - Min(TryEndAt.Value, DAssMaxRangeSize)) + then begin + {$IFDEF DBG_VERBOSE} + DebugLn(['INFO: Limit Range for Disass: TryEndAt.Value=', TryEndAt.Value, ' TryStartAt.Value=', TryStartAt.Value ]); + {$ENDIF} + TryStartAt := InitAddress(TmpAddr, avGuessed); + end; + if (dcsCanceled in SeenStates) then break; // Try to disassemble the range if not DoDisassembleRange(TryStartAt, TryEndAt, 0, -1) then begin diff --git a/debugger/test/Gdbmi/TestGdbmi.lpr b/debugger/test/Gdbmi/TestGdbmi.lpr index 2ed7f4d08e..2e856dd0f4 100644 --- a/debugger/test/Gdbmi/TestGdbmi.lpr +++ b/debugger/test/Gdbmi/TestGdbmi.lpr @@ -4,7 +4,8 @@ program TestGdbmi; uses Interfaces, Forms, GuiTestRunner, CompileHelpers, - TestGdbType, TestGDBMIControl, + TestGdbType, TestDisAss, + TestGDBMIControl, TestBase, TestException, Testwatches, TestBreakPoint; {$R *.res} diff --git a/debugger/test/Gdbmi/testdisass.pas b/debugger/test/Gdbmi/testdisass.pas new file mode 100644 index 0000000000..c581885c3a --- /dev/null +++ b/debugger/test/Gdbmi/testdisass.pas @@ -0,0 +1,470 @@ +unit TestDisAss; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testutils, testregistry, LCLProc, + GDBMIDebugger, Debugger, DebugManager, maps; + +type + TTestDisAssRegion = record + FirstAddr, LastAddr: TDBGPtr; + FirstOutAddr, LastOutAddr: TDBGPtr; // continue output to last_out addr + FuncName: String; + FileName: String; + BaseLine, InstrPerLine: Integer; + InstrLen: Integer + end; + + { TTestBrkGDBMIDebugger } + + TTestBrkGDBMIDebugger = class(TGDBMIDebugger) + protected + FTestCmdLine: String; + procedure SendCmdLn(const ACommand: String); override; overload; + function ReadLine(const APeek: Boolean; ATimeOut: Integer = - 1): String; override; overload; + function CreateDebugProcess(const AOptions: String): Boolean; override; + function GetDebugProcessRunning: Boolean; override; + protected + function TestRespodDisass(AStartAddr, AEndAddr: TDBGPtr; AWithSrc: Boolean): String; + function TestMemDump(AStartAddr: TDBGPtr; AWSize, ARowCnt, AColCnt: Integer): String; + public + TestDisAssRegions: array of TTestDisAssRegion; + TestDefaultRegion: TTestDisAssRegion; + TestFailMemDump: Boolean; + TestIsFailed: Boolean; + procedure TestSetState(const AValue: TDBGState); + end; + + { TTestDisAss } + + TTestDisAss = class(TTestCase) + published + procedure RangeMap; + procedure Disassemble; + end; + +implementation + +{ TTestBrkGDBMIDebugger } + +procedure TTestBrkGDBMIDebugger.SendCmdLn(const ACommand: String); +begin + debugln(['############', ACommand]); + FTestCmdLine := ACommand; +end; + +function TTestBrkGDBMIDebugger.ReadLine(const APeek: Boolean; ATimeOut: Integer): String; + procedure SkipSpaces(var pos: Integer); + begin + while (pos <= length(FTestCmdLine)) and (FTestCmdLine[pos] = ' ') do inc(pos); + end; + function CheckString(var pos: Integer; const txt: String; ASkipSpaces: Boolean=True): Boolean; + begin + Result := copy(FTestCmdLine, pos, length(txt)) = txt; + if Result then inc(pos, length(txt)); + if Result and ASkipSpaces then SkipSpaces(pos); + end; + function GetNum(var pos: Integer; out num: Integer; ASkipSpaces: Boolean=True): Boolean; + var p1: Integer; + begin + p1 := pos; + if not (FTestCmdLine[pos] in ['0'..'9']) then exit(False); + while (pos <= length(FTestCmdLine)) and (FTestCmdLine[pos] in ['0'..'9']) do inc(pos); + if (pos <= length(FTestCmdLine)) and not (FTestCmdLine[pos] in [' ']) then exit(False); + num := StrToIntDef(copy(FTestCmdLine, p1, pos-p1), -1); + if ASkipSpaces then SkipSpaces(pos); + Result := True; + end; + + +var + i,j : Integer; + StartAddr, EndAddr, WSize, RowCnt, ColCnt: Integer; + WithSrc: Boolean; +begin + if FTestCmdLine = '' then exit('(gdb) '); + + i := 1; + if CheckString(i, '-data-disassemble ') + then if CheckString(i, '-s') + then if GetNum(i, StartAddr) + then if CheckString(i, '-e') + then if GetNum(i, EndAddr) + then begin + WithSrc := False; + if CheckString(i, '--') + then begin + WithSrc := GetNum(i, j); + WithSrc := j = 1; + end; +debugln([StartAddr]); +debugln([EndAddr]); + FTestCmdLine := ''; + Result := TestRespodDisass(StartAddr, EndAddr, WithSrc); + debugln(['###>>>',Result,'###<<<']); + exit; + end; + + // -data-read-memory 65608 x 1 1 389 + i:=1; + if CheckString(i, '-data-read-memory ') + then if GetNum(i, StartAddr) + then if CheckString(i, 'x') // only hex supported + then if GetNum(i, WSize) // word-size: 1=byte + then if GetNum(i, RowCnt) // rows + then if GetNum(i, ColCnt) // col + then begin + FTestCmdLine := ''; + if TestFailMemDump then exit('(gdb) '); + Result := TestMemDump(StartAddr, WSize, RowCnt, ColCnt); + debugln(['###>>>',Result,'###<<<']); + exit; + end; + + + TestIsFailed := True; // unknow command + FTestCmdLine := ''; + Result := ''; +end; + +function TTestBrkGDBMIDebugger.CreateDebugProcess(const AOptions: String): Boolean; +begin + Result := True; +end; + +function TTestBrkGDBMIDebugger.GetDebugProcessRunning: Boolean; +begin + Result := True; +end; + +function TTestBrkGDBMIDebugger.TestRespodDisass(AStartAddr, AEndAddr: TDBGPtr; + AWithSrc: Boolean): String; + + function GetDisAssRegion(AnAddr: TDBGPtr): TTestDisAssRegion; + var + i: Integer; + begin + Result := TestDefaultRegion; + for i := 0 to high(TestDisAssRegions) do + if (AnAddr >= TestDisAssRegions[i].FirstAddr) and + (AnAddr <= TestDisAssRegions[i].LastAddr) + then begin + Result := (TestDisAssRegions[i]); + break; + end; + if Result.InstrLen < 1 then Result.InstrLen := 4; + if Result.InstrPerLine < 1 then Result.InstrPerLine := 8; + end; + + function GetMIInstr(AnAddr: TDBGPtr; ARgn: TTestDisAssRegion): String; + begin + Result := Format('{address="0x%x",', [AnAddr]); + if ARgn.FuncName <> '' then + Result := Result + Format('func-name="%s",offset="%d",', [ARgn.FuncName, AnAddr -ARgn.FirstAddr]); + Result := Result + 'inst="nop"}' + end; + +var + CurAddr: TDBGPtr; + Rgn: TTestDisAssRegion; + CurLine, lc: Integer; +begin + Result := ''; + Rgn := GetDisAssRegion(AStartAddr); + if (Rgn.FileName = '') then AWithSrc := False; + if Rgn.FirstOutAddr <> 0 then AStartAddr := Rgn.FirstOutAddr; + if (Rgn.LastOutAddr <> 0) and (AEndAddr < Rgn.LastOutAddr) then AEndAddr := Rgn.LastOutAddr; + + CurAddr := AStartAddr; + CurLine := Rgn.BaseLine + (CurAddr - Rgn.FirstAddr) div Rgn.InstrPerLine; + Result := '^done,asm_insns=['; + lc := 0; + if AWithSrc then begin + Result := Result + Format('src_and_asm_line={line="%d",file="%s",line_asm_insn=[ ', + [CurLine, Rgn.FileName]); + end; + + while CurAddr <= AEndAddr do begin; + if AWithSrc and (lc >= Rgn.InstrPerLine) then begin + Result := Result + ']}'; + Result := Result + Format(',src_and_asm_line={line="%d",file="%s",line_asm_insn=[ ', + [CurLine, Rgn.FileName]); + lc := 0; + end; + inc(lc); + + if CurAddr > Rgn.LastOutAddr then begin + Rgn := GetDisAssRegion(CurAddr); + end; + if (Result <> '') and not (Result[length(Result)] in ['{', '[']) then Result := Result + ',';; + Result := Result + GetMIInstr(CurAddr, Rgn); + CurAddr := CurAddr + Rgn.InstrLen; + end; + + if AWithSrc then + Result := Result + ']}'; + Result := Result + ']';//+LineEnding; +end; + +function TTestBrkGDBMIDebugger.TestMemDump(AStartAddr: TDBGPtr; AWSize, ARowCnt, + AColCnt: Integer): String; +var + i: Integer; +begin + // only supports single row + Result := Format('^done,' + +'addr="0x%x",nr-bytes="%d",total-bytes="%d",' + +'next-row="0x%x",prev-row="0x%x",' + +'next-page="0x%x",prev-page="0x%x",' + +'memory=[{addr="0x%x",data=["0x00"', // first entry + [AStartAddr, AColCnt, AColCnt, + AStartAddr+AColCnt, AStartAddr-AColCnt, + AStartAddr+AColCnt, AStartAddr-AColCnt, + AStartAddr + ]); + for i := 2 to AColCnt do + Result := Result + ',"0x00"'; + Result := Result + ']}]';//+LineEnding; +end; + +procedure TTestBrkGDBMIDebugger.TestSetState(const AValue: TDBGState); +begin + SetState(AValue); +end; + +procedure TTestDisAss.RangeMap; +var Errors: String; + + function AddRangeItem(ARange: TDBGDisassemblerEntryRange; AnAddr: TDBGPtr): TDisassemblerEntry; + begin + Result.Addr := AnAddr; + ARange.Append(@Result); + end; + + function CreateRange(AFirst, ALast, AItemEnd: TDBGPtr; AList: Array of TDBGPtr): TDBGDisassemblerEntryRange; + var + i: Integer; + begin + Result := TDBGDisassemblerEntryRange.Create; + Result.RangeStartAddr := AFirst; + Result.RangeEndAddr := ALast; + Result.LastEntryEndAddr := AItemEnd; + for i := low(AList) to high(AList) do + AddRangeItem(Result, AList[i]); + end; + + procedure TestValue(AName: String; AExp, AGot: String); overload; + begin + if AExp <> AGot then Errors := Format('%s%s: ExP: %s Got %s%s', [Errors, AName, AExp, AGot, LineEnding]);; + end; + procedure TestValue(AName: String; AExp, AGot: TDBGPtr); overload; + begin + if AExp <> AGot then Errors := Format('%s%s: ExP: %d Got %d%s', [Errors, AName, AExp, AGot, LineEnding]);; + end; + procedure TestRange(AName: String; ARange: TDBGDisassemblerEntryRange; AFirst, ALast, AItemEnd: TDBGPtr; AList: Array of TDBGPtr); + var + i: Integer; + begin + try + if ARange = nil then begin + Errors := Format('%s%s: No Range found%s', [Errors, AName, LineEnding]); + end; + TestValue(AName + 'RangeStartAddr', ARange.RangeStartAddr, AFirst); + TestValue(AName + 'RangeEndAddr', ARange.RangeEndAddr, ALast); + TestValue(AName + 'LastEntryEndAddr', ARange.LastEntryEndAddr, AItemEnd); + TestValue(AName + 'Count', ARange.Count, high(AList)-low(AList)+1); + for i := 0 to ARange.Count-1 do debugln([i,': ',ARange.EntriesPtr[i]^.Addr]); + for i := low(AList) to high(AList) do + TestValue(AName + 'Item '+IntToStr(i), ARange.EntriesPtr[i]^.Addr, AList[i]); + except + on e: exception do Errors := Format('%s%s: Exception: %s %s%s', [Errors, AName, e.ClassName, e.Message, LineEnding]); + end; + end; + +var + EntryRanges: TDBGDisassemblerEntryMap; + Range: TDBGDisassemblerEntryRange; +begin + //TDBGDisassemblerEntryMapIterator + EntryRanges := TDBGDisassemblerEntryMap.Create(itu8, SizeOf(TDBGDisassemblerEntryRange)); + + EntryRanges.AddRange(CreateRange(100, 120, 120, [100, 108, 116])); + TestValue('Map Count', 1, EntryRanges.Count); + Range := EntryRanges.GetRangeForAddr(100, False); + TestRange('R1', Range, 100, 120, 120, [100, 108, 116]); + + EntryRanges.AddRange(CreateRange(120, 140, 140, [120, 128, 136])); + TestValue('Map Count (merged to end)', 1, EntryRanges.Count); + Range := EntryRanges.GetRangeForAddr(100, False); + TestRange('R1 (merged to end)', Range, 100, 140, 140, [100, 108, 116, 120, 128, 136]); + + EntryRanges.AddRange(CreateRange(80, 100, 100, [80, 90])); + TestValue('Map Count (merged to start)', 1, EntryRanges.Count); + Range := EntryRanges.GetRangeForAddr(100, False); + TestRange('R1 (merged to end)', Range, 80, 140, 140, [80, 90, 100, 108, 116, 120, 128, 136]); + + FreeAndNil(EntryRanges); + + AssertEquals('', Errors); +end; + +procedure TTestDisAss.Disassemble; +var + IdeDisAss: TIDEDisassembler; + Gdb: TTestBrkGDBMIDebugger; + + procedure Init; + begin + FreeAndNil(IdeDisAss); + FreeAndNil(Gdb); + Gdb := TTestBrkGDBMIDebugger.Create(''); + IdeDisAss := TIDEDisassembler.Create; + IdeDisAss.Master := Gdb.Disassembler; + Gdb.TestSetState(dsPause); + Gdb.TestIsFailed := False;; + Gdb.TestFailMemDump := False; + end; + + procedure Test(Name: String;Addr: TDBGPtr; MinBefore, MinAfter: Integer); + var + t: String; + a, b: TDBGPtr; + i: Integer; + begin + t := ''; + if Gdb.TestIsFailed then t := t + ' - Unknown Command'; + if IdeDisAss.BaseAddr <> Addr then t := t + Format(' - BaseAddr, Exp %x, got %x', [Addr, IdeDisAss.BaseAddr]); + if IdeDisAss.CountBefore < MinBefore then t := t + Format(' - CountBefore, Exp %d, got %d', [MinBefore, IdeDisAss.CountBefore]); + if IdeDisAss.CountAfter < MinAfter then t := t + Format(' - CountAfter, Exp %d, got %d', [MinAfter, IdeDisAss.CountAfter]); + + if IdeDisAss.Entries[0].Addr <> Addr then t := t + Format(' - Entries[0].Addr, Exp %x, got %x', [Addr, IdeDisAss.Entries[0].Addr]); + + a := IdeDisAss.Entries[-IdeDisAss.CountBefore].Addr; + for i := -IdeDisAss.CountBefore + 1 to IdeDisAss.CountAfter - 1 do begin + b := IdeDisAss.Entries[i].Addr; + if b <= a then t := t + Format(' - Entries[%d].Addr went back, Exp greater %x, got %x', [i, a, b]); + a := b; + end;; + + if t <> '' then t := Name+LineEnding+t; + AssertEquals('', t); + end; + + procedure TestSrc(Name: String; StartAddr, EndAddr: TDBGPtr); + var + t: String; + b: TDBGPtr; + i: Integer; + itm: TDisassemblerEntry; + begin + t := ''; + for i := -IdeDisAss.CountBefore to IdeDisAss.CountAfter - 1 do begin + itm := IdeDisAss.Entries[i]; + b := itm.Addr; + if (b >= StartAddr) and (b <= EndAddr) and + not(itm.SrcFileName <> '') + then t := t + Format(' - Entries[%d] Addr(%x) has no source', [i, b]); + end;; + + if t <> '' then t := Name+LineEnding+t; + AssertEquals('', t); + end; + +begin + Gdb := nil; IdeDisAss := nil; + + {%region NO SOURCE} + //{%region simple block} + //Init; + //IdeDisAss.PrepareRange($20100, 10, 20); + //Test('no src, no offset, 1 block', $20100, 10, 19); + //{%endregion} + + //{%region multi simple block} + //Init; + //SetLength(Gdb.TestDisAssRegions, 2); + //with Gdb.TestDisAssRegions[0] do begin + // FirstAddr := $30100-184; LastAddr := $30100-25; InstrLen := 8; + // FuncName := 'abc'; + //end; + //with Gdb.TestDisAssRegions[1] do begin + // FirstAddr := $30100-24; LastAddr := $30100+200; InstrLen := 8; + // FuncName := 'def'; + //end; + //IdeDisAss.PrepareRange($30100, 10, 20); + //Test('no src, multi block', $30100, 10, 19); + //{%endregion} + + {%region multi simple block, overlap} + // The first block disassembles 4 bytes into the 2nd + // The 2nd block must be re-done + Init; + SetLength(Gdb.TestDisAssRegions, 2); + with Gdb.TestDisAssRegions[0] do begin + FirstAddr := $30100-180; LastAddr := $30100-20; InstrLen := 8; + FuncName := 'abc'; + end; + with Gdb.TestDisAssRegions[1] do begin + FirstAddr := $30100-24; LastAddr := $30100+200; InstrLen := 8; + FuncName := 'def'; + end; + IdeDisAss.PrepareRange($30100, 10, 20); + Test('no src, multi block, overlap of 4', $30100, 10, 19); + {%endregion} + {%endregion NO SOURCE} + + + if false then begin + {%region simple block, src} + Init; + SetLength(Gdb.TestDisAssRegions, 1); + with Gdb.TestDisAssRegions[0] do begin + FirstAddr := $30100-400; LastAddr := $30100+400; InstrLen := 8; + FuncName := 'abc'; FileName := 'foo.pp'; InstrPerLine := 5; + end; + IdeDisAss.PrepareRange($30100, 10, 20); + Test('src, 1 block', $30100, 10, 19); + TestSrc('src, 1 block', $30100-400, $30100+400); + {%endregion} + + {%region 2 block, part src} + Init; + SetLength(Gdb.TestDisAssRegions, 2); + with Gdb.TestDisAssRegions[0] do begin + FirstAddr := $30100-400; LastAddr := $30100-9; InstrLen := 8; + FuncName := 'abc'; + end; + with Gdb.TestDisAssRegions[1] do begin + FirstAddr := $30100-8; LastAddr := $30100+400; InstrLen := 8; + FuncName := 'def'; FileName := 'foo.pp'; InstrPerLine := 5; + end; + IdeDisAss.PrepareRange($30100, 10, 20); + Test('part-src, 1 block', $30100, 10, 19); + TestSrc('part-src, 1 block', $30100-8, $30100+400); + {%endregion} + + + + + + {%region fail mem dump} + Init; + Gdb.TestFailMemDump := True; + IdeDisAss.PrepareRange($10100, 10, 20); + // just enough, if it din't crash => go error state. + {%endregion} +end;//xxxxxxxxxxxx + FreeAndNil(IdeDisAss); + FreeAndNil(Gdb); +end; + + + +initialization + + RegisterTest(TTestDisAss); +end. +