mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-11 04:42:38 +02:00
999 lines
35 KiB
ObjectPascal
999 lines
35 KiB
ObjectPascal
unit TestBreakPoint;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, math, TestDbgControl, TestDbgTestSuites,
|
|
TTestWatchUtilities, TestCommonSources, TestDbgConfig, TTestDebuggerClasses,
|
|
LazDebuggerIntf, LazDebuggerIntfBaseTypes, DbgIntfDebuggerBase,
|
|
DbgIntfBaseTypes, LazLoggerBase, Forms;
|
|
|
|
type
|
|
|
|
// Info used by tests based on TestBreakPointThreadPrg
|
|
TBreakThreadPrgInfoEntry = record
|
|
ID: Integer;
|
|
Address: TDBGPtr;
|
|
Line, PreviousLine: Integer;
|
|
Val, LastVal: int64; // AX register
|
|
Loop, PreviousLoop: int64; // BX register
|
|
IsCurrent: Boolean;
|
|
LastBrkLine, LastBrkLoop: Integer;
|
|
PreviousLastBrkLine, PreviousLastBrkLoop: Integer;
|
|
end;
|
|
TBreakThreadPrgInfo = record
|
|
ThrLoopFirst, ThrLoopLast, ThrLoopLine0, ThrLoopInc: Integer; // Linenumbers of the breakpoints
|
|
// -1 => Main thread
|
|
Threads: array[-1..10] of TBreakThreadPrgInfoEntry;
|
|
end;
|
|
|
|
{ TTestBreakPoint }
|
|
|
|
TTestBreakPoint = class(TDBGTestCase)
|
|
protected
|
|
// Info/Methods used by tests based on TestBreakPointThreadPrg
|
|
FThrPrgInfo: TBreakThreadPrgInfo;
|
|
procedure ThrPrgInitializeThreads(ATestName: String);
|
|
procedure ThrPrgUpdateThreads(ATestName: String);
|
|
|
|
(* ThrPrgCheckNoSkip
|
|
Check that AX is correct for the line.
|
|
Ensure the instruction restored from "int3" was executed
|
|
*)
|
|
procedure ThrPrgCheckNoSkip(ATestName: String='');
|
|
(* ThrPrgInfoHasGoneThroughLine
|
|
Has gone over the line WITHOUT stopping at the breakpoint
|
|
- If the thread was NOT on the line, it has gone over it at least once.
|
|
- if the thread was AT the line,
|
|
~ and had NOT reorted yet for the line => it as gone away from the line anyway
|
|
~ and had reported for the line => it has gone OVER the line in the NEXT loop already
|
|
*)
|
|
function ThrPrgInfoHasGoneThroughLine(AIndex, ALine: Integer): boolean;
|
|
protected
|
|
Src: TCommonSource;
|
|
Dbg: TDebuggerIntf;
|
|
procedure TestLocation(ATestName, ABrkName: String; ABreakHitCount: Integer = 1);
|
|
procedure TestHitCnt(ATestName, ABrkName: String; ABreakHitCount: Integer);
|
|
published
|
|
(* Ensure the debugger can correctly run/step after hidding a breakpoit
|
|
- the original instruction is executed
|
|
- the breakpoint can be hit again
|
|
- stepping continues correctly to next line
|
|
- breakpoints are "hit" (recognized) when stepping onto them (not actually triggering them)
|
|
*)
|
|
procedure TestBreakPoints;
|
|
|
|
(* Ensure, that while one thread steps over a breakpoint (int3 removed),
|
|
no other thread ignores the breakpoint
|
|
*)
|
|
procedure TestBreakThreadsNoSkip;
|
|
|
|
(* Remove Breakpoints, while other threads have a pending event for the Brk.
|
|
Make sure the other thread, still executeds the original instruction,
|
|
hidden by the int3
|
|
*** The test can actually NOT force multiple breakpoints to be hit.
|
|
It only creates a high likelihood for this to happen.
|
|
*)
|
|
procedure TestBreakThreadsMoveBreak1;
|
|
procedure TestBreakThreadsMoveBreak2;
|
|
|
|
// check that each brk point hit was reported (one per thread)
|
|
procedure TestBreakThreadsHitBreak;
|
|
|
|
(* Only ONE thread running the breakpoints. Plenty of events from other
|
|
threads (including thread-exit).
|
|
Hit each breakpoint in order, WITHOUT ever re-hitting the last brk.
|
|
*HOPE* is that some events will happen while the main thread single steps
|
|
over a temp-removed break, and the single step has NOT yet moved.
|
|
So the single step will still need the brk to be tmp-removed when it
|
|
continues.
|
|
*)
|
|
procedure TestBreakThreadsIgnoreOther;
|
|
end;
|
|
|
|
implementation
|
|
|
|
var
|
|
ControlTest, ControlTestBreak, ControlTestThreadNoSkip,
|
|
ControlTestThreadMove1, ControlTestThreadMove2, ControlTestThreadHit,
|
|
ControlTestThreadIgnoreOther: Pointer;
|
|
|
|
procedure TTestBreakPoint.TestLocation(ATestName, ABrkName: String;
|
|
ABreakHitCount: Integer);
|
|
var
|
|
lc: TDBGLocationRec;
|
|
begin
|
|
AssertDebuggerState(dsPause);
|
|
lc := Debugger.LazDebugger.GetLocation;
|
|
TestEquals(ATestName+' '+ABrkName+' Loc', Src.BreakPoints[ABrkName], lc.SrcLine);
|
|
if ABreakHitCount >= 0 then
|
|
TestEquals(ATestName+' '+ABrkName+' HitCnt', Debugger.BreakPointByName(ABrkName).HitCount, ABreakHitCount);
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestHitCnt(ATestName, ABrkName: String;
|
|
ABreakHitCount: Integer);
|
|
begin
|
|
TestEquals(ATestName+' '+ABrkName+' HitCnt', Debugger.BreakPointByName(ABrkName).HitCount, ABreakHitCount);
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestBreakPoints;
|
|
var
|
|
ExeName: String;
|
|
loc: TDBGLocationRec;
|
|
b1, b2: TDBGBreakPoint;
|
|
MainThreadId: Integer;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestBreak) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'BreakPointPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
AssertTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
Debugger.SetBreakPoint(Src, 'PrgStep1');
|
|
Debugger.SetBreakPoint(Src, 'PrgStep2');
|
|
Debugger.SetBreakPoint(Src, 'PrgRun1');
|
|
Debugger.SetBreakPoint(Src, 'PrgRun2');
|
|
|
|
Debugger.SetBreakPoint(Src, 'AsmStep1');
|
|
Debugger.SetBreakPoint(Src, 'AsmStep2');
|
|
Debugger.SetBreakPoint(Src, 'AsmRun1');
|
|
Debugger.SetBreakPoint(Src, 'AsmRun2');
|
|
|
|
Debugger.SetBreakPoint(Src, 'AsmBranch1');
|
|
Debugger.SetBreakPoint(Src, 'AsmNever1');
|
|
Debugger.SetBreakPoint(Src, 'AsmBranchEnd1');
|
|
Debugger.SetBreakPoint(Src, 'AsmBranch2');
|
|
Debugger.SetBreakPoint(Src, 'AsmNever2');
|
|
Debugger.SetBreakPoint(Src, 'AsmBranchEnd2');
|
|
|
|
Debugger.SetBreakPoint(Src, 'Foo1');
|
|
Debugger.SetBreakPoint(Src, 'Foo2');
|
|
|
|
Debugger.SetBreakPoint(Src, 'PrgAfterFoo2');
|
|
|
|
Debugger.SetBreakPoint(Src, 'Thread1');
|
|
AssertDebuggerNotInErrorState;
|
|
|
|
// Step to break next line
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Init', 'PrgStep1');
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
TestLocation('Stepped to break at next line', 'PrgStep2');
|
|
TestHitCnt('Stepped to break at next line', 'PrgStep1', 1);
|
|
|
|
// Run to break next line
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to break', 'PrgRun1');
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to break at next line', 'PrgRun2');
|
|
TestHitCnt('Run to break at next line', 'PrgRun1', 1);
|
|
|
|
|
|
// Run/Step with one byte asm steps
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Init NOP', 'AsmStep1');
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
TestLocation('Stepped to NOP at next line', 'AsmStep2');
|
|
TestHitCnt('Stepped to NOP at next line', 'AsmStep1', 1);
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to NOP', 'AsmRun1');
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to NOP at next line', 'AsmRun2');
|
|
TestHitCnt('Run to NOP at next line', 'AsmRun1', 1);
|
|
|
|
// check that the command hidden by the int3 breakpoint is executed
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to NOP at next line', 'AsmBranch1');
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
TestLocation('Run to NOP at next line', 'AsmBranchEnd1');
|
|
TestHitCnt('Run to NOP at next line', 'AsmBranch1', 1);
|
|
TestHitCnt('Run to NOP at next line', 'AsmNever1', 0);
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to NOP at next line', 'AsmBranch2');
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run to NOP at next line', 'AsmBranchEnd2');
|
|
TestHitCnt('Run to NOP at next line', 'AsmBranch2', 1);
|
|
TestHitCnt('Run to NOP at next line', 'AsmNever2', 0);
|
|
|
|
|
|
// Run to same line (repeat proc call)
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Init Foo1', 'Foo1');
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run Foo1', 'Foo1', 2);
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Step/Run Foo1', 'Foo1', 3);
|
|
|
|
// Run to same line (loop)
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run Foo2', 'Foo2');
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run Foo2', 'Foo2', 2);
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestLocation('Run Foo2 ended in prg', 'PrgAfterFoo2');
|
|
TestHitCnt('Run Foo2', 'Foo2', 2);
|
|
|
|
// new breakpoint
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
TestLocation('Before insernt', 'New1', -1);
|
|
|
|
Debugger.SetBreakPoint(Src, 'New1');
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
TestLocation('After insernt', 'New2', -1);
|
|
TestHitCnt('After insernt', 'New1', 0);
|
|
|
|
Debugger.SetBreakPoint(Src, 'New2');
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestHitCnt('After insernt', 'New1', 0);
|
|
TestHitCnt('After insernt', 'New2', 0);
|
|
|
|
// in threads
|
|
(* In each thread set a breakpoint at the next instruction.
|
|
So when all threads are started, both should run/step OVER the breakpoint
|
|
without hitting it
|
|
*)
|
|
TestLocation('After insernt', 'Thread1');
|
|
|
|
|
|
MainThreadId := dbg.Threads.CurrentThreads.CurrentThreadId;
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
TestLocation('After insernt', 'Thread2', -1); // not set
|
|
|
|
loc := dbg.GetLocation;
|
|
TestEquals('loc in thread', loc.SrcLine, Src.BreakPoints['Thread2']);
|
|
b1 := dbg.BreakPoints.Add(loc.Address);
|
|
b1.InitialEnabled := True;
|
|
b1.Enabled := True;
|
|
|
|
dbg.Threads.ChangeCurrentThread(MainThreadId);
|
|
Debugger.RunToNextPause(dcStepOver); // continue stepping in main thread
|
|
//TODO: not yet implemented
|
|
|
|
loc := dbg.GetLocation;
|
|
// TestTrue('loc thread main', loc.SrcLine > Src.BreakPoints['New2']);
|
|
b2 := dbg.BreakPoints.Add(loc.Address);
|
|
b2.InitialEnabled := True;
|
|
b2.Enabled := True;
|
|
|
|
|
|
If loc.SrcLine >= Src.BreakPoints['Main2']-2 then
|
|
Debugger.SetBreakPoint(Src, 'Main1')
|
|
else
|
|
Debugger.SetBreakPoint(Src, 'Main2');
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
|
|
(* // TODO: breakpoints in diff threads may need to be hit, after the other break continued
|
|
If loc.SrcLine >= Src.BreakPoints['Main2']-2 then begin
|
|
TestLocation('main1', 'Main1');
|
|
Debugger.BreakPointByName('Main1').Enabled := False;
|
|
end else begin
|
|
TestLocation('main2', 'Main2');
|
|
Debugger.BreakPointByName('Main2').Enabled := False;
|
|
end;
|
|
|
|
TestEquals('b1 hits', 0, b1.HitCount);
|
|
TestEquals('b2 hits', 0, b2.HitCount);
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
TestEquals('b1 hits after', 0, b1.HitCount);
|
|
TestEquals('b2 hits after', 0, b2.HitCount);
|
|
*)
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.ThrPrgInitializeThreads(ATestName: String);
|
|
var
|
|
i, j: Integer;
|
|
t: TThreadEntry;
|
|
begin
|
|
FThrPrgInfo.ThrLoopFirst := Src.BreakPoints['BrkThreadBegin'];
|
|
FThrPrgInfo.ThrLoopLast := Src.BreakPoints['BrkThreadEnd'];
|
|
FThrPrgInfo.ThrLoopLine0 := Src.BreakPoints['BrkThread1'];
|
|
FThrPrgInfo.ThrLoopInc := Src.BreakPoints['BrkThreadIncLoop'];
|
|
FThrPrgInfo.Threads[-1].ID := 0;
|
|
(* Initialize
|
|
Find all threads
|
|
*)
|
|
dbg.Threads.CurrentThreads.Count;
|
|
j := 0;
|
|
while TTestThreads(dbg.Threads.CurrentThreads).DataValidity <> ddsValid do begin
|
|
sleep(50);
|
|
Application.ProcessMessages;
|
|
inc(j);
|
|
if j > 100 then break;
|
|
end;
|
|
|
|
j := 0;
|
|
for i := 0 to dbg.Threads.CurrentThreads.Count-1 do begin
|
|
t := dbg.Threads.CurrentThreads.Entries[i];
|
|
if t.TopFrame.Line < FThrPrgInfo.ThrLoopFirst then begin
|
|
debugln(['Ignoring Thread ', t.ThreadId, ' at ',t.TopFrame.Address]);
|
|
Continue;
|
|
end;
|
|
if t.TopFrame.Line > FThrPrgInfo.ThrLoopLast then begin
|
|
debugln(['MAIN Thread ', t.ThreadId, ' at ',t.TopFrame.Address, ' line ', t.TopFrame.Line]);
|
|
TestTrue('Only one main thread', FThrPrgInfo.Threads[-1].ID = 0);
|
|
if FThrPrgInfo.Threads[-1].ID = 0 then
|
|
FThrPrgInfo.Threads[-1].ID := t.ThreadId;
|
|
Continue;
|
|
end;
|
|
|
|
FThrPrgInfo.Threads[j].ID := t.ThreadId;
|
|
FThrPrgInfo.Threads[j].Line := -1;
|
|
FThrPrgInfo.Threads[j].Loop := -5;
|
|
FThrPrgInfo.Threads[j].LastBrkLine := -1;
|
|
FThrPrgInfo.Threads[j].PreviousLastBrkLine := -1;
|
|
debugln(['++ ADDED tid ',t.ThreadId]);
|
|
inc(j);
|
|
if j >= 11 then
|
|
break;
|
|
end;
|
|
AssertEquals('Found 10 threads', 10, j);
|
|
TestTrue('Found main thread', FThrPrgInfo.Threads[-1].ID <> 0);
|
|
end;
|
|
|
|
procedure TTestBreakPoint.ThrPrgUpdateThreads(ATestName: String);
|
|
var
|
|
i, j: Integer;
|
|
t: TThreadEntry;
|
|
r: TRegisters;
|
|
ax, bx: TRegisterValue;
|
|
begin
|
|
dbg.Threads.CurrentThreads.Count;
|
|
j := 0;
|
|
while TTestThreads(dbg.Threads.CurrentThreads).DataValidity <> ddsValid do begin
|
|
sleep(50);
|
|
Application.ProcessMessages;
|
|
inc(j);
|
|
if j > 100 then break;
|
|
end;
|
|
|
|
for i := -1 to Min(9, dbg.Threads.CurrentThreads.Count-1) do begin
|
|
t := dbg.Threads.CurrentThreads.EntryById[FThrPrgInfo.Threads[i].ID];
|
|
TestTrue(ATestName+' thread for '+inttostr(FThrPrgInfo.Threads[i].ID), t<> nil);
|
|
if t=nil then
|
|
continue;
|
|
r := dbg.Registers.CurrentRegistersList.Entries[FThrPrgInfo.Threads[i].ID, 0];
|
|
ax := nil;
|
|
for j := 0 to r.Count-1 do
|
|
if (lowercase(r.Entries[j].Name) = 'eax') or (lowercase(r.Entries[j].Name) = 'rax')
|
|
then
|
|
ax := r.Entries[j];
|
|
bx := nil;
|
|
for j := 0 to r.Count-1 do
|
|
if (lowercase(r.Entries[j].Name) = 'ebx') or (lowercase(r.Entries[j].Name) = 'rbx')
|
|
then
|
|
bx := r.Entries[j];
|
|
|
|
FThrPrgInfo.Threads[i].IsCurrent := FThrPrgInfo.Threads[i].ID = dbg.Threads.CurrentThreads.CurrentThreadId;
|
|
|
|
FThrPrgInfo.Threads[i].PreviousLine := FThrPrgInfo.Threads[i].Line;
|
|
FThrPrgInfo.Threads[i].LastVal := FThrPrgInfo.Threads[i].Val;
|
|
FThrPrgInfo.Threads[i].PreviousLoop := FThrPrgInfo.Threads[i].Loop;
|
|
FThrPrgInfo.Threads[i].Address := t.TopFrame.Address;
|
|
FThrPrgInfo.Threads[i].Line := t.TopFrame.Line;
|
|
if ax <> nil then
|
|
FThrPrgInfo.Threads[i].Val := StrToInt64Def(ax.Value,-1) and $7FFFFFFF;
|
|
if bx <> nil then
|
|
FThrPrgInfo.Threads[i].Loop := StrToInt64Def(bx.Value,-1) and $7FFFFFFF;
|
|
|
|
if FThrPrgInfo.Threads[i].ID = dbg.Threads.CurrentThreads.CurrentThreadId then begin
|
|
FThrPrgInfo.Threads[i].PreviousLastBrkLine := FThrPrgInfo.Threads[i].LastBrkLine;
|
|
FThrPrgInfo.Threads[i].PreviousLastBrkLoop := FThrPrgInfo.Threads[i].LastBrkLoop;
|
|
|
|
FThrPrgInfo.Threads[i].LastBrkLine := FThrPrgInfo.Threads[i].Line;
|
|
FThrPrgInfo.Threads[i].LastBrkLoop := FThrPrgInfo.Threads[i].Loop;
|
|
end;
|
|
|
|
debugln('Thread %d: ID=%d Cur=%s (%x): Line: %d (%d) (was %d) Val: %d (was %d) LOOP: %d (was %d) Brk: %d %d (%d %d)', [
|
|
i,
|
|
FThrPrgInfo.Threads[i].ID, dbgs(FThrPrgInfo.Threads[i].IsCurrent), FThrPrgInfo.Threads[i].Address,
|
|
FThrPrgInfo.Threads[i].Line-FThrPrgInfo.ThrLoopLine0, FThrPrgInfo.Threads[i].Line,
|
|
FThrPrgInfo.Threads[i].PreviousLine-FThrPrgInfo.ThrLoopLine0,
|
|
FThrPrgInfo.Threads[i].Val, FThrPrgInfo.Threads[i].LastVal,
|
|
FThrPrgInfo.Threads[i].Loop, FThrPrgInfo.Threads[i].PreviousLoop,
|
|
FThrPrgInfo.Threads[i].LastBrkLine, FThrPrgInfo.Threads[i].LastBrkLoop,
|
|
FThrPrgInfo.Threads[i].PreviousLastBrkLine, FThrPrgInfo.Threads[i].PreviousLastBrkLoop
|
|
]);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.ThrPrgCheckNoSkip(ATestName: String);
|
|
// Make sure no thread skipped any add. All EAX values must be correct
|
|
var
|
|
i, l: Integer;
|
|
const
|
|
ExpVal: array[0..10] of integer = (
|
|
0, 1, 3, 7, 15,
|
|
31, 63, 127, 255, 511,
|
|
1023
|
|
);
|
|
begin
|
|
for i := 0 to 9 do begin
|
|
if FThrPrgInfo.Threads[i].Line = -1 then
|
|
continue;
|
|
l := FThrPrgInfo.Threads[i].Line - FThrPrgInfo.ThrLoopLine0;
|
|
TestTrue(ATestName+' line in range tid: '+inttostr(FThrPrgInfo.Threads[i].ID), (l>=-1) and (l<FThrPrgInfo.ThrLoopLast-FThrPrgInfo.ThrLoopLine0));
|
|
if l > 9 then l := 10;
|
|
if l < 0 then l := 10;
|
|
|
|
TestEquals(ATestName+' Reg val for '+inttostr(FThrPrgInfo.Threads[i].ID)+ ' / '+inttostr(FThrPrgInfo.Threads[i].Line - FThrPrgInfo.ThrLoopLine0),
|
|
ExpVal[l],
|
|
FThrPrgInfo.Threads[i].Val
|
|
);
|
|
end;
|
|
end;
|
|
|
|
function TTestBreakPoint.ThrPrgInfoHasGoneThroughLine(AIndex, ALine: Integer): boolean;
|
|
var
|
|
LoopAdjust, LastLoopAdjust: Integer;
|
|
LoopDiff: Integer;
|
|
Entry: TBreakThreadPrgInfoEntry;
|
|
begin
|
|
Result := True;
|
|
LoopAdjust := 0;
|
|
Entry := FThrPrgInfo.Threads[AIndex];
|
|
|
|
if Entry.Line > FThrPrgInfo.ThrLoopInc then LoopAdjust := 1;
|
|
LastLoopAdjust := 0;
|
|
if Entry.PreviousLine > FThrPrgInfo.ThrLoopInc then LastLoopAdjust := 1;
|
|
LoopDiff :=
|
|
(Entry.Loop-LoopAdjust)
|
|
- (Entry.PreviousLoop-LastLoopAdjust);
|
|
|
|
// Was in front of line, and now after (or even in next loop)?
|
|
if (Entry.PreviousLine < ALine) and
|
|
( (Entry.Line > ALine) or
|
|
(LoopDiff > 0)
|
|
)
|
|
then
|
|
exit;
|
|
// Was after line, and now after AND in next loop-LoopAdjust?
|
|
if (Entry.PreviousLine > ALine) and
|
|
(Entry.Line > ALine) and
|
|
(LoopDiff > 0)
|
|
then
|
|
exit;
|
|
|
|
// Was exactly at line, and now after AND in next loop-LoopAdjust?
|
|
if (Entry.PreviousLine = ALine) and
|
|
(Entry.Line > ALine) and
|
|
(LoopDiff > 1)
|
|
then
|
|
exit;
|
|
|
|
// More than one loop-LoopAdjust ...
|
|
if (LoopDiff > 1)
|
|
then
|
|
exit;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestBreakThreadsNoSkip;
|
|
procedure HasManyAtLine(ALine: Integer; var AManyAt, AManyAfter: Integer);
|
|
var
|
|
AtLine, AfterLine, i: Integer;
|
|
begin
|
|
AtLine := 0;
|
|
AfterLine := 0;
|
|
for i := 0 to 9 do
|
|
if FThrPrgInfo.Threads[i].Line = ALine then
|
|
inc(AtLine)
|
|
else
|
|
if FThrPrgInfo.Threads[i].PreviousLine = ALine then // Current line moved on, stepped over break
|
|
inc(AfterLine);
|
|
if AtLine > 3 then Inc(AManyAt);
|
|
if AfterLine > 2 then Inc(AManyAfter);
|
|
end;
|
|
var
|
|
ExeName: String;
|
|
i, j: Integer;
|
|
MainBrk, Brk1, Brk2, Brk3, Brk4, Brk5: TDBGBreakPoint;
|
|
ManyAtBrk1, ManyAfterBrk1: Integer;
|
|
Entry: TBreakThreadPrgInfoEntry;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestThreadNoSkip) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'BreakPointThreadPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
MainBrk := Debugger.SetBreakPoint(Src, 'BrkMain1');
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgInitializeThreads('');
|
|
ThrPrgUpdateThreads('Init');
|
|
ThrPrgCheckNoSkip('Init');
|
|
|
|
(* Stopped in the main thread.
|
|
Set fixed breakpoints in the thread loop.
|
|
On each run, no thread must step over any of the breakpoints.
|
|
|
|
Since threads do reach the breakpoints, they will have to temp remove them
|
|
*)
|
|
MainBrk.Enabled := False;
|
|
|
|
Brk1 := Debugger.SetBreakPoint(Src, 'BrkThread1');
|
|
|
|
(* ManyAtBrk1 / ManyAfterBrk1
|
|
Cumulative count accross all "j" loop iterations.
|
|
ManyAtBrk1: Count "j"-iterations with at least 3 threads have been at "Brk1"
|
|
ManyAfterBrk1: Count "j"-iterations with at least 3 threads just stepped/run away from "Brk1"
|
|
*)
|
|
ManyAtBrk1 := 0;
|
|
ManyAfterBrk1 := 0;
|
|
|
|
(* Each iteration the test checks that all "add eax, n" have been executed.
|
|
*)
|
|
for j := 0 to 300 do begin
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgUpdateThreads('loop fixed brk '+IntToStr(j));
|
|
ThrPrgCheckNoSkip('loop, fixed brk '+IntToStr(j)); // Compare AX with line
|
|
|
|
for i := 0 to 9 do begin
|
|
Entry := FThrPrgInfo.Threads[i];
|
|
if Entry.PreviousLine < 0 then
|
|
continue;
|
|
|
|
TestTrue('THread not gone over break 1 at line '+IntToStr(Brk1.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk1.Line)
|
|
);
|
|
end;
|
|
|
|
HasManyAtLine(Brk1.Line, ManyAtBrk1, ManyAfterBrk1);
|
|
if (j > 50) and (ManyAtBrk1 > 20) and (ManyAfterBrk1 > 15) then begin
|
|
DebugLn('~~~~~~~~~~~~~ End loop early j=%d at=%d after=%d', [j, ManyAtBrk1, ManyAfterBrk1]);
|
|
break;
|
|
end;
|
|
end;
|
|
TestTrue('Had Many at brk1 (loop1)', ManyAtBrk1 > 0, 0, 'Ignore / not enforcable');
|
|
TestTrue('Had Many after brk1 (loop1)', ManyAfterBrk1 > 0, 0, 'Ignore / not enforcable');
|
|
|
|
// Add more breaks
|
|
Brk3 := Debugger.SetBreakPoint(Src, 'BrkThread5');
|
|
Brk5 := Debugger.SetBreakPoint(Src, 'BrkThread9');
|
|
// clear values from last loop
|
|
for i := 0 to 9 do begin
|
|
FThrPrgInfo.Threads[i].LastBrkLine := -1;
|
|
FThrPrgInfo.Threads[i].PreviousLastBrkLine := -1;
|
|
end;
|
|
|
|
for j := 0 to 100 do begin
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgUpdateThreads('loop fixed brk '+IntToStr(j));
|
|
ThrPrgCheckNoSkip('loop, fixed brk '+IntToStr(j));
|
|
|
|
for i := 0 to 9 do begin
|
|
Entry := FThrPrgInfo.Threads[i];
|
|
if Entry.PreviousLine < 0 then
|
|
continue;
|
|
|
|
TestTrue('THread not gone over break(n/3) 1 at line '+IntToStr(Brk1.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk1.Line)
|
|
);
|
|
TestTrue('THread not gone over break(n/3) 3 at line '+IntToStr(Brk3.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk3.Line)
|
|
);
|
|
TestTrue('THread not gone over break(n/3) 5 at line '+IntToStr(Brk5.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk5.Line)
|
|
);
|
|
|
|
if Entry.PreviousLastBrkLine > 0 then begin
|
|
if Entry.LastBrkLine = Brk1.Line then begin
|
|
TestEquals('Previous break(n/3) before Brk1', Brk5.Line, Entry.PreviousLastBrkLine);
|
|
TestTrue ('Previous break-loop(n/3) before Brk1', Entry.LastBrkLoop = Entry.PreviousLastBrkLoop + 1);
|
|
end
|
|
else
|
|
if Entry.LastBrkLine = Brk3.Line then begin
|
|
TestEquals('Previous break(n/3) before Brk3', Brk1.Line, Entry.PreviousLastBrkLine);
|
|
TestTrue ('Previous break-loop(n/3) before Brk3', Entry.LastBrkLoop = Entry.PreviousLastBrkLoop);
|
|
end
|
|
else
|
|
if Entry.LastBrkLine = Brk5.Line then begin
|
|
TestEquals('Previous break(n/3) before Brk5', Brk3.Line, Entry.PreviousLastBrkLine);
|
|
TestTrue ('Previous break-loop(n/3) before Brk5', Entry.LastBrkLoop = Entry.PreviousLastBrkLoop);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Add more breaks
|
|
Brk2 := Debugger.SetBreakPoint(Src, 'BrkThread3');
|
|
Brk4 := Debugger.SetBreakPoint(Src, 'BrkThread7');
|
|
// clear values from last loop
|
|
for i := 0 to 9 do begin
|
|
FThrPrgInfo.Threads[i].LastBrkLine := -1;
|
|
FThrPrgInfo.Threads[i].PreviousLastBrkLine := -1;
|
|
end;
|
|
|
|
for j := 0 to 100 do begin
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgUpdateThreads('loop fixed brk '+IntToStr(j));
|
|
ThrPrgCheckNoSkip('loop, fixed brk '+IntToStr(j));
|
|
|
|
for i := 0 to 9 do begin
|
|
Entry := FThrPrgInfo.Threads[i];
|
|
if Entry.PreviousLine < 0 then
|
|
continue;
|
|
|
|
TestTrue('THread not gone over break(n/5) 1 at line '+IntToStr(Brk1.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk1.Line)
|
|
);
|
|
TestTrue('THread not gone over break(n/5) 2 at line '+IntToStr(Brk2.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk2.Line)
|
|
);
|
|
TestTrue('THread not gone over break(n/5) 3 at line '+IntToStr(Brk3.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk3.Line)
|
|
);
|
|
TestTrue('THread not gone over break(n/5) 4 at line '+IntToStr(Brk4.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk4.Line)
|
|
);
|
|
TestTrue('THread not gone over break(n/5) 5 at line '+IntToStr(Brk5.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk5.Line)
|
|
);
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestBreakThreadsMoveBreak1;
|
|
var
|
|
ExeName: String;
|
|
i, j: Integer;
|
|
MainBrk, Brk1: TDBGBreakPoint;
|
|
Entry: TBreakThreadPrgInfoEntry;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestThreadMove1) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'BreakPointThreadPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
MainBrk := Debugger.SetBreakPoint(Src, 'BrkMain1');
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgInitializeThreads('');
|
|
ThrPrgUpdateThreads('Init');
|
|
ThrPrgCheckNoSkip('Init');
|
|
|
|
(* Stopped in the main thread.
|
|
Set a new breakpoint at the current address of one of the subthreads.
|
|
It should be skipped until next loop.
|
|
Other sub-thread must not accidentally go over the breakpoint for sub-threads
|
|
*)
|
|
|
|
for j := 0 to 100 do begin
|
|
Brk1 := Debugger.SetBreakPoint(Src.FileName, FThrPrgInfo.Threads[0].Line);
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgUpdateThreads('loop one brk '+IntToStr(j));
|
|
ThrPrgCheckNoSkip('loop, one brk '+IntToStr(j));
|
|
|
|
for i := 0 to 9 do begin
|
|
Entry := FThrPrgInfo.Threads[i];
|
|
if Entry.PreviousLine < 0 then
|
|
continue;
|
|
TestTrue('THread not gone over break at line '+IntToStr(Brk1.Line)+' '+IntToStr(i),
|
|
not ThrPrgInfoHasGoneThroughLine(i, Brk1.Line)
|
|
);
|
|
end;
|
|
|
|
Brk1.ReleaseReference;
|
|
if j = 0 then
|
|
MainBrk.Enabled := False;
|
|
end;
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestBreakThreadsMoveBreak2;
|
|
var
|
|
ExeName: String;
|
|
i, j: Integer;
|
|
MainBrk, Brk1, Brk2, Brk3, Brk4, Brk5: TDBGBreakPoint;
|
|
Entry: TBreakThreadPrgInfoEntry;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestThreadMove2) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'BreakPointThreadPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
MainBrk := Debugger.SetBreakPoint(Src, 'BrkMain1');
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgInitializeThreads('');
|
|
ThrPrgUpdateThreads('Init');
|
|
ThrPrgCheckNoSkip('Init');
|
|
|
|
(* Try more breakpoints => so there is a likelihood that a several threads
|
|
hit breakpoints at the same time.
|
|
Then remove the breakpoints, while the FThrPrgInfo.ThrLoopFirst thread reports the hit
|
|
*)
|
|
for j := 0 to 150 do begin
|
|
if (j and 1) = 0 then begin
|
|
Brk1 := Debugger.SetBreakPoint(Src, 'BrkThread1');
|
|
Brk2 := Debugger.SetBreakPoint(Src, 'BrkThread3');
|
|
Brk3 := Debugger.SetBreakPoint(Src, 'BrkThread5');
|
|
Brk4 := Debugger.SetBreakPoint(Src, 'BrkThread7');
|
|
Brk5 := Debugger.SetBreakPoint(Src, 'BrkThread9');
|
|
end else begin
|
|
Brk1 := Debugger.SetBreakPoint(Src, 'BrkThread2');
|
|
Brk2 := Debugger.SetBreakPoint(Src, 'BrkThread4');
|
|
Brk3 := Debugger.SetBreakPoint(Src, 'BrkThread6');
|
|
Brk4 := Debugger.SetBreakPoint(Src, 'BrkThread8');
|
|
Brk5 := Debugger.SetBreakPoint(Src, 'BrkThread10');
|
|
end;
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgUpdateThreads('loop, changing brk '+IntToStr(j));
|
|
ThrPrgCheckNoSkip('loop, changing brk '+IntToStr(j));
|
|
|
|
for i := 0 to 9 do begin
|
|
Entry := FThrPrgInfo.Threads[i];
|
|
if Entry.PreviousLine < 0 then
|
|
continue;
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk1.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk2.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk3.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk4.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk5.Line) );
|
|
end;
|
|
|
|
Brk1.ReleaseReference; Brk2.ReleaseReference; Brk3.ReleaseReference; Brk4.ReleaseReference; Brk5.ReleaseReference;
|
|
end;
|
|
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestBreakThreadsHitBreak;
|
|
function CheckLastBrk(AnIdx, BrkLine, NextBrkLine: Integer): Boolean;
|
|
begin
|
|
Result := True; // defaults to ok, if Line is not in this range.
|
|
if FThrPrgInfo.Threads[AnIdx].LastBrkLine = -1 then exit;
|
|
if FThrPrgInfo.Threads[AnIdx].PreviousLastBrkLine = -1 then exit;
|
|
|
|
if FThrPrgInfo.Threads[AnIdx].Line = BrkLine then begin
|
|
if FThrPrgInfo.Threads[AnIdx].IsCurrent then
|
|
Result := FThrPrgInfo.Threads[AnIdx].LastBrkLine = BrkLine;
|
|
// otherwise...
|
|
end;
|
|
|
|
if FThrPrgInfo.Threads[AnIdx].Line = NextBrkLine then begin
|
|
if (FThrPrgInfo.Threads[AnIdx].LastBrkLine = NextBrkLine) then
|
|
Result := (FThrPrgInfo.Threads[AnIdx].LastBrkLoop = FThrPrgInfo.Threads[AnIdx].Loop) and
|
|
(FThrPrgInfo.Threads[AnIdx].PreviousLastBrkLine = BrkLine)
|
|
// PreviousLastBrkLoop can be equal or 1 less
|
|
else
|
|
Result := (FThrPrgInfo.Threads[AnIdx].LastBrkLine = BrkLine)
|
|
;
|
|
end;
|
|
|
|
if BrkLine < NextBrkLine then begin
|
|
if (FThrPrgInfo.Threads[AnIdx].Line > BrkLine) and
|
|
(FThrPrgInfo.Threads[AnIdx].Line < NextBrkLine)
|
|
then begin
|
|
Result := (FThrPrgInfo.Threads[AnIdx].LastBrkLine = BrkLine) and
|
|
(FThrPrgInfo.Threads[AnIdx].LastBrkLoop = FThrPrgInfo.Threads[AnIdx].Loop);
|
|
end;
|
|
end
|
|
else begin // going over loop-end
|
|
if (FThrPrgInfo.Threads[AnIdx].Line > BrkLine) or
|
|
(FThrPrgInfo.Threads[AnIdx].Line < NextBrkLine)
|
|
then begin
|
|
Result := (FThrPrgInfo.Threads[AnIdx].LastBrkLine = BrkLine) and
|
|
( (FThrPrgInfo.Threads[AnIdx].LastBrkLoop = FThrPrgInfo.Threads[AnIdx].Loop) or
|
|
(FThrPrgInfo.Threads[AnIdx].LastBrkLoop = FThrPrgInfo.Threads[AnIdx].Loop - 1)
|
|
);
|
|
end;
|
|
end;
|
|
end;
|
|
var
|
|
ExeName: String;
|
|
i, j: Integer;
|
|
MainBrk, Brk1, Brk2, Brk3, Brk4, Brk5: TDBGBreakPoint;
|
|
Entry: TBreakThreadPrgInfoEntry;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestThreadHit) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'BreakPointThreadPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
MainBrk := Debugger.SetBreakPoint(Src, 'BrkMain1');
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgInitializeThreads('');
|
|
ThrPrgUpdateThreads('Init');
|
|
ThrPrgCheckNoSkip('Init');
|
|
|
|
Brk1 := Debugger.SetBreakPoint(Src, 'BrkThread1');
|
|
Brk2 := Debugger.SetBreakPoint(Src, 'BrkThread3');
|
|
Brk3 := Debugger.SetBreakPoint(Src, 'BrkThread5');
|
|
Brk4 := Debugger.SetBreakPoint(Src, 'BrkThread7');
|
|
Brk5 := Debugger.SetBreakPoint(Src, 'BrkThread9');
|
|
|
|
for j := 0 to 150 do begin
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
ThrPrgUpdateThreads('loop, changing brk '+IntToStr(j));
|
|
ThrPrgCheckNoSkip('loop, changing brk '+IntToStr(j));
|
|
|
|
for i := 0 to 9 do begin
|
|
Entry := FThrPrgInfo.Threads[i];
|
|
if Entry.PreviousLine < 0 then
|
|
continue;
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk1.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk2.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk3.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk4.Line) );
|
|
TestTrue('THread not gone over break '+IntToStr(i), not ThrPrgInfoHasGoneThroughLine(i, Brk5.Line) );
|
|
|
|
// The thread must be between any 2 of thebreakpoint, and it must have hit
|
|
// the breakpoint at the start of the range.
|
|
TestTrue('Has hit last brk between 1 - 2 / thr= '+IntToStr(i), CheckLastBrk(i, Brk1.Line, Brk2.Line));
|
|
TestTrue('Has hit last brk between 2 - 3 / thr= '+IntToStr(i), CheckLastBrk(i, Brk2.Line, Brk3.Line));
|
|
TestTrue('Has hit last brk between 3 - 4 / thr= '+IntToStr(i), CheckLastBrk(i, Brk3.Line, Brk4.Line));
|
|
TestTrue('Has hit last brk between 4 - 5 / thr= '+IntToStr(i), CheckLastBrk(i, Brk4.Line, Brk5.Line));
|
|
TestTrue('Has hit last brk between 5 - 1 / thr= '+IntToStr(i), CheckLastBrk(i, Brk5.Line, Brk1.Line));
|
|
end;
|
|
end;
|
|
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBreakPoint.TestBreakThreadsIgnoreOther;
|
|
var
|
|
ExeName: String;
|
|
i, j, ThreadIdMain: Integer;
|
|
Brk: array [0..9] of TDBGBreakPoint;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestThreadIgnoreOther) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'BreakPointThread2Prg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
Debugger.SetBreakPoint(Src, 'BrkMainBegin');
|
|
for i := 0 to 9 do
|
|
Brk[i] := Debugger.SetBreakPoint(Src, 'BrkMain'+IntToStr(i));
|
|
AssertDebuggerNotInErrorState;
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause, 'main init');
|
|
|
|
ThreadIdMain := dbg.Threads.CurrentThreads.CurrentThreadId;
|
|
|
|
|
|
for j := 1 to 70 do begin
|
|
for i := 0 to 9 do begin
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause, 'in loop');
|
|
TestEquals('ThreadId', ThreadIdMain, dbg.Threads.CurrentThreads.CurrentThreadId);
|
|
TestLocation('loop '+IntToStr(j)+', '+IntToStr(i), 'BrkMain'+IntToStr(i), j);
|
|
|
|
if i > 0 then
|
|
TestEquals('prev brk hitcnt '+IntToStr(j)+', '+IntToStr(i),
|
|
j, Debugger.BreakPointByName('BrkMain'+IntToStr(i)).HitCount)
|
|
else
|
|
TestEquals('prev brk hitcnt '+IntToStr(j)+', '+IntToStr(i),
|
|
j-1, Debugger.BreakPointByName('BrkMain9').HitCount);
|
|
end;
|
|
end;
|
|
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
|
|
initialization
|
|
|
|
RegisterDbgTest(TTestBreakPoint);
|
|
ControlTest := TestControlRegisterTest('TTestBreak');
|
|
ControlTestBreak := TestControlRegisterTest('TTestBreakPoint', ControlTest);
|
|
ControlTestThreadNoSkip := TestControlRegisterTest('TTestBreakThreadNoSkip', ControlTest);
|
|
ControlTestThreadMove1 := TestControlRegisterTest('TTestBreakThreadMove1', ControlTest);
|
|
ControlTestThreadMove2 := TestControlRegisterTest('TTestBreakThreadMove2', ControlTest);
|
|
ControlTestThreadHit := TestControlRegisterTest('TTestBreakThreadHit', ControlTest);
|
|
ControlTestThreadIgnoreOther := TestControlRegisterTest('TTestBreakThreadIgnoreOther', ControlTest);
|
|
end.
|
|
|