Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for SupportsInstructionBreakpoints #1192

Merged
merged 8 commits into from
Aug 16, 2021
Merged
11 changes: 10 additions & 1 deletion src/DebugEngineHost.VSCode/HostMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,16 @@ public static IntPtr GetIntPtrForDataBreakpointAddress(string address)
/// <returns>code context object</returns>
public static IDebugCodeContext2 GetDebugCodeContextForIntPtr(IntPtr contextId)
{
throw new NotImplementedException();
lock (s_codeContexts)
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
{
IDebugCodeContext2 codeContext;
if (!s_codeContexts.TryGet(contextId.ToInt32(), out codeContext))
{
throw new ArgumentOutOfRangeException(nameof(contextId));
}

return codeContext;
}
}

public static IDebugEventCallback2 GetThreadSafeEventCallback(IDebugEventCallback2 ad7Callback)
Expand Down
156 changes: 149 additions & 7 deletions src/OpenDebugAD7/AD7DebugSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ internal sealed class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDe
private int m_nextContextId = 1;

private Dictionary<string, IDebugPendingBreakpoint2> m_functionBreakpoints;
private Dictionary<string, IDebugPendingBreakpoint2> m_instructionBreakpoints;
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
private readonly HandleCollection<IDebugStackFrame2> m_frameHandles;

private IDebugProgram2 m_program;
Expand Down Expand Up @@ -119,6 +120,7 @@ public AD7DebugSession(Stream debugAdapterStdIn, Stream debugAdapterStdOut, List
m_frameHandles = new HandleCollection<IDebugStackFrame2>();
m_breakpoints = new Dictionary<string, Dictionary<int, IDebugPendingBreakpoint2>>();
m_functionBreakpoints = new Dictionary<string, IDebugPendingBreakpoint2>();
m_instructionBreakpoints = new Dictionary<string, IDebugPendingBreakpoint2>();
m_variableManager = new VariableManager();
}

Expand Down Expand Up @@ -413,11 +415,12 @@ internal void FireStoppedEvent(IDebugThread2 thread, StoppedEvent.ReasonValue re
Protocol.SendEvent(new OpenDebugStoppedEvent()
{
Reason = reason,
Text = text,
ThreadId = thread.Id(),
// Additional Breakpoint Information for Testing/Logging
Source = textPosition.Source,
Line = textPosition.Line,
Column = textPosition.Column,
Text = text,
ThreadId = thread.Id()
});
});

Expand Down Expand Up @@ -825,6 +828,7 @@ protected override void HandleInitializeRequestAsync(IRequestResponder<Initializ
SupportsDisassembleRequest = true,
SupportsValueFormattingOptions = true,
SupportsSteppingGranularity = true,
SupportsInstructionBreakpoints = m_engine is IDebugMemoryBytesDAP
};

responder.SetResponse(initializeResponse);
Expand Down Expand Up @@ -2572,9 +2576,116 @@ protected override void HandleReadMemoryRequestAsync(IRequestResponder<ReadMemor
}
}

#endregion
protected override void HandleSetInstructionBreakpointsRequestAsync(IRequestResponder<SetInstructionBreakpointsArguments, SetInstructionBreakpointsResponse> responder)
{
if (responder.Arguments.Breakpoints == null)
{
responder.SetError(new ProtocolException("HandleSetInstructionBreakpointsRequest failed: Missing 'breakpoints'."));
return;
}

SetInstructionBreakpointsResponse response = new SetInstructionBreakpointsResponse();
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved

List<InstructionBreakpoint> breakpoints = responder.Arguments.Breakpoints;
Dictionary<string, IDebugPendingBreakpoint2> newBreakpoints = new Dictionary<string, IDebugPendingBreakpoint2>();

foreach (KeyValuePair<string, IDebugPendingBreakpoint2> b in m_instructionBreakpoints)
{
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
if (responder.Arguments.Breakpoints.Find((p) => p.InstructionReference == b.Key) != null)
{
newBreakpoints[b.Key] = b.Value; // breakpoint still in new list
}
else
{
b.Value.Delete(); // not in new list so delete it
}
}

#region IDebugPortNotify2
foreach (var instructionBp in responder.Arguments.Breakpoints)
{
if (GetMemoryContext(instructionBp.InstructionReference, instructionBp.Offset, out IDebugMemoryContext2 memoryContext, out _) == HRConstants.E_NOTIMPL)
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
{
responder.SetError(new ProtocolException("Debug Engine does not support InstructionBreakpoints."));
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
return;
}

if (m_instructionBreakpoints.ContainsKey(instructionBp.InstructionReference))
{
IDebugBreakpointRequest2 breakpointRequest;
if (m_instructionBreakpoints[instructionBp.InstructionReference].GetBreakpointRequest(out breakpointRequest) == 0 &&
breakpointRequest is AD7BreakPointRequest ad7BPRequest)
{
// Check to see if this breakpoint has a condition that has changed.
if (!StringComparer.Ordinal.Equals(ad7BPRequest.Condition, instructionBp.Condition))
{
// Condition has been modified. Delete breakpoint so it will be recreated with the updated condition.
var toRemove = m_instructionBreakpoints[instructionBp.InstructionReference];
toRemove.Delete();
m_instructionBreakpoints.Remove(instructionBp.InstructionReference);
}
else
{
if (ad7BPRequest.BindResult != null)
{
response.Breakpoints.Add(ad7BPRequest.BindResult);
}
else
{
response.Breakpoints.Add(new Breakpoint()
{
Id = (int)ad7BPRequest.Id,
Verified = true,
Line = 0
});

}
continue;
}
}
}
else
{
IDebugPendingBreakpoint2 pendingBp;
AD7BreakPointRequest pBPRequest = new AD7BreakPointRequest(memoryContext);
int hr = HRConstants.E_FAIL;

if (m_engine.CreatePendingBreakpoint(pBPRequest, out pendingBp) == HRConstants.S_OK && pendingBp != null)
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
{
hr = pendingBp.Bind();
}

m_instructionBreakpoints.Add(instructionBp.InstructionReference, pendingBp);
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved

if (hr == HRConstants.S_OK)
{
newBreakpoints[instructionBp.InstructionReference] = pendingBp;
response.Breakpoints.Add(new Breakpoint()
{
Id = (int)pBPRequest.Id,
Verified = true,
Line = 0
}); // success
}
else
{
response.Breakpoints.Add(new Breakpoint()
{
Id = (int)pBPRequest.Id,
Verified = false,
Line = 0
}); // couldn't create and/or bind
}
}
}

m_instructionBreakpoints = newBreakpoints;

responder.SetResponse(response);
}

#endregion

#region IDebugPortNotify2

int IDebugPortNotify2.AddProgramNode(IDebugProgramNode2 programNode)
{
Expand Down Expand Up @@ -2693,7 +2804,38 @@ public void HandleIDebugEntryPointEvent2(IDebugEngine2 pEngine, IDebugProcess2 p

public void HandleIDebugBreakpointEvent2(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent)
{
IList<Tracepoint> tracepoints = GetTracepoints(pEvent as IDebugBreakpointEvent2);
StoppedEvent.ReasonValue reason = StoppedEvent.ReasonValue.Breakpoint;

IDebugBreakpointEvent2 breakpointEvent = pEvent as IDebugBreakpointEvent2;
if (breakpointEvent != null)
{
if (breakpointEvent.EnumBreakpoints(out IEnumDebugBoundBreakpoints2 enumBreakpoints) == HRConstants.S_OK)
{
IDebugBoundBreakpoint2[] boundBp = new IDebugBoundBreakpoint2[1];
uint fetched = 0;

while (enumBreakpoints.Next(1, boundBp, ref fetched) == HRConstants.S_OK)
{

if (boundBp[0].GetPendingBreakpoint(out IDebugPendingBreakpoint2 pendingBreakpoint) == HRConstants.S_OK)
{
if (pendingBreakpoint.GetBreakpointRequest(out IDebugBreakpointRequest2 breakpointRequest) == HRConstants.S_OK)
{
AD7BreakPointRequest request = breakpointRequest as AD7BreakPointRequest;

if (breakpointRequest != null && request.MemoryContext != null)
{
reason = StoppedEvent.ReasonValue.InstructionBreakpoint;
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
break;
}
}
}

}
}
}

IList<Tracepoint> tracepoints = GetTracepoints(breakpointEvent);
if (tracepoints.Any())
{
ThreadPool.QueueUserWorkItem((o) =>
Expand Down Expand Up @@ -2724,13 +2866,13 @@ public void HandleIDebugBreakpointEvent2(IDebugEngine2 pEngine, IDebugProcess2 p
}
else
{
FireStoppedEvent(pThread, StoppedEvent.ReasonValue.Breakpoint);
FireStoppedEvent(pThread, reason);
}
});
}
else
{
FireStoppedEvent(pThread, StoppedEvent.ReasonValue.Breakpoint);
FireStoppedEvent(pThread, reason);
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ static public uint GetNextBreakpointId()

public AD7FunctionPosition FunctionPosition { get; private set; }

public IDebugMemoryContext2 MemoryContext { get; private set; }

// Unique identifier for breakpoint when communicating with VSCode
public uint Id { get; private set; }

Expand All @@ -41,6 +43,11 @@ public AD7BreakPointRequest(string functionName)
FunctionPosition = new AD7FunctionPosition(functionName);
}

public AD7BreakPointRequest(IDebugMemoryContext2 memoryContext)
{
MemoryContext = memoryContext;
}

public int GetLocationType(enum_BP_LOCATION_TYPE[] pBPLocationType)
{
if (DocumentPosition != null)
Expand All @@ -51,6 +58,10 @@ public int GetLocationType(enum_BP_LOCATION_TYPE[] pBPLocationType)
{
pBPLocationType[0] = enum_BP_LOCATION_TYPE.BPLT_CODE_FUNC_OFFSET;
}
else if (MemoryContext != null)
{
pBPLocationType[0] = enum_BP_LOCATION_TYPE.BPLT_CODE_CONTEXT;
}

return 0;
}
Expand All @@ -71,6 +82,11 @@ public int GetRequestInfo(enum_BPREQI_FIELDS dwFields, BP_REQUEST_INFO[] pBPRequ
pBPRequestInfo[0].bpLocation.bpLocationType = (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FUNC_OFFSET;
pBPRequestInfo[0].bpLocation.unionmember2 = HostMarshal.RegisterFunctionPosition(FunctionPosition);
}
else if (MemoryContext != null)
{
pBPRequestInfo[0].bpLocation.bpLocationType = (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_CONTEXT;
pBPRequestInfo[0].bpLocation.unionmember1 = HostMarshal.RegisterCodeContext(MemoryContext as IDebugCodeContext2);
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
}
}
if ((dwFields & enum_BPREQI_FIELDS.BPREQI_CONDITION) != 0 && !string.IsNullOrWhiteSpace(Condition))
{
Expand Down