From 7393b7253d726345101b9f862754bb77c261597f Mon Sep 17 00:00:00 2001 From: paulmaybee Date: Wed, 20 Oct 2021 15:36:53 -0700 Subject: [PATCH 1/9] make radix callback on calling thread (#1234) * make radix callback on calling thread * review comments * pass radix as zero when asking for return value --- src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs | 2 +- src/MIDebugEngine/Engine.Impl/Variables.cs | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index 02885bd2f..72e3d3e26 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -1255,7 +1255,7 @@ private async Task HandleBreakModeEvent(ResultEventArgs results, BreakRequest br if (!string.IsNullOrEmpty(resultVar)) { ReturnValue = new VariableInformation("$ReturnValue", resultVar, cxt, Engine, (AD7Thread)thread.Client, isParameter: false); - await ReturnValue.Eval(); + await ReturnValue.Eval(radix: 0); } _callback.OnStepComplete(thread); } diff --git a/src/MIDebugEngine/Engine.Impl/Variables.cs b/src/MIDebugEngine/Engine.Impl/Variables.cs index 3a00b4a2f..fd0da0ebf 100644 --- a/src/MIDebugEngine/Engine.Impl/Variables.cs +++ b/src/MIDebugEngine/Engine.Impl/Variables.cs @@ -60,7 +60,7 @@ internal SimpleVariableInformation(string name, bool isParam = false, string val internal async Task CreateMIDebuggerVariable(ThreadContext ctx, AD7Engine engine, AD7Thread thread) { VariableInformation vi = new VariableInformation(Name, Name, ctx, engine, thread, IsParameter); - await vi.Eval(); + await vi.Eval(engine.CurrentRadix()); return vi; } } @@ -446,9 +446,10 @@ public void AsyncEval(IDebugEventCallback2 pExprCallback) engineCallback = _engine.Callback; } + uint radix = _engine.CurrentRadix(); Task evalTask = Task.Run(async () => { - await Eval(); + await Eval(radix); }); Action onComplete = (Task t) => @@ -473,9 +474,10 @@ public void AsyncError(IDebugEventCallback2 pExprCallback, IDebugProperty2 error public void SyncEval(enum_EVALFLAGS dwFlags = 0, DAPEvalFlags dwDAPFlags = 0) { + uint radix = _engine.CurrentRadix(); Task eval = Task.Run(async () => { - await Eval(dwFlags, dwDAPFlags); + await Eval(radix, dwFlags, dwDAPFlags); }); eval.Wait(); } @@ -493,11 +495,14 @@ public string EvalDependentExpression(string expr) return val; } - internal async Task Eval(enum_EVALFLAGS dwFlags = 0, DAPEvalFlags dwDAPFlags = 0) + internal async Task Eval(uint radix, enum_EVALFLAGS dwFlags = 0, DAPEvalFlags dwDAPFlags = 0) { this.VerifyNotDisposed(); - await _engine.UpdateRadixAsync(_engine.CurrentRadix()); // ensure the radix value is up-to-date + if (radix != 0) + { + await _engine.UpdateRadixAsync(radix); // ensure the radix value is up-to-date + } try { From d8e84841c31b11db3cf767a3794f0972a349299a Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 9 Nov 2021 16:50:48 -0800 Subject: [PATCH 2/9] Downgrade GDB testing to 10.2 (#1238) mingw-w64-x86_64-toolchain downloads mingw-w64-x86_64-gdb-11.1-2 which causes GDB to return "During startup program exited with code 0xc0000139" Downgrade to working mingw-w64-x86_64-gdb-10.2-2 --- .github/workflows/pull-request.yml | 55 +++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index a878db6d5..67e92e6e8 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -76,12 +76,65 @@ jobs: update: true install: >- mingw-w64-x86_64-toolchain + + # As of Nov 8, 2021, mingw-w64-x86_64-toolchain-11.1.x + # causes GDB to return "During startup program exited with code 0xc0000139" + # Downgrade to working toolchain (04-May-2021) + # TODO: Remove this task when there is a newer version of toolchain than 11.1.x + - shell: msys2 {0} + name: Downgrade toolchain to 10.x + run: | + # Download GDB + DOWNLOAD_DOWNGRADED_GDB_PATH='${{ github.workspace }}/mingw-w64-x86_64-gdb-10.2-2-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_GDB_PATH=${DOWNLOAD_DOWNGRADED_GDB_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_GDB_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gdb-10.2-2-any.pkg.tar.zst + + # Download GCC + DOWNLOAD_DOWNGRADED_GCC_PATH='${{ github.workspace }}/mingw-w64-x86_64-gcc-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_GCC_PATH=${DOWNLOAD_DOWNGRADED_GCC_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_GCC_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-10.3.0-8-any.pkg.tar.zst + + # Download GCC Lib + DOWNLOAD_DOWNGRADED_GCCLIB_PATH='${{ github.workspace }}/mingw-w64-x86_64-gcc-libs-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_GCCLIB_PATH=${DOWNLOAD_DOWNGRADED_GCCLIB_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_GCCLIB_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-libs-10.3.0-8-any.pkg.tar.zst + + # Download GCC Ada + DOWNLOAD_DOWNGRADED_ADA_PATH='${{ github.workspace }}/mingw-w64-x86_64-gcc-ada-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_ADA_PATH=${DOWNLOAD_DOWNGRADED_ADA_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_ADA_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-ada-10.3.0-8-any.pkg.tar.zst + + # Download GCC Fortran + DOWNLOAD_DOWNGRADED_FORTRAN_PATH='${{ github.workspace }}/mingw-w64-x86_64-gcc-fortran-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_FORTRAN_PATH=${DOWNLOAD_DOWNGRADED_FORTRAN_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_FORTRAN_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-fortran-10.3.0-8-any.pkg.tar.zst + + # Download GCC Libfortran + DOWNLOAD_DOWNGRADED_LIBFORTRAN_PATH='${{ github.workspace }}/mingw-w64-x86_64-gcc-libgfortran-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_LIBFORTRAN_PATH=${DOWNLOAD_DOWNGRADED_LIBFORTRAN_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_LIBFORTRAN_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-libgfortran-10.3.0-8-any.pkg.tar.zst + + # Download GCC Obj-C + DOWNLOAD_DOWNGRADED_OBJC_PATH='${{ github.workspace }}/mingw-w64-x86_64-gcc-objc-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_OBJC_PATH=${DOWNLOAD_DOWNGRADED_OBJC_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_OBJC_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-objc-10.3.0-8-any.pkg.tar.zst + + # Download GCC libgccjit + DOWNLOAD_DOWNGRADED_LIBGCCJIT_PATH='${{ github.workspace }}/mingw-w64-x86_64-libgccjit-10.3.0-8-any.pkg.tar.zst' + DOWNLOAD_DOWNGRADED_LIBGCCJIT_PATH=${DOWNLOAD_DOWNGRADED_LIBGCCJIT_PATH//\\//} + wget -O ${DOWNLOAD_DOWNGRADED_LIBGCCJIT_PATH} http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libgccjit-10.3.0-8-any.pkg.tar.zst + + # Install + pacman -U --noconfirm $DOWNLOAD_DOWNGRADED_GDB_PATH $DOWNLOAD_DOWNGRADED_GCC_PATH $DOWNLOAD_DOWNGRADED_GCCLIB_PATH $DOWNLOAD_DOWNGRADED_ADA_PATH $DOWNLOAD_DOWNGRADED_FORTRAN_PATH $DOWNLOAD_DOWNGRADED_LIBFORTRAN_PATH $DOWNLOAD_DOWNGRADED_OBJC_PATH $DOWNLOAD_DOWNGRADED_LIBGCCJIT_PATH + - shell: msys2 {0} name: Gather c++ toolchain paths run: | cygpath -w $(which g++) - cygpath -w $(which g++) + g++ --version + cygpath -w $(which gdb) + gdb --version - run: > From 13b4f5f917579097e552ca92c5dc5b5b43095599 Mon Sep 17 00:00:00 2001 From: paulmaybee Date: Thu, 18 Nov 2021 17:58:23 -0800 Subject: [PATCH 3/9] Allow passwords in ssh connection strings (#1239) * Allow passwords in ssh connection strings * fix test * more test fixes * Check passwords --- src/SSHDebugPS/ConnectionManager.cs | 73 +++++++++++++++++-- src/SSHDebugPS/StringResources.Designer.cs | 24 +++++- src/SSHDebugPS/StringResources.resx | 11 +++ src/SSHDebugTests/SSHConnectionStringTests.cs | 54 +++++++++++++- 4 files changed, 152 insertions(+), 10 deletions(-) diff --git a/src/SSHDebugPS/ConnectionManager.cs b/src/SSHDebugPS/ConnectionManager.cs index 6a316e689..0222926b0 100644 --- a/src/SSHDebugPS/ConnectionManager.cs +++ b/src/SSHDebugPS/ConnectionManager.cs @@ -5,8 +5,10 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Security; using System.Text.RegularExpressions; using System.Threading; +using System.Windows; using System.Windows.Interop; using EnvDTE; using liblinux; @@ -82,19 +84,48 @@ public static SSHConnection GetSSHConnection(string name) IVsConnectionManager connectionManager = (IVsConnectionManager)ServiceProvider.GlobalProvider.GetService(typeof(IVsConnectionManager)); if (connectionManager != null) { - IConnectionManagerResult result; + IConnectionManagerResult result = null; if (string.IsNullOrWhiteSpace(name)) { result = connectionManager.ShowDialog(); } else { - ParseSSHConnectionString(name, out string userName, out string hostName, out int port); + ParseSSHConnectionString(name, out string userName, out SecureString password, out string hostName, out int port); - result = connectionManager.ShowDialog(new PasswordConnectionInfo(hostName, port, Timeout.InfiniteTimeSpan, userName, new System.Security.SecureString())); + if (password == null) + { + result = connectionManager.ShowDialog(new PasswordConnectionInfo(hostName, port, Timeout.InfiniteTimeSpan, userName, new SecureString())); + } + else + { + connectionInfo = new PasswordConnectionInfo(hostName, port, Timeout.InfiniteTimeSpan, userName, password); + + // Attempt to open the temp connection so that the host key can be verified. + try + { + using (var system = new UnixSystem()) + { + system.Connect(connectionInfo); + } + } + catch (RemoteConnectivityException e) when (!String.IsNullOrEmpty(e.Fingerprint)) + { + var answer = MessageBox.Show(string.Format(CultureInfo.CurrentCulture, StringResources.NewHostKeyMessage, connectionInfo.HostNameOrAddress, e.HostKeyName, e.Fingerprint), StringResources.HostKeyCaption, MessageBoxButton.YesNo); + + if (answer == MessageBoxResult.Yes) + { + connectionInfo.Fingerprint = e.Fingerprint; + } + else + { + throw; + } + } + } } - if ((result.DialogResult & ConnectionManagerDialogResult.Succeeded) == ConnectionManagerDialogResult.Succeeded) + if (result != null && (result.DialogResult & ConnectionManagerDialogResult.Succeeded) == ConnectionManagerDialogResult.Succeeded) { // Retrieve the newly added connection store.Load(); @@ -154,14 +185,33 @@ public static bool ShowContainerPickerWindow(IntPtr hwnd, bool supportSSHConnect // Default SSH port is 22 internal const int DefaultSSHPort = 22; + + /// + /// Converts a string to a SecureString. + /// + /// The raw string to convert. + /// The SecureString, which the client must dispose of. + /// This function is generally unsafe to use, as it defeats the purpose of + /// a SecureString to have its unsecured copy floating in memory. This is used to + /// shim two APIs. + private static SecureString ToSecureString(string str) + { + var ss = new SecureString(); + foreach (var c in str) + ss.AppendChar(c); + ss.MakeReadOnly(); + return ss; + } + /// - /// Parses the SSH connection string. Expected format is some permutation of username@hostname:portnumber. + /// Parses the SSH connection string. Expected format is some permutation of username[:password]@hostname:portnumber. /// If not defined, will provide default values. /// - internal static void ParseSSHConnectionString(string connectionString, out string userName, out string hostName, out int port) + internal static void ParseSSHConnectionString(string connectionString, out string userName, out SecureString password, out string hostName, out int port) { userName = StringResources.UserName_PlaceHolder; hostName = StringResources.HostName_PlaceHolder; + password = null; port = DefaultSSHPort; const string TempUriPrefix = "ssh://"; @@ -172,7 +222,18 @@ internal static void ParseSSHConnectionString(string connectionString, out strin Uri connectionUri = new Uri(TempUriPrefix + connectionString); if (!string.IsNullOrWhiteSpace(connectionUri.UserInfo)) + { userName = connectionUri.UserInfo; + if (userName.Contains(':')) + { + var userAndPassword = userName.Split(':'); + if (userAndPassword.Length == 2) + { + userName = userAndPassword[0]; + password = ToSecureString(userAndPassword[1]); + } + } + } if (!string.IsNullOrWhiteSpace(connectionUri.Host)) hostName = connectionUri.Host; diff --git a/src/SSHDebugPS/StringResources.Designer.cs b/src/SSHDebugPS/StringResources.Designer.cs index f63ad6c6a..78a19bb65 100644 --- a/src/SSHDebugPS/StringResources.Designer.cs +++ b/src/SSHDebugPS/StringResources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.SSHDebugPS { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class StringResources { @@ -334,6 +334,15 @@ internal static string HeaderTextFormat { } } + /// + /// Looks up a localized string similar to Accept Host Key. + /// + internal static string HostKeyCaption { + get { + return ResourceManager.GetString("HostKeyCaption", resourceCulture); + } + } + /// /// Looks up a localized string similar to <hostname>. /// @@ -343,6 +352,19 @@ internal static string HostName_PlaceHolder { } } + /// + /// Looks up a localized string similar to The authenticity of the host '{0}' needs to be established. + /// + ///'{1}' key fingerprint is '{2}'. + /// + ///Would you like to continue connecting?. + /// + internal static string NewHostKeyMessage { + get { + return ResourceManager.GetString("NewHostKeyMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to <unknown>. /// diff --git a/src/SSHDebugPS/StringResources.resx b/src/SSHDebugPS/StringResources.resx index 0dd555cf3..af44a77c5 100644 --- a/src/SSHDebugPS/StringResources.resx +++ b/src/SSHDebugPS/StringResources.resx @@ -273,4 +273,15 @@ Error: The installed version of Windows Subsystem for Linux (WSL) is incompatible with Visual Studio's attach support. Please upgrade to WSL version 2 or newer. + + Accept Host Key + + + The authenticity of the host '{0}' needs to be established. + +'{1}' key fingerprint is '{2}'. + +Would you like to continue connecting? + {0} is a hostname, {1} is a key name, {2} is a fingerprint key value + \ No newline at end of file diff --git a/src/SSHDebugTests/SSHConnectionStringTests.cs b/src/SSHDebugTests/SSHConnectionStringTests.cs index fb295d21e..8e92c8692 100644 --- a/src/SSHDebugTests/SSHConnectionStringTests.cs +++ b/src/SSHDebugTests/SSHConnectionStringTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Security; using Microsoft.SSHDebugPS; using Microsoft.SSHDebugPS.Utilities; using Xunit; @@ -16,6 +17,7 @@ internal struct ConnectionStringTestItem { internal string rawConnectionString; internal string expectedUsername; + internal string expectedPassword; internal string expectedHostname; internal int expectedPort; } @@ -30,6 +32,7 @@ public void IPv6ConnectionStrings() // valid rawConnectionString = "testuser@[1:2:3:4:5:6:7:8]:24", expectedUsername = "testuser", + expectedPassword = null, expectedHostname = "[1:2:3:4:5:6:7:8]", expectedPort = 24 }); @@ -39,15 +42,17 @@ public void IPv6ConnectionStrings() // valid with no port rawConnectionString = "testuser@[1:2:3:4:5:6:7:8]", expectedUsername = "testuser", + expectedPassword = null, expectedHostname = "[1:2:3:4:5:6:7:8]", expectedPort = ConnectionManager.DefaultSSHPort }); ipv6TestStrings.Add( new ConnectionStringTestItem() { - // valid with custom username + // valid with username:password rawConnectionString = "test:user@[1234::6:7:8]", - expectedUsername = "test:user", + expectedUsername = "test", + expectedPassword = "user", expectedHostname = "[1234::6:7:8]", expectedPort = ConnectionManager.DefaultSSHPort }); @@ -57,6 +62,7 @@ public void IPv6ConnectionStrings() // Valid with large port rawConnectionString = "[1:2:3:4:5:6:7:8]:12345", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = "[1:2:3:4:5:6:7:8]", expectedPort = 12345 }); @@ -66,6 +72,7 @@ public void IPv6ConnectionStrings() // Invalid format rawConnectionString = "testuser@:8", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = StringResources.HostName_PlaceHolder, expectedPort = ConnectionManager.DefaultSSHPort }); @@ -75,6 +82,7 @@ public void IPv6ConnectionStrings() // Invalid string (just port) rawConnectionString = ":8", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = StringResources.HostName_PlaceHolder, expectedPort = ConnectionManager.DefaultSSHPort }); @@ -84,6 +92,7 @@ public void IPv6ConnectionStrings() // Empty String rawConnectionString = string.Empty, expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = StringResources.HostName_PlaceHolder, expectedPort = ConnectionManager.DefaultSSHPort }); @@ -93,6 +102,7 @@ public void IPv6ConnectionStrings() // Invalid port rawConnectionString = "[1:2:3:4:5:6:7:8]:123456", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = StringResources.HostName_PlaceHolder, expectedPort = ConnectionManager.DefaultSSHPort }); @@ -113,6 +123,7 @@ public void Ipv4ConnectionStrings() // valid no username rawConnectionString = "192.168.1.1:156", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = "192.168.1.1", expectedPort = 156 }); @@ -122,6 +133,7 @@ public void Ipv4ConnectionStrings() // valid username with port rawConnectionString = "customUser@192.168.1.1:65354", expectedUsername = "customUser", + expectedPassword = null, expectedHostname = "192.168.1.1", expectedPort = 65354 }); @@ -131,6 +143,7 @@ public void Ipv4ConnectionStrings() // valid no username, Large port rawConnectionString = "192.168.1.1:" + (ushort.MaxValue).ToString("d", CultureInfo.InvariantCulture), expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = "192.168.1.1", expectedPort = ushort.MaxValue }); @@ -140,15 +153,27 @@ public void Ipv4ConnectionStrings() // valid username no port rawConnectionString = "user@10.10.10.10", expectedUsername = "user", + expectedPassword = null, expectedHostname = "10.10.10.10", expectedPort = ConnectionManager.DefaultSSHPort }); + ipv4TestStrings.Add( + new ConnectionStringTestItem() + { + // valid username no port with password + rawConnectionString = "user:pass@10.10.10.10", + expectedUsername = "user", + expectedPassword = "pass", + expectedHostname = "10.10.10.10", + expectedPort = ConnectionManager.DefaultSSHPort + }); ipv4TestStrings.Add( new ConnectionStringTestItem() { // Invalid port rawConnectionString = "192.168.1.1:123456", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = StringResources.HostName_PlaceHolder, expectedPort = ConnectionManager.DefaultSSHPort }); @@ -158,6 +183,7 @@ public void Ipv4ConnectionStrings() // Invalid address rawConnectionString = "1%92.168.1.1:23", expectedUsername = StringResources.UserName_PlaceHolder, + expectedPassword = null, expectedHostname = StringResources.HostName_PlaceHolder, expectedPort = ConnectionManager.DefaultSSHPort }); @@ -174,11 +200,33 @@ private void ParseConnectionAndValidate(ConnectionStringTestItem item) string username; string hostname; int port; - ConnectionManager.ParseSSHConnectionString(item.rawConnectionString, out username, out hostname, out port); + ConnectionManager.ParseSSHConnectionString(item.rawConnectionString, out username, out SecureString password, out hostname, out port); Assert.True(item.expectedUsername.Equals(username, StringComparison.Ordinal), _comparisonErrorStringFormat.FormatInvariantWithArgs("UserName", item.expectedUsername, username)); Assert.True(item.expectedHostname.Equals(hostname, StringComparison.Ordinal), _comparisonErrorStringFormat.FormatInvariantWithArgs("Hostname", item.expectedHostname, hostname)); Assert.True(item.expectedPort == port, _comparisonErrorStringFormat.FormatInvariantWithArgs("Port", item.expectedPort, port)); + if (item.expectedPassword == null) + { + Assert.True(password == null); + } + else + { + string passwordString = StringFromSecureString(password); + Assert.True(item.expectedPassword.Equals(passwordString, StringComparison.Ordinal), _comparisonErrorStringFormat.FormatInvariantWithArgs("Password", item.expectedPassword, passwordString)); + } + } + + private static string StringFromSecureString(SecureString secString) + { + if (secString == null) + { + return null; + } + + IntPtr bstr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(secString); + string value = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(bstr); + System.Runtime.InteropServices.Marshal.FreeBSTR(bstr); + return value; } } } From 3f7a0d663af8688ee8fe9289d0a3a0651215ca98 Mon Sep 17 00:00:00 2001 From: csigs Date: Fri, 19 Nov 2021 17:10:20 -0800 Subject: [PATCH 4/9] LEGO: check in for main to temporary branch. (#1240) --- loc/lcl/DEU/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/ITA/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/KOR/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/loc/lcl/DEU/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/DEU/Microsoft.SSHDebugPS.dll.lcl index 0ae56658c..e04c77111 100644 --- a/loc/lcl/DEU/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/DEU/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/ITA/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/ITA/Microsoft.SSHDebugPS.dll.lcl index 02eda887c..126f24abe 100644 --- a/loc/lcl/ITA/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/ITA/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/KOR/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/KOR/Microsoft.SSHDebugPS.dll.lcl index 36922ddbc..4dcd06cc2 100644 --- a/loc/lcl/KOR/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/KOR/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + From f064e58a11c3ca8803d9c03f9ca700e26ecc8e67 Mon Sep 17 00:00:00 2001 From: csigs Date: Sat, 20 Nov 2021 14:12:15 -0800 Subject: [PATCH 5/9] LEGO: check in for main to temporary branch. (#1242) --- loc/lcl/CHS/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/ESN/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/PLK/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/TRK/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/loc/lcl/CHS/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/CHS/Microsoft.SSHDebugPS.dll.lcl index 6e889f8b1..f7bcef49c 100644 --- a/loc/lcl/CHS/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/CHS/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/ESN/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/ESN/Microsoft.SSHDebugPS.dll.lcl index 9cc418359..3f0c41e93 100644 --- a/loc/lcl/ESN/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/ESN/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/PLK/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/PLK/Microsoft.SSHDebugPS.dll.lcl index 99ff217c4..f5c9c8b0e 100644 --- a/loc/lcl/PLK/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/PLK/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/TRK/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/TRK/Microsoft.SSHDebugPS.dll.lcl index 843ba4c4e..ed3606cc1 100644 --- a/loc/lcl/TRK/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/TRK/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + From ba265a96a8c2680d0da05690b1a1ca589f5c1d81 Mon Sep 17 00:00:00 2001 From: csigs Date: Sat, 20 Nov 2021 14:13:00 -0800 Subject: [PATCH 6/9] LEGO: check in for main to temporary branch. (#1241) --- loc/lcl/PTB/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/RUS/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/loc/lcl/PTB/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/PTB/Microsoft.SSHDebugPS.dll.lcl index 044d484da..915c119a8 100644 --- a/loc/lcl/PTB/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/PTB/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/RUS/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/RUS/Microsoft.SSHDebugPS.dll.lcl index 8d2e58087..535ce8576 100644 --- a/loc/lcl/RUS/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/RUS/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + From ad4a536e0a6dcba16042969c0a7c8fc4c9247d59 Mon Sep 17 00:00:00 2001 From: csigs Date: Sat, 20 Nov 2021 16:17:56 -0800 Subject: [PATCH 7/9] LEGO: check in for main to temporary branch. (#1243) --- loc/lcl/CHT/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/CSY/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/loc/lcl/CHT/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/CHT/Microsoft.SSHDebugPS.dll.lcl index bf7a6b3b1..94075ea4f 100644 --- a/loc/lcl/CHT/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/CHT/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/CSY/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/CSY/Microsoft.SSHDebugPS.dll.lcl index a5d509749..8f5db7fab 100644 --- a/loc/lcl/CSY/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/CSY/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + From 0b4f59c72d4fef28f9f40f96408d104e283a6c1d Mon Sep 17 00:00:00 2001 From: csigs Date: Sun, 21 Nov 2021 13:56:12 -0800 Subject: [PATCH 8/9] LEGO: check in for main to temporary branch. (#1244) --- loc/lcl/FRA/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- loc/lcl/JPN/Microsoft.SSHDebugPS.dll.lcl | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/loc/lcl/FRA/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/FRA/Microsoft.SSHDebugPS.dll.lcl index f8b865839..32dc1aadd 100644 --- a/loc/lcl/FRA/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/FRA/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + diff --git a/loc/lcl/JPN/Microsoft.SSHDebugPS.dll.lcl b/loc/lcl/JPN/Microsoft.SSHDebugPS.dll.lcl index 68dc203e3..5d9f51eae 100644 --- a/loc/lcl/JPN/Microsoft.SSHDebugPS.dll.lcl +++ b/loc/lcl/JPN/Microsoft.SSHDebugPS.dll.lcl @@ -292,6 +292,15 @@ + + + + + + + + + ]]> @@ -301,6 +310,15 @@ + + + + + + + + + ]]> @@ -850,7 +868,7 @@ - + From 6ea59296af336c238f35de1a6dcaa6460a29ca5f Mon Sep 17 00:00:00 2001 From: gc46 Date: Tue, 7 Dec 2021 08:05:47 -0800 Subject: [PATCH 9/9] Linux Debugging: Debugger does not stop for handled C++ exceptions (#1245) --- src/MICore/CommandFactories/gdb.cs | 57 ++++ .../Engine.Impl/DebuggedProcess.cs | 4 + .../Engine.Impl/ExceptionManager.cs | 279 ++++++++++++------ src/MIDebugEngine/ResourceStrings.Designer.cs | 22 +- src/MIDebugEngine/ResourceStrings.resx | 9 + src/OpenDebugAD7/cppdbg.ad7Engine.json | 10 +- 6 files changed, 295 insertions(+), 86 deletions(-) diff --git a/src/MICore/CommandFactories/gdb.cs b/src/MICore/CommandFactories/gdb.cs index 906af4bb8..6bd7e119b 100644 --- a/src/MICore/CommandFactories/gdb.cs +++ b/src/MICore/CommandFactories/gdb.cs @@ -295,5 +295,62 @@ public override async Task AutoComplete(string command, int threadId, return matchlist?.AsStrings; } + + public override IEnumerable GetSupportedExceptionCategories() + { + const string CppExceptionCategoryString = "{3A12D0B7-C26C-11D0-B442-00A0244A1DD2}"; + return new Guid[] { new Guid(CppExceptionCategoryString) }; + } + + public override async Task> SetExceptionBreakpoints(Guid exceptionCategory, IEnumerable exceptionNames, ExceptionBreakpointStates exceptionBreakpointStates) + { + string command; + Results result; + List breakpointNumbers = new List(); + + if (exceptionNames == null) // set breakpoint for all exceptions in exceptionCategory + { + command = "-catch-throw"; + result = await _debugger.CmdAsync(command, ResultClass.None); + switch (result.ResultClass) + { + case ResultClass.done: + var breakpointNumber = result.Find("bkpt").FindUint("number"); + breakpointNumbers.Add(breakpointNumber); + break; + case ResultClass.error: + default: + throw new NotSupportedException(); + } + } + else // set breakpoint for each exceptionName in exceptionNames + { + command = "-catch-throw -r \\b"; + foreach (string exceptionName in exceptionNames) + { + result = await _debugger.CmdAsync(command + exceptionName + "\\b", ResultClass.None); + switch (result.ResultClass) + { + case ResultClass.done: + var breakpointNumber = result.Find("bkpt").FindUint("number"); + breakpointNumbers.Add(breakpointNumber); + break; + case ResultClass.error: + default: + throw new NotSupportedException(); + } + } + } + + return breakpointNumbers; + } + + public override async Task RemoveExceptionBreakpoint(Guid exceptionCategory, IEnumerable exceptionBreakpoints) + { + foreach (ulong breakpointNumber in exceptionBreakpoints) + { + await BreakDelete(breakpointNumber.ToString(CultureInfo.InvariantCulture)); + } + } } } diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index 72e3d3e26..fd47aafb3 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -1189,6 +1189,10 @@ private async Task HandleBreakModeEvent(ResultEventArgs results, BreakRequest br bplist.AddRange(bkpt); _callback.OnBreakpoint(thread, bplist.AsReadOnly()); } + else if (ExceptionManager.TryGetExceptionBreakpoint(bkptno, out string exceptionName, out Guid exceptionCategoryGuid)) // exception breakpoint hit + { + _callback.OnException(thread, exceptionName, "", 0, exceptionCategoryGuid, ExceptionBreakpointStates.BreakThrown); + } else if (!this.EntrypointHit) { this.EntrypointHit = true; diff --git a/src/MIDebugEngine/Engine.Impl/ExceptionManager.cs b/src/MIDebugEngine/Engine.Impl/ExceptionManager.cs index 0f9b8506c..7c9286329 100644 --- a/src/MIDebugEngine/Engine.Impl/ExceptionManager.cs +++ b/src/MIDebugEngine/Engine.Impl/ExceptionManager.cs @@ -33,13 +33,16 @@ internal class ExceptionManager private readonly ReadOnlyDictionary _categoryMap; private readonly ISampleEngineCallback _callback; private bool _initialSettingssSent; + private bool _canProcessExceptions = true; private readonly object _updateLock = new object(); private int? _lastUpdateTime; private Task _updateTask; private CancellationTokenSource _updateDelayCancelSource; - private class SettingsUpdates + private static readonly Guid CppExceptionCategoryGuid = new Guid("{3A12D0B7-C26C-11D0-B442-00A0244A1DD2}"); + + private class SettingsUpdates { // Threading note: these are only modified on the main thread public ExceptionBreakpointStates? NewCategoryState; @@ -94,8 +97,10 @@ private class ExceptionCategorySettings public readonly ExceptionBreakpointStates DefaultCategoryState; public readonly ReadOnlyDictionary DefaultRules; - // Threading note: these are only read or updated by the FlushSettingsUpdates thread (in UpdateCatagory), and we - // guarantee that there will only be one active FlushSettingsUpdates task at a time + // Threading note: these are only read or updated by the FlushSettingsUpdates thread (in UpdateCategory) and TryGetExceptionBreakpoint. + // The following rules apply: + // 1. Writes and reads when *NOT* on the FlushSettingsUpdates thread - collection should be locked on itself + // 2. Reads on the FlushSettingsUpdates thread - no locking is needed public ExceptionBreakpointStates CategoryState; public readonly Dictionary CurrentRules = new Dictionary(); @@ -174,108 +179,145 @@ public ExceptionManager(MICommandFactory commandFactory, WorkerThread worker, IS public void RemoveAllSetExceptions(Guid guidType) { - if (guidType == Guid.Empty) + if (_canProcessExceptions) { - foreach (var key in _categoryMap.Keys) + if (guidType == Guid.Empty) { - RemoveAllSetExceptions(key); + foreach (var key in _categoryMap.Keys) + { + RemoveAllSetExceptions(key); + } + } + else + { + ExceptionCategorySettings categorySettings; + if (!_categoryMap.TryGetValue(guidType, out categorySettings)) + { + return; // not a category that we care about + } + + using (var settingsUpdateHolder = categorySettings.GetSettingsUpdate()) + { + settingsUpdateHolder.Value.RulesToAdd.Clear(); + settingsUpdateHolder.Value.RulesToRemove.Clear(); + + settingsUpdateHolder.Value.NewCategoryState = categorySettings.DefaultCategoryState; + foreach (var defaultRule in categorySettings.DefaultRules) + { + settingsUpdateHolder.Value.RulesToAdd.Add(defaultRule.Key, defaultRule.Value); + } + } } } - else + } + + public void RemoveSetException(ref EXCEPTION_INFO exceptionInfo) + { + if (_canProcessExceptions) { ExceptionCategorySettings categorySettings; - if (!_categoryMap.TryGetValue(guidType, out categorySettings)) + if (!_categoryMap.TryGetValue(exceptionInfo.guidType, out categorySettings)) { return; // not a category that we care about } - using (var settingsUpdateHolder = categorySettings.GetSettingsUpdate()) + if (categorySettings.CategoryName.Equals(exceptionInfo.bstrExceptionName, StringComparison.OrdinalIgnoreCase)) { - settingsUpdateHolder.Value.RulesToAdd.Clear(); - settingsUpdateHolder.Value.RulesToRemove.Clear(); + // We treat removing an exception category to be the same as setting all the exceptions in the category to break unhandled. + EXCEPTION_INFO setExceptionInfo = exceptionInfo; + setExceptionInfo.dwState = enum_EXCEPTION_STATE.EXCEPTION_STOP_SECOND_CHANCE; + SetException(ref setExceptionInfo); + } + else + { + string exceptionName = GetExceptionId(exceptionInfo.bstrExceptionName, exceptionInfo.dwCode); + + if (!IsSupportedException(exceptionName)) + { + return; + } - settingsUpdateHolder.Value.NewCategoryState = categorySettings.DefaultCategoryState; - foreach (var defaultRule in categorySettings.DefaultRules) + using (var settingsUpdateHolder = categorySettings.GetSettingsUpdate()) { - settingsUpdateHolder.Value.RulesToAdd.Add(defaultRule.Key, defaultRule.Value); + settingsUpdateHolder.Value.RulesToRemove.Add(exceptionName); } } } } - public void RemoveSetException(ref EXCEPTION_INFO exceptionInfo) + public void SetException(ref EXCEPTION_INFO exceptionInfo) { - ExceptionCategorySettings categorySettings; - if (!_categoryMap.TryGetValue(exceptionInfo.guidType, out categorySettings)) + if (_canProcessExceptions) { - return; // not a category that we care about - } + ExceptionCategorySettings categorySettings; + if (!_categoryMap.TryGetValue(exceptionInfo.guidType, out categorySettings)) + { + return; // not a category that we care about + } - if (categorySettings.CategoryName.Equals(exceptionInfo.bstrExceptionName, StringComparison.OrdinalIgnoreCase)) - { - // We treat removing an exception category to be the same as setting all the exceptions in the category to break unhandled. - EXCEPTION_INFO setExceptionInfo = exceptionInfo; - setExceptionInfo.dwState = enum_EXCEPTION_STATE.EXCEPTION_STOP_SECOND_CHANCE; - SetException(ref setExceptionInfo); - } - else - { - string exceptionName = GetExceptionId(exceptionInfo.bstrExceptionName, exceptionInfo.dwCode); + var newState = ToExceptionBreakpointState(exceptionInfo.dwState); - if (!IsSupportedException(exceptionName)) + if (categorySettings.CategoryName.Equals(exceptionInfo.bstrExceptionName, StringComparison.OrdinalIgnoreCase)) { - return; - } + // Setting the exception category will clear all the existing rules in that category - using (var settingsUpdateHolder = categorySettings.GetSettingsUpdate()) + SetCategory(categorySettings, newState); + } + else { - settingsUpdateHolder.Value.RulesToAdd.Remove(exceptionName); - settingsUpdateHolder.Value.RulesToRemove.Add(exceptionName); + string exceptionName = GetExceptionId(exceptionInfo.bstrExceptionName, exceptionInfo.dwCode); + + if (!IsSupportedException(exceptionName)) + { + return; + } + + using (var settingsUpdateHolder = categorySettings.GetSettingsUpdate()) + { + settingsUpdateHolder.Value.RulesToRemove.Remove(exceptionName); + settingsUpdateHolder.Value.RulesToAdd[exceptionName] = newState; + } } } } - public void SetException(ref EXCEPTION_INFO exceptionInfo) + public void SetAllExceptions(enum_EXCEPTION_STATE dwState) { - ExceptionCategorySettings categorySettings; - if (!_categoryMap.TryGetValue(exceptionInfo.guidType, out categorySettings)) - { - return; // not a category that we care about - } - - var newState = ToExceptionBreakpointState(exceptionInfo.dwState); - - if (categorySettings.CategoryName.Equals(exceptionInfo.bstrExceptionName, StringComparison.OrdinalIgnoreCase)) + if (_canProcessExceptions) { - // Setting the exception category will clear all the existing rules in that category + var newState = ToExceptionBreakpointState(dwState); - SetCategory(categorySettings, newState); - } - else - { - string exceptionName = GetExceptionId(exceptionInfo.bstrExceptionName, exceptionInfo.dwCode); - - if (!IsSupportedException(exceptionName)) - { - return; - } - - using (var settingsUpdateHolder = categorySettings.GetSettingsUpdate()) + foreach (var pair in _categoryMap) { - settingsUpdateHolder.Value.RulesToRemove.Remove(exceptionName); - settingsUpdateHolder.Value.RulesToAdd[exceptionName] = newState; + SetCategory(pair.Value, newState); } } } - public void SetAllExceptions(enum_EXCEPTION_STATE dwState) + public bool TryGetExceptionBreakpoint(string bkptno, out string exceptionName, out Guid exceptionCategoryGuid) { - var newState = ToExceptionBreakpointState(dwState); - - foreach (var pair in _categoryMap) + exceptionName = null; + exceptionCategoryGuid = Guid.Empty; + ExceptionCategorySettings categorySettings; + if (_categoryMap.TryGetValue(CppExceptionCategoryGuid, out categorySettings)) { - SetCategory(pair.Value, newState); + ulong breakpointNumber = Convert.ToUInt32(bkptno, CultureInfo.InvariantCulture); + lock (categorySettings.CurrentRules) + { + exceptionName = categorySettings.CurrentRules.FirstOrDefault(pair => pair.Value == breakpointNumber).Key; + if (exceptionName != null) + { + if (exceptionName.Length < 1 || exceptionName == "*") // if exceptionName is "*", the exceptions category is selected + { + exceptionName = categorySettings.CategoryName; + } + exceptionCategoryGuid = CppExceptionCategoryGuid; + return true; + + } + } } + return false; } private static void SetCategory(ExceptionCategorySettings categorySettings, ExceptionBreakpointStates newState) @@ -398,7 +440,7 @@ private async Task FlushSettingsUpdates() continue; } - await UpdateCatagory(categoryPair.Key, categorySettings, settingsUpdate); + await UpdateCategory(categoryPair.Key, categorySettings, settingsUpdate); } } catch (MIException e) @@ -425,7 +467,7 @@ private async Task FlushSettingsUpdates() } } - private async Task UpdateCatagory(Guid categoryId, ExceptionCategorySettings categorySettings, SettingsUpdates updates) + private async Task UpdateCategory(Guid categoryId, ExceptionCategorySettings categorySettings, SettingsUpdates updates) { // Update the category if (updates.NewCategoryState.HasValue && ( @@ -434,13 +476,33 @@ private async Task UpdateCatagory(Guid categoryId, ExceptionCategorySettings cat { ExceptionBreakpointStates newCategoryState = updates.NewCategoryState.Value; categorySettings.CategoryState = newCategoryState; - categorySettings.CurrentRules.Clear(); - IEnumerable breakpointIds = await _commandFactory.SetExceptionBreakpoints(categoryId, null, newCategoryState); + // remove exception breakpoints before categorySettings.CurrentRules is cleared + await _commandFactory.RemoveExceptionBreakpoint(categoryId, categorySettings.CurrentRules.Values); + + lock (categorySettings.CurrentRules) + { + categorySettings.CurrentRules.Clear(); + } + + // only do a generic catch throw if C++ exceptions category is checked if (newCategoryState != ExceptionBreakpointStates.None) { - ulong breakpointId = breakpointIds.Single(); - categorySettings.CurrentRules.Add("*", breakpointId); + try + { + IEnumerable breakpointIds = await _commandFactory.SetExceptionBreakpoints(categoryId, null, newCategoryState); + ulong breakpointId = breakpointIds.Single(); + lock (categorySettings.CurrentRules) + { + categorySettings.CurrentRules.Add("*", breakpointId); + } + } + catch (NotSupportedException) + { + _canProcessExceptions = false; + _callback.OnOutputMessage(new OutputMessage(ResourceStrings.Warning_ExceptionsNotSupported, enum_MESSAGETYPE.MT_OUTPUTSTRING, OutputMessage.Severity.Warning)); + return; + } } } @@ -449,14 +511,17 @@ private async Task UpdateCatagory(Guid categoryId, ExceptionCategorySettings cat { // Detach these exceptions from 'CurrentRules' List breakpointsToRemove = new List(); - foreach (string exceptionToRemove in updates.RulesToRemove) + lock (categorySettings.CurrentRules) { - ulong breakpointId; - if (!categorySettings.CurrentRules.TryGetValue(exceptionToRemove, out breakpointId)) - continue; + foreach (string exceptionToRemove in updates.RulesToRemove) + { + ulong breakpointId; + if (!categorySettings.CurrentRules.TryGetValue(exceptionToRemove, out breakpointId)) + continue; - categorySettings.CurrentRules.Remove(exceptionToRemove); - breakpointsToRemove.Add(breakpointId); + categorySettings.CurrentRules.Remove(exceptionToRemove); + breakpointsToRemove.Add(breakpointId); + } } if (breakpointsToRemove.Count > 0) @@ -473,24 +538,70 @@ private async Task UpdateCatagory(Guid categoryId, ExceptionCategorySettings cat if (grouping.Key == categorySettings.CategoryState) { // A request to set an exception to the same state as the category is redundant unless we have previously changed the state of that exception to something else - exceptionNames = exceptionNames.Intersect(categorySettings.CurrentRules.Keys); + lock (categorySettings.CurrentRules) + { + exceptionNames = exceptionNames.Intersect(categorySettings.CurrentRules.Keys); + } if (!exceptionNames.Any()) { continue; // no exceptions left, so ignore this group } } - IEnumerable breakpointIds = await _commandFactory.SetExceptionBreakpoints(categoryId, exceptionNames, grouping.Key); + bool isBreakThrown = grouping.Key.HasFlag(ExceptionBreakpointStates.BreakThrown); - int count = exceptionNames.Zip(breakpointIds, (exceptionName, breakpointId) => + if (!categorySettings.CategoryState.HasFlag(ExceptionBreakpointStates.BreakThrown) && isBreakThrown) { - categorySettings.CurrentRules[exceptionName] = breakpointId; - return 1; - }).Sum(); + try + { + IEnumerable breakpointIds = await _commandFactory.SetExceptionBreakpoints(categoryId, exceptionNames, grouping.Key); + + lock (categorySettings.CurrentRules) + { + int count = exceptionNames.Zip(breakpointIds, (exceptionName, breakpointId) => + { + // remove old breakpoint if exceptionName is in categorySettings.CurrentRules.Keys + if (categorySettings.CurrentRules.ContainsKey(exceptionName)) + { + _commandFactory.RemoveExceptionBreakpoint(categoryId, new ulong[] { categorySettings.CurrentRules[exceptionName] }); + } + categorySettings.CurrentRules[exceptionName] = breakpointId; + return 1; + }).Sum(); #if DEBUG - Debug.Assert(count == exceptionNames.Count()); + Debug.Assert(count == exceptionNames.Count()); #endif + } + } + catch (NotSupportedException) + { + _canProcessExceptions = false; + _callback.OnOutputMessage(new OutputMessage(ResourceStrings.Warning_ExceptionsNotSupported, enum_MESSAGETYPE.MT_OUTPUTSTRING, OutputMessage.Severity.Warning)); + return; + } + } + else if (grouping.Key != categorySettings.CategoryState && !isBreakThrown) + { + // Send warning when there are unchecked exceptions in a checked exceptions category + _callback.OnOutputMessage(new OutputMessage(ResourceStrings.Warning_UncheckedExceptionsInCheckedCategory, enum_MESSAGETYPE.MT_OUTPUTSTRING, OutputMessage.Severity.Warning)); + } + if (!isBreakThrown) + { + ulong breakpointId; + lock (categorySettings.CurrentRules) + { + foreach (string exceptionName in exceptionNames) + { + if (!categorySettings.CurrentRules.TryGetValue(exceptionName, out breakpointId)) + continue; + + _commandFactory.RemoveExceptionBreakpoint(categoryId, new ulong[] { breakpointId }); + categorySettings.CurrentRules.Remove(exceptionName); + } + } + } + } } diff --git a/src/MIDebugEngine/ResourceStrings.Designer.cs b/src/MIDebugEngine/ResourceStrings.Designer.cs index 055a119b2..fb3627d78 100755 --- a/src/MIDebugEngine/ResourceStrings.Designer.cs +++ b/src/MIDebugEngine/ResourceStrings.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.MIDebugEngine { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class ResourceStrings { @@ -409,6 +409,16 @@ internal static string Warning_DarwinDebuggerUnsigned { } } + /// + /// Looks up a localized string similar to Warning: Exceptions are not supported in this scenario. + ///. + /// + internal static string Warning_ExceptionsNotSupported { + get { + return ResourceManager.GetString("Warning_ExceptionsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Warning: Source file '{0}' is newer than module file '{1}'.. /// @@ -418,6 +428,16 @@ internal static string Warning_SourceFileOutOfDate_Arg2 { } } + /// + /// Looks up a localized string similar to Warning: One or more C++ exceptions are unchecked while '<All C++ Exceptions not in this list>' is enabled. This is unsupported for GDB and the debugger will break on all C++ exceptions. + ///. + /// + internal static string Warning_UncheckedExceptionsInCheckedCategory { + get { + return ResourceManager.GetString("Warning_UncheckedExceptionsInCheckedCategory", resourceCulture); + } + } + /// /// Looks up a localized string similar to Warning: Debuggee TargetArchitecture not detected, assuming x86_64.. /// diff --git a/src/MIDebugEngine/ResourceStrings.resx b/src/MIDebugEngine/ResourceStrings.resx index 88b9adb6e..0ef1beb6b 100755 --- a/src/MIDebugEngine/ResourceStrings.resx +++ b/src/MIDebugEngine/ResourceStrings.resx @@ -253,4 +253,13 @@ See https://aka.ms/miengine-gdb-troubleshooting for details. Debugging will now abort. + + Warning: One or more C++ exceptions are unchecked while '<All C++ Exceptions not in this list>' is enabled. This is unsupported for GDB and the debugger will break on all C++ exceptions. + + S + + + Warning: Exceptions are not supported in this scenario. + + \ No newline at end of file diff --git a/src/OpenDebugAD7/cppdbg.ad7Engine.json b/src/OpenDebugAD7/cppdbg.ad7Engine.json index 292f7c5b3..8f7b41d31 100644 --- a/src/OpenDebugAD7/cppdbg.ad7Engine.json +++ b/src/OpenDebugAD7/cppdbg.ad7Engine.json @@ -3,5 +3,13 @@ "engineClassName": "Microsoft.MIDebugEngine.AD7Engine", "conditionalBP": true, "functionBP": true, - "clipboardContext": true + "clipboardContext": true, + "exceptionSettings": { + "categories": [ + { + "name": "CppExceptionCategory", + "id": "3A12D0B7-C26C-11D0-B442-00A0244A1DD2" + } + ] + } } \ No newline at end of file