diff --git a/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs b/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs index c47b1c105..63d622d95 100644 --- a/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs +++ b/src/DebugEngineHost.Stub/DebugEngineHost.ref.cs @@ -229,6 +229,18 @@ public static IDebugDocumentPosition2 GetDocumentPositionForIntPtr(IntPtr docume throw new NotImplementedException(); } + /// + /// Obtains a function position interface given the specified IntPtr of the location. + /// + /// In VS, the IUnknown pointer to QI for a function position. In VS Code, + /// the identifier for the function position + /// Function position object + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ForInt")] + public static IDebugFunctionPosition2 GetDebugFunctionPositionForIntPtr(IntPtr locationId) + { + throw new NotImplementedException(); + } + /// /// Obtains an event callback interface that can be used to send events on any threads /// diff --git a/src/DebugEngineHost/HostMarshal.cs b/src/DebugEngineHost/HostMarshal.cs index f4940eda4..85aed6b6d 100644 --- a/src/DebugEngineHost/HostMarshal.cs +++ b/src/DebugEngineHost/HostMarshal.cs @@ -45,6 +45,19 @@ public static IDebugDocumentPosition2 GetDocumentPositionForIntPtr(IntPtr docume return (IDebugDocumentPosition2)Marshal.GetObjectForIUnknown(documentPositionId); } + /// + /// Obtains a function position interface given the specified IntPtr of the location. + /// + /// In VS, the IUnknown pointer to QI for a function position. In VS Code, + /// the identifier for the function position + /// Function position object + public static IDebugFunctionPosition2 GetDebugFunctionPositionForIntPtr(IntPtr locationId) + { + // TODO: It looks like the MIEngine currently leaks the native document position. Fix that. + + return (IDebugFunctionPosition2)Marshal.GetObjectForIUnknown(locationId); + } + /// /// Obtains an event callback interface that can be used to send events on any threads /// diff --git a/src/MICore/MICommandFactory.cs b/src/MICore/MICommandFactory.cs index b2c446d6b..1a19f462b 100644 --- a/src/MICore/MICommandFactory.cs +++ b/src/MICore/MICommandFactory.cs @@ -384,7 +384,7 @@ public virtual async Task Terminate() #region Breakpoints - public virtual async Task BreakInsert(string filename, uint line, string condition, ResultClass resultClass = ResultClass.done) + private StringBuilder BuildBreakInsert(string condition) { StringBuilder cmd = new StringBuilder("-break-insert -f "); if (condition != null) @@ -393,12 +393,26 @@ public virtual async Task BreakInsert(string filename, uint line, strin cmd.Append(condition); cmd.Append("\" "); } + return cmd; + } + + public virtual async Task BreakInsert(string filename, uint line, string condition, ResultClass resultClass = ResultClass.done) + { + StringBuilder cmd = BuildBreakInsert(condition); cmd.Append(filename); cmd.Append(":"); cmd.Append(line.ToString()); return await _debugger.CmdAsync(cmd.ToString(), resultClass); } + public virtual async Task BreakInsert(string functionName, string condition, ResultClass resultClass = ResultClass.done) + { + StringBuilder cmd = BuildBreakInsert(condition); + // TODO: Add support of break function type filename:function locations + cmd.Append(functionName); + return await _debugger.CmdAsync(cmd.ToString(), resultClass); + } + public virtual async Task BreakInfo(string bkptno) { Results bindResult = await _debugger.CmdAsync("-break-info " + bkptno, ResultClass.None); diff --git a/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs b/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs index 5c3a5bf00..8bf5ef521 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs @@ -77,8 +77,10 @@ private bool VerifyCondition(BP_CONDITION request) private bool CanBind() { - // The sample engine only supports breakpoints on a file and line number. No other types of breakpoints are supported. - if (_deleted || _bpRequestInfo.bpLocation.bpLocationType != (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FILE_LINE) + // The sample engine only supports breakpoints on a file and line number or function name. No other types of breakpoints are supported. + if (_deleted || + (_bpRequestInfo.bpLocation.bpLocationType != (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FILE_LINE + && _bpRequestInfo.bpLocation.bpLocationType != (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FUNC_OFFSET)) { _BPError = new AD7ErrorBreakpoint(this, ResourceStrings.UnsupportedBreakpoint, enum_BP_ERROR_TYPE.BPET_GENERAL_ERROR); return false; @@ -184,6 +186,7 @@ internal async Task BindAsync() if (CanBind()) { string documentName = null; + string functionName = null; TEXT_POSITION[] startPosition = new TEXT_POSITION[1]; TEXT_POSITION[] endPosition = new TEXT_POSITION[1]; string condition = null; @@ -195,22 +198,41 @@ internal async Task BindAsync() Debug.Fail("Breakpoint already bound"); return; } - IDebugDocumentPosition2 docPosition = HostMarshal.GetDocumentPositionForIntPtr(_bpRequestInfo.bpLocation.unionmember2); + if ((_bpRequestInfo.dwFields & enum_BPREQI_FIELDS.BPREQI_BPLOCATION) != 0) + { + if (_bpRequestInfo.bpLocation.bpLocationType == (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FUNC_OFFSET) + { + IDebugFunctionPosition2 functionPosition = HostMarshal.GetDebugFunctionPositionForIntPtr(_bpRequestInfo.bpLocation.unionmember2); + EngineUtils.CheckOk(functionPosition.GetFunctionName(out functionName)); + } + else if (_bpRequestInfo.bpLocation.bpLocationType == (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FILE_LINE) + { + IDebugDocumentPosition2 docPosition = HostMarshal.GetDocumentPositionForIntPtr(_bpRequestInfo.bpLocation.unionmember2); - // Get the name of the document that the breakpoint was put in - EngineUtils.CheckOk(docPosition.GetFileName(out documentName)); + // Get the name of the document that the breakpoint was put in + EngineUtils.CheckOk(docPosition.GetFileName(out documentName)); - // Get the location in the document that the breakpoint is in. - EngineUtils.CheckOk(docPosition.GetRange(startPosition, endPosition)); + // Get the location in the document that the breakpoint is in. + EngineUtils.CheckOk(docPosition.GetRange(startPosition, endPosition)); + } + } if ((_bpRequestInfo.dwFields & enum_BPREQI_FIELDS.BPREQI_CONDITION) != 0 && _bpRequestInfo.bpCondition.styleCondition == enum_BP_COND_STYLE.BP_COND_WHEN_TRUE) { condition = _bpRequestInfo.bpCondition.bstrCondition; } - } + } + PendingBreakpoint.BindResult bindResult; // Bind all breakpoints that match this source and line number. - PendingBreakpoint.BindResult bindResult = await PendingBreakpoint.Bind(documentName, startPosition[0].dwLine + 1, startPosition[0].dwColumn, _engine.DebuggedProcess, condition, this); + if (documentName != null) + { + bindResult = await PendingBreakpoint.Bind(documentName, startPosition[0].dwLine + 1, startPosition[0].dwColumn, _engine.DebuggedProcess, condition, this); + } + else + { + bindResult = await PendingBreakpoint.Bind(functionName, _engine.DebuggedProcess, condition, this); + } lock (_boundBreakpoints) { diff --git a/src/MIDebugEngine/Engine.Impl/Breakpoints.cs b/src/MIDebugEngine/Engine.Impl/Breakpoints.cs index 5e27d5c04..8cd9afd94 100644 --- a/src/MIDebugEngine/Engine.Impl/Breakpoints.cs +++ b/src/MIDebugEngine/Engine.Impl/Breakpoints.cs @@ -84,10 +84,19 @@ private static MIBreakpointState StringToBreakpointState(string addr) } } + internal static async Task Bind(string functionName, DebuggedProcess process, string condition, AD7PendingBreakpoint pbreak) + { + return EvalBindResult(await process.MICommandFactory.BreakInsert(functionName, condition, ResultClass.None), pbreak); + } + internal static async Task Bind(string documentName, uint line, uint column, DebuggedProcess process, string condition, AD7PendingBreakpoint pbreak) { string basename = System.IO.Path.GetFileName(documentName); // get basename from Windows path - Results bindResult = await process.MICommandFactory.BreakInsert(process.EscapePath(basename), line, condition, ResultClass.None); + return EvalBindResult(await process.MICommandFactory.BreakInsert(process.EscapePath(basename), line, condition, ResultClass.None), pbreak); + } + + private static BindResult EvalBindResult(Results bindResult, AD7PendingBreakpoint pbreak) + { string errormsg = "Unknown error"; if (bindResult.ResultClass == ResultClass.error) {