diff --git a/.github/workflows/Build-And-Test.yml b/.github/workflows/Build-And-Test.yml index 731711a59..73cfff4f4 100644 --- a/.github/workflows/Build-And-Test.yml +++ b/.github/workflows/Build-And-Test.yml @@ -22,20 +22,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Install .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.1 - name: Setup NuGet.exe for use with actions - uses: NuGet/setup-nuget@v1.0.5 + uses: NuGet/setup-nuget@v1 - name: Build MIDebugEngine run: | @@ -44,7 +44,7 @@ jobs: Configuration: ${{ matrix.configuration }} - name: Setup VSTest.console.exe - uses: darenm/Setup-VSTest@v1 + uses: darenm/Setup-VSTest@v1.2 - name: Run VS Extension tests run: vstest.console.exe ${{ github.workspace }}\bin\${{ matrix.configuration }}\MICoreUnitTests.dll ${{ github.workspace }}\bin\${{ matrix.configuration }}\JDbgUnitTests.dll ${{ github.workspace }}\bin\${{ matrix.configuration }}\SSHDebugTests.dll ${{ github.workspace }}\bin\${{ matrix.configuration }}\MIDebugEngineUnitTests.dll @@ -54,20 +54,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Install .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.1 - name: Setup NuGet.exe for use with actions - uses: NuGet/setup-nuget@v1.0.5 + uses: NuGet/setup-nuget@v1 - name: Build MIDebugEngine run: | @@ -102,7 +102,7 @@ jobs: dotnet test $CppTestsPath --logger "trx;LogFileName=$ResultsPath" - name: 'Upload Test Results' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ always() }} with: name: win_msys2_x64_results @@ -112,12 +112,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Install .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x @@ -143,7 +143,7 @@ jobs: ${{ github.workspace }}/eng/Scripts/CI-Test.sh - name: 'Upload Test Results' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ always() }} with: name: linux_x64_results @@ -153,12 +153,12 @@ jobs: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Install .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x @@ -172,7 +172,7 @@ jobs: ${{ github.workspace }}/eng/Scripts/CI-Test.sh - name: 'Upload Test Results' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ always() }} with: name: osx_x64_results diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e138ec5d6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/eng/pipelines/MIDebugEngine-CI.yml b/eng/pipelines/MIDebugEngine-CI.yml index c41558db5..3f3bc40f4 100644 --- a/eng/pipelines/MIDebugEngine-CI.yml +++ b/eng/pipelines/MIDebugEngine-CI.yml @@ -1,5 +1,15 @@ --- name: $(Date:yyyMMdd).$(Rev:r) + +schedules: +# Run on the 1st and 15th of every month +- cron: 30 1 1,15 * * + displayName: Biweekly Build + branches: + include: + - main + always: true # Run even if there are no code changes + stages: - stage: CI dependsOn: [] @@ -8,6 +18,8 @@ stages: value: test - name: TeamName value: MDDDebugger + - name: Codeql.Enabled + value: true jobs: - template: ./jobs/VSEngSS-MicroBuild2022-1ES.job.yml parameters: diff --git a/eng/pipelines/resources/TSAConfig.json b/eng/pipelines/resources/TSAConfig.json new file mode 100644 index 000000000..bc6b0b5ca --- /dev/null +++ b/eng/pipelines/resources/TSAConfig.json @@ -0,0 +1,9 @@ +{ + "codebaseName": "MIEngine", + "notificationAliases": ["vsdbgnft@microsoft.com"], + "instanceUrl": "https://devdiv.visualstudio.com", + "projectName": "DevDiv", + "areaPath": "DevDiv\\VS Diagnostics\\Debugger - XPlat\\Cpp", + "iterationPath": "DevDiv", + "allTools": true +} \ No newline at end of file diff --git a/eng/pipelines/resources/falsepositives.gdnsuppress b/eng/pipelines/resources/falsepositives.gdnsuppress new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/eng/pipelines/resources/falsepositives.gdnsuppress @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/eng/pipelines/steps/APIScan.yml b/eng/pipelines/steps/APIScan.yml index 7633c3b58..4aa947f75 100644 --- a/eng/pipelines/steps/APIScan.yml +++ b/eng/pipelines/steps/APIScan.yml @@ -1,21 +1,33 @@ parameters: - FolderToScan: '$(Pipeline.Workspace)\Lab.Release' + SourceFolder: '$(Pipeline.Workspace)\Lab.Release' steps: - task: CopyFiles@2 displayName: 'Copy Files to: $(Pipeline.Workspace)\ApiScanFiles' inputs: - SourceFolder: ${{ parameters.FolderToScan }} + SourceFolder: ${{ parameters.SourceFolder }} Contents: | **\*Microsoft@(*.dll|*.pdb|*.exe) - **\*Newtonsoft@(*.dll|*.pdb|*.exe) **\*OpenDebugAD7@(*.dll|*.pdb|*.exe) **\*WindowsDebugLauncher@(*.dll|*.pdb|*.exe) + **\Microsoft.VisualStudio.Debugger.Interop.UnixPortSupplier.DesignTime.dll !**\*.resources.dll + !**\Microsoft.VisualStudio.Debugger.Interop* + !**\vscode\Microsoft.VisualStudio.Interop.dll + !**\vscode\Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.dll TargetFolder: '$(Pipeline.Workspace)\ApiScanFiles' CleanTargetFolder: true OverWrite: true +# This gets excluded by !**\Microsoft.VisualStudio.Debugger.Interop* but we create Microsoft.VisualStudio.Debugger.Interop.UnixPortSupplier.DesignTime.dll. +- task: CopyFiles@2 + displayName: 'Copy UnixPortSupplier to: $(Pipeline.Workspace)\ApiScanFiles' + inputs: + SourceFolder: ${{ parameters.SourceFolder }} + Contents: | + **\Microsoft.VisualStudio.Debugger.Interop.UnixPortSupplier.DesignTime.dll + TargetFolder: '$(Pipeline.Workspace)\ApiScanFiles' + - task: securedevelopmentteam.vss-secure-development-tools.build-task-apiscan.APIScan@2 displayName: 'Run APIScan' inputs: diff --git a/eng/pipelines/steps/PostAnalysis.yml b/eng/pipelines/steps/PostAnalysis.yml new file mode 100644 index 000000000..904e38b58 --- /dev/null +++ b/eng/pipelines/steps/PostAnalysis.yml @@ -0,0 +1,19 @@ +parameters: + GdnSuppressionFiles: $(Build.SourcesDirectory)\eng\pipelines\resources\falsepositives.gdnsuppress + +steps: +- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@2 + displayName: 🏋️‍♀️ Break on compliance issues + inputs: + GdnBreakAllTools: true + GdnBreakSuppressionFiles: ${{ parameters.GdnSuppressionFiles }} + GdnBreakSuppressionSets: falsepositives + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@2 + displayName: 📝 Generate Guardian Suppressions File + inputs: + GdnBreakAllTools: true + GdnBreakOutputSuppressionFile: $(Build.ArtifactStagingDirectory)\GuardianSuppressions + GdnBreakOutputSuppressionSet: falsepositives + continueOnError: true + condition: failed() \ No newline at end of file diff --git a/eng/pipelines/tasks/AntiMalware.yml b/eng/pipelines/tasks/AntiMalware.yml new file mode 100644 index 000000000..f6a85b339 --- /dev/null +++ b/eng/pipelines/tasks/AntiMalware.yml @@ -0,0 +1,21 @@ +parameters: + SourcePath: $(Build.SourcesDirectory) + ArtifactPath: $(Pipeline.Workspace) + +steps: +- task: AntiMalware@4 + displayName: 🔎 Run AntiMalware on source + inputs: + InputType: Basic + ScanType: CustomScan + FileDirPath: ${{ parameters.SourcePath }} + continueOnError: true + +- task: AntiMalware@4 + displayName: 🔎 Run AntiMalware on artifacts + inputs: + InputType: Basic + ScanType: CustomScan + FileDirPath: ${{ parameters.ArtifactPath }} + DisableRemediation: false + continueOnError: true \ No newline at end of file diff --git a/eng/pipelines/tasks/CredScan.yml b/eng/pipelines/tasks/CredScan.yml index 27deee744..7cac0e555 100644 --- a/eng/pipelines/tasks/CredScan.yml +++ b/eng/pipelines/tasks/CredScan.yml @@ -3,4 +3,5 @@ steps: displayName: 'Run CredScan' inputs: outputFormat: pre - debugMode: false \ No newline at end of file + debugMode: false + continueOnError: true \ No newline at end of file diff --git a/eng/pipelines/tasks/PSScriptAnalyzer.yml b/eng/pipelines/tasks/PSScriptAnalyzer.yml new file mode 100644 index 000000000..c1e50459b --- /dev/null +++ b/eng/pipelines/tasks/PSScriptAnalyzer.yml @@ -0,0 +1,7 @@ +steps: +- task: PSScriptAnalyzer@1 + displayName: 🔎 Run PSScriptAnalyzer + inputs: + Path: '$(Build.SourcesDirectory)' + Settings: required + Recurse: true \ No newline at end of file diff --git a/eng/pipelines/tasks/TSAUpload.yml b/eng/pipelines/tasks/TSAUpload.yml new file mode 100644 index 000000000..a71baa2d4 --- /dev/null +++ b/eng/pipelines/tasks/TSAUpload.yml @@ -0,0 +1,10 @@ +parameters: + TSAConfigFilePath: $(Build.SourcesDirectory)\eng\pipelines\resources\TSAconfig.json + +steps: +- task: securedevelopmentteam.vss-secure-development-tools.build-task-uploadtotsa.TSAUpload@2 + displayName: 📢 Create bugs for compliance tools results + inputs: + GdnPublishTsaOnboard: true + GdnPublishTsaConfigFile: ${{ parameters.TSAConfigFilePath }} # All relevant settings are in this file. + condition: succeededOrFailed() \ No newline at end of file diff --git a/eng/pipelines/templates/CodeAnalysis.template.yml b/eng/pipelines/templates/CodeAnalysis.template.yml index 20388caaf..84adf530a 100644 --- a/eng/pipelines/templates/CodeAnalysis.template.yml +++ b/eng/pipelines/templates/CodeAnalysis.template.yml @@ -14,13 +14,32 @@ steps: parameters: FolderToScan: $(Pipeline.Workspace)\Lab.Release +- template: ../tasks/AntiMalware.yml + parameters: + SourcePath: $(Build.SourcesDirectory)\src + - template: ../tasks/BinSkim.yml - template: ../tasks/PoliCheck.yml +- template: ../tasks/PSScriptAnalyzer.yml + - template: ../tasks/SdtReport.yml - template: ../tasks/PublishSecurityAnalysisLogs.yml parameters: ArtifactName: 'CodeAnalysis' + +## Create any bugs associated with the results. +- template: ../tasks/TSAUpload.yml + +## Finally, break the build if anything was found. This is so we can bring the issue to our attention. +- template: ../steps/PostAnalysis.yml + +- template: ../tasks/PublishPipelineArtifact.yml + parameters: + DisplayName: 🎁 Publish Artifact for Guardian Suppressions + artifactName: Guardian Suppressions + path: $(Build.ArtifactStagingDirectory)\GuardianSuppressions + condition: failed() ... \ No newline at end of file diff --git a/src/MICore/CommandFactories/MICommandFactory.cs b/src/MICore/CommandFactories/MICommandFactory.cs index 3529d37d3..7b8454857 100644 --- a/src/MICore/CommandFactories/MICommandFactory.cs +++ b/src/MICore/CommandFactories/MICommandFactory.cs @@ -35,6 +35,17 @@ public enum ExceptionBreakpointStates BreakThrown = 0x2 } + /// + /// The signals that are using for async-break. + /// None will be used for no signal or signals that are not listed in the enum + /// + public enum AsyncBreakSignal + { + None = 0, + SIGTRAP = 2, + SIGINT = 5 + } + public abstract class MICommandFactory { protected Debugger _debugger; @@ -43,6 +54,8 @@ public abstract class MICommandFactory public abstract string Name { get; } + internal int MajorVersion { get; set; } + public static MICommandFactory GetInstance(MIMode mode, Debugger debugger) { MICommandFactory commandFactory; @@ -108,16 +121,18 @@ public virtual async Task ThreadInfo(uint? threadid = null) public async Task StackInfoDepth(int threadId, int maxDepth = 1000, ResultClass resultClass = ResultClass.done) { - string command = string.Format(CultureInfo.InvariantCulture, @"-stack-info-depth {0}", maxDepth); - Results results = await ThreadCmdAsync(command, resultClass, threadId); + string command = "-stack-info-depth"; + string args = string.Format(CultureInfo.InvariantCulture, $@"{maxDepth}"); + Results results = await ThreadCmdAsync(command, args, resultClass, threadId); return results; } public async Task StackListFrames(int threadId, uint lowFrameLevel, uint highFrameLevel = 1000) { - string command = string.Format(CultureInfo.InvariantCulture, @"-stack-list-frames {0} {1}", lowFrameLevel, highFrameLevel); - Results results = await ThreadCmdAsync(command, ResultClass.done, threadId); + string command = "-stack-list-frames"; + string args = string.Format(CultureInfo.InvariantCulture, $@"{lowFrameLevel} {highFrameLevel}"); + Results results = await ThreadCmdAsync(command, args, ResultClass.done, threadId); ListValue list = results.Find("stack"); if (list is ResultListValue) @@ -153,9 +168,10 @@ public async Task StackInfoFrame() /// public async Task StackListLocals(PrintValue printValues, int threadId, uint frameLevel) { - string cmd = string.Format(CultureInfo.InvariantCulture, @"-stack-list-locals {0}", (int)printValues); + string cmd = "-stack-list-locals"; + string args = string.Format(CultureInfo.InvariantCulture, $@"{(int)printValues}"); - Results localsResults = await ThreadFrameCmdAsync(cmd, ResultClass.done, threadId, frameLevel); + Results localsResults = await ThreadFrameCmdAsync(cmd, args, ResultClass.done, threadId, frameLevel); return localsResults.Find("locals"); } @@ -169,8 +185,9 @@ public async Task StackListLocals(PrintValue printValues, int threa /// This returns an array of results of frames, which contains a level and an args array. public virtual async Task StackListArguments(PrintValue printValues, int threadId, uint lowFrameLevel, uint hiFrameLevel) { - string cmd = string.Format(CultureInfo.InvariantCulture, @"-stack-list-arguments {0} {1} {2}", (int)printValues, lowFrameLevel, hiFrameLevel); - Results argumentsResults = await ThreadCmdAsync(cmd, ResultClass.done, threadId); + string command = "-stack-list-arguments"; + string args = string.Format(CultureInfo.InvariantCulture, $@"{(int)printValues} {lowFrameLevel} {hiFrameLevel}"); + Results argumentsResults = await ThreadCmdAsync(command, args, ResultClass.done, threadId); return argumentsResults.Find("stack-args").IsEmpty() ? new TupleValue[0] @@ -202,9 +219,9 @@ public async Task StackListArguments(PrintValue printValues, int thre /// Returns an array of results for variables public async Task StackListVariables(PrintValue printValues, int threadId, uint frameLevel) { - string cmd = string.Format(CultureInfo.InvariantCulture, @"-stack-list-variables {0}", (int)printValues); - - Results variablesResults = await ThreadFrameCmdAsync(cmd, ResultClass.done, threadId, frameLevel); + string cmd = "-stack-list-variables"; + string args = string.Format(CultureInfo.InvariantCulture, $@"{(int)printValues}"); + Results variablesResults = await ThreadFrameCmdAsync(cmd, args, ResultClass.done, threadId, frameLevel); return variablesResults.Find("variables"); } @@ -215,31 +232,36 @@ public async Task StackListVariables(PrintValue printValues, int public async Task ExecStep(int threadId, ResultClass resultClass = ResultClass.running) { string command = "-exec-step"; - await ThreadFrameCmdAsync(command, resultClass, threadId, 0); + string args = string.Empty; + await ThreadFrameCmdAsync(command, args, resultClass, threadId, 0); } public async Task ExecNext(int threadId, ResultClass resultClass = ResultClass.running) { string command = "-exec-next"; - await ThreadFrameCmdAsync(command, resultClass, threadId, 0); + string args = string.Empty; + await ThreadFrameCmdAsync(command, args, resultClass, threadId, 0); } public async Task ExecFinish(int threadId, ResultClass resultClass = ResultClass.running) { string command = "-exec-finish"; - await ThreadFrameCmdAsync(command, resultClass, threadId, 0); + string args = string.Empty; + await ThreadFrameCmdAsync(command, args, resultClass, threadId, 0); } public async Task ExecStepInstruction(int threadId, ResultClass resultClass = ResultClass.running) { string command = "-exec-step-instruction"; - await ThreadFrameCmdAsync(command, resultClass, threadId, 0); + string args = string.Empty; + await ThreadFrameCmdAsync(command, args, resultClass, threadId, 0); } public async Task ExecNextInstruction(int threadId, ResultClass resultClass = ResultClass.running) { string command = "-exec-next-instruction"; - await ThreadFrameCmdAsync(command, resultClass, threadId, 0); + string args = string.Empty; + await ThreadFrameCmdAsync(command, args, resultClass, threadId, 0); } /// @@ -285,15 +307,17 @@ public async Task DataListRegisterNames() public async Task DataListRegisterValues(int threadId) { - string command = "-data-list-register-values x"; - Results results = await ThreadCmdAsync(command, ResultClass.done, threadId); + string command = "-data-list-register-values"; + string args = "x"; + Results results = await ThreadCmdAsync(command, args, ResultClass.done, threadId); return results.Find("register-values").AsArray(); } public async Task DataEvaluateExpression(string expr, int threadId, uint frame) { - string command = "-data-evaluate-expression \"" + expr + "\""; - Results results = await ThreadFrameCmdAsync(command, ResultClass.None, threadId, frame); + string command = "-data-evaluate-expression"; + string args = $"\"{expr}\""; + Results results = await ThreadFrameCmdAsync(command, args, ResultClass.None, threadId, frame); return results.FindString("value"); } @@ -333,8 +357,9 @@ public virtual Task SetStepFiltering(bool enabled) public virtual async Task VarCreate(string expression, int threadId, uint frameLevel, enum_EVALFLAGS dwFlags, ResultClass resultClass = ResultClass.done) { string quoteEscapedExpression = EscapeQuotes(HandleInvalidChars(expression)); - string command = string.Format(CultureInfo.InvariantCulture, "-var-create - * \"{0}\"", quoteEscapedExpression); - Results results = await ThreadFrameCmdAsync(command, resultClass, threadId, frameLevel); + string command = "-var-create"; + string args = string.Format(CultureInfo.InvariantCulture, $" - * \"{quoteEscapedExpression}\""); + Results results = await ThreadFrameCmdAsync(command, args, resultClass, threadId, frameLevel); return results; } @@ -635,8 +660,8 @@ internal string HandleInvalidChars(string str) #region Other - abstract protected Task ThreadFrameCmdAsync(string command, ResultClass expectedResultClass, int threadId, uint frameLevel); - abstract protected Task ThreadCmdAsync(string command, ResultClass expectedResultClass, int threadId); + abstract protected Task ThreadFrameCmdAsync(string command, string args, ResultClass expectedResultClass, int threadId, uint frameLevel); + abstract protected Task ThreadCmdAsync(string command, string args, ResultClass expectedResultClass, int threadId); abstract public string GetSetEnvironmentVariableCommand(string name, string value); @@ -652,19 +677,22 @@ public virtual bool SupportsFrameFormatting get { return false; } } - public virtual bool IsAsyncBreakSignal(Results results) + public virtual AsyncBreakSignal GetAsyncBreakSignal(Results results) { - bool isAsyncBreak = false; - if (results.TryFindString("reason") == "signal-received") { - if (results.TryFindString("signal-name") == "SIGTRAP") + string signalName = results.TryFindString("signal-name"); + if (signalName == "SIGTRAP") { - isAsyncBreak = true; + return MICore.AsyncBreakSignal.SIGTRAP; + } + else if (signalName == "SIGINT") + { + return MICore.AsyncBreakSignal.SIGINT; } } - return isAsyncBreak; + return MICore.AsyncBreakSignal.None; } public Results IsModuleLoad(string cmd) @@ -697,6 +725,24 @@ public virtual bool SupportsBreakpointChecksums() { return false; } + + /// + /// Get the set of features supported by the underlying debugger. + /// + public virtual Task> GetFeatures() + { + return Task.FromResult(new HashSet()); + } + + /// + /// True if the underlying debugger excludes the printing of references to + /// compound types when PrintValue.SimpleValues is used as an argument to + /// StackListLocals(), StackListArguments() and StackListVariables(). + /// + public virtual Task SupportsSimpleValuesExcludesRefTypes() + { + return Task.FromResult(false); + } #endregion } } diff --git a/src/MICore/CommandFactories/gdb.cs b/src/MICore/CommandFactories/gdb.cs index 65246bbfd..83100ab3e 100644 --- a/src/MICore/CommandFactories/gdb.cs +++ b/src/MICore/CommandFactories/gdb.cs @@ -48,7 +48,7 @@ public override bool AllowCommandsWhileRunning() return false; } - protected override async Task ThreadFrameCmdAsync(string command, ResultClass expectedResultClass, int threadId, uint frameLevel) + protected override async Task ThreadFrameCmdAsync(string command, string args, ResultClass expectedResultClass, int threadId, uint frameLevel) { // first aquire an exclusive lock. This is used as we don't want to fight with other commands that also require the current // thread to be set to a particular value @@ -56,8 +56,20 @@ protected override async Task ThreadFrameCmdAsync(string command, Resul try { - await ThreadSelect(threadId, lockToken); - await StackSelectFrame(frameLevel, lockToken); + + string threadFrameCommand; + // With source code of gdb 7.0.0, the --thread and --frame options were introduced and -thread-select and + // -stack-select-frame were deprecated + if (MajorVersion < 7) + { + await ThreadSelect(threadId, lockToken); + await StackSelectFrame(frameLevel, lockToken); + threadFrameCommand = string.Format(CultureInfo.InvariantCulture, $@"{command} {args}"); + } + else + { + threadFrameCommand = string.Format(CultureInfo.InvariantCulture, $@"{command} --thread {threadId} --frame {frameLevel} {args}"); + } // Before we execute the provided command, we need to switch to a shared lock. This is because the provided // command may be an expression evaluation command which could be long running, and we don't want to hold the @@ -65,7 +77,7 @@ protected override async Task ThreadFrameCmdAsync(string command, Resul lockToken.ConvertToSharedLock(); lockToken = null; - return await _debugger.CmdAsync(command, expectedResultClass); + return await _debugger.CmdAsync(threadFrameCommand, expectedResultClass); } finally { @@ -82,7 +94,7 @@ protected override async Task ThreadFrameCmdAsync(string command, Resul } } - protected override async Task ThreadCmdAsync(string command, ResultClass expectedResultClass, int threadId) + protected override async Task ThreadCmdAsync(string command, string args, ResultClass expectedResultClass, int threadId) { // first aquire an exclusive lock. This is used as we don't want to fight with other commands that also require the current // thread to be set to a particular value @@ -90,7 +102,18 @@ protected override async Task ThreadCmdAsync(string command, ResultClas try { - await ThreadSelect(threadId, lockToken); + string threadCommand; + // With source code of gdb 7.0.0, the --thread option was introduced and -thread-select + // was deprecated + if (MajorVersion < 7) + { + await ThreadSelect(threadId, lockToken); + threadCommand = string.Format(CultureInfo.InvariantCulture, $@"{command} {args}"); + } + else + { + threadCommand = string.Format(CultureInfo.InvariantCulture, $@"{command} --thread {threadId} {args}"); ; + } // Before we execute the provided command, we need to switch to a shared lock. This is because the provided // command may be an expression evaluation command which could be long running, and we don't want to hold the @@ -98,7 +121,7 @@ protected override async Task ThreadCmdAsync(string command, ResultClas lockToken.ConvertToSharedLock(); lockToken = null; - return await _debugger.CmdAsync(command, expectedResultClass); + return await _debugger.CmdAsync(threadCommand, expectedResultClass); } finally { @@ -198,6 +221,18 @@ public override async Task EnableTargetAsyncOption() } } + public override async Task> GetFeatures() + { + Results results = await _debugger.CmdAsync("-list-features", ResultClass.done); + return new HashSet(results.Find("features").AsStrings); + } + + public override async Task SupportsSimpleValuesExcludesRefTypes() + { + HashSet features = await GetFeatures(); + return features.Contains("simple-values-ref-types"); + } + public override async Task Terminate() { // Although the mi documentation states that the correct command to terminate is -exec-abort @@ -289,12 +324,13 @@ public override async Task Catch(string name, bool onlyOnce = false, ResultClass public override async Task AutoComplete(string command, int threadId, uint frameLevel) { - command = "-complete \"" + command + "\""; + string cmd = "-complete"; + string args = $"\"{command}\""; Results res; if (threadId == -1) - res = await _debugger.CmdAsync(command, ResultClass.done); + res = await _debugger.CmdAsync($"{cmd} {args}", ResultClass.done); else - res = await ThreadFrameCmdAsync(command, ResultClass.done, threadId, frameLevel); + res = await ThreadFrameCmdAsync(cmd, args, ResultClass.done, threadId, frameLevel); var matchlist = res.Find("matches"); diff --git a/src/MICore/CommandFactories/lldb.cs b/src/MICore/CommandFactories/lldb.cs index 738a47f5a..b430f3257 100644 --- a/src/MICore/CommandFactories/lldb.cs +++ b/src/MICore/CommandFactories/lldb.cs @@ -76,9 +76,10 @@ public async override Task BuildBreakInsert(string condition, boo public override async Task VarCreate(string expression, int threadId, uint frameLevel, enum_EVALFLAGS dwFlags, ResultClass resultClass = ResultClass.done) { + string command = "-var-create"; string quoteEscapedExpression = EscapeQuotes(expression); - string command = string.Format(CultureInfo.InvariantCulture, "-var-create - - \"{0}\"", quoteEscapedExpression); // use '-' to indicate that "--frame" should be used to determine the frame number - Results results = await ThreadFrameCmdAsync(command, resultClass, threadId, frameLevel); + string args = string.Format(CultureInfo.InvariantCulture, $"- - \"{quoteEscapedExpression}\""); // use '-' to indicate that "--frame" should be used to determine the frame number + Results results = await ThreadFrameCmdAsync(command, args, resultClass, threadId, frameLevel); return results; } @@ -94,16 +95,16 @@ public override async Task VarListChildren(string variableReference, en return results; } - protected override async Task ThreadFrameCmdAsync(string command, ResultClass exepctedResultClass, int threadId, uint frameLevel) + protected override async Task ThreadFrameCmdAsync(string command, string args, ResultClass exepctedResultClass, int threadId, uint frameLevel) { - string threadFrameCommand = string.Format(CultureInfo.InvariantCulture, @"{0} --thread {1} --frame {2}", command, threadId, frameLevel); + string threadFrameCommand = string.Format(CultureInfo.InvariantCulture, $@"{command} {args} --thread {threadId} --frame {frameLevel}"); return await _debugger.CmdAsync(threadFrameCommand, exepctedResultClass); } - protected override async Task ThreadCmdAsync(string command, ResultClass expectedResultClass, int threadId) + protected override async Task ThreadCmdAsync(string command, string args, ResultClass expectedResultClass, int threadId) { - string threadCommand = string.Format(CultureInfo.InvariantCulture, @"{0} --thread {1}", command, threadId); + string threadCommand = string.Format(CultureInfo.InvariantCulture, $@"{command} {args} --thread {threadId}"); return await _debugger.CmdAsync(threadCommand, expectedResultClass); } diff --git a/src/MICore/Debugger.cs b/src/MICore/Debugger.cs index 295b919ca..c3367de6b 100755 --- a/src/MICore/Debugger.cs +++ b/src/MICore/Debugger.cs @@ -283,8 +283,9 @@ private async void OnStopped(Results results) results = results.Add("frame", frameResult.Find("frame")); } - bool fIsAsyncBreak = MICommandFactory.IsAsyncBreakSignal(results); - if (await DoInternalBreakActions(fIsAsyncBreak)) + AsyncBreakSignal signal = MICommandFactory.GetAsyncBreakSignal(results); + bool isAsyncBreak = signal == AsyncBreakSignal.SIGTRAP || (IsUsingExecInterrupt && signal == AsyncBreakSignal.SIGINT); + if (await DoInternalBreakActions(isAsyncBreak)) { return; } @@ -409,6 +410,8 @@ private async Task DoInternalBreakActions(bool fIsAsyncBreak) { CmdContinueAsync(); processContinued = true; + // Reset since this -exec-interrupt was for an internal breakpoint. + IsUsingExecInterrupt = false; } if (firstException != null) @@ -591,7 +594,7 @@ internal bool IsLocalGdbTarget() _launchOptions is LocalLaunchOptions && !IsLocalLaunchUsingServer()); } - private bool IsRemoteGdbTarget() + internal bool IsRemoteGdbTarget() { return MICommandFactory.Mode == MIMode.Gdb && (_launchOptions is PipeLaunchOptions || _launchOptions is UnixShellPortLaunchOptions || @@ -606,6 +609,11 @@ protected bool IsCoreDump } } + /// + /// Flag to indicate that '-exec-interrupt' was used for async-break scenarios. + /// + public bool IsUsingExecInterrupt { get; protected set; } = false; + public async Task CmdTerminate() { if (!_terminating) @@ -749,6 +757,7 @@ public Task CmdBreakInternal() } } + IsUsingExecInterrupt = true; var res = CmdAsync("-exec-interrupt", ResultClass.done); return res.ContinueWith((t) => { @@ -971,6 +980,13 @@ public void OnDebuggerProcessExit(/*OPTIONAL*/ string exitCode) MIDebuggerInitializeFailedException exception; string version = GdbVersionFromLog(); + int majorVersion = -1; + if (!string.IsNullOrWhiteSpace(version)) + { + int.TryParse(version.Split('.').FirstOrDefault(), out majorVersion); + } + MICommandFactory.MajorVersion = majorVersion; + // We can't use IsMinGW or IsCygwin because we never connected to the debugger bool isMinGWOrCygwin = _launchOptions is LocalLaunchOptions && PlatformUtilities.IsWindows() && diff --git a/src/MICore/JsonLaunchOptions.cs b/src/MICore/JsonLaunchOptions.cs index 5193234a8..6277a5633 100644 --- a/src/MICore/JsonLaunchOptions.cs +++ b/src/MICore/JsonLaunchOptions.cs @@ -54,7 +54,7 @@ public abstract partial class BaseOptions /// /// Indicates the console debugger that the MIDebugEngine will connect to. Allowed values are "gdb" "lldb". /// - [JsonProperty("MIMode", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty(nameof(MIMode), DefaultValueHandling = DefaultValueHandling.Ignore)] public string MIMode { get; set; } /// diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index f7b68e0fd..6b9632386 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -52,6 +52,7 @@ internal class DebuggedProcess : MICore.Debugger private IProcessSequence _childProcessHandler; private bool _deleteEntryPointBreakpoint; private string _entryPointBreakpoint = string.Empty; + private bool? _simpleValuesExcludesRefTypes = null; public DebuggedProcess(bool bLaunched, LaunchOptions launchOptions, ISampleEngineCallback callback, WorkerThread worker, BreakpointManager bpman, AD7Engine engine, HostConfigurationStore configStore, HostWaitLoop waitLoop = null) : base(launchOptions, engine.Logger) { @@ -1332,14 +1333,18 @@ private async Task HandleBreakModeEvent(ResultEventArgs results, BreakRequest br else if (reason == "signal-received") { string name = results.Results.TryFindString("signal-name"); + AsyncBreakSignal signal = MICommandFactory.GetAsyncBreakSignal(results.Results); + bool isAsyncBreak = signal == AsyncBreakSignal.SIGTRAP || (IsUsingExecInterrupt && signal == AsyncBreakSignal.SIGINT); if ((name == "SIG32") || (name == "SIG33")) { // we are going to ignore these (Sigma) signals for now CmdContinueAsyncConditional(breakRequest); } - else if (MICommandFactory.IsAsyncBreakSignal(results.Results)) + else if (isAsyncBreak) { _callback.OnAsyncBreakComplete(thread); + // Reset flag for real async break + IsUsingExecInterrupt = false; } else { @@ -1981,8 +1986,26 @@ public async Task> GetParameterInfoOnly(AD7Threa //NOTE: eval is not called public async Task> GetParameterInfoOnly(AD7Thread thread, bool values, bool types, uint low, uint high) { - // If values are requested, request simple values, otherwise we'll use -var-create to get the type of argument it is. - var frames = await MICommandFactory.StackListArguments(values ? PrintValue.SimpleValues : PrintValue.NoValues, thread.Id, low, high); + PrintValue printValue = values ? PrintValue.SimpleValues : PrintValue.NoValues; + if (types && !values) + { + // We want types but not values. There is no PrintValue option for this, but if + // SimpleValues excludes printing values for references to compound types, then + // the fastest approach is to use SimpleValues (and ignore the values). + // Otherwise, the potential performance penalty of fetching values for + // references to compound types is too high, so use NoValues and follow up with + // -var-create to get the types. + if (!_simpleValuesExcludesRefTypes.HasValue) + { + _simpleValuesExcludesRefTypes = await this.MICommandFactory.SupportsSimpleValuesExcludesRefTypes(); + } + if (_simpleValuesExcludesRefTypes.Value) + { + printValue = PrintValue.SimpleValues; + } + } + + var frames = await MICommandFactory.StackListArguments(printValue, thread.Id, low, high); List parameters = new List(); foreach (var f in frames) diff --git a/src/MIDebugEngine/Natvis.Impl/Natvis.cs b/src/MIDebugEngine/Natvis.Impl/Natvis.cs index afe8ed237..5091a78ae 100755 --- a/src/MIDebugEngine/Natvis.Impl/Natvis.cs +++ b/src/MIDebugEngine/Natvis.Impl/Natvis.cs @@ -676,12 +676,8 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable) for (uint index = 0; index < requestedSize; ++index) { - string displayName = (startIndex + index).ToString(CultureInfo.InvariantCulture); - if (rank > 1) - { - displayName = GetDisplayNameFromArrayIndex(index, rank, dimensions, isForward); - } - + uint currentOffsetIndex = startIndex + index; + string displayName = rank > 1 ? GetDisplayNameFromArrayIndex(currentOffsetIndex, rank, dimensions, isForward) : currentOffsetIndex.ToString(CultureInfo.InvariantCulture); children.Add(new SimpleWrapper("[" + displayName + "]", _process.Engine, arrayExpr.Children[index])); } diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index c49b7b0b6..bd22d1d05 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -128,8 +128,38 @@ public AD7DebugSession(Stream debugAdapterStdIn, Stream debugAdapterStdOut, List m_dataBreakpoints = new Dictionary(); m_exceptionBreakpoints = new List(); m_variableManager = new VariableManager(); + + //Register sendInvalidate request + Protocol.RegisterRequestType(r => this.HandleSendInvalidateRequestAsync(r)); + } + private void HandleSendInvalidateRequestAsync(IRequestResponder responder) + { + InvalidatedEvent invalidated = new InvalidatedEvent(); + // Set the Arguments only if passed + if (null != responder.Arguments) { + // Setting the Areas if passed + if (null != responder.Arguments.Areas) { + invalidated.Areas = responder.Arguments.Areas; + } + + // Setting the StackFrameId if passed (and the 'threadId' is ignored). + if (null != responder.Arguments.StackFrameId) + { + invalidated.StackFrameId = responder.Arguments.StackFrameId; + } + + // Setting the ThreadId if passed + else if (null != responder.Arguments.ThreadId) + { + invalidated.ThreadId = responder.Arguments.ThreadId; + } + } + + Protocol.SendEvent(invalidated); + + } #endregion #region Utility @@ -3874,4 +3904,21 @@ int IDebugSettingsCallback110.ShouldSuppressImplicitToStringCalls(out int pfSupp } } } + + internal class SendInvalidateRequest : DebugRequest + { + + public SendInvalidateRequest(): base("sendInvalidate") + { + } + } + + internal class SendInvalidateArguments : DebugRequestArguments + { + + public List Areas { get; set; } + public int? ThreadId { get; set; } + public int? StackFrameId { get; set; } + + } } diff --git a/src/SSHDebugPS/Docker/DockerContainerInstance.cs b/src/SSHDebugPS/Docker/DockerContainerInstance.cs index f54362d3c..61b95713f 100644 --- a/src/SSHDebugPS/Docker/DockerContainerInstance.cs +++ b/src/SSHDebugPS/Docker/DockerContainerInstance.cs @@ -47,16 +47,16 @@ private DockerContainerInstance() { } [JsonProperty("Names")] public override string Name { get; set; } - [JsonProperty("Image")] + [JsonProperty(nameof(Image))] public string Image { get; private set; } - [JsonProperty("Ports")] + [JsonProperty(nameof(Ports))] public string Ports { get; set; } - [JsonProperty("Command")] + [JsonProperty(nameof(Command))] public string Command { get; private set; } - [JsonProperty("Status")] + [JsonProperty(nameof(Status))] public string Status { get; private set; } [JsonProperty("CreatedAt")] diff --git a/test/CppTests/Tests/NatvisTests.cs b/test/CppTests/Tests/NatvisTests.cs index 279af0664..d7fdf5d12 100644 --- a/test/CppTests/Tests/NatvisTests.cs +++ b/test/CppTests/Tests/NatvisTests.cs @@ -181,7 +181,8 @@ public void TestArrayItems(ITestSettings settings) // Multi-dimensional array var matrix = currentFrame.GetVariable("matrix"); - Assert.Equal("3", matrix.GetVariable("[1,1]").Value); + Assert.Equal("1", matrix.GetVariable("[0,1]").Value); + Assert.Equal("51", matrix.GetVariable("[More...]").GetVariable("[0,51]").Value); } runner.Expects.ExitedEvent(exitCode: 0).TerminatedEvent().AfterContinue(); diff --git a/test/CppTests/debuggees/natvis/src/SimpleMatrix.h b/test/CppTests/debuggees/natvis/src/SimpleMatrix.h index e315e749b..4889358ed 100644 --- a/test/CppTests/debuggees/natvis/src/SimpleMatrix.h +++ b/test/CppTests/debuggees/natvis/src/SimpleMatrix.h @@ -1,27 +1,20 @@ class SimpleMatrix { public: - int m_size1; - int m_size2; - bool m_fUseSize1; + int m_rows; + int m_cols; int* m_pData; - SimpleMatrix(int size1, int size2, bool fUseSize1) + SimpleMatrix(int row, int col) { - m_size1 = size1; - m_size2 = size2; - m_fUseSize1 = fUseSize1; + m_rows = row; + m_cols = col; - m_pData = new int[GetSize()]; + m_pData = new int[row * col]; - for (int i = 0; i < GetSize(); i++) + for (int i = 0; i < row * col; i++) { m_pData[i] = i; } } - - int GetSize() - { - return m_fUseSize1 ? m_size1 : m_size2; - } }; \ No newline at end of file diff --git a/test/CppTests/debuggees/natvis/src/main.cpp b/test/CppTests/debuggees/natvis/src/main.cpp index 8cc695507..0563e0c5c 100644 --- a/test/CppTests/debuggees/natvis/src/main.cpp +++ b/test/CppTests/debuggees/natvis/src/main.cpp @@ -46,7 +46,7 @@ int main(int argc, char** argv) SimpleClass* simpleClass = nullptr; simpleClass = new SimpleClass(); - SimpleMatrix matrix(5, 8, false); + SimpleMatrix matrix(2, 256); return 0; -} \ No newline at end of file +} diff --git a/test/CppTests/debuggees/natvis/src/visualizer_files/Simple.natvis b/test/CppTests/debuggees/natvis/src/visualizer_files/Simple.natvis index 0c3df50fb..775290ebb 100644 --- a/test/CppTests/debuggees/natvis/src/visualizer_files/Simple.natvis +++ b/test/CppTests/debuggees/natvis/src/visualizer_files/Simple.natvis @@ -64,7 +64,7 @@ Forward 2 - ($i == 1) ? 2 : m_size2/2 + ($i == 0) ? m_rows : m_cols m_pData diff --git a/test/CppTests/debuggees/natvis/src/visualizer_files/Simple2.natvis b/test/CppTests/debuggees/natvis/src/visualizer_files/Simple2.natvis index 959a3ed54..adfcf967a 100644 --- a/test/CppTests/debuggees/natvis/src/visualizer_files/Simple2.natvis +++ b/test/CppTests/debuggees/natvis/src/visualizer_files/Simple2.natvis @@ -27,16 +27,4 @@ - - - SimpleMatrix - - - Forward - 2 - ($i == 1) ? 2 : m_size2/2 - m_pData - - - \ No newline at end of file