Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/MICore/CommandFactories/MICommandFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,15 @@ public virtual async Task BreakCondition(string bkptno, string expr)
await _debugger.CmdAsync(command, ResultClass.done);
}

/// <summary>
/// Sends -break-after to set an ignore count on a breakpoint.
/// </summary>
public virtual async Task<Results> BreakAfter(string bkptno, uint count)
{
string command = string.Format(CultureInfo.InvariantCulture, "-break-after {0} {1}", bkptno, count);
return await _debugger.CmdAsync(command, ResultClass.done);
}

public virtual IEnumerable<Guid> GetSupportedExceptionCategories()
{
return new Guid[0];
Expand Down
58 changes: 45 additions & 13 deletions src/MIDebugEngine/AD7.Impl/AD7BoundBreakpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ internal class AD7BoundBreakpoint : IDebugBoundBreakpoint2
private BoundBreakpoint _bp;

private bool _deleted;
private enum_BP_PASSCOUNT_STYLE _passCountStyle;
private uint _passCountValue;

internal bool Enabled
{
Expand All @@ -37,6 +39,7 @@ internal bool Enabled
internal string Number { get { return _bp.Number; } }
internal AD7PendingBreakpoint PendingBreakpoint { get { return _pendingBreakpoint; } }
internal bool IsDataBreakpoint { get { return PendingBreakpoint.IsDataBreakpoint; } }
internal bool HasPassCount { get { return _passCountStyle != enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_NONE; } }

public AD7BoundBreakpoint(AD7Engine engine, AD7PendingBreakpoint pendingBreakpoint, AD7BreakpointResolution breakpointResolution, BoundBreakpoint bp)
{
Expand Down Expand Up @@ -143,8 +146,7 @@ int IDebugBoundBreakpoint2.GetState(enum_BP_STATE[] pState)
return Constants.S_OK;
}

// The sample engine does not support hit counts on breakpoints. A real-world debugger will want to keep track
// of how many times a particular bound breakpoint has been hit and return it here.
// Returns the number of times this breakpoint has been hit.
int IDebugBoundBreakpoint2.GetHitCount(out uint pdwHitCount)
{
pdwHitCount = _bp.HitCount;
Expand All @@ -156,29 +158,59 @@ int IDebugBoundBreakpoint2.SetCondition(BP_CONDITION bpCondition)
return ((IDebugPendingBreakpoint2)_pendingBreakpoint).SetCondition(bpCondition); // setting on the pending break will set the condition
}

// The sample engine does not support hit counts on breakpoints. A real-world debugger will want to keep track
// of how many times a particular bound breakpoint has been hit. The debugger calls SetHitCount when the user
// resets a breakpoint's hit count.
// Called by the debugger when the user resets a breakpoint's hit count.
int IDebugBoundBreakpoint2.SetHitCount(uint dwHitCount)
{
throw new NotImplementedException();
_bp.SetHitCount(dwHitCount);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_bp.SetHitCount(dwHitCount);

Since we also call SetHitCount from the times value of a breakpoint, don't we need to maintain a delta between GDB's hit count and the AD7 hit count?

return Constants.S_OK;
}

/// <summary>
/// Syncs the hit count from GDB's "times" field in =breakpoint-modified events.
/// </summary>
internal void SetHitCount(uint hitCount)
{
_bp.SetHitCount(hitCount);
}

// The sample engine does not support pass counts on breakpoints.
// This is used to specify the breakpoint hit count condition.
int IDebugBoundBreakpoint2.SetPassCount(BP_PASSCOUNT bpPassCount)
{
if (bpPassCount.stylePassCount != enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_NONE)
{
Delete();
_engine.Callback.OnBreakpointUnbound(this, enum_BP_UNBOUND_REASON.BPUR_BREAKPOINT_ERROR);
return Constants.E_FAIL;
}
_passCountStyle = bpPassCount.stylePassCount;
_passCountValue = bpPassCount.dwPassCount;
return Constants.S_OK;
}

#endregion

internal void IncrementHitCount()
{
_bp.IncrementHitCount();
}

/// <summary>
/// Evaluates whether the debugger should break at this breakpoint based on the
/// current hit count and the configured pass count condition.
/// Must be called after IncrementHitCount.
/// </summary>
internal bool ShouldBreak()
{
uint hitCount = _bp.HitCount;
switch (_passCountStyle)
{
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_NONE:
return true;
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_EQUAL:
return hitCount == _passCountValue;
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_EQUAL_OR_GREATER:
return hitCount >= _passCountValue;
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_MOD:
return _passCountValue != 0 && (hitCount % _passCountValue) == 0;
default:
return true;
}
}

internal void UpdateAddr(ulong addr)
{
_bp.Addr = addr;
Expand Down
86 changes: 77 additions & 9 deletions src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,6 @@ private bool CanBind()
return false;
}
}
if ((_bpRequestInfo.dwFields & enum_BPREQI_FIELDS.BPREQI_PASSCOUNT) != 0)
{
this.SetError(new AD7ErrorBreakpoint(this, ResourceStrings.UnsupportedPassCountBreakpoint, enum_BP_ERROR_TYPE.BPET_GENERAL_ERROR));
return false;
}

return true;
}
Expand Down Expand Up @@ -393,6 +388,40 @@ internal async Task BindAsync()
}
}
}

// Set ignore count via -break-after if a pass count is configured
if (_bp != null && (_bpRequestInfo.dwFields & enum_BPREQI_FIELDS.BPREQI_PASSCOUNT) != 0
&& _bpRequestInfo.bpPassCount.stylePassCount != enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_NONE)
{
uint ignoreCount = ComputeIgnoreCount(_bpRequestInfo.bpPassCount.stylePassCount, _bpRequestInfo.bpPassCount.dwPassCount, 0);
await _bp.SetBreakAfterAsync(ignoreCount, _engine.DebuggedProcess);
}
}
}

/// <summary>
/// Computes the ignore count for -break-after, accounting for hits already
/// counted from a prior breakpoint (<paramref name="currentHits"/>).
/// </summary>
private static uint ComputeIgnoreCount(enum_BP_PASSCOUNT_STYLE style, uint passCount, uint currentHits)
{
if (passCount == 0)
{
return 0;
}

switch (style)
{
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_EQUAL:
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_EQUAL_OR_GREATER:
// Need to stop at hit N. Already counted currentHits, so skip (N - 1 - currentHits) more.
return passCount - 1 > currentHits ? passCount - 1 - currentHits : 0;
case enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_MOD:
// Next stop is at the next multiple of passCount after currentHits.
uint remainder = currentHits % passCount;
return remainder == 0 ? passCount - 1 : passCount - 1 - remainder;
default:
return 0;
}
}

Expand All @@ -406,6 +435,11 @@ internal AD7BoundBreakpoint AddBoundBreakpoint(BoundBreakpoint bp)
}
AD7BreakpointResolution breakpointResolution = new AD7BreakpointResolution(_engine, IsDataBreakpoint, bp.Addr, bp.FunctionName, bp.DocumentContext(_engine));
AD7BoundBreakpoint boundBreakpoint = new AD7BoundBreakpoint(_engine, this, breakpointResolution, bp);
// Apply pass count (hit count condition) from the original request to the bound breakpoint
if ((_bpRequestInfo.dwFields & enum_BPREQI_FIELDS.BPREQI_PASSCOUNT) != 0)
{
((IDebugBoundBreakpoint2)boundBreakpoint).SetPassCount(_bpRequestInfo.bpPassCount);
}
//check can bind one last time. If the pending breakpoint was deleted before now, we need to clean up gdb side
if (CanBind())
{
Expand Down Expand Up @@ -645,13 +679,47 @@ int IDebugPendingBreakpoint2.SetCondition(BP_CONDITION bpCondition)
return Constants.S_OK;
}

// The sample engine does not support pass counts on breakpoints.
int IDebugPendingBreakpoint2.SetPassCount(BP_PASSCOUNT bpPassCount)
{
if (bpPassCount.stylePassCount != enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_NONE)
_bpRequestInfo.bpPassCount = bpPassCount;
_bpRequestInfo.dwFields |= enum_BPREQI_FIELDS.BPREQI_PASSCOUNT;

PendingBreakpoint bp = null;
lock (_boundBreakpoints)
{
this.SetError(new AD7ErrorBreakpoint(this, ResourceStrings.UnsupportedPassCountBreakpoint, enum_BP_ERROR_TYPE.BPET_GENERAL_ERROR), true);
return Constants.E_FAIL;
foreach (AD7BoundBreakpoint boundBp in _boundBreakpoints)
{
((IDebugBoundBreakpoint2)boundBp).SetPassCount(bpPassCount);
}
if (_bp != null)
{
bp = _bp;
}
}

// Re-send -break-after to GDB with the updated ignore count, accounting
// for the current hit count so the breakpoint fires at the right time.
if (bp != null && bpPassCount.stylePassCount != enum_BP_PASSCOUNT_STYLE.BP_PASSCOUNT_NONE)
{
uint currentHits = 0;
lock (_boundBreakpoints)
{
foreach (AD7BoundBreakpoint boundBp in _boundBreakpoints)
{
uint hc;
if (((IDebugBoundBreakpoint2)boundBp).GetHitCount(out hc) == Constants.S_OK && hc > currentHits)
{
currentHits = hc;
}
}
}
uint ignoreCount = ComputeIgnoreCount(bpPassCount.stylePassCount, bpPassCount.dwPassCount, currentHits);
_engine.DebuggedProcess.WorkerThread.RunOperation(() =>
{
_engine.DebuggedProcess.AddInternalBreakAction(
() => bp.SetBreakAfterAsync(ignoreCount, _engine.DebuggedProcess)
);
});
}
return Constants.S_OK;
}
Expand Down
24 changes: 23 additions & 1 deletion src/MIDebugEngine/Engine.Impl/BreakpointManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ public async Task BreakpointModified(object sender, EventArgs args)
return;
}

// Sync GDB's hit count ("times") for pass count breakpoints.
// e.g. =breakpoint-modified,bkpt={number="1",...,times="5",ignore="2",...}
string timesStr = bkpt.TryFindString("times");
if (!string.IsNullOrEmpty(timesStr) && uint.TryParse(timesStr, out uint times))
{
foreach (AD7BoundBreakpoint boundBp in pending.EnumBoundBreakpoints())
{
if (boundBp.HasPassCount)
{
boundBp.SetHitCount(times);
}
}
}

string warning = bkpt.TryFindString("warning");
if (!string.IsNullOrEmpty(warning))
{
Expand Down Expand Up @@ -212,7 +226,15 @@ public AD7BoundBreakpoint[] FindHitBreakpoints(string bkptno, ulong addr, /*OPTI
continue;
}

hitBoundBreakpoints.Add(currBoundBp);
// Pass count breakpoints get their hit count from =breakpoint-modified.
if (!currBoundBp.HasPassCount)
{
currBoundBp.IncrementHitCount();
}
if (currBoundBp.ShouldBreak())
{
hitBoundBreakpoints.Add(currBoundBp);
}
}

fContinue = (hitBoundBreakpoints.Count == 0 && hitBps.Length != 0);
Expand Down
23 changes: 23 additions & 0 deletions src/MIDebugEngine/Engine.Impl/Breakpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,18 @@ internal async Task SetConditionAsync(string expr, DebuggedProcess process)
await process.MICommandFactory.BreakCondition(Number, expr);
}
}

/// <summary>
/// Sends -break-after to set an ignore count on this breakpoint.
/// </summary>
internal async Task<Results> SetBreakAfterAsync(uint count, DebuggedProcess process)
{
if (process.ProcessState != MICore.ProcessState.Exited)
{
return await process.MICommandFactory.BreakAfter(Number, count);
}
return null;
}
}

internal class BoundBreakpoint
Expand Down Expand Up @@ -398,6 +410,7 @@ internal BoundBreakpoint(PendingBreakpoint parent, ulong addr, /*optional*/ Tupl
internal BoundBreakpoint(PendingBreakpoint parent, ulong addr, uint size, string bkptno)
{
Addr = addr;
HitCount = 0;
Enabled = true;
this.Number = bkptno;
_parent = parent;
Expand Down Expand Up @@ -434,5 +447,15 @@ internal uint Line
_textPosition = new MITextPosition(_textPosition.FileName, value);
}
}

internal void IncrementHitCount()
{
HitCount++;
}

internal void SetHitCount(uint count)
{
HitCount = count;
}
}
}
Loading
Loading