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/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 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
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/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/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/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 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
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 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
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 @@
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
-
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/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/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/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
index 8d9a766f1..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))));
}
@@ -2248,7 +2252,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
}
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 0c145ad68..bb46637d6 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 +3512,7 @@ public void HandleIDebugBreakpointErrorEvent2(IDebugEngine2 pEngine, IDebugProce
};
}
}
- else
+ else if (ad7BPRequest.FunctionPosition != null)
{
bp = new Breakpoint()
{
@@ -3324,6 +3526,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