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
Show file tree
Hide file tree
Changes from 6 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
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)
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
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;
gregg-miskelly 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<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;
gregg-miskelly marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

if (allInstructionBreakpoints)
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved
{
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 = true,
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();
WardenGnaw marked this conversation as resolved.
Show resolved Hide resolved

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