Skip to content

Commit

Permalink
Add support for SupportsInstructionBreakpoints (#1192)
Browse files Browse the repository at this point in the history
* Add support for SetInstructionBreakpointsRequest
  • Loading branch information
WardenGnaw authored Aug 16, 2021
1 parent d30bcc0 commit 3f1c642
Show file tree
Hide file tree
Showing 18 changed files with 708 additions and 29 deletions.
25 changes: 10 additions & 15 deletions src/DebugEngineHost.VSCode/HostMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,6 @@ public static void ReleaseCodeContextId(IntPtr codeContextId)
}
}

public static IDebugCodeContext2 GetCodeContextForIntPtr(IntPtr codeContextId)
{
lock (s_codeContexts)
{
IDebugCodeContext2 codeContext;
if (!s_codeContexts.TryGet(codeContextId.ToInt32(), out codeContext))
{
throw new ArgumentOutOfRangeException(nameof(codeContextId));
}

return codeContext;
}
}

public static IDebugDocumentPosition2 GetDocumentPositionForIntPtr(IntPtr documentPositionId)
{
lock (s_documentPositions)
Expand Down Expand Up @@ -145,7 +131,16 @@ public static IntPtr GetIntPtrForDataBreakpointAddress(string address)
/// <returns>code context object</returns>
public static IDebugCodeContext2 GetDebugCodeContextForIntPtr(IntPtr contextId)
{
throw new NotImplementedException();
lock (s_codeContexts)
{
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
197 changes: 187 additions & 10 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<ulong, IDebugPendingBreakpoint2> m_instructionBreakpoints;
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<ulong, IDebugPendingBreakpoint2>();
m_variableManager = new VariableManager();
}

Expand Down Expand Up @@ -307,6 +309,49 @@ ppBPRequest is AD7BreakPointRequest ad7BreakpointRequest &&
return tracepoints;
}

public StoppedEvent.ReasonValue GetStoppedEventReason(IDebugBreakpointEvent2 breakpointEvent)
{
StoppedEvent.ReasonValue reason = StoppedEvent.ReasonValue.Breakpoint;

if (breakpointEvent != null)
{
if (breakpointEvent.EnumBreakpoints(out IEnumDebugBoundBreakpoints2 enumBreakpoints) == HRConstants.S_OK &&
enumBreakpoints.GetCount(out uint bpCount) == HRConstants.S_OK &&
bpCount > 0)
{

bool allInstructionBreakpoints = true;

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)
{
allInstructionBreakpoints = false;
break;
}
}
}
}

if (allInstructionBreakpoints)
{
reason = StoppedEvent.ReasonValue.InstructionBreakpoint;
}
}
}

return reason;
}

private static long FileTimeToPosix(FILETIME ft)
{
long date = ((long)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
Expand All @@ -318,9 +363,9 @@ private static long FileTimeToPosix(FILETIME ft)
return date / 10000000;
}

private int GetMemoryContext(string memoryReference, int? offset, out IDebugMemoryContext2 memoryContext, out ulong address)
private ulong ResolveInstructionReference(string memoryReference, int? offset)
{
memoryContext = null;
ulong address;

if (memoryReference.StartsWith("0x", StringComparison.Ordinal))
{
Expand All @@ -343,6 +388,15 @@ private int GetMemoryContext(string memoryReference, int? offset, out IDebugMemo
}
}

return address;
}

private int GetMemoryContext(string memoryReference, int? offset, out IDebugMemoryContext2 memoryContext, out ulong address)
{
memoryContext = null;

address = ResolveInstructionReference(memoryReference, offset);

int hr = HRConstants.E_NOTIMPL; // Engine does not support IDebugMemoryBytesDAP

if (m_engine is IDebugMemoryBytesDAP debugMemoryBytesDAPEngine)
Expand Down Expand Up @@ -413,11 +467,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 @@ -492,7 +547,7 @@ private static IEnumerable<IDebugBoundBreakpoint2> GetBoundBreakpoints(IDebugBre
IDebugCodeContext2 codeContext;
try
{
codeContext = HostMarshal.GetCodeContextForIntPtr(location.unionmember1);
codeContext = HostMarshal.GetDebugCodeContextForIntPtr(location.unionmember1);
HostMarshal.ReleaseCodeContextId(location.unionmember1);
location.unionmember1 = IntPtr.Zero;
}
Expand Down Expand Up @@ -825,6 +880,7 @@ protected override void HandleInitializeRequestAsync(IRequestResponder<Initializ
SupportsDisassembleRequest = m_engine is IDebugMemoryBytesDAP,
SupportsValueFormattingOptions = true,
SupportsSteppingGranularity = true,
SupportsInstructionBreakpoints = m_engine is IDebugMemoryBytesDAP
};

responder.SetResponse(initializeResponse);
Expand Down Expand Up @@ -2572,9 +2628,128 @@ 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;
}

ErrorBuilder eb = new ErrorBuilder(() => AD7Resources.Error_UnableToSetInstructionBreakpoint);

SetInstructionBreakpointsResponse response = new SetInstructionBreakpointsResponse();

List<InstructionBreakpoint> breakpoints = responder.Arguments.Breakpoints;
Dictionary<ulong, IDebugPendingBreakpoint2> newBreakpoints = new Dictionary<ulong, IDebugPendingBreakpoint2>();
try
{
HashSet<ulong> requestAddresses = responder.Arguments.Breakpoints.Select(x => ResolveInstructionReference(x.InstructionReference, x.Offset)).ToHashSet();

foreach (KeyValuePair<ulong, IDebugPendingBreakpoint2> b in m_instructionBreakpoints)
{
if (requestAddresses.Contains(b.Key))
{
newBreakpoints[b.Key] = b.Value; // breakpoint still in new list
}
else
{
IDebugPendingBreakpoint2 pendingBp = b.Value;
if (pendingBp != null &&
pendingBp.GetBreakpointRequest(out IDebugBreakpointRequest2 request) == HRConstants.S_OK &&
request is AD7BreakPointRequest ad7Request)
{
HostMarshal.ReleaseCodeContextId(ad7Request.MemoryContextIntPtr);
}
else
{
Debug.Fail("Why can't we retrieve the MemoryContextIntPtr?");
}
b.Value.Delete(); // not in new list so delete it
}
}

foreach (var instructionBp in responder.Arguments.Breakpoints)
{
eb.CheckHR(GetMemoryContext(instructionBp.InstructionReference, instructionBp.Offset, out IDebugMemoryContext2 memoryContext, out ulong address));

if (m_instructionBreakpoints.ContainsKey(address))
{
IDebugBreakpointRequest2 breakpointRequest;
if (m_instructionBreakpoints[address].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[address];
toRemove.Delete();
m_instructionBreakpoints.Remove(address);
}
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);

eb.CheckHR(m_engine.CreatePendingBreakpoint(pBPRequest, out pendingBp));

if (pendingBp != null && pendingBp.Bind() == HRConstants.S_OK)
{
newBreakpoints[address] = 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,
Message = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_UnableToSetInstructionBreakpoint, address)
}); // couldn't create and/or bind
}
}
}

m_instructionBreakpoints = newBreakpoints;

responder.SetResponse(response);
}
catch (Exception e)
{
responder.SetError(new ProtocolException(e.Message));
}
}

#endregion

#region IDebugPortNotify2
#region IDebugPortNotify2

int IDebugPortNotify2.AddProgramNode(IDebugProgramNode2 programNode)
{
Expand Down Expand Up @@ -2693,7 +2868,9 @@ 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);
IDebugBreakpointEvent2 breakpointEvent = pEvent as IDebugBreakpointEvent2;
StoppedEvent.ReasonValue reason = GetStoppedEventReason(breakpointEvent);
IList <Tracepoint> tracepoints = GetTracepoints(breakpointEvent);
if (tracepoints.Any())
{
ThreadPool.QueueUserWorkItem((o) =>
Expand Down Expand Up @@ -2724,13 +2901,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
23 changes: 23 additions & 0 deletions src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ static public uint GetNextBreakpointId()

public AD7FunctionPosition FunctionPosition { get; private set; }

public IDebugMemoryContext2 MemoryContext { get; private set; }

// Used for Releasing the MemoryContext.
// Caller of AD7BreakPointRequest(MemoryContext) is required to
// release it with HostMarshal.ReleaseCodeContextId
public IntPtr MemoryContextIntPtr { get; private set; }

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

Expand All @@ -41,6 +48,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 +63,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 +87,13 @@ 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;
MemoryContextIntPtr = HostMarshal.RegisterCodeContext(MemoryContext as IDebugCodeContext2);
pBPRequestInfo[0].bpLocation.unionmember1 = MemoryContextIntPtr;

}
}
if ((dwFields & enum_BPREQI_FIELDS.BPREQI_CONDITION) != 0 && !string.IsNullOrWhiteSpace(Condition))
{
Expand Down
11 changes: 10 additions & 1 deletion src/OpenDebugAD7/AD7Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3f1c642

Please sign in to comment.