DBG: fixes for Disassembler

git-svn-id: trunk@30515 -
This commit is contained in:
martin 2011-05-01 14:34:36 +00:00
parent 66cdd0e590
commit 5c02251e3c
6 changed files with 720 additions and 153 deletions

1
.gitattributes vendored
View File

@ -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

View File

@ -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;

View File

@ -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 )

View File

@ -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

View File

@ -4,7 +4,8 @@ program TestGdbmi;
uses
Interfaces, Forms, GuiTestRunner, CompileHelpers,
TestGdbType, TestGDBMIControl,
TestGdbType, TestDisAss,
TestGDBMIControl,
TestBase, TestException, Testwatches, TestBreakPoint;
{$R *.res}

View File

@ -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.