[dotnet-trace] Updated dotnet-trace to use IPC to communicate with the runtime (...
authorJosé Rivero <jorive@microsoft.com>
Wed, 27 Mar 2019 23:19:20 +0000 (16:19 -0700)
committerGitHub <noreply@github.com>
Wed, 27 Mar 2019 23:19:20 +0000 (16:19 -0700)
* Microsoft.Diagnostics.Tools.Collect -> Microsoft.Diagnostics.Tools.Trace
* Updated dotnet-trace with the following options:
  start - Starts tracing
  stop - Stops tracing
* Ignore netperf files.
* Naive stream.
* Helper scripts.
* Update the `diagnostics.sln` file.
* Small changes in API

29 files changed:
.gitignore
diagnostics.sln
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/DiagnosticMessageType.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/MessageHeader.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/Provider.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Extensions.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Microsoft.Diagnostics.Tools.RuntimeClient.csproj [new file with mode: 0644]
src/Tools/dotnet-trace/CollectionConfiguration.cs [deleted file]
src/Tools/dotnet-trace/CollectionProfile.cs [deleted file]
src/Tools/dotnet-trace/CommandLine/Commands/PortsCommandHandler.cs [new file with mode: 0644]
src/Tools/dotnet-trace/CommandLine/Commands/ProvidersCommandHandler.cs [new file with mode: 0644]
src/Tools/dotnet-trace/CommandLine/Commands/StartCommandHandler.cs [new file with mode: 0644]
src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs [new file with mode: 0644]
src/Tools/dotnet-trace/CommandLine/Commands/StreamCommand.cs [new file with mode: 0644]
src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs [new file with mode: 0644]
src/Tools/dotnet-trace/ConfigPathDetector.cs [deleted file]
src/Tools/dotnet-trace/EtwCollector.cs [deleted file]
src/Tools/dotnet-trace/EventCollector.cs [deleted file]
src/Tools/dotnet-trace/EventPipeCollector.cs [deleted file]
src/Tools/dotnet-trace/EventSpec.cs [deleted file]
src/Tools/dotnet-trace/KnownData.cs [deleted file]
src/Tools/dotnet-trace/LoggerSpec.cs [deleted file]
src/Tools/dotnet-trace/Program.cs
src/Tools/dotnet-trace/StringParser.cs [deleted file]
src/Tools/dotnet-trace/build.cmd [new file with mode: 0644]
src/Tools/dotnet-trace/dotnet-trace.csproj
src/Tools/dotnet-trace/run.cmd [new file with mode: 0644]

index 5f458400c8dd70f5b803c144ee3d8218e95add31..073132444f78861b2dba3e3da980657c60d09759 100644 (file)
@@ -114,3 +114,5 @@ $RECYCLE.BIN/
 # SOS stress log
 StressLog.txt
 
+# EventPipe files
+*.netperf
index d5da8fd2ac45fedbe5386f33b2d9739b05b6cfca..73cf4d2072b9e3c800b068924cfb31558793c168 100644 (file)
@@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostic.Repl",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-counters", "src\Tools\dotnet-counters\dotnet-counters.csproj", "{2A9B5988-982F-4E26-9E44-D38AC5978C30}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.Tools.RuntimeClient", "src\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj", "{54C240C5-7932-4421-A5FB-75205DE0B824}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Checked|Any CPU = Checked|Any CPU
@@ -343,6 +345,46 @@ Global
                {1576314E-F823-4A24-BC90-22282AB33353}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
                {1576314E-F823-4A24-BC90-22282AB33353}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
                {1576314E-F823-4A24-BC90-22282AB33353}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|Any CPU.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM64.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x64.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x86.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM64.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x64.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x86.Build.0 = Debug|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|Any CPU.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM64.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x64.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x64.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x86.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x86.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
                {43D41DE9-7CCC-4DCB-A68A-B9099E538125}.Checked|Any CPU.ActiveCfg = Release|Any CPU
                {43D41DE9-7CCC-4DCB-A68A-B9099E538125}.Checked|Any CPU.Build.0 = Release|Any CPU
                {43D41DE9-7CCC-4DCB-A68A-B9099E538125}.Checked|ARM.ActiveCfg = Release|Any CPU
@@ -679,46 +721,46 @@ Global
                {2A9B5988-982F-4E26-9E44-D38AC5978C30}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
                {2A9B5988-982F-4E26-9E44-D38AC5978C30}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
                {2A9B5988-982F-4E26-9E44-D38AC5978C30}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|Any CPU.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM64.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|ARM64.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x64.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x86.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Checked|x86.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|Any CPU.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM64.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|ARM64.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x64.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x64.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x86.ActiveCfg = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Debug|x86.Build.0 = Debug|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|Any CPU.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|Any CPU.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM64.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|ARM64.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x64.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x64.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x86.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.Release|x86.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|Any CPU.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|ARM.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|ARM.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|ARM64.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|x64.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Checked|x86.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|ARM.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|ARM64.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|x64.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Debug|x86.Build.0 = Debug|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|Any CPU.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|ARM.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|ARM.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|ARM64.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|ARM64.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|x64.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|x64.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|x86.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.Release|x86.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
+               {54C240C5-7932-4421-A5FB-75205DE0B824}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -733,6 +775,7 @@ Global
                {D52C65C4-2C7D-45E6-9F5C-6F3A96796018} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
                {B62728C8-1267-4043-B46F-5537BBAEC692} = {19FAB78C-3351-4911-8F0C-8C6056401740}
                {1576314E-F823-4A24-BC90-22282AB33353} = {B62728C8-1267-4043-B46F-5537BBAEC692}
+               {718350FA-2DD9-4950-BA41-D7A7F66DAC91} = {B62728C8-1267-4043-B46F-5537BBAEC692}
                {43D41DE9-7CCC-4DCB-A68A-B9099E538125} = {B62728C8-1267-4043-B46F-5537BBAEC692}
                {41F59D85-FC36-3015-861B-F177863252BC} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
                {A9A7C879-C320-3327-BB84-16E1322E17AE} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
@@ -742,7 +785,8 @@ Global
                {41351955-16D5-48D7-AF4C-AF25F5FB2E78} = {B62728C8-1267-4043-B46F-5537BBAEC692}
                {ED27F39F-DF5C-4E22-87E0-EC5B5873B503} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
                {90CF2633-58F0-44EE-943B-D70207455F20} = {19FAB78C-3351-4911-8F0C-8C6056401740}
-               {718350FA-2DD9-4950-BA41-D7A7F66DAC91} = {B62728C8-1267-4043-B46F-5537BBAEC692}
+               {2A9B5988-982F-4E26-9E44-D38AC5978C30} = {B62728C8-1267-4043-B46F-5537BBAEC692}
+               {54C240C5-7932-4421-A5FB-75205DE0B824} = {19FAB78C-3351-4911-8F0C-8C6056401740}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/DiagnosticMessageType.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/DiagnosticMessageType.cs
new file mode 100644 (file)
index 0000000..aae7811
--- /dev/null
@@ -0,0 +1,15 @@
+// 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.Tools.RuntimeClient.Eventing
+{
+    enum DiagnosticMessageType : uint
+    {
+        StartSession = 1024,
+        StopSession,
+        Stream,
+        Attach,
+        Detach,
+    }
+}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs
new file mode 100644 (file)
index 0000000..636259f
--- /dev/null
@@ -0,0 +1,175 @@
+// 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.Globalization;
+using System.IO;
+using System.IO.Pipes;
+using System.Linq;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.Diagnostics.Tools.RuntimeClient.Eventing
+{
+    public static class EventPipeClient
+    {
+        private static string DiagnosticPortPattern { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"^dotnetcore-diagnostic-(\d+)$" : @"^dotnetcore-diagnostic-(\d+)-(\d+)-socket$";
+
+        private static string IpcRootPath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"\\.\pipe\" : Path.GetTempPath();
+
+        public static ulong SendCommand(int processId, byte[] buffer)
+        {
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                var pipeName = $"dotnetcore-diagnostic-{processId}";
+                using (var namedPipe = new NamedPipeClientStream(
+                    ".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation))
+                {
+                    namedPipe.Connect((int)TimeSpan.FromSeconds(20).TotalMilliseconds);
+
+                    var sw = new BinaryWriter(namedPipe);
+                    sw.Write(buffer);
+
+                    var br = new BinaryReader(namedPipe);
+                    return br.ReadUInt64();
+                }
+            }
+            else
+            {
+                //throw new PlatformNotSupportedException("TODO: Get the ApplicationGroupId to form the string: 'dotnetcore-diagnostic-{processId}-{ApplicationGroupId}-socket'");
+                var ipcPort = Directory.GetFiles(IpcRootPath) // Try best match.
+                    .Select(namedPipe => (new FileInfo(namedPipe)).Name)
+                    .Single(input => Regex.IsMatch(input, $"^dotnetcore-diagnostic-{processId}-(\\d+)-socket$"));
+                var path = Path.Combine(Path.GetTempPath(), ipcPort);
+                var remoteEP = new UnixDomainSocketEndPoint(path);
+
+                using (var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified))
+                {
+                    socket.Bind(remoteEP);
+                    socket.Connect(remoteEP);
+                    socket.Send(buffer);
+
+                    var content = new byte[sizeof(ulong)];
+                    int nReceivedBytes = socket.Receive(content);
+                    return (nReceivedBytes == sizeof(ulong)) ? BitConverter.ToUInt64(content, 0) : 0;
+                }
+            }
+        }
+
+        public static IEnumerable<int> ListAvailablePorts()
+        {
+            return Directory.GetFiles(IpcRootPath)
+                .Select(namedPipe => (new FileInfo(namedPipe)).Name)
+                .Where(input => Regex.IsMatch(input, DiagnosticPortPattern))
+                .Select(input => int.Parse(Regex.Match(input, DiagnosticPortPattern).Groups[1].Value, NumberStyles.Integer));
+        }
+
+        public static BinaryReader StreamTracingToFile(int processId, SessionConfiguration configuration, out ulong sessionId)
+        {
+            sessionId = 0;
+
+            var header = new MessageHeader {
+                RequestType = DiagnosticMessageType.Stream,
+                Pid = (uint)Process.GetCurrentProcess().Id,
+            };
+
+            byte[] serializedConfiguration;
+            using (var stream = new MemoryStream())
+                serializedConfiguration = Serialize(header, configuration, stream);
+
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                var pipeName = $"dotnetcore-diagnostic-{processId}";
+                var namedPipe = new NamedPipeClientStream(
+                    ".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation);
+                namedPipe.Connect((int)TimeSpan.FromSeconds(20).TotalMilliseconds);
+
+                var sw = new BinaryWriter(namedPipe);
+                sw.Write(serializedConfiguration);
+
+                var br = new BinaryReader(namedPipe);
+                sessionId = br.ReadUInt64();
+                return br;
+            }
+            else
+            {
+                throw new PlatformNotSupportedException("TODO: Get the ApplicationGroupId to form the string: 'dotnetcore-diagnostic-{processId}-{ApplicationGroupId}-socket'");
+            }
+        }
+
+        public static ulong EnableTracingToFile(int processId, SessionConfiguration configuration)
+        {
+            var header = new MessageHeader {
+                RequestType = DiagnosticMessageType.StartSession,
+                Pid = (uint)Process.GetCurrentProcess().Id,
+            };
+
+            byte[] serializedConfiguration;
+            using (var stream = new MemoryStream())
+                serializedConfiguration = Serialize(header, configuration, stream);
+
+            return SendCommand(processId, serializedConfiguration);
+        }
+
+        public static ulong DisableTracingToFile(int processId, ulong sessionId)
+        {
+            var header = new MessageHeader {
+                RequestType = DiagnosticMessageType.StopSession,
+                Pid = (uint)Process.GetCurrentProcess().Id,
+            };
+
+            byte[] sessionIdInBytes;
+            using (var stream = new MemoryStream())
+            {
+                using (var sw = new BinaryWriter(stream))
+                {
+                    sw.Write((uint)header.RequestType);
+                    sw.Write(header.Pid);
+
+                    sw.Write(sessionId);
+                    sw.Flush();
+                    sessionIdInBytes = stream.ToArray();
+                }
+            }
+
+            return SendCommand(processId, sessionIdInBytes);
+        }
+
+        private static byte[] Serialize(MessageHeader header, SessionConfiguration configuration, Stream stream)
+        {
+            using (var bw = new BinaryWriter(stream))
+            {
+                bw.Write((uint)header.RequestType);
+                bw.Write(header.Pid);
+
+                bw.Write(configuration.CircularBufferSizeInMB);
+                bw.Write(configuration.MultiFileTraceLengthInSeconds);
+
+                bw.WriteString(configuration.OutputPath);
+
+                bw.Write(configuration.Providers.Count());
+                foreach (var provider in configuration.Providers)
+                {
+                    bw.Write(provider.Keywords);
+                    bw.Write((uint)provider.EventLevel);
+
+                    bw.WriteString(provider.Name);
+                    bw.WriteString(provider.FilterData);
+                }
+
+                bw.Flush();
+                stream.Position = 0;
+
+                var bytes = new byte[stream.Length];
+                stream.Read(bytes, 0, bytes.Length);
+                return bytes;
+            }
+
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/MessageHeader.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/MessageHeader.cs
new file mode 100644 (file)
index 0000000..14ab6e6
--- /dev/null
@@ -0,0 +1,16 @@
+// 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.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics.Tools.RuntimeClient.Eventing
+{
+    [StructLayout(LayoutKind.Sequential)]
+    struct MessageHeader
+    {
+        public DiagnosticMessageType RequestType;
+
+        public uint Pid;
+    }
+}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/Provider.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/Provider.cs
new file mode 100644 (file)
index 0000000..8995a8c
--- /dev/null
@@ -0,0 +1,54 @@
+// 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.Tracing;
+
+namespace Microsoft.Diagnostics.Tools.RuntimeClient.Eventing
+{
+    public struct Provider
+    {
+        public Provider(
+            string name,
+            ulong keywords = ulong.MaxValue,
+            EventLevel eventLevel = EventLevel.Verbose,
+            string filterData = null)
+        {
+            if (string.IsNullOrWhiteSpace(name))
+                throw new ArgumentNullException(nameof(name));
+
+            Name = name;
+            Keywords = keywords;
+            EventLevel = eventLevel;
+            FilterData = string.IsNullOrWhiteSpace(filterData) ? null : filterData;
+        }
+
+        public static Provider ToProvider(string provider)
+        {
+            if (string.IsNullOrWhiteSpace(provider))
+                throw new ArgumentNullException(nameof(provider));
+
+            var tokens = provider.Split(new[] { ':' }, 4, StringSplitOptions.None); // Keep empty tokens;
+
+            string providerName = tokens.Length > 0 ? tokens[0] : null;
+            if (string.IsNullOrWhiteSpace(providerName))
+                throw new ArgumentException("Provider name was not specified.");
+
+            ulong keywords = tokens.Length > 1 ? Convert.ToUInt64(tokens[1], 16) : ulong.MaxValue;
+            EventLevel eventLevel = tokens.Length > 2 && uint.TryParse(tokens[2], out var level) ?
+                (EventLevel)level : EventLevel.Verbose;
+            string filterData = tokens.Length > 3 ? tokens[3] : null;
+
+            return new Provider(providerName, keywords, eventLevel, filterData);
+        }
+
+        public ulong Keywords { get; }
+
+        public EventLevel EventLevel { get; }
+
+        public string Name { get; }
+
+        public string FilterData { get; }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs
new file mode 100644 (file)
index 0000000..ad7c329
--- /dev/null
@@ -0,0 +1,38 @@
+// 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.Tools.RuntimeClient.Eventing
+{
+    public struct SessionConfiguration
+    {
+        public SessionConfiguration(uint circularBufferSizeMB, ulong multiFileSec, string outputPath, IEnumerable<Provider> providers)
+        {
+            if (providers == null)
+                throw new ArgumentNullException(nameof(providers));
+            if (providers.Count() <= 0)
+                throw new ArgumentException($"Specified providers collection is empty.");
+
+            CircularBufferSizeInMB = circularBufferSizeMB;
+            MultiFileTraceLengthInSeconds = multiFileSec;
+            _outputPath = new FileInfo(fileName: outputPath ?? $"eventpipe-{DateTime.Now:yyyyMMdd_HHmmss}.netperf");
+            _providers = new List<Provider>(providers);
+        }
+
+        public uint CircularBufferSizeInMB { get; }
+
+        public ulong MultiFileTraceLengthInSeconds { get; }
+
+        public string OutputPath => _outputPath.FullName;
+
+        public IEnumerable<Provider> Providers => _providers;
+
+        private readonly FileInfo _outputPath;
+        private readonly List<Provider> _providers;
+    }
+}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Extensions.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Extensions.cs
new file mode 100644 (file)
index 0000000..4f581ec
--- /dev/null
@@ -0,0 +1,55 @@
+using Microsoft.Diagnostics.Tools.RuntimeClient.Eventing;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Microsoft.Diagnostics.Tools.RuntimeClient
+{
+    internal static class Extensions
+    {
+        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'));
+        }
+
+
+#if DEBUG
+        private static int GetByteCount(this string @this)
+        {
+            if (@this == null)
+                throw new ArgumentNullException(nameof(@this));
+
+            var strLength = @this == null ? 0 : Encoding.Unicode.GetByteCount(@this + '\0');
+            return Marshal.SizeOf(typeof(int)) + strLength;
+        }
+
+        public static int GetByteCount(this SessionConfiguration @this)
+        {
+            int size = 0;
+
+            size += Marshal.SizeOf(@this.CircularBufferSizeInMB.GetType());
+            size += Marshal.SizeOf(@this.MultiFileTraceLengthInSeconds.GetType());
+
+            size += @this.OutputPath.GetByteCount();
+
+            size += Marshal.SizeOf(typeof(int));
+            foreach (var provider in @this.Providers)
+            {
+                size += Marshal.SizeOf(provider.Keywords.GetType());
+                size += Marshal.SizeOf(typeof(uint)); // provider.EventLevel.GetType()
+                size += provider.Name.GetByteCount();
+                size += provider.FilterData.GetByteCount();
+            }
+
+            return size;
+        }
+#endif // DEBUG
+    }
+}
diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Microsoft.Diagnostics.Tools.RuntimeClient.csproj b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Microsoft.Diagnostics.Tools.RuntimeClient.csproj
new file mode 100644 (file)
index 0000000..0aa4474
--- /dev/null
@@ -0,0 +1,32 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Library</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <RootNamespace>Microsoft.Diagnostics.Tools.RuntimeClient</RootNamespace>
+    <IsPackable>False</IsPackable>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <UseSharedCompilation>False</UseSharedCompilation>
+    <LangVersion>Latest</LangVersion>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>True</TreatWarningsAsErrors>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.38" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <!--
+      Suppress warnings for using preview SDK:
+      You are working with a preview version of the .NET Core SDK.
+      You can define the SDK version via a global.json file in the current project.
+      More at https://go.microsoft.com/fwlink/?linkid=869452
+    -->
+    <SuppressNETCoreSdkPreviewMessage>True</SuppressNETCoreSdkPreviewMessage>
+  </PropertyGroup>
+
+</Project>
diff --git a/src/Tools/dotnet-trace/CollectionConfiguration.cs b/src/Tools/dotnet-trace/CollectionConfiguration.cs
deleted file mode 100644 (file)
index e7af348..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.Tracing;
-using System.Linq;
-using System.Text;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public class CollectionConfiguration
-    {
-        public int? ProcessId { get; set; }
-        public string OutputPath { get; set; }
-        public int? CircularMB { get; set; }
-        public IList<EventSpec> Providers { get; set; } = new List<EventSpec>();
-        public IList<LoggerSpec> Loggers { get; set; } = new List<LoggerSpec>();
-
-        internal string ToConfigString()
-        {
-            var builder = new StringBuilder();
-            if (ProcessId != null)
-            {
-                builder.AppendLine($"ProcessId={ProcessId.Value}");
-            }
-            if (!string.IsNullOrEmpty(OutputPath))
-            {
-                builder.AppendLine($"OutputPath={OutputPath}");
-            }
-            if (CircularMB != null)
-            {
-                builder.AppendLine($"CircularMB={CircularMB}");
-            }
-            if (Providers != null && Providers.Count > 0)
-            {
-                builder.AppendLine($"Providers={SerializeProviders(Enumerable.Concat(Providers, GenerateLoggerSpec(Loggers)))}");
-            }
-            return builder.ToString();
-        }
-
-        public void AddProfile(CollectionProfile profile)
-        {
-            foreach (var provider in profile.EventSpecs)
-            {
-                Providers.Add(provider);
-            }
-
-            foreach (var logger in profile.LoggerSpecs)
-            {
-                Loggers.Add(logger);
-            }
-        }
-
-        private string SerializeProviders(IEnumerable<EventSpec> providers) => string.Join(",", providers.Select(s => s.ToConfigString()));
-
-        private IEnumerable<EventSpec> GenerateLoggerSpec(IList<LoggerSpec> loggers)
-        {
-            if (loggers.Count > 0)
-            {
-                var filterSpec = new StringBuilder();
-                foreach (var logger in loggers)
-                {
-                    if (string.IsNullOrEmpty(logger.Level))
-                    {
-                        filterSpec.Append($"{logger.Prefix}");
-                    }
-                    else
-                    {
-                        filterSpec.Append($"{logger.Prefix}:{logger.Level}");
-                    }
-                    filterSpec.Append(";");
-                }
-
-                // Remove trailing ';'
-                filterSpec.Length -= 1;
-
-                yield return new EventSpec(
-                    provider: "Microsoft-Extensions-Logging",
-                    keywords: 0x04 | 0x08, // FormattedMessage | JsonMessage (source: https://github.com/aspnet/Extensions/blob/aa7fa91cfc8f6ff078b020a428bcad71ae7a32ab/src/Logging/Logging.EventSource/src/LoggingEventSource.cs#L95)
-                    level: EventLevel.LogAlways,
-                    parameters: new Dictionary<string, string>() {
-                    { "FilterSpecs", filterSpec.ToString() }
-                    });
-            }
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/CollectionProfile.cs b/src/Tools/dotnet-trace/CollectionProfile.cs
deleted file mode 100644 (file)
index 0e3f03e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public class CollectionProfile
-    {
-        public static readonly string DefaultProfileName = "Default";
-
-        public string Name { get; }
-        public string Description { get; }
-        public IReadOnlyList<EventSpec> EventSpecs { get; }
-        public IReadOnlyList<LoggerSpec> LoggerSpecs { get; }
-
-        public CollectionProfile(string name, string description, IEnumerable<EventSpec> eventSpecs, IEnumerable<LoggerSpec> loggerSpecs)
-        {
-            Name = name;
-            Description = description;
-            EventSpecs = eventSpecs.ToList();
-            LoggerSpecs = loggerSpecs.ToList();
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/PortsCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/PortsCommandHandler.cs
new file mode 100644 (file)
index 0000000..b8d5bd0
--- /dev/null
@@ -0,0 +1,39 @@
+// 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.Eventing;
+using System;
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Threading.Tasks;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal static class PortsCommandHandler
+    {
+        public static async Task<int> GetActivePorts(IConsole console)
+        {
+            try
+            {
+                foreach (var pid in EventPipeClient.ListAvailablePorts())
+                    Console.Out.WriteLine($"{System.Diagnostics.Process.GetProcessById(pid).ProcessName}({pid})");
+
+                await Task.FromResult(0);
+                return 0;
+            }
+            catch (Exception ex)
+            {
+                Console.Error.WriteLine($"[ERROR]: {ex.ToString()}");
+                return 1;
+            }
+        }
+
+        public static Command ActivePortsCommand() =>
+            new Command(
+                name: "ports",
+                description: "List all active DotNet Core Diagnostic ports.",
+                handler: CommandHandler.Create<IConsole>(GetActivePorts),
+                isHidden: true);
+    }
+}
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/ProvidersCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/ProvidersCommandHandler.cs
new file mode 100644 (file)
index 0000000..c48d3c0
--- /dev/null
@@ -0,0 +1,52 @@
+// 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.Eventing;
+using System;
+using System.Collections.Generic;
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Diagnostics.Tracing;
+using System.Threading.Tasks;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal static class ProvidersCommandHandler
+    {
+        public static async Task<int> KnownProviders(IConsole console)
+        {
+            try
+            {
+                foreach (var provider in CommonProviders)
+                {
+                    var filterData = provider.FilterData == null ? "" : $":{provider.FilterData}";
+                    Console.Out.WriteLine($"Provider={provider.Name}:0x{provider.Keywords:X16}:{(uint)provider.EventLevel}{filterData}");
+                }
+
+                await Task.FromResult(0);
+                return 0;
+            }
+            catch (Exception ex)
+            {
+                Console.Error.WriteLine($"[ERROR]: {ex.ToString()}");
+                return 1;
+            }
+        }
+
+        public static Command KnownProvidersCommand() =>
+            new Command(
+                name: "knownproviders",
+                description: "List known tracing flags.",
+                handler: CommandHandler.Create<IConsole>(KnownProviders),
+                isHidden: true);
+
+        private static IEnumerable<Provider> CommonProviders { get; } = new[] {
+            new Provider("Microsoft-Windows-DotNETRuntime", 0x0000000000000001, EventLevel.Informational),  // ClrGC
+            new Provider("Microsoft-Windows-DotNETRuntime", 0x0000000000000002, EventLevel.Informational),  // ClrThreadPool
+            new Provider("Microsoft-Windows-DotNETRuntime", 0x00000000FFFFFFFF, EventLevel.Verbose),        // ClrAll
+            new Provider("Microsoft-Windows-DotNETRuntime", 0x00000004C14FCCBD, EventLevel.Informational),  //
+            new Provider("Microsoft-Windows-DotNETRuntimeRundown"),
+        };
+    }
+}
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/StartCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/StartCommandHandler.cs
new file mode 100644 (file)
index 0000000..22ae309
--- /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 Microsoft.Diagnostics.Tools.RuntimeClient.Eventing;
+using System;
+using System.Collections.Generic;
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal static class StartCommandHandler
+    {
+        public static async Task<int> Start(IConsole console, int pid, string output, uint buffersize, string providers, ulong multiFileSec)
+        {
+            try
+            {
+                var configuration = new SessionConfiguration(
+                    circularBufferSizeMB: buffersize,
+                    multiFileSec: multiFileSec,
+                    outputPath: output,
+                    ToProviders(providers));
+                var sessionId = EventPipeClient.EnableTracingToFile(pid, configuration);
+                Console.Out.WriteLine($"OutputPath={configuration.OutputPath}");
+                Console.Out.WriteLine($"SessionId=0x{sessionId:X16}");
+
+                await Task.FromResult(0);
+                return sessionId != 0 ? 0 : 1;
+            }
+            catch (Exception ex)
+            {
+                Console.Error.WriteLine($"[ERROR]: {ex.ToString()}");
+                return 1;
+            }
+        }
+
+        public static Command StartCommand() =>
+            new Command(
+                name: "start",
+                description: "Starts an EventPipe session.",
+                symbols: new Option[] {
+                    CommonOptions.ProcessIdOption(),
+                    OutputPathOption(),
+                    CircularBufferOption(),
+                    ProvidersOption(),
+                    MultiFileSecOption(),
+                },
+                handler: CommandHandler.Create<IConsole, int, string, uint, string, ulong>(Start));
+
+        private static Option OutputPathOption() =>
+            new Option(
+                new[] { "-o", "--output" },
+                @"The file name to log events to.",
+                new Argument<string> { Name = "filename" });
+
+        private static Option CircularBufferOption() =>
+            new Option(
+                new[] { "--buffersize" },
+                @"Sets the size of the in-memory circular buffer in megabytes.",
+                new Argument<uint> { Name = "Size" }); // TODO: 1024 ? Default ?
+
+        private static Option ProvidersOption() =>
+            new Option(
+                aliases: new[] { "--providers" },
+                description: @"A list EventPipe provider to be enabled in the form 'Provider[,Provider]', where Provider is in the form: '(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'",
+                argument: new Argument<string> { Name = "Providers" }); // TODO: Can we specify an actual type?
+
+        private static Option MultiFileSecOption() =>
+            new Option(
+                aliases: new[] { "--multifilesec" },
+                description: @"Enable a file switch timer every 'n' seconds (Default is 0)",
+                argument: new Argument<ulong> { Name = "MultiFileSec" });
+
+        private static IEnumerable<Provider> ToProviders(string providers)
+        {
+            if (string.IsNullOrWhiteSpace(providers))
+                throw new ArgumentNullException(nameof(providers));
+            return providers.Split(',')
+                .Select(Provider.ToProvider);
+        }
+    }
+}
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs
new file mode 100644 (file)
index 0000000..230e9fc
--- /dev/null
@@ -0,0 +1,47 @@
+// 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.Eventing;
+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 pid, ulong sessionId)
+        {
+            try
+            {
+                EventPipeClient.DisableTracingToFile(pid, 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));
+
+        private static Option SessionIdOption() =>
+            new Option(
+                new[] { "--session-id" },
+                @"Session Id being recorded.",
+                new Argument<ulong> { Name = "SessionId" });
+    }
+}
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/StreamCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/StreamCommand.cs
new file mode 100644 (file)
index 0000000..d673195
--- /dev/null
@@ -0,0 +1,84 @@
+using Microsoft.Diagnostics.Tools.RuntimeClient.Eventing;
+using System;
+using System.Collections.Generic;
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal static class StreamCommandHandler
+    {
+        public static async Task<int> Stream(IConsole console, int pid, string output, uint buffersize, string providers)
+        {
+            try
+            {
+                var configuration = new SessionConfiguration(
+                    circularBufferSizeMB: buffersize,
+                    multiFileSec: 0,
+                    outputPath: output,
+                    ToProviders(providers));
+                var binaryReader = EventPipeClient.StreamTracingToFile(pid, configuration, out var sessionId);
+                Console.Out.WriteLine($"SessionId=0x{sessionId:X16}");
+
+                if (sessionId != 0)
+                {
+                    var filePath = $"dotnetcore-eventpipe-{pid}-0x{sessionId:X16}.netperf";
+                    filePath = Path.Combine(@"S:\github.com\jorive\diagnostics\src\Tools\dotnet-trace", filePath);
+                    using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
+                    {
+                        while (true)
+                        {
+                            var buffer = new byte[1024];
+                            int nBytesRead = binaryReader.Read(buffer, 0, buffer.Length);
+                            if (nBytesRead <= 0)
+                                break;
+                            fs.Write(buffer, 0, nBytesRead);
+                        }
+                    }
+                }
+
+                await Task.FromResult(0);
+                return sessionId != 0 ? 0 : 1;
+            }
+            catch (Exception ex)
+            {
+                Console.Error.WriteLine($"[ERROR]: {ex.ToString()}");
+                return 1;
+            }
+        }
+
+        public static Command StartCommand() =>
+            new Command(
+                name: "stream",
+                description: "Starts an EventPipe session.",
+                symbols: new Option[] {
+                    CommonOptions.ProcessIdOption(),
+                    CircularBufferOption(),
+                    ProvidersOption(),
+                },
+                handler: CommandHandler.Create<IConsole, int, string, uint, string>(Stream));
+
+        private static Option CircularBufferOption() =>
+            new Option(
+                new[] { "--buffersize" },
+                @"Sets the size of the in-memory circular buffer in megabytes.",
+                new Argument<uint> { Name = "Size" }); // TODO: 1024 ? Default ?
+
+        private static Option ProvidersOption() =>
+            new Option(
+                aliases: new[] { "--providers" },
+                description: @"A list EventPipe provider to be enabled in the form 'Provider[,Provider]', where Provider is in the form: '(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'",
+                argument: new Argument<string> { Name = "Providers" }); // TODO: Can we specify an actual type?
+
+        private static IEnumerable<Provider> ToProviders(string providers)
+        {
+            if (string.IsNullOrWhiteSpace(providers))
+                throw new ArgumentNullException(nameof(providers));
+            return providers.Split(',')
+                .Select(Provider.ToProvider);
+        }
+    }
+}
diff --git a/src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs b/src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs
new file mode 100644 (file)
index 0000000..e0c5475
--- /dev/null
@@ -0,0 +1,17 @@
+// 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.CommandLine;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal static class CommonOptions
+    {
+        public static Option ProcessIdOption() =>
+            new Option(
+                new[] { "-pid" },
+                "The unique identifier of the associated process to connect to.",
+                new Argument<int> { Name = "ProcessId" });
+    }
+}
diff --git a/src/Tools/dotnet-trace/ConfigPathDetector.cs b/src/Tools/dotnet-trace/ConfigPathDetector.cs
deleted file mode 100644 (file)
index 050f546..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    internal static class ConfigPathDetector
-    {
-        private static readonly HashSet<string> _managedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".exe", ".dll" };
-
-        // Known .NET Platform Assemblies
-        private static readonly HashSet<string> _platformAssemblies = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
-        {
-            "System.Private.CoreLib.dll",
-            "clrjit.dll",
-        };
-
-        internal static string TryDetectConfigPath(int processId)
-        {
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-            {
-                return Windows.TryDetectConfigPath(processId);
-            }
-            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-            {
-                return Linux.TryDetectConfigPath(processId);
-            }
-            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
-            {
-                return OSX.TryDetectConfigPath(processId);
-            }
-            return null;
-        }
-
-        private static class OSX
-        {
-            // This is defined in proc_info.h (https://opensource.apple.com/source/xnu/xnu-1228/bsd/sys/proc_info.h)
-            private const int PROC_PIDPATHINFO_MAXSIZE = 1024 * 4;
-
-            /// <summary>
-            /// Gets the full path to the executable file identified by the specified PID
-            /// </summary>
-            /// <param name="pid">The PID of the running process</param>
-            /// <param name="buffer">A pointer to an allocated block of memory that will be filled with the process path</param>
-            /// <param name="bufferSize">The size of the buffer, should be PROC_PIDPATHINFO_MAXSIZE</param>
-            /// <returns>Returns the length of the path returned on success</returns>
-            [DllImport("libproc.dylib", SetLastError = true)]
-            private static extern unsafe int proc_pidpath(
-                int pid, 
-                byte* buffer, 
-                uint bufferSize);
-
-            /// <summary>
-            /// Gets the full path to the executable file identified by the specified PID
-            /// </summary>
-            /// <param name="pid">The PID of the running process</param>
-            /// <returns>Returns the full path to the process executable</returns>
-            internal static unsafe string proc_pidpath(int pid)
-            {
-                // The path is a fixed buffer size, so use that and trim it after
-                int result = 0;
-                byte* pBuffer = stackalloc byte[PROC_PIDPATHINFO_MAXSIZE];
-
-                // WARNING - Despite its name, don't try to pass in a smaller size than specified by PROC_PIDPATHINFO_MAXSIZE.
-                // For some reason libproc returns -1 if you specify something that's NOT EQUAL to PROC_PIDPATHINFO_MAXSIZE
-                // even if you declare your buffer to be smaller/larger than this size. 
-                result = proc_pidpath(pid, pBuffer, (uint)(PROC_PIDPATHINFO_MAXSIZE * sizeof(byte)));
-                if (result <= 0)
-                {
-                    throw new InvalidOperationException("Could not find procpath using libproc.");
-                }
-
-                // OS X uses UTF-8. The conversion may not strip off all trailing \0s so remove them here
-                return System.Text.Encoding.UTF8.GetString(pBuffer, result);
-            }
-
-            public static string TryDetectConfigPath(int processId)
-            {
-                try
-                {
-                    var path = proc_pidpath(processId);
-                    var candidateDir = Path.GetDirectoryName(path);
-                    var candidateName = Path.GetFileNameWithoutExtension(path);
-                    return Path.Combine(candidateDir, $"{candidateName}.eventpipeconfig");
-                }
-                catch (InvalidOperationException)
-                {
-                    return null;  // The pinvoke above may fail - return null in that case to handle error gracefully.
-                }
-            }
-        }
-
-        private static class Linux
-        {
-            public static string TryDetectConfigPath(int processId)
-            {
-                // Read procfs maps list
-                var lines = File.ReadAllLines($"/proc/{processId}/maps");
-
-                foreach (var line in lines)
-                {
-                    try
-                    {
-                        var parser = new StringParser(line, separator: ' ', skipEmpty: true);
-
-                        // Skip the address range
-                        parser.MoveNext();
-
-                        var permissions = parser.MoveAndExtractNext();
-
-                        // The managed entry point is Read-Only, Non-Execute and Shared.
-                        if (!string.Equals(permissions, "r--s", StringComparison.Ordinal))
-                        {
-                            continue;
-                        }
-
-                        // Skip offset, dev, and inode
-                        parser.MoveNext();
-                        parser.MoveNext();
-                        parser.MoveNext();
-
-                        // Parse the path
-                        if (!parser.MoveNext())
-                        {
-                            continue;
-                        }
-
-                        var path = parser.ExtractCurrentToEnd();
-                        var candidateDir = Path.GetDirectoryName(path);
-                        var candidateName = Path.GetFileNameWithoutExtension(path);
-                        if (File.Exists(Path.Combine(candidateDir, $"{candidateName}.deps.json")))
-                        {
-                            return Path.Combine(candidateDir, $"{candidateName}.eventpipeconfig");
-                        }
-                    }
-                    catch (ArgumentNullException) { return null; }
-                    catch (InvalidDataException) { return null; }
-                    catch (InvalidOperationException) { return null; }
-                }
-                return null;
-            }
-        }
-
-        private static class Windows
-        {
-            private static readonly HashSet<string> _knownNativeLibraries = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
-            {
-                // .NET Core Host
-                "dotnet.exe",
-                "hostfxr.dll",
-                "hostpolicy.dll",
-                "coreclr.dll",
-
-                // Windows Native Libraries
-                "ntdll.dll",
-                "kernel32.dll",
-                "kernelbase.dll",
-                "apphelp.dll",
-                "ucrtbase.dll",
-                "advapi32.dll",
-                "msvcrt.dll",
-                "sechost.dll",
-                "rpcrt4.dll",
-                "ole32.dll",
-                "combase.dll",
-                "bcryptPrimitives.dll",
-                "gdi32.dll",
-                "gdi32full.dll",
-                "msvcp_win.dll",
-                "user32.dll",
-                "win32u.dll",
-                "oleaut32.dll",
-                "shlwapi.dll",
-                "version.dll",
-                "bcrypt.dll",
-                "imm32.dll",
-                "kernel.appcore.dll",
-            };
-
-            public static string TryDetectConfigPath(int processId)
-            {
-                var process = Process.GetProcessById(processId);
-
-                // Iterate over modules
-                foreach (var module in process.Modules.Cast<ProcessModule>())
-                {
-                    // Filter out things that aren't exes and dlls (useful on Unix/macOS to skip native libraries)
-                    var extension = Path.GetExtension(module.FileName);
-                    var name = Path.GetFileName(module.FileName);
-                    if (_managedExtensions.Contains(extension) && !_knownNativeLibraries.Contains(name) && !_platformAssemblies.Contains(name))
-                    {
-                        var candidateDir = Path.GetDirectoryName(module.FileName);
-                        var appName = Path.GetFileNameWithoutExtension(module.FileName);
-
-                        // Check for the deps.json file
-                        // TODO: Self-contained apps?
-                        if (File.Exists(Path.Combine(candidateDir, $"{appName}.deps.json")))
-                        {
-                            // This is an app!
-                            return Path.Combine(candidateDir, $"{appName}.eventpipeconfig");
-                        }
-                    }
-                }
-
-                return null;
-            }
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/EtwCollector.cs b/src/Tools/dotnet-trace/EtwCollector.cs
deleted file mode 100644 (file)
index d896b58..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.Tracing;
-using System.IO;
-using System.Threading.Tasks;
-using Microsoft.Diagnostics.Tracing;
-using Microsoft.Diagnostics.Tracing.Session;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public class EtwCollector : EventCollector
-    {
-        private readonly CollectionConfiguration _config;
-        private TraceEventSession _session;
-
-        public EtwCollector(CollectionConfiguration config)
-        {
-            _config = config;
-        }
-
-        public override Task StartCollectingAsync()
-        {
-            // TODO: Allow a file name to be provided
-            var outputFile = _config.ProcessId == null ?
-                Path.Combine(_config.OutputPath, "dotnet-trace.etl") :
-                Path.Combine(_config.OutputPath, $"dotnet-trace.{_config.ProcessId.Value}.etl");
-            if (File.Exists(outputFile))
-            {
-                throw new InvalidOperationException($"Target file already exists: {outputFile}");
-            }
-            _session = new TraceEventSession("dotnet-trace", outputFile);
-
-            if (_config.CircularMB is int circularMb)
-            {
-                _session.CircularBufferMB = circularMb;
-            }
-
-            var options = new TraceEventProviderOptions();
-            if (_config.ProcessId is int pid)
-            {
-                options.ProcessIDFilter = new List<int>() { pid };
-            }
-
-            // Enable the providers requested
-            foreach (var provider in _config.Providers)
-            {
-                _session.EnableProvider(provider.Provider, ConvertLevel(provider.Level), provider.Keywords, options);
-            }
-
-            return Task.CompletedTask;
-        }
-
-        private TraceEventLevel ConvertLevel(EventLevel level)
-        {
-            switch (level)
-            {
-                case EventLevel.Critical: return TraceEventLevel.Critical;
-                case EventLevel.Error: return TraceEventLevel.Error;
-                case EventLevel.Informational: return TraceEventLevel.Informational;
-                case EventLevel.LogAlways: return TraceEventLevel.Always;
-                case EventLevel.Verbose: return TraceEventLevel.Verbose;
-                case EventLevel.Warning: return TraceEventLevel.Warning;
-                default:
-                    throw new InvalidOperationException($"Unknown EventLevel: {level}");
-            }
-        }
-
-        public override Task StopCollectingAsync()
-        {
-            _session.Dispose();
-            return Task.CompletedTask;
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/EventCollector.cs b/src/Tools/dotnet-trace/EventCollector.cs
deleted file mode 100644 (file)
index 6403306..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public abstract class EventCollector
-    {
-        public abstract Task StartCollectingAsync();
-        public abstract Task StopCollectingAsync();
-    }
-}
diff --git a/src/Tools/dotnet-trace/EventPipeCollector.cs b/src/Tools/dotnet-trace/EventPipeCollector.cs
deleted file mode 100644 (file)
index 47c05ea..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.IO;
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public class EventPipeCollector : EventCollector
-    {
-        private readonly CollectionConfiguration _config;
-        private readonly string _configPath;
-
-        public EventPipeCollector(CollectionConfiguration config, string configPath)
-        {
-            _config = config;
-            _configPath = configPath;
-        }
-
-        public override Task StartCollectingAsync()
-        {
-            var configContent = _config.ToConfigString();
-            return File.WriteAllTextAsync(_configPath, configContent);
-        }
-
-        public override Task StopCollectingAsync()
-        {
-            File.Delete(_configPath);
-            return Task.CompletedTask;
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/EventSpec.cs b/src/Tools/dotnet-trace/EventSpec.cs
deleted file mode 100644 (file)
index e384e70..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.Tracing;
-using System.Globalization;
-using System.Text;
-using Microsoft.Internal.Utilities;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public class EventSpec
-    {
-        public string Provider { get; }
-        public ulong Keywords { get; }
-        public EventLevel Level { get; }
-        public IDictionary<string, string> Parameters { get; }
-
-        public EventSpec(string provider, ulong keywords, EventLevel level)
-            : this(provider, keywords, level, new Dictionary<string, string>())
-        {
-        }
-
-        public EventSpec(string provider, ulong keywords, EventLevel level, IDictionary<string, string> parameters)
-        {
-            Provider = provider;
-            Keywords = keywords;
-            Level = level;
-            Parameters = parameters;
-        }
-
-        public static bool TryParse(string input, out EventSpec spec)
-        {
-            spec = null;
-            var splat = input.Split(':');
-
-            if (splat.Length == 0)
-            {
-                return false;
-            }
-
-            var provider = splat[0];
-            var keywords = ulong.MaxValue;
-            var level = EventLevel.Verbose;
-            var parameters = new Dictionary<string, string>();
-
-            if (splat.Length > 1)
-            {
-                if (!TryParseKeywords(splat[1], provider, out keywords))
-                {
-                    return false;
-                }
-            }
-
-            if (splat.Length > 2)
-            {
-                if (!TryParseLevel(splat[2], out level))
-                {
-                    return false;
-                }
-            }
-
-            if (splat.Length > 3)
-            {
-                if (!TryParseParameters(splat[3], parameters))
-                {
-                    return false;
-                }
-            }
-
-            spec = new EventSpec(provider, keywords, level, parameters);
-            return true;
-        }
-
-        public string ToConfigString()
-        {
-            var config = $"{Provider}:0x{Keywords:X}:{(int)Level}";
-            if(Parameters.Count > 0)
-            {
-                config += $":{FormatParameters(Parameters)}";
-            }
-            return config;
-        }
-
-        private static string FormatParameters(IDictionary<string, string> parameters)
-        {
-            var builder = new StringBuilder();
-            foreach(var (key, value) in parameters)
-            {
-                builder.Append($"{key}={value};");
-            }
-
-            // Remove the trailing ';'
-            builder.Length -= 1;
-
-            return builder.ToString();
-        }
-
-        private static bool TryParseParameters(string input, IDictionary<string, string> parameters)
-        {
-            var splat = input.Split(';');
-            foreach(var item in splat)
-            {
-                var splot = item.Split('=');
-                if(splot.Length != 2)
-                {
-                    return false;
-                }
-
-                parameters[splot[0]] = splot[1];
-            }
-
-            return true;
-        }
-
-        private static bool TryParseLevel(string input, out EventLevel level)
-        {
-            level = EventLevel.Verbose;
-            if (int.TryParse(input, out var intLevel))
-            {
-                if (intLevel >= (int)EventLevel.LogAlways && intLevel <= (int)EventLevel.Verbose)
-                {
-                    level = (EventLevel)intLevel;
-                    return true;
-                }
-            }
-            else if (Enum.TryParse(input, ignoreCase: true, out level))
-            {
-                return true;
-            }
-            return false;
-        }
-
-        private static bool TryParseKeywords(string input, string provider, out ulong keywords)
-        {
-            if (string.Equals("*", input, StringComparison.Ordinal))
-            {
-                keywords = ulong.MaxValue;
-                return true;
-            }
-            else if (input.StartsWith("0x"))
-            {
-                // Keywords
-                if (ulong.TryParse(input, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out keywords))
-                {
-                    return true;
-                }
-            }
-            else if(KnownData.TryGetProvider(provider, out var knownProvider))
-            {
-                var splat = input.Split(',');
-                keywords = 0;
-                foreach(var item in splat)
-                {
-                    if(knownProvider.Keywords.TryGetValue(item, out var knownKeyword))
-                    {
-                        keywords |= knownKeyword.Value;
-                    }
-                    else
-                    {
-                        throw new CommandLineException($"Keyword '{item}' is not a well-known keyword for '{provider}'");
-                    }
-                }
-                return true;
-            }
-
-            keywords = ulong.MaxValue;
-            return false;
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/KnownData.cs b/src/Tools/dotnet-trace/KnownData.cs
deleted file mode 100644 (file)
index 1ac0a40..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.Tracing;
-using System.Linq;
-using Microsoft.Diagnostics.Tracing.Parsers;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    internal static class KnownData
-    {
-        private static readonly IReadOnlyDictionary<string, KnownProvider> _knownProviders =
-            CreateKnownProviders().ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
-
-        private static readonly IReadOnlyDictionary<string, CollectionProfile> _knownProfiles =
-            CreateProfiles().ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
-
-        private static IEnumerable<KnownProvider> CreateKnownProviders()
-        {
-            yield return CreateClrProvider();
-        }
-
-        private static IEnumerable<CollectionProfile> CreateProfiles()
-        {
-            yield return new CollectionProfile(
-                CollectionProfile.DefaultProfileName,
-                "A default set of event providers useful for diagosing problems in any .NET application.",
-                new[] {
-                    new EventSpec(ClrTraceEventParser.ProviderName, (ulong)ClrTraceEventParser.Keywords.Default, EventLevel.Informational)
-                },
-                Array.Empty<LoggerSpec>());
-
-            yield return new CollectionProfile(
-                "AspNetCore",
-                "A set of event providers useful for diagnosing problems in ASP.NET Core applications.",
-                new[]
-                {
-                    new EventSpec("Microsoft-AspNetCore-Hosting", ulong.MaxValue, EventLevel.Informational),
-                },
-                Array.Empty<LoggerSpec>());
-
-            yield return new CollectionProfile(
-                "Kestrel",
-                "Detailed events for ASP.NET Core Kestrel",
-                new[]
-                {
-                    new EventSpec("Microsoft-AspNetCore-Server-Kestrel", ulong.MaxValue, EventLevel.Verbose),
-                },
-                Array.Empty<LoggerSpec>());
-        }
-
-        private static KnownProvider CreateClrProvider()
-        {
-            return new KnownProvider(
-                ClrTraceEventParser.ProviderName,
-                ClrTraceEventParser.ProviderGuid,
-                ScanKeywordType(typeof(ClrTraceEventParser.Keywords)));
-        }
-
-        public static IReadOnlyList<CollectionProfile> GetAllProfiles() => _knownProfiles.Values.ToList();
-        public static IReadOnlyList<KnownProvider> GetAllProviders() => _knownProviders.Values.ToList();
-
-        public static bool TryGetProvider(string providerName, out KnownProvider provider) => _knownProviders.TryGetValue(providerName, out provider);
-        public static bool TryGetProfile(string profileName, out CollectionProfile profile) => _knownProfiles.TryGetValue(profileName, out profile);
-
-        private static IEnumerable<KnownKeyword> ScanKeywordType(Type keywordType)
-        {
-            var values = Enum.GetValues(keywordType).Cast<long>().ToList();
-            var keywords = values.Distinct().Select(v => new KnownKeyword(Enum.GetName(keywordType, v), (ulong)v)).ToList();
-            return keywords;
-        }
-    }
-
-    internal class KnownProvider
-    {
-        public string Name { get; }
-        public Guid Guid { get; }
-        public IReadOnlyDictionary<string, KnownKeyword> Keywords { get; }
-
-        public KnownProvider(string name, Guid guid, IEnumerable<KnownKeyword> keywords)
-        {
-            Name = name;
-            Guid = guid;
-            Keywords = keywords.ToDictionary(k => k.Name, StringComparer.OrdinalIgnoreCase);
-        }
-    }
-
-    internal class KnownKeyword
-    {
-        public string Name { get; }
-        public ulong Value { get; }
-
-        public KnownKeyword(string name, ulong value)
-        {
-            Name = name;
-            Value = value;
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/LoggerSpec.cs b/src/Tools/dotnet-trace/LoggerSpec.cs
deleted file mode 100644 (file)
index 4e1d1e8..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Microsoft.Diagnostics.Tools.Collect
-{
-    public class LoggerSpec
-    {
-        // Handles case normalization because key lookup is case-insensitive.
-        private static readonly Dictionary<string, string> _levelMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-        {
-            { "Trace", "Trace" },
-            { "Debug", "Debug" },
-            { "Information", "Information" },
-            { "Warning", "Warning" },
-            { "Error", "Error" },
-            { "Critical", "Critical" },
-        };
-
-        public string Prefix { get; }
-        public string Level { get; }
-
-        public LoggerSpec(string prefix, string level)
-        {
-            Prefix = prefix;
-            Level = level;
-        }
-
-        public static bool TryParse(string input, out LoggerSpec spec)
-        {
-            var splat = input.Split(':');
-
-            var prefix = splat[0];
-            string level = null;
-            if (splat.Length > 1)
-            {
-                if (!_levelMap.TryGetValue(splat[1], out level))
-                {
-                    spec = null;
-                    return false;
-                }
-            }
-
-            spec = new LoggerSpec(prefix, level);
-            return true;
-        }
-    }
-}
index 076e05722b7b276b464a1361a9d22c5b19fdcf79..9bd16f1a3b970d7b0a8297f0863f04b1016bbf51 100644 (file)
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
+// 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.CommandLine.Builder;
+using System.CommandLine.Invocation;
 using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Internal.Utilities;
 
-namespace Microsoft.Diagnostics.Tools.Collect
+namespace Microsoft.Diagnostics.Tools.Trace
 {
-    [Command(Name = "dotnet-trace", Description = "Collects Event Traces from .NET processes")]
-    internal class Program
+    class Program
     {
-
-        [Option("-c|--config-path <CONFIG_PATH>", Description = "The path of the EventPipe config file to write, must be named [AppName].eventpipeconfig and be in the base directory for a managed app.")]
-        public string ConfigPath { get; set; }
-
-        [Option("--etw", Description = "Specify this flag to use ETW to collect events rather than using EventPipe (Windows only).")]
-        public bool Etw { get; set; }
-
-        [Required]
-        [Option("-p|--process-id <PROCESS_ID>", Description = "Filter to only the process with the specified process ID.")]
-        public int ProcessId { get; set; }
-
-        [Option("-o|--output <OUTPUT_DIRECTORY>", Description = "The directory to write the trace to. Defaults to the current working directory.")]
-        public string OutputDir { get; set; }
-
-        [Option("--buffer <BUFFER_SIZE_IN_MB>", Description = "The size of the in-memory circular buffer in megabytes.")]
-        public int? CircularMB { get; set; }
-
-        [Option("--provider <PROVIDER_SPEC>", Description = "An EventPipe provider to enable. A string in the form '<provider name>:<keywords>:<level>:<parameters>'. Can be specified multiple times to enable multiple providers.")]
-        public IList<string> Providers { get; set; }
-
-        [Option("--profile <PROFILE_NAME>", Description = "A collection profile to enable. Use '--list-profiles' to get a list of all available profiles. Can be mixed with '--provider' and specified multiple times.")]
-        public IList<string> Profiles { get; set; }
-
-        [Option("--logger <LOGGER_NAME>", Description = "A Microsoft.Extensions.Logging logger to enable. A string in the form '<logger prefix>:<level>'. Can be specified multiple times to enable multiple loggers.")]
-        public IList<string> Loggers { get; set; }
-
-        [Option("--keywords-for <PROVIDER_NAME>", Description = "Gets a list of known keywords (if any) for the specified provider.")]
-        public string KeywordsForProvider { get; set; }
-
-        [Option("--list-profiles", Description = "Gets a list of predefined collection profiles.")]
-        public bool ListProfiles { get; set; }
-
-        [Option("--no-default", Description = "Don't enable the default profile.")]
-        public bool NoDefault { get; set; }
-
-        public async Task<int> OnExecuteAsync(IConsole console, CommandLineApplication app)
-        {
-            if (ListProfiles)
-            {
-                WriteProfileList(console.Out);
-                return 0;
-            }
-
-            if (!string.IsNullOrEmpty(KeywordsForProvider))
-            {
-                return ExecuteKeywordsForAsync(console);
-            }
-
-            if(string.IsNullOrEmpty(ConfigPath))
-            {
-                ConfigPath = ConfigPathDetector.TryDetectConfigPath(ProcessId);
-                if(string.IsNullOrEmpty(ConfigPath))
-                {
-                    console.Error.WriteLine("Couldn't determine the path for the eventpipeconfig file from the process ID. Specify the '--config-path' option to provide it manually.");
-                    return 1;
-                }
-                console.WriteLine($"Detected config file path: {ConfigPath}");
-            }
-
-            var config = new CollectionConfiguration()
-            {
-                ProcessId = ProcessId,
-                CircularMB = CircularMB,
-                OutputPath = string.IsNullOrEmpty(OutputDir) ? Directory.GetCurrentDirectory() : OutputDir
-            };
-
-            if (Profiles != null && Profiles.Count > 0)
-            {
-                foreach (var profile in Profiles)
-                {
-                    if (!KnownData.TryGetProfile(profile, out var collectionProfile))
-                    {
-                        console.Error.WriteLine($"Unknown profile name: '{profile}'. See 'dotnet-trace --list-profiles' to get a list of profiles.");
-                        return 1;
-                    }
-                    config.AddProfile(collectionProfile);
-                }
-            }
-
-            if (Providers != null && Providers.Count > 0)
-            {
-                foreach (var provider in Providers)
-                {
-                    if (!EventSpec.TryParse(provider, out var providerSpec))
-                    {
-                        console.Error.WriteLine($"Invalid provider specification: '{provider}'. See 'dotnet-trace --help' for more information.");
-                        return 1;
-                    }
-                    config.Providers.Add(providerSpec);
-                }
-            }
-
-            if (Loggers != null && Loggers.Count > 0)
-            {
-                foreach (var logger in Loggers)
-                {
-                    if (!LoggerSpec.TryParse(logger, out var loggerSpec))
-                    {
-                        console.Error.WriteLine($"Invalid logger specification: '{logger}'. See 'dotnet-trace --help' for more information.");
-                        return 1;
-                    }
-                    config.Loggers.Add(loggerSpec);
-                }
-            }
-
-            if (!NoDefault)
-            {
-                // Enable the default profile if nothing is specified
-                if (!KnownData.TryGetProfile(CollectionProfile.DefaultProfileName, out var defaultProfile))
-                {
-                    console.Error.WriteLine("No providers or profiles were specified and there is no default profile available.");
-                    return 1;
-                }
-                config.AddProfile(defaultProfile);
-            }
-
-            if (!TryCreateCollector(console, config, out var collector))
-            {
-                return 1;
-            }
-
-            // Write the config file contents
-            await collector.StartCollectingAsync();
-            console.WriteLine("Tracing has started. Press Ctrl-C to stop.");
-
-            await console.WaitForCtrlCAsync();
-
-            await collector.StopCollectingAsync();
-            console.WriteLine($"Tracing stopped. Trace files written to {config.OutputPath}");
-
-            return 0;
-        }
-
-        private static void WriteProfileList(TextWriter console)
-        {
-            var profiles = KnownData.GetAllProfiles();
-            var maxNameLength = profiles.Max(p => p.Name.Length);
-            console.WriteLine("Available profiles:");
-            foreach (var profile in profiles)
-            {
-                console.WriteLine($"* {profile.Name.PadRight(maxNameLength)}  {profile.Description}");
-            }
-        }
-
-        private int ExecuteKeywordsForAsync(IConsole console)
-        {
-            if (KnownData.TryGetProvider(KeywordsForProvider, out var provider))
-            {
-                console.WriteLine($"Known keywords for {provider.Name} ({provider.Guid}):");
-                foreach (var keyword in provider.Keywords.Values)
-                {
-                    console.WriteLine($"* 0x{keyword.Value:x16} {keyword.Name}");
-                }
-                return 0;
-            }
-            else
-            {
-                console.WriteLine($"There are no known keywords for {KeywordsForProvider}.");
-                return 1;
-            }
-        }
-
-        private bool TryCreateCollector(IConsole console, CollectionConfiguration config, out EventCollector collector)
-        {
-            collector = null;
-
-            if (Etw)
-            {
-                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                {
-                    console.Error.WriteLine("Error: ETW-based collection is only supported on Windows.");
-                    return false;
-                }
-
-                if (!string.IsNullOrEmpty(ConfigPath))
-                {
-                    console.Error.WriteLine("WARNING: The '-c' option is ignored when using ETW-based collection.");
-                }
-                collector = new EtwCollector(config);
-                return true;
-            }
-            else
-            {
-                if (File.Exists(ConfigPath))
-                {
-                    console.Error.WriteLine("Config file already exists, tracing is already underway by a different consumer.");
-                    return false;
-                }
-
-                collector = new EventPipeCollector(config, ConfigPath);
-                return true;
-            }
-        }
-
-        private static int Main(string[] args)
-        {
-            DebugUtil.WaitForDebuggerIfRequested(ref args);
-
-            try
-            {
-                var app = new CommandLineApplication<Program>();
-                app.Conventions.UseDefaultConventions();
-                app.ExtendedHelpText = GetExtendedHelp();
-                return app.Execute(args);
-            }
-            catch (PlatformNotSupportedException ex)
-            {
-                Console.Error.WriteLine(ex.Message);
-                return 1;
-            }
-            catch (CommandLineException clex)
-            {
-                Console.Error.WriteLine(clex.Message);
-                return 1;
-            }
-            catch (OperationCanceledException)
-            {
-                return 0;
-            }
-        }
-
-        private static string GetExtendedHelp()
+        public static Task<int> Main(string[] args)
         {
-            using (var writer = new StringWriter())
-            {
-                writer.WriteLine();
-                writer.WriteLine("Profiles");
-                writer.WriteLine("  Profiles are sets of well-defined provider configurations that provide useful information.");
-                writer.WriteLine();
-                WriteProfileList(writer);
-                writer.WriteLine();
-                writer.WriteLine("Specifying Loggers:");
-                writer.WriteLine("  Use one of the following formats to specify a logger in '--logger'");
-                writer.WriteLine("    *                                                 - Enable all messages at all levels from all loggers.");
-                writer.WriteLine("    *:<level>                                         - Enable messages at the specified '<level>' or higher from all loggers.");
-                writer.WriteLine("    <loggerPrefix>                                    - Enable all messages at all levels from all loggers starting with '<loggerPrefix>'.");
-                writer.WriteLine("    <loggerPrefix>:<level>                            - Enable messages at the specified '<level>' or higher from all loggers starting with '<loggerPrefix>'.");
-                writer.WriteLine();
-                writer.WriteLine("  '<loggerPrefix>' is the prefix for a logger to enable. For example 'Microsoft.AspNetCore' to enable all ASP.NET Core loggers.");
-                writer.WriteLine("  '<level>' can be one of: Critical, Error, Warning, Informational, Debug, or Trace.");
-                writer.WriteLine();
-                writer.WriteLine("Specifying Providers:");
-                writer.WriteLine("  Use one of the following formats to specify a provider in '--provider'");
-                writer.WriteLine("    <providerName>                                    - Enable all events at all levels for the provider.");
-                writer.WriteLine("    <providerName>:<keywords>                         - Enable events matching the specified keywords for the specified provider.");
-                writer.WriteLine("    <providerName>:<keywords>:<level>                 - Enable events matching the specified keywords, at the specified level for the specified provider.");
-                writer.WriteLine("    <providerName>:<keywords>:<level>:<parameters>    - Enable events matching the specified keywords, at the specified level for the specified provider and provide key-value parameters.");
-                writer.WriteLine();
-                writer.WriteLine("  '<provider>' must be the name of the EventSource.");
-                writer.WriteLine("  '<level>' can be one of: Critical (1), Error (2), Warning (3), Informational (4), Verbose (5). Either the name or number can be specified.");
-                writer.WriteLine("  '<keywords>' is one of the following:");
-                writer.WriteLine("    A '*' character, indicating ALL keywords should be enabled (this can be very costly for some providers!)");
-                writer.WriteLine("    A comma-separated list of known keywords for a provider (use 'dotnet trace collect --keywords-for [providerName]' to get a list of known keywords for a provider)");
-                writer.WriteLine("    A 64-bit hexadecimal number, starting with '0x' indicating the keywords to enable");
-                writer.WriteLine("  '<parameters>' is an optional list of key-value parameters to provide to the EventPipe provider. The expected values depend on the provider you are enabling.");
-                writer.WriteLine("    This should be a list of key-value pairs, in the form: '<key1>=<value1>;<key2>=<value2>;...'. Note that some shells, such as PowerShell, require that you");
-                writer.WriteLine("    quote or escape the ';' character.");
-                return writer.GetStringBuilder().ToString();
-            }
+            var parser = new CommandLineBuilder()
+#if DEBUG
+                .AddCommand(PortsCommandHandler.ActivePortsCommand())
+                .AddCommand(ProvidersCommandHandler.KnownProvidersCommand())
+#endif
+                .AddCommand(StreamCommandHandler.StartCommand())
+                .AddCommand(StartCommandHandler.StartCommand())
+                .AddCommand(StopCommandHandler.StopCommand())
+                .UseDefaults()
+                .Build();
+
+            return parser.InvokeAsync(args);
         }
     }
 }
diff --git a/src/Tools/dotnet-trace/StringParser.cs b/src/Tools/dotnet-trace/StringParser.cs
deleted file mode 100644 (file)
index 5c3c643..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-// Borrowed from https://github.com/dotnet/corefx/blob/b2f960abe1d8690be9d68dd9b56ea7636fb4a38b/src/Common/src/System/IO/StringParser.cs
-
-// 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.Diagnostics;
-
-namespace System.IO
-{
-    /// <summary>
-    /// Provides a string parser that may be used instead of String.Split 
-    /// to avoid unnecessary string and array allocations.
-    /// </summary>
-    internal struct StringParser
-    {
-        /// <summary>The string being parsed.</summary>
-        private readonly string _buffer;
-
-        /// <summary>The separator character used to separate subcomponents of the larger string.</summary>
-        private readonly char _separator;
-
-        /// <summary>true if empty subcomponents should be skipped; false to treat them as valid entries.</summary>
-        private readonly bool _skipEmpty;
-
-        /// <summary>The starting index from which to parse the current entry.</summary>
-        private int _startIndex;
-
-        /// <summary>The ending index that represents the next index after the last character that's part of the current entry.</summary>
-        private int _endIndex;
-
-        /// <summary>Initialize the StringParser.</summary>
-        /// <param name="buffer">The string to parse.</param>
-        /// <param name="separator">The separator character used to separate subcomponents of <paramref name="buffer"/>.</param>
-        /// <param name="skipEmpty">true if empty subcomponents should be skipped; false to treat them as valid entries.  Defaults to false.</param>
-        public StringParser(string buffer, char separator, bool skipEmpty = false)
-        {
-            if (buffer == null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
-            _buffer = buffer;
-            _separator = separator;
-            _skipEmpty = skipEmpty;
-            _startIndex = -1;
-            _endIndex = -1;
-        }
-
-        /// <summary>Moves to the next component of the string.</summary>
-        /// <returns>true if there is a next component to be parsed; otherwise, false.</returns>
-        public bool MoveNext()
-        {
-            if (_buffer == null)
-            {
-                throw new InvalidOperationException();
-            }
-
-            while (true)
-            {
-                if (_endIndex >= _buffer.Length)
-                {
-                    _startIndex = _endIndex;
-                    return false;
-                }
-
-                int nextSeparator = _buffer.IndexOf(_separator, _endIndex + 1);
-                _startIndex = _endIndex + 1;
-                _endIndex = nextSeparator >= 0 ? nextSeparator : _buffer.Length;
-
-                if (!_skipEmpty || _endIndex >= _startIndex + 1)
-                {
-                    return true;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Moves to the next component of the string.  If there isn't one, it throws an exception.
-        /// </summary>
-        public void MoveNextOrFail()
-        {
-            if (!MoveNext())
-            {
-                ThrowForInvalidData();
-            }
-        }
-
-        /// <summary>
-        /// Moves to the next component of the string and returns it as a string.
-        /// </summary>
-        /// <returns></returns>
-        public string MoveAndExtractNext()
-        {
-            MoveNextOrFail();
-            return _buffer.Substring(_startIndex, _endIndex - _startIndex);
-        }
-
-        /// <summary>
-        /// Moves to the next component of the string, which must be enclosed in the only set of top-level parentheses
-        /// in the string.  The extracted value will be everything between (not including) those parentheses.
-        /// </summary>
-        /// <returns></returns>
-        public string MoveAndExtractNextInOuterParens()
-        {
-            // Move to the next position
-            MoveNextOrFail();
-
-            // After doing so, we should be sitting at a the opening paren.
-            if (_buffer[_startIndex] != '(')
-            {
-                ThrowForInvalidData();
-            }
-
-            // Since we only allow for one top-level set of parentheses, find the last
-            // parenthesis in the string; it's paired with the opening one we just found.
-            int lastParen = _buffer.LastIndexOf(')');
-            if (lastParen == -1 || lastParen < _startIndex)
-            {
-                ThrowForInvalidData();
-            }
-
-            // Extract the contents of the parens, then move our ending position to be after the paren
-            string result = _buffer.Substring(_startIndex + 1, lastParen - _startIndex - 1);
-            _endIndex = lastParen + 1;
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the current subcomponent of the string as a string.
-        /// </summary>
-        public string ExtractCurrent()
-        {
-            if (_buffer == null || _startIndex == -1)
-            {
-                throw new InvalidOperationException();
-            }
-            return _buffer.Substring(_startIndex, _endIndex - _startIndex);
-        }
-
-        /// <summary>Moves to the next component and parses it as an Int32.</summary>
-        public unsafe int ParseNextInt32()
-        {
-            MoveNextOrFail();
-
-            bool negative = false;
-            int result = 0;
-
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-
-                if (p == end)
-                {
-                    ThrowForInvalidData();
-                }
-
-                if (*p == '-')
-                {
-                    negative = true;
-                    p++;
-                    if (p == end)
-                    {
-                        ThrowForInvalidData();
-                    }
-                }
-
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = negative ? checked((result * 10) - d) : checked((result * 10) + d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == int.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as an Int64.</summary>
-        public unsafe long ParseNextInt64()
-        {
-            MoveNextOrFail();
-
-            bool negative = false;
-            long result = 0;
-
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-
-                if (p == end)
-                {
-                    ThrowForInvalidData();
-                }
-
-                if (*p == '-')
-                {
-                    negative = true;
-                    p++;
-                    if (p == end)
-                    {
-                        ThrowForInvalidData();
-                    }
-                }
-
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = negative ? checked((result * 10) - d) : checked((result * 10) + d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == long.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as a UInt32.</summary>
-        public unsafe uint ParseNextUInt32()
-        {
-            MoveNextOrFail();
-            if (_startIndex == _endIndex)
-            {
-                ThrowForInvalidData();
-            }
-
-            uint result = 0;
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = (uint)checked((result * 10) + d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == uint.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as a UInt64.</summary>
-        public unsafe ulong ParseNextUInt64()
-        {
-            MoveNextOrFail();
-
-            ulong result = 0;
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = checked((result * 10ul) + (ulong)d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == ulong.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as a Char.</summary>
-        public char ParseNextChar()
-        {
-            MoveNextOrFail();
-
-            if (_endIndex - _startIndex != 1)
-            {
-                ThrowForInvalidData();
-            }
-            char result = _buffer[_startIndex];
-
-            Debug.Assert(result == char.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        internal delegate T ParseRawFunc<T>(string buffer, ref int startIndex, ref int endIndex);
-
-        /// <summary>
-        /// Moves to the next component and hands the raw buffer and indexing data to a selector function
-        /// that can validate and return the appropriate data from the component.
-        /// </summary>
-        internal T ParseRaw<T>(ParseRawFunc<T> selector)
-        {
-            MoveNextOrFail();
-            return selector(_buffer, ref _startIndex, ref _endIndex);
-        }
-
-        /// <summary>
-        /// Gets the current subcomponent and all remaining components of the string as a string.
-        /// </summary>
-        public string ExtractCurrentToEnd()
-        {
-            if (_buffer == null || _startIndex == -1)
-            {
-                throw new InvalidOperationException();
-            }
-            return _buffer.Substring(_startIndex);
-        }
-
-        /// <summary>Throws unconditionally for invalid data.</summary>
-        private static void ThrowForInvalidData()
-        {
-            throw new InvalidDataException();
-        }
-    }
-}
diff --git a/src/Tools/dotnet-trace/build.cmd b/src/Tools/dotnet-trace/build.cmd
new file mode 100644 (file)
index 0000000..ee7a8b0
--- /dev/null
@@ -0,0 +1,14 @@
+@if not defined _echo echo off
+cls
+
+dotnet.exe restore "%~dp0dotnet-trace.csproj" --packages "%~dp0..\..\..\artifacts\packages" || (
+    echo [ERROR] Failed to restore.
+    exit /b 1
+)
+
+for %%c in (Debug Release) do (
+    dotnet.exe build "%~dp0dotnet-trace.csproj" -c %%c --no-restore || (
+        echo [ERROR] Failed to build %%c.
+        exit /b 1
+    )
+)
index 092fb25d52d29d78a6534e86f85680d6a4ec81b3..f315774c013d4f6e32cddea404e7ab54396c6069 100644 (file)
@@ -2,28 +2,36 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-
-    <!-- Target .NET Core 2.1 so it will run on LTS -->
     <TargetFramework>netcoreapp2.1</TargetFramework>
+    <RootNamespace>Microsoft.Diagnostics.Tools.Trace</RootNamespace>
+    <IsPackable>False</IsPackable>
+    <PackAsTool>False</PackAsTool>
+  </PropertyGroup>
 
-    <RootNamespace>Microsoft.Diagnostics.Tools.Collect</RootNamespace>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-
-    <!-- Don't pack until ship engineering is done. Currently causing the official job to fail.
-    <IsPackable>true</IsPackable>
-    <PackAsTool>true</PackAsTool>
-    -->
+  <PropertyGroup>
+    <UseSharedCompilation>False</UseSharedCompilation>
+    <LangVersion>Latest</LangVersion>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>True</TreatWarningsAsErrors>
+    <GenerateDocumentationFile>False</GenerateDocumentationFile>
   </PropertyGroup>
 
   <ItemGroup>
-    <Compile Include="..\Common\CommandLineException.cs" Link="CommandLineException.cs" />
-    <Compile Include="..\Common\ConsoleCancellation.cs" Link="ConsoleCancellation.cs" />
-    <Compile Include="..\Common\DebugUtil.cs" Link="DebugUtil.cs" />
+    <PackageReference Include="System.CommandLine.Experimental" Version="0.2.0-alpha.19167.2" />
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.2.5" />
-    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.30" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
   </ItemGroup>
 
+  <PropertyGroup>
+    <!--
+      Suppress warnings for using preview SDK:
+      You are working with a preview version of the .NET Core SDK.
+      You can define the SDK version via a global.json file in the current project.
+      More at https://go.microsoft.com/fwlink/?linkid=869452
+    -->
+    <SuppressNETCoreSdkPreviewMessage>True</SuppressNETCoreSdkPreviewMessage>
+  </PropertyGroup>
+
 </Project>
\ No newline at end of file
diff --git a/src/Tools/dotnet-trace/run.cmd b/src/Tools/dotnet-trace/run.cmd
new file mode 100644 (file)
index 0000000..dffa8cf
--- /dev/null
@@ -0,0 +1,11 @@
+@if not defined _echo echo off
+
+call :run_command dotnet.exe run -c Debug --no-restore --no-build -- %*
+exit /b %ERRORLEVEL%
+
+:run_command
+    echo/%USERNAME%@%COMPUTERNAME% "%CD%"
+    echo/[%DATE% %TIME%] $ %*
+    echo/
+    call %*
+    exit /b %ERRORLEVEL%