From c57efd6cbade7e41d7bc9e206428b8e04869d449 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 1 Jul 2022 21:12:31 -0700 Subject: [PATCH] Testing main, adding in net7.0 testing (#3127) Testing main, adding in net7.0 testing Add common test runner and project (CommonTestRunner) Disable 7.0 singlefile and aspnet tests until new test infra is in DotNetBuildDebuggeeTestStep.cs resilient against test environment Change TFM on .NET 7 for Unix as well Adjust ProcessInfo tests to account for full "managed" commandline Remove net5.0 testing Don't install 3.1 on arm64 Build and run tests on MacOS M1 https://github.com/dotnet/diagnostics/issues/2568 Reenable EventLogsPipeUnitTests and EventCounterTriggerTests https://github.com/dotnet/diagnostics/issues/2541 TestLogsAllCategoriesDefaultLevelFallback fails frequently https://github.com/dotnet/diagnostics/issues/2819 Diagnostics tests don't run on MacOS arm64 Fix x86 GCEventTest Don't dispose EventPipeSession in EventPipeSessionTests because they hang for 30secs during the dispose. Disable (setting COMPlus_EnableWriteXorExecute=0) W^E in SOSRunner so bpmd tests work on 7.0. Add --address option to modules command Update test SDK to version with SOS buffer fixes Made changes to SOS runner to use the local DAC via the runtime directory for single-file apps instead of depending on downloading it from the symbol server. Disable checking GCFreeSegment callbacks on 7.0 Add singlefile to test helper (like SOSRunner) log names Disable eeversion test on 7.0 --- NuGet.config | 1 - diagnostics.sln | 93 ++++- eng/InstallRuntimes.proj | 43 ++- eng/Version.Details.xml | 20 +- eng/Versions.props | 27 +- global.json | 14 +- .../TargetFromDataReader.cs | 6 +- .../Host/ModulesCommand.cs | 92 +++-- .../IProcessLogger.cs | 1 + .../ProcessRunner.cs | 18 +- .../TestConfiguration.cs | 16 + .../TestOutputProcessLogger.cs | 24 +- .../SOS.Hosting/SymbolServiceExtensions.cs | 26 +- .../Unix/Debugger.Tests.Config.txt | 49 ++- .../Windows/Debugger.Tests.Config.txt | 51 ++- .../Debuggees/DesktopClrHost/CMakeLists.txt | 2 +- .../Debuggees/Directory.Build.props | 2 +- .../Debuggees/WebApp3/WebApp3.csproj | 3 +- src/SOS/SOS.UnitTests/SOS.cs | 2 +- src/SOS/SOS.UnitTests/SOSRunner.cs | 31 +- .../Scripts/ConcurrentDictionaries.script | 3 + src/SOS/SOS.UnitTests/Scripts/DumpGen.script | 3 + .../Scripts/StackAndOtherTests.script | 3 + .../SOS.UnitTests/Scripts/StackTests.script | 3 + .../CommonTestRunner/CommonTestRunner.csproj | 51 +++ .../Unix/Debugger.Tests.Config.txt | 54 +++ .../Windows/Debugger.Tests.Config.txt | 53 +++ .../DiagnosticPortsHelper.cs | 5 +- src/tests/CommonTestRunner/TestRunner.cs | 342 ++++++++++++++++++ .../Unix/Debugger.Tests.Config.txt | 16 +- .../Windows/Debugger.Tests.Config.txt | 13 +- src/tests/DbgShim.UnitTests/DebuggeeInfo.cs | 2 +- .../SimpleDebuggee/SimpleDebuggee.cs | 3 +- src/tests/Directory.Build.props | 16 + .../EventPipeTracee/EventPipeTracee.csproj | 2 +- src/tests/EventPipeTracee/Program.cs | 70 ++-- .../ExitCodeTracee/ExitCodeTracee.csproj | 2 +- ...Diagnostics.DebugServices.UnitTests.csproj | 3 +- .../EventCounterPipelineUnitTests.cs | 27 +- .../EventCounterTriggerTests.cs | 30 +- .../EventLogsPipelineUnitTests.cs | 86 ++--- .../EventTracePipelineUnitTests.cs | 51 +-- ...tics.Monitoring.EventPipe.UnitTests.csproj | 7 +- .../PipelineTestUtilities.cs | 61 ++-- ...ft.Diagnostics.Monitoring.UnitTests.csproj | 6 +- .../PipelineTests.cs | 4 - .../CommonHelper.cs | 42 --- .../EventPipeSessionTests.cs | 111 +++--- .../GetProcessEnvironmentTests.cs | 34 +- .../GetProcessInfoTests.cs | 59 ++- .../GetPublishedProcessesTests.cs | 94 ++--- ...iagnostics.NETCore.Client.UnitTests.csproj | 61 +--- .../RemoteTestExecution.cs | 110 ------ .../ReversedServerTests.cs | 128 ++++--- .../TestRunner.cs | 178 --------- src/tests/StackTracee/Program.cs | 2 +- src/tests/StackTracee/StackTracee.csproj | 2 +- src/tests/Tracee/Program.cs | 47 ++- src/tests/Tracee/Tracee.csproj | 2 +- .../DotnetCounters.UnitTests.csproj | 2 +- .../dotnet-stack/DotnetStack.UnitTests.csproj | 6 +- src/tests/dotnet-stack/StackTests.cs | 66 +++- src/tests/dotnet-trace/ChildProcessTests.cs | 85 +++-- .../dotnet-trace/DotnetTrace.UnitTests.csproj | 9 +- .../eventpipe/EventPipe.UnitTests.csproj | 4 +- src/tests/eventpipe/GCEvents.cs | 16 +- src/tests/eventpipe/LoaderEvents.cs | 3 +- 67 files changed, 1468 insertions(+), 1030 deletions(-) create mode 100644 src/tests/CommonTestRunner/CommonTestRunner.csproj create mode 100644 src/tests/CommonTestRunner/ConfigFiles/Unix/Debugger.Tests.Config.txt create mode 100644 src/tests/CommonTestRunner/ConfigFiles/Windows/Debugger.Tests.Config.txt rename src/tests/{Microsoft.Diagnostics.NETCore.Client => CommonTestRunner}/DiagnosticPortsHelper.cs (92%) create mode 100644 src/tests/CommonTestRunner/TestRunner.cs create mode 100644 src/tests/Directory.Build.props delete mode 100644 src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs delete mode 100644 src/tests/Microsoft.Diagnostics.NETCore.Client/RemoteTestExecution.cs delete mode 100644 src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs diff --git a/NuGet.config b/NuGet.config index fd1f2a9ce2..de789f31f8 100644 --- a/NuGet.config +++ b/NuGet.config @@ -7,7 +7,6 @@ - diff --git a/diagnostics.sln b/diagnostics.sln index 5f940760e0..1a0860f67d 100644 --- a/diagnostics.sln +++ b/diagnostics.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29019.234 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32519.111 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.TestHelpers", "src\Microsoft.Diagnostics.TestHelpers\Microsoft.Diagnostics.TestHelpers.csproj", "{730C1201-1848-4F1E-8C1F-6316FB886C35}" EndProject @@ -41,9 +41,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SOS.Symbol.Package", "src\SOS\SOS.Package\SOS.Symbol.Package.csproj", "{410394E0-7F4F-42D5-B5FA-30956F44ACBC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{03479E19-3F18-49A6-910A-F5041E27E7C0}" - ProjectSection(SolutionItems) = preProject - src\tests\eventpipe\EventPipe.UnitTests.csproj = src\tests\eventpipe\EventPipe.UnitTests.csproj - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetTrace.UnitTests", "src\tests\dotnet-trace\DotnetTrace.UnitTests.csproj", "{AEDCCF5B-5AD0-4D64-BF73-5CF468E07D22}" EndProject @@ -173,8 +170,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{41BDFD6D-D16 src\shared\inc\sstring.h = src\shared\inc\sstring.h src\shared\inc\sstring.inl = src\shared\inc\sstring.inl src\shared\inc\stacktrace.h = src\shared\inc\stacktrace.h - src\shared\inc\static_assert.h = src\shared\inc\static_assert.h src\shared\inc\staticcontract.h = src\shared\inc\staticcontract.h + src\shared\inc\static_assert.h = src\shared\inc\static_assert.h src\shared\inc\stdmacros.h = src\shared\inc\stdmacros.h src\shared\inc\stresslog.h = src\shared\inc\stresslog.h src\shared\inc\switches.h = src\shared\inc\switches.h @@ -265,6 +262,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "minipal", "minipal", "{795B EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbgShim.UnitTests", "src\tests\DbgShim.UnitTests\DbgShim.UnitTests.csproj", "{DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonTestRunner", "src\tests\CommonTestRunner\CommonTestRunner.csproj", "{DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetStack.UnitTests", "src\tests\dotnet-stack\DotnetStack.UnitTests.csproj", "{E8F133F8-4D20-475D-9D16-2BA236DAB65F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|Any CPU = Checked|Any CPU @@ -1786,6 +1787,84 @@ Global {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x86.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|Any CPU.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|ARM.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|ARM.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|ARM64.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|ARM64.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|x64.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|x64.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|x86.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Checked|x86.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|ARM.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|ARM64.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|x64.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Debug|x86.Build.0 = Debug|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|Any CPU.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|ARM.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|ARM.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|ARM64.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|ARM64.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|x64.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|x64.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|x86.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.Release|x86.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F}.RelWithDebInfo|x86.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|Any CPU.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|ARM.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|ARM.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|ARM64.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|ARM64.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|x64.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|x64.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|x86.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Checked|x86.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|ARM.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|ARM64.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|x64.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Debug|x86.Build.0 = Debug|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|ARM.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|ARM.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|ARM64.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|ARM64.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|x64.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|x64.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|x86.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.Release|x86.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU + {E8F133F8-4D20-475D-9D16-2BA236DAB65F}.RelWithDebInfo|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1842,6 +1921,8 @@ Global {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D} = {7852EDE4-93DF-4DB1-8A86-C521703811AF} {795B7A50-1B1F-406E-94E0-F9B35104EF22} = {7852EDE4-93DF-4DB1-8A86-C521703811AF} {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728} = {03479E19-3F18-49A6-910A-F5041E27E7C0} + {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F} = {03479E19-3F18-49A6-910A-F5041E27E7C0} + {E8F133F8-4D20-475D-9D16-2BA236DAB65F} = {03479E19-3F18-49A6-910A-F5041E27E7C0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0} diff --git a/eng/InstallRuntimes.proj b/eng/InstallRuntimes.proj index c11cd5f6ac..eac7bdc378 100644 --- a/eng/InstallRuntimes.proj +++ b/eng/InstallRuntimes.proj @@ -15,16 +15,17 @@ $(MicrosoftDotnetSdkInternalVersion) - .NET SDK to use for testing - $(VSRedistCommonNetCoreSharedFrameworkx6460Version) - latest dotnet runtime package version (the version to install) - $(MicrosoftNETCoreAppRuntimewinx64Version) - latest dotnet runtime stable version (the version that actually is installed) + $(VSRedistCommonNetCoreSharedFrameworkx6470Version) - latest dotnet runtime package version (the version to install) + $(MicrosoftNETCoreAppRuntimewinx64Version) - latest dotnet runtime stable version (the version that actually is installed) - $(MicrosoftAspNetCoreAppRefInternalVersion) - latest dotnet aspnetcore package version (the version to install) - $(MicrosoftAspNetCoreAppRefVersion) - latest dotnet aspnetcore stable version (the version that actually is installed) + $(MicrosoftAspNetCoreAppRefInternalVersion) - latest dotnet aspnetcore package version (the version to install) + $(MicrosoftAspNetCoreAppRefVersion) - latest dotnet aspnetcore stable version (the version that actually is installed) - $(MicrosoftNETCoreApp50Version) $(MicrosoftAspNetCoreApp50Version) - 5.0 version - $(MicrosoftNETCoreApp31Version) $(MicrosoftAspNetCoreApp31Version) - 3.1 version + $(MicrosoftNETCoreApp60Version) $(MicrosoftAspNetCoreApp60Version) - 6.0 version + $(MicrosoftNETCoreApp31Version) $(MicrosoftAspNetCoreApp31Version) - 3.1 version - $(SingleFileRuntimeVersion) - The version of the runtime used to build single-file apps + $(SingleFileRuntimeLatestVersion) - The latest version of the runtime used to build single-file apps + $(SingleFileRuntime60Version) - The 6.0.x version of the runtime used to build single-file apps From Arcade: @@ -63,6 +64,11 @@ regedit.exe + + true + false + + @@ -78,14 +84,14 @@ - - - + + + - + @@ -147,11 +153,11 @@ Outputs="$(TestConfigFileName)"> - $(MicrosoftNETCoreApp31Version) - $(MicrosoftAspNetCoreApp31Version) + $(MicrosoftNETCoreApp31Version) + $(MicrosoftAspNetCoreApp31Version) - $(MicrosoftNETCoreApp50Version) - $(MicrosoftAspNetCoreApp50Version) + $(MicrosoftNETCoreApp60Version) + $(MicrosoftAspNetCoreApp60Version) @@ -170,13 +176,14 @@ $(RuntimeVersion31) $(AspNetCoreVersion31) - $(RuntimeVersion50) - $(AspNetCoreVersion50) + $(RuntimeVersion60) + $(AspNetCoreVersion60) $(RuntimeVersionLatest) $(AspNetCoreVersionLatest) - $(SingleFileRuntimeVersion) + $(SingleFileRuntimeLatestVersion) + $(SingleFileRuntime60Version) ]]> diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b85fc624a4..244495331c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,7 +14,7 @@ https://github.com/dotnet/source-build-reference-packages - 993b067a1849e4884360b4548cbf74f0bfaa2f35 + 3dbb19f76474f2f22749b2e64d34c15178381ffb @@ -28,25 +28,25 @@ https://github.com/dotnet/arcade ccfe6da198c5f05534863bbb1bff66e830e0c6ab - + https://github.com/dotnet/installer - d62c7a24e4421ec3d70783fad181f50fde2e3901 + 51211d8d2eb12dfbee4d88d24eff9af26d6b7262 - + https://github.com/dotnet/aspnetcore - 511eaa8ee5923bf6e33fe86ae424bc79e4723003 + 154d0530fb24e142c22b5b737e881f1b7c0c65d3 - + https://github.com/dotnet/aspnetcore 511eaa8ee5923bf6e33fe86ae424bc79e4723003 - + https://github.com/dotnet/runtime - a21b9a2dd4c31cf5bd37626562b7612faf21cee6 + f21ace52e357bbf0019da5c9e42d66705a087235 - + https://github.com/dotnet/runtime - a21b9a2dd4c31cf5bd37626562b7612faf21cee6 + f21ace52e357bbf0019da5c9e42d66705a087235 diff --git a/eng/Versions.props b/eng/Versions.props index c1b8d7af23..72cc436113 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -15,23 +15,26 @@ true - - 6.0.5 1.0.332901 - - 3.1.18 - $(MicrosoftNETCoreApp31Version) - 5.0.9 - $(MicrosoftNETCoreApp50Version) - 6.0.5-servicing.22213.11 - 6.0.5 + 7.0.0-preview.6.22329.5 + 7.0.0-preview.6.22329.5 - 6.0.7-servicing.22313.13 - 6.0.7 + 7.0.0-preview.7.22330.6 + 7.0.0-preview.7.22330.6 - 6.0.107-servicing.22310.12 + 7.0.100-preview.7.22351.2 + + + + 3.1.25 + $(MicrosoftNETCoreApp31Version) + 6.0.6 + $(MicrosoftNETCoreApp60Version) + + 6.0.6 + 7.0.0-preview.6.22329.5 diff --git a/global.json b/global.json index 4f9b7f2039..ef7df67613 100644 --- a/global.json +++ b/global.json @@ -1,16 +1,20 @@ { "tools": { - "dotnet": "7.0.100-preview.2.22153.17", + "dotnet": "7.0.100-preview.3.22179.4", "runtimes": { "dotnet/x64": [ "$(MicrosoftNETCoreApp31Version)", - "$(MicrosoftNETCoreApp50Version)", - "$(VSRedistCommonNetCoreSharedFrameworkx6460Version)" + "$(MicrosoftNETCoreApp60Version)", + "$(VSRedistCommonNetCoreSharedFrameworkx6470Version)" ], "dotnet/x86": [ "$(MicrosoftNETCoreApp31Version)", - "$(MicrosoftNETCoreApp50Version)", - "$(VSRedistCommonNetCoreSharedFrameworkx6460Version)" + "$(MicrosoftNETCoreApp60Version)", + "$(VSRedistCommonNetCoreSharedFrameworkx6470Version)" + ], + "dotnet/arm64": [ + "$(MicrosoftNETCoreApp60Version)", + "$(VSRedistCommonNetCoreSharedFrameworkx6470Version)" ] } }, diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs index ee499a3022..07d901bb04 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs @@ -49,8 +49,10 @@ public TargetFromDataReader(IDataReader dataReader, OSPlatform targetOS, IHost h { memoryService = new ImageMappingMemoryService(this, memoryService); // Any dump created for a MacOS target does not have managed assemblies in the module service so - // we need to use the metadata mapping memory service to make sure the metadata is available. - if (targetOS == OSPlatform.OSX) + // we need to use the metadata mapping memory service to make sure the metadata is available and + // 7.0 Linux builds have an extra System.Private.CoreLib module mapping that causes the image + // mapper not to be able to map in the metadata. + if (targetOS == OSPlatform.OSX || targetOS == OSPlatform.Linux) { memoryService = new MetadataMappingMemoryService(this, memoryService); } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/ModulesCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/ModulesCommand.cs index 9a450ea1ad..74fd7a9cab 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/ModulesCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/ModulesCommand.cs @@ -28,51 +28,73 @@ public class ModulesCommand : CommandBase [Option(Name = "--name", Aliases = new string[] { "-n" }, Help = "RegEx filter on module name (path not included).")] public string ModuleName { get; set; } + [Option(Name = "--address", Aliases = new string[] { "-a" }, Help = "Lookup address in module list.")] + public ulong? Address { get; set; } + public IModuleService ModuleService { get; set; } public override void Invoke() { - Regex regex = ModuleName is not null ? new Regex(ModuleName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null; - ulong totalSize = 0; - - foreach (IModule module in ModuleService.EnumerateModules().OrderBy((m) => m.ModuleIndex)) + if (Address.HasValue) { - totalSize += module.ImageSize; - if (regex is null || !string.IsNullOrEmpty(module.FileName) && regex.IsMatch(Path.GetFileName(module.FileName))) + IModule module = ModuleService.GetModuleFromAddress(Address.Value); + if (module != null) { - if (Verbose) - { - WriteLine("{0} {1}", module.ModuleIndex, module.FileName); - WriteLine(" Address: {0:X16}", module.ImageBase); - WriteLine(" ImageSize: {0:X8}", module.ImageSize); - WriteLine(" IsPEImage: {0}", module.IsPEImage); - WriteLine(" IsManaged: {0}", module.IsManaged); - WriteLine(" IsFileLayout: {0}", module.IsFileLayout?.ToString() ?? ""); - WriteLine(" IndexFileSize: {0}", module.IndexFileSize?.ToString("X8") ?? ""); - WriteLine(" IndexTimeStamp: {0}", module.IndexTimeStamp?.ToString("X8") ?? ""); - WriteLine(" Version: {0}", module.GetVersionData()?.ToString() ?? ""); - string versionString = module.GetVersionString(); - if (!string.IsNullOrEmpty(versionString)) - { - WriteLine(" {0}", versionString); - } - foreach (PdbFileInfo pdbFileInfo in module.GetPdbFileInfos()) - { - WriteLine(" PdbInfo: {0}", pdbFileInfo); - } - WriteLine(" BuildId: {0}", !module.BuildId.IsDefaultOrEmpty ? string.Concat(module.BuildId.Select((b) => b.ToString("x2"))) : ""); - } - else - { - WriteLine("{0:X16} {1:X8} {2}", module.ImageBase, module.ImageSize, module.FileName); - } - if (Segment) + DisplayModule(module); + } + else + { + WriteLineError($"Address 0x{Address:X16} not found"); + } + } + else + { + Regex regex = ModuleName is not null ? new Regex(ModuleName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null; + ulong totalSize = 0; + foreach (IModule module in ModuleService.EnumerateModules().OrderBy((m) => m.ModuleIndex)) + { + totalSize += module.ImageSize; + if (regex is null || !string.IsNullOrEmpty(module.FileName) && regex.IsMatch(Path.GetFileName(module.FileName))) { - DisplaySegments(module); + DisplayModule(module); } } + WriteLine("Total image size: {0}", totalSize); + } + } + + void DisplayModule(IModule module) + { + if (Verbose) + { + WriteLine("{0} {1}", module.ModuleIndex, module.FileName); + WriteLine(" Address: {0:X16}", module.ImageBase); + WriteLine(" ImageSize: {0:X8}", module.ImageSize); + WriteLine(" IsPEImage: {0}", module.IsPEImage); + WriteLine(" IsManaged: {0}", module.IsManaged); + WriteLine(" IsFileLayout: {0}", module.IsFileLayout?.ToString() ?? ""); + WriteLine(" IndexFileSize: {0}", module.IndexFileSize?.ToString("X8") ?? ""); + WriteLine(" IndexTimeStamp: {0}", module.IndexTimeStamp?.ToString("X8") ?? ""); + WriteLine(" Version: {0}", module.GetVersionData()?.ToString() ?? ""); + string versionString = module.GetVersionString(); + if (!string.IsNullOrEmpty(versionString)) + { + WriteLine(" {0}", versionString); + } + foreach (PdbFileInfo pdbFileInfo in module.GetPdbFileInfos()) + { + WriteLine(" PdbInfo: {0}", pdbFileInfo); + } + WriteLine(" BuildId: {0}", !module.BuildId.IsDefaultOrEmpty ? string.Concat(module.BuildId.Select((b) => b.ToString("x2"))) : ""); + } + else + { + WriteLine("{0:X16} {1:X8} {2}", module.ImageBase, module.ImageSize, module.FileName); + } + if (Segment) + { + DisplaySegments(module); } - WriteLine("Total image size: {0}", totalSize); } public ITarget Target { get; set; } diff --git a/src/Microsoft.Diagnostics.TestHelpers/IProcessLogger.cs b/src/Microsoft.Diagnostics.TestHelpers/IProcessLogger.cs index 233372a1f5..f99080b752 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/IProcessLogger.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/IProcessLogger.cs @@ -15,6 +15,7 @@ public enum ProcessStream public enum KillReason { TimedOut, + Stopped, Unknown } diff --git a/src/Microsoft.Diagnostics.TestHelpers/ProcessRunner.cs b/src/Microsoft.Diagnostics.TestHelpers/ProcessRunner.cs index e5bca1393d..9bee89c895 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/ProcessRunner.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/ProcessRunner.cs @@ -29,7 +29,7 @@ namespace Microsoft.Diagnostics.TestHelpers /// only calls to Kill() and property getters invoked within the logging callbacks will be called /// asynchronously. /// - public class ProcessRunner + public class ProcessRunner : IDisposable { // All of the locals might accessed from multiple threads and need to read/written under // the _lock. We also use the lock to synchronize property access on the process object. @@ -232,6 +232,11 @@ public int ExitCode get { lock (_lock) { return _p.ExitCode; } } } + public int ModuleCount + { + get { lock (_lock) { return _p.Modules.Count; } } + } + public void StandardInputWriteLine(string line) { IProcessLogger[] loggers = null; @@ -246,6 +251,7 @@ public void StandardInputWriteLine(string line) logger.WriteLine(this, line, ProcessStream.StandardIn); } inputStream.WriteLine(line); + inputStream.Flush(); } public Task Run() @@ -262,6 +268,16 @@ public Task WaitForExit() } } + public void Dispose() + { + Process p = null; + lock (_lock) + { + p = _p; + } + p?.Dispose(); + } + public ProcessRunner Start() { Process p = null; diff --git a/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs b/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs index ece382ad0d..5aa681063e 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs @@ -454,6 +454,10 @@ private string GetStringViewWithVersion(string version) sb.Append("."); sb.Append(debuggeeBuildProcess); } + if (PublishSingleFile) + { + sb.Append(".singlefile"); + } if (!string.IsNullOrEmpty(version)) { sb.Append("."); @@ -781,6 +785,18 @@ public string LinkerPackageVersion get { return GetValue("LinkerPackageVersion"); } } + /// + /// The root of the dotnet install to use to run the test (i.e. $(RepoRootDir)/.dotnet-test) + /// + public string DotNetRoot + { + get + { + string dotnetRoot = GetValue("DotNetRoot"); + return MakeCanonicalPath(dotnetRoot); + } + } + #region Runtime Features properties /// diff --git a/src/Microsoft.Diagnostics.TestHelpers/TestOutputProcessLogger.cs b/src/Microsoft.Diagnostics.TestHelpers/TestOutputProcessLogger.cs index 6966668dd7..475d45398f 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/TestOutputProcessLogger.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/TestOutputProcessLogger.cs @@ -12,9 +12,9 @@ namespace Microsoft.Diagnostics.TestHelpers { public class TestOutputProcessLogger : IProcessLogger { - string _timeFormat = "mm\\:ss\\.fff"; - ITestOutputHelper _output; - StringBuilder[] _lineBuffers; + private static readonly string _timeFormat = "mm\\:ss\\.fff"; + private readonly ITestOutputHelper _output; + private readonly StringBuilder[] _lineBuffers; public TestOutputProcessLogger(ITestOutputHelper output) { @@ -69,7 +69,7 @@ public virtual void ProcessExited(ProcessRunner runner) { TimeSpan offset = runner.StartTime - DateTime.Now; _output.WriteLine("}"); - _output.WriteLine("Exit code: " + runner.ExitCode + " ( " + offset.ToString(_timeFormat) + " elapsed)"); + _output.WriteLine($"Process {runner.ProcessId} exited {runner.ExitCode} ({offset.ToString(_timeFormat)} elapsed)"); _output.WriteLine(""); } } @@ -79,16 +79,14 @@ public void ProcessKilled(ProcessRunner runner, KillReason reason) lock (this) { TimeSpan offset = runner.StartTime - DateTime.Now; - string reasonText = ""; - if (reason == KillReason.TimedOut) + string reasonText = reason switch { - reasonText = "Process timed out"; - } - else if (reason == KillReason.Unknown) - { - reasonText = "Kill() was called"; - } - _output.WriteLine(" Killing process: " + offset.ToString(_timeFormat) + ": " + reasonText); + KillReason.TimedOut => "Process timed out", + KillReason.Stopped => "Process was stopped", + KillReason.Unknown => "Kill() was called", + _ => "Reason Unknown" + }; + _output.WriteLine($"Killing process {runner.ProcessId}: {offset.ToString(_timeFormat)} - {reasonText}"); } } diff --git a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs index 285b1cace5..2317f10981 100644 --- a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs +++ b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs @@ -120,18 +120,26 @@ public static int GetICorDebugMetadataLocator( { SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize); string localFilePath = symbolService.DownloadFile(key); - localFilePath += "\0"; // null terminate the string - actualSize = localFilePath.Length; - - if (pathBufferSize > actualSize) + if (!string.IsNullOrWhiteSpace(localFilePath)) { - Trace.TraceInformation($"GetICorDebugMetadataLocator: SUCCEEDED {localFilePath}"); - Marshal.Copy(localFilePath.ToCharArray(), 0, pwszPathBuffer, actualSize); + localFilePath += "\0"; // null terminate the string + actualSize = localFilePath.Length; + + if (pathBufferSize > actualSize) + { + Trace.TraceInformation($"GetICorDebugMetadataLocator: SUCCEEDED {localFilePath}"); + Marshal.Copy(localFilePath.ToCharArray(), 0, pwszPathBuffer, actualSize); + } + else + { + Trace.TraceError("GetICorDebugMetadataLocator: E_INSUFFICIENT_BUFFER"); + hr = E_INSUFFICIENT_BUFFER; + } } - else + else { - Trace.TraceError("GetICorDebugMetadataLocator: E_INSUFFICIENT_BUFFER"); - hr = E_INSUFFICIENT_BUFFER; + Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} download FAILED"); + hr = HResult.E_FAIL; } } else diff --git a/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt b/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt index 38cab1529a..3e76b20f05 100644 --- a/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt +++ b/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt @@ -24,19 +24,19 @@ true false - - net6.0 + net7.0 net5.0 + net6.0 netcoreapp3.1 $(RepoRootDir)/src/SOS/SOS.UnitTests/Debuggees sdk.prebuilt $(RootBinDir) - - $(RepoRootDir)/.dotnet/dotnet + $(DotNetRoot)/dotnet + dotnet7=https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet7/nuget/v3/index.json; dotnet6=https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet6/nuget/v3/index.json; dotnet-core=https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; dotnet-public=https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json @@ -50,7 +50,7 @@ cli $(RootBinDir)/Debuggees/SingleFile $(BuildProjectFrameworkLatest) - $(RuntimeVersionLatest) + $(SingleFileRuntimeLatestVersion) true @@ -66,9 +75,9 @@ $(RuntimeVersionLatest) $(DotNetRoot)/shared/Microsoft.NETCore.App/$(RuntimeFrameworkVersion) - + - - - + - + - $(InstallDir)/libdbgshim.so $(RuntimeModuleDir)/libcoreclr.so $(RuntimeModuleDir)/libmscordbi.so $(RuntimeModuleDir)/libmscordaccore.so $(InstallDir)\dbgshim.dll $(RuntimeModuleDir)\coreclr.dll $(RuntimeModuleDir)\mscordbi.dll diff --git a/src/tests/DbgShim.UnitTests/DebuggeeInfo.cs b/src/tests/DbgShim.UnitTests/DebuggeeInfo.cs index 3ac8d3d4cd..5bcd43986b 100644 --- a/src/tests/DbgShim.UnitTests/DebuggeeInfo.cs +++ b/src/tests/DbgShim.UnitTests/DebuggeeInfo.cs @@ -77,7 +77,7 @@ public async Task WaitForDebuggee() await _pipeServer.WaitForConnectionAsync(source.Token); Trace.TraceInformation($"DebuggeeInfo.WaitForDebuggee: after wait {ProcessId}"); } - catch (OperationCanceledException ex) + catch (Exception ex) when (ex is TaskCanceledException || ex is OperationCanceledException) { Trace.TraceError($"DebuggeeInfo.WaitForDebuggee: canceled {ex}"); return false; diff --git a/src/tests/DbgShim.UnitTests/Debuggees/SimpleDebuggee/SimpleDebuggee.cs b/src/tests/DbgShim.UnitTests/Debuggees/SimpleDebuggee/SimpleDebuggee.cs index 0e870ae3e1..8be1fbab04 100644 --- a/src/tests/DbgShim.UnitTests/Debuggees/SimpleDebuggee/SimpleDebuggee.cs +++ b/src/tests/DbgShim.UnitTests/Debuggees/SimpleDebuggee/SimpleDebuggee.cs @@ -16,12 +16,11 @@ public static int Main(string[] args) { try { - int timeout = TimeSpan.FromMinutes(5).Milliseconds; using var pipeStream = new NamedPipeClientStream(pipeServerName); Console.WriteLine("{0} SimpleDebuggee: connecting to pipe", pid); Console.Out.Flush(); - pipeStream.Connect(timeout); + pipeStream.Connect((int)TimeSpan.FromMinutes(5).TotalMilliseconds); Console.WriteLine("{0} SimpleDebuggee: connected to pipe", pid); Console.Out.Flush(); diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props new file mode 100644 index 0000000000..eaf4b4e2f5 --- /dev/null +++ b/src/tests/Directory.Build.props @@ -0,0 +1,16 @@ + + + + + $(Platform) + $([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant) + + + true + false + + netcoreapp3.1;net6.0;net7.0 + net6.0;net7.0 + + + diff --git a/src/tests/EventPipeTracee/EventPipeTracee.csproj b/src/tests/EventPipeTracee/EventPipeTracee.csproj index 1b898c05dc..0d7605719e 100644 --- a/src/tests/EventPipeTracee/EventPipeTracee.csproj +++ b/src/tests/EventPipeTracee/EventPipeTracee.csproj @@ -2,7 +2,7 @@ Exe $(BuildProjectFramework) - netcoreapp3.1;net5.0 + netcoreapp3.1;net6.0;net7.0 diff --git a/src/tests/EventPipeTracee/Program.cs b/src/tests/EventPipeTracee/Program.cs index 0daa16664f..752c803ea7 100644 --- a/src/tests/EventPipeTracee/Program.cs +++ b/src/tests/EventPipeTracee/Program.cs @@ -6,6 +6,8 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Pipes; using System.Linq; namespace EventPipeTracee @@ -14,16 +16,29 @@ class Program { private const string AppLoggerCategoryName = "AppLoggerCategory"; - static void Main(string[] args) + public static int Main(string[] args) { - bool spinWait10 = args.Length > 1 && "SpinWait10".Equals(args[1], StringComparison.Ordinal); - TestBody(args[0], spinWait10); - } + int pid = Process.GetCurrentProcess().Id; + string pipeServerName = args.Length > 0 ? args[0] : null; + if (pipeServerName == null) + { + Console.Error.WriteLine($"{pid} EventPipeTracee: no pipe name"); + Console.Error.Flush(); + return -1; + } + using var pipeStream = new NamedPipeClientStream(pipeServerName); + bool spinWait10 = args.Length > 2 && "SpinWait10".Equals(args[2], StringComparison.Ordinal); + string loggerCategory = args[1]; - private static void TestBody(string loggerCategory, bool spinWait10) - { - Console.Error.WriteLine("Starting remote test process"); - Console.Error.Flush(); + Console.WriteLine($"{pid} EventPipeTracee: start process"); + Console.Out.Flush(); + + // Signal that the tracee has started + Console.WriteLine($"{pid} EventPipeTracee: connecting to pipe"); + Console.Out.Flush(); + pipeStream.Connect(5 * 60 * 1000); + Console.WriteLine($"{pid} EventPipeTracee: connected to pipe"); + Console.Out.Flush(); ServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddLogging(builder => @@ -38,19 +53,20 @@ private static void TestBody(string loggerCategory, bool spinWait10) var customCategoryLogger = loggerFactory.CreateLogger(loggerCategory); var appCategoryLogger = loggerFactory.CreateLogger(AppLoggerCategoryName); - Console.Error.WriteLine($"{DateTime.UtcNow} Awaiting start"); - Console.Error.Flush(); - if (Console.Read() == -1) - { - throw new InvalidOperationException("Unable to receive start signal"); - } + Console.WriteLine($"{pid} EventPipeTracee: {DateTime.UtcNow} Awaiting start"); + Console.Out.Flush(); + + // Wait for server to send something + int input = pipeStream.ReadByte(); + + Console.WriteLine($"{pid} {DateTime.UtcNow} Starting test body '{input}'"); + Console.Out.Flush(); - Console.Error.WriteLine($"{DateTime.UtcNow} Starting test body"); - Console.Error.Flush(); TestBodyCore(customCategoryLogger, appCategoryLogger); - //Signal end of test data - Console.WriteLine("1"); + Console.WriteLine($"{pid} EventPipeTracee: signal end of test data"); + Console.Out.Flush(); + pipeStream.WriteByte(31); if (spinWait10) { @@ -62,22 +78,22 @@ private static void TestBody(string loggerCategory, bool spinWait10) acc++; if (acc % 1_000_000 == 0) { - Console.Error.WriteLine("Spin waiting..."); + Console.WriteLine($"{pid} Spin waiting..."); } } } - Console.Error.WriteLine($"{DateTime.UtcNow} Awaiting end"); - Console.Error.Flush(); - if (Console.Read() == -1) - { - throw new InvalidOperationException("Unable to receive end signal"); - } + Console.WriteLine($"{pid} {DateTime.UtcNow} Awaiting end"); + Console.Out.Flush(); + + // Wait for server to send something + input = pipeStream.ReadByte(); - Console.Error.WriteLine($"{DateTime.UtcNow} Ending remote test process"); + Console.WriteLine($"{pid} EventPipeTracee {DateTime.UtcNow} Ending remote test process '{input}'"); + return 0; } - //TODO At some point we may want parameters to choose different test bodies. + // TODO At some point we may want parameters to choose different test bodies. private static void TestBodyCore(ILogger customCategoryLogger, ILogger appCategoryLogger) { //Json data is always converted to strings for ActivityStart events. diff --git a/src/tests/ExitCodeTracee/ExitCodeTracee.csproj b/src/tests/ExitCodeTracee/ExitCodeTracee.csproj index cd0510ad22..6b634ef181 100644 --- a/src/tests/ExitCodeTracee/ExitCodeTracee.csproj +++ b/src/tests/ExitCodeTracee/ExitCodeTracee.csproj @@ -2,6 +2,6 @@ Exe $(BuildProjectFramework) - netcoreapp3.1;net5.0 + netcoreapp3.1;net6.0;net7.0 diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj index d41a2bd2d9..ed9a4290f1 100644 --- a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj +++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/Microsoft.Diagnostics.DebugServices.UnitTests.csproj @@ -1,8 +1,7 @@  - - netcoreapp3.1 + net6.0 $(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt 1.0.257801 diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs index cf7cc8e976..bfb15c6b6b 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs @@ -3,14 +3,15 @@ // See the LICENSE file in the project root for more information. using Microsoft.Diagnostics.NETCore.Client; -using Microsoft.Diagnostics.NETCore.Client.UnitTests; -using System; +using Microsoft.Diagnostics.TestHelpers; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests { @@ -18,6 +19,8 @@ public class EventCounterPipelineUnitTests { private readonly ITestOutputHelper _output; + public static IEnumerable Configurations => TestRunner.Configurations; + public EventCounterPipelineUnitTests(ITestOutputHelper output) { _output = output; @@ -83,8 +86,8 @@ private static string CreateKey(string providerName, string counterName) } } - [Fact] - public async Task TestCounterEventPipeline() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestCounterEventPipeline(TestConfiguration config) { var expectedCounters = new[] { "cpu-usage", "working-set" }; string expectedProvider = "System.Runtime"; @@ -96,11 +99,9 @@ public async Task TestCounterEventPipeline() var logger = new TestMetricsLogger(expectedMap, foundExpectedCountersSource); - await using (var testExecution = StartTraceeProcess("CounterRemoteTest")) + await using (var testRunner = await PipelineTestUtilities.StartProcess(config, "CounterRemoteTest", _output)) { - //TestRunner should account for start delay to make sure that the diagnostic pipe is available. - - var client = new DiagnosticsClient(testExecution.TestRunner.Pid); + var client = new DiagnosticsClient(testRunner.Pid); await using EventCounterPipeline pipeline = new EventCounterPipeline(client, new EventPipeCounterPipelineSettings { @@ -116,10 +117,9 @@ public async Task TestCounterEventPipeline() CounterIntervalSeconds = 1 }, new[] { logger }); - await PipelineTestUtilities.ExecutePipelineWithDebugee( - _output, + await PipelineTestUtilities.ExecutePipelineWithTracee( pipeline, - testExecution, + testRunner, foundExpectedCountersSource); } @@ -130,10 +130,5 @@ await PipelineTestUtilities.ExecutePipelineWithDebugee( Assert.Equal(expectedCounters, actualMetrics); Assert.True(logger.Metrics.All(m => string.Equals(m.Provider, expectedProvider))); } - - private RemoteTestExecution StartTraceeProcess(string loggerCategory) - { - return RemoteTestExecution.StartProcess(CommonHelper.GetTraceePathWithArgs("EventPipeTracee") + " " + loggerCategory, _output); - } } } diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs index 257b6b078c..4b6fd60137 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs @@ -5,8 +5,9 @@ using Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.EventCounter; using Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.Pipelines; using Microsoft.Diagnostics.NETCore.Client; -using Microsoft.Diagnostics.NETCore.Client.UnitTests; +using Microsoft.Diagnostics.TestHelpers; using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Runtime.InteropServices; @@ -15,6 +16,7 @@ using Xunit; using Xunit.Abstractions; using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests { @@ -22,6 +24,8 @@ public class EventCounterTriggerTests { private readonly ITestOutputHelper _output; + public static IEnumerable Configurations => TestRunner.Configurations; + public EventCounterTriggerTests(ITestOutputHelper output) { _output = output; @@ -315,12 +319,12 @@ public void EventCounterTriggerDropTest() /// Tests that the trigger condition can be detected on a live application /// using the EventPipeTriggerPipeline. /// - [SkippableFact] - public async Task EventCounterTriggerWithEventPipePipelineTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task EventCounterTriggerWithEventPipePipelineTest(TestConfiguration config) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && config.RuntimeFrameworkVersionMajor < 5) { - throw new SkipTestException("https://github.com/dotnet/diagnostics/issues/2568"); + throw new SkipTestException("Unreliable on Linux .NET 3.1"); } EventCounterTriggerSettings settings = new() { @@ -331,11 +335,9 @@ public async Task EventCounterTriggerWithEventPipePipelineTest() CounterIntervalSeconds = 1 }; - await using (var testExecution = StartTraceeProcess("TriggerRemoteTest")) + await using (var testRunner = await PipelineTestUtilities.StartProcess(config, "TriggerRemoteTest SpinWait10", _output, testProcessTimeout: 2 * 60 * 1000)) { - //TestRunner should account for start delay to make sure that the diagnostic pipe is available. - - DiagnosticsClient client = new(testExecution.TestRunner.Pid); + DiagnosticsClient client = new(testRunner.Pid); TaskCompletionSource waitSource = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -353,10 +355,9 @@ public async Task EventCounterTriggerWithEventPipePipelineTest() waitSource.TrySetResult(null); }); - await PipelineTestUtilities.ExecutePipelineWithDebugee( - _output, + await PipelineTestUtilities.ExecutePipelineWithTracee( pipeline, - testExecution, + testRunner, waitSource); Assert.True(waitSource.Task.IsCompletedSuccessfully); @@ -404,11 +405,6 @@ private void SimulateDataVerifyTrigger(EventCounterTriggerSettings settings, Cpu } } - private RemoteTestExecution StartTraceeProcess(string loggerCategory) - { - return RemoteTestExecution.StartProcess(CommonHelper.GetTraceePathWithArgs("EventPipeTracee") + " " + loggerCategory + " SpinWait10", _output); - } - private sealed class CpuData { public CpuData(double value, bool? result, bool drop = false) diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs index a53777ed37..abd792d9f4 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.CommonTestRunner; using Microsoft.Diagnostics.NETCore.Client; -using Microsoft.Diagnostics.NETCore.Client.UnitTests; +using Microsoft.Diagnostics.TestHelpers; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -15,6 +16,7 @@ using Xunit; using Xunit.Abstractions; using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests { @@ -26,6 +28,8 @@ public class EventLogsPipelineUnitTests private readonly ITestOutputHelper _output; + public static IEnumerable Configurations => TestRunner.Configurations; + public EventLogsPipelineUnitTests(ITestOutputHelper output) { _output = output; @@ -34,15 +38,10 @@ public EventLogsPipelineUnitTests(ITestOutputHelper output) /// /// Test that all log events are collected if no filters are specified. /// - [SkippableFact] - public async Task TestLogsAllCategoriesAllLevels() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsAllCategoriesAllLevels(TestConfiguration config) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - throw new SkipTestException("https://github.com/dotnet/diagnostics/issues/2541"); - } - - using Stream outputStream = await GetLogsAsync(settings => + using Stream outputStream = await GetLogsAsync(config, settings => { settings.UseAppFilters = false; }); @@ -63,15 +62,10 @@ public async Task TestLogsAllCategoriesAllLevels() /// /// Test that log events at or above the default level are collected. /// - [SkippableFact] - public async Task TestLogsAllCategoriesDefaultLevel() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsAllCategoriesDefaultLevel(TestConfiguration config) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - throw new SkipTestException("https://github.com/dotnet/diagnostics/issues/2541"); - } - - using Stream outputStream = await GetLogsAsync(settings => + using Stream outputStream = await GetLogsAsync(config, settings => { settings.UseAppFilters = false; settings.LogLevel = LogLevel.Warning; @@ -91,15 +85,10 @@ public async Task TestLogsAllCategoriesDefaultLevel() /// /// Test that log events at the default level are collected for categories without a specified level. /// - [SkippableFact] - public async Task TestLogsAllCategoriesDefaultLevelFallback() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsAllCategoriesDefaultLevelFallback(TestConfiguration config) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - throw new SkipTestException("https://github.com/dotnet/diagnostics/issues/2541"); - } - - using Stream outputStream = await GetLogsAsync(settings => + using Stream outputStream = await GetLogsAsync(config, settings => { settings.UseAppFilters = false; settings.LogLevel = LogLevel.Error; @@ -124,12 +113,13 @@ public async Task TestLogsAllCategoriesDefaultLevelFallback() /// /// Test that LogLevel.None is not supported as the default log level. /// - [Fact] - public async Task TestLogsAllCategoriesDefaultLevelNoneNotSupported() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsAllCategoriesDefaultLevelNoneNotSupported(TestConfiguration config) { // Pipeline should throw PipelineException with inner exception of NotSupportedException. PipelineException exception = await Assert.ThrowsAsync( () => GetLogsAsync( + config, settings => { settings.UseAppFilters = false; @@ -142,10 +132,10 @@ public async Task TestLogsAllCategoriesDefaultLevelNoneNotSupported() /// /// Test that log events are collected for the categories and levels specified by the application. /// - [Fact] - public async Task TestLogsUseAppFilters() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsUseAppFilters(TestConfiguration config) { - using Stream outputStream = await GetLogsAsync(); + using Stream outputStream = await GetLogsAsync(config); Assert.True(outputStream.Length > 0, "No data written by logging process."); @@ -161,15 +151,10 @@ public async Task TestLogsUseAppFilters() /// Test that log events are collected for the categories and levels specified by the application /// and for the categories and levels specified in the filter specs. /// - [SkippableFact] - public async Task TestLogsUseAppFiltersAndFilterSpecs() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsUseAppFiltersAndFilterSpecs(TestConfiguration config) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - throw new SkipTestException("https://github.com/dotnet/diagnostics/issues/2541"); - } - - using Stream outputStream = await GetLogsAsync(settings => + using Stream outputStream = await GetLogsAsync(config, settings => { settings.FilterSpecs = new Dictionary() { @@ -191,14 +176,10 @@ public async Task TestLogsUseAppFiltersAndFilterSpecs() /// /// Test that log events are collected for wildcard categories. /// - [SkippableFact] - public async Task TestLogsWildcardCategory() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestLogsWildcardCategory(TestConfiguration config) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - throw new SkipTestException("https://github.com/dotnet/diagnostics/issues/2568"); - } - using Stream outputStream = await GetLogsAsync(settings => + using Stream outputStream = await GetLogsAsync(config, settings => { settings.UseAppFilters = false; settings.LogLevel = LogLevel.Critical; @@ -219,16 +200,14 @@ public async Task TestLogsWildcardCategory() Assert.True(reader.EndOfStream, "Expected to have read all entries from stream."); } - private async Task GetLogsAsync(Action settingsCallback = null) + private async Task GetLogsAsync(TestConfiguration config, Action settingsCallback = null) { var outputStream = new MemoryStream(); - await using (var testExecution = StartTraceeProcess(LoggerRemoteTestName)) + await using (var testRunner = await PipelineTestUtilities.StartProcess(config, LoggerRemoteTestName, _output)) { - //TestRunner should account for start delay to make sure that the diagnostic pipe is available. - using var loggerFactory = new LoggerFactory(new[] { new TestStreamingLoggerProvider(outputStream) }); - var client = new DiagnosticsClient(testExecution.TestRunner.Pid); + var client = new DiagnosticsClient(testRunner.Pid); var logSettings = new EventLogsPipelineSettings { Duration = Timeout.InfiniteTimeSpan }; if (null != settingsCallback) @@ -237,7 +216,7 @@ private async Task GetLogsAsync(Action settin } await using var pipeline = new EventLogsPipeline(client, logSettings, loggerFactory); - await PipelineTestUtilities.ExecutePipelineWithDebugee(_output, pipeline, testExecution); + await PipelineTestUtilities.ExecutePipelineWithTracee(pipeline, testRunner); } outputStream.Position = 0L; @@ -335,11 +314,6 @@ private static void Validate(IDictionary values, params (st } } - private RemoteTestExecution StartTraceeProcess(string loggerCategory) - { - return RemoteTestExecution.StartProcess(CommonHelper.GetTraceePathWithArgs("EventPipeTracee") + " " + loggerCategory, _output); - } - private sealed class LoggerTestResult { public string Category { get; set; } diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventTracePipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventTracePipelineUnitTests.cs index 13dd2bbb80..8c88286e28 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventTracePipelineUnitTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventTracePipelineUnitTests.cs @@ -2,22 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.CommonTestRunner; +using Microsoft.Diagnostics.NETCore.Client; +using Microsoft.Diagnostics.TestHelpers; +using Microsoft.Diagnostics.Tracing; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Linq; using System.Runtime.InteropServices; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.Diagnostics.NETCore.Client; -using Microsoft.Diagnostics.NETCore.Client.UnitTests; -using Microsoft.Diagnostics.Tracing; -using Microsoft.Extensions.Logging; using Xunit; using Xunit.Abstractions; using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests { @@ -25,20 +23,20 @@ public class EventTracePipelineUnitTests { private readonly ITestOutputHelper _output; + public static IEnumerable Configurations => TestRunner.Configurations; + public EventTracePipelineUnitTests(ITestOutputHelper output) { _output = output; } - [Fact] - public async Task TestTraceStopAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestTraceStopAsync(TestConfiguration config) { Stream eventStream = null; - await using (var testExecution = StartTraceeProcess("TraceStopTest")) + await using (var testRunner = await PipelineTestUtilities.StartProcess(config, "TraceStopTest", _output)) { - //TestRunner should account for start delay to make sure that the diagnostic pipe is available. - - var client = new DiagnosticsClient(testExecution.TestRunner.Pid); + var client = new DiagnosticsClient(testRunner.Pid); var settings = new EventTracePipelineSettings() { Duration = Timeout.InfiniteTimeSpan, @@ -67,10 +65,9 @@ public async Task TestTraceStopAsync() await Task.Run(() => Assert.True(eventSource.Process()), token); }); - await PipelineTestUtilities.ExecutePipelineWithDebugee( - _output, + await PipelineTestUtilities.ExecutePipelineWithTracee( pipeline, - testExecution, + testRunner, foundProviderSource); } @@ -78,21 +75,19 @@ await PipelineTestUtilities.ExecutePipelineWithDebugee( Assert.Throws(() => eventStream.Read(new byte[4], 0, 4)); } - [SkippableFact] - public async Task TestEventStreamCleanup() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestEventStreamCleanup(TestConfiguration config) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - throw new SkipTestException("Test debugee sigfaults for OSX/Linux"); + throw new SkipTestException("Test tracee sigfaults for OSX/Linux"); } Stream eventStream = null; using var cancellationTokenSource = new CancellationTokenSource(); - await using (var testExecution = StartTraceeProcess("TestEventStreamCleanup")) + await using (var testRunner = await PipelineTestUtilities.StartProcess(config, "TestEventStreamCleanup", _output)) { - //TestRunner should account for start delay to make sure that the diagnostic pipe is available. - - var client = new DiagnosticsClient(testExecution.TestRunner.Pid); + var client = new DiagnosticsClient(testRunner.Pid); var settings = new EventTracePipelineSettings() { Duration = Timeout.InfiniteTimeSpan, @@ -108,20 +103,14 @@ public async Task TestEventStreamCleanup() }); await Assert.ThrowsAsync( - async () => await PipelineTestUtilities.ExecutePipelineWithDebugee( - _output, + async () => await PipelineTestUtilities.ExecutePipelineWithTracee( pipeline, - testExecution, + testRunner, cancellationTokenSource.Token)); } //Validate that the stream is only valid for the lifetime of the callback in the trace pipeline. Assert.Throws(() => eventStream.Read(new byte[4], 0, 4)); } - - private RemoteTestExecution StartTraceeProcess(string loggerCategory) - { - return RemoteTestExecution.StartProcess(CommonHelper.GetTraceePathWithArgs("EventPipeTracee") + " " + loggerCategory, _output); - } } } diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests.csproj index 8410ef25cd..2b066212c5 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests.csproj +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests.csproj @@ -1,16 +1,13 @@  - netcoreapp3.1;net5.0 + $(UnitTestTargetFrameworks) - - - false - + diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs index bc7992542a..4941ff9ecd 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs @@ -2,84 +2,85 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Diagnostics.NETCore.Client.UnitTests; +using Microsoft.Diagnostics.TestHelpers; using System; using System.Threading; using System.Threading.Tasks; using Xunit.Abstractions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests { internal static class PipelineTestUtilities { - private static readonly TimeSpan DefaultPipelineRunTimeout = TimeSpan.FromMinutes(1); + private static readonly TimeSpan DefaultPipelineRunTimeout = TimeSpan.FromMinutes(2); - public static async Task ExecutePipelineWithDebugee( - ITestOutputHelper outputHelper, + public static async Task StartProcess(TestConfiguration config, string testArguments, ITestOutputHelper outputHelper, int testProcessTimeout = 60_000) + { + TestRunner runner = await TestRunner.Create(config, outputHelper, "EventPipeTracee", testArguments); + await runner.Start(testProcessTimeout); + return runner; + } + + public static async Task ExecutePipelineWithTracee( Pipeline pipeline, - RemoteTestExecution testExecution, + TestRunner testRunner, TaskCompletionSource waitTaskSource = null) { using var cancellation = new CancellationTokenSource(DefaultPipelineRunTimeout); - await ExecutePipelineWithDebugee( - outputHelper, + await ExecutePipelineWithTracee( pipeline, - testExecution, + testRunner, cancellation.Token, waitTaskSource); } - public static async Task ExecutePipelineWithDebugee( - ITestOutputHelper outputHelper, + public static async Task ExecutePipelineWithTracee( EventSourcePipeline pipeline, - RemoteTestExecution testExecution, + TestRunner testRunner, TaskCompletionSource waitTaskSource = null) where T : EventSourcePipelineSettings { using var cancellation = new CancellationTokenSource(DefaultPipelineRunTimeout); - await ExecutePipelineWithDebugee( - outputHelper, + await ExecutePipelineWithTracee( pipeline, (p, t) => p.StartAsync(t), - testExecution, + testRunner, cancellation.Token, waitTaskSource); } - public static Task ExecutePipelineWithDebugee( - ITestOutputHelper outputHelper, + public static Task ExecutePipelineWithTracee( Pipeline pipeline, - RemoteTestExecution testExecution, + TestRunner testRunner, CancellationToken token, TaskCompletionSource waitTaskSource = null) { - return ExecutePipelineWithDebugee( - outputHelper, + return ExecutePipelineWithTracee( pipeline, (p, t) => Task.FromResult(p.RunAsync(t)), - testExecution, + testRunner, token, waitTaskSource); } - private static async Task ExecutePipelineWithDebugee( - ITestOutputHelper outputHelper, + private static async Task ExecutePipelineWithTracee( TPipeline pipeline, Func> startPipelineAsync, - RemoteTestExecution testExecution, + TestRunner testRunner, CancellationToken token, TaskCompletionSource waitTaskSource = null) where TPipeline : Pipeline { Task runTask = await startPipelineAsync(pipeline, token); - //Begin event production - testExecution.SendSignal(); + // Begin event production + testRunner.WakeupTracee(); - //Wait for event production to be done - testExecution.WaitForSignal(); + // Wait for event production to be done + testRunner.WaitForSignal(); try { @@ -88,7 +89,7 @@ private static async Task ExecutePipelineWithDebugee( { using var _ = token.Register(() => { - outputHelper.WriteLine("Did not receive completion signal before cancellation."); + testRunner.WriteLine("Did not receive completion signal before cancellation."); waitTaskSource.TrySetCanceled(token); }); @@ -103,8 +104,8 @@ private static async Task ExecutePipelineWithDebugee( } finally { - //Signal for debugee that's ok to end/move on. - testExecution.SendSignal(); + // Signal for debugee that's ok to end/move on. + testRunner.WakeupTracee(); } } } diff --git a/src/tests/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.UnitTests.csproj index a7ee91a2ff..7b2263b52e 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.UnitTests.csproj +++ b/src/tests/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.UnitTests.csproj @@ -1,17 +1,13 @@  - netcoreapp3.1;net5.0 + $(UnitTestTargetFrameworks) - - - false - diff --git a/src/tests/Microsoft.Diagnostics.Monitoring/PipelineTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring/PipelineTests.cs index 227e4a2927..eab58c889f 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring/PipelineTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring/PipelineTests.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Diagnostics.Monitoring; -using Microsoft.Diagnostics.Tracing.Parsers.Kernel; using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using Xunit; diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs deleted file mode 100644 index 965759bc5e..0000000000 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace Microsoft.Diagnostics.NETCore.Client -{ - public partial class CommonHelper - { - public static string HostExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? - (RuntimeInformation.ProcessArchitecture == Architecture.X86 ? - "..\\..\\..\\..\\..\\.dotnet\\x86\\dotnet.exe" : - "..\\..\\..\\..\\..\\.dotnet\\dotnet.exe") : - "../../../../../.dotnet/dotnet"; - - /// - /// gets the tracee path, with args for the dotnet host for finding the correct version of the runtime.!-- - /// example: "--fx-version 5.0.0-rc.1.12345.12 /path/to/tracee" - /// - public static string GetTraceePathWithArgs(string traceeName = "Tracee", string targetFramework = "netcoreapp3.1") - { - var curPath = Directory.GetCurrentDirectory(); - - var traceePath = curPath - .Replace(System.Reflection.Assembly.GetCallingAssembly().GetName().Name, traceeName) - .Replace("netcoreapp3.1", targetFramework); - - traceePath = Path.Combine(traceePath, Path.ChangeExtension(traceeName, ".dll")); - - - // CurrentDARCVersion is generated at build time by Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj - // This value will be set to whatever the value for the newest runtime in eng/Versions.Props is - if (targetFramework.Equals("net5.0", StringComparison.InvariantCultureIgnoreCase)) - traceePath = $"--fx-version {CurrentDARCVersion} {traceePath}"; - - return traceePath; - } - } -} \ No newline at end of file diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs index 8a761289ca..1a3c39212e 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.CommonTestRunner; +using Microsoft.Diagnostics.TestHelpers; using Microsoft.Diagnostics.Tracing; using System; using System.Collections.Generic; @@ -9,71 +11,72 @@ using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.NETCore.Client { public class EventPipeSessionTests { - private readonly ITestOutputHelper output; + private readonly ITestOutputHelper _output; + + public static IEnumerable Configurations => TestRunner.Configurations; public EventPipeSessionTests(ITestOutputHelper outputHelper) { - output = outputHelper; + _output = outputHelper; } - [Fact] - public Task BasicEventPipeSessionTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicEventPipeSessionTest(TestConfiguration config) { - return BasicEventPipeSessionTestCore(useAsync: false); + return BasicEventPipeSessionTestCore(config, useAsync: false); } - [Fact] - public Task BasicEventPipeSessionTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicEventPipeSessionTestAsync(TestConfiguration config) { - return BasicEventPipeSessionTestCore(useAsync: true); + return BasicEventPipeSessionTestCore(config, useAsync: true); } /// /// A simple test that checks if we can create an EventPipeSession on a child process /// - private async Task BasicEventPipeSessionTestCore(bool useAsync) - + private async Task BasicEventPipeSessionTestCore(TestConfiguration config, bool useAsync) { - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(), output); - runner.Start(timeoutInMSPipeCreation: 15_000, testProcessTimeout: 60_000); + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); + await runner.Start(testProcessTimeout: 60_000); DiagnosticsClientApiShim clientShim = new DiagnosticsClientApiShim(new DiagnosticsClient(runner.Pid), useAsync); - using (var session = await clientShim.StartEventPipeSession(new List() + // Don't dispose of the session here because it unnecessarily hangs the test for 30 secs + EventPipeSession session = await clientShim.StartEventPipeSession(new List() { new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational) - })) - { - Assert.True(session.EventStream != null); - } + }); + Assert.True(session.EventStream != null); runner.Stop(); } - [Fact] - public Task EventPipeSessionStreamTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task EventPipeSessionStreamTest(TestConfiguration config) { - return EventPipeSessionStreamTestCore(useAsync: false); + return EventPipeSessionStreamTestCore(config, useAsync: false); } - [Fact] - public Task EventPipeSessionStreamTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task EventPipeSessionStreamTestAsync(TestConfiguration config) { - return EventPipeSessionStreamTestCore(useAsync: true); + return EventPipeSessionStreamTestCore(config, useAsync: true); } /// /// Checks if we can create an EventPipeSession and can get some expected events out of it. /// - private async Task EventPipeSessionStreamTestCore(bool useAsync) + private async Task EventPipeSessionStreamTestCore(TestConfiguration config, bool useAsync) { - TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(), output); - runner.Start(timeoutInMSPipeCreation: 15_000, testProcessTimeout: 60_000); + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); + await runner.Start(testProcessTimeout: 60_000); DiagnosticsClientApiShim clientShim = new DiagnosticsClientApiShim(new DiagnosticsClient(runner.Pid), useAsync); - runner.PrintStatus(); - output.WriteLine($"[{DateTime.Now.ToString()}] Trying to start an EventPipe session on process {runner.Pid}"); + runner.WriteLine($"Trying to start an EventPipe session"); using (var session = await clientShim.StartEventPipeSession(new List() { new EventPipeProvider("System.Runtime", EventLevel.Informational, 0, new Dictionary() { @@ -86,47 +89,46 @@ private async Task EventPipeSessionStreamTestCore(bool useAsync) Task streamTask = Task.Run(() => { var source = new EventPipeEventSource(session.EventStream); source.Dynamic.All += (TraceEvent obj) => { - output.WriteLine("Got an event"); + runner.WriteLine("Got an event"); evntCnt += 1; }; try { source.Process(); } - catch (Exception e) + catch (Exception ex) { // This exception can happen if the target process exits while EventPipeEventSource is in the middle of reading from the pipe. - output.WriteLine("Error encountered while processing events"); - output.WriteLine(e.ToString()); + runner.WriteLine($"Error encountered while processing events {ex}"); } finally { - runner.Stop(); + runner.WakeupTracee(); } }); - output.WriteLine("Waiting for stream Task"); + runner.WriteLine("Waiting for stream Task"); streamTask.Wait(10000); - output.WriteLine("Done waiting for stream Task"); + runner.WriteLine("Done waiting for stream Task"); Assert.True(evntCnt > 0); } } - [Fact] - public Task EventPipeSessionUnavailableTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task EventPipeSessionUnavailableTest(TestConfiguration config) { - return EventPipeSessionUnavailableTestCore(useAsync: false); + return EventPipeSessionUnavailableTestCore(config, useAsync: false); } - [Fact] - public Task EventPipeSessionUnavailableTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task EventPipeSessionUnavailableTestAsync(TestConfiguration config) { - return EventPipeSessionUnavailableTestCore(useAsync: true); + return EventPipeSessionUnavailableTestCore(config, useAsync: true); } /// /// Tries to start an EventPipe session on a non-existent process /// - private async Task EventPipeSessionUnavailableTestCore(bool useAsync) + private async Task EventPipeSessionUnavailableTestCore(TestConfiguration config, bool useAsync) { List pids = new List(DiagnosticsClient.GetPublishedProcesses()); int arbitraryPid = 1; @@ -139,30 +141,29 @@ await Assert.ThrowsAsync(() => clientShim.StartEven })); } - [Fact] - public Task StartEventPipeSessionWithSingleProviderTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task StartEventPipeSessionWithSingleProviderTest(TestConfiguration config) { - return StartEventPipeSessionWithSingleProviderTestCore(useAsync: false); + return StartEventPipeSessionWithSingleProviderTestCore(config, useAsync: false); } - [Fact] - public Task StartEventPipeSessionWithSingleProviderTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task StartEventPipeSessionWithSingleProviderTestAsync(TestConfiguration config) { - return StartEventPipeSessionWithSingleProviderTestCore(useAsync: true); + return StartEventPipeSessionWithSingleProviderTestCore(config, useAsync: true); } /// /// Test for the method overload: public EventPipeSession StartEventPipeSession(EventPipeProvider provider, bool requestRundown=true, int circularBufferMB=256) /// - private async Task StartEventPipeSessionWithSingleProviderTestCore(bool useAsync) + private async Task StartEventPipeSessionWithSingleProviderTestCore(TestConfiguration config, bool useAsync) { - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(), output); - runner.Start(timeoutInMSPipeCreation: 15_000, testProcessTimeout: 60_000); + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); + await runner.Start(testProcessTimeout: 60_000); DiagnosticsClientApiShim clientShim = new DiagnosticsClientApiShim(new DiagnosticsClient(runner.Pid), useAsync); - using (var session = await clientShim.StartEventPipeSession(new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational))) - { - Assert.True(session.EventStream != null); - } + // Don't dispose of the session here because it unnecessarily hangs the test for 30 secs + EventPipeSession session = await clientShim.StartEventPipeSession(new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational)); + Assert.True(session.EventStream != null); runner.Stop(); } } diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessEnvironmentTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessEnvironmentTests.cs index 5a27f825fc..d191b42e20 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessEnvironmentTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessEnvironmentTests.cs @@ -2,53 +2,61 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; +using Microsoft.Diagnostics.TestHelpers; using System.Collections.Generic; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.NETCore.Client { public class ProcessEnvironmentTests { - private readonly ITestOutputHelper output; + private readonly ITestOutputHelper _output; + + public static IEnumerable Configurations => TestRunner.Configurations; public ProcessEnvironmentTests(ITestOutputHelper outputHelper) { - output = outputHelper; + _output = outputHelper; } - [Fact] - public Task BasicEnvTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicEnvTest(TestConfiguration config) { - return BasicEnvTestCore(useAsync: false); + return BasicEnvTestCore(config, useAsync: false); } - [Fact] - public Task BasicEnvTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicEnvTestAsync(TestConfiguration config) { - return BasicEnvTestCore(useAsync: true); + return BasicEnvTestCore(config, useAsync: true); } /// /// A simple test that collects process environment. /// - private async Task BasicEnvTestCore(bool useAsync) + private async Task BasicEnvTestCore(TestConfiguration config, bool useAsync) { + if (config.RuntimeFrameworkVersionMajor < 5) + { + throw new SkipTestException("Not supported on < .NET 5.0"); + } // as the attribute says, this test requires 5.0-rc1 or newer. This has been tested locally on // an rc1 build and passes. It is equivalent to the dotnet/runtime version of this test. - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), output); + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); string testKey = "FOO"; string testVal = "BAR"; runner.AddEnvVar(testKey, testVal); - runner.Start(timeoutInMSPipeCreation: 3000); + await runner.Start(); var clientShim = new DiagnosticsClientApiShim(new DiagnosticsClient(runner.Pid), useAsync); Dictionary env = await clientShim.GetProcessEnvironment(); Assert.True(env.ContainsKey(testKey) && env[testKey].Equals(testVal)); - runner.Stop(); + runner.WakeupTracee(); } } } diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessInfoTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessInfoTests.cs index 53b514b1e4..2b986fabf8 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessInfoTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessInfoTests.cs @@ -2,10 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.CommonTestRunner; +using Microsoft.Diagnostics.TestHelpers; using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.NETCore.Client { @@ -13,43 +19,49 @@ public class GetProcessInfoTests { private readonly ITestOutputHelper _output; + public static IEnumerable Configurations => TestRunner.Configurations; + public GetProcessInfoTests(ITestOutputHelper outputHelper) { _output = outputHelper; } - [Fact] - public Task BasicProcessInfoNoSuspendTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicProcessInfoNoSuspendTest(TestConfiguration config) { - return BasicProcessInfoTestCore(useAsync: false, suspend: false); + return BasicProcessInfoTestCore(config, useAsync: false, suspend: false); } - [Fact] - public Task BasicProcessInfoNoSuspendTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicProcessInfoNoSuspendTestAsync(TestConfiguration config) { - return BasicProcessInfoTestCore(useAsync: true, suspend: false); + return BasicProcessInfoTestCore(config, useAsync: true, suspend: false); } - [Fact] - public Task BasicProcessInfoSuspendTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicProcessInfoSuspendTest(TestConfiguration config) { - return BasicProcessInfoTestCore(useAsync: false, suspend: true); + return BasicProcessInfoTestCore(config, useAsync: false, suspend: true); } - [Fact] - public Task BasicProcessInfoSuspendTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public Task BasicProcessInfoSuspendTestAsync(TestConfiguration config) { - return BasicProcessInfoTestCore(useAsync: true, suspend: true); + return BasicProcessInfoTestCore(config, useAsync: true, suspend: true); } - private async Task BasicProcessInfoTestCore(bool useAsync, bool suspend) + private async Task BasicProcessInfoTestCore(TestConfiguration config, bool useAsync, bool suspend) { - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _output); + if (config.RuntimeFrameworkVersionMajor < 5) + { + throw new SkipTestException("Not supported on < .NET 5.0"); + } + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); if (suspend) { runner.SuspendDefaultDiagnosticPort(); } - runner.Start(); + await runner.Start(testProcessTimeout: 60_000, waitForTracee: !suspend); try { @@ -64,23 +76,36 @@ private async Task BasicProcessInfoTestCore(bool useAsync, bool suspend) Assert.True(string.IsNullOrEmpty(processInfoBeforeResume.ManagedEntrypointAssemblyName)); await clientShim.ResumeRuntime(); + + await runner.WaitForTracee(); } // The entrypoint information is available some short time after the runtime // begins to execute. Retry getting process information until entrypoint is available. ProcessInfo processInfo = await GetProcessInfoWithEntrypointAsync(clientShim); ValidateProcessInfo(runner.Pid, processInfo); + + // This is only true if targetFramework for the tracee app is greater than Assert.Equal("Tracee", processInfo.ManagedEntrypointAssemblyName); - // Validate values before resume (except for entrypoint) are the same after resume. if (suspend) { Assert.Equal(processInfoBeforeResume.ProcessId, processInfo.ProcessId); Assert.Equal(processInfoBeforeResume.RuntimeInstanceCookie, processInfo.RuntimeInstanceCookie); - Assert.Equal(processInfoBeforeResume.CommandLine, processInfo.CommandLine); Assert.Equal(processInfoBeforeResume.OperatingSystem, processInfo.OperatingSystem); Assert.Equal(processInfoBeforeResume.ProcessArchitecture, processInfo.ProcessArchitecture); Assert.Equal(processInfoBeforeResume.ClrProductVersionString, processInfo.ClrProductVersionString); + // Given we are in a .NET 6.0+ app, we should have ProcessInfo2 available. Pre and post pause should differ. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Equal($"\"{runner.ExePath}\" {runner.Arguments}", processInfoBeforeResume.CommandLine); + Assert.Equal($"\"{runner.ExePath}\" {runner.Arguments}", processInfo.CommandLine); + } + else + { + Assert.Equal($"{runner.ExePath}", processInfoBeforeResume.CommandLine); + Assert.Equal($"{runner.ExePath} {runner.ManagedArguments}", processInfo.CommandLine); + } } } finally diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs index 9f571b465e..ff77348124 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.TestHelpers; using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -9,6 +10,8 @@ using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.NETCore.Client { @@ -18,76 +21,75 @@ namespace Microsoft.Diagnostics.NETCore.Client /// public class GetPublishedProcessesTest { - private readonly ITestOutputHelper output; + private readonly ITestOutputHelper _output; + + public static IEnumerable Configurations => TestRunner.Configurations; public GetPublishedProcessesTest(ITestOutputHelper outputHelper) { - output = outputHelper; + _output = outputHelper; } - [Fact] - public void PublishedProcessTest1() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task PublishedProcessTest1(TestConfiguration config) { - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(), output); - runner.Start(timeoutInMSPipeCreation: 3000); - // On Windows, runner.Start will not wait for named pipe creation since for other tests, NamedPipeClientStream will - // just wait until the named pipe is created. - // For these tests, we need to sleep an arbitrary time before pipe is created. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Thread.Sleep(5000); - } + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); + await runner.Start(); + List publishedProcesses = new List(DiagnosticsClient.GetPublishedProcesses()); foreach (int p in publishedProcesses) { - output.WriteLine($"[{DateTime.Now.ToString()}] Saw published process {p}"); + runner.WriteLine($"Saw published process {p}"); } Assert.Contains(publishedProcesses, p => p == runner.Pid); - runner.Stop(); + runner.WakeupTracee(); } - [Fact] - public void MultiplePublishedProcessTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task MultiplePublishedProcessTest(TestConfiguration config) { TestRunner[] runner = new TestRunner[3]; int[] pids = new int[3]; - for (var i = 0; i < 3; i++) - { - runner[i] = new TestRunner(CommonHelper.GetTraceePathWithArgs(), output); - runner[i].Start(); - pids[i] = runner[i].Pid; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Thread.Sleep(5000); - } - List publishedProcesses = new List(DiagnosticsClient.GetPublishedProcesses()); - foreach (int p in publishedProcesses) + try { - output.WriteLine($"[{DateTime.Now.ToString()}] Saw published process {p}"); - } + for (var i = 0; i < 3; i++) + { + runner[i] = await TestRunner.Create(config, _output, "Tracee"); + await runner[i].Start(); + pids[i] = runner[i].Pid; + } - for (var i = 0; i < 3; i++) - { - Assert.Contains(publishedProcesses, p => p == pids[i]); - } + List publishedProcesses = new List(DiagnosticsClient.GetPublishedProcesses()); + foreach (int p in publishedProcesses) + { + _output.WriteLine($"[{DateTime.Now}] Saw published process {p}"); + } + + for (var i = 0; i < 3; i++) + { + Assert.Contains(publishedProcesses, p => p == pids[i]); + } - for (var i = 0 ; i < 3; i++) + for (var i = 0; i < 3; i++) + { + runner[i].WakeupTracee(); + } + } + finally { - runner[i].Stop(); + for (var i = 0; i < 3; i++) + { + await runner[i].DisposeAsync(); + } } } - [Fact] - public async Task WaitForConnectionTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task WaitForConnectionTest(TestConfiguration config) { - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(), output); - runner.Start(timeoutInMSPipeCreation: 3000); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Thread.Sleep(5000); - } + await using TestRunner runner = await TestRunner.Create(config, _output, "Tracee"); + await runner.Start(); var client = new DiagnosticsClient(runner.Pid); using var timeoutSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(250)); @@ -97,7 +99,7 @@ public async Task WaitForConnectionTest() } finally { - runner.Stop(); + runner.WakeupTracee(); } } } diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj b/src/tests/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj index b1c14ebee1..8db5cfa78f 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj @@ -1,66 +1,17 @@  - - - - $(IntermediateOutputPath)/DARCVersion.g.cs - - - - - - - - - - - - - - - - - - - - netcoreapp3.1;net5.0 + $(UnitTestTargetFrameworks) - - - + + + + - - - - + diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/RemoteTestExecution.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/RemoteTestExecution.cs deleted file mode 100644 index a3b592339d..0000000000 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/RemoteTestExecution.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Diagnostics.NETCore.Client; -using Xunit.Abstractions; - -namespace Microsoft.Diagnostics.NETCore.Client.UnitTests -{ - /// - /// Utility class to control remote test execution. - /// - public sealed class RemoteTestExecution : IAsyncDisposable - { - private Task IoReadingTask { get; } - - private ITestOutputHelper OutputHelper { get; } - - public TestRunner TestRunner { get; } - - private RemoteTestExecution(TestRunner runner, Task ioReadingTask, ITestOutputHelper outputHelper) - { - TestRunner = runner; - IoReadingTask = ioReadingTask; - OutputHelper = outputHelper; - } - - //Very simple signals that synchronize execution between the test process and the debuggee process. - - public void SendSignal() - { - //We cannot use named synchronization primitives since they do not work across processes - //on Linux. Use redirected standard input instead. - TestRunner.StandardInput.Write('0'); - TestRunner.StandardInput.Flush(); - } - - public void WaitForSignal() - { - var result = TestRunner.StandardOutput.ReadLine(); - if (string.Equals(result, "1")) - { - return; - } - } - - public static RemoteTestExecution StartProcess(string commandLine, ITestOutputHelper outputHelper, string reversedServerTransportName = null) - { - TestRunner runner = new TestRunner(commandLine, outputHelper, redirectError: true, redirectInput: true); - if (!string.IsNullOrEmpty(reversedServerTransportName)) - { - runner.SetDiagnosticPort(reversedServerTransportName, suspend: false); - } - runner.Start(testProcessTimeout: 60_000); - - Task readingTask = ReadAllOutput(runner.StandardOutput, runner.StandardError, outputHelper); - - return new RemoteTestExecution(runner, readingTask, outputHelper); - } - - private static Task ReadAllOutput(StreamReader output, StreamReader error, ITestOutputHelper outputHelper) - { - return Task.Run(async () => - { - try - { - Task stdErrorTask = error.ReadToEndAsync(); - - try - { - string result = await stdErrorTask; - outputHelper.WriteLine("Stderr:"); - if (result != null) - { - outputHelper.WriteLine(result); - } - } - catch (Exception e) - { - outputHelper.WriteLine("Error reading standard error from child process: " + e.ToString()); - } - } - catch (ObjectDisposedException) - { - outputHelper.WriteLine("Failed to collect remote process's output"); - } - }); - } - - public async ValueTask DisposeAsync() - { - using var timeoutSource = new CancellationTokenSource(TimeSpan.FromSeconds(1)); - try - { - await TestRunner.WaitForExitAsync(timeoutSource.Token); - } - catch (OperationCanceledException) - { - OutputHelper.WriteLine("Remote process did not exit within timeout period. Forcefully stopping process."); - TestRunner.Stop(); - } - - await IoReadingTask; - } - } -} diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/ReversedServerTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/ReversedServerTests.cs index 07f6a17170..e7a1867f1c 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/ReversedServerTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/ReversedServerTests.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.CommonTestRunner; +using Microsoft.Diagnostics.TestHelpers; +using Microsoft.Diagnostics.Tracing; using System; using System.Collections.Generic; using System.Diagnostics.Tracing; @@ -9,9 +12,10 @@ using System.Net; using System.Threading; using System.Threading.Tasks; -using Microsoft.Diagnostics.Tracing; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.NETCore.Client { @@ -23,6 +27,8 @@ public class ReversedServerTests private readonly ITestOutputHelper _outputHelper; + public static IEnumerable Configurations => TestRunner.Configurations; + public ReversedServerTests(ITestOutputHelper outputHelper) { _outputHelper = outputHelper; @@ -111,22 +117,22 @@ public async Task ReversedServerAcceptAsyncYieldsTest() Assert.True(acceptTask.IsCanceled); } - [Fact] - public async Task ReversedServerNonExistingRuntimeIdentifierTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerNonExistingRuntimeIdentifierTest(TestConfiguration config) { - await ReversedServerNonExistingRuntimeIdentifierTestCore(useAsync: false); + await ReversedServerNonExistingRuntimeIdentifierTestCore(config, useAsync: false); } - [Fact] - public async Task ReversedServerNonExistingRuntimeIdentifierTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerNonExistingRuntimeIdentifierTestAsync(TestConfiguration config) { - await ReversedServerNonExistingRuntimeIdentifierTestCore(useAsync: true); + await ReversedServerNonExistingRuntimeIdentifierTestCore(config, useAsync: true); } /// /// Tests that invoking server methods with non-existing runtime identifier appropriately fail. /// - private async Task ReversedServerNonExistingRuntimeIdentifierTestCore(bool useAsync) + private async Task ReversedServerNonExistingRuntimeIdentifierTestCore(TestConfiguration config, bool useAsync) { await using var server = CreateReversedServer(out string transportName); @@ -149,16 +155,16 @@ private async Task ReversedServerNonExistingRuntimeIdentifierTestCore(bool useAs Assert.False(server.RemoveConnection(Guid.NewGuid()), "Removal of nonexisting connection should fail."); } - [Fact] - public async Task ReversedServerSingleTargetMultipleUseClientTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerSingleTargetMultipleUseClientTest(TestConfiguration config) { - await ReversedServerSingleTargetMultipleUseClientTestCore(useAsync: false); + await ReversedServerSingleTargetMultipleUseClientTestCore(config, useAsync: false); } - [Fact] - public async Task ReversedServerSingleTargetMultipleUseClientTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerSingleTargetMultipleUseClientTestAsync(TestConfiguration config) { - await ReversedServerSingleTargetMultipleUseClientTestCore(useAsync: true); + await ReversedServerSingleTargetMultipleUseClientTestCore(config, useAsync: true); } /// @@ -170,18 +176,19 @@ public async Task ReversedServerSingleTargetMultipleUseClientTestAsync() /// because of how the endpoint is updated with new stream information each /// time the target process reconnects to the server. /// - private async Task ReversedServerSingleTargetMultipleUseClientTestCore(bool useAsync) + private async Task ReversedServerSingleTargetMultipleUseClientTestCore(TestConfiguration config, bool useAsync) { + if (config.RuntimeFrameworkVersionMajor < 5) + { + throw new SkipTestException("Not supported on < .NET 5.0"); + } await using var server = CreateReversedServer(out string transportName); server.Start(); - - TestRunner runner = null; IpcEndpointInfo info; - try - { - // Start client pointing to diagnostics server - runner = StartTracee(transportName); + // Start client pointing to diagnostics server + await using (TestRunner runner = await StartTracee(config, transportName)) + { info = await AcceptEndpointInfo(server, useAsync); await VerifyEndpointInfo(runner, info, useAsync); @@ -191,13 +198,10 @@ private async Task ReversedServerSingleTargetMultipleUseClientTestCore(bool useA await ResumeRuntime(info, useAsync); + await runner.WaitForTracee(); + await VerifySingleSession(info, useAsync); } - finally - { - _outputHelper.WriteLine("Stopping tracee."); - runner?.Stop(); - } // Wait some time for the process to exit await Task.Delay(TimeSpan.FromSeconds(1)); @@ -211,33 +215,34 @@ private async Task ReversedServerSingleTargetMultipleUseClientTestCore(bool useA await VerifyNoNewEndpointInfos(server, useAsync); } - [Fact] - public async Task ReversedServerSingleTargetExitsClientInviableTest() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerSingleTargetExitsClientInviableTest(TestConfiguration config) { - await ReversedServerSingleTargetExitsClientInviableTestCore(useAsync: false); + await ReversedServerSingleTargetExitsClientInviableTestCore(config, useAsync: false); } - [Fact] - public async Task ReversedServerSingleTargetExitsClientInviableTestAsync() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerSingleTargetExitsClientInviableTestAsync(TestConfiguration config) { - await ReversedServerSingleTargetExitsClientInviableTestCore(useAsync: true); + await ReversedServerSingleTargetExitsClientInviableTestCore(config, useAsync: true); } /// /// Tests that a DiagnosticsClient is not viable after target exists. /// - private async Task ReversedServerSingleTargetExitsClientInviableTestCore(bool useAsync) + private async Task ReversedServerSingleTargetExitsClientInviableTestCore(TestConfiguration config, bool useAsync) { + if (config.RuntimeFrameworkVersionMajor < 5) + { + throw new SkipTestException("Not supported on < .NET 5.0"); + } await using var server = CreateReversedServer(out string transportName); server.Start(); - TestRunner runner = null; + // Start client pointing to diagnostics server IpcEndpointInfo info; - try + await using (TestRunner runner = await StartTracee(config, transportName)) { - // Start client pointing to diagnostics server - runner = StartTracee(transportName); - // Get client connection info = await AcceptEndpointInfo(server, useAsync); @@ -248,13 +253,10 @@ private async Task ReversedServerSingleTargetExitsClientInviableTestCore(bool us await ResumeRuntime(info, useAsync); + await runner.WaitForTracee(); + await VerifyWaitForConnection(info, useAsync); } - finally - { - _outputHelper.WriteLine("Stopping tracee."); - runner?.Stop(); - } // Wait some time for the process to exit await Task.Delay(TimeSpan.FromSeconds(1)); @@ -272,22 +274,22 @@ private async Task ReversedServerSingleTargetExitsClientInviableTestCore(bool us /// Validates that the does not create a new server /// transport during disposal. /// - [Fact] - public async Task ReversedServerNoCreateTransportAfterDispose() + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReversedServerNoCreateTransportAfterDispose(TestConfiguration config) { + if (config.RuntimeFrameworkVersionMajor < 5) + { + throw new SkipTestException("Not supported on < .NET 5.0"); + } var transportCallback = new IpcServerTransportCallback(); - int transportVersion = 0; - TestRunner runner = null; - try - { - await using var server = CreateReversedServer(out string transportName); - server.TransportCallback = transportCallback; - server.Start(); - // Start client pointing to diagnostics server - runner = StartTracee(transportName); + await using var server = CreateReversedServer(out string transportName); + server.TransportCallback = transportCallback; + server.Start(); + await using (TestRunner runner = await StartTracee(config, transportName)) + { // Get client connection IpcEndpointInfo info = await AcceptEndpointInfo(server, useAsync: true); @@ -298,17 +300,14 @@ public async Task ReversedServerNoCreateTransportAfterDispose() await ResumeRuntime(info, useAsync: true); + await runner.WaitForTracee(); + await VerifyWaitForConnection(info, useAsync: true); transportVersion = await transportCallback.GetStableTransportVersion(); // Server will be disposed } - finally - { - _outputHelper.WriteLine("Stopping tracee."); - runner?.Stop(); - } // Check that the reversed server did not create a new server transport upon disposal. Assert.Equal(transportVersion, await transportCallback.GetStableTransportVersion()); @@ -328,12 +327,11 @@ private async Task AcceptEndpointInfo(ReversedDiagnosticsServer return await shim.Accept(DefaultPositiveVerificationTimeout); } - private TestRunner StartTracee(string transportName) + private async Task StartTracee(TestConfiguration config, string transportName) { - _outputHelper.WriteLine("Starting tracee."); - var runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _outputHelper); + TestRunner runner = await TestRunner.Create(config, _outputHelper, "Tracee"); runner.SetDiagnosticPort(transportName, suspend: true); - runner.Start(); + await runner.Start(waitForTracee: false); return runner; } @@ -364,7 +362,7 @@ private async Task VerifyNoNewEndpointInfos(ReversedDiagnosticsServer server, bo /// private async Task VerifyEndpointInfo(TestRunner runner, IpcEndpointInfo info, bool useAsync, bool expectTimeout = false) { - _outputHelper.WriteLine($"Verifying connection information for process ID {runner.Pid}."); + runner.WriteLine("Verifying connection information for process"); Assert.NotNull(runner); Assert.Equal(runner.Pid, info.ProcessId); Assert.NotEqual(Guid.Empty, info.RuntimeInstanceCookie); @@ -372,7 +370,7 @@ private async Task VerifyEndpointInfo(TestRunner runner, IpcEndpointInfo info, b await VerifyWaitForConnection(info, useAsync, expectTimeout); - _outputHelper.WriteLine($"Connection: {info.DebuggerDisplay}"); + runner.WriteLine($"Connection: {info.DebuggerDisplay}"); } private async Task ResumeRuntime(IpcEndpointInfo info, bool useAsync) diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs deleted file mode 100644 index 88fa0014ee..0000000000 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -using System; -using System.Linq; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Xunit.Abstractions; -using System.Collections.Generic; - -namespace Microsoft.Diagnostics.NETCore.Client -{ - public class TestRunner : IDisposable - { - private Process testProcess; - private ProcessStartInfo startInfo; - private ITestOutputHelper outputHelper; - private CancellationTokenSource cts; - - public TestRunner(string testExePath, ITestOutputHelper _outputHelper = null, - bool redirectError = false, bool redirectInput = false, Dictionary envVars = null) - { - startInfo = new ProcessStartInfo(CommonHelper.HostExe, testExePath); - startInfo.UseShellExecute = false; - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = redirectError; - startInfo.RedirectStandardInput = redirectInput; - envVars?.ToList().ForEach(item => startInfo.Environment.Add(item.Key, item.Value)); - outputHelper = _outputHelper; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - try - { - // Make a good will attempt to end the tracee process - // and its process tree - testProcess?.Kill(entireProcessTree: true); - } - catch {} - - if(disposing) - { - testProcess?.Dispose(); - } - - cts.Dispose(); - } - - public void AddEnvVar(string key, string value) - { - startInfo.EnvironmentVariables[key] = value; - } - - public StreamWriter StandardInput => testProcess.StandardInput; - public StreamReader StandardOutput => testProcess.StandardOutput; - public StreamReader StandardError => testProcess.StandardError; - - public void Start(int timeoutInMSPipeCreation=15_000, int testProcessTimeout=30_000) - { - if (outputHelper != null) - outputHelper.WriteLine($"[{DateTime.Now.ToString()}] Launching test: " + startInfo.FileName + " " + startInfo.Arguments); - - testProcess = new Process(); - testProcess.StartInfo = startInfo; - testProcess.EnableRaisingEvents = true; - - if (!testProcess.Start()) - { - outputHelper.WriteLine($"Could not start process: " + startInfo.FileName); - } - - if (testProcess.HasExited) - { - outputHelper.WriteLine($"Process " + startInfo.FileName + " came back as exited"); - } - - cts = new CancellationTokenSource(testProcessTimeout); - cts.Token.Register(() => testProcess.Kill()); - - if (outputHelper != null) - { - outputHelper.WriteLine($"[{DateTime.Now.ToString()}] Successfully started process {testProcess.Id}"); - // Retry getting the module count because we can catch the process during startup and it fails temporarily. - for (int retry = 0; retry < 5; retry++) - { - try - { - outputHelper.WriteLine($"Have total {testProcess.Modules.Count} modules loaded"); - break; - } - catch (Win32Exception) - { - } - } - } - - // Block until we see the IPC channel created, or until timeout specified. - Task monitorSocketTask = Task.Run(() => - { - while (true) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // On Windows, named pipe connection will block until the named pipe is ready to connect so no need to block here - break; - } - else - { - // On Linux, we wait until the socket is created. - var matchingFiles = Directory.GetFiles(Path.GetTempPath(), $"dotnet-diagnostic-{testProcess.Id}-*-socket"); // Try best match. - if (matchingFiles.Length > 0) - { - break; - } - } - Task.Delay(100); - } - }); - - monitorSocketTask.Wait(TimeSpan.FromMilliseconds(timeoutInMSPipeCreation)); - } - - public void Stop() - { - this.Dispose(); - } - - public int Pid { - get { return testProcess.Id; } - } - - public void PrintStatus() - { - if (testProcess.HasExited) - { - outputHelper.WriteLine($"Process {testProcess.Id} status: Exited 0x{testProcess.ExitCode:X}"); - } - else - { - outputHelper.WriteLine($"Process {testProcess.Id} status: Running"); - } - } - - public async Task WaitForExitAsync(CancellationToken token) - { - TaskCompletionSource exitedSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - EventHandler exitedHandler = (s, e) => exitedSource.TrySetResult(null); - - testProcess.Exited += exitedHandler; - try - { - if (!testProcess.HasExited) - { - using var _ = token.Register(() => exitedSource.TrySetCanceled(token)); - - await exitedSource.Task; - } - } - finally - { - testProcess.Exited -= exitedHandler; - } - } - } -} diff --git a/src/tests/StackTracee/Program.cs b/src/tests/StackTracee/Program.cs index 12dc739197..efbd514851 100644 --- a/src/tests/StackTracee/Program.cs +++ b/src/tests/StackTracee/Program.cs @@ -10,7 +10,7 @@ class Program { static void Main(string[] args) { - Console.ReadKey(); + Console.Read(); } } } diff --git a/src/tests/StackTracee/StackTracee.csproj b/src/tests/StackTracee/StackTracee.csproj index cd0510ad22..6b634ef181 100644 --- a/src/tests/StackTracee/StackTracee.csproj +++ b/src/tests/StackTracee/StackTracee.csproj @@ -2,6 +2,6 @@ Exe $(BuildProjectFramework) - netcoreapp3.1;net5.0 + netcoreapp3.1;net6.0;net7.0 diff --git a/src/tests/Tracee/Program.cs b/src/tests/Tracee/Program.cs index 8250ea7c8e..2107a7f9fa 100644 --- a/src/tests/Tracee/Program.cs +++ b/src/tests/Tracee/Program.cs @@ -3,27 +3,50 @@ // See the LICENSE file in the project root for more information. using System; -using System.Threading; +using System.Diagnostics; +using System.IO.Pipes; namespace Tracee { class Program { - private const int LoopCount = 30; - - static void Main(string[] args) + public static int Main(string[] args) { - Console.WriteLine("Sleep in loop for {0} seconds.", LoopCount); - - // Runs for max of 30 sec - for (var i = 0; i < LoopCount; i++) + int pid = Process.GetCurrentProcess().Id; + string pipeServerName = args.Length > 0 ? args[0] : null; + if (pipeServerName == null) { - Console.WriteLine("Iteration #{0}", i); - Thread.Sleep(1000); + Console.Error.WriteLine($"{pid} Tracee: no pipe name"); + Console.Error.Flush(); + return -1; } + Console.WriteLine($"{pid} Tracee: pipe server: {pipeServerName}"); + Console.Out.Flush(); + try + { + using var pipeStream = new NamedPipeClientStream(pipeServerName); + + Console.WriteLine("{0} Tracee: connecting to pipe", pid); + Console.Out.Flush(); + pipeStream.Connect(5 * 60 * 1000); + Console.WriteLine("{0} Tracee: connected to pipe", pid); + Console.Out.Flush(); - Console.WriteLine("Press any key to exit."); - Console.ReadKey(); + // Wait for server to send something + int input = pipeStream.ReadByte(); + + Console.WriteLine("{0} Tracee: waking up {1}", pid, input); + Console.Out.Flush(); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.ToString()); + Console.Error.Flush(); + return -1; + } + Console.WriteLine("{0} Tracee: exiting normally", pid); + Console.Out.Flush(); + return 0; } } } diff --git a/src/tests/Tracee/Tracee.csproj b/src/tests/Tracee/Tracee.csproj index cd0510ad22..6b634ef181 100644 --- a/src/tests/Tracee/Tracee.csproj +++ b/src/tests/Tracee/Tracee.csproj @@ -2,6 +2,6 @@ Exe $(BuildProjectFramework) - netcoreapp3.1;net5.0 + netcoreapp3.1;net6.0;net7.0 diff --git a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj index 3e891e2a45..06c1878141 100644 --- a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj +++ b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 diff --git a/src/tests/dotnet-stack/DotnetStack.UnitTests.csproj b/src/tests/dotnet-stack/DotnetStack.UnitTests.csproj index 94b587a2ec..7cdad15afc 100644 --- a/src/tests/dotnet-stack/DotnetStack.UnitTests.csproj +++ b/src/tests/dotnet-stack/DotnetStack.UnitTests.csproj @@ -1,12 +1,12 @@ - net5.0 + net6.0 - - + + diff --git a/src/tests/dotnet-stack/StackTests.cs b/src/tests/dotnet-stack/StackTests.cs index e98820c08e..c106d1e366 100644 --- a/src/tests/dotnet-stack/StackTests.cs +++ b/src/tests/dotnet-stack/StackTests.cs @@ -2,45 +2,71 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Diagnostics.NETCore.Client; +using Microsoft.Diagnostics.TestHelpers; using System; +using System.Collections.Generic; using System.CommandLine; using System.CommandLine.IO; using System.CommandLine.Parsing; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Tools.Stack { public class StackTests { - private readonly ITestOutputHelper output; + private readonly ITestOutputHelper _output; + + private const string _correctStack70 = @" [Native Frames] + System.Console.il!Interop+Kernel32.ReadFile(int,unsigned int8*,int32,int32&,int) + System.Console.il!System.ConsolePal+WindowsConsoleStream.ReadFileNative(int,value class System.Span`1,bool,int32&,bool) + System.Console.il!System.ConsolePal+WindowsConsoleStream.Read(value class System.Span`1) + System.Console.il!System.IO.ConsoleStream.Read(unsigned int8[],int32,int32) + System.Private.CoreLib.il!System.IO.StreamReader.ReadBuffer() + System.Private.CoreLib.il!System.IO.StreamReader.Read() + System.Console.il!System.IO.SyncTextReader.Read() + System.Console.il!System.Console.Read() + ?!?"; + + private const string _correctStack60 = @" [Native Frames] + System.Console.il!System.ConsolePal+WindowsConsoleStream.ReadFileNative(int,value class System.Span`1,bool,int32&,bool) + System.Console.il!System.ConsolePal+WindowsConsoleStream.Read(value class System.Span`1) + System.Console.il!System.IO.ConsoleStream.Read(unsigned int8[],int32,int32) + System.Private.CoreLib.il!System.IO.StreamReader.ReadBuffer() + System.Private.CoreLib.il!System.IO.StreamReader.Read() + System.Console.il!System.IO.SyncTextReader.Read() + System.Console.il!System.Console.Read() + StackTracee!Tracee.Program.Main(class System.String[])"; - private readonly string correctStack = @" [Native Frames] - System.Console!System.IO.StdInReader.ReadKey(bool&) - System.Console!System.IO.SyncTextReader.ReadKey(bool&) - System.Console!System.ConsolePal.ReadKey(bool) - System.Console!System.Console.ReadKey() + private const string _correctStack31 = @" [Native Frames] + System.Console.il!System.ConsolePal+WindowsConsoleStream.ReadFileNative(int,unsigned int8[],int32,int32,bool,int32&,bool) + System.Console.il!System.ConsolePal+WindowsConsoleStream.Read(unsigned int8[],int32,int32) + System.Private.CoreLib.il!System.IO.StreamReader.ReadBuffer() + System.Private.CoreLib.il!System.IO.StreamReader.Read() + System.Console.il!System.IO.SyncTextReader.Read() + System.Console.il!System.Console.Read() StackTracee!Tracee.Program.Main(class System.String[])"; + public static IEnumerable Configurations => TestRunner.Configurations; + public StackTests(ITestOutputHelper outputHelper) { - output = outputHelper; + _output = outputHelper; } - [Theory] - [InlineData("netcoreapp3.1")] - [InlineData("net5.0")] - public async Task ReportsStacksCorrectly(string traceeFramework) + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task ReportsStacksCorrectly(TestConfiguration config) { Command reportCommand = ReportCommandHandler.ReportCommand(); var console = new TestConsole(); var parser = new Parser(reportCommand); - using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(traceeName: "StackTracee", targetFramework: traceeFramework), output); - runner.Start(); + await using TestRunner runner = await TestRunner.Create(config, _output, "StackTracee", usePipe: false); + await runner.Start(); // Wait for tracee to get to readkey call await Task.Delay(TimeSpan.FromSeconds(1)); @@ -49,10 +75,16 @@ public async Task ReportsStacksCorrectly(string traceeFramework) string report = console.Out.ToString(); - output.WriteLine($"REPORT_START\n{report}REPORT_END"); + runner.WriteLine($"REPORT_START\n{report}REPORT_END"); Assert.True(!string.IsNullOrEmpty(report)); - + string correctStack = config.RuntimeFrameworkVersionMajor switch + { + 7 => _correctStack70, + 6 => _correctStack60, + 3 => _correctStack31, + _ => throw new NotSupportedException($"Runtime version {config.RuntimeFrameworkVersionMajor} not supported") + }; string[] correctStackParts = correctStack.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); string[] stackParts = report.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); @@ -68,4 +100,4 @@ public async Task ReportsStacksCorrectly(string traceeFramework) } } } -} \ No newline at end of file +} diff --git a/src/tests/dotnet-trace/ChildProcessTests.cs b/src/tests/dotnet-trace/ChildProcessTests.cs index 14101e28c9..35a003046f 100644 --- a/src/tests/dotnet-trace/ChildProcessTests.cs +++ b/src/tests/dotnet-trace/ChildProcessTests.cs @@ -2,19 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Diagnostics.NETCore.Client; -using System; -using Xunit; -using Xunit.Abstractions; +using Microsoft.Diagnostics.CommonTestRunner; +using Microsoft.Diagnostics.TestHelpers; using System.Collections.Generic; -using System.Linq; using System.Diagnostics; +using System.Text; +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; namespace Microsoft.Diagnostics.Tools.Trace { public class ChildProcessTests { + public static IEnumerable Configurations => TestRunner.Configurations; + // Pass ITestOutputHelper into the test class, which xunit provides per-test public ChildProcessTests(ITestOutputHelper outputHelper) { @@ -23,10 +27,35 @@ public ChildProcessTests(ITestOutputHelper outputHelper) private ITestOutputHelper OutputHelper { get; } - private void LaunchDotNetTrace(string command, out int exitCode, out string stdOut, out string stdErr) + private void LaunchDotNetTrace(TestConfiguration config, string dotnetTraceCommand, string traceeArguments, out int exitCode, out string stdOut, out string stdErr) { - string dotnetTracePathWithArgs = CommonHelper.GetTraceePathWithArgs(traceeName: "dotnet-trace").Replace("net5.0", "netcoreapp3.1"); - ProcessStartInfo startInfo = new ProcessStartInfo(CommonHelper.HostExe, $"{dotnetTracePathWithArgs} {command}"); + if (config.RuntimeFrameworkVersionMajor < 5) + { + throw new SkipTestException("Not supported on < .NET 5.0"); + } + DebuggeeConfiguration debuggeeConfig = DebuggeeCompiler.Execute(config, "ExitCodeTracee", OutputHelper).GetAwaiter().GetResult(); + + var dotnetTraceArguments = new StringBuilder(); + dotnetTraceArguments.Append(config.DotNetTracePath()); + dotnetTraceArguments.Append(' '); + dotnetTraceArguments.Append(dotnetTraceCommand); + dotnetTraceArguments.Append(" -- "); + + if (!string.IsNullOrWhiteSpace(config.HostExe)) + { + dotnetTraceArguments.Append(config.HostExe); + dotnetTraceArguments.Append(' '); + if (!string.IsNullOrWhiteSpace(config.HostArgs)) + { + dotnetTraceArguments.Append(config.HostArgs); + dotnetTraceArguments.Append(' '); + } + } + dotnetTraceArguments.Append(debuggeeConfig.BinaryExePath); + dotnetTraceArguments.Append(' '); + dotnetTraceArguments.Append(traceeArguments); + + ProcessStartInfo startInfo = new ProcessStartInfo(config.DotNetTraceHost(), dotnetTraceArguments.ToString()); OutputHelper.WriteLine($"Launching: {startInfo.FileName} {startInfo.Arguments}"); startInfo.RedirectStandardInput = true; @@ -53,45 +82,47 @@ private void LaunchDotNetTrace(string command, out int exitCode, out string stdO Assert.True(processExitedCleanly, "Launched process failed to exit"); exitCode = process.ExitCode; } + + if (!string.IsNullOrWhiteSpace(stdErr)) + { + OutputHelper.WriteLine(stdErr); + } } - [Theory] - [InlineData("232", 232)] - [InlineData("0", 0)] - public void VerifyExitCode(string commandLineArg, int exitCode) + [SkippableTheory, MemberData(nameof(Configurations))] + public void VerifyExitCode(TestConfiguration config) { - string exitCodeTraceePath = CommonHelper.GetTraceePathWithArgs(traceeName: "ExitCodeTracee", targetFramework: "net5.0"); + VerifyExitCodeX(config, "232", 232); + VerifyExitCodeX(config, "0", 0); + } - LaunchDotNetTrace($"collect -o verifyexitcode.nettrace -- {CommonHelper.HostExe} {exitCodeTraceePath} {commandLineArg}", out int dotnetTraceExitCode, out string stdOut, out string stdErr); + private void VerifyExitCodeX(TestConfiguration config, string commandLineArg, int exitCode) + { + LaunchDotNetTrace(config, "collect -o verifyexitcode.nettrace", commandLineArg, out int dotnetTraceExitCode, out string stdOut, out string stdErr); Assert.Equal(exitCode, dotnetTraceExitCode); - Assert.Contains($"Process exited with code '{exitCode}'.", stdOut); } - [Theory] - [InlineData("0 this is a message", new string[] { "\nthis\n", "\nis\n", "\na\n" })] - public void VerifyHideIO(string commandLineArg, string[] stringsInOutput) + [SkippableTheory, MemberData(nameof(Configurations))] + public void VerifyHideIO(TestConfiguration config) { - string exitCodeTraceePath = CommonHelper.GetTraceePathWithArgs(traceeName: "ExitCodeTracee", targetFramework: "net5.0"); - - LaunchDotNetTrace($"collect -o VerifyHideIO.nettrace -- {CommonHelper.HostExe} {exitCodeTraceePath} {commandLineArg}", out int dotnetTraceExitCode, out string stdOut, out string stdErr); + LaunchDotNetTrace(config, "collect -o VerifyHideIO.nettrace", "0 this is a message", out int dotnetTraceExitCode, out string stdOut, out string stdErr); Assert.Equal(0, dotnetTraceExitCode); stdOut = stdOut.Replace("\r", ""); + string[] stringsInOutput = new string[] { "\nthis\n", "\nis\n", "\na\n" }; foreach (string s in stringsInOutput) Assert.DoesNotContain(s, stdOut); } - [Theory] - [InlineData("0 this is a message", new string[] { "\nthis\n", "\nis\n", "\na\n" })] - public void VerifyShowIO(string commandLineArg, string[] stringsInOutput) + [SkippableTheory, MemberData(nameof(Configurations))] + public void VerifyShowIO(TestConfiguration config) { - string exitCodeTraceePath = CommonHelper.GetTraceePathWithArgs(traceeName: "ExitCodeTracee", targetFramework: "net5.0"); - - LaunchDotNetTrace($"collect -o VerifyShowIO.nettrace --show-child-io -- {CommonHelper.HostExe} {exitCodeTraceePath} {commandLineArg}", out int dotnetTraceExitCode, out string stdOut, out string stdErr); + LaunchDotNetTrace(config, "collect -o VerifyShowIO.nettrace --show-child-io", "0 this is a message", out int dotnetTraceExitCode, out string stdOut, out string stdErr); Assert.Equal(0, dotnetTraceExitCode); stdOut = stdOut.Replace("\r", ""); + string[] stringsInOutput = new string[] { "\nthis\n", "\nis\n", "\na\n" }; foreach (string s in stringsInOutput) Assert.Contains(s, stdOut); } diff --git a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj index 5c99730da2..fb372850fe 100644 --- a/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj +++ b/src/tests/dotnet-trace/DotnetTrace.UnitTests.csproj @@ -1,13 +1,12 @@ - + - net5.0 + net6.0 - - - + + diff --git a/src/tests/eventpipe/EventPipe.UnitTests.csproj b/src/tests/eventpipe/EventPipe.UnitTests.csproj index f6c26727c4..efa46f1b5c 100644 --- a/src/tests/eventpipe/EventPipe.UnitTests.csproj +++ b/src/tests/eventpipe/EventPipe.UnitTests.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + $(UnitTestTargetFrameworks) diff --git a/src/tests/eventpipe/GCEvents.cs b/src/tests/eventpipe/GCEvents.cs index ad36377fa2..02f4fd5093 100644 --- a/src/tests/eventpipe/GCEvents.cs +++ b/src/tests/eventpipe/GCEvents.cs @@ -10,6 +10,8 @@ using EventPipe.UnitTests.Common; using Microsoft.Diagnostics.NETCore.Client; using Microsoft.Diagnostics.Tracing; +using System.Reflection; +using System.Runtime.InteropServices; namespace EventPipe.UnitTests.GCEventsValidation { @@ -188,8 +190,16 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => Action _eventGeneratingAction = () => { List testList = new List(); - for(int i = 0; i < 100000000; i ++) + for (int i = 0; i < 100_000_000; i ++) { + // This test was failing (no GCFreeSegment callbacks) on x86 until this GC Collects happened. + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + if (i % 1_000_000 == 0) + { + GC.Collect(); + } + } string t = "Test string!"; testList.Add(t); } @@ -216,7 +226,9 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => Logger.logger.Log("GCCreateSegmentEvents: " + GCCreateSegmentEvents); Logger.logger.Log("GCFreeSegmentEvents: " + GCFreeSegmentEvents); - bool GCSegmentResult = GCCreateSegmentEvents > 0 && GCFreeSegmentEvents > 0; + + // Disable checking GCFreeSegmentEvents on .NET 7.0 issue: https://github.com/dotnet/diagnostics/issues/3143 + bool GCSegmentResult = GCCreateSegmentEvents > 0 && (GCFreeSegmentEvents > 0 || Environment.Version.Major >= 7); Logger.logger.Log("GCSegmentResult: " + GCSegmentResult); Logger.logger.Log("GCAllocationTickEvents: " + GCAllocationTickEvents); diff --git a/src/tests/eventpipe/LoaderEvents.cs b/src/tests/eventpipe/LoaderEvents.cs index 6f68063aa7..50c21c4ebb 100644 --- a/src/tests/eventpipe/LoaderEvents.cs +++ b/src/tests/eventpipe/LoaderEvents.cs @@ -54,7 +54,7 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => GetAssemblyPath(); try { - for(int i=0; i<100; i++) + for (int i = 0; i < 100; i++) { if (i % 10 == 0) Logger.logger.Log($"Load/Unload Assembly {i} times..."); @@ -62,6 +62,7 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => assemblyLoad.LoadFromAssemblyPath(assemblyPath+"\\Microsoft.Diagnostics.Runtime.dll"); assemblyLoad.Unload(); } + GC.Collect(); } catch(Exception ex) {