From c265294f4aaff88329665b3823e48b600216ad9e Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 20 Jul 2020 12:00:20 -0700 Subject: [PATCH] Prototype --- .../DiagnosticsClient/DiagnosticsClient.cs | 13 +++--- .../DiagnosticsIpc/IpcCommands.cs | 2 + src/Tools/dotnet-dump/Dumper.cs | 41 ++++++++++--------- src/Tools/dotnet-dump/Program.cs | 12 +++++- src/Tools/dotnet-dump/build.cmd | 14 +++++++ src/Tools/dotnet-dump/run.cmd | 11 +++++ .../CommandLine/CollectCommandHandler.cs | 2 + src/Tools/dotnet-gcdump/build.cmd | 14 +++++++ src/Tools/dotnet-gcdump/run.cmd | 11 +++++ 9 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 src/Tools/dotnet-dump/build.cmd create mode 100644 src/Tools/dotnet-dump/run.cmd create mode 100644 src/Tools/dotnet-gcdump/build.cmd create mode 100644 src/Tools/dotnet-gcdump/run.cmd diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs index 480f1e8892..d1dfba8a69 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs @@ -69,13 +69,15 @@ public EventPipeSession StartEventPipeSession(EventPipeProvider provider, bool r /// Type of the dump to be generated /// Full path to the dump to be generated. By default it is /tmp/coredump.{pid} /// When set to true, display the dump generation debug log to the console. - public void WriteDump(DumpType dumpType, string dumpPath, bool logDumpGeneration=false) + /// The condition + /// The identity for cancellation + public void WriteDump(DumpType dumpType, string dumpPath, bool logDumpGeneration=false, string condition=null, string identity=null) { if (string.IsNullOrEmpty(dumpPath)) throw new ArgumentNullException($"{nameof(dumpPath)} required"); - byte[] payload = SerializeCoreDump(dumpPath, dumpType, logDumpGeneration); - IpcMessage message = new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload); + byte[] payload = SerializeCoreDumpV2(dumpPath, dumpType, logDumpGeneration, condition, identity); + IpcMessage message = new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump2, payload); IpcMessage response = IpcClient.SendMessage(_processId, message); switch ((DiagnosticsServerCommandId)response.Header.CommandId) { @@ -145,7 +147,7 @@ public static IEnumerable GetPublishedProcesses() .Distinct(); } - private static byte[] SerializeCoreDump(string dumpName, DumpType dumpType, bool diagnostics) + private static byte[] SerializeCoreDumpV2(string dumpName, DumpType dumpType, bool diagnostics, string condition, string identity) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) @@ -153,7 +155,8 @@ private static byte[] SerializeCoreDump(string dumpName, DumpType dumpType, bool writer.WriteString(dumpName); writer.Write((uint)dumpType); writer.Write((uint)(diagnostics ? 1 : 0)); - + writer.WriteString(condition); + writer.WriteString(identity); writer.Flush(); return stream.ToArray(); } diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs index 7c870e3c1f..7d7639b98e 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs @@ -28,11 +28,13 @@ internal enum EventPipeCommandId : byte StopTracing = 0x01, CollectTracing = 0x02, CollectTracing2 = 0x03, + // TODO, andrewau, a new command for cancelling conditional dump collection } internal enum DumpCommandId : byte { GenerateCoreDump = 0x01, + GenerateCoreDump2 = 0x02, } internal enum ProfilerCommandId : byte diff --git a/src/Tools/dotnet-dump/Dumper.cs b/src/Tools/dotnet-dump/Dumper.cs index 4a3ce13757..728e4237b0 100644 --- a/src/Tools/dotnet-dump/Dumper.cs +++ b/src/Tools/dotnet-dump/Dumper.cs @@ -33,8 +33,9 @@ public Dumper() { } - public int Collect(IConsole console, int processId, string output, bool diag, DumpTypeOption type, string name) + public int Collect(IConsole console, int processId, string output, bool diag, DumpTypeOption type, string name, string condition) { + // TODO, andrewau, handle Control+C for cancellation of conditional request Console.WriteLine(name); if (name != null) { @@ -83,6 +84,8 @@ public int Collect(IConsole console, int processId, string output, bool diag, Du } console.Out.WriteLine($"Writing {dumpTypeMessage} to {output}"); + // TODO, andrewau, be backward compatible to older runtimes + /* if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Get the process @@ -90,27 +93,27 @@ public int Collect(IConsole console, int processId, string output, bool diag, Du Windows.CollectDump(process, output, type); } - else - { - var client = new DiagnosticsClient(processId); + */ - DumpType dumpType = DumpType.Normal; - switch (type) - { - case DumpTypeOption.Full: - dumpType = DumpType.Full; - break; - case DumpTypeOption.Heap: - dumpType = DumpType.WithHeap; - break; - case DumpTypeOption.Mini: - dumpType = DumpType.Normal; - break; - } + var client = new DiagnosticsClient(processId); - // Send the command to the runtime to initiate the core dump - client.WriteDump(dumpType, output, diag); + DumpType dumpType = DumpType.Normal; + switch (type) + { + case DumpTypeOption.Full: + dumpType = DumpType.Full; + break; + case DumpTypeOption.Heap: + dumpType = DumpType.WithHeap; + break; + case DumpTypeOption.Mini: + dumpType = DumpType.Normal; + break; } + + // Send the command to the runtime to initiate the core dump + Guid identity = Guid.NewGuid(); + client.WriteDump(dumpType, output, diag, condition, identity.ToString()); } catch (Exception ex) when (ex is FileNotFoundException || diff --git a/src/Tools/dotnet-dump/Program.cs b/src/Tools/dotnet-dump/Program.cs index cbbbe50e2e..158a87c025 100644 --- a/src/Tools/dotnet-dump/Program.cs +++ b/src/Tools/dotnet-dump/Program.cs @@ -30,9 +30,9 @@ private static Command CollectCommand() => new Command( name: "collect", description: "Capture dumps from a process") { // Handler - CommandHandler.Create(new Dumper().Collect), + CommandHandler.Create(new Dumper().Collect), // Options - ProcessIdOption(), OutputOption(), DiagnosticLoggingOption(), TypeOption(), ProcessNameOption() + ProcessIdOption(), OutputOption(), DiagnosticLoggingOption(), TypeOption(), ProcessNameOption(), ConditionOption() }; private static Option ProcessIdOption() => @@ -51,6 +51,14 @@ private static Option ProcessNameOption() => Argument = new Argument(name: "name") }; + private static Option ConditionOption() => + new Option( + aliases: new[] { "-c", "--condition" }, + description: "The condition (TODO - documentation).") + { + Argument = new Argument(name: "condition") + }; + private static Option OutputOption() => new Option( aliases: new[] { "-o", "--output" }, diff --git a/src/Tools/dotnet-dump/build.cmd b/src/Tools/dotnet-dump/build.cmd new file mode 100644 index 0000000000..1efd38f9af --- /dev/null +++ b/src/Tools/dotnet-dump/build.cmd @@ -0,0 +1,14 @@ +@if not defined _echo echo off +cls + +dotnet.exe restore "%~dp0dotnet-dump.csproj" --packages "%~dp0..\..\..\artifacts\packages" || ( + echo [ERROR] Failed to restore. + exit /b 1 +) + +for %%c in (Debug Release) do ( + dotnet.exe build "%~dp0dotnet-dump.csproj" -c %%c --no-restore || ( + echo [ERROR] Failed to build %%c. + exit /b 1 + ) +) diff --git a/src/Tools/dotnet-dump/run.cmd b/src/Tools/dotnet-dump/run.cmd new file mode 100644 index 0000000000..dffa8cf06e --- /dev/null +++ b/src/Tools/dotnet-dump/run.cmd @@ -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% diff --git a/src/Tools/dotnet-gcdump/CommandLine/CollectCommandHandler.cs b/src/Tools/dotnet-gcdump/CommandLine/CollectCommandHandler.cs index f756e4ea21..744e6e4b20 100644 --- a/src/Tools/dotnet-gcdump/CommandLine/CollectCommandHandler.cs +++ b/src/Tools/dotnet-gcdump/CommandLine/CollectCommandHandler.cs @@ -28,6 +28,8 @@ internal static class CollectCommandHandler /// private static async Task Collect(CancellationToken ct, IConsole console, int processId, string output, int timeout, bool verbose, string name) { + // TODO, andrewau, support the condition option + // TODO, andrewau, handle Ctrl+C for cancellation for conditional request if (name != null) { if (processId != 0) diff --git a/src/Tools/dotnet-gcdump/build.cmd b/src/Tools/dotnet-gcdump/build.cmd new file mode 100644 index 0000000000..e15b9efaa1 --- /dev/null +++ b/src/Tools/dotnet-gcdump/build.cmd @@ -0,0 +1,14 @@ +@if not defined _echo echo off +cls + +dotnet.exe restore "%~dp0dotnet-gcdump.csproj" --packages "%~dp0..\..\..\artifacts\packages" || ( + echo [ERROR] Failed to restore. + exit /b 1 +) + +for %%c in (Debug Release) do ( + dotnet.exe build "%~dp0dotnet-gcdump.csproj" -c %%c --no-restore || ( + echo [ERROR] Failed to build %%c. + exit /b 1 + ) +) diff --git a/src/Tools/dotnet-gcdump/run.cmd b/src/Tools/dotnet-gcdump/run.cmd new file mode 100644 index 0000000000..dffa8cf06e --- /dev/null +++ b/src/Tools/dotnet-gcdump/run.cmd @@ -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%