mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-18 20:49:27 +02:00
DBG: fixes for Disassembler
git-svn-id: trunk@30515 -
This commit is contained in:
parent
66cdd0e590
commit
5c02251e3c
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -4,7 +4,8 @@ program TestGdbmi;
|
||||
|
||||
uses
|
||||
Interfaces, Forms, GuiTestRunner, CompileHelpers,
|
||||
TestGdbType, TestGDBMIControl,
|
||||
TestGdbType, TestDisAss,
|
||||
TestGDBMIControl,
|
||||
TestBase, TestException, Testwatches, TestBreakPoint;
|
||||
|
||||
{$R *.res}
|
||||
|
470
debugger/test/Gdbmi/testdisass.pas
Normal file
470
debugger/test/Gdbmi/testdisass.pas
Normal 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.
|
||||
|
Loading…
Reference in New Issue
Block a user