From 440b13027876ec0dbbbcb397aa7885d316e3ba48 Mon Sep 17 00:00:00 2001 From: gc46 Date: Thu, 6 Jan 2022 14:32:28 -0800 Subject: [PATCH 1/9] Bug 1452506: The word "Refresh" disappears after moving the cursor over it in Docker(Linux Container) testing (#1255) * Bug 1452506: Changed foreground color for Refresh button * Bug 1452506: Fixed foreground color issues on triggers for Refresh button --- src/SSHDebugPS/UI/ContainerPickerDialogWindow.xaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SSHDebugPS/UI/ContainerPickerDialogWindow.xaml b/src/SSHDebugPS/UI/ContainerPickerDialogWindow.xaml index 6b3a214a3..c765009f6 100644 --- a/src/SSHDebugPS/UI/ContainerPickerDialogWindow.xaml +++ b/src/SSHDebugPS/UI/ContainerPickerDialogWindow.xaml @@ -225,12 +225,12 @@ + Value="{DynamicResource {x:Static platformui:EnvironmentColors.ControlLinkTextBrushKey}}" /> + Value="{DynamicResource {x:Static platformui:EnvironmentColors.ControlLinkTextBrushKey}}" /> @@ -240,7 +240,7 @@ + Value="{DynamicResource {x:Static platformui:EnvironmentColors.ControlLinkTextBrushKey}}" /> Date: Thu, 6 Jan 2022 16:30:05 -0800 Subject: [PATCH 2/9] Send unix styled paths for Cygwin GDB sessions (#1155) * Send unix styled paths for Cygwin GDB sessions This PR updates the following comands to send unix paths instead of Windows paths: -gdb-set solib-search-path -environment-cd -file-exec-and-symbols --- .../Engine.Impl/CygwinFileMapper.cs | 57 +++++++- .../Engine.Impl/DebuggedProcess.cs | 124 +++++++++--------- src/MIDebugEngine/Engine.Impl/Disassembly.cs | 2 +- src/MIDebugEngine/Engine.Impl/SourceLine.cs | 2 +- 4 files changed, 117 insertions(+), 68 deletions(-) diff --git a/src/MIDebugEngine/Engine.Impl/CygwinFileMapper.cs b/src/MIDebugEngine/Engine.Impl/CygwinFileMapper.cs index 534a6ef5a..a8339fb8d 100644 --- a/src/MIDebugEngine/Engine.Impl/CygwinFileMapper.cs +++ b/src/MIDebugEngine/Engine.Impl/CygwinFileMapper.cs @@ -22,6 +22,14 @@ public CygwinFilePathMapper(DebuggedProcess debuggedProcess) _cygwinToWindows = new Dictionary(); } + /// + /// Maps cygwin paths (/usr/bin) to Windows Paths (C:\User\bin) + /// + /// We cache these paths because most of the time it comes from callstacks that we need to provide + /// source file maps. + /// + /// A string representing the cygwin path. + /// A string as a full Windows path public string MapCygwinToWindows(string origCygwinPath) { if (!(_debuggedProcess.LaunchOptions is LocalLaunchOptions)) @@ -39,7 +47,7 @@ public string MapCygwinToWindows(string origCygwinPath) { if (!_cygwinToWindows.TryGetValue(cygwinPath, out windowsPath)) { - if (!LaunchCygPathAndReadResult(cygwinPath, localLaunchOptions.MIDebuggerPath, out windowsPath)) + if (!LaunchCygPathAndReadResult(cygwinPath, localLaunchOptions.MIDebuggerPath, convertToWindowsPath: true, out windowsPath)) { return origCygwinPath; } @@ -51,14 +59,40 @@ public string MapCygwinToWindows(string origCygwinPath) return windowsPath; } + /// + /// Maps Windows paths (C:\User\bin) to Cygwin Paths (/usr/bin) + /// + /// Not cached since we only do this for setting the program, symbol, and working dir. + /// + /// A string representing the Windows path. + /// A string as a full unix path + public string MapWindowsToCygwin(string origWindowsPath) + { + if (!(_debuggedProcess.LaunchOptions is LocalLaunchOptions)) + { + return origWindowsPath; + } + + LocalLaunchOptions localLaunchOptions = (LocalLaunchOptions)_debuggedProcess.LaunchOptions; + + string windowsPath = PlatformUtilities.UnixPathToWindowsPath(origWindowsPath); + + if (!LaunchCygPathAndReadResult(windowsPath, localLaunchOptions.MIDebuggerPath, convertToWindowsPath: false, out string cygwinPath)) + { + return origWindowsPath; + } + + return cygwinPath; + } + // There is an issue launching Cygwin apps that if a process is launched using a bitness mismatched console, // process launch will fail. To avoid that, launch cygpath with its own console. This requires calling CreateProcess // directly because the creation flags are not exposed in System.Diagnostics.Process. // // Return true if successful. False otherwise. - private bool LaunchCygPathAndReadResult(string cygwinPath, string miDebuggerPath, out string windowsPath) + private bool LaunchCygPathAndReadResult(string inputPath, string miDebuggerPath, bool convertToWindowsPath, out string outputPath) { - windowsPath = ""; + outputPath = ""; if (String.IsNullOrEmpty(miDebuggerPath)) { @@ -108,8 +142,19 @@ private bool LaunchCygPathAndReadResult(string cygwinPath, string miDebuggerPath const uint DETACHED_PROCESS = 0x00000008; uint flags = DETACHED_PROCESS; - // ex: "C:\\cygwin64\\bin\\cygpath.exe -w " + cygwinPath, - string command = String.Concat(cygpathPath, " -w ", cygwinPath); + string command = string.Empty; + if (convertToWindowsPath) + { + // -w, --windows print Windows form of NAMEs (C:\WINNT) + // ex: "C:\\cygwin64\\bin\\cygpath.exe -w " + inputPath, + command = String.Concat(cygpathPath, " -w ", inputPath); + } + else + { + // -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt) + // ex: "C:\\cygwin64\\bin\\cygpath.exe -u " + inputPath, + command = String.Concat(cygpathPath, " -u ", inputPath); + } if (!CreateProcess( null, command, @@ -153,7 +198,7 @@ out processInfo FileStream fs = new FileStream(stdoutRead, FileAccess.Read); StreamReader sr = new StreamReader(fs); - windowsPath = sr.ReadLine(); + outputPath = sr.ReadLine(); } finally { diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index e4f655971..8d9a766f1 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -551,6 +551,9 @@ public async Task Initialize(HostWaitLoop waitLoop, CancellationToken token) try { await this.MICommandFactory.EnableTargetAsyncOption(); + + await this.CheckCygwin(_launchOptions as LocalLaunchOptions); + List commands = await GetInitializeCommands(); _childProcessHandler?.Enable(); @@ -632,9 +635,11 @@ private async Task> GetInitializeCommands() commands.Add(new LaunchCommand("-gdb-set solib-absolute-prefix " + _launchOptions.AbsolutePrefixSOLibSearchPath)); } - // On Windows ';' appears to correctly works as a path seperator and from the documentation, it is ':' on unix - string pathEntrySeperator = _launchOptions.UseUnixSymbolPaths ? ":" : ";"; - string escapedSearchPath = string.Join(pathEntrySeperator, _launchOptions.GetSOLibSearchPath().Select(path => EscapeSymbolPath(path, ignoreSpaces: true))); + // On Windows ';' appears to correctly works as a path seperator and from the documentation, it is ':' on unix or cygwin envrionments + string pathEntrySeperator = (_launchOptions.UseUnixSymbolPaths || IsCygwin) ? ":" : ";"; + string escapedSearchPath = string.Join(pathEntrySeperator, _launchOptions.GetSOLibSearchPath().Select(path => { + return EnsureProperPathSeparators(path, ignoreSpaces: true); + })); if (!string.IsNullOrWhiteSpace(escapedSearchPath)) { if (_launchOptions.DebuggerMIMode == MIMode.Gdb) @@ -691,7 +696,7 @@ private async Task> GetInitializeCommands() this.AddGetTargetArchitectureCommand(commands); // Add core dump information (linux/mac does not support quotes around this path but spaces in the path do work) - string coreDump = this.UseUnixPathSeparators ? _launchOptions.CoreDumpPath : this.EnsureProperPathSeparators(_launchOptions.CoreDumpPath); + string coreDump = this.UseUnixPathSeparators ? _launchOptions.CoreDumpPath : this.EnsureProperPathSeparators(_launchOptions.CoreDumpPath, true); string coreDumpCommand = _launchOptions.DebuggerMIMode == MIMode.Lldb ? String.Concat("target create --core ", coreDump) : String.Concat("-target-select core ", coreDump); string coreDumpDescription = String.Format(CultureInfo.CurrentCulture, ResourceStrings.LoadingCoreDumpMessage, _launchOptions.CoreDumpPath); commands.Add(new LaunchCommand(coreDumpCommand, coreDumpDescription, ignoreFailures: false)); @@ -700,8 +705,6 @@ private async Task> GetInitializeCommands() { // This is an attach - CheckCygwin(commands, localLaunchOptions); - if (this.MICommandFactory.Mode == MIMode.Gdb) { if (_launchOptions is UnixShellPortLaunchOptions) @@ -766,7 +769,8 @@ private async Task> GetInitializeCommands() if (!string.IsNullOrWhiteSpace(_launchOptions.WorkingDirectory)) { - string escapedDir = this.EnsureProperPathSeparators(_launchOptions.WorkingDirectory); + string escapedDir = this.EnsureProperPathSeparators(_launchOptions.WorkingDirectory, true); + commands.Add(new LaunchCommand("-environment-cd " + escapedDir)); } @@ -779,8 +783,6 @@ private async Task> GetInitializeCommands() commands.Add(new LaunchCommand("-gdb-set new-console on", ignoreFailures: true)); } - CheckCygwin(commands, localLaunchOptions); - this.AddExecutablePathCommand(commands); // Important: this must occur after file-exec-and-symbols but before anything else. @@ -853,40 +855,54 @@ private async Task> GetInitializeCommands() return commands; } - private void CheckCygwin(List commands, LocalLaunchOptions localLaunchOptions) + /// + /// Checks to see if we are running Cygwin or not. + /// + /// + /// + private async Task CheckCygwin(LocalLaunchOptions localLaunchOptions) { - // If running locally on windows, determine if gdb is running from cygwin - if (localLaunchOptions != null && PlatformUtilities.IsWindows() && this.MICommandFactory.Mode == MIMode.Gdb) - { + // Checks to see if: + // 1. LocalLaunch Debugging + // 2. On Windows + // 3. With GDB + // 4. Does not have custom commands + // 5. Is not Android Debugging + // 6. Is not Dump Debugging + if (localLaunchOptions != null && + PlatformUtilities.IsWindows() && + this.MICommandFactory.Mode == MIMode.Gdb && + localLaunchOptions.CustomLaunchSetupCommands == null && + localLaunchOptions.DeviceAppLauncher == null && + !this.IsCoreDump) + { + string resultString = await ConsoleCmdAsync("show configuration", allowWhileRunning: false, ignoreFailures: true); + // mingw will not implement this command, but to be safe, also check if the results contains the string cygwin. - LaunchCommand lc = new LaunchCommand("show configuration", null, true, null, (string resStr) => - { - // Look to see if configuration has "cywgin" within a word boundry. - // Also look for "msys" since it is a modified version of Cygwin. - if (Regex.IsMatch(resStr, "\\bcygwin\\b|\\bmsys\\b")) - { - this.IsCygwin = true; - this.CygwinFilePathMapper = new CygwinFilePathMapper(this); - _engineTelemetry.SendWindowsRuntimeEnvironment(EngineTelemetry.WindowsRuntimeEnvironment.Cygwin); - } - else - { - this.IsMinGW = true; - // Gdb on windows and not cygwin implies mingw - _engineTelemetry.SendWindowsRuntimeEnvironment(EngineTelemetry.WindowsRuntimeEnvironment.MinGW); - } + // Look to see if configuration has "cywgin" within a word boundry. + // Also look for "msys" since it is a modified version of Cygwin. + if (Regex.IsMatch(resultString, "\\bcygwin\\b|\\bmsys\\b")) + { + this.IsCygwin = true; + this.CygwinFilePathMapper = new CygwinFilePathMapper(this); - return Task.FromResult(0); - }); - commands.Add(lc); + _engineTelemetry.SendWindowsRuntimeEnvironment(EngineTelemetry.WindowsRuntimeEnvironment.Cygwin); + } + else + { + this.IsMinGW = true; + // Gdb on windows and not cygwin implies mingw + _engineTelemetry.SendWindowsRuntimeEnvironment(EngineTelemetry.WindowsRuntimeEnvironment.MinGW); + } } } private void AddExecutablePathCommand(IList commands) { - string exe = this.EnsureProperPathSeparators(_launchOptions.ExePath); - string description = string.Format(CultureInfo.CurrentCulture, ResourceStrings.LoadingSymbolMessage, _launchOptions.ExePath); + string exe = this.EnsureProperPathSeparators(_launchOptions.ExePath, true); + + string description = string.Format(CultureInfo.CurrentCulture, ResourceStrings.LoadingSymbolMessage, exe); Action failureHandler = (string miError) => { @@ -1424,36 +1440,22 @@ internal WorkerThread WorkerThread get { return _worker; } } + private readonly char[] RemotePathSeperators = new char[] { ' ', '\'' }; + private readonly char[] LocalPathSeperators = new char[] { ' ' }; + /// - /// Use to ensure path separators are correct for files that exist on the target debugger's machine. - /// If you are debugging on Windows to a remote instance of gdb or gdbserver, it will update it to Unix path separators. + /// Use to ensure path separators are correct for files we are setting for GDB. + /// If you are debugging on Windows to a remote instance of gdb or gdbserver, it will update it to Unix path separators that exist on the target debugger's machine. + /// If you are debugging on Windows locally, it will escape the Windows path seperator. + /// If you are debugging on Windows locally with Cygwin, we will update it to use unix path seperators and resolve the cygwin path. /// - internal string EnsureProperPathSeparators(string path) + internal string EnsureProperPathSeparators(string path, bool isRemote = false, bool ignoreSpaces = false) { - if (this.UseUnixPathSeparators) - { - path = PlatformUtilities.WindowsPathToUnixPath(path); - } - else - { - path = path.Trim(); - path = path.Replace(@"\", @"\\"); - } - - if (path.IndexOfAny(new char[] { ' ', '\'' }) != -1) + if (IsCygwin) { - path = '"' + path + '"'; + path = CygwinFilePathMapper.MapWindowsToCygwin(path); } - return path; - } - - /// - /// This method should be used to escape paths that are used by GDB (and NOT gdbserver) locally. - /// Any path that gdbserver would use in remote server scenarios should use EnsureProperPathSeparators instead. - /// - internal string EscapeSymbolPath(string path, bool ignoreSpaces = false) - { - if (this.UseUnixSymbolPaths) + else if (this.UseUnixPathSeparators) { path = PlatformUtilities.WindowsPathToUnixPath(path); } @@ -1463,7 +1465,9 @@ internal string EscapeSymbolPath(string path, bool ignoreSpaces = false) path = path.Replace(@"\", @"\\"); } - if (!ignoreSpaces && path.IndexOf(' ') != -1) + char[] pathSeperator = isRemote ? RemotePathSeperators : LocalPathSeperators; + + if (!ignoreSpaces && path.IndexOfAny(pathSeperator) != -1) { path = '"' + path + '"'; } diff --git a/src/MIDebugEngine/Engine.Impl/Disassembly.cs b/src/MIDebugEngine/Engine.Impl/Disassembly.cs index eb2d07993..f08b928ec 100644 --- a/src/MIDebugEngine/Engine.Impl/Disassembly.cs +++ b/src/MIDebugEngine/Engine.Impl/Disassembly.cs @@ -323,7 +323,7 @@ internal async Task> Disassemble(DebuggedProcess { if (file.IndexOf(' ') >= 0) // only needs escaping if filename contains a space { - file = process.EscapeSymbolPath(file); + file = process.EnsureProperPathSeparators(file); } string cmd = "-data-disassemble -f " + file + " -l " + line.ToString(CultureInfo.InvariantCulture) + " -n " + dwInstructions.ToString(CultureInfo.InvariantCulture) + " -- 1"; Results results = await process.CmdAsync(cmd, ResultClass.None); diff --git a/src/MIDebugEngine/Engine.Impl/SourceLine.cs b/src/MIDebugEngine/Engine.Impl/SourceLine.cs index 37b439018..f05464a68 100644 --- a/src/MIDebugEngine/Engine.Impl/SourceLine.cs +++ b/src/MIDebugEngine/Engine.Impl/SourceLine.cs @@ -92,7 +92,7 @@ internal async Task GetLinesForFile(string file) } private async Task LinesForFile(string file) { - string cmd = "-symbol-list-lines " + _process.EscapeSymbolPath(file); + string cmd = "-symbol-list-lines " + _process.EnsureProperPathSeparators(file); Results results = await _process.CmdAsync(cmd, ResultClass.None); if (results.ResultClass != ResultClass.done) From c2ed23eb1cb5575659e756ba9af9f42d52f71254 Mon Sep 17 00:00:00 2001 From: Calvin Gagliano Date: Tue, 18 Jan 2022 16:13:52 -0800 Subject: [PATCH 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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 @@ + + + + + + + + + + + + + + + + + +