Push Microsoft.Diagnostics.NETCore.Client to release branch (#705)
authorSung Yoon Whang <suwhang@microsoft.com>
Tue, 17 Dec 2019 20:32:48 +0000 (12:32 -0800)
committerGitHub <noreply@github.com>
Tue, 17 Dec 2019 20:32:48 +0000 (12:32 -0800)
* Microsoft.Diagnostics.NETCore.Client Implementation (#617)

This is the initial implementation of Microsoft.Diagnostics.NETCore.Client library.

This will be released with version 1.0.0-preview, and when I feel that enough customers have validated it that we can match it with the rest of the diagnostics OOB packages and bump up the version to 3.1.

* add .NET Standard 2.0 for diagnostics client library (#700)

* Make MultiplePublishedProcessTest more stable (#701)

* Make MultiplePublishedProcessTest more stable

* Bump up DiagnosticsClient library version to 0.2.0 (#704)

44 files changed:
diagnostics.sln
src/Microsoft.Diagnostics.NETCore.Client/BinaryWriterExtensions.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClientExceptions.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DumpType.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeProvider.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcClient.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcHeader.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcMessage.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.csproj [new file with mode: 0644]
src/Tools/Common/Commands/ProcessStatus.cs
src/Tools/dotnet-counters/CounterMonitor.cs
src/Tools/dotnet-counters/Program.cs
src/Tools/dotnet-counters/dotnet-counters.csproj
src/Tools/dotnet-dump/Dumper.cs
src/Tools/dotnet-dump/Program.cs
src/Tools/dotnet-dump/dotnet-dump.csproj
src/Tools/dotnet-gcdump/CommandLine/CollectCommandHandler.cs
src/Tools/dotnet-gcdump/CommandLine/ProcessStatusCommandHandler.cs
src/Tools/dotnet-gcdump/DotNetHeapDump/EventPipeDotNetHeapDumper.cs
src/Tools/dotnet-gcdump/dotnet-gcdump.csproj
src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs
src/Tools/dotnet-trace/CommandLine/Commands/ConvertCommand.cs
src/Tools/dotnet-trace/CommandLine/Commands/ListProcessesCommandHandler.cs
src/Tools/dotnet-trace/CommandLine/Commands/ListProfilesCommandHandler.cs
src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs [deleted file]
src/Tools/dotnet-trace/Extensions.cs
src/Tools/dotnet-trace/Profile.cs
src/Tools/dotnet-trace/Program.cs
src/Tools/dotnet-trace/dotnet-trace.csproj
src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeProviderTests.cs [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.NETCore.Client/WriteDumpTests.cs [new file with mode: 0644]
src/tests/Tracee/Program.cs [new file with mode: 0644]
src/tests/Tracee/Tracee.csproj [new file with mode: 0644]
src/tests/dotnet-trace/ProfileProviderMerging.cs
src/tests/dotnet-trace/ProviderParsing.cs

index 6796cda20753ff4f6e916edac704d7ecf3a5d9f5..45df2c9e38a392506ff2b41e7b2fabb33f2504de 100644 (file)
@@ -55,6 +55,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-gcdump", "src\Tools\
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetCounters.UnitTests", "src\tests\dotnet-counters\DotnetCounters.UnitTests.csproj", "{E5A7DC6C-BF8D-418A-BCBD-094EB748FA82}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.NETCore.Client", "src\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj", "{D8BE9C81-194E-43E5-82CF-2080892FBDE7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.NETCore.Client.UnitTests", "src\tests\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj", "{6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tracee", "src\tests\Tracee\Tracee.csproj", "{C79D6069-2C18-48CB-846E-71F7168C2F7D}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventPipe.UnitTests", "src\tests\eventpipe\EventPipe.UnitTests.csproj", "{CED9ABBA-861E-4C0A-9359-22351208EF27}"
 EndProject
 Global
@@ -890,6 +896,126 @@ Global
                {E5A7DC6C-BF8D-418A-BCBD-094EB748FA82}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
                {E5A7DC6C-BF8D-418A-BCBD-094EB748FA82}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
                {E5A7DC6C-BF8D-418A-BCBD-094EB748FA82}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|Any CPU.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|ARM.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|ARM.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|ARM64.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|x64.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Checked|x86.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|ARM.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|ARM64.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|x64.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Debug|x86.Build.0 = Debug|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|Any CPU.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|ARM.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|ARM.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|ARM64.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|x64.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|x64.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|x86.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.Release|x86.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|Any CPU.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|ARM.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|ARM.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|ARM64.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|x64.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Checked|x86.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|ARM.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|ARM64.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|x64.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Debug|x86.Build.0 = Debug|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|Any CPU.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|ARM.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|ARM.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|ARM64.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|x64.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|x64.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|x86.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.Release|x86.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|Any CPU.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|ARM.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|ARM.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|ARM64.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|x64.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Checked|x86.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|ARM.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|ARM64.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|x64.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Debug|x86.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|Any CPU.Build.0 = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|ARM.ActiveCfg = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|ARM.Build.0 = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|ARM64.Build.0 = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|x64.ActiveCfg = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|x64.Build.0 = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|x86.ActiveCfg = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.Release|x86.Build.0 = Release|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|ARM.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|ARM.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|ARM64.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|ARM64.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|x86.ActiveCfg = Debug|Any CPU
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D}.RelWithDebInfo|x86.Build.0 = Debug|Any CPU
                {CED9ABBA-861E-4C0A-9359-22351208EF27}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
                {CED9ABBA-861E-4C0A-9359-22351208EF27}.Checked|Any CPU.Build.0 = Debug|Any CPU
                {CED9ABBA-861E-4C0A-9359-22351208EF27}.Checked|ARM.ActiveCfg = Debug|Any CPU
@@ -960,6 +1086,9 @@ Global
                {AEDCCF5B-5AD0-4D64-BF73-5CF468E07D22} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
                {936678B3-3392-4F4F-943C-B6A4BFCBAADC} = {B62728C8-1267-4043-B46F-5537BBAEC692}
                {E5A7DC6C-BF8D-418A-BCBD-094EB748FA82} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
+               {D8BE9C81-194E-43E5-82CF-2080892FBDE7} = {19FAB78C-3351-4911-8F0C-8C6056401740}
+               {6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
+               {C79D6069-2C18-48CB-846E-71F7168C2F7D} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
                {CED9ABBA-861E-4C0A-9359-22351208EF27} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/BinaryWriterExtensions.cs b/src/Microsoft.Diagnostics.NETCore.Client/BinaryWriterExtensions.cs
new file mode 100644 (file)
index 0000000..c95b24d
--- /dev/null
@@ -0,0 +1,25 @@
+// 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;
+using System.Text;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    internal static class BinaryWriterExtensions
+    {
+        public static void WriteString(this BinaryWriter @this, string value)
+        {
+            if (@this == null)
+                throw new ArgumentNullException(nameof(@this));
+
+            @this.Write(value != null ? (value.Length + 1) : 0);
+            if (value != null)
+                @this.Write(Encoding.Unicode.GetBytes(value + '\0'));
+        }
+
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs
new file mode 100644 (file)
index 0000000..5d558c4
--- /dev/null
@@ -0,0 +1,163 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    /// <summary>
+    /// This is a top-level class that contains methods to send various diagnostics command to the runtime.
+    /// </summary>
+    public sealed class DiagnosticsClient
+    {
+        private int _processId;
+
+        public DiagnosticsClient(int processId)
+        {
+            _processId = processId;
+        }
+
+        /// <summary>
+        /// Start tracing the application and return an EventPipeSession object
+        /// </summary>
+        /// <param name="providers">An IEnumerable containing the list of Providers to turn on.</param>
+        /// <param name="requestRundown">If true, request rundown events from the runtime</param>
+        /// <param name="circularBufferMB">The size of the runtime's buffer for collecting events in MB</param>
+        /// <returns>
+        /// An EventPipeSession object representing the EventPipe session that just started.
+        /// </returns> 
+        public EventPipeSession StartEventPipeSession(IEnumerable<EventPipeProvider> providers, bool requestRundown=true, int circularBufferMB=256)
+        {
+            return new EventPipeSession(_processId, providers, requestRundown, circularBufferMB);
+        }
+
+        /// <summary>
+        /// Trigger a core dump generation.
+        /// </summary> 
+        /// <param name="dumpType">Type of the dump to be generated</param>
+        /// <param name="dumpPath">Full path to the dump to be generated. By default it is /tmp/coredump.{pid}</param>
+        /// <param name="logDumpGeneration">When set to true, display the dump generation debug log to the console.</param>
+        public void WriteDump(DumpType dumpType, string dumpPath, bool logDumpGeneration=false)
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}");
+
+            if (string.IsNullOrEmpty(dumpPath))
+                throw new ArgumentNullException($"{nameof(dumpPath)} required");
+
+            var payload = SerializeCoreDump(dumpPath, dumpType, logDumpGeneration);
+            var message = new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload);
+            var response = IpcClient.SendMessage(_processId, message);
+            var hr = 0;
+            switch ((DiagnosticsServerCommandId)response.Header.CommandId)
+            {
+                case DiagnosticsServerCommandId.Error:
+                    hr = BitConverter.ToInt32(response.Payload, 0);
+                    throw new ServerErrorException($"Writing dump failed (HRESULT: 0x{hr:X8})");
+                case DiagnosticsServerCommandId.OK:
+                    return;
+                default:
+                    throw new ServerErrorException($"Writing dump failed - server responded with unknown command");
+            }
+        }
+
+        /// <summary>
+        /// Attach a profiler.
+        /// </summary>
+        /// <param name="attachTimeout">Timeout for attaching the profiler</param>
+        /// <param name="profilerGuid">Guid for the profiler to be attached</param>
+        /// <param name="profilerPath">Path to the profiler to be attached</param>
+        /// <param name="additionalData">Additional data to be passed to the profiler</param>
+        public void AttachProfiler(TimeSpan attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData=null)
+        {
+            if (profilerGuid == null || profilerGuid == Guid.Empty)
+            {
+                throw new ArgumentException($"{nameof(profilerGuid)} must be a valid Guid");
+            }
+
+            if (String.IsNullOrEmpty(profilerPath))
+            {
+                throw new ArgumentException($"{nameof(profilerPath)} must be non-null");
+            }
+
+            byte[] serializedConfiguration = SerializeProfilerAttach((uint)attachTimeout.TotalSeconds, profilerGuid, profilerPath, additionalData);
+            var message = new IpcMessage(DiagnosticsServerCommandSet.Profiler, (byte)ProfilerCommandId.AttachProfiler, serializedConfiguration);
+            var response = IpcClient.SendMessage(_processId, message);
+            switch ((DiagnosticsServerCommandId)response.Header.CommandId)
+            {
+                case DiagnosticsServerCommandId.Error:
+                    var hr = BitConverter.ToInt32(response.Payload, 0);
+                    throw new ServerErrorException($"Profiler attach failed (HRESULT: 0x{hr:X8})");
+                case DiagnosticsServerCommandId.OK:
+                    return;
+                default:
+                    throw new ServerErrorException($"Profiler attach failed - server responded with unknown command");
+            }
+
+            // The call to set up the pipe and send the message operates on a different timeout than attachTimeout, which is for the runtime.
+            // We should eventually have a configurable timeout for the message passing, potentially either separately from the 
+            // runtime timeout or respect attachTimeout as one total duration.
+        }
+
+        /// <summary>
+        /// Get all the active processes that can be attached to.
+        /// </summary>
+        /// <returns>
+        /// IEnumerable of all the active process IDs.
+        /// </returns>
+        public static IEnumerable<int> GetPublishedProcesses()
+        {
+            return Directory.GetFiles(IpcClient.IpcRootPath)
+                .Select(namedPipe => (new FileInfo(namedPipe)).Name)
+                .Where(input => Regex.IsMatch(input, IpcClient.DiagnosticsPortPattern))
+                .Select(input => int.Parse(Regex.Match(input, IpcClient.DiagnosticsPortPattern).Groups[1].Value, NumberStyles.Integer))
+                .Distinct();
+        }
+
+        private static byte[] SerializeCoreDump(string dumpName, DumpType dumpType, bool diagnostics)
+        {
+            using (var stream = new MemoryStream())
+            using (var writer = new BinaryWriter(stream))
+            {
+                writer.WriteString(dumpName);
+                writer.Write((uint)dumpType);
+                writer.Write((uint)(diagnostics ? 1 : 0));
+
+                writer.Flush();
+                return stream.ToArray();
+            }
+        }
+
+        private static byte[] SerializeProfilerAttach(uint attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData)
+        {
+            using (var stream = new MemoryStream())
+            using (var writer = new BinaryWriter(stream))
+            {
+                writer.Write(attachTimeout);
+                writer.Write(profilerGuid.ToByteArray());
+                writer.WriteString(profilerPath);
+
+                if (additionalData == null)
+                {
+                    writer.Write(0);
+                }
+                else
+                {
+                    writer.Write(additionalData.Length);
+                    writer.Write(additionalData);
+                }
+
+                writer.Flush();
+                return stream.ToArray();
+            }
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClientExceptions.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClientExceptions.cs
new file mode 100644 (file)
index 0000000..a313e3c
--- /dev/null
@@ -0,0 +1,31 @@
+// 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;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public class DiagnosticsClientException : Exception
+    {
+        public DiagnosticsClientException(string msg) : base(msg) {}
+    }
+
+    // When a certian command is not supported by either the library or the target process' runtime
+    public class UnsupportedProtocolException : DiagnosticsClientException
+    {
+        public UnsupportedProtocolException(string msg) : base(msg) {}
+    }
+
+    // When the runtime is no longer availble for attaching.
+    public class ServerNotAvailableException : DiagnosticsClientException
+    {
+        public ServerNotAvailableException(string msg) : base(msg) {}
+    }
+
+    // When the runtime responded with an error
+    public class ServerErrorException : DiagnosticsClientException
+    {
+        public ServerErrorException(string msg): base(msg) {}
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DumpType.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DumpType.cs
new file mode 100644 (file)
index 0000000..2200e84
--- /dev/null
@@ -0,0 +1,14 @@
+// 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.
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public enum DumpType
+    {
+        Normal = 1,
+        WithHeap = 2,
+        Triage = 3,
+        Full = 4
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeProvider.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeProvider.cs
new file mode 100644 (file)
index 0000000..332ecd2
--- /dev/null
@@ -0,0 +1,76 @@
+// 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.Collections.Generic;
+using System.Diagnostics.Tracing;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public sealed class EventPipeProvider
+    {
+        public EventPipeProvider(string name, EventLevel eventLevel, long keywords = 0, IDictionary<string, string> arguments = null)
+        {
+            Name = name;
+            EventLevel = eventLevel;
+            Keywords = keywords;
+            Arguments = arguments;
+        }
+
+        public long Keywords { get; }
+
+        public EventLevel EventLevel { get; }
+
+        public string Name { get; }
+
+        public IDictionary<string, string> Arguments { get; }
+
+        public override string ToString()
+        {
+            return $"{Name}:0x{Keywords:X16}:{(uint)EventLevel}{(Arguments == null ? "" : $":{GetArgumentString()}")}";
+        }
+        
+        public override bool Equals(object obj)
+        {
+            if (obj == null || GetType() != obj.GetType())
+            {
+                return false;
+            }
+            
+            return this == (EventPipeProvider)obj;
+        }
+
+        public override int GetHashCode()
+        {
+            int hash = 0;
+            hash ^= this.Name.GetHashCode();
+            hash ^= this.Keywords.GetHashCode();
+            hash ^= this.EventLevel.GetHashCode();
+            hash ^= GetArgumentString().GetHashCode();
+            return hash;
+        }
+
+        public static bool operator ==(EventPipeProvider left, EventPipeProvider right)
+        {
+            return left.ToString() == right.ToString();
+        }
+
+        public static bool operator !=(EventPipeProvider left, EventPipeProvider right)
+        {
+            return !(left == right);    
+        }
+
+        internal string GetArgumentString()
+        {
+            if (Arguments == null)
+            {
+                return "";
+            }
+            return string.Join(";", Arguments.Select(a => $"{a.Key}={a.Value}"));
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs
new file mode 100644 (file)
index 0000000..fc6fa84
--- /dev/null
@@ -0,0 +1,85 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public class EventPipeSession : IDisposable
+    {
+        private IEnumerable<EventPipeProvider> _providers;
+        private bool _requestRundown;
+        private int _circularBufferMB;
+        private long _sessionId;
+        private int _processId;
+        private bool disposedValue = false; // To detect redundant calls
+
+        internal EventPipeSession(int processId, IEnumerable<EventPipeProvider> providers, bool requestRundown, int circularBufferMB)
+        {
+            _processId = processId;
+            _providers = providers;
+            _requestRundown = requestRundown;
+            _circularBufferMB = circularBufferMB;
+            
+            var config = new EventPipeSessionConfiguration(circularBufferMB, EventPipeSerializationFormat.NetTrace, providers, requestRundown);
+            var message = new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)EventPipeCommandId.CollectTracing2, config.SerializeV2());
+            EventStream = IpcClient.SendMessage(processId, message, out var response);
+            switch ((DiagnosticsServerCommandId)response.Header.CommandId)
+            {
+                case DiagnosticsServerCommandId.OK:
+                    _sessionId = BitConverter.ToInt64(response.Payload, 0);
+                    break;
+                case DiagnosticsServerCommandId.Error:
+                    var hr = BitConverter.ToInt32(response.Payload, 0);
+                    throw new ServerErrorException($"EventPipe session start failed (HRESULT: 0x{hr:X8})");
+                default:
+                    throw new ServerErrorException($"EventPipe session start failed - Server responded with unknown command");
+            }
+        }
+
+        public Stream EventStream { get; }
+
+        ///<summary>
+        /// Stops the given session
+        ///</summary>
+        public void Stop()
+        {
+            Debug.Assert(_sessionId > 0);
+
+            byte[] payload = BitConverter.GetBytes(_sessionId);
+            var response = IpcClient.SendMessage(_processId, new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)EventPipeCommandId.StopTracing, payload));
+
+            switch ((DiagnosticsServerCommandId)response.Header.CommandId)
+            {
+                case DiagnosticsServerCommandId.OK:
+                    return;
+                case DiagnosticsServerCommandId.Error:
+                    var hr = BitConverter.ToInt32(response.Payload, 0);
+                    throw new ServerErrorException($"EventPipe session stop failed (HRESULT: 0x{hr:X8})");
+                default:
+                    throw new ServerErrorException($"EventPipe session stop failed - Server responded with unknown command");
+            }
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!disposedValue)
+            {
+                if (disposing)
+                {
+                    EventStream?.Dispose();
+                }
+                disposedValue = true;
+            }
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs
new file mode 100644 (file)
index 0000000..abda645
--- /dev/null
@@ -0,0 +1,72 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    internal enum EventPipeSerializationFormat
+    {
+        NetPerf,
+        NetTrace
+    }
+
+    internal class EventPipeSessionConfiguration
+    {
+        public EventPipeSessionConfiguration(int circularBufferSizeMB, EventPipeSerializationFormat format, IEnumerable<EventPipeProvider> providers, bool requestRundown=true)
+        {
+            if (circularBufferSizeMB == 0)
+                throw new ArgumentException($"Buffer size cannot be zero.");
+            if (format != EventPipeSerializationFormat.NetPerf && format != EventPipeSerializationFormat.NetTrace)
+                throw new ArgumentException("Unrecognized format");
+            if (providers == null)
+                throw new ArgumentNullException(nameof(providers));
+
+            CircularBufferSizeInMB = circularBufferSizeMB;
+            Format = format;
+            RequestRundown = requestRundown;
+            _providers = new List<EventPipeProvider>(providers);
+        }
+
+        public bool RequestRundown { get; }
+        public int CircularBufferSizeInMB { get; }
+        public EventPipeSerializationFormat Format { get; }
+
+        public IReadOnlyCollection<EventPipeProvider> Providers => _providers.AsReadOnly();
+
+        private readonly List<EventPipeProvider> _providers;
+
+        public byte[] SerializeV2()
+        {
+            byte[] serializedData = null;
+            using (var stream = new MemoryStream())
+            using (var writer = new BinaryWriter(stream))
+            {
+                writer.Write(CircularBufferSizeInMB);
+                writer.Write((uint)Format);
+                writer.Write(RequestRundown);
+
+                writer.Write(Providers.Count());
+                foreach (var provider in Providers)
+                {
+                    writer.Write(provider.Keywords);
+                    writer.Write((uint)provider.EventLevel);
+
+                    writer.WriteString(provider.Name);
+                    writer.WriteString(provider.GetArgumentString());
+                }
+
+                writer.Flush();
+                serializedData = stream.ToArray();
+            }
+
+            return serializedData;
+        }
+
+
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcClient.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcClient.cs
new file mode 100644 (file)
index 0000000..1ab6409
--- /dev/null
@@ -0,0 +1,136 @@
+// 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.Diagnostics;
+using System.IO;
+using System.IO.Pipes;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    internal class IpcClient
+    {
+        public static string IpcRootPath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"\\.\pipe\" : Path.GetTempPath();
+        public static string DiagnosticsPortPattern { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"^dotnet-diagnostic-(\d+)$" : @"^dotnet-diagnostic-(\d+)-(\d+)-socket$";
+
+        private static double ConnectTimeoutMilliseconds { get; } = TimeSpan.FromSeconds(3).TotalMilliseconds;
+
+        /// <summary>
+        /// Get the OS Transport to be used for communicating with a dotnet process.
+        /// </summary>
+        /// <param name="processId">The PID of the dotnet process to get the transport for</param>
+        /// <returns>A System.IO.Stream wrapper around the transport</returns>
+        private static Stream GetTransport(int processId)
+        {
+            try 
+            {
+                var process = Process.GetProcessById(processId);
+            }
+            catch (System.ArgumentException)
+            {
+                throw new ServerNotAvailableException($"Process {processId} is not running.");
+            }
+            catch (System.InvalidOperationException)
+            {
+                throw new ServerNotAvailableException($"Process {processId} seems to be elevated.");
+            }
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                string pipeName = $"dotnet-diagnostic-{processId}";
+                var namedPipe = new NamedPipeClientStream(
+                    ".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation);
+                namedPipe.Connect((int)ConnectTimeoutMilliseconds);
+                return namedPipe;
+            }
+            else
+            {
+                string ipcPort;
+                try
+                {
+                    ipcPort = Directory.GetFiles(IpcRootPath, $"dotnet-diagnostic-{processId}-*-socket") // Try best match.
+                                .OrderByDescending(f => new FileInfo(f).LastWriteTime)
+                                .FirstOrDefault();
+                    if (ipcPort == null)
+                    {
+                        throw new ServerNotAvailableException($"Process {processId} not running compatible .NET Core runtime.");
+                    }
+                }
+                catch (InvalidOperationException)
+                {
+                    throw new ServerNotAvailableException($"Process {processId} not running compatible .NET Core runtime.");
+                }
+                string path = Path.Combine(IpcRootPath, ipcPort);
+                var remoteEP = CreateUnixDomainSocketEndPoint(path);
+
+                var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
+                socket.Connect(remoteEP);
+                return new NetworkStream(socket);
+            }
+        }
+
+        /// <summary>
+        /// Sends a single DiagnosticsIpc Message to the dotnet process with PID processId.
+        /// </summary>
+        /// <param name="processId">The PID of the dotnet process</param>
+        /// <param name="message">The DiagnosticsIpc Message to be sent</param>
+        /// <returns>The response DiagnosticsIpc Message from the dotnet process</returns>
+        public static IpcMessage SendMessage(int processId, IpcMessage message)
+        {
+            using (var stream = GetTransport(processId))
+            {
+                Write(stream, message);
+                return Read(stream);
+            }
+        }
+
+        /// <summary>
+        /// Sends a single DiagnosticsIpc Message to the dotnet process with PID processId
+        /// and returns the Stream for reuse in Optional Continuations.
+        /// </summary>
+        /// <param name="processId">The PID of the dotnet process</param>
+        /// <param name="message">The DiagnosticsIpc Message to be sent</param>
+        /// <param name="response">out var for response message</param>
+        /// <returns>The response DiagnosticsIpc Message from the dotnet process</returns>
+        public static Stream SendMessage(int processId, IpcMessage message, out IpcMessage response)
+        {
+            var stream = GetTransport(processId);
+            Write(stream, message);
+            response = Read(stream);
+            return stream;
+        }
+
+        private static void Write(Stream stream, byte[] buffer)
+        {
+            stream.Write(buffer, 0, buffer.Length);
+        }
+
+        private static void Write(Stream stream, IpcMessage message)
+        {
+            Write(stream, message.Serialize());
+        }
+
+        private static IpcMessage Read(Stream stream)
+        {
+            return IpcMessage.Parse(stream);
+        }
+
+        private static EndPoint CreateUnixDomainSocketEndPoint(string path)
+        {
+#if NETCOREAPP
+            return new UnixDomainSocketEndPoint(path);
+#elif NETSTANDARD2_0
+            // UnixDomainSocketEndPoint is not part of .NET Standard 2.0
+            var type = typeof(Socket).Assembly.GetType("System.Net.Sockets.UnixDomainSocketEndPoint");
+            var ctor = type.GetConstructor(new[] { typeof(string) });
+            return (EndPoint)ctor.Invoke(new object[] { path });
+#endif
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs
new file mode 100644 (file)
index 0000000..7c870e3
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    internal enum DiagnosticsServerCommandSet : byte
+    {
+        Dump           = 0x01,
+        EventPipe      = 0x02,
+        Profiler       = 0x03,
+
+        Server         = 0xFF,
+    }
+
+    internal enum DiagnosticsServerCommandId : byte
+    {
+        OK    = 0x00,
+        Error = 0xFF,
+    }
+
+    internal enum EventPipeCommandId : byte
+    {
+        StopTracing     = 0x01,
+        CollectTracing  = 0x02,
+        CollectTracing2 = 0x03,
+    }
+
+    internal enum DumpCommandId : byte
+    {
+        GenerateCoreDump = 0x01,
+    }
+
+    internal enum ProfilerCommandId : byte
+    {
+        AttachProfiler = 0x01,
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcHeader.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcHeader.cs
new file mode 100644 (file)
index 0000000..3a8fe59
--- /dev/null
@@ -0,0 +1,75 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    internal class IpcHeader
+    {
+        IpcHeader() { }
+
+        public IpcHeader(DiagnosticsServerCommandSet commandSet, byte commandId)
+        {
+            CommandSet = (byte)commandSet;
+            CommandId = commandId;
+        }
+
+        // the number of bytes for the DiagnosticsIpc::IpcHeader type in native code
+        public static readonly UInt16 HeaderSizeInBytes = 20;
+        private static readonly UInt16 MagicSizeInBytes = 14;
+
+        public byte[] Magic = DotnetIpcV1; // byte[14] in native code
+        public UInt16 Size = HeaderSizeInBytes;
+        public byte CommandSet;
+        public byte CommandId;
+        public UInt16 Reserved = 0x0000;
+
+
+        // Helper expression to quickly get V1 magic string for comparison
+        // should be 14 bytes long
+        public static byte[] DotnetIpcV1 => Encoding.ASCII.GetBytes("DOTNET_IPC_V1" + '\0');
+
+        public byte[] Serialize()
+        {
+            using (var stream = new MemoryStream())
+            using (var writer = new BinaryWriter(stream))
+            {
+                writer.Write(Magic);
+                Debug.Assert(Magic.Length == MagicSizeInBytes);
+                writer.Write(Size);
+                writer.Write(CommandSet);
+                writer.Write(CommandId);
+                writer.Write((UInt16)0x0000);
+                writer.Flush();
+                return stream.ToArray();
+            }
+        }
+
+        public static IpcHeader TryParse(BinaryReader reader)
+        {
+            IpcHeader header = new IpcHeader
+            {
+                Magic = reader.ReadBytes(14),
+                Size = reader.ReadUInt16(),
+                CommandSet = reader.ReadByte(),
+                CommandId = reader.ReadByte(),
+                Reserved = reader.ReadUInt16()
+            };
+
+            return header;
+        }
+
+        override public string ToString()
+        {
+            return $"{{ Magic={Magic}; Size={Size}; CommandSet={CommandSet}; CommandId={CommandId}; Reserved={Reserved} }}";
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcMessage.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcMessage.cs
new file mode 100644 (file)
index 0000000..06cd79d
--- /dev/null
@@ -0,0 +1,108 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    /// <summary>
+    /// Different diagnostic message types that are handled by the runtime.
+    /// </summary>
+    internal enum DiagnosticsMessageType : uint
+    {
+        /// <summary>
+        /// Initiates core dump generation 
+        /// </summary>
+        GenerateCoreDump = 1,
+        /// <summary>
+        /// Starts an EventPipe session that writes events to a file when the session is stopped or the application exits.
+        /// </summary>
+        StartEventPipeTracing = 1024,
+        /// <summary>
+        /// Stops an EventPipe session.
+        /// </summary>
+        StopEventPipeTracing = 1025,
+        /// <summary>
+        /// Starts an EventPipe session that sends events out-of-proc through IPC.
+        /// </summary>
+        CollectEventPipeTracing = 1026,
+        /// <summary>
+        /// Attaches a profiler to an existing process
+        /// </summary>
+        AttachProfiler = 2048,
+    }
+
+
+    /// <summary>
+    /// Message header used to send commands to the .NET Core runtime through IPC.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct MessageHeader
+    {
+        /// <summary>
+        /// Request type.
+        /// </summary>
+        public DiagnosticsMessageType RequestType;
+
+        /// <summary>
+        /// Remote process Id.
+        /// </summary>
+        public uint Pid;
+    }
+
+
+    internal class IpcMessage
+    {
+        public IpcMessage()
+        { }
+
+        public IpcMessage(IpcHeader header, byte[] payload)
+        {
+            Payload = payload;
+            Header = header;
+        }
+
+        public IpcMessage(DiagnosticsServerCommandSet commandSet, byte commandId, byte[] payload = null)
+        : this(new IpcHeader(commandSet, commandId), payload)
+        {
+        }
+
+        public byte[] Payload { get; private set; } = null;
+        public IpcHeader Header { get; private set; } = default;
+
+        public byte[] Serialize()
+        { 
+            byte[] serializedData = null;
+            // Verify things will fit in the size capacity
+            Header.Size = checked((UInt16)(IpcHeader.HeaderSizeInBytes + Payload.Length)); ;
+            byte[] headerBytes = Header.Serialize();
+
+            using (var stream = new MemoryStream())
+            using (var writer = new BinaryWriter(stream))
+            {
+                writer.Write(headerBytes);
+                writer.Write(Payload);
+                writer.Flush();
+                serializedData = stream.ToArray();
+            }
+
+            return serializedData;
+        }
+
+        public static IpcMessage Parse(Stream stream)
+        {
+            IpcMessage message = new IpcMessage();
+            using (var reader = new BinaryReader(stream, Encoding.UTF8, true))
+            {
+                message.Header = IpcHeader.TryParse(reader);
+                message.Payload = reader.ReadBytes(message.Header.Size - IpcHeader.HeaderSizeInBytes);
+                return message;
+            }
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.csproj b/src/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.csproj
new file mode 100644 (file)
index 0000000..7e530dc
--- /dev/null
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Library</OutputType>
+    <TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
+    <RootNamespace>Microsoft.Diagnostics.NETCore.Client</RootNamespace>
+    <Description>.NET Core Diagnostics Client Library</Description>
+    <VersionPrefix>0.2.0</VersionPrefix>
+    <IsPackable>true</IsPackable>
+    <PackageTags>Diagnostic</PackageTags>
+    <PackageReleaseNotes>$(Description)</PackageReleaseNotes>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <IncludeSymbols>true</IncludeSymbols>
+    <IsShippingAssembly>true</IsShippingAssembly>
+  </PropertyGroup>
+</Project>
index cdf2111fbda02a62e09194339d2c6441cdfef913..8fc3ce68a80321eaafd291f37346ebe46296cfef 100644 (file)
@@ -8,7 +8,7 @@ using System.Diagnostics;
 using System.Linq;
 using System.Text;
 
-using Microsoft.Diagnostics.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 
 namespace Microsoft.Internal.Common.Commands
 {
@@ -22,7 +22,7 @@ namespace Microsoft.Internal.Common.Commands
             try
             {
                 StringBuilder sb = new StringBuilder();
-                var processes = EventPipeClient.ListAvailablePorts()
+                var processes = DiagnosticsClient.GetPublishedProcesses()
                     .Select(GetProcessById)
                     .Where(process => process != null)
                     .OrderBy(process => process.ProcessName)
index d4097ecb22284dbaae622d8894760fab2241ba22..835ee6f7a774b9f1194fd2284e19da5f9e4fe5ba 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using System;
 using System.Collections.Generic;
 using System.CommandLine;
@@ -26,9 +26,10 @@ namespace Microsoft.Diagnostics.Tools.Counters
         private IConsole _console;
         private ICounterRenderer _renderer;
         private CounterFilter filter;
-        private ulong _sessionId;
         private string _output;
         private bool pauseCmdSet;
+        private DiagnosticsClient _diagnosticsClient;
+        private EventPipeSession _session;
 
         public CounterMonitor()
         {
@@ -59,7 +60,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
         {
             try
             {
-                EventPipeClient.StopTracing(_processId, _sessionId);
+                _session.Stop();
             }
             catch (EndOfStreamException ex)
             {
@@ -91,6 +92,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
                 _processId = processId;
                 _interval = refreshInterval;
                 _renderer = new ConsoleWriter();
+                _diagnosticsClient = new DiagnosticsClient(processId);
 
                 return await Start();
             }
@@ -99,7 +101,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
             {
                 try
                 {
-                    EventPipeClient.StopTracing(_processId, _sessionId);
+                    _session.Stop();
                 }
                 catch (Exception) {} // Swallow all exceptions for now.
                 
@@ -118,6 +120,7 @@ namespace Microsoft.Diagnostics.Tools.Counters
                 _processId = processId;
                 _interval = refreshInterval;
                 _output = output;
+                _diagnosticsClient = new DiagnosticsClient(processId);
 
                 if (_output.Length == 0)
                 {
@@ -154,30 +157,6 @@ namespace Microsoft.Diagnostics.Tools.Counters
             return 1;
         }
 
-
-        // Use EventPipe CollectTracing2 command to start monitoring. This may throw.
-        private EventPipeEventSource RequestTracingV2(string providerString)
-        {
-            var configuration = new SessionConfigurationV2(
-                                        circularBufferSizeMB: 1000,
-                                        format: EventPipeSerializationFormat.NetTrace,
-                                        requestRundown: false,
-                                        providers: Trace.Extensions.ToProviders(providerString));
-            var binaryReader = EventPipeClient.CollectTracing2(_processId, configuration, out _sessionId);
-            return new EventPipeEventSource(binaryReader);
-        }
-
-        // Use EventPipe CollectTracing command to start monitoring. This may throw.
-        private EventPipeEventSource RequestTracingV1(string providerString)
-        {
-            var configuration = new SessionConfiguration(
-                                        circularBufferSizeMB: 1000,
-                                        format: EventPipeSerializationFormat.NetTrace,
-                                        providers: Trace.Extensions.ToProviders(providerString));
-            var binaryReader = EventPipeClient.CollectTracing(_processId, configuration, out _sessionId);
-            return new EventPipeEventSource(binaryReader);
-        }
-
         private string BuildProviderString()
         {
             string providerString;
@@ -257,23 +236,16 @@ namespace Microsoft.Diagnostics.Tools.Counters
             Task monitorTask = new Task(() => {
                 try
                 {
-                    EventPipeEventSource source = null;
-
-                    try
-                    {
-                        source = RequestTracingV2(providerString);
-                    }
-                    catch (EventPipeUnknownCommandException)
-                    {
-                        // If unknown command exception is thrown, it's likely the app being monitored is 
-                        // running an older version of runtime that doesn't support CollectTracingV2. Try again with V1.
-                        source = RequestTracingV1(providerString);
-                    }
-
+                    _session = _diagnosticsClient.StartEventPipeSession(Trace.Extensions.ToProviders(providerString), false);
+                    var source = new EventPipeEventSource(_session.EventStream);
                     source.Dynamic.All += DynamicAllMonitor;
                     _renderer.EventPipeSourceConnected();
                     source.Process();
                 }
+                catch (DiagnosticsClientException ex)
+                {
+                    Console.WriteLine($"Failed to start the counter session: {ex.ToString()}");
+                }
                 catch (Exception ex)
                 {
                     Debug.WriteLine($"[ERROR] {ex.ToString()}");
index 318d40fba95320b4a94a9660e8604afb666b2175..ca6d20b9f9deef7fab0c2d5d2f226cf780d35c24 100644 (file)
@@ -11,7 +11,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Collections.Generic;
-using Microsoft.Diagnostics.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using Microsoft.Internal.Common.Commands;
 
 namespace Microsoft.Diagnostics.Tools.Counters
index b8418707eb56f0f4bb80a759ae07218b90eda0ca..4dc5bed68dcbb07d5229398d2fce7c0a12bc3e0f 100644 (file)
@@ -17,7 +17,7 @@
 
   <ItemGroup>
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Repl\Microsoft.Diagnostics.Repl.csproj" />
-    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
   </ItemGroup>
 
   <ItemGroup>
index 5d94f515d08bc32ffff96fae6d6bfd5e240db011..3f1c37d50eb06a5034af08dbf171a9593414c379 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using System;
 using System.CommandLine;
 using System.Diagnostics;
@@ -61,14 +61,11 @@ namespace Microsoft.Diagnostics.Tools.Dump
                 }
                 else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                 {
-                    DiagnosticsHelpers.DumpType dumpType = type == DumpTypeOption.Heap ? DiagnosticsHelpers.DumpType.WithHeap : DiagnosticsHelpers.DumpType.Normal;
+                    var client = new DiagnosticsClient(processId);
+                    DumpType dumpType = type == DumpTypeOption.Heap ? DumpType.WithHeap : DumpType.Normal;
 
                     // Send the command to the runtime to initiate the core dump
-                    var hr = DiagnosticsHelpers.GenerateCoreDump(processId, output, dumpType, diag);
-                    if (hr != 0)
-                    {
-                        throw new InvalidOperationException($"Core dump generation FAILED 0x{hr:X8}");
-                    }
+                    client.WriteDump(dumpType, output, diag);
                 }
                 else {
                     throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}");
@@ -81,7 +78,8 @@ namespace Microsoft.Diagnostics.Tools.Dump
                  ex is PlatformNotSupportedException || 
                  ex is InvalidDataException ||
                  ex is InvalidOperationException ||
-                 ex is NotSupportedException)
+                 ex is NotSupportedException ||
+                 ex is DiagnosticsClientException)
             {
                 console.Error.WriteLine($"{ex.Message}");
                 return 1;
index 2f2d9aa04186b658ae2340b5dfc982edc4066a2f..c206bd3365e5cda9d6027c86fc37689ab2537341 100644 (file)
@@ -7,7 +7,7 @@ using System.CommandLine.Builder;
 using System.CommandLine.Invocation;
 using System.IO;
 using System.Threading.Tasks;
-using Microsoft.Diagnostics.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using Microsoft.Internal.Common.Commands;
 
 namespace Microsoft.Diagnostics.Tools.Dump
index c6ba3724d250614fadcfda8e2061376399479989..d93281273ebfca0d63fe5bc355cf4b3c16dbaaf4 100644 (file)
@@ -23,7 +23,7 @@
   
   <ItemGroup>
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Repl\Microsoft.Diagnostics.Repl.csproj" />
-    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\SOS\SOS.Hosting\SOS.Hosting.csproj" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\SOS\SOS.NETCore\SOS.NETCore.csproj" />
     <ProjectReference Include="..\..\Microsoft.Diagnostics.DebugServices\Microsoft.Diagnostics.DebugServices.csproj" />
index 5ad9bd92454df5a4c64337ff78b5fe1ebf1ebd94..4be21afb8e88383218476a7695ffb69a9f7f81ed 100644 (file)
@@ -2,7 +2,6 @@
 // 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.Tools.RuntimeClient;
 using System;
 using System.Collections.Generic;
 using System.CommandLine;
index 056be272940b0aede2487de5ee81def6a44815df..4e15083f0cd919ea699c58f1461bf75675fe40f4 100644 (file)
@@ -2,7 +2,6 @@
 // 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.Tools.RuntimeClient;
 using Microsoft.Internal.Common.Commands;
 using System;
 using System.CommandLine;
index 440a2b2e5130a884da2dae0fe4dbf1e734a93475..511bb448f25a2213cbf2525fb461f51c0ca4a76a 100644 (file)
@@ -3,13 +3,14 @@
 // See the LICENSE file in the project root for more information.
 
 using Graphs;
-using Microsoft.Diagnostics.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using Microsoft.Diagnostics.Tracing;
 using Microsoft.Diagnostics.Tracing.Parsers;
 using Microsoft.Diagnostics.Tracing.Parsers.Clr;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.Tracing;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
@@ -48,7 +49,9 @@ namespace Microsoft.Diagnostics.Tools.GCDump
                 bool fDone = false;
                 log.WriteLine("{0,5:n1}s: Creating type table flushing task", getElapsed().TotalSeconds);
 
-                using (EventPipeSession typeFlushSession = new EventPipeSession(processID, new List<Provider> { new Provider("Microsoft-DotNETCore-SampleProfiler") }, false))
+                using (EventPipeSessionController typeFlushSession = new EventPipeSessionController(processID, new List<EventPipeProvider> { 
+                    new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational)
+                }, false))
                 {
                     log.WriteLine("{0,5:n1}s: Flushing the type table", getElapsed().TotalSeconds);
                     typeFlushSession.Source.AllEvents += (traceEvent) => {
@@ -68,7 +71,9 @@ namespace Microsoft.Diagnostics.Tools.GCDump
                 // Start the providers and trigger the GCs.  
                 log.WriteLine("{0,5:n1}s: Requesting a .NET Heap Dump", getElapsed().TotalSeconds);
 
-                using EventPipeSession gcDumpSession = new EventPipeSession(processID, new List<Provider> { new Provider("Microsoft-Windows-DotNETRuntime", (ulong)(ClrTraceEventParser.Keywords.GCHeapSnapshot)) });
+                using EventPipeSessionController gcDumpSession = new EventPipeSessionController(processID, new List<EventPipeProvider> { 
+                    new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Verbose, (long)(ClrTraceEventParser.Keywords.GCHeapSnapshot)) 
+                });
                 log.WriteLine("{0,5:n1}s: gcdump EventPipe Session started", getElapsed().TotalSeconds);
 
                 int gcNum = -1;
@@ -211,35 +216,29 @@ namespace Microsoft.Diagnostics.Tools.GCDump
         }
     }
 
-    internal class EventPipeSession : IDisposable
+    internal class EventPipeSessionController : IDisposable
     {
-        private List<Provider> _providers;
-        private Stream _eventPipeStream;
+        private List<EventPipeProvider> _providers;
+        private DiagnosticsClient _client;
+        private EventPipeSession _session;
         private EventPipeEventSource _source;
-        private ulong _sessionId;
         private int _pid;
 
-        public ulong SessionId => _sessionId;
-        public IReadOnlyList<Provider> Providers => _providers.AsReadOnly();
+        public IReadOnlyList<EventPipeProvider> Providers => _providers.AsReadOnly();
         public EventPipeEventSource Source => _source;
 
-        public EventPipeSession(int pid, List<Provider> providers, bool requestRundown = true)
+        public EventPipeSessionController(int pid, List<EventPipeProvider> providers, bool requestRundown = true)
         {
             _pid = pid;
             _providers = providers;
-            var config = new SessionConfigurationV2(
-                circularBufferSizeMB: 1024,
-                format: EventPipeSerializationFormat.NetTrace,
-                requestRundown: requestRundown,
-                providers
-            );
-            _eventPipeStream = EventPipeClient.CollectTracing2(pid, config, out _sessionId);
-            _source = new EventPipeEventSource(_eventPipeStream);
+            _client = new DiagnosticsClient(pid);
+            _session = _client.StartEventPipeSession(providers, requestRundown, 1024);
+            _source = new EventPipeEventSource(_session.EventStream);
         }
 
         public void EndSession()
         {
-            EventPipeClient.StopTracing(_pid, _sessionId);
+            _session.Stop();
         }
 
         #region IDisposable Support
@@ -251,7 +250,7 @@ namespace Microsoft.Diagnostics.Tools.GCDump
             {
                 if (disposing)
                 {
-                    _eventPipeStream?.Dispose();
+                    _session?.Dispose();
                     _source?.Dispose();
                 }
                 disposedValue = true;
index 6c09dc9963b438119af4889a7795b50cd46bf9d0..11d02c0a2a972cc3a1c8464707e4a6ac22a4355c 100644 (file)
@@ -17,7 +17,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
   </ItemGroup>
 
   <ItemGroup>
index fac9c13b1aac3845b1142328a3334fa23730193e..b8c156c4114bc8039e805cb293fb7c2364d8859a 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using System;
 using System.Collections.Generic;
 using System.CommandLine;
@@ -59,7 +59,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
                 Dictionary<string, string> enabledBy = new Dictionary<string, string>();
 
                 var providerCollection = Extensions.ToProviders(providers);
-                foreach (Provider providerCollectionProvider in providerCollection)
+                foreach (EventPipeProvider providerCollectionProvider in providerCollection)
                 {
                     enabledBy[providerCollectionProvider.Name] = "--providers ";
                 }
@@ -87,11 +87,6 @@ namespace Microsoft.Diagnostics.Tools.Trace
                 PrintProviders(providerCollection, enabledBy);
 
                 var process = Process.GetProcessById(processId);
-                var configuration = new SessionConfiguration(
-                    circularBufferSizeMB: buffersize,
-                    format: EventPipeSerializationFormat.NetTrace,
-                    providers: providerCollection);
-
                 var shouldExit = new ManualResetEvent(false);
                 var shouldStopAfterDuration = duration != default(TimeSpan);
                 var failed = false;
@@ -100,11 +95,20 @@ namespace Microsoft.Diagnostics.Tools.Trace
 
                 ct.Register(() => shouldExit.Set());
 
-                ulong sessionId = 0;
-                using (Stream stream = EventPipeClient.CollectTracing(processId, configuration, out sessionId))
+                var diagnosticsClient = new DiagnosticsClient(processId);
                 using (VirtualTerminalMode vTermMode = VirtualTerminalMode.TryEnable())
                 {
-                    if (sessionId == 0)
+                    EventPipeSession session = null;
+                    try
+                    {
+                        session = diagnosticsClient.StartEventPipeSession(providerCollection, true);
+                    }
+                    catch (DiagnosticsClientException e)
+                    {
+                        Console.Error.WriteLine($"Unable to start a tracing session: {e.ToString()}");
+                    }
+
+                    if (session == null)
                     {
                         Console.Error.WriteLine("Unable to create session.");
                         return ErrorCodes.SessionCreationError;
@@ -137,7 +141,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
 
                                 while (true)
                                 {
-                                    int nBytesRead = stream.Read(buffer, 0, buffer.Length);
+                                    int nBytesRead = session.EventStream.Read(buffer, 0, buffer.Length);
                                     if (nBytesRead <= 0)
                                         break;
                                     fs.Write(buffer, 0, nBytesRead);
@@ -170,7 +174,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
                     if (!terminated)
                     {
                         durationTimer?.Stop();
-                        EventPipeClient.StopTracing(processId, sessionId);
+                        session.Stop();
                     }
                     await collectingTask;
                 }
@@ -190,7 +194,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
             }
         }
 
-        private static void PrintProviders(IReadOnlyList<Provider> providers, Dictionary<string, string> enabledBy)
+        private static void PrintProviders(IReadOnlyList<EventPipeProvider> providers, Dictionary<string, string> enabledBy)
         {
             Console.Out.WriteLine("");
             Console.Out.Write(String.Format("{0, -40}","Provider Name"));  // +4 is for the tab
@@ -199,10 +203,12 @@ namespace Microsoft.Diagnostics.Tools.Trace
             Console.Out.Write("Enabled By\n");
             foreach (var provider in providers)
             {
-                Console.Out.WriteLine(String.Format("{0, -80}", $"{provider.ToDisplayString()}") + $"{enabledBy[provider.Name]}");
+                Console.Out.WriteLine(String.Format("{0, -80}", $"{GetProviderDisplayString(provider)}") + $"{enabledBy[provider.Name]}");
             }
             Console.Out.WriteLine();
         }
+        private static string GetProviderDisplayString(EventPipeProvider provider) =>
+            String.Format("{0, -40}", provider.Name) + String.Format("0x{0, -18}", $"{provider.Keywords:X16}") + String.Format("{0, -8}", provider.EventLevel.ToString() + $"({(int)provider.EventLevel})");
 
         private static int prevBufferWidth = 0;
         private static string clearLineString = "";
index cddfb890899956de0d1f5bb3452a18263e3cf8f6..ced5842d4bc1ac3ffe28c6448bf3f44551b6bf52 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using System;
 using System.IO;
 using System.CommandLine;
index c8afc5695d36fbdfdcbe125fc29e561591d529c5..c30ec6940449f11c9c1a65e76a9ee0682c0e7d7f 100644 (file)
@@ -2,7 +2,6 @@
 // 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.Tools.RuntimeClient;
 using Microsoft.Internal.Common.Commands;
 using System;
 using System.CommandLine;
index f8ec9089fda5aa78c5ce585b4352784b9d3644a4..a182ae3f4a6abc8b57b14d0e1a51510ffecd520e 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using Microsoft.Diagnostics.Tracing.Parsers;
 using System;
 using System.Collections.Generic;
@@ -39,35 +39,35 @@ namespace Microsoft.Diagnostics.Tools.Trace
                 handler: CommandHandler.Create<IConsole>(GetProfiles),
                 isHidden: false);
 
-        // FIXME: Read from a config file!
         internal static IEnumerable<Profile> DotNETRuntimeProfiles { get; } = new[] {
             new Profile(
                 "cpu-sampling",
-                new Provider[] {
-                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
-                    new Provider("Microsoft-Windows-DotNETRuntime", (ulong)ClrTraceEventParser.Keywords.Default, EventLevel.Informational),
+                new EventPipeProvider[] {
+                    new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational),
+                    new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default)
                 },
                 "Useful for tracking CPU usage and general .NET runtime information. This is the default option if no profile or providers are specified."),
             new Profile(
                 "gc-verbose",
-                new Provider[] {
-                    new Provider(
+                new EventPipeProvider[] {
+                    new EventPipeProvider(
                         name: "Microsoft-Windows-DotNETRuntime",
-                        keywords: (ulong)ClrTraceEventParser.Keywords.GC |
-                                  (ulong)ClrTraceEventParser.Keywords.GCHandle |
-                                  (ulong)ClrTraceEventParser.Keywords.Exception,
-                        eventLevel: EventLevel.Verbose
+                        eventLevel: EventLevel.Verbose,
+                        keywords: (long)ClrTraceEventParser.Keywords.GC |
+                                  (long)ClrTraceEventParser.Keywords.GCHandle |
+                                  (long)ClrTraceEventParser.Keywords.Exception
                     ),
                 },
                 "Tracks GC collections and samples object allocations."),
             new Profile(
                 "gc-collect",
-                new Provider[] {
-                    new Provider(
+                new EventPipeProvider[] {
+                    new EventPipeProvider(
                         name: "Microsoft-Windows-DotNETRuntime",
-                        keywords:   (ulong)ClrTraceEventParser.Keywords.GC |
-                                    (ulong)ClrTraceEventParser.Keywords.Exception,
-                        eventLevel: EventLevel.Informational),
+                        eventLevel: EventLevel.Informational,
+                        keywords:   (long)ClrTraceEventParser.Keywords.GC |
+                                    (long)ClrTraceEventParser.Keywords.Exception
+                    )
                 },
                 "Tracks GC collections only at very low overhead."),
         };
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs
deleted file mode 100644 (file)
index 073ffb4..0000000
+++ /dev/null
@@ -1,48 +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 Microsoft.Diagnostics.Tools.RuntimeClient;
-using System;
-using System.CommandLine;
-using System.CommandLine.Invocation;
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Trace
-{
-    internal static class StopCommandHandler
-    {
-        public static async Task<int> Stop(IConsole console, int processId, ulong sessionId)
-        {
-            try
-            {
-                EventPipeClient.StopTracing(processId, sessionId);
-
-                await Task.FromResult(0);
-                return sessionId != 0 ? 0 : 1;
-            }
-            catch (Exception ex)
-            {
-                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
-                return 1;
-            }
-        }
-
-        public static Command StopCommand() =>
-            new Command(
-                name: "stop",
-                description: "Stops an EventPipe session.",
-                symbols: new Option[] {
-                    CommonOptions.ProcessIdOption(),
-                    SessionIdOption(),
-                },
-                handler: CommandHandler.Create<IConsole, int, ulong>(Stop),
-                isHidden: true);
-
-        private static Option SessionIdOption() =>
-            new Option(
-                new[] { "--session-id" },
-                @"Session Id being recorded.",
-                new Argument<ulong> { Name = "SessionId" });
-    }
-}
index a1055eeeb33f1c4477611be2a67f4568e72a84e6..59b54664ec68b0243acadce7c35e6e9618d0503b 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.Tracing;
@@ -14,12 +14,12 @@ namespace Microsoft.Diagnostics.Tools.Trace
     {
         private static EventLevel defaultEventLevel = EventLevel.Verbose;
 
-        public static List<Provider> ToProviders(string providers)
+        public static List<EventPipeProvider> ToProviders(string providers)
         {
             if (providers == null)
                 throw new ArgumentNullException(nameof(providers));
             return string.IsNullOrWhiteSpace(providers) ?
-                new List<Provider>() : providers.Split(',').Select(ToProvider).ToList();
+                new List<EventPipeProvider>() : providers.Split(',').Select(ToProvider).ToList();
         }
 
         private static EventLevel GetEventLevel(string token)
@@ -51,7 +51,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
             }
         }
 
-        private static Provider ToProvider(string provider)
+        private static EventPipeProvider ToProvider(string provider)
         {
             if (string.IsNullOrWhiteSpace(provider))
                 throw new ArgumentNullException(nameof(provider));
@@ -71,8 +71,8 @@ namespace Microsoft.Diagnostics.Tools.Trace
                 throw new ArgumentException("Provider name was not specified.");
 
             // Keywords
-            ulong keywords = tokens.Length > 1 && !string.IsNullOrWhiteSpace(tokens[1]) ?
-                Convert.ToUInt64(tokens[1], 16) : ulong.MaxValue;
+            long keywords = tokens.Length > 1 && !string.IsNullOrWhiteSpace(tokens[1]) ?
+                Convert.ToInt64(tokens[1], 16) : -1;
 
             // Level
             EventLevel eventLevel = tokens.Length > 2 && !string.IsNullOrWhiteSpace(tokens[2]) ?
@@ -80,9 +80,57 @@ namespace Microsoft.Diagnostics.Tools.Trace
 
             // Event counters
             string filterData = tokens.Length > 3 ? tokens[3] : null;
-            filterData = string.IsNullOrWhiteSpace(filterData) ? null : filterData;
+            var argument = string.IsNullOrWhiteSpace(filterData) ? null : ParseArgumentString(filterData); 
+            return new EventPipeProvider(providerName, eventLevel, keywords, argument);
+        }
+
+        private static Dictionary<string, string> ParseArgumentString(string argument)
+        {
+            if (argument == "")
+            {
+                return null;
+            }
+            var argumentDict = new Dictionary<string, string>();
 
-            return new Provider(providerName, keywords, eventLevel, filterData);
+            int keyStart = 0;
+            int keyEnd = 0;
+            int valStart = 0;
+            int valEnd = 0;
+            int curIdx = 0;
+            bool inQuote = false;
+            foreach (var c in argument)
+            {
+                if (inQuote)
+                {
+                    if (c == '\"')
+                    {
+                        inQuote = false;
+                    }
+                }
+                else
+                {
+                    if (c == '=')
+                    {
+                        keyEnd = curIdx;
+                        valStart = curIdx+1;
+                    }
+                    else if (c == ';')
+                    {
+                        valEnd = curIdx;
+                        argumentDict.Add(argument.Substring(keyStart, keyEnd-keyStart), argument.Substring(valStart, valEnd-valStart));
+                        keyStart = curIdx+1; // new key starts
+                    }
+                    else if (c == '\"')
+                    {
+                        inQuote = true;
+                    }
+                }
+                curIdx += 1;
+            }
+            string key = argument.Substring(keyStart, keyEnd - keyStart);
+            string val = argument.Substring(valStart);
+            argumentDict.Add(key, val);
+            return argumentDict;
         }
     }
 }
index 7bc28c08877cfb9309eaff668e421f36f9c43599..078ef9d8546aed0863370dffd6d298980973a8fc 100644 (file)
@@ -2,7 +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.Tools.RuntimeClient;
+using Microsoft.Diagnostics.NETCore.Client;
 using System.Collections.Generic;
 using System.Linq;
 
@@ -10,32 +10,32 @@ namespace Microsoft.Diagnostics.Tools.Trace
 {
     internal sealed class Profile
     {
-        public Profile(string name, IEnumerable<Provider> providers, string description)
+        public Profile(string name, IEnumerable<EventPipeProvider> providers, string description)
         {
             Name = name;
-            Providers = providers == null ? Enumerable.Empty<Provider>() : new List<Provider>(providers).AsReadOnly();
+            Providers = providers == null ? Enumerable.Empty<EventPipeProvider>() : new List<EventPipeProvider>(providers).AsReadOnly();
             Description = description;
         }
 
         public string Name { get; }
 
-        public IEnumerable<Provider> Providers { get; }
+        public IEnumerable<EventPipeProvider> Providers { get; }
 
         public string Description { get; }
 
-        public static void MergeProfileAndProviders(Profile selectedProfile, List<Provider> providerCollection, Dictionary<string, string> enabledBy)
+        public static void MergeProfileAndProviders(Profile selectedProfile, List<EventPipeProvider> providerCollection, Dictionary<string, string> enabledBy)
         {
-            var profileProviders = new List<Provider>();
+            var profileProviders = new List<EventPipeProvider>();
             // If user defined a different key/level on the same provider via --providers option that was specified via --profile option,
             // --providers option takes precedence. Go through the list of providers specified and only add it if it wasn't specified
             // via --providers options.
             if (selectedProfile.Providers != null)
             {
-                foreach (Provider selectedProfileProvider in selectedProfile.Providers)
+                foreach (EventPipeProvider selectedProfileProvider in selectedProfile.Providers)
                 {
                     bool shouldAdd = true;
 
-                    foreach (Provider providerCollectionProvider in providerCollection)
+                    foreach (EventPipeProvider providerCollectionProvider in providerCollection)
                     {
                         if (providerCollectionProvider.Name.Equals(selectedProfileProvider.Name))
                         {
index 48d00fb3276b3de5dfd8007edd649e4ebdb5c27f..606f7ae62c7782e303e313aa93e66bd5c0265d7e 100644 (file)
@@ -13,9 +13,6 @@ namespace Microsoft.Diagnostics.Tools.Trace
         public static Task<int> Main(string[] args)
         {
             var parser = new CommandLineBuilder()
-#if DEBUG
-                .AddCommand(StopCommandHandler.StopCommand())
-#endif
                 .AddCommand(CollectCommandHandler.CollectCommand())
                 .AddCommand(ListProcessesCommandHandler.ListProcessesCommand())
                 .AddCommand(ListProfilesCommandHandler.ListProfilesCommand())
index 43dfc2dc2ab8eac281783ea3eab685fb16dcc2ad..2f7e3713334e49a358f978ba7e010ab81c37b196 100644 (file)
@@ -16,7 +16,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/CommonHelper.cs
new file mode 100644 (file)
index 0000000..d110911
--- /dev/null
@@ -0,0 +1,28 @@
+// 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.Reflection;
+using System.Runtime.InteropServices;
+
+using Microsoft.Diagnostics.TestHelpers;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public class CommonHelper
+    {
+        public static string HostExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 
+            "..\\..\\..\\..\\..\\.dotnet\\dotnet.exe" : "../../../../../.dotnet/dotnet";
+        
+        public static string GetTraceePath()
+        {
+            var curPath = Directory.GetCurrentDirectory();
+;
+            var traceePath = curPath.Replace("Microsoft.Diagnostics.NETCore.Client.UnitTests", "Tracee");
+
+            return Path.Combine(traceePath, "Tracee.dll");
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeProviderTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeProviderTests.cs
new file mode 100644 (file)
index 0000000..5f66f28
--- /dev/null
@@ -0,0 +1,113 @@
+// 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 Microsoft.Diagnostics.NETCore.Client;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Tracing;
+using Xunit;
+
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+
+    /// <summary>
+    /// Suite of tests that test top-level commands
+    /// </summary>
+    public class EventPipeProviderTests
+    {
+        [Fact]
+        public void EqualTest1()
+        {
+            EventPipeProvider provider1 = new EventPipeProvider("myProvider", EventLevel.Informational);
+            EventPipeProvider provider2 = new EventPipeProvider("myProvider", EventLevel.Informational);
+            Assert.True(provider1 == provider2);
+        }
+
+        [Fact]
+        public void EqualTest2()
+        {
+            EventPipeProvider provider1 = new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Verbose, (long)(-1));
+            EventPipeProvider provider2 = new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Verbose, (long)(-1));
+            Assert.True(provider1 == provider2);
+        }
+
+        [Fact]
+        public void EqualTest3()
+        {
+            EventPipeProvider provider1 = new EventPipeProvider(
+                "System.Runtime",
+                EventLevel.Verbose,
+                (long)(-1),
+                new Dictionary<string, string>() {
+                    { "EventCounterIntervalSec", "1" }
+                });
+            EventPipeProvider provider2 = new EventPipeProvider(
+                "System.Runtime",
+                EventLevel.Verbose,
+                (long)(-1),
+                new Dictionary<string, string>() {
+                    { "EventCounterIntervalSec", "1" }
+                });
+            Assert.True(provider1 == provider2);
+        }
+
+        [Fact]
+        public void InEqualityTest()
+        {
+            var providers = new EventPipeProvider[5];
+            providers[0] = new EventPipeProvider("myProvider", EventLevel.Informational);
+            providers[1] = new EventPipeProvider("myProvider", EventLevel.Informational, (long)(-1));
+            providers[2] = new EventPipeProvider("myProvider", EventLevel.Verbose, (long)(-1));
+            providers[3] = new EventPipeProvider(
+                "myProvider",
+                EventLevel.Verbose,
+                (long)(-1),
+                new Dictionary<string, string>() {
+                    { "EventCounterIntervalSec", "1" }
+                });
+            providers[4] = new EventPipeProvider(
+                "myProvider",
+                EventLevel.Verbose,
+                (long)(-1),
+                new Dictionary<string, string>() {
+                    { "EventCounterIntervalSec", "2" }
+                });
+
+            for (var i = 0; i < providers.Length-1; i++)
+            {
+                for (var j = i+1; j < providers.Length; j++)
+                {
+                    Assert.True(providers[i] != providers[j]);
+                }
+            }
+        }
+
+        [Fact]
+        public void ToStringTest1()
+        {
+            var provider = new EventPipeProvider("MyProvider", EventLevel.Verbose, (long)(0xdeadbeef));
+            Assert.Equal("MyProvider:0x00000000DEADBEEF:5", provider.ToString());
+        }
+
+        [Fact]
+        public void ToStringTest2()
+        {
+            var provider1 = new EventPipeProvider("MyProvider", EventLevel.Verbose, (long)(0xdeadbeef),
+                new Dictionary<string, string>()
+                {
+                    { "key1", "value1" },
+                });
+            var provider2 = new EventPipeProvider("MyProvider", EventLevel.Verbose, (long)(0xdeadbeef),
+                new Dictionary<string, string>()
+                {
+                    { "key1", "value1" },
+                    { "key2", "value2" }
+                });
+            Assert.Equal("MyProvider:0x00000000DEADBEEF:5:key1=value1", provider1.ToString());
+            Assert.Equal("MyProvider:0x00000000DEADBEEF:5:key1=value1;key2=value2", provider2.ToString());
+        }
+    }
+}
diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs
new file mode 100644 (file)
index 0000000..6344f47
--- /dev/null
@@ -0,0 +1,115 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Tracing;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.TestHelpers;
+using Microsoft.Diagnostics.NETCore.Client;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public class EventPipeSessionTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public EventPipeSessionTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        /// <summary>
+        /// A simple test that checks if we can create an EventPipeSession on a child process
+        /// </summary>
+        [Fact]
+        public void BasicEventPipeSessionTest()
+        {
+            TestRunner runner = new TestRunner(CommonHelper.GetTraceePath(), output);
+            runner.Start(3000);
+            DiagnosticsClient client = new DiagnosticsClient(runner.Pid);
+            using (var session = client.StartEventPipeSession(new List<EventPipeProvider>()
+            {
+                new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational)
+            }))
+            {
+                Assert.True(session.EventStream != null);
+            }
+            runner.Stop();
+        }
+
+        /// <summary>
+        /// Checks if we can create an EventPipeSession and can get some expected events out of it.
+        /// </summary>
+        [Fact]
+        public void EventPipeSessionStreamTest()
+        {
+            TestRunner runner = new TestRunner(CommonHelper.GetTraceePath(), output);
+            runner.Start(5000);
+            DiagnosticsClient client = new DiagnosticsClient(runner.Pid);
+            runner.PrintStatus();
+            output.WriteLine($"[{DateTime.Now.ToString()}] Trying to start an EventPipe session on process {runner.Pid}");
+            using (var session = client.StartEventPipeSession(new List<EventPipeProvider>()
+            {
+                new EventPipeProvider("System.Runtime", EventLevel.Informational, 0, new Dictionary<string, string>() {
+                    { "EventCounterIntervalSec", "1" }
+                })
+            }))
+            {
+                var evntCnt = 0;
+
+                Task streamTask = Task.Run(() => {
+                    var source = new EventPipeEventSource(session.EventStream);
+                    source.Dynamic.All += (TraceEvent obj) => {
+                        output.WriteLine("Got an event");
+                        evntCnt += 1;
+                    };
+                    try
+                    {
+                        source.Process();
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine("Error encountered while processing events");
+                        Assert.Equal("", e.ToString());
+                    }
+                    finally
+                    {
+                        runner.Stop();
+                    }
+                });
+                output.WriteLine("Waiting for stream Task");
+                streamTask.Wait(10000);
+                output.WriteLine("Done waiting for stream Task");
+                Assert.True(evntCnt > 0);
+            }
+        }
+
+        /// <summary>
+        /// Tries to start an EventPipe session on a non-existent process
+        /// </summary>
+        [Fact]
+        public void EventPipeSessionUnavailableTest()
+        {
+            List<int> pids = new List<int>(DiagnosticsClient.GetPublishedProcesses());
+            int arbitraryPid = 1;
+
+            DiagnosticsClient client = new DiagnosticsClient(arbitraryPid);
+
+            Assert.Throws<ServerNotAvailableException>(() => client.StartEventPipeSession(new List<EventPipeProvider>()
+            {
+                new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational)
+            }));
+        }
+    }
+}
diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/GetPublishedProcessesTests.cs
new file mode 100644 (file)
index 0000000..e46884d
--- /dev/null
@@ -0,0 +1,76 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Runtime.InteropServices;
+using Xunit;
+using Xunit.Abstractions;
+
+using Microsoft.Diagnostics.TestHelpers;
+using Microsoft.Diagnostics.NETCore.Client;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    
+    /// <summary>
+    /// Suite of tests that test top-level commands
+    /// </summary>
+    public class GetPublishedProcessesTest
+    {
+        private readonly ITestOutputHelper output;
+
+        public GetPublishedProcessesTest(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        [Fact]
+        public void PublishedProcessTest1()
+        {
+            TestRunner runner = new TestRunner(CommonHelper.GetTraceePath(), output);
+            runner.Start(3000);
+            List<int> publishedProcesses = new List<int>(DiagnosticsClient.GetPublishedProcesses());
+            foreach (int p in publishedProcesses)
+            {
+                output.WriteLine($"[{DateTime.Now.ToString()}] Saw published process {p}");
+            }
+            Assert.Contains(publishedProcesses, p => p == runner.Pid);
+            runner.Stop();
+        }
+
+        [Fact]
+        public void MultiplePublishedProcessTest()
+        {
+            TestRunner[] runner = new TestRunner[3];
+            int[] pids = new int[3];
+
+            for (var i = 0; i < 3; i++)
+            {
+                runner[i] = new TestRunner(CommonHelper.GetTraceePath(), output);
+                runner[i].Start();
+                pids[i] = runner[i].Pid;
+            }
+            System.Threading.Thread.Sleep(2000);
+            List<int> publishedProcesses = new List<int>(DiagnosticsClient.GetPublishedProcesses());
+            foreach (int p in publishedProcesses)
+            {
+                output.WriteLine($"[{DateTime.Now.ToString()}] 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++)
+            {
+                runner[i].Stop();
+            }
+        }
+    }
+}
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
new file mode 100644 (file)
index 0000000..772f23d
--- /dev/null
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="../../Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.csproj" />
+    <ProjectReference Include="../../Microsoft.Diagnostics.TestHelpers/Microsoft.Diagnostics.TestHelpers.csproj" />
+    <ProjectReference Include="../Tracee/Tracee.csproj" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.47" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/TestRunner.cs
new file mode 100644 (file)
index 0000000..420bde8
--- /dev/null
@@ -0,0 +1,86 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using Xunit;
+using Xunit.Abstractions;
+
+using Microsoft.Diagnostics.TestHelpers;
+
+using Microsoft.Diagnostics.NETCore.Client;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public class TestRunner
+    {
+        private Process testProcess;
+        private ProcessStartInfo startInfo;
+        private ITestOutputHelper outputHelper;
+
+        public TestRunner(string testExePath, ITestOutputHelper _outputHelper=null)
+        {
+            startInfo = new ProcessStartInfo(CommonHelper.HostExe, testExePath);
+            startInfo.UseShellExecute = false;
+            startInfo.RedirectStandardOutput = true;
+            outputHelper = _outputHelper;
+        }
+
+        public void AddEnvVar(string key, string value)
+        {
+            startInfo.EnvironmentVariables[key] = value;
+        }
+
+        public void Start(int timeoutInMS=0)
+        {
+            if (outputHelper != null)
+                outputHelper.WriteLine("$[{DateTime.Now.ToString()}] Launching test: " + startInfo.FileName);
+
+            testProcess = Process.Start(startInfo);
+
+            if (testProcess == null)
+            {
+                outputHelper.WriteLine($"Could not start process: " + startInfo.FileName);
+            }
+
+            if (testProcess.HasExited)
+            {
+                outputHelper.WriteLine($"Process " + startInfo.FileName + " came back as exited");
+            }
+
+            if (outputHelper != null)
+            {
+                outputHelper.WriteLine($"[{DateTime.Now.ToString()}] Successfuly started process {testProcess.Id}");
+                outputHelper.WriteLine($"Have total {testProcess.Modules.Count} modules loaded");
+            }
+
+            outputHelper.WriteLine($"[{DateTime.Now.ToString()}] Sleeping for {timeoutInMS} ms.");
+            Thread.Sleep(timeoutInMS);
+            outputHelper.WriteLine($"[{DateTime.Now.ToString()}] Done sleeping. Ready to test.");
+        }
+
+        public void Stop()
+        {
+            testProcess.Kill();
+        }
+
+        public int Pid {
+            get { return testProcess.Id; }
+        }
+
+        public void PrintStatus()
+        {
+            if (testProcess.HasExited)
+            {
+                outputHelper.WriteLine($"Process {testProcess.Id} status: Exited");
+            }
+            else
+            {
+                outputHelper.WriteLine($"Process {testProcess.Id} status: Running");
+            }
+        }
+    }
+}
diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/WriteDumpTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/WriteDumpTests.cs
new file mode 100644 (file)
index 0000000..f438f88
--- /dev/null
@@ -0,0 +1,124 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Tracing;
+using System.IO;
+using System.Runtime.InteropServices;
+using Xunit;
+using Xunit.Abstractions;
+
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.TestHelpers;
+using Microsoft.Diagnostics.NETCore.Client;
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public class WriteDumpTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public WriteDumpTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        /// <summary>
+        /// A simple test that writes a single dump file
+        /// </summary>
+        [Fact]
+        public void BasicWriteDumpTest()
+        {
+            var dumpPath = "./myDump.dmp";
+            TestRunner runner = new TestRunner(CommonHelper.GetTraceePath(), output);
+            runner.Start(3000);
+            DiagnosticsClient client = new DiagnosticsClient(runner.Pid);
+
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => client.WriteDump(DumpType.Normal, dumpPath));
+            }
+            else
+            {
+                output.WriteLine($"Requesting dump at {DateTime.Now.ToString()}");
+                client.WriteDump(DumpType.Normal, dumpPath);
+                Assert.True(File.Exists(dumpPath));
+                File.Delete(dumpPath);
+            }
+            runner.Stop();
+        }
+
+        /// <summary>
+        /// A test that writes all the different types of dump file
+        /// </summary>
+        [Fact]
+        public void WriteAllDumpTypesTest()
+        {
+            var normalDumpPath = "./myDump-normal.dmp";
+            var heapDumpPath = "./myDump-heap.dmp";
+            var triageDumpPath = "./myDump-triage.dmp";
+            var fullDumpPath = "./myDump-full.dmp";
+            TestRunner runner = new TestRunner(CommonHelper.GetTraceePath(), output);
+            runner.Start(3000);
+            DiagnosticsClient client = new DiagnosticsClient(runner.Pid);
+
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => client.WriteDump(DumpType.Normal, normalDumpPath));
+                Assert.Throws<PlatformNotSupportedException>(() => client.WriteDump(DumpType.WithHeap, heapDumpPath));
+                Assert.Throws<PlatformNotSupportedException>(() => client.WriteDump(DumpType.Triage, triageDumpPath));
+                Assert.Throws<PlatformNotSupportedException>(() => client.WriteDump(DumpType.Full, fullDumpPath));
+            }
+            else
+            {
+                // Write each type of dump
+                output.WriteLine($"Requesting dump at {DateTime.Now.ToString()}");
+                client.WriteDump(DumpType.Normal, normalDumpPath);
+                client.WriteDump(DumpType.WithHeap, heapDumpPath);
+                client.WriteDump(DumpType.Triage, triageDumpPath);
+                client.WriteDump(DumpType.Full, fullDumpPath);
+
+                // Check they were all created
+                Assert.True(File.Exists(normalDumpPath));
+                Assert.True(File.Exists(heapDumpPath));
+                Assert.True(File.Exists(triageDumpPath));
+                Assert.True(File.Exists(fullDumpPath));
+
+                // Remove them
+                File.Delete(normalDumpPath);
+                File.Delete(heapDumpPath);
+                File.Delete(triageDumpPath);
+                File.Delete(fullDumpPath);
+            }
+            runner.Stop();
+        }
+
+        /// <summary>
+        /// A test that tries to write a dump of a non-existent process
+        /// </summary>
+        [Fact]
+        public void WriteDumpFailTest()
+        {
+            List<int> pids = new List<int>(DiagnosticsClient.GetPublishedProcesses());
+            int arbitraryPid = 1;
+            string dumpPath = "./myDump.dmp";
+            while (pids.Contains(arbitraryPid))
+            {
+                arbitraryPid += 1;
+            }
+
+            var client = new DiagnosticsClient(arbitraryPid);
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => client.WriteDump(DumpType.Normal, dumpPath));
+            }
+            else
+            {
+                Assert.Throws<ServerNotAvailableException>(() => client.WriteDump(DumpType.Normal, "./myDump.dmp"));
+            }
+        }
+    }
+}
diff --git a/src/tests/Tracee/Program.cs b/src/tests/Tracee/Program.cs
new file mode 100644 (file)
index 0000000..c6143cf
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.Threading;
+
+namespace Tracee
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            // Runs for max of 30 sec
+            for(var i = 0; i < 30; i++)
+            {
+                Thread.Sleep(1000);
+            }
+        }
+    }
+}
diff --git a/src/tests/Tracee/Tracee.csproj b/src/tests/Tracee/Tracee.csproj
new file mode 100644 (file)
index 0000000..58b042c
--- /dev/null
@@ -0,0 +1,7 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework Condition="'$(BuildProjectFramework)' != ''">$(BuildProjectFramework)</TargetFramework>
+    <TargetFrameworks Condition="'$(BuildProjectFramework)' == ''">netcoreapp3.0;</TargetFrameworks>
+  </PropertyGroup>
+</Project>
index 6cf17b120b2dbfce4413e961418bfc7a2df4e227..c438728da3d02360f9c273edbd8ebf44316df896 100644 (file)
@@ -1,10 +1,9 @@
 // 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 Microsoft.Diagnostics.NETCore.Client;
 using System;
 using Xunit;
-using Microsoft.Diagnostics.Tools.RuntimeClient;
 using System.Collections.Generic;
 using System.Linq;
 using System.Diagnostics.Tracing;
@@ -21,7 +20,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         {
             Dictionary<string, string> enabledBy = new Dictionary<string, string>();
 
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
 
             foreach (var provider in parsedProviders)
             {
@@ -35,10 +34,9 @@ namespace Microsoft.Diagnostics.Tools.Trace
             Profile.MergeProfileAndProviders(selectedProfile, parsedProviders, enabledBy);
 
             var enabledProvider = parsedProviders.SingleOrDefault(p => p.Name == "Microsoft-Windows-DotNETRuntime");
-            Assert.True(enabledProvider != default(Provider));
 
             // Assert that our specified provider overrides the version in the profile
-            Assert.True(enabledProvider.Keywords == UInt64.MaxValue);
+            Assert.True(enabledProvider.Keywords == (long)(-1));
             Assert.True(enabledProvider.EventLevel == EventLevel.Verbose);
             Assert.True(enabledBy[enabledProvider.Name] == "--providers");
         }
index 8d1930d4b957f81bec434e3bf62011150bcfbc8f..a311a5746641867c53cce5f0bd1c3ddcac92db9e 100644 (file)
@@ -2,9 +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.NETCore.Client;
 using System;
 using Xunit;
-using Microsoft.Diagnostics.Tools.RuntimeClient;
 using System.Collections.Generic;
 using System.Linq;
 
@@ -17,26 +17,28 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("VeryCoolProvider:1:5:FilterAndPayloadSpecs=\"QuotedValue\"")]
         public void ValidProvider_CorrectlyParses(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
-            Provider provider = parsedProviders.First();
+            EventPipeProvider provider = parsedProviders.First();
             Assert.True(provider.Name == "VeryCoolProvider");
             Assert.True(provider.Keywords == 1);
             Assert.True(provider.EventLevel == System.Diagnostics.Tracing.EventLevel.Verbose);
-            Assert.True(provider.FilterData == "FilterAndPayloadSpecs=\"QuotedValue\"");
+            Assert.True(provider.Arguments.Count == 1);
+            Assert.True(provider.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue\"");
         }
 
         [Theory]
         [InlineData("VeryCoolProvider:0x1:5:FilterAndPayloadSpecs=\"QuotedValue:-\r\nQuoted/Value\"")]
         public void ValidProviderFilter_CorrectlyParses(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
-            Provider provider = parsedProviders.First();
+            EventPipeProvider provider = parsedProviders.First();
             Assert.True(provider.Name == "VeryCoolProvider");
             Assert.True(provider.Keywords == 1);
             Assert.True(provider.EventLevel == System.Diagnostics.Tracing.EventLevel.Verbose);
-            Assert.True(provider.FilterData == "FilterAndPayloadSpecs=\"QuotedValue:-\r\nQuoted/Value\"");
+            Assert.True(provider.Arguments.Count == 1);
+            Assert.True(provider.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue:-\r\nQuoted/Value\"");
         }
 
         [Theory]
@@ -57,16 +59,16 @@ namespace Microsoft.Diagnostics.Tools.Trace
 
         [Theory]
         [InlineData("VeryCoolProvider:0xFFFFFFFFFFFFFFFF:5:FilterAndPayloadSpecs=\"QuotedValue\"")]
-        [InlineData("VeryCoolProvider::5:FilterAndPayloadSpecs=\"QuotedValue\"")]
         public void ValidProviderKeyword_CorrectlyParses(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
-            Provider provider = parsedProviders.First();
+            EventPipeProvider provider = parsedProviders.First();
             Assert.True(provider.Name == "VeryCoolProvider");
-            Assert.True(provider.Keywords == ulong.MaxValue);
+            Assert.True(provider.Keywords == (long)(-1));
             Assert.True(provider.EventLevel == System.Diagnostics.Tracing.EventLevel.Verbose);
-            Assert.True(provider.FilterData == "FilterAndPayloadSpecs=\"QuotedValue\"");
+            Assert.True(provider.Arguments.Count == 1);
+            Assert.True(provider.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue\"");
         }
 
         [Theory]
@@ -74,13 +76,14 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("VeryCoolProvider:::FilterAndPayloadSpecs=\"QuotedValue\"")]
         public void ValidProviderEventLevel_CorrectlyParses(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
-            Provider provider = parsedProviders.First();
+            EventPipeProvider provider = parsedProviders.First();
             Assert.True(provider.Name == "VeryCoolProvider");
-            Assert.True(provider.Keywords == ulong.MaxValue);
+            Assert.True(provider.Keywords == (long)(-1));
             Assert.True(provider.EventLevel == System.Diagnostics.Tracing.EventLevel.Verbose);
-            Assert.True(provider.FilterData == "FilterAndPayloadSpecs=\"QuotedValue\"");
+            Assert.True(provider.Arguments.Count == 1);
+            Assert.True(provider.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue\"");
         }
 
         [Theory]
@@ -104,26 +107,29 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:1:1:FilterAndPayloadSpecs=\"QuotedValue\",ProviderTwo:0x2:2:key=value,ProviderThree:0x3:3:key=value")]
         public void MultipleValidProviders_CorrectlyParses(string providersToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providersToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providersToParse);
             Assert.True(parsedProviders.Count == 3);
-            Provider providerOne = parsedProviders[0];
-            Provider providerTwo = parsedProviders[1];
-            Provider providerThree = parsedProviders[2];
+            EventPipeProvider providerOne = parsedProviders[0];
+            EventPipeProvider providerTwo = parsedProviders[1];
+            EventPipeProvider providerThree = parsedProviders[2];
 
             Assert.True(providerOne.Name == "ProviderOne");
             Assert.True(providerOne.Keywords == 1);
             Assert.True(providerOne.EventLevel == System.Diagnostics.Tracing.EventLevel.Critical);
-            Assert.True(providerOne.FilterData == "FilterAndPayloadSpecs=\"QuotedValue\"");
+            Assert.True(providerOne.Arguments.Count == 1);
+            Assert.True(providerOne.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue\"");
 
             Assert.True(providerTwo.Name == "ProviderTwo");
             Assert.True(providerTwo.Keywords == 2);
             Assert.True(providerTwo.EventLevel == System.Diagnostics.Tracing.EventLevel.Error);
-            Assert.True(providerTwo.FilterData == "key=value");
+            Assert.True(providerTwo.Arguments.Count == 1);
+            Assert.True(providerTwo.Arguments["key"] == "value");
 
             Assert.True(providerThree.Name == "ProviderThree");
             Assert.True(providerThree.Keywords == 3);
             Assert.True(providerThree.EventLevel == System.Diagnostics.Tracing.EventLevel.Warning);
-            Assert.True(providerThree.FilterData == "key=value");
+            Assert.True(providerThree.Arguments.Count == 1);
+            Assert.True(providerThree.Arguments["key"] == "value");
         }
 
         [Theory]
@@ -157,26 +163,29 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:1:FilterAndPayloadSpecs=\"QuotedValue:-\r\nQuoted/Value:-A=B;C=D;\",ProviderTwo:2:2:FilterAndPayloadSpecs=\"QuotedValue\",ProviderThree:3:3:FilterAndPayloadSpecs=\"QuotedValue:-\r\nQuoted/Value:-A=B;C=D;\"")]
         public void MultipleProvidersWithComplexFilters_CorrectlyParse(string providersToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providersToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providersToParse);
             Assert.True(parsedProviders.Count == 3);
-            Provider providerOne = parsedProviders[0];
-            Provider providerTwo = parsedProviders[1];
-            Provider providerThree = parsedProviders[2];
+            EventPipeProvider providerOne = parsedProviders[0];
+            EventPipeProvider providerTwo = parsedProviders[1];
+            EventPipeProvider providerThree = parsedProviders[2];
 
             Assert.True(providerOne.Name == "ProviderOne");
             Assert.True(providerOne.Keywords == 1);
             Assert.True(providerOne.EventLevel == System.Diagnostics.Tracing.EventLevel.Critical);
-            Assert.True(providerOne.FilterData == "FilterAndPayloadSpecs=\"QuotedValue:-\r\nQuoted/Value:-A=B;C=D;\"");
+            Assert.True(providerOne.Arguments.Count == 1);
+            Assert.True(providerOne.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue:-\r\nQuoted/Value:-A=B;C=D;\"");
 
             Assert.True(providerTwo.Name == "ProviderTwo");
             Assert.True(providerTwo.Keywords == 2);
             Assert.True(providerTwo.EventLevel == System.Diagnostics.Tracing.EventLevel.Error);
-            Assert.True(providerTwo.FilterData == "FilterAndPayloadSpecs=\"QuotedValue\"");
+            Assert.True(providerTwo.Arguments.Count == 1);
+            Assert.True(providerTwo.Arguments["FilterAndPayloadSpecs"]== "\"QuotedValue\"");
 
             Assert.True(providerThree.Name == "ProviderThree");
             Assert.True(providerThree.Keywords == 3);
             Assert.True(providerThree.EventLevel == System.Diagnostics.Tracing.EventLevel.Warning);
-            Assert.True(providerThree.FilterData == "FilterAndPayloadSpecs=\"QuotedValue:-\r\nQuoted/Value:-A=B;C=D;\"");
+            Assert.True(providerThree.Arguments.Count == 1);
+            Assert.True(providerThree.Arguments["FilterAndPayloadSpecs"] == "\"QuotedValue:-\r\nQuoted/Value:-A=B;C=D;\"");
         }
 
         [Theory]
@@ -184,7 +193,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:verbose")]
         public void TextLevelProviderSpecVerbose_CorrectlyParse(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
             Assert.True(parsedProviders[0].Name == "ProviderOne");
             Assert.True(parsedProviders[0].Keywords == 1);
@@ -196,7 +205,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:INFORMATIONAL")]
         public void TextLevelProviderSpecInformational_CorrectlyParse(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
             Assert.True(parsedProviders[0].Name == "ProviderOne");
             Assert.True(parsedProviders[0].Keywords == 1);
@@ -208,7 +217,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:LogAlwayS")]        
         public void TextLevelProviderSpecLogAlways_CorrectlyParse(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
             Assert.True(parsedProviders[0].Name == "ProviderOne");
             Assert.True(parsedProviders[0].Keywords == 1);
@@ -220,7 +229,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:ERRor")]
         public void TextLevelProviderSpecError_CorrectlyParse(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
             Assert.True(parsedProviders[0].Name == "ProviderOne");
             Assert.True(parsedProviders[0].Keywords == 1);
@@ -232,7 +241,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:CRITICAL")]
         public void TextLevelProviderSpecCritical_CorrectlyParse(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
             Assert.True(parsedProviders[0].Name == "ProviderOne");
             Assert.True(parsedProviders[0].Keywords == 1);
@@ -244,7 +253,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
         [InlineData("ProviderOne:0x1:warning")]
         public void TextLevelProviderSpecWarning_CorrectlyParse(string providerToParse)
         {
-            List<Provider> parsedProviders = Extensions.ToProviders(providerToParse);
+            List<EventPipeProvider> parsedProviders = Extensions.ToProviders(providerToParse);
             Assert.True(parsedProviders.Count == 1);
             Assert.True(parsedProviders[0].Name == "ProviderOne");
             Assert.True(parsedProviders[0].Keywords == 1);