From c2ed23eb1cb5575659e756ba9af9f42d52f71254 Mon Sep 17 00:00:00 2001 From: Calvin Gagliano Date: Tue, 18 Jan 2022 16:13:52 -0800 Subject: [PATCH 1/7] Data breakpoints in OpenDebugAD7 (#1257) --- src/DebugEngineHost.VSCode/HostMarshal.cs | 5 +- .../VSCode/EngineConfiguration.cs | 7 + .../AD7.Impl/AD7PendingBreakpoint.cs | 16 +- src/MIDebugEngine/AD7.Impl/AD7Property.cs | 28 ++- src/OpenDebugAD7/AD7DebugSession.cs | 215 +++++++++++++++++- .../AD7Impl/AD7BreakPointRequest.cs | 25 +- src/OpenDebugAD7/AD7Resources.Designer.cs | 45 ++++ src/OpenDebugAD7/AD7Resources.resx | 19 ++ src/OpenDebugAD7/VariableManager.cs | 31 +++ src/OpenDebugAD7/cppdbg.ad7Engine.json | 1 + 10 files changed, 381 insertions(+), 11 deletions(-) diff --git a/src/DebugEngineHost.VSCode/HostMarshal.cs b/src/DebugEngineHost.VSCode/HostMarshal.cs index f6a243124..41f780164 100644 --- a/src/DebugEngineHost.VSCode/HostMarshal.cs +++ b/src/DebugEngineHost.VSCode/HostMarshal.cs @@ -4,6 +4,7 @@ using Microsoft.DebugEngineHost.VSCode; using Microsoft.VisualStudio.Debugger.Interop; using System; +using System.Runtime.InteropServices; namespace Microsoft.DebugEngineHost { @@ -110,7 +111,7 @@ public static IDebugFunctionPosition2 GetDebugFunctionPositionForIntPtr(IntPtr f /// public static string GetDataBreakpointStringForIntPtr(IntPtr stringId) { - throw new NotImplementedException(); + return Marshal.PtrToStringBSTR(stringId); } /// @@ -120,7 +121,7 @@ public static string GetDataBreakpointStringForIntPtr(IntPtr stringId) /// IntPtr to a BSTR which can be returned to VS. public static IntPtr GetIntPtrForDataBreakpointAddress(string address) { - throw new NotImplementedException(); + return Marshal.StringToBSTR(address); } /// diff --git a/src/DebugEngineHost.VSCode/VSCode/EngineConfiguration.cs b/src/DebugEngineHost.VSCode/VSCode/EngineConfiguration.cs index 938ed4a49..265d4246c 100644 --- a/src/DebugEngineHost.VSCode/VSCode/EngineConfiguration.cs +++ b/src/DebugEngineHost.VSCode/VSCode/EngineConfiguration.cs @@ -33,6 +33,7 @@ public sealed class EngineConfiguration private bool _conditionalBP; private bool _functionBP; private bool _clipboardContext; + private bool _dataBP; // NOTE: CoreCLR doesn't support providing a code base when loading assemblies. So all debug engines // must be placed in the directory of OpenDebugAD7.exe @@ -74,6 +75,12 @@ public bool ClipboardContext set { SetProperty(out _clipboardContext, value); } } + public bool DataBP + { + get { return _dataBP; } + set { SetProperty(out _dataBP, value); } + } + /// /// Provides the directory of the debug adapter. This is the directory where diff --git a/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs b/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs index e284d76b9..3dab46a82 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7PendingBreakpoint.cs @@ -321,6 +321,11 @@ private int BindWithTimeout() this.SetError(new AD7ErrorBreakpoint(this, ResourceStrings.LongBind, enum_BP_ERROR_TYPE.BPET_SEV_LOW | enum_BP_ERROR_TYPE.BPET_TYPE_WARNING), true); return Constants.S_FALSE; } + else if (this._BPError != null) + { + // Ran into some sort of error + return Constants.E_FAIL; + } else { if ((enum_BP_LOCATION_TYPE)_bpRequestInfo.bpLocation.bpLocationType == enum_BP_LOCATION_TYPE.BPLT_DATA_STRING) @@ -364,7 +369,16 @@ internal async Task BindAsync() } else { - bindResult = await PendingBreakpoint.Bind(_address, _size, _engine.DebuggedProcess, _condition, this); + try + { + bindResult = await PendingBreakpoint.Bind(_address, _size, _engine.DebuggedProcess, _condition, this); + } + catch (ArgumentException ex) + { + // There was an error binding at the address with the specified size. This could happen if the size is greater + // than the max size a data bp can watch. + bindResult = new PendingBreakpoint.BindResult(ex.Message); + } } lock (_boundBreakpoints) diff --git a/src/MIDebugEngine/AD7.Impl/AD7Property.cs b/src/MIDebugEngine/AD7.Impl/AD7Property.cs index 6387aed3c..596068765 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Property.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Property.cs @@ -141,10 +141,32 @@ public int EnumChildren(enum_DEBUGPROP_INFO_FLAGS dwFields, uint dwRadix, ref Gu { _engine.DebuggedProcess.Natvis.WaitDialog.ShowWaitDialog(_variableInformation.Name); var children = _engine.DebuggedProcess.Natvis.Expand(_variableInformation); - DEBUG_PROPERTY_INFO[] properties = new DEBUG_PROPERTY_INFO[children.Length]; - for (int i = 0; i < children.Length; i++) + + // Count number of children that fit filter (results saved in "fitsFilter") + int propertyCount = children.Length; + bool[] fitsFilter = null; + if (!string.IsNullOrEmpty(pszNameFilter)) + { + fitsFilter = new bool[children.Length]; + for (int i = 0; i < children.Length; i++) + { + fitsFilter[i] = string.Equals(children[i].Name, pszNameFilter, StringComparison.Ordinal); + if (!fitsFilter[i]) + { + propertyCount--; + } + } + } + + // Create property array + DEBUG_PROPERTY_INFO[] properties = new DEBUG_PROPERTY_INFO[propertyCount]; + for (int i = 0, j = 0; i < children.Length; i++) { - properties[i] = (new AD7Property(_engine, children[i])).ConstructDebugPropertyInfo(dwFields); + if (fitsFilter == null || fitsFilter[i]) + { + properties[j] = (new AD7Property(_engine, children[i])).ConstructDebugPropertyInfo(dwFields); + ++j; // increment j if we fit filter, this allows us to traverse "properties" array properly. + } } ppEnum = new AD7PropertyEnum(properties); return Constants.S_OK; diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index 0c145ad68..b935c2b76 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -51,6 +51,8 @@ internal sealed class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDe private Dictionary m_functionBreakpoints; private Dictionary m_instructionBreakpoints; + private Dictionary m_dataBreakpoints; + private List m_exceptionBreakpoints; private readonly HandleCollection m_frameHandles; @@ -122,6 +124,7 @@ public AD7DebugSession(Stream debugAdapterStdIn, Stream debugAdapterStdOut, List m_breakpoints = new Dictionary>(); m_functionBreakpoints = new Dictionary(); m_instructionBreakpoints = new Dictionary(); + m_dataBreakpoints = new Dictionary(); m_exceptionBreakpoints = new List(); m_variableManager = new VariableManager(); } @@ -649,6 +652,7 @@ private VariablesResponse VariablesFromFrame(VariableScope vref, uint radix) while (varEnum.Next(1, props, out nProps) == HRConstants.S_OK) { response.Variables.Add(m_variableManager.CreateVariable(props[0].pProperty, GetDefaultPropertyInfoFlags())); + m_variableManager.AddFrameVariable(frame, props[0]); } } @@ -905,6 +909,7 @@ protected override void HandleInitializeRequestAsync(IRequestResponder responder) + { + if (responder.Arguments.Name == null) + { + responder.SetError(new ProtocolException("DataBreakpointInfo failed: Missing 'Name'.")); + return; + } + + DataBreakpointInfoResponse response = new DataBreakpointInfoResponse(); + + try + { + string name = responder.Arguments.Name; + IDebugProperty2 property = null; + string errorMessage = null; + ErrorBuilder eb = new ErrorBuilder(() => AD7Resources.Error_DataBreakpointInfoFail); + int hr = HRConstants.S_OK; + + // Did our request come with a parent object? + if (responder.Arguments.VariablesReference.HasValue) + { + int variableReference = responder.Arguments.VariablesReference.Value; + if (!m_variableManager.TryGet(variableReference, out object variableObj)) + { + responder.SetError(new ProtocolException("DataBreakpointInfo failed: Invalid 'VariableReference'.")); + return; + } + + if (variableObj is VariableScope varScope) + { + // We have a scope object. We can grab a frame for evaluation from this + IDebugStackFrame2 frame = varScope.StackFrame; + m_variableManager.TryGetProperty((frame, name), out property); + } + else if (variableObj is VariableEvaluationData varEvalData) + { + // We have a variable parent object. + IDebugProperty2 parentProperty = varEvalData.DebugProperty; + m_variableManager.TryGetProperty((variableReference, name), out property); + } + } + else + { + // We don't have a parent object. Default to using top stack frame + if (m_frameHandles == null || !m_frameHandles.TryGetFirst(out IDebugStackFrame2 frame)) + { + response.Description = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_DataBreakpointInfoFail, AD7Resources.Error_NoParentObject); + } + else + { + m_variableManager.TryGetProperty((frame, name), out property); + } + } + + // If we've found a valid child property to set the data breakpoint on, get the address/size and return the DataId. + if (property != null && property is IDebugProperty160 property160) + { + hr = property160.GetDataBreakpointInfo160(out string address, out uint size, out string displayName, out errorMessage); + eb.CheckHR(hr); + if (!string.IsNullOrEmpty(errorMessage)) + { + response.Description = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_DataBreakpointInfoFail, errorMessage); + } + else + { + // If we succeeded, return response that we can set a data bp. + string sSize = size.ToString(CultureInfo.InvariantCulture); + response.DataId = $"{address},{sSize}"; + response.Description = string.Format(CultureInfo.CurrentCulture, AD7Resources.DataBreakpointDisplayString, displayName, sSize); + response.AccessTypes = new List() { DataBreakpointAccessType.Write }; + } + } + else if (response.Description == null) + { + response.Description = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_DataBreakpointInfoFail, AD7Resources.Error_ChildPropertyNotFound); + } + } + catch (Exception ex) + { + if (ex is AD7Exception ad7ex) + response.Description = ad7ex.Message; + else + response.Description = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_DataBreakpointInfoFail, ""); + } + finally + { + responder.SetResponse(response); + } + } + + protected override void HandleSetDataBreakpointsRequestAsync(IRequestResponder responder) + { + if (responder.Arguments.Breakpoints == null) + { + responder.SetError(new ProtocolException("SetDataBreakpointRequest failed: Missing 'breakpoints'.")); + return; + } + + List breakpoints = responder.Arguments.Breakpoints; + SetDataBreakpointsResponse response = new SetDataBreakpointsResponse(); + Dictionary newBreakpoints = new Dictionary(); + ErrorBuilder eb = new ErrorBuilder(() => AD7Resources.Error_DataBreakpointInfoFail); + + try + { + foreach (KeyValuePair b in m_dataBreakpoints) + { + if (breakpoints.Find((p) => p.DataId == b.Key) != null) + { + newBreakpoints[b.Key] = b.Value; // breakpoint still in new list + } + else + { + b.Value.Delete(); // not in new list so delete it + } + } + + foreach (DataBreakpoint b in breakpoints) + { + if (m_dataBreakpoints.ContainsKey(b.DataId)) + { // already created + IDebugBreakpointRequest2 breakpointRequest; + if (m_dataBreakpoints[b.DataId].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, b.Condition)) + { + // Condition has been modified. Delete breakpoint so it will be recreated with the updated condition. + var toRemove = m_dataBreakpoints[b.DataId]; + toRemove.Delete(); + m_dataBreakpoints.Remove(b.DataId); + } + else + { + if (ad7BPRequest.BindResult != null) + { + response.Breakpoints.Add(ad7BPRequest.BindResult); + } + else + { + response.Breakpoints.Add(new Breakpoint() + { + Id = (int)ad7BPRequest.Id, + Verified = false, + Line = 0 + }); + + } + continue; + } + } + } + + // Bind the new data bp + if (!m_dataBreakpoints.ContainsKey(b.DataId)) + { + int hr = HRConstants.S_OK; + + int lastCommaIdx = b.DataId.LastIndexOf(','); + if (lastCommaIdx == -1) + { + eb.ThrowHR(HRConstants.E_FAIL); + } + + // format is "{dataId},{size}" where dataId = "{address},{displayName}" + string strSize = b.DataId.Substring(lastCommaIdx + 1); + string address = b.DataId.Substring(0, lastCommaIdx); + uint size = uint.Parse(strSize, CultureInfo.InvariantCulture); + + AD7BreakPointRequest pBPRequest = new AD7BreakPointRequest(address, size); + hr = m_engine.CreatePendingBreakpoint(pBPRequest, out IDebugPendingBreakpoint2 pendingBp); + eb.CheckHR(hr); + + hr = pendingBp.Bind(); + if (hr == HRConstants.S_OK) + { + newBreakpoints[b.DataId] = pendingBp; + } + response.Breakpoints.Add(pBPRequest.BindResult); + } + } + responder.SetResponse(response); + } + catch (Exception ex) + { + responder.SetError(new ProtocolException(ex.Message)); + } + finally + { + m_dataBreakpoints = newBreakpoints; + } + } + + protected override void HandleSetExceptionBreakpointsRequestAsync(IRequestResponder responder) { HashSet activeExceptionCategories = new HashSet(); @@ -3310,7 +3511,7 @@ public void HandleIDebugBreakpointErrorEvent2(IDebugEngine2 pEngine, IDebugProce }; } } - else + else if (ad7BPRequest.FunctionPosition != null) { bp = new Breakpoint() { @@ -3324,6 +3525,18 @@ public void HandleIDebugBreakpointErrorEvent2(IDebugEngine2 pEngine, IDebugProce string outputMsg = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_FunctionBreakpoint, ad7BPRequest.FunctionPosition.Name, errorMsg); m_logger.WriteLine(LoggingCategory.DebuggerError, outputMsg); } + else // data bp + { + string outputMsg = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_InvalidDataBreakpoint, errorMsg); + bp = new Breakpoint() + { + Verified = false, + Id = (int)ad7BPRequest.Id, + Line = 0, + Message = outputMsg + }; + m_logger.WriteLine(LoggingCategory.DebuggerError, outputMsg); + } ad7BPRequest.BindResult = bp; Protocol.SendEvent(new BreakpointEvent(BreakpointEvent.ReasonValue.Changed, bp)); diff --git a/src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs b/src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs index 63f70afa0..2843beb40 100644 --- a/src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs +++ b/src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs @@ -25,13 +25,16 @@ static public uint GetNextBreakpointId() public IDebugMemoryContext2 MemoryContext { get; private set; } + public string DataAddress { get; private set; } + public uint DataSize { 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; } + public uint Id { get; } = GetNextBreakpointId(); // Bind result from IDebugBreakpointErrorEvent2 or IDebugBreakpointBoundEvent2 public Breakpoint BindResult { get; set; } @@ -40,7 +43,6 @@ public AD7BreakPointRequest(SessionConfiguration config, string path, int line, { DocumentPosition = new AD7DocumentPosition(config, path, line); Condition = condition; - Id = GetNextBreakpointId(); } public AD7BreakPointRequest(string functionName) @@ -48,6 +50,13 @@ public AD7BreakPointRequest(string functionName) FunctionPosition = new AD7FunctionPosition(functionName); } + // Data breakpoint constructor + public AD7BreakPointRequest(string address, uint size) + { + DataAddress = address; + DataSize = size; + } + public AD7BreakPointRequest(IDebugMemoryContext2 memoryContext) { MemoryContext = memoryContext; @@ -67,7 +76,10 @@ public int GetLocationType(enum_BP_LOCATION_TYPE[] pBPLocationType) { pBPLocationType[0] = enum_BP_LOCATION_TYPE.BPLT_CODE_CONTEXT; } - + else if (DataAddress != null) + { + pBPLocationType[0] = enum_BP_LOCATION_TYPE.BPLT_DATA_STRING; + } return 0; } @@ -92,7 +104,12 @@ public int GetRequestInfo(enum_BPREQI_FIELDS dwFields, BP_REQUEST_INFO[] pBPRequ pBPRequestInfo[0].bpLocation.bpLocationType = (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_CONTEXT; MemoryContextIntPtr = HostMarshal.RegisterCodeContext(MemoryContext as IDebugCodeContext2); pBPRequestInfo[0].bpLocation.unionmember1 = MemoryContextIntPtr; - + } + else if (DataAddress != null) + { + pBPRequestInfo[0].bpLocation.bpLocationType = (uint)enum_BP_LOCATION_TYPE.BPLT_DATA_STRING; + pBPRequestInfo[0].bpLocation.unionmember3 = HostMarshal.GetIntPtrForDataBreakpointAddress(DataAddress); + pBPRequestInfo[0].bpLocation.unionmember4 = (IntPtr)DataSize; } } if ((dwFields & enum_BPREQI_FIELDS.BPREQI_CONDITION) != 0 && !string.IsNullOrWhiteSpace(Condition)) diff --git a/src/OpenDebugAD7/AD7Resources.Designer.cs b/src/OpenDebugAD7/AD7Resources.Designer.cs index 6ef7590b3..516b91680 100644 --- a/src/OpenDebugAD7/AD7Resources.Designer.cs +++ b/src/OpenDebugAD7/AD7Resources.Designer.cs @@ -60,6 +60,15 @@ internal AD7Resources() { } } + /// + /// Looks up a localized string similar to When '{0}' changes ({1} bytes). + /// + internal static string DataBreakpointDisplayString { + get { + return ResourceManager.GetString("DataBreakpointDisplayString", resourceCulture); + } + } + /// /// Looks up a localized string similar to Execute debugger commands using "-exec <command>", for example "-exec info registers" will list registers in use (when GDB is the debugger). /// @@ -78,6 +87,15 @@ internal static string DebuggerDisconnectMessage { } } + /// + /// Looks up a localized string similar to Unable to find child property.. + /// + internal static string Error_ChildPropertyNotFound { + get { + return ResourceManager.GetString("Error_ChildPropertyNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Condition "{0}" : {1}. /// @@ -107,6 +125,15 @@ internal static string Error_CorruptingException { } } + /// + /// Looks up a localized string similar to Unable to get info for data breakpoint. {0}. + /// + internal static string Error_DataBreakpointInfoFail { + get { + return ResourceManager.GetString("Error_DataBreakpointInfoFail", resourceCulture); + } + } + /// /// Looks up a localized string similar to Exception occurred: '{0}'. /// @@ -215,6 +242,15 @@ internal static string Error_Invalid_Exception_Condition { } } + /// + /// Looks up a localized string similar to Unable to set data breakpoint: {0}. + /// + internal static string Error_InvalidDataBreakpoint { + get { + return ResourceManager.GetString("Error_InvalidDataBreakpoint", resourceCulture); + } + } + /// /// Looks up a localized string similar to DNX runtime process exited unexpectedly with error code {0}.. /// @@ -244,6 +280,15 @@ internal static string Error_MissingOutParam { } } + /// + /// Looks up a localized string similar to Unable to get parent object: No scope or variable found.. + /// + internal static string Error_NoParentObject { + get { + return ResourceManager.GetString("Error_NoParentObject", resourceCulture); + } + } + /// /// Looks up a localized string similar to Set next statement is not supported by the current debugger.. /// diff --git a/src/OpenDebugAD7/AD7Resources.resx b/src/OpenDebugAD7/AD7Resources.resx index f2f020d8a..443ed33ee 100644 --- a/src/OpenDebugAD7/AD7Resources.resx +++ b/src/OpenDebugAD7/AD7Resources.resx @@ -312,4 +312,23 @@ ERROR: '{0}' is an invalid exception condition. See https://aka.ms/VSCode-Cpp-ExceptionSettings for more information. {0} is the exception condition + + Unable to get info for data breakpoint. {0} + {0} is the reason (could be exception condition) + + + Unable to get parent object: No scope or variable found. + {0} is a number, {1} is the property name + + + When '{0}' changes ({1} bytes) + {0} is the variable name, {1} is a number (size of the variable) + + + Unable to find child property. + + + Unable to set data breakpoint: {0} + {0} is the reason + \ No newline at end of file diff --git a/src/OpenDebugAD7/VariableManager.cs b/src/OpenDebugAD7/VariableManager.cs index def25f74d..8f7d37e53 100644 --- a/src/OpenDebugAD7/VariableManager.cs +++ b/src/OpenDebugAD7/VariableManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using Microsoft.DebugEngineHost.VSCode; using Microsoft.VisualStudio.Debugger.Interop; using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; @@ -31,14 +32,24 @@ internal class VariableManager // NOTE: The value being stored can be a VariableScope or a VariableEvaluationData private readonly HandleCollection m_variableHandles; + // NOTE: (VariableReference, ChildName) -> IDebugProperty2 + private readonly Dictionary, IDebugProperty2> m_variablesChildren; + + // NOTE: (Frame, Name) -> IDebugProperty2 + private readonly Dictionary, IDebugProperty2> m_framesVariables; + internal VariableManager() { m_variableHandles = new HandleCollection(); + m_variablesChildren = new Dictionary, IDebugProperty2>(); + m_framesVariables = new Dictionary, IDebugProperty2>(); } internal void Reset() { m_variableHandles.Reset(); + m_variablesChildren.Clear(); + m_framesVariables.Clear(); } internal Boolean IsEmpty() @@ -46,6 +57,16 @@ internal Boolean IsEmpty() return m_variableHandles.IsEmpty; } + internal bool TryGetProperty((int, string) key, out IDebugProperty2 prop) + { + return m_variablesChildren.TryGetValue(Tuple.Create(key.Item1, key.Item2), out prop); + } + + internal bool TryGetProperty((IDebugStackFrame2, string) key, out IDebugProperty2 prop) + { + return m_framesVariables.TryGetValue(Tuple.Create(key.Item1, key.Item2), out prop); + } + internal bool TryGet(int handle, out object value) { return m_variableHandles.TryGet(handle, out value); @@ -56,6 +77,16 @@ internal int Create(VariableScope scope) return m_variableHandles.Create(scope); } + public void AddChildVariable(int parentHandle, DEBUG_PROPERTY_INFO propInfo) + { + m_variablesChildren.Add(Tuple.Create(parentHandle, propInfo.bstrName), propInfo.pProperty); + } + + public void AddFrameVariable(IDebugStackFrame2 frame, DEBUG_PROPERTY_INFO propInfo) + { + m_framesVariables.Add(Tuple.Create(frame, propInfo.bstrName), propInfo.pProperty); + } + internal Variable CreateVariable(IDebugProperty2 property, enum_DEBUGPROP_INFO_FLAGS propertyInfoFlags) { var propertyInfo = new DEBUG_PROPERTY_INFO[1]; diff --git a/src/OpenDebugAD7/cppdbg.ad7Engine.json b/src/OpenDebugAD7/cppdbg.ad7Engine.json index 8fcf48ff2..51b8269bc 100644 --- a/src/OpenDebugAD7/cppdbg.ad7Engine.json +++ b/src/OpenDebugAD7/cppdbg.ad7Engine.json @@ -3,6 +3,7 @@ "engineClassName": "Microsoft.MIDebugEngine.AD7Engine", "conditionalBP": true, "functionBP": true, + "dataBP": true, "clipboardContext": true, "exceptionSettings": { "categories": [ From bb373ff928150b771e2499a46586dfe41c4372e8 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Thu, 20 Jan 2022 11:24:18 -0800 Subject: [PATCH 2/7] Fix Windows Path check in SrcFileMap (#1259) If a user had \\ at the start of their compilerSrc string, we would incorrectly detect that the string does not contain any windows paths. This PR fixes the IndexOf check to see if it returned -1. --- src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index 8d9a766f1..2f38f0e5a 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -2248,7 +2248,7 @@ public bool MapCurrentSrcToCompileTimeSrc(string currentSrc, out string compiler continue; // match didn't end at a directory separator, not actually a match } compilerSrc = Path.Combine(e.CompileTimePath, file); // map to the compiled location - if (compilerSrc.IndexOf('\\') > 0) + if (compilerSrc.IndexOf('\\') != -1) { compilerSrc = PlatformUtilities.WindowsPathToUnixPath(compilerSrc); // use Unix notation for the compiled path } From e8851dc2ab240980d09f54f43a7486658c9002eb Mon Sep 17 00:00:00 2001 From: xisui-MSFT <44103947+xisui-MSFT@users.noreply.github.com> Date: Thu, 27 Jan 2022 13:37:39 -0800 Subject: [PATCH 3/7] Added extended-remote support (#1260) * Replace target remote with extended-remote * Added option useExtendedRemote * Resolving comments --- src/IOSDebugLauncher/Launcher.cs | 2 +- src/MICore/JsonLaunchOptions.cs | 10 ++++++++ src/MICore/LaunchOptions.cs | 23 +++++++++++++++++-- src/MICore/LaunchOptions.xsd | 5 ++++ .../LaunchOptions.xsd.types.designer.cs | 6 ++++- .../Engine.Impl/DebuggedProcess.cs | 10 +++++--- src/MIDebugPackage/OpenFolderSchema.json | 4 ++++ src/OpenDebugAD7/AD7DebugSession.cs | 5 ++-- 8 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/IOSDebugLauncher/Launcher.cs b/src/IOSDebugLauncher/Launcher.cs index 084a24cd3..9c5461a4c 100644 --- a/src/IOSDebugLauncher/Launcher.cs +++ b/src/IOSDebugLauncher/Launcher.cs @@ -92,7 +92,7 @@ void IPlatformAppLauncher.SetupForDebugging(out LaunchOptions debuggerLaunchOpti return _client.ServerCertificateValidationCallback(sender, (X509Certificate)certificate, (X509Chain)chain, sslPolicyErrors); }; } - + debuggerLaunchOptions.TargetArchitecture = _launchOptions.TargetArchitecture; debuggerLaunchOptions.AdditionalSOLibSearchPath = _launchOptions.AdditionalSOLibSearchPath; debuggerLaunchOptions.DebuggerMIMode = MIMode.Lldb; diff --git a/src/MICore/JsonLaunchOptions.cs b/src/MICore/JsonLaunchOptions.cs index 2b63b144c..5dc63d0bb 100644 --- a/src/MICore/JsonLaunchOptions.cs +++ b/src/MICore/JsonLaunchOptions.cs @@ -72,6 +72,12 @@ public abstract partial class BaseOptions [JsonProperty("miDebuggerServerAddress", DefaultValueHandling = DefaultValueHandling.Ignore)] public string MiDebuggerServerAddress { get; set; } + /// + /// If true, use gdb extended-remote mode to connect to gdbserver. + /// + [JsonProperty("useExtendedRemote", DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? UseExtendedRemote { get; set; } + /// /// Optional source file mappings passed to the debug engine. Example: '{ "/original/source/path":"/current/source/path" }' /// @@ -137,6 +143,7 @@ public AttachOptions( string miDebuggerPath = null, string miDebuggerArgs = null, string miDebuggerServerAddress = null, + bool? useExtendedRemote = null, HardwareBreakpointInfo hardwareBreakpointInfo = null, Dictionary sourceFileMap = null, PipeTransport pipeTransport = null, @@ -152,6 +159,7 @@ public AttachOptions( this.MiDebuggerPath = miDebuggerPath; this.MiDebuggerArgs = miDebuggerArgs; this.MiDebuggerServerAddress = miDebuggerServerAddress; + this.UseExtendedRemote = useExtendedRemote; this.ProcessId = processId; this.HardwareBreakpointInfo = hardwareBreakpointInfo; this.SourceFileMap = sourceFileMap; @@ -390,6 +398,7 @@ public LaunchOptions( string miDebuggerPath = null, string miDebuggerArgs = null, string miDebuggerServerAddress = null, + bool? useExtendedRemote = null, bool? stopAtEntry = null, string debugServerPath = null, string debugServerArgs = null, @@ -421,6 +430,7 @@ public LaunchOptions( this.MiDebuggerPath = miDebuggerPath; this.MiDebuggerArgs = miDebuggerArgs; this.MiDebuggerServerAddress = miDebuggerServerAddress; + this.UseExtendedRemote = useExtendedRemote; this.StopAtEntry = stopAtEntry; this.DebugServerPath = debugServerPath; this.DebugServerArgs = debugServerArgs; diff --git a/src/MICore/LaunchOptions.cs b/src/MICore/LaunchOptions.cs index 2315055b6..ad4740091 100644 --- a/src/MICore/LaunchOptions.cs +++ b/src/MICore/LaunchOptions.cs @@ -443,6 +443,18 @@ public LocalLaunchOptions(string MIDebuggerPath, string MIDebuggerServerAddress, this.MIDebuggerArgs = MIDebuggerArgs; } + public LocalLaunchOptions(string MIDebuggerPath, string MIDebuggerServerAddress, bool UseExtendedRemote) : + this(MIDebuggerPath, MIDebuggerServerAddress) + { + this.UseExtendedRemote = UseExtendedRemote; + } + + public LocalLaunchOptions(string MIDebuggerPath, string MIDebuggerServerAddress, string MIDebuggerArgs, bool UseExtendedRemote) : + this(MIDebuggerPath, MIDebuggerServerAddress, MIDebuggerArgs) + { + this.UseExtendedRemote = UseExtendedRemote; + } + private void InitializeServerOptions(Json.LaunchOptions.LaunchOptions launchOptions) { if (!String.IsNullOrWhiteSpace(launchOptions.DebugServerPath)) @@ -533,7 +545,8 @@ static internal LocalLaunchOptions CreateFromJson(JObject parsedOptions) LocalLaunchOptions localLaunchOptions = new LocalLaunchOptions(RequireAttribute(miDebuggerPath, nameof(miDebuggerPath)), launchOptions.MiDebuggerServerAddress, - launchOptions.MiDebuggerArgs + launchOptions.MiDebuggerArgs, + launchOptions.UseExtendedRemote.GetValueOrDefault(false) ); // Load up common options @@ -561,7 +574,8 @@ static internal LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOp var options = new LocalLaunchOptions( RequireAttribute(miDebuggerPath, "MIDebuggerPath"), source.MIDebuggerServerAddress, - source.MIDebuggerArgs); + source.MIDebuggerArgs, + source.UseExtendedRemote); options.InitializeCommonOptions(source); options.InitializeServerOptions(source); options._useExternalConsole = source.ExternalConsole; @@ -666,6 +680,11 @@ private static string EnsureDebuggerPath(string miDebuggerPath, string debuggerB /// public string MIDebuggerServerAddress { get; private set; } + /// + /// [Optional] If true, use gdb extended-remote mode to connect to gdbserver. + /// + public bool UseExtendedRemote { get; private set; } + /// /// [Optional] MI Debugger Server exe, if non-null then the MIEngine will start the debug server before starting the debugger /// diff --git a/src/MICore/LaunchOptions.xsd b/src/MICore/LaunchOptions.xsd index b5ef5db1e..2e3f63b9b 100644 --- a/src/MICore/LaunchOptions.xsd +++ b/src/MICore/LaunchOptions.xsd @@ -230,6 +230,11 @@ Network address of the MI Debugger Server to connect to (example: localhost:1234). + + + If true, use gdb extended-remote mode to connect to gdbserver. + + Full path to the server executable. If non-null then the MIEngine will start the server. diff --git a/src/MICore/LaunchOptions.xsd.types.designer.cs b/src/MICore/LaunchOptions.xsd.types.designer.cs index 5551c7e82..f8b2766a2 100644 --- a/src/MICore/LaunchOptions.xsd.types.designer.cs +++ b/src/MICore/LaunchOptions.xsd.types.designer.cs @@ -575,7 +575,11 @@ public partial class LocalLaunchOptions : BaseLaunchOptions { /// [System.Xml.Serialization.XmlAttributeAttribute()] public string MIDebuggerServerAddress; - + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public bool UseExtendedRemote; + /// [System.Xml.Serialization.XmlAttributeAttribute()] public string DebugServer; diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index 2f38f0e5a..5375744d9 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -726,11 +726,14 @@ private async Task> GetInitializeCommands() // check for remote string destination = localLaunchOptions?.MIDebuggerServerAddress; + bool useExtendedRemote = localLaunchOptions?.UseExtendedRemote ?? false; if (!string.IsNullOrWhiteSpace(destination)) { - commands.Add(new LaunchCommand("-target-select remote " + destination, string.Format(CultureInfo.CurrentCulture, ResourceStrings.ConnectingMessage, destination))); + string remoteMode = useExtendedRemote ? "extended-remote" : "remote"; + commands.Add(new LaunchCommand($"-target-select {remoteMode} {destination}", string.Format(CultureInfo.CurrentCulture, ResourceStrings.ConnectingMessage, destination))); } - else // gdbserver is already attached when using LocalLaunchOptions + // Allow attach after connection only in extended-remote mode + if (useExtendedRemote || (!useExtendedRemote && string.IsNullOrWhiteSpace(destination))) { Action failureHandler = (string miError) => { @@ -831,7 +834,8 @@ private async Task> GetInitializeCommands() string destination = localLaunchOptions.MIDebuggerServerAddress; if (!string.IsNullOrWhiteSpace(destination)) { - commands.Add(new LaunchCommand("-target-select remote " + destination, string.Format(CultureInfo.CurrentCulture, ResourceStrings.ConnectingMessage, destination))); + string remoteMode = localLaunchOptions.UseExtendedRemote ? "extended-remote" : "remote"; + commands.Add(new LaunchCommand($"-target-select {remoteMode} {destination}", string.Format(CultureInfo.CurrentCulture, ResourceStrings.ConnectingMessage, destination))); if (localLaunchOptions.RequireHardwareBreakpoints && localLaunchOptions.HardwareBreakpointLimit > 0) { commands.Add(new LaunchCommand(string.Format(CultureInfo.InvariantCulture, "-interpreter-exec console \"set remote hardware-breakpoint-limit {0}\"", localLaunchOptions.HardwareBreakpointLimit.ToString(CultureInfo.InvariantCulture)))); } diff --git a/src/MIDebugPackage/OpenFolderSchema.json b/src/MIDebugPackage/OpenFolderSchema.json index 4d69d7b0c..c7c60efec 100644 --- a/src/MIDebugPackage/OpenFolderSchema.json +++ b/src/MIDebugPackage/OpenFolderSchema.json @@ -113,6 +113,10 @@ "type": "string", "description": "Network address of the MI-enabled debugger server to connect to. \nExample: localhost:1234." }, + "useExtendedRemote": { + "type": "boolean", + "description": "If true, use gdb extended-remote mode to connect to gdbserver." + }, "setupCommands": { "type": "array", "description": "One or more GDB/LLDB commands to execute in order to setup the underlying debugger. \nExample: \"setupCommands\": [ { \"text\": \"-enable-pretty-printing\", \"description\": \"Enable GDB pretty printing\", \"ignoreFailures\": true }].", diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index b935c2b76..bb46637d6 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -1129,6 +1129,7 @@ protected override void HandleAttachRequestAsync(IRequestResponder Date: Fri, 28 Jan 2022 09:48:22 -0800 Subject: [PATCH 4/7] LEGO: check in for main to temporary branch. (#1261) --- loc/lcl/CHT/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/loc/lcl/CHT/OpenFolderSchema.json.lcl b/loc/lcl/CHT/OpenFolderSchema.json.lcl index d05fe03da..0ce4d648b 100644 --- a/loc/lcl/CHT/OpenFolderSchema.json.lcl +++ b/loc/lcl/CHT/OpenFolderSchema.json.lcl @@ -865,6 +865,24 @@ + + + + + + + + + + + + + + + + + + From adf2039236695df719fd35f6d342a1e22e57c2ba Mon Sep 17 00:00:00 2001 From: csigs Date: Fri, 28 Jan 2022 09:48:54 -0800 Subject: [PATCH 5/7] LEGO: check in for main to temporary branch. (#1263) --- loc/lcl/JPN/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/loc/lcl/JPN/OpenFolderSchema.json.lcl b/loc/lcl/JPN/OpenFolderSchema.json.lcl index 319c23253..68e3dd5f7 100644 --- a/loc/lcl/JPN/OpenFolderSchema.json.lcl +++ b/loc/lcl/JPN/OpenFolderSchema.json.lcl @@ -865,6 +865,24 @@ + + + + + + + + + + + + + + + + + + From 2f7887f3e6b536142dcecbd2e4d1e86d4c879b79 Mon Sep 17 00:00:00 2001 From: csigs Date: Fri, 28 Jan 2022 13:23:16 -0800 Subject: [PATCH 6/7] LEGO: check in for main to temporary branch. (#1262) Co-authored-by: Andrew Wang --- loc/lcl/CHS/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ loc/lcl/DEU/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ loc/lcl/RUS/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/loc/lcl/CHS/OpenFolderSchema.json.lcl b/loc/lcl/CHS/OpenFolderSchema.json.lcl index 140e54f7f..a17ab9b24 100644 --- a/loc/lcl/CHS/OpenFolderSchema.json.lcl +++ b/loc/lcl/CHS/OpenFolderSchema.json.lcl @@ -865,6 +865,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/loc/lcl/DEU/OpenFolderSchema.json.lcl b/loc/lcl/DEU/OpenFolderSchema.json.lcl index d00b8ffe8..b8941d421 100644 --- a/loc/lcl/DEU/OpenFolderSchema.json.lcl +++ b/loc/lcl/DEU/OpenFolderSchema.json.lcl @@ -865,6 +865,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/loc/lcl/RUS/OpenFolderSchema.json.lcl b/loc/lcl/RUS/OpenFolderSchema.json.lcl index 23799a41e..03db31052 100644 --- a/loc/lcl/RUS/OpenFolderSchema.json.lcl +++ b/loc/lcl/RUS/OpenFolderSchema.json.lcl @@ -814,6 +814,24 @@ + + + + + + + + + + + + + + + + + + From c3ea386df57149b876cc4c728f28e75ce0b3b689 Mon Sep 17 00:00:00 2001 From: csigs Date: Fri, 28 Jan 2022 16:09:55 -0800 Subject: [PATCH 7/7] LEGO: check in for main to temporary branch. (#1266) --- loc/lcl/CSY/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ loc/lcl/ESN/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ loc/lcl/KOR/OpenFolderSchema.json.lcl | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/loc/lcl/CSY/OpenFolderSchema.json.lcl b/loc/lcl/CSY/OpenFolderSchema.json.lcl index e444a35ba..d3dd4ad6f 100644 --- a/loc/lcl/CSY/OpenFolderSchema.json.lcl +++ b/loc/lcl/CSY/OpenFolderSchema.json.lcl @@ -865,6 +865,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/loc/lcl/ESN/OpenFolderSchema.json.lcl b/loc/lcl/ESN/OpenFolderSchema.json.lcl index 08193bf9a..8a9a7376d 100644 --- a/loc/lcl/ESN/OpenFolderSchema.json.lcl +++ b/loc/lcl/ESN/OpenFolderSchema.json.lcl @@ -814,6 +814,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/loc/lcl/KOR/OpenFolderSchema.json.lcl b/loc/lcl/KOR/OpenFolderSchema.json.lcl index fcc9775b5..2aa095794 100644 --- a/loc/lcl/KOR/OpenFolderSchema.json.lcl +++ b/loc/lcl/KOR/OpenFolderSchema.json.lcl @@ -865,6 +865,24 @@ + + + + + + + + + + + + + + + + + +