Add enable crash report flag to WriteDump client API (#2715)
authorMike McLaughlin <mikem@microsoft.com>
Mon, 1 Nov 2021 16:12:47 +0000 (09:12 -0700)
committerGitHub <noreply@github.com>
Mon, 1 Nov 2021 16:12:47 +0000 (09:12 -0700)
Add enable crash report flag to WriteDump client API

Uses new write dump command supported by 6.0 runtime to pass a set of flags instead
of just the logging enabled bool.

Issue: https://github.com/dotnet/diagnostics/issues/2698

Code review feedback. Throw exception on unsupported flags

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/WriteDumpFlags.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs
src/Tools/dotnet-dump/Dumper.cs
src/Tools/dotnet-dump/Program.cs

index d77497edf8b5e7d6294719a1d78492a0b4b766b7..30f912f4f2913c2a27e87d2df3287f21544aef9f 100644 (file)
@@ -127,6 +127,26 @@ namespace Microsoft.Diagnostics.NETCore.Client
             ValidateResponseMessage(response, nameof(WriteDump));
         }
 
+        /// <summary>
+        /// Trigger a core dump generation.
+        /// </summary> 
+        /// <param name="dumpType">Type of the dump to be generated</param>
+        /// <param name="dumpPath">Full path to the dump to be generated. By default it is /tmp/coredump.{pid}</param>
+        /// <param name="flags">logging and crash report flags. On runtimes less than 6.0, only LoggingEnabled is supported.</param>
+        public void WriteDump(DumpType dumpType, string dumpPath, WriteDumpFlags flags)
+        {
+            IpcMessage request = CreateWriteDumpMessage2(dumpType, dumpPath, flags);
+            IpcMessage response = IpcClient.SendMessage(_endpoint, request);
+            if (!ValidateResponseMessage(response, nameof(WriteDump), ValidateResponseOptions.UnknownCommandReturnsFalse))
+            {
+                if ((flags & ~WriteDumpFlags.LoggingEnabled) != 0)
+                {
+                    throw new ArgumentException($"Only {nameof(WriteDumpFlags.LoggingEnabled)} flag is supported by this runtime version", nameof(flags));
+                }
+                WriteDump(dumpType, dumpPath, logDumpGeneration: (flags & WriteDumpFlags.LoggingEnabled) != 0);
+            }
+        }
+
         /// <summary>
         /// Trigger a core dump generation.
         /// </summary> 
@@ -134,13 +154,34 @@ namespace Microsoft.Diagnostics.NETCore.Client
         /// <param name="dumpPath">Full path to the dump to be generated. By default it is /tmp/coredump.{pid}</param>
         /// <param name="logDumpGeneration">When set to true, display the dump generation debug log to the console.</param>
         /// <param name="token">The token to monitor for cancellation requests.</param>
-        internal async Task WriteDumpAsync(DumpType dumpType, string dumpPath, bool logDumpGeneration, CancellationToken token)
+        public async Task WriteDumpAsync(DumpType dumpType, string dumpPath, bool logDumpGeneration, CancellationToken token)
         {
             IpcMessage request = CreateWriteDumpMessage(dumpType, dumpPath, logDumpGeneration);
             IpcMessage response = await IpcClient.SendMessageAsync(_endpoint, request, token).ConfigureAwait(false);
             ValidateResponseMessage(response, nameof(WriteDumpAsync));
         }
 
+        /// <summary>
+        /// Trigger a core dump generation.
+        /// </summary> 
+        /// <param name="dumpType">Type of the dump to be generated</param>
+        /// <param name="dumpPath">Full path to the dump to be generated. By default it is /tmp/coredump.{pid}</param>
+        /// <param name="flags">logging and crash report flags. On runtimes less than 6.0, only LoggingEnabled is supported.</param>
+        /// <param name="token">The token to monitor for cancellation requests.</param>
+        public async Task WriteDumpAsync(DumpType dumpType, string dumpPath, WriteDumpFlags flags, CancellationToken token)
+        {
+            IpcMessage request = CreateWriteDumpMessage2(dumpType, dumpPath, flags);
+            IpcMessage response = await IpcClient.SendMessageAsync(_endpoint, request, token).ConfigureAwait(false);
+            if (!ValidateResponseMessage(response, nameof(WriteDumpAsync), ValidateResponseOptions.UnknownCommandReturnsFalse))
+            {
+                if ((flags & ~WriteDumpFlags.LoggingEnabled) != 0)
+                {
+                    throw new ArgumentException($"Only {nameof(WriteDumpFlags.LoggingEnabled)} flag is supported by this runtime version", nameof(flags));
+                }
+                await WriteDumpAsync(dumpType, dumpPath, logDumpGeneration: (flags & WriteDumpFlags.LoggingEnabled) != 0, token);
+            }
+        }
+
         /// <summary>
         /// Attach a profiler.
         /// </summary>
@@ -471,6 +512,15 @@ namespace Microsoft.Diagnostics.NETCore.Client
             return new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload);
         }
 
+        private static IpcMessage CreateWriteDumpMessage2(DumpType dumpType, string dumpPath, WriteDumpFlags flags)
+        {
+            if (string.IsNullOrEmpty(dumpPath))
+                throw new ArgumentNullException($"{nameof(dumpPath)} required");
+
+            byte[] payload = SerializePayload(dumpPath, (uint)dumpType, (uint)flags);
+            return new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump2, payload);
+        }
+
         private static ProcessInfo GetProcessInfoFromResponse(IpcResponse response, string operationName)
         {
             ValidateResponseMessage(response.Message, operationName);
diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/WriteDumpFlags.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/WriteDumpFlags.cs
new file mode 100644 (file)
index 0000000..b1f93cd
--- /dev/null
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Diagnostics.NETCore.Client
+{
+    public enum WriteDumpFlags
+    {
+        None = 0x00,
+        LoggingEnabled = 0x01,
+        VerboseLoggingEnabled = 0x02,
+        CrashReportEnabled = 0x04
+    }
+}
\ No newline at end of file
index d76f0df0bf228362bf9cc311d0d77209eae7d2ac..b1f4e80ef17da29b2541c7021afa635cac6a7868 100644 (file)
@@ -35,6 +35,7 @@ namespace Microsoft.Diagnostics.NETCore.Client
     internal enum DumpCommandId : byte
     {
         GenerateCoreDump = 0x01,
+        GenerateCoreDump2 = 0x02,
     }
 
     internal enum ProfilerCommandId : byte
index 8d9140a96d87af18a1da4f49756eead1645980ee..ce8ec9d5c9161056f0c01431c27d5b9eade8c268 100644 (file)
@@ -34,7 +34,7 @@ namespace Microsoft.Diagnostics.Tools.Dump
         {
         }
 
-        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, bool crashreport, DumpTypeOption type, string name)
         {
             Console.WriteLine(name);
             if (name != null)
@@ -86,6 +86,12 @@ namespace Microsoft.Diagnostics.Tools.Dump
 
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                 {
+                    if (crashreport)
+                    {
+                        Console.WriteLine("Crash reports not supported on Windows.");
+                        return 0;
+                    }
+
                     // Get the process
                     Process process = Process.GetProcessById(processId);
 
@@ -112,8 +118,17 @@ namespace Microsoft.Diagnostics.Tools.Dump
                             break;
                     }
 
+                    WriteDumpFlags flags = WriteDumpFlags.None;
+                    if (diag)
+                    {
+                        flags |= WriteDumpFlags.LoggingEnabled;
+                    }
+                    if (crashreport)
+                    {
+                        flags |= WriteDumpFlags.CrashReportEnabled;
+                    }
                     // Send the command to the runtime to initiate the core dump
-                    client.WriteDump(dumpType, output, diag);
+                    client.WriteDump(dumpType, output, flags);
                 }
             }
             catch (Exception ex) when 
index f3b2bafbc3219f69092157358043c085ebd667d7..aed69c55bc31b0d3b78720a2441459810efc037a 100644 (file)
@@ -32,9 +32,9 @@ namespace Microsoft.Diagnostics.Tools.Dump
             new Command( name: "collect", description: "Capture dumps from a process")
             {
                 // Handler
-                CommandHandler.Create<IConsole, int, string, bool, Dumper.DumpTypeOption, string>(new Dumper().Collect),
+                CommandHandler.Create<IConsole, int, string, bool, bool, Dumper.DumpTypeOption, string>(new Dumper().Collect),
                 // Options
-                ProcessIdOption(), OutputOption(), DiagnosticLoggingOption(), TypeOption(), ProcessNameOption()
+                ProcessIdOption(), OutputOption(), DiagnosticLoggingOption(), CrashReportOption(), TypeOption(), ProcessNameOption()
             };
 
         private static Option ProcessIdOption() =>
@@ -70,6 +70,14 @@ on Linux where YYYYMMDD is Year/Month/Day and HHMMSS is Hour/Minute/Second. Othe
                 Argument = new Argument<bool>(name: "diag")
             };
 
+        private static Option CrashReportOption() =>
+            new Option(
+                alias: "--crashreport", 
+                description: "Enable crash report generation.") 
+            {
+                Argument = new Argument<bool>(name: "crashreport")
+            };
+
         private static Option TypeOption() =>
             new Option(
                 alias: "--type",