From 21056407b6d2b9eb28a4eb5c4b8c2b10bc1471ff Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:06:12 +0000 Subject: [PATCH 01/82] [main] Update dependencies from dotnet/runtime (#4271) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 23878c8d5d..29a1d86ad4 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore 4292763bd5143205daabb682311ee34f23897d9b - + https://github.com/dotnet/runtime - 0933e300f0c0647a15a0433f1a3b07bcab9882f4 + 885100b00bc944cbb698bc4cc2ec3ec18007534f - + https://github.com/dotnet/runtime - 0933e300f0c0647a15a0433f1a3b07bcab9882f4 + 885100b00bc944cbb698bc4cc2ec3ec18007534f https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index f2b21dc365..a2c81393b6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23476.15 - 8.0.0-rtm.23476.15 + 8.0.0-rtm.23477.9 + 8.0.0-rtm.23477.9 8.0.0-rtm.23476.22 8.0.0-rtm.23476.22 From a1c0993ef6ce9cd55dcba7e887ddd0a49d3cc1d7 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:10:26 +0000 Subject: [PATCH 02/82] [main] Update dependencies from dotnet/aspnetcore (#4270) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 29a1d86ad4..9136113505 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer f8a61a24ac843529a82a8f6ede35fc08a6fb8c35 - + https://github.com/dotnet/aspnetcore - 4292763bd5143205daabb682311ee34f23897d9b + 96da75d67ff058d95e14de12d9fc2ff215074e3c - + https://github.com/dotnet/aspnetcore - 4292763bd5143205daabb682311ee34f23897d9b + 96da75d67ff058d95e14de12d9fc2ff215074e3c https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index a2c81393b6..a67e1885e1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23477.9 8.0.0-rtm.23477.9 - 8.0.0-rtm.23476.22 - 8.0.0-rtm.23476.22 + 8.0.0-rtm.23477.14 + 8.0.0-rtm.23477.14 8.0.100-rtm.23474.2 From 2640ef635e9ba5df3d36934b4172cbc77495bb1e Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Thu, 28 Sep 2023 09:40:32 -0700 Subject: [PATCH 03/82] Change Frozen object heap to NonGC heap in !EEHeap output (#4269) --- src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs index ac2f9507f6..c565148650 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs @@ -525,11 +525,11 @@ private ulong PrintGCHeap(ClrRuntime clrRuntime) WriteSegment(gcOutput, segment); } - // print frozen object heap + // print NonGC heap segments = HeapWithFilters.EnumerateFilteredSegments(gc_heap).Where(seg => seg.Kind == GCSegmentKind.Frozen).OrderBy(seg => seg.Start); if (segments.Any()) { - Console.WriteLine("Frozen object heap"); + Console.WriteLine("NonGC heap"); WriteSegmentHeader(gcOutput); foreach (ClrSegment segment in segments) From 20ad4db666102e7ebedbd897baeea9f27b88e2a6 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:00:18 +0000 Subject: [PATCH 04/82] [main] Update dependencies from dotnet/aspnetcore (#4273) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9136113505..b39d72dbda 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer f8a61a24ac843529a82a8f6ede35fc08a6fb8c35 - + https://github.com/dotnet/aspnetcore - 96da75d67ff058d95e14de12d9fc2ff215074e3c + 96b96df20c1d09ec5dc2a0831b6a39c33529c14a - + https://github.com/dotnet/aspnetcore - 96da75d67ff058d95e14de12d9fc2ff215074e3c + 96b96df20c1d09ec5dc2a0831b6a39c33529c14a https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index a67e1885e1..f49eaa68cb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23477.9 8.0.0-rtm.23477.9 - 8.0.0-rtm.23477.14 - 8.0.0-rtm.23477.14 + 8.0.0-rtm.23478.18 + 8.0.0-rtm.23478.18 8.0.100-rtm.23474.2 From 65e4c7673b35162926c82095c3e6000aa2c9a93d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:21:30 +0000 Subject: [PATCH 05/82] [main] Update dependencies from dotnet/runtime (#4275) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b39d72dbda..e251977510 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore 96b96df20c1d09ec5dc2a0831b6a39c33529c14a - + https://github.com/dotnet/runtime - 885100b00bc944cbb698bc4cc2ec3ec18007534f + b20f704cc00f390e5560a137deb8f0e64e863e1d - + https://github.com/dotnet/runtime - 885100b00bc944cbb698bc4cc2ec3ec18007534f + b20f704cc00f390e5560a137deb8f0e64e863e1d https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index f49eaa68cb..090f35a917 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23477.9 - 8.0.0-rtm.23477.9 + 8.0.0-rtm.23478.17 + 8.0.0-rtm.23478.17 8.0.0-rtm.23478.18 8.0.0-rtm.23478.18 From 6c5b2e85913d2f1852181791fe22a575cb970051 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 12:34:10 -0700 Subject: [PATCH 06/82] [main] Update dependencies from microsoft/clrmd (#4274) This pull request updates the following dependencies [marker]: <> (Begin:cb58fe07-ae24-4e73-0e84-08d8e40a189f) ## From https://github.com/microsoft/clrmd - **Subscription**: cb58fe07-ae24-4e73-0e84-08d8e40a189f - **Build**: 20230928.1 - **Date Produced**: September 28, 2023 10:49:03 PM UTC - **Commit**: 10e87fb192d41e22d5fd0fd54f02ed011726e991 - **Branch**: refs/heads/main [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.Diagnostics.Runtime**: [from 3.0.447501 to 3.0.447801][1] - **Microsoft.Diagnostics.Runtime.Utilities**: [from 3.0.447501 to 3.0.447801][1] [1]: https://github.com/microsoft/clrmd/compare/6d7c5a7288...10e87fb192 [DependencyUpdate]: <> (End) [marker]: <> (End:cb58fe07-ae24-4e73-0e84-08d8e40a189f) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e251977510..f2d11ab822 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 8cc6f03fdbd9c79f0bf9ffbe0a788dca1a81348a - + https://github.com/microsoft/clrmd - 6d7c5a7288c0e93e5eb56893a6064575ac6e3ea8 + 10e87fb192d41e22d5fd0fd54f02ed011726e991 - + https://github.com/microsoft/clrmd - 6d7c5a7288c0e93e5eb56893a6064575ac6e3ea8 + 10e87fb192d41e22d5fd0fd54f02ed011726e991 diff --git a/eng/Versions.props b/eng/Versions.props index 090f35a917..ddc149eed2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,7 @@ 5.0.0 6.0.0 - 3.0.447501 + 3.0.447801 16.11.27-beta1.23180.1 3.0.7 6.0.0 From 407bf0417ccaa46448b950c90ce71ecfc101f5a9 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 29 Sep 2023 17:22:13 -0700 Subject: [PATCH 07/82] Fix build breaks (#4276) --- src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs index 76cda04435..67e42bac97 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs @@ -995,7 +995,7 @@ private IEnumerable EnumerateConcurrentQueueCore(ulong address) ClrType slotType = _heap.GetObjectType(slotEntry.ToUInt64()); if (slotType.IsString) { - yield return $"\"{new ClrObject(slotEntry.ToUInt64(), slotType).AsString()}\""; + yield return $"\"{_heap.GetObject(slotEntry.ToUInt64(), slotType).AsString()}\""; } else { @@ -1106,7 +1106,7 @@ private static bool IsSimpleType(string typeName) } } - private static string DumpPropertyValue(ClrObject obj, string propertyName) + private string DumpPropertyValue(ClrObject obj, string propertyName) { const string defaultContent = "?"; @@ -1115,7 +1115,7 @@ private static string DumpPropertyValue(ClrObject obj, string propertyName) { if (fieldType.IsString) { - return $"\"{new ClrObject(field.Address, fieldType).AsString()}\""; + return $"\"{_heap.GetObject(field.Address, fieldType).AsString()}\""; } else if (fieldType.IsArray) { From 9560442b6c0275a19d5826df57d39ca9ca3f6b62 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:55:29 +0000 Subject: [PATCH 08/82] [main] Update dependencies from dotnet/aspnetcore (#4277) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f2d11ab822..baed3b5b21 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer f8a61a24ac843529a82a8f6ede35fc08a6fb8c35 - + https://github.com/dotnet/aspnetcore - 96b96df20c1d09ec5dc2a0831b6a39c33529c14a + 064ae8e7674c3c11f8aef78a9a132979c8778cc9 - + https://github.com/dotnet/aspnetcore - 96b96df20c1d09ec5dc2a0831b6a39c33529c14a + 064ae8e7674c3c11f8aef78a9a132979c8778cc9 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index ddc149eed2..79d83bccd7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23478.17 8.0.0-rtm.23478.17 - 8.0.0-rtm.23478.18 - 8.0.0-rtm.23478.18 + 8.0.0-rtm.23479.3 + 8.0.0-rtm.23479.3 8.0.100-rtm.23474.2 From 81c0bcb6592d9d41eceafb922f4078d855bf6c7c Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Sat, 30 Sep 2023 11:30:11 -0700 Subject: [PATCH 09/82] Update SOS to show the relevant information for the !ThreadPool command when using the Windows thread pool (#4160) Uses the properties exposed in https://github.com/microsoft/clrmd/pull/1175 to show relevant Windows thread pool information if it's enabled --------- Co-authored-by: Mike McLaughlin --- eng/Version.Details.xml | 8 +-- eng/Versions.props | 2 +- .../ThreadPoolCommand.cs | 70 ++++++++++++------- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index baed3b5b21..0a8ec93343 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 8cc6f03fdbd9c79f0bf9ffbe0a788dca1a81348a - + https://github.com/microsoft/clrmd - 10e87fb192d41e22d5fd0fd54f02ed011726e991 + 547545a301475a7cfd23ce8c2f6e24eddf55d83e - + https://github.com/microsoft/clrmd - 10e87fb192d41e22d5fd0fd54f02ed011726e991 + 547545a301475a7cfd23ce8c2f6e24eddf55d83e diff --git a/eng/Versions.props b/eng/Versions.props index 79d83bccd7..e6fb2b5f86 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,7 @@ 5.0.0 6.0.0 - 3.0.447801 + 3.0.447901 16.11.27-beta1.23180.1 3.0.7 6.0.0 diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs index 85f7c4d973..621b6a501c 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs @@ -33,15 +33,25 @@ public override void Invoke() } else { - Table output = new(Console, Text.WithWidth(17), Text); - output.WriteRow("CPU utilization:", $"{threadPool.CpuUtilization}%"); - output.WriteRow("Workers Total:", threadPool.ActiveWorkerThreads + threadPool.IdleWorkerThreads + threadPool.RetiredWorkerThreads); - output.WriteRow("Workers Running:", threadPool.ActiveWorkerThreads); - output.WriteRow("Workers Idle:", threadPool.IdleWorkerThreads); - output.WriteRow("Worker Min Limit:", threadPool.MinThreads); - output.WriteRow("Worker Max Limit:", threadPool.MaxThreads); + string threadpoolType = threadPool.UsingWindowsThreadPool ? "Windows" : "Portable"; + Console.WriteLine($"Using the {threadpoolType} thread pool."); Console.WriteLine(); + Table output = new(Console, Text.WithWidth(17), Text); + if (threadPool.UsingWindowsThreadPool) + { + output.WriteRow("Thread count:", threadPool.WindowsThreadPoolThreadCount); + } + else + { + output.WriteRow("CPU utilization:", $"{threadPool.CpuUtilization}%"); + output.WriteRow("Workers Total:", threadPool.ActiveWorkerThreads + threadPool.IdleWorkerThreads + threadPool.RetiredWorkerThreads); + output.WriteRow("Workers Running:", threadPool.ActiveWorkerThreads); + output.WriteRow("Workers Idle:", threadPool.IdleWorkerThreads); + output.WriteRow("Worker Min Limit:", threadPool.MinThreads); + output.WriteRow("Worker Max Limit:", threadPool.MaxThreads); + } + Console.WriteLine(); ClrType threadPoolType = Runtime.BaseClassLibrary.GetTypeByName("System.Threading.ThreadPool"); ClrStaticField usePortableIOField = threadPoolType?.GetStaticFieldByName("UsePortableThreadPoolForIO"); @@ -68,10 +78,14 @@ public override void Invoke() } } - // We will assume that if UsePortableThreadPoolForIO field is deleted from ThreadPool then we are always - // using C# version. - bool usingPortableCompletionPorts = threadPool.Portable && (usePortableIOField is null || usePortableIOField.Read(usePortableIOField.Type.Module.AppDomain)); - if (!usingPortableCompletionPorts) + /* + The IO completion thread pool exists in .NET 7 and earlier + It is the only option in .NET 6 and below. The UsePortableThreadPoolForIO field doesn't exist. + In .NET 7, the UsePortableThreadPoolForIO field exists and is true by default, in which case the IO completion thread pool is not used, but that can be changed through config + In .NET 8, the UsePortableThreadPoolForIO field doesn't exist and the IO completion thread pool doesn't exist. However, in .NET 8, GetThreadpoolData returns E_NOTIMPL. + */ + bool usingIOCompletionThreadPool = threadPool.HasLegacyData && (usePortableIOField is null || !usePortableIOField.Read(usePortableIOField.Type.Module.AppDomain)); + if (usingIOCompletionThreadPool) { output.Columns[0] = output.Columns[0].WithWidth(19); output.WriteRow("Completion Total:", threadPool.TotalCompletionPorts); @@ -87,28 +101,36 @@ public override void Invoke() if (PrintHillClimbingLog) { - HillClimbingLogEntry[] hcl = threadPool.EnumerateHillClimbingLog().ToArray(); - if (hcl.Length > 0) + if (threadPool.UsingWindowsThreadPool) + { + Console.WriteLine("Hill Climbing Log is not supported by the Windows thread pool."); + Console.WriteLine(); + } + else { - output = new(Console, Text.WithWidth(10).WithAlignment(Align.Right), Column.ForEnum(), Integer, Integer, Text.WithAlignment(Align.Right)); + HillClimbingLogEntry[] hcl = threadPool.EnumerateHillClimbingLog().ToArray(); + if (hcl.Length > 0) + { + output = new(Console, Text.WithWidth(10).WithAlignment(Align.Right), Column.ForEnum(), Integer, Integer, Text.WithAlignment(Align.Right)); - Console.WriteLine("Hill Climbing Log:"); - output.WriteHeader("Time", "Transition", "#New Threads", "#Samples", "Throughput"); + Console.WriteLine("Hill Climbing Log:"); + output.WriteHeader("Time", "Transition", "#New Threads", "#Samples", "Throughput"); - int end = hcl.Last().TickCount; - foreach (HillClimbingLogEntry entry in hcl) - { - Console.CancellationToken.ThrowIfCancellationRequested(); - output.WriteRow($"{(entry.TickCount - end)/1000.0:0.00}", entry.StateOrTransition, entry.NewThreadCount, entry.SampleCount, $"{entry.Throughput:0.00}"); - } + int end = hcl.Last().TickCount; + foreach (HillClimbingLogEntry entry in hcl) + { + Console.CancellationToken.ThrowIfCancellationRequested(); + output.WriteRow($"{(entry.TickCount - end) / 1000.0:0.00}", entry.StateOrTransition, entry.NewThreadCount, entry.SampleCount, $"{entry.Throughput:0.00}"); + } - Console.WriteLine(); + Console.WriteLine(); + } } } } // We can print managed work items even if we failed to request the ThreadPool. - if (PrintWorkItems && (threadPool is null || threadPool.Portable)) + if (PrintWorkItems && (threadPool is null || threadPool.UsingPortableThreadPool || threadPool.UsingWindowsThreadPool)) { DumpWorkItems(); } From 28ab415e148cd9c39d68f6a8ddc0426b27a73dca Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 30 Sep 2023 11:31:39 -0700 Subject: [PATCH 10/82] [main] Update dependencies from microsoft/clrmd (#4278) This pull request updates the following dependencies [marker]: <> (Begin:cb58fe07-ae24-4e73-0e84-08d8e40a189f) ## From https://github.com/microsoft/clrmd - **Subscription**: cb58fe07-ae24-4e73-0e84-08d8e40a189f - **Build**: 20230929.1 - **Date Produced**: September 29, 2023 3:41:14 PM UTC - **Commit**: 547545a301475a7cfd23ce8c2f6e24eddf55d83e - **Branch**: refs/heads/main [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.Diagnostics.Runtime**: [from 3.0.447801 to 3.0.447901][1] - **Microsoft.Diagnostics.Runtime.Utilities**: [from 3.0.447801 to 3.0.447901][1] [1]: https://github.com/microsoft/clrmd/compare/10e87fb192...547545a301 [DependencyUpdate]: <> (End) [marker]: <> (End:cb58fe07-ae24-4e73-0e84-08d8e40a189f) Co-authored-by: dotnet-maestro[bot] From 9cdcd7bfa6b0c77e5aca4f825c74eabf07fcbb3c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 13:09:38 +0000 Subject: [PATCH 11/82] [main] Update dependencies from dotnet/aspnetcore (#4280) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0a8ec93343..f3467e78d7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer f8a61a24ac843529a82a8f6ede35fc08a6fb8c35 - + https://github.com/dotnet/aspnetcore - 064ae8e7674c3c11f8aef78a9a132979c8778cc9 + ad432054a6a2d87beb2208880ee1e6fa35348ebc - + https://github.com/dotnet/aspnetcore - 064ae8e7674c3c11f8aef78a9a132979c8778cc9 + ad432054a6a2d87beb2208880ee1e6fa35348ebc https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index e6fb2b5f86..21f43906b2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23478.17 8.0.0-rtm.23478.17 - 8.0.0-rtm.23479.3 - 8.0.0-rtm.23479.3 + 8.0.0-rtm.23480.4 + 8.0.0-rtm.23480.4 8.0.100-rtm.23474.2 From 9bbe7161393e2f80d44d81cf534f00a8442fe583 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Mon, 2 Oct 2023 11:25:27 +0200 Subject: [PATCH 12/82] Improve dotnet-dsrouter log levels and info. (#4199) * Add support for -v none, suppressing all logging making dotnet-dsrouter quiet. * Add support for --info option for new iOS/Android preconfig commands, outputs details on how to configure app and diagnostic tools to connect to current running dsrouter instance. * Improve error handling when failing to find or execute adb binary. * Specify and validate supported verbose logging levels. --- .../DiagnosticsServerRouterFactory.cs | 2 + .../dotnet-dsrouter/ADBTcpRouterFactory.cs | 69 ++++--- .../DiagnosticsServerRouterCommands.cs | 170 +++++++++++------- src/Tools/dotnet-dsrouter/Program.cs | 42 +++-- 4 files changed, 185 insertions(+), 98 deletions(-) diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs index a8f09868ed..124d7bb333 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsServerRouter/DiagnosticsServerRouterFactory.cs @@ -406,6 +406,8 @@ internal class TcpClientRouterFactory protected int TcpClientRetryTimeoutMs { get; set; } = 500; + protected ILogger Logger => _logger; + public delegate TcpClientRouterFactory CreateInstanceDelegate(string tcpClient, int runtimeTimeoutMs, ILogger logger); public static TcpClientRouterFactory CreateDefaultInstance(string tcpClient, int runtimeTimeoutMs, ILogger logger) diff --git a/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs b/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs index 5177e6b307..95c3abf28f 100644 --- a/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs +++ b/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs @@ -13,12 +13,12 @@ namespace Microsoft.Diagnostics.Tools.DiagnosticsServerRouter { internal static class ADBCommandExec { - public static bool AdbAddPortForward(int port, ILogger logger) + public static bool AdbAddPortForward(int port, bool rethrow, ILogger logger) { bool ownsPortForward = false; - if (!RunAdbCommandInternal($"forward --list", $"tcp:{port}", 0, logger)) + if (!RunAdbCommandInternal($"forward --list", $"tcp:{port}", 0, rethrow, logger)) { - ownsPortForward = RunAdbCommandInternal($"forward tcp:{port} tcp:{port}", "", 0, logger); + ownsPortForward = RunAdbCommandInternal($"forward tcp:{port} tcp:{port}", "", 0, rethrow, logger); if (!ownsPortForward) { logger?.LogError($"Failed setting up port forward for tcp:{port}."); @@ -27,12 +27,12 @@ public static bool AdbAddPortForward(int port, ILogger logger) return ownsPortForward; } - public static bool AdbAddPortReverse(int port, ILogger logger) + public static bool AdbAddPortReverse(int port, bool rethrow, ILogger logger) { bool ownsPortForward = false; - if (!RunAdbCommandInternal($"reverse --list", $"tcp:{port}", 0, logger)) + if (!RunAdbCommandInternal($"reverse --list", $"tcp:{port}", 0, rethrow, logger)) { - ownsPortForward = RunAdbCommandInternal($"reverse tcp:{port} tcp:{port}", "", 0, logger); + ownsPortForward = RunAdbCommandInternal($"reverse tcp:{port} tcp:{port}", "", 0, rethrow, logger); if (!ownsPortForward) { logger?.LogError($"Failed setting up port forward for tcp:{port}."); @@ -41,36 +41,36 @@ public static bool AdbAddPortReverse(int port, ILogger logger) return ownsPortForward; } - public static void AdbRemovePortForward(int port, bool ownsPortForward, ILogger logger) + public static void AdbRemovePortForward(int port, bool ownsPortForward, bool rethrow, ILogger logger) { if (ownsPortForward) { - if (!RunAdbCommandInternal($"forward --remove tcp:{port}", "", 0, logger)) + if (!RunAdbCommandInternal($"forward --remove tcp:{port}", "", 0, rethrow, logger)) { logger?.LogError($"Failed removing port forward for tcp:{port}."); } } } - public static void AdbRemovePortReverse(int port, bool ownsPortForward, ILogger logger) + public static void AdbRemovePortReverse(int port, bool ownsPortForward, bool rethrow, ILogger logger) { if (ownsPortForward) { - if (!RunAdbCommandInternal($"reverse --remove tcp:{port}", "", 0, logger)) + if (!RunAdbCommandInternal($"reverse --remove tcp:{port}", "", 0, rethrow, logger)) { logger?.LogError($"Failed removing port forward for tcp:{port}."); } } } - public static bool RunAdbCommandInternal(string command, string expectedOutput, int expectedExitCode, ILogger logger) + public static bool RunAdbCommandInternal(string command, string expectedOutput, int expectedExitCode, bool rethrow, ILogger logger) { string sdkRoot = Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT"); string adbTool = "adb"; if (!string.IsNullOrEmpty(sdkRoot)) { - adbTool = sdkRoot + Path.DirectorySeparatorChar + "platform-tools" + Path.DirectorySeparatorChar + adbTool; + adbTool = Path.Combine(sdkRoot, "platform-tools", adbTool); } logger?.LogDebug($"Executing {adbTool} {command}."); @@ -94,7 +94,11 @@ public static bool RunAdbCommandInternal(string command, string expectedOutput, } catch (Exception ex) { - logger.LogError($"Failed executing {adbTool} {command}. Error: {ex.Message}."); + logger.LogError($"Failed executing {adbTool} {command}. Error: {ex.Message}"); + if (rethrow) + { + throw ex; + } } if (processStartedResult) @@ -116,10 +120,7 @@ public static bool RunAdbCommandInternal(string command, string expectedOutput, { logger.LogError($"stderr: {stderr.TrimEnd()}"); } - } - if (processStartedResult) - { process.WaitForExit(); expectedExitCodeResult = (expectedExitCode != -1) ? (process.ExitCode == expectedExitCode) : true; } @@ -149,7 +150,19 @@ public ADBTcpServerRouterFactory(string tcpServer, int runtimeTimeoutMs, ILogger public override void Start() { // Enable port reverse. - _ownsPortReverse = ADBCommandExec.AdbAddPortReverse(_port, Logger); + try + { + _ownsPortReverse = ADBCommandExec.AdbAddPortReverse(_port, true, Logger); + } + catch + { + _ownsPortReverse = false; + Logger.LogError("Failed setting up adb port reverse." + + " This might lead to problems communicating with Android application." + + " Make sure env variable ANDROID_SDK_ROOT is set and points to an Android SDK." + + $" Executing with unknown adb status for port {_port}."); + return; + } _portReverseTaskCancelToken = new CancellationTokenSource(); _portReverseTask = Task.Run(async () => { @@ -157,7 +170,7 @@ public override void Start() while (await timer.WaitForNextTickAsync(_portReverseTaskCancelToken.Token).ConfigureAwait(false) && !_portReverseTaskCancelToken.Token.IsCancellationRequested) { // Make sure reverse port configuration is still active. - if (ADBCommandExec.AdbAddPortReverse(_port, Logger) && !_ownsPortReverse) + if (ADBCommandExec.AdbAddPortReverse(_port, false, Logger) && !_ownsPortReverse) { _ownsPortReverse = true; } @@ -179,7 +192,7 @@ public override async Task Stop() catch { } // Disable port reverse. - ADBCommandExec.AdbRemovePortReverse(_port, _ownsPortReverse, Logger); + ADBCommandExec.AdbRemovePortReverse(_port, _ownsPortReverse, false, Logger); _ownsPortReverse = false; } } @@ -205,7 +218,19 @@ public ADBTcpClientRouterFactory(string tcpClient, int runtimeTimeoutMs, ILogger public override void Start() { // Enable port forwarding. - _ownsPortForward = ADBCommandExec.AdbAddPortForward(_port, _logger); + try + { + _ownsPortForward = ADBCommandExec.AdbAddPortForward(_port, true, Logger); + } + catch + { + _ownsPortForward = false; + Logger.LogError("Failed setting up adb port forward." + + " This might lead to problems communicating with Android application." + + " Make sure env variable ANDROID_SDK_ROOT is set and points to an Android SDK." + + $" Executing with unknown adb status for port {_port}."); + return; + } _portForwardTaskCancelToken = new CancellationTokenSource(); _portForwardTask = Task.Run(async () => { @@ -213,7 +238,7 @@ public override void Start() while (await timer.WaitForNextTickAsync(_portForwardTaskCancelToken.Token).ConfigureAwait(false) && !_portForwardTaskCancelToken.Token.IsCancellationRequested) { // Make sure forward port configuration is still active. - if (ADBCommandExec.AdbAddPortForward(_port, _logger) && !_ownsPortForward) + if (ADBCommandExec.AdbAddPortForward(_port, false, Logger) && !_ownsPortForward) { _ownsPortForward = true; } @@ -231,7 +256,7 @@ public override void Stop() catch { } // Disable port forwarding. - ADBCommandExec.AdbRemovePortForward(_port, _ownsPortForward, _logger); + ADBCommandExec.AdbRemovePortForward(_port, _ownsPortForward, false, Logger); _ownsPortForward = false; } } diff --git a/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs b/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs index 5b83ceda81..20fd5b312b 100644 --- a/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs +++ b/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs @@ -72,27 +72,8 @@ protected SpecificRunnerBase(LogLevel logLevel) LogLevel = logLevel; } - protected SpecificRunnerBase(string logLevel) : this(ParseLogLevel(logLevel)) - { - } - public abstract void ConfigureLauncher(CancellationToken cancellationToken); - protected static LogLevel ParseLogLevel(string verbose) - { - LogLevel logLevel = LogLevel.Information; - if (string.Equals(verbose, "debug", StringComparison.OrdinalIgnoreCase)) - { - logLevel = LogLevel.Debug; - } - else if (string.Equals(verbose, "trace", StringComparison.OrdinalIgnoreCase)) - { - logLevel = LogLevel.Trace; - } - - return logLevel; - } - // The basic run loop: configure logging and the launcher, then create the router and run it until it exits or the user interrupts public async Task CommonRunLoop(Func> createRouterTask, CancellationToken token) { @@ -103,9 +84,11 @@ public async Task CommonRunLoop(Func routerTask = createRouterTask(logger, Launcher, linkedCancelToken); @@ -147,13 +130,13 @@ await Task.WhenAny(routerTask, Task.Delay( private sealed class IpcClientTcpServerRunner : SpecificRunnerBase { - public IpcClientTcpServerRunner(string verbose) : base(verbose) { } + public IpcClientTcpServerRunner(LogLevel logLevel) : base(logLevel) { } public override void ConfigureLauncher(CancellationToken cancellationToken) { Launcher.SuspendProcess = true; Launcher.ConnectMode = true; - Launcher.Verbose = LogLevel != LogLevel.Information; + Launcher.Verbose = LogLevel < LogLevel.Information; Launcher.CommandToken = cancellationToken; } @@ -169,9 +152,11 @@ public override ILoggerFactory ConfigureLogging() public async Task RunIpcClientTcpServerRouter(CancellationToken token, string ipcClient, string tcpServer, int runtimeTimeout, string verbose, string forwardPort) { - checkLoopbackOnly(tcpServer); + LogLevel logLevel = ParseLogLevel(verbose); - IpcClientTcpServerRunner runner = new(verbose); + checkLoopbackOnly(tcpServer, logLevel); + + IpcClientTcpServerRunner runner = new(logLevel); return await runner.CommonRunLoop((logger, launcherCallbacks, linkedCancelToken) => { NetServerRouterFactory.CreateInstanceDelegate tcpServerRouterFactory = ChooseTcpServerRouterFactory(forwardPort, logger); @@ -183,22 +168,24 @@ public async Task RunIpcClientTcpServerRouter(CancellationToken token, stri private sealed class IpcServerTcpServerRunner : SpecificRunnerBase { - public IpcServerTcpServerRunner(string verbose) : base(verbose) { } + public IpcServerTcpServerRunner(LogLevel logLevel) : base(logLevel) { } public override void ConfigureLauncher(CancellationToken cancellationToken) { Launcher.SuspendProcess = false; Launcher.ConnectMode = true; - Launcher.Verbose = LogLevel != LogLevel.Information; + Launcher.Verbose = LogLevel < LogLevel.Information; Launcher.CommandToken = cancellationToken; } } public async Task RunIpcServerTcpServerRouter(CancellationToken token, string ipcServer, string tcpServer, int runtimeTimeout, string verbose, string forwardPort) { - checkLoopbackOnly(tcpServer); + LogLevel logLevel = ParseLogLevel(verbose); + + checkLoopbackOnly(tcpServer, logLevel); - IpcServerTcpServerRunner runner = new(verbose); + IpcServerTcpServerRunner runner = new(logLevel); return await runner.CommonRunLoop((logger, launcherCallbacks, linkedCancelToken) => { NetServerRouterFactory.CreateInstanceDelegate tcpServerRouterFactory = ChooseTcpServerRouterFactory(forwardPort, logger); @@ -215,20 +202,20 @@ public async Task RunIpcServerTcpServerRouter(CancellationToken token, stri private sealed class IpcServerTcpClientRunner : SpecificRunnerBase { - public IpcServerTcpClientRunner(string verbose) : base(verbose) { } + public IpcServerTcpClientRunner(LogLevel logLevel) : base(logLevel) { } public override void ConfigureLauncher(CancellationToken cancellationToken) { Launcher.SuspendProcess = false; Launcher.ConnectMode = false; - Launcher.Verbose = LogLevel != LogLevel.Information; + Launcher.Verbose = LogLevel < LogLevel.Information; Launcher.CommandToken = cancellationToken; } } public async Task RunIpcServerTcpClientRouter(CancellationToken token, string ipcServer, string tcpClient, int runtimeTimeout, string verbose, string forwardPort) { - IpcServerTcpClientRunner runner = new(verbose); + IpcServerTcpClientRunner runner = new(ParseLogLevel(verbose)); return await runner.CommonRunLoop((logger, launcherCallbacks, linkedCancelToken) => { TcpClientRouterFactory.CreateInstanceDelegate tcpClientRouterFactory = ChooseTcpClientRouterFactory(forwardPort, logger); @@ -244,20 +231,20 @@ public async Task RunIpcServerTcpClientRouter(CancellationToken token, stri private sealed class IpcClientTcpClientRunner : SpecificRunnerBase { - public IpcClientTcpClientRunner(string verbose) : base(verbose) { } + public IpcClientTcpClientRunner(LogLevel logLevel) : base(logLevel) { } public override void ConfigureLauncher(CancellationToken cancellationToken) { Launcher.SuspendProcess = true; Launcher.ConnectMode = false; - Launcher.Verbose = LogLevel != LogLevel.Information; + Launcher.Verbose = LogLevel < LogLevel.Information; Launcher.CommandToken = cancellationToken; } } public async Task RunIpcClientTcpClientRouter(CancellationToken token, string ipcClient, string tcpClient, int runtimeTimeout, string verbose, string forwardPort) { - IpcClientTcpClientRunner runner = new(verbose); + IpcClientTcpClientRunner runner = new(ParseLogLevel(verbose)); return await runner.CommonRunLoop((logger, launcherCallbacks, linkedCancelToken) => { TcpClientRouterFactory.CreateInstanceDelegate tcpClientRouterFactory = ChooseTcpClientRouterFactory(forwardPort, logger); @@ -268,20 +255,20 @@ public async Task RunIpcClientTcpClientRouter(CancellationToken token, stri private sealed class IpcServerWebSocketServerRunner : SpecificRunnerBase { - public IpcServerWebSocketServerRunner(string verbose) : base(verbose) { } + public IpcServerWebSocketServerRunner(LogLevel logLevel) : base(logLevel) { } public override void ConfigureLauncher(CancellationToken cancellationToken) { Launcher.SuspendProcess = false; Launcher.ConnectMode = true; - Launcher.Verbose = LogLevel != LogLevel.Information; + Launcher.Verbose = LogLevel < LogLevel.Information; Launcher.CommandToken = cancellationToken; } } public async Task RunIpcServerWebSocketServerRouter(CancellationToken token, string ipcServer, string webSocket, int runtimeTimeout, string verbose) { - IpcServerWebSocketServerRunner runner = new(verbose); + IpcServerWebSocketServerRunner runner = new(ParseLogLevel(verbose)); WebSocketServer.WebSocketServerImpl server = new(runner.LogLevel); @@ -311,20 +298,20 @@ public async Task RunIpcServerWebSocketServerRouter(CancellationToken token private sealed class IpcClientWebSocketServerRunner : SpecificRunnerBase { - public IpcClientWebSocketServerRunner(string verbose) : base(verbose) { } + public IpcClientWebSocketServerRunner(LogLevel logLevel) : base(logLevel) { } public override void ConfigureLauncher(CancellationToken cancellationToken) { Launcher.SuspendProcess = true; Launcher.ConnectMode = true; - Launcher.Verbose = LogLevel != LogLevel.Information; + Launcher.Verbose = LogLevel < LogLevel.Information; Launcher.CommandToken = cancellationToken; } } public async Task RunIpcClientWebSocketServerRouter(CancellationToken token, string ipcClient, string webSocket, int runtimeTimeout, string verbose) { - IpcClientWebSocketServerRunner runner = new(verbose); + IpcClientWebSocketServerRunner runner = new(ParseLogLevel(verbose)); WebSocketServer.WebSocketServerImpl server = new(runner.LogLevel); @@ -347,27 +334,43 @@ public async Task RunIpcClientWebSocketServerRouter(CancellationToken token } } - public async Task RunIpcServerIOSSimulatorRouter(CancellationToken token, int runtimeTimeout, string verbose) + public async Task RunIpcServerIOSSimulatorRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - logDiagnosticPortsConfiguration("ios simulator", "127.0.0.1:9000", false, verbose); - return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "").ConfigureAwait(false); + if (info) + { + logRouterUsageInfo("ios simulator", "127.0.0.1:9000", true); + } + + return await RunIpcServerTcpClientRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "").ConfigureAwait(false); } - public async Task RunIpcServerIOSRouter(CancellationToken token, int runtimeTimeout, string verbose) + public async Task RunIpcServerIOSRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - logDiagnosticPortsConfiguration("ios device", "127.0.0.1:9000", true, verbose); + if (info) + { + logRouterUsageInfo("ios device", "127.0.0.1:9000", true); + } + return await RunIpcServerTcpClientRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "iOS").ConfigureAwait(false); } - public async Task RunIpcServerAndroidEmulatorRouter(CancellationToken token, int runtimeTimeout, string verbose) + public async Task RunIpcServerAndroidEmulatorRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - logDiagnosticPortsConfiguration("android emulator", "10.0.2.2:9000", false, verbose); + if (info) + { + logRouterUsageInfo("android emulator", "10.0.2.2:9000", false); + } + return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "").ConfigureAwait(false); } - public async Task RunIpcServerAndroidRouter(CancellationToken token, int runtimeTimeout, string verbose) + public async Task RunIpcServerAndroidRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - logDiagnosticPortsConfiguration("android emulator", "127.0.0.1:9000", false, verbose); + if (info) + { + logRouterUsageInfo("android device", "127.0.0.1:9000", false); + } + return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "Android").ConfigureAwait(false); } @@ -436,26 +439,71 @@ private static NetServerRouterFactory.CreateInstanceDelegate ChooseTcpServerRout return tcpServerRouterFactory; } - private static void logDiagnosticPortsConfiguration(string deviceName, string deviceTcpIpAddress, bool deviceListenMode, string verbose) + private static LogLevel ParseLogLevel(string verbose) { - StringBuilder message = new(); - - if (!string.IsNullOrEmpty(verbose)) + LogLevel logLevel; + if (string.Equals(verbose, "none", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.None; + } + else if (string.Equals(verbose, "critical", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Critical; + } + else if (string.Equals(verbose, "error", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Error; + } + else if (string.Equals(verbose, "warning", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Warning; + } + else if (string.Equals(verbose, "info", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Information; + } + else if (string.Equals(verbose, "debug", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Debug; + } + else if (string.Equals(verbose, "trace", StringComparison.OrdinalIgnoreCase)) + { + logLevel = LogLevel.Trace; + } + else { - deviceName = !string.IsNullOrEmpty(deviceName) ? $" on {deviceName} " : " "; - message.AppendLine($"Start an application{deviceName}with one of the following environment variables set:"); + throw new ArgumentException($"Unknown verbose log level, {verbose}"); } - string listenMode = deviceListenMode ? ",listen" : ",connect"; - message.AppendLine($"DOTNET_DiagnosticPorts={deviceTcpIpAddress},nosuspend{listenMode}"); - message.AppendLine($"DOTNET_DiagnosticPorts={deviceTcpIpAddress},suspend{listenMode}"); + return logLevel; + } + + private static void logRouterUsageInfo(string deviceName, string deviceTcpIpAddress, bool deviceListenMode) + { + StringBuilder message = new(); + string listenMode = deviceListenMode ? "listen" : "connect"; + int pid = Process.GetCurrentProcess().Id; + + message.AppendLine($"How to connect current dotnet-dsrouter pid={pid} with {deviceName} and diagnostics tooling."); + message.AppendLine($"Start an application on {deviceName} with ONE of the following environment variables set:"); + message.AppendLine("[Default Tracing]"); + message.AppendLine($"DOTNET_DiagnosticPorts={deviceTcpIpAddress},nosuspend,{listenMode}"); + message.AppendLine("[Startup Tracing]"); + message.AppendLine($"DOTNET_DiagnosticPorts={deviceTcpIpAddress},suspend,{listenMode}"); + message.AppendLine($"Run diagnotic tool connecting application on {deviceName} through dotnet-dsrouter pid={pid}:"); + message.AppendLine($"dotnet-trace collect -p {pid}"); + message.AppendLine($"See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dsrouter for additional details and examples."); + + ConsoleColor currentColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(message.ToString()); + Console.ForegroundColor = currentColor; } - private static void checkLoopbackOnly(string tcpServer) + private static void checkLoopbackOnly(string tcpServer, LogLevel logLevel) { - if (!string.IsNullOrEmpty(tcpServer) && !DiagnosticsServerRouterRunner.isLoopbackOnly(tcpServer)) + if (logLevel != LogLevel.None && !string.IsNullOrEmpty(tcpServer) && !DiagnosticsServerRouterRunner.isLoopbackOnly(tcpServer)) { StringBuilder message = new(); diff --git a/src/Tools/dotnet-dsrouter/Program.cs b/src/Tools/dotnet-dsrouter/Program.cs index 4ed1a3ee98..bc3b53281d 100644 --- a/src/Tools/dotnet-dsrouter/Program.cs +++ b/src/Tools/dotnet-dsrouter/Program.cs @@ -27,13 +27,13 @@ internal sealed class Program private delegate Task DiagnosticsServerIpcClientWebSocketServerRouterDelegate(CancellationToken ct, string ipcClient, string webSocket, int runtimeTimeoutS, string verbose); - private delegate Task DiagnosticsServerIpcServerIOSSimulatorRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose); + private delegate Task DiagnosticsServerIpcServerIOSSimulatorRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose, bool info); - private delegate Task DiagnosticsServerIpcServerIOSRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose); + private delegate Task DiagnosticsServerIpcServerIOSRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose, bool info); - private delegate Task DiagnosticsServerIpcServerAndroidEmulatorRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose); + private delegate Task DiagnosticsServerIpcServerAndroidEmulatorRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose, bool info); - private delegate Task DiagnosticsServerIpcServerAndroidRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose); + private delegate Task DiagnosticsServerIpcServerAndroidRouterDelegate(CancellationToken ct, int runtimeTimeoutS, string verbose, bool info); private static Command IpcClientTcpServerRouterCommand() => new( @@ -122,7 +122,7 @@ private static Command IOSSimulatorRouterCommand() => // Handler HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerIOSSimulatorRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerIOSSimulatorRouter).GetCommandHandler(), // Options - RuntimeTimeoutOption(), VerboseOption() + RuntimeTimeoutOption(), VerboseOption(), InfoOption() }; private static Command IOSRouterCommand() => @@ -135,7 +135,7 @@ private static Command IOSRouterCommand() => // Handler HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerIOSRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerIOSRouter).GetCommandHandler(), // Options - RuntimeTimeoutOption(), VerboseOption() + RuntimeTimeoutOption(), VerboseOption(), InfoOption() }; private static Command AndroidEmulatorRouterCommand() => @@ -148,7 +148,7 @@ private static Command AndroidEmulatorRouterCommand() => // Handler HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerAndroidEmulatorRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerAndroidEmulatorRouter).GetCommandHandler(), // Options - RuntimeTimeoutOption(), VerboseOption() + RuntimeTimeoutOption(), VerboseOption(), InfoOption() }; private static Command AndroidRouterCommand() => @@ -161,7 +161,7 @@ private static Command AndroidRouterCommand() => // Handler HandlerDescriptor.FromDelegate((DiagnosticsServerIpcServerAndroidRouterDelegate)new DiagnosticsServerRouterCommands().RunIpcServerAndroidRouter).GetCommandHandler(), // Options - RuntimeTimeoutOption(), VerboseOption() + RuntimeTimeoutOption(), VerboseOption(), InfoOption() }; private static Option IpcClientAddressOption() => @@ -226,9 +226,9 @@ private static Option RuntimeTimeoutOption() => private static Option VerboseOption() => new( aliases: new[] { "--verbose", "-v" }, - description: "Enable verbose logging (debug|trace)") + description: "Enable verbose logging (none|critical|error|warning|info|debug|trace)") { - Argument = new Argument(name: "verbose", getDefaultValue: () => "") + Argument = new Argument(name: "verbose", getDefaultValue: () => "info") }; private static Option ForwardPortOption() => @@ -239,13 +239,16 @@ private static Option ForwardPortOption() => Argument = new Argument(name: "forwardPort", getDefaultValue: () => "") }; + private static Option InfoOption() => + new( + aliases: new[] { "--info", "-i" }, + description: "Print info on how to use current dotnet-dsrouter instance with application and diagnostic tooling.") + { + Argument = new Argument(name: "info", getDefaultValue: () => false) + }; + private static int Main(string[] args) { - ConsoleColor currentColor = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("WARNING: dotnet-dsrouter is a development tool not intended for production environments." + Environment.NewLine); - Console.ForegroundColor = currentColor; - Parser parser = new CommandLineBuilder() .AddCommand(IpcClientTcpServerRouterCommand()) .AddCommand(IpcServerTcpServerRouterCommand()) @@ -267,6 +270,15 @@ private static int Main(string[] args) ProcessLauncher.Launcher.PrepareChildProcess(args); } + string verbose = parseResult.ValueForOption("-v"); + if (!string.Equals(verbose, "none", StringComparison.OrdinalIgnoreCase)) + { + ConsoleColor currentColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("WARNING: dotnet-dsrouter is a development tool not intended for production environments." + Environment.NewLine); + Console.ForegroundColor = currentColor; + } + return parser.InvokeAsync(args).Result; } } From fb1b03dfe9d16999d73cf2c1ffb03db4dd06275f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 13:02:36 +0000 Subject: [PATCH 13/82] [main] Update dependencies from microsoft/clrmd (#4281) [main] Update dependencies from microsoft/clrmd --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f3467e78d7..1baa957c56 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 8cc6f03fdbd9c79f0bf9ffbe0a788dca1a81348a - + https://github.com/microsoft/clrmd - 547545a301475a7cfd23ce8c2f6e24eddf55d83e + 1c8f0f18ea71856142b99cfd64ec2f25e6294dc3 - + https://github.com/microsoft/clrmd - 547545a301475a7cfd23ce8c2f6e24eddf55d83e + 1c8f0f18ea71856142b99cfd64ec2f25e6294dc3 diff --git a/eng/Versions.props b/eng/Versions.props index 21f43906b2..e3e42c138d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,7 @@ 5.0.0 6.0.0 - 3.0.447901 + 3.0.450101 16.11.27-beta1.23180.1 3.0.7 6.0.0 From f4e915c7e9b5869d9b3b646aadf87755061c6638 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 13:05:32 +0000 Subject: [PATCH 14/82] [main] Update dependencies from dotnet/aspnetcore (#4282) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1baa957c56..4a4e77f0b2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer f8a61a24ac843529a82a8f6ede35fc08a6fb8c35 - + https://github.com/dotnet/aspnetcore - ad432054a6a2d87beb2208880ee1e6fa35348ebc + 6416a017337c45efc42cb6774d377ddb28c2c117 - + https://github.com/dotnet/aspnetcore - ad432054a6a2d87beb2208880ee1e6fa35348ebc + 6416a017337c45efc42cb6774d377ddb28c2c117 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index e3e42c138d..3974605e1c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23478.17 8.0.0-rtm.23478.17 - 8.0.0-rtm.23480.4 - 8.0.0-rtm.23480.4 + 8.0.0-rtm.23501.1 + 8.0.0-rtm.23501.1 8.0.100-rtm.23474.2 From f30d9c57ce195663e7c6785c60a53a2a00bb638e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:31:12 +0000 Subject: [PATCH 15/82] [main] Update dependencies from dotnet/installer (#4283) [main] Update dependencies from dotnet/installer - Update Versions.props --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4a4e77f0b2..136ecc6b37 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,9 +27,9 @@ https://github.com/dotnet/arcade ccfe6da198c5f05534863bbb1bff66e830e0c6ab - + https://github.com/dotnet/installer - f8a61a24ac843529a82a8f6ede35fc08a6fb8c35 + 6dae8493a54d682ff64973e67bba2bd1333047a3 https://github.com/dotnet/aspnetcore diff --git a/eng/Versions.props b/eng/Versions.props index 3974605e1c..da0a4d3784 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -24,7 +24,7 @@ 8.0.0-rtm.23501.1 8.0.0-rtm.23501.1 - 8.0.100-rtm.23474.2 + 8.0.100-rtm.23479.3 @@ -35,7 +35,7 @@ $(MicrosoftNETCoreApp60Version) $(MicrosoftNETCoreApp70Version) - 8.0.0-rtm.23472.12 + 8.0.0-rtm.23477.9 From 415440718d1a44f95f6f5a38a028d3b8d9615ad9 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 2 Oct 2023 11:52:14 -0700 Subject: [PATCH 16/82] Remove the symstore dependency from Microsoft.Diagnostics.DebugServices (#4279) This is needed to reduce the conflicts/dependencies on loadable extensions. --- eng/Versions.props | 1 + .../Runtime.cs | 2 +- .../SymbolService.cs | 83 ++++++++++--------- .../ISymbolService.cs | 7 +- ...Microsoft.Diagnostics.DebugServices.csproj | 4 +- ...osoft.Diagnostics.ExtensionCommands.csproj | 3 +- src/SOS/SOS.Hosting/SOS.Hosting.csproj | 1 + .../SOS.Hosting/SymbolServiceExtensions.cs | 2 +- .../LibraryProviderWrapper.cs | 4 +- 9 files changed, 58 insertions(+), 49 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index da0a4d3784..7fec60baa8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -43,6 +43,7 @@ true 5.0.0 + 6.0.0 6.0.0 3.0.450101 diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs index 11b2308e2f..351bd02df7 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs @@ -252,7 +252,7 @@ private string DownloadFile(DebugLibraryInfo libraryInfo) if (key is not null) { // Now download the DAC module from the symbol server - filePath = _symbolService.DownloadFile(key); + filePath = _symbolService.DownloadFile(key.Index, key.FullPathName); } } else diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs index f764a4b336..4b5ad2b9ba 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs @@ -400,44 +400,9 @@ public string DownloadSymbolFile(IModule module) /// /// Download a file from the symbol stores/server. /// - /// index of the file to download - /// path to the downloaded file either in the cache or in the temp directory or null if error - public string DownloadFile(SymbolStoreKey key) - { - string downloadFilePath = null; - - if (IsSymbolStoreEnabled) - { - using SymbolStoreFile file = GetSymbolStoreFile(key); - if (file != null) - { - try - { - downloadFilePath = file.FileName; - - // Make sure the stream is at the beginning of the module - file.Stream.Position = 0; - - // If the downloaded doesn't already exists on disk in the cache, then write it to a temporary location. - if (!File.Exists(downloadFilePath)) - { - downloadFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + "-" + Path.GetFileName(key.FullPathName)); - using (Stream destinationStream = File.OpenWrite(downloadFilePath)) - { - file.Stream.CopyTo(destinationStream); - } - Trace.WriteLine($"Downloaded symbol file {key.FullPathName}"); - } - } - catch (Exception ex) when (ex is UnauthorizedAccessException or DirectoryNotFoundException) - { - Trace.TraceError("{0}: {1}", file.FileName, ex.Message); - downloadFilePath = null; - } - } - } - return downloadFilePath; - } + /// index to lookup on symbol server + /// the full path name of the file + public string DownloadFile(string index, string file) => DownloadFile(new SymbolStoreKey(index, file)); /// /// Returns the metadata for the assembly @@ -842,6 +807,48 @@ private string DownloadMachO(IModule module, KeyTypeFlags flags) return null; } + /// + /// Download a file from the symbol stores/server. + /// + /// index of the file to download + /// path to the downloaded file either in the cache or in the temp directory or null if error + private string DownloadFile(SymbolStoreKey key) + { + string downloadFilePath = null; + + if (IsSymbolStoreEnabled) + { + using SymbolStoreFile file = GetSymbolStoreFile(key); + if (file != null) + { + try + { + downloadFilePath = file.FileName; + + // Make sure the stream is at the beginning of the module + file.Stream.Position = 0; + + // If the downloaded doesn't already exists on disk in the cache, then write it to a temporary location. + if (!File.Exists(downloadFilePath)) + { + downloadFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + "-" + Path.GetFileName(key.FullPathName)); + using (Stream destinationStream = File.OpenWrite(downloadFilePath)) + { + file.Stream.CopyTo(destinationStream); + } + Trace.WriteLine($"Downloaded symbol file {key.FullPathName}"); + } + } + catch (Exception ex) when (ex is UnauthorizedAccessException or DirectoryNotFoundException) + { + Trace.TraceError("{0}: {1}", file.FileName, ex.Message); + downloadFilePath = null; + } + } + } + return downloadFilePath; + } + private static void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry) { // See spec: https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md diff --git a/src/Microsoft.Diagnostics.DebugServices/ISymbolService.cs b/src/Microsoft.Diagnostics.DebugServices/ISymbolService.cs index af5dc291c9..13e66f4a63 100644 --- a/src/Microsoft.Diagnostics.DebugServices/ISymbolService.cs +++ b/src/Microsoft.Diagnostics.DebugServices/ISymbolService.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.IO; -using Microsoft.SymbolStore; namespace Microsoft.Diagnostics.DebugServices { @@ -96,9 +95,9 @@ public interface ISymbolService /// /// Download a file from the symbol stores/server. /// - /// index of the file to download - /// path to the downloaded file either in the cache or in the temp directory or null if error - string DownloadFile(SymbolStoreKey key); + /// index to lookup on symbol server + /// the full path name of the file + string DownloadFile(string index, string file); /// /// Returns the metadata for the assembly diff --git a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj index 33f0fe3e90..e550154f29 100644 --- a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj +++ b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj @@ -13,8 +13,8 @@ true false - + - + diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj b/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj index a18bae9228..a7c8e842cc 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -16,6 +16,7 @@ + diff --git a/src/SOS/SOS.Hosting/SOS.Hosting.csproj b/src/SOS/SOS.Hosting/SOS.Hosting.csproj index 7fee627a72..df9961fb69 100644 --- a/src/SOS/SOS.Hosting/SOS.Hosting.csproj +++ b/src/SOS/SOS.Hosting/SOS.Hosting.csproj @@ -11,6 +11,7 @@ + diff --git a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs index aebf7bc0c2..6fe61d348c 100644 --- a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs +++ b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs @@ -120,7 +120,7 @@ public static int GetICorDebugMetadataLocator( if (symbolService.IsSymbolStoreEnabled) { SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize); - string localFilePath = symbolService.DownloadFile(key); + string localFilePath = symbolService.DownloadFile(key.Index, key.FullPathName); if (!string.IsNullOrWhiteSpace(localFilePath)) { localFilePath += "\0"; // null terminate the string diff --git a/src/tests/DbgShim.UnitTests/LibraryProviderWrapper.cs b/src/tests/DbgShim.UnitTests/LibraryProviderWrapper.cs index e3c2b99cdf..a1c54f93d4 100644 --- a/src/tests/DbgShim.UnitTests/LibraryProviderWrapper.cs +++ b/src/tests/DbgShim.UnitTests/LibraryProviderWrapper.cs @@ -331,7 +331,7 @@ private string DownloadModule(string moduleName, uint timeStamp, uint sizeOfImag Assert.True(timeStamp != 0 && sizeOfImage != 0); SymbolStoreKey key = PEFileKeyGenerator.GetKey(moduleName, timeStamp, sizeOfImage); Assert.NotNull(key); - string downloadedPath = SymbolService.DownloadFile(key); + string downloadedPath = SymbolService.DownloadFile(key.Index, key.FullPathName); Assert.NotNull(downloadedPath); return downloadedPath; } @@ -368,7 +368,7 @@ private string DownloadModule(string moduleName, byte[] buildId) key = MachOFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, moduleName, buildId, symbolFile: false, symbolFileName: null).SingleOrDefault(); } Assert.NotNull(key); - string downloadedPath = SymbolService.DownloadFile(key); + string downloadedPath = SymbolService.DownloadFile(key.Index, key.FullPathName); Assert.NotNull(downloadedPath); return downloadedPath; } From 4592978e0cc8c28c0fa1ebb2694638bc78bcaaee Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 2 Oct 2023 17:28:43 -0700 Subject: [PATCH 17/82] Add the "crashinfo" command for NativeAOT. (#4125) Reads the JSON crash info from the triage buffer in the NativeAOT runtime. Adds a ICrashInfoService and SpecialDiagInfo helper class. --- eng/Versions.props | 4 +- .../CrashInfoService.cs | 192 ++++++++++++++++++ .../DataReader.cs | 2 +- ...ostics.DebugServices.Implementation.csproj | 4 +- .../SpecialDiagInfo.cs | 137 +++++++++++++ .../Target.cs | 2 + .../TargetFromDataReader.cs | 4 + .../ICrashInfoService.cs | 59 ++++++ .../IManagedException.cs | 43 ++++ .../IRuntime.cs | 3 +- .../IStackFrame.cs | 36 ++++ .../IType.cs | 5 - .../CrashInfoCommand.cs | 81 ++++++++ src/SOS/SOS.Extensions/DebuggerServices.cs | 39 +++- .../ModuleServiceFromDebuggerServices.cs | 2 - src/SOS/SOS.Extensions/RemoteMemoryService.cs | 2 +- src/SOS/SOS.Extensions/SOS.Extensions.csproj | 2 +- .../TargetFromFromDebuggerServices.cs | 37 ++++ src/SOS/SOS.Hosting/RuntimeWrapper.cs | 30 +-- src/SOS/Strike/dbgengservices.cpp | 24 +++ src/SOS/Strike/dbgengservices.h | 13 +- src/SOS/Strike/sos.def | 1 + src/SOS/Strike/strike.cpp | 18 +- src/SOS/inc/debuggerservices.h | 11 + src/SOS/inc/specialdiaginfo.h | 31 +++ src/SOS/lldbplugin/services.cpp | 86 ++++---- src/SOS/lldbplugin/soscommand.cpp | 1 + src/SOS/lldbplugin/sosplugin.h | 1 + 28 files changed, 783 insertions(+), 87 deletions(-) create mode 100644 src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs create mode 100644 src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs create mode 100644 src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs create mode 100644 src/Microsoft.Diagnostics.DebugServices/IManagedException.cs create mode 100644 src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs create mode 100644 src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs create mode 100644 src/SOS/inc/specialdiaginfo.h diff --git a/eng/Versions.props b/eng/Versions.props index 7fec60baa8..bd4a6589eb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -59,8 +59,8 @@ 4.5.1 4.5.5 4.3.0 - 4.7.2 - 4.7.1 + 6.0.0 + 6.0.8 2.0.3 8.0.0-beta.23463.1 1.2.0-beta.406 diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs new file mode 100644 index 0000000000..a10c4001a3 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.Diagnostics.DebugServices.Implementation +{ + public class CrashInfoService : ICrashInfoService + { + /// + /// This is a "transport" exception code required by Watson to trigger the proper analyzer/provider for bucketing + /// + public const uint STATUS_STACK_BUFFER_OVERRUN = 0xC0000409; + + /// + /// This is the Native AOT fail fast subcode used by Watson + /// + public const uint FAST_FAIL_EXCEPTION_DOTNET_AOT = 0x48; + + public sealed class CrashInfoJson + { + [JsonPropertyName("version")] + public string Version { get; set; } + + [JsonPropertyName("reason")] + public int Reason { get; set; } + + [JsonPropertyName("runtime")] + public string Runtime { get; set; } + + [JsonPropertyName("runtime_type")] + public int RuntimeType { get; set; } + + [JsonPropertyName("thread")] + [JsonConverter(typeof(HexUInt32Converter))] + public uint Thread { get; set; } + + [JsonPropertyName("message")] + public string Message { get; set; } + + [JsonPropertyName("exception")] + public CrashInfoException Exception { get; set; } + } + + public sealed class CrashInfoException : IManagedException + { + [JsonPropertyName("address")] + [JsonConverter(typeof(HexUInt64Converter))] + public ulong Address { get; set; } + + [JsonPropertyName("hr")] + [JsonConverter(typeof(HexUInt32Converter))] + public uint HResult { get; set; } + + [JsonPropertyName("message")] + public string Message { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("stack")] + public CrashInfoStackFrame[] Stack { get; set; } + + IEnumerable IManagedException.Stack => Stack; + + [JsonPropertyName("inner")] + public CrashInfoException[] InnerExceptions { get; set; } + + IEnumerable IManagedException.InnerExceptions => InnerExceptions; + } + + public sealed class CrashInfoStackFrame : IStackFrame + { + [JsonPropertyName("ip")] + [JsonConverter(typeof(HexUInt64Converter))] + public ulong InstructionPointer { get; set; } + + [JsonPropertyName("sp")] + [JsonConverter(typeof(HexUInt64Converter))] + public ulong StackPointer { get; set; } + + [JsonPropertyName("module")] + [JsonConverter(typeof(HexUInt64Converter))] + public ulong ModuleBase { get; set; } + + [JsonPropertyName("offset")] + [JsonConverter(typeof(HexUInt32Converter))] + public uint Offset { get; set; } + + [JsonPropertyName("name")] + public string MethodName { get; set; } + } + + public sealed class HexUInt64Converter : JsonConverter + { + public override ulong Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string valueString = reader.GetString(); + if (valueString == null || + !valueString.StartsWith("0x") || + !ulong.TryParse(valueString.Substring(2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out ulong value)) + { + throw new JsonException("Invalid hex value"); + } + return value; + } + + public override void Write(Utf8JsonWriter writer, ulong value, JsonSerializerOptions options) => throw new NotImplementedException(); + } + + public sealed class HexUInt32Converter : JsonConverter + { + public override uint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string valueString = reader.GetString(); + if (valueString == null || + !valueString.StartsWith("0x") || + !uint.TryParse(valueString.Substring(2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out uint value)) + { + throw new JsonException("Invalid hex value"); + } + return value; + } + + public override void Write(Utf8JsonWriter writer, uint value, JsonSerializerOptions options) => throw new NotImplementedException(); + } + + public static ICrashInfoService Create(uint hresult, ReadOnlySpan triageBuffer) + { + CrashInfoService crashInfoService = null; + try + { + JsonSerializerOptions options = new() { AllowTrailingCommas = true, NumberHandling = JsonNumberHandling.AllowReadingFromString }; + CrashInfoJson crashInfo = JsonSerializer.Deserialize(triageBuffer, options); + if (crashInfo != null) + { + if (Version.TryParse(crashInfo.Version, out Version protocolVersion) && protocolVersion.Major >= 1) + { + crashInfoService = new(crashInfo.Thread, hresult, crashInfo); + } + else + { + Trace.TraceError($"CrashInfoService: invalid or not supported protocol version {crashInfo.Version}"); + } + } + else + { + Trace.TraceError($"CrashInfoService: JsonSerializer.Deserialize failed"); + } + } + catch (Exception ex) when (ex is JsonException or NotSupportedException or DecoderFallbackException or ArgumentException) + { + Trace.TraceError($"CrashInfoService: {ex}"); + } + return crashInfoService; + } + + private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo) + { + ThreadId = threadId; + HResult = hresult; + CrashReason = (CrashReason)crashInfo.Reason; + RuntimeVersion = crashInfo.Runtime; + RuntimeType = (RuntimeType)crashInfo.RuntimeType; + Message = crashInfo.Message; + Exception = crashInfo.Exception; + } + + #region ICrashInfoService + + public uint ThreadId { get; } + + public uint HResult { get; } + + public CrashReason CrashReason { get; } + + public string RuntimeVersion { get; } + + public RuntimeType RuntimeType { get; } + + public string Message { get; } + + public IManagedException Exception { get; } + + #endregion + } +} diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs index ba1ecc6d21..86e6120208 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs @@ -13,7 +13,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation { /// - /// ClrMD runtime service implementation + /// ClrMD runtime service implementation. This MUST never be disposable. /// [ServiceExport(Type = typeof(IDataReader), Scope = ServiceScope.Target)] public class DataReader : IDataReader diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj b/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj index 06c99c30c2..63facc266b 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj @@ -1,7 +1,8 @@ - + netstandard2.0 + true ;1591;1701 Diagnostics debug services true @@ -20,6 +21,7 @@ + diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs new file mode 100644 index 0000000000..32d7989a93 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// ****************************************************************************** +// WARNING!!!: This code is also used by createdump in the runtime repo. +// See: https://github.com/dotnet/runtime/blob/main/src/coreclr/debug/createdump/specialdiaginfo.h +// ****************************************************************************** + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.Diagnostics.DebugServices.Implementation +{ + /// + /// This is a special memory region added to ELF and MachO dumps that contains extra diagnostics + /// information like the exception record for a crash for a NativeAOT app. The exception record + /// contains the pointer to the JSON formatted crash info. + /// + public unsafe class SpecialDiagInfo + { + private static readonly byte[] SPECIAL_DIAGINFO_SIGNATURE = Encoding.ASCII.GetBytes("DIAGINFOHEADER"); + private const int SPECIAL_DIAGINFO_VERSION = 1; + + private const ulong SpecialDiagInfoAddressMacOS64 = 0x7fffffff10000000; + private const ulong SpecialDiagInfoAddress64 = 0x00007ffffff10000; + private const ulong SpecialDiagInfoAddress32 = 0x7fff1000; + + [StructLayout(LayoutKind.Sequential)] + private struct SpecialDiagInfoHeader + { + public const int SignatureSize = 16; + public fixed byte Signature[SignatureSize]; + public int Version; + public ulong ExceptionRecordAddress; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct EXCEPTION_RECORD64 + { + public uint ExceptionCode; + public uint ExceptionFlags; + public ulong ExceptionRecord; + public ulong ExceptionAddress; + public uint NumberParameters; + public uint __unusedAlignment; + public fixed ulong ExceptionInformation[15]; //EXCEPTION_MAXIMUM_PARAMETERS + } + + private readonly ITarget _target; + private readonly IMemoryService _memoryService; + + public SpecialDiagInfo(ITarget target, IMemoryService memoryService) + { + _target = target; + _memoryService = memoryService; + } + + private ulong SpecialDiagInfoAddress + { + get + { + if (_target.OperatingSystem == OSPlatform.OSX) + { + if (_memoryService.PointerSize == 8) + { + return SpecialDiagInfoAddressMacOS64; + } + } + else if (_target.OperatingSystem == OSPlatform.Linux) + { + if (_memoryService.PointerSize == 8) + { + return SpecialDiagInfoAddress64; + } + else + { + return SpecialDiagInfoAddress32; + } + } + return 0; + } + } + + public static ICrashInfoService CreateCrashInfoService(IServiceProvider services) + { + EXCEPTION_RECORD64 exceptionRecord; + + SpecialDiagInfo diagInfo = new(services.GetService(), services.GetService()); + exceptionRecord = diagInfo.GetExceptionRecord(); + + if (exceptionRecord.ExceptionCode == CrashInfoService.STATUS_STACK_BUFFER_OVERRUN && + exceptionRecord.NumberParameters >= 4 && + exceptionRecord.ExceptionInformation[0] == CrashInfoService.FAST_FAIL_EXCEPTION_DOTNET_AOT) + { + uint hresult = (uint)exceptionRecord.ExceptionInformation[1]; + ulong triageBufferAddress = exceptionRecord.ExceptionInformation[2]; + int triageBufferSize = (int)exceptionRecord.ExceptionInformation[3]; + + Span buffer = new byte[triageBufferSize]; + if (services.GetService().ReadMemory(triageBufferAddress, buffer, out int bytesRead) && bytesRead == triageBufferSize) + { + return CrashInfoService.Create(hresult, buffer); + } + else + { + Trace.TraceError($"SpecialDiagInfo: ReadMemory({triageBufferAddress}) failed"); + } + } + return null; + } + + internal EXCEPTION_RECORD64 GetExceptionRecord() + { + Span headerBuffer = stackalloc byte[Unsafe.SizeOf()]; + if (_memoryService.ReadMemory(SpecialDiagInfoAddress, headerBuffer, out int bytesRead) && bytesRead == headerBuffer.Length) + { + SpecialDiagInfoHeader header = Unsafe.As(ref MemoryMarshal.GetReference(headerBuffer)); + ReadOnlySpan signature = new(header.Signature, SPECIAL_DIAGINFO_SIGNATURE.Length); + if (signature.SequenceEqual(SPECIAL_DIAGINFO_SIGNATURE)) + { + if (header.Version >= SPECIAL_DIAGINFO_VERSION && header.ExceptionRecordAddress != 0) + { + Span exceptionRecordBuffer = stackalloc byte[Unsafe.SizeOf()]; + if (_memoryService.ReadMemory(header.ExceptionRecordAddress, exceptionRecordBuffer, out bytesRead) && bytesRead == exceptionRecordBuffer.Length) + { + return Unsafe.As(ref MemoryMarshal.GetReference(exceptionRecordBuffer)); + } + } + } + } + return default; + } + } +} diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Target.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Target.cs index 9150458413..9addd99b10 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Target.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Target.cs @@ -43,6 +43,8 @@ protected void Finished() Host.OnTargetCreate.Fire(this); } + protected void FlushService() => _serviceContainer?.RemoveService(typeof(T)); + #region ITarget /// diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs index 604205ae69..5266ea006c 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs @@ -66,6 +66,10 @@ public TargetFromDataReader(IDataReader dataReader, OSPlatform targetOS, IHost h return memoryService; }); + // Add optional crash info service (currently only for Native AOT on Linux/MacOS). + _serviceContainerFactory.AddServiceFactory((services) => SpecialDiagInfo.CreateCrashInfoService(services)); + OnFlushEvent.Register(() => FlushService()); + Finished(); } } diff --git a/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs b/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs new file mode 100644 index 0000000000..797fb75fd1 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// The kind or reason of crash for the triage JSON + /// + public enum CrashReason + { + Unknown = 0, + UnhandledException = 1, + EnvironmentFailFast = 2, + InternalFailFast = 3, + } + + /// + /// Crash information service. Details about the unhandled exception or crash. + /// + public interface ICrashInfoService + { + /// + /// The kind or reason for the crash + /// + CrashReason CrashReason { get; } + + /// + /// Crashing OS thread id + /// + uint ThreadId { get; } + + /// + /// The HRESULT passed to Watson + /// + uint HResult { get; } + + /// + /// Runtime type or flavor + /// + RuntimeType RuntimeType { get; } + + /// + /// Runtime version and possible commit id + /// + string RuntimeVersion { get; } + + /// + /// Crash or FailFast message + /// + string Message { get; } + + /// + /// The exception that caused the crash or null + /// + IManagedException Exception { get; } + } +} diff --git a/src/Microsoft.Diagnostics.DebugServices/IManagedException.cs b/src/Microsoft.Diagnostics.DebugServices/IManagedException.cs new file mode 100644 index 0000000000..f73b172e5c --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/IManagedException.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// Describes a managed exception + /// + public interface IManagedException + { + /// + /// Exception object address + /// + ulong Address { get; } + + /// + /// The exception type name + /// + string Type { get; } + + /// + /// The exception message + /// + string Message { get; } + + /// + /// Exception.HResult + /// + uint HResult { get; } + + /// + /// Stack trace of exception + /// + IEnumerable Stack { get; } + + /// + /// The inner exception or exceptions in the AggregateException case + /// + IEnumerable InnerExceptions { get; } + } +} diff --git a/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs b/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs index e369639384..5dd9e4ce93 100644 --- a/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs +++ b/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs @@ -14,7 +14,8 @@ public enum RuntimeType Desktop = 1, NetCore = 2, SingleFile = 3, - Other = 4 + NativeAOT = 4, + Other = 5 } /// diff --git a/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs b/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs new file mode 100644 index 0000000000..42f33eb7f6 --- /dev/null +++ b/src/Microsoft.Diagnostics.DebugServices/IStackFrame.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DebugServices +{ + /// + /// Describes a stack frame + /// + public interface IStackFrame + { + /// + /// The instruction pointer for this frame + /// + ulong InstructionPointer { get; } + + /// + /// The stack pointer of this frame or 0 + /// + ulong StackPointer { get; } + + /// + /// The module base of the IP + /// + public ulong ModuleBase { get; } + + /// + /// Offset from beginning of method + /// + uint Offset { get; } + + /// + /// The exception type name + /// + string MethodName { get; } + } +} diff --git a/src/Microsoft.Diagnostics.DebugServices/IType.cs b/src/Microsoft.Diagnostics.DebugServices/IType.cs index 28a1355709..6682f059f7 100644 --- a/src/Microsoft.Diagnostics.DebugServices/IType.cs +++ b/src/Microsoft.Diagnostics.DebugServices/IType.cs @@ -20,11 +20,6 @@ public interface IType /// IModule Module { get; } - /// - /// A list of all the fields in the type - /// - List Fields { get; } - /// /// Get a field by name /// diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs new file mode 100644 index 0000000000..994522dd2a --- /dev/null +++ b/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Diagnostics.DebugServices; + +namespace Microsoft.Diagnostics.ExtensionCommands +{ + [Command(Name = "crashinfo", Help = "Displays the crash details that created the dump.")] + public class CrashInfoCommand : CommandBase + { + [ServiceImport(Optional = true)] + public ICrashInfoService CrashInfo { get; set; } + + [ServiceImport] + public IModuleService ModuleService { get; set; } + + public override void Invoke() + { + if (CrashInfo == null) + { + throw new DiagnosticsException("No crash info to display"); + } + WriteLine(); + + WriteLine($"CrashReason: {CrashInfo.CrashReason}"); + WriteLine($"ThreadId: {CrashInfo.ThreadId:X4}"); + WriteLine($"HResult: {CrashInfo.HResult:X4}"); + WriteLine($"RuntimeType: {CrashInfo.RuntimeType}"); + WriteLine($"RuntimeVersion: {CrashInfo.RuntimeVersion}"); + WriteLine($"Message: {CrashInfo.Message}"); + + if (CrashInfo.Exception != null) + { + WriteLine("-----------------------------------------------"); + PrintException(CrashInfo.Exception, string.Empty); + } + } + + private void PrintException(IManagedException exception, string indent) + { + WriteLine($"{indent}Exception object: {exception.Address:X16}"); + WriteLine($"{indent}Exception type: {exception.Type}"); + WriteLine($"{indent}HResult: {exception.HResult:X8}"); + WriteLine($"{indent}Message: {exception.Message}"); + + if (exception.Stack != null && exception.Stack.Any()) + { + WriteLine($"{indent}StackTrace:"); + WriteLine($"{indent} IP Function"); + foreach (IStackFrame frame in exception.Stack) + { + string moduleName = ""; + if (frame.ModuleBase != 0) + { + IModule module = ModuleService.GetModuleFromBaseAddress(frame.ModuleBase); + if (module != null) + { + moduleName = Path.GetFileName(module.FileName); + } + } + string methodName = frame.MethodName ?? ""; + WriteLine($"{indent} {frame.InstructionPointer:X16} {moduleName}!{methodName} + 0x{frame.Offset:X}"); + } + } + + if (exception.InnerExceptions != null) + { + WriteLine("InnerExceptions:"); + foreach (IManagedException inner in exception.InnerExceptions) + { + WriteLine("-----------------------------------------------"); + PrintException(inner, " "); + } + } + } + } +} diff --git a/src/SOS/SOS.Extensions/DebuggerServices.cs b/src/SOS/SOS.Extensions/DebuggerServices.cs index 0df0a5fd4b..97ea031083 100644 --- a/src/SOS/SOS.Extensions/DebuggerServices.cs +++ b/src/SOS/SOS.Extensions/DebuggerServices.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,7 +13,7 @@ using Microsoft.Diagnostics.Runtime.Utilities; using SOS.Hosting.DbgEng.Interop; -namespace SOS +namespace SOS.Extensions { internal sealed unsafe class DebuggerServices : CallableCOMWrapper { @@ -424,6 +425,41 @@ public HResult AddModuleSymbol(string symbolFileName) } } + public HResult GetLastException(out uint processId, out int threadId, out EXCEPTION_RECORD64 exceptionRecord) + { + exceptionRecord = default; + + uint type; + HResult hr = VTable.GetLastEventInformation(Self, out type, out processId, out threadId, null, 0, null, null, 0, null); + if (hr.IsOK) + { + if (type != (uint)DEBUG_EVENT.EXCEPTION) + { + return HResult.E_FAIL; + } + } + + DEBUG_LAST_EVENT_INFO_EXCEPTION exceptionInfo; + hr = VTable.GetLastEventInformation( + Self, + out _, + out processId, + out threadId, + &exceptionInfo, + Unsafe.SizeOf(), + null, + null, + 0, + null); + + if (hr.IsOK) + { + exceptionRecord = exceptionInfo.ExceptionRecord; + } + Debug.Assert(hr != HResult.S_FALSE); + return hr; + } + [StructLayout(LayoutKind.Sequential)] private readonly unsafe struct IDebuggerServicesVTable { @@ -455,6 +491,7 @@ private readonly unsafe struct IDebuggerServicesVTable public readonly delegate* unmanaged[Stdcall] SupportsDml; public readonly delegate* unmanaged[Stdcall] OutputDmlString; public readonly delegate* unmanaged[Stdcall] AddModuleSymbol; + public readonly delegate* unmanaged[Stdcall] GetLastEventInformation; } } } diff --git a/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs b/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs index ed88c37705..4137ee1dfd 100644 --- a/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs @@ -49,8 +49,6 @@ public TypeFromDebuggerServices(ModuleServiceFromDebuggerServices moduleService, public string Name { get; } - public List Fields => throw new NotImplementedException(); - public bool TryGetField(string fieldName, out IField field) { HResult hr = _moduleService._debuggerServices.GetFieldOffset(Module.ModuleIndex, _typeId, Name, fieldName, out uint offset); diff --git a/src/SOS/SOS.Extensions/RemoteMemoryService.cs b/src/SOS/SOS.Extensions/RemoteMemoryService.cs index a6d24bcfa2..d844b6d44c 100644 --- a/src/SOS/SOS.Extensions/RemoteMemoryService.cs +++ b/src/SOS/SOS.Extensions/RemoteMemoryService.cs @@ -8,7 +8,7 @@ using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime.Utilities; -namespace SOS +namespace SOS.Extensions { internal sealed unsafe class RemoteMemoryService : CallableCOMWrapper, IRemoteMemoryService { diff --git a/src/SOS/SOS.Extensions/SOS.Extensions.csproj b/src/SOS/SOS.Extensions/SOS.Extensions.csproj index 5deb184576..444196d7b4 100644 --- a/src/SOS/SOS.Extensions/SOS.Extensions.csproj +++ b/src/SOS/SOS.Extensions/SOS.Extensions.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 SOS.Extensions diff --git a/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs b/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs index 71e4e60020..60aea85883 100644 --- a/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs @@ -7,6 +7,7 @@ using Microsoft.Diagnostics.DebugServices; using Microsoft.Diagnostics.DebugServices.Implementation; using Microsoft.Diagnostics.Runtime.Utilities; +using SOS.Hosting; using SOS.Hosting.DbgEng.Interop; using Architecture = System.Runtime.InteropServices.Architecture; @@ -94,7 +95,43 @@ internal TargetFromDebuggerServices(DebuggerServices debuggerServices, IHost hos return memoryService; }); + // Add optional crash info service (currently only for Native AOT). + _serviceContainerFactory.AddServiceFactory((services) => CreateCrashInfoService(services, debuggerServices)); + OnFlushEvent.Register(() => FlushService()); + Finished(); } + + private unsafe ICrashInfoService CreateCrashInfoService(IServiceProvider services, DebuggerServices debuggerServices) + { + // For Linux/OSX dumps loaded under dbgeng the GetLastException API doesn't return the necessary information + if (Host.HostType == HostType.DbgEng && (OperatingSystem == OSPlatform.Linux || OperatingSystem == OSPlatform.OSX)) + { + return SpecialDiagInfo.CreateCrashInfoService(services); + } + HResult hr = debuggerServices.GetLastException(out uint processId, out int threadIndex, out EXCEPTION_RECORD64 exceptionRecord); + if (hr.IsOK) + { + if (exceptionRecord.ExceptionCode == CrashInfoService.STATUS_STACK_BUFFER_OVERRUN && + exceptionRecord.NumberParameters >= 4 && + exceptionRecord.ExceptionInformation[0] == CrashInfoService.FAST_FAIL_EXCEPTION_DOTNET_AOT) + { + uint hresult = (uint)exceptionRecord.ExceptionInformation[1]; + ulong triageBufferAddress = exceptionRecord.ExceptionInformation[2]; + int triageBufferSize = (int)exceptionRecord.ExceptionInformation[3]; + + Span buffer = new byte[triageBufferSize]; + if (services.GetService().ReadMemory(triageBufferAddress, buffer, out int bytesRead) && bytesRead == triageBufferSize) + { + return CrashInfoService.Create(hresult, buffer); + } + else + { + Trace.TraceError($"CrashInfoService: ReadMemory({triageBufferAddress}) failed"); + } + } + } + return null; + } } } diff --git a/src/SOS/SOS.Hosting/RuntimeWrapper.cs b/src/SOS/SOS.Hosting/RuntimeWrapper.cs index 09c93312ed..c537b270e0 100644 --- a/src/SOS/SOS.Hosting/RuntimeWrapper.cs +++ b/src/SOS/SOS.Hosting/RuntimeWrapper.cs @@ -83,7 +83,6 @@ private delegate IntPtr LoadLibraryWDelegate( private readonly IServiceProvider _services; private readonly IRuntime _runtime; - private readonly IDisposable _onFlushEvent; private IntPtr _clrDataProcess = IntPtr.Zero; private IntPtr _corDebugProcess = IntPtr.Zero; private IntPtr _dacHandle = IntPtr.Zero; @@ -97,7 +96,6 @@ public RuntimeWrapper(IServiceProvider services, IRuntime runtime) Debug.Assert(runtime != null); _services = services; _runtime = runtime; - _onFlushEvent = runtime.Target.OnFlushEvent.Register(Flush); VTableBuilder builder = AddInterface(IID_IRuntime, validate: false); @@ -124,34 +122,26 @@ void IDisposable.Dispose() protected override void Destroy() { Trace.TraceInformation("RuntimeWrapper.Destroy"); - _onFlushEvent.Dispose(); - Flush(); - if (_dacHandle != IntPtr.Zero) - { - DataTarget.PlatformFunctions.FreeLibrary(_dacHandle); - _dacHandle = IntPtr.Zero; - } - if (_dbiHandle != IntPtr.Zero) - { - DataTarget.PlatformFunctions.FreeLibrary(_dbiHandle); - _dbiHandle = IntPtr.Zero; - } - } - - private void Flush() - { - // TODO: there is a better way to flush _corDebugProcess with ICorDebugProcess4::ProcessStateChanged(FLUSH_ALL) if (_corDebugProcess != IntPtr.Zero) { ComWrapper.ReleaseWithCheck(_corDebugProcess); _corDebugProcess = IntPtr.Zero; } - // TODO: there is a better way to flush _clrDataProcess with ICLRDataProcess::Flush() if (_clrDataProcess != IntPtr.Zero) { ComWrapper.ReleaseWithCheck(_clrDataProcess); _clrDataProcess = IntPtr.Zero; } + if (_dacHandle != IntPtr.Zero) + { + DataTarget.PlatformFunctions.FreeLibrary(_dacHandle); + _dacHandle = IntPtr.Zero; + } + if (_dbiHandle != IntPtr.Zero) + { + DataTarget.PlatformFunctions.FreeLibrary(_dbiHandle); + _dbiHandle = IntPtr.Zero; + } } #region IRuntime (native) diff --git a/src/SOS/Strike/dbgengservices.cpp b/src/SOS/Strike/dbgengservices.cpp index d538079f33..58187515b9 100644 --- a/src/SOS/Strike/dbgengservices.cpp +++ b/src/SOS/Strike/dbgengservices.cpp @@ -512,6 +512,30 @@ DbgEngServices::AddModuleSymbol( return S_OK; } +HRESULT +DbgEngServices::GetLastEventInformation( + PULONG type, + PULONG processId, + PULONG threadId, + PVOID extraInformation, + ULONG extraInformationSize, + PULONG extraInformationUsed, + PSTR description, + ULONG descriptionSize, + PULONG descriptionUsed) +{ + return m_control->GetLastEventInformation( + type, + processId, + threadId, + extraInformation, + extraInformationSize, + extraInformationUsed, + description, + descriptionSize, + descriptionUsed); +} + //---------------------------------------------------------------------------- // IRemoteMemoryService //---------------------------------------------------------------------------- diff --git a/src/SOS/Strike/dbgengservices.h b/src/SOS/Strike/dbgengservices.h index d8530646ca..f7ba96cac2 100644 --- a/src/SOS/Strike/dbgengservices.h +++ b/src/SOS/Strike/dbgengservices.h @@ -207,6 +207,17 @@ class DbgEngServices : public IDebuggerServices, public IRemoteMemoryService, pu void* param, const char* symbolFileName); + HRESULT STDMETHODCALLTYPE GetLastEventInformation( + PULONG type, + PULONG processId, + PULONG threadId, + PVOID extraInformation, + ULONG extraInformationSize, + PULONG extraInformationUsed, + PSTR description, + ULONG descriptionSize, + PULONG descriptionUsed); + //---------------------------------------------------------------------------- // IRemoteMemoryService //---------------------------------------------------------------------------- @@ -296,4 +307,4 @@ class DbgEngServices : public IDebuggerServices, public IRemoteMemoryService, pu #ifdef __cplusplus }; -#endif \ No newline at end of file +#endif diff --git a/src/SOS/Strike/sos.def b/src/SOS/Strike/sos.def index 0e9876503f..4b9b8fcd7d 100644 --- a/src/SOS/Strike/sos.def +++ b/src/SOS/Strike/sos.def @@ -11,6 +11,7 @@ EXPORTS ClrStack clrstack=ClrStack CLRStack=ClrStack + crashinfo DumpALC dumpalc=DumpALC DumpArray diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 20feea9e79..b6f36b5db4 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -5857,10 +5857,9 @@ BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle) return FALSE; } - // The new DAC based interface doesn't exists so ask the debugger for the last exception - // information. NOTE: this function doesn't work on xplat version when the coreclr symbols - // have been stripped. + // The new DAC based interface doesn't exists so ask the debugger for the last exception information. +#ifdef HOST_WINDOWS ULONG Type, ProcessId, ThreadId; ULONG ExtraInformationUsed; Status = g_ExtControl->GetLastEventInformation( @@ -5883,8 +5882,10 @@ BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle) { return FALSE; } - return TRUE; +#else + return FALSE; +#endif } HRESULT HandleCLRNotificationEvent() @@ -13799,6 +13800,15 @@ DECLARE_API(clrmodules) return ExecuteCommand("clrmodules", args); } +// +// Dumps the Native AOT crash info +// +DECLARE_API(crashinfo) +{ + INIT_API_EXT(); + return ExecuteCommand("crashinfo", args); +} + // // Dumps async stacks // diff --git a/src/SOS/inc/debuggerservices.h b/src/SOS/inc/debuggerservices.h index cc39b44ca6..1ec815f4a0 100644 --- a/src/SOS/inc/debuggerservices.h +++ b/src/SOS/inc/debuggerservices.h @@ -166,6 +166,17 @@ IDebuggerServices : public IUnknown virtual HRESULT STDMETHODCALLTYPE AddModuleSymbol( void* param, const char* symbolFileName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLastEventInformation( + PULONG type, + PULONG processId, + PULONG threadId, + PVOID extraInformation, + ULONG extraInformationSize, + PULONG extraInformationUsed, + PSTR description, + ULONG descriptionSize, + PULONG descriptionUsed) = 0; }; #ifdef __cplusplus diff --git a/src/SOS/inc/specialdiaginfo.h b/src/SOS/inc/specialdiaginfo.h new file mode 100644 index 0000000000..9dfb1e9145 --- /dev/null +++ b/src/SOS/inc/specialdiaginfo.h @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// ****************************************************************************** +// WARNING!!!: This code is also used by createdump in the runtime repo. +// See: https://github.com/dotnet/runtime/blob/main/src/coreclr/debug/createdump/specialdiaginfo.h +// ****************************************************************************** + +// This is a special memory region added to ELF and MachO dumps that contains extra diagnostics +// information like the exception record for a crash for a NativeAOT app. The exception record +// contains the pointer to the JSON formatted crash info. + +#define SPECIAL_DIAGINFO_SIGNATURE "DIAGINFOHEADER" +#define SPECIAL_DIAGINFO_VERSION 1 + +#ifdef __APPLE__ +const uint64_t SpecialDiagInfoAddress = 0x7fffffff10000000; +#else +#if TARGET_64BIT +const uint64_t SpecialDiagInfoAddress = 0x00007ffffff10000; +#else +const uint64_t SpecialDiagInfoAddress = 0x7fff1000; +#endif +#endif + +struct SpecialDiagInfoHeader +{ + char Signature[16]; + int32_t Version; + uint64_t ExceptionRecordAddress; +}; diff --git a/src/SOS/lldbplugin/services.cpp b/src/SOS/lldbplugin/services.cpp index e23dc1e382..1ecc095710 100644 --- a/src/SOS/lldbplugin/services.cpp +++ b/src/SOS/lldbplugin/services.cpp @@ -471,12 +471,6 @@ LLDBServices::Execute( return status <= lldb::eReturnStatusSuccessContinuingResult ? S_OK : E_FAIL; } -// PAL raise exception function and exception record pointer variable name -// See coreclr\src\pal\src\exception\seh-unwind.cpp for the details. This -// function depends on RtlpRaisException not being inlined or optimized. -#define FUNCTION_NAME "RtlpRaiseException" -#define VARIABLE_NAME "ExceptionRecord" - HRESULT LLDBServices::GetLastEventInformation( PULONG type, @@ -489,8 +483,7 @@ LLDBServices::GetLastEventInformation( ULONG descriptionSize, PULONG descriptionUsed) { - if (extraInformationSize < sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION) || - type == NULL || processId == NULL || threadId == NULL || extraInformationUsed == NULL) + if (type == NULL || processId == NULL || threadId == NULL) { return E_INVALIDARG; } @@ -498,10 +491,25 @@ LLDBServices::GetLastEventInformation( *type = DEBUG_EVENT_EXCEPTION; *processId = 0; *threadId = 0; - *extraInformationUsed = sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION); + + if (extraInformationUsed != nullptr) + { + *extraInformationUsed = sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION); + } + + if (extraInformation == nullptr) + { + return S_OK; + } + + if (extraInformationSize < sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION)) + { + return E_INVALIDARG; + } DEBUG_LAST_EVENT_INFO_EXCEPTION *pdle = (DEBUG_LAST_EVENT_INFO_EXCEPTION *)extraInformation; pdle->FirstChance = 1; + lldb::SBError error; lldb::SBProcess process = GetCurrentProcess(); if (!process.IsValid()) @@ -518,47 +526,31 @@ LLDBServices::GetLastEventInformation( *processId = GetProcessId(process); *threadId = GetThreadId(thread); - // Enumerate each stack frame at the special "throw" - // breakpoint and find the raise exception function - // with the exception record parameter. - int numFrames = thread.GetNumFrames(); - for (int i = 0; i < numFrames; i++) + SpecialDiagInfoHeader header; + size_t read = process.ReadMemory(SpecialDiagInfoAddress, &header, sizeof(header), error); + if (error.Fail() || read != sizeof(header)) { - lldb::SBFrame frame = thread.GetFrameAtIndex(i); - if (!frame.IsValid()) - { - break; - } - - const char *functionName = frame.GetFunctionName(); - if (functionName == NULL || strncmp(functionName, FUNCTION_NAME, sizeof(FUNCTION_NAME) - 1) != 0) - { - continue; - } - - lldb::SBValue exValue = frame.FindVariable(VARIABLE_NAME); - if (!exValue.IsValid()) - { - break; - } - - lldb::SBError error; - ULONG64 pExceptionRecord = exValue.GetValueAsUnsigned(error); - if (error.Fail()) - { - break; - } - - process.ReadMemory(pExceptionRecord, &pdle->ExceptionRecord, sizeof(pdle->ExceptionRecord), error); - if (error.Fail()) - { - break; - } - - return S_OK; + Output(DEBUG_OUTPUT_WARNING, "Special diagnostics info read failed\n"); + return E_FAIL; + } + if (strncmp(header.Signature, SPECIAL_DIAGINFO_SIGNATURE, sizeof(SPECIAL_DIAGINFO_SIGNATURE)) != 0) + { + Output(DEBUG_OUTPUT_WARNING, "Special diagnostics info signature invalid\n"); + return E_FAIL; + } + if (header.Version < SPECIAL_DIAGINFO_VERSION || header.ExceptionRecordAddress == 0) + { + Output(DEBUG_OUTPUT_WARNING, "No exception record in special diagnostics info\n"); + return E_FAIL; + } + read = process.ReadMemory(header.ExceptionRecordAddress, &pdle->ExceptionRecord, sizeof(pdle->ExceptionRecord), error); + if (error.Fail() || read != sizeof(pdle->ExceptionRecord)) + { + Output(DEBUG_OUTPUT_WARNING, "Exception record in special diagnostics info read failed\n"); + return E_FAIL; } - return E_FAIL; + return S_OK; } HRESULT diff --git a/src/SOS/lldbplugin/soscommand.cpp b/src/SOS/lldbplugin/soscommand.cpp index 7f79ec8bcb..cdb5c55376 100644 --- a/src/SOS/lldbplugin/soscommand.cpp +++ b/src/SOS/lldbplugin/soscommand.cpp @@ -161,6 +161,7 @@ sosCommandInitialize(lldb::SBDebugger debugger) g_services->AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only."); g_services->AddCommand("clrthreads", new sosCommand("Threads"), "Lists the managed threads running."); g_services->AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method."); + g_services->AddManagedCommand("crashinfo", "Displays the Native AOT crash info."); g_services->AddCommand("dbgout", new sosCommand("dbgout"), "Enables/disables (-off) internal SOS logging."); g_services->AddCommand("dumpalc", new sosCommand("DumpALC"), "Displays details about a collectible AssemblyLoadContext to which the specified object is loaded."); g_services->AddCommand("dumparray", new sosCommand("DumpArray"), "Displays details about a managed array."); diff --git a/src/SOS/lldbplugin/sosplugin.h b/src/SOS/lldbplugin/sosplugin.h index ad3b2395e1..4f387de027 100644 --- a/src/SOS/lldbplugin/sosplugin.h +++ b/src/SOS/lldbplugin/sosplugin.h @@ -9,6 +9,7 @@ #include "lldbservices.h" #include "extensions.h" #include "dbgtargetcontext.h" +#include "specialdiaginfo.h" #include "specialthreadinfo.h" #include "services.h" From db17b5f2f4f7702f2f125f587388df35603e9eca Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:14:33 +0000 Subject: [PATCH 18/82] [main] Update dependencies from dotnet/source-build-reference-packages (#4289) [main] Update dependencies from dotnet/source-build-reference-packages --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 136ecc6b37..8a252816a6 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -47,9 +47,9 @@ https://github.com/dotnet/runtime b20f704cc00f390e5560a137deb8f0e64e863e1d - + https://github.com/dotnet/source-build-reference-packages - 0650b50b2a5263c735d12b5c36c5deb34e7e6b60 + 05ffbf9df6c1dc621665ee1864874c4fe6de874c diff --git a/eng/Versions.props b/eng/Versions.props index bd4a6589eb..410a1ad89f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -67,7 +67,7 @@ 7.0.0-beta.22316.2 10.0.18362 13.0.1 - 9.0.0-alpha.1.23475.1 + 9.0.0-alpha.1.23502.1 3.11.0 From dc9ee787000e0aad388a9e97c49277f9fa537d18 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:21:16 +0000 Subject: [PATCH 19/82] [main] Update dependencies from dotnet/aspnetcore (#4286) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8a252816a6..8fcb75e33c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 6dae8493a54d682ff64973e67bba2bd1333047a3 - + https://github.com/dotnet/aspnetcore - 6416a017337c45efc42cb6774d377ddb28c2c117 + f37eb785c92218e668cf98e8c50139c1395492ad - + https://github.com/dotnet/aspnetcore - 6416a017337c45efc42cb6774d377ddb28c2c117 + f37eb785c92218e668cf98e8c50139c1395492ad https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 410a1ad89f..26b50b17a9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23478.17 8.0.0-rtm.23478.17 - 8.0.0-rtm.23501.1 - 8.0.0-rtm.23501.1 + 8.0.0-rtm.23502.22 + 8.0.0-rtm.23502.22 8.0.100-rtm.23479.3 From 50495328e3422fc5efb72b0f37402e81dbbf1100 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:29:48 -0700 Subject: [PATCH 20/82] [main] Update dependencies from dotnet/runtime (#4288) This pull request updates the following dependencies [marker]: <> (Begin:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) ## From https://github.com/dotnet/runtime - **Subscription**: e4bfb556-e13c-47f6-eb5a-08d8e4d5099b - **Build**: 20231002.11 - **Date Produced**: October 3, 2023 11:06:54 AM UTC - **Commit**: ef6283ac0a14c78d9e9fef4841545099bd7ad12b - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.NETCore.App.Runtime.win-x64**: [from 8.0.0-rtm.23478.17 to 8.0.0-rtm.23502.11][1] - **VS.Redist.Common.NetCore.SharedFramework.x64.8.0**: [from 8.0.0-rtm.23478.17 to 8.0.0-rtm.23502.11][1] [1]: https://github.com/dotnet/runtime/compare/b20f704cc0...ef6283ac0a [DependencyUpdate]: <> (End) [marker]: <> (End:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8fcb75e33c..ce129e08f9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore f37eb785c92218e668cf98e8c50139c1395492ad - + https://github.com/dotnet/runtime - b20f704cc00f390e5560a137deb8f0e64e863e1d + ef6283ac0a14c78d9e9fef4841545099bd7ad12b - + https://github.com/dotnet/runtime - b20f704cc00f390e5560a137deb8f0e64e863e1d + ef6283ac0a14c78d9e9fef4841545099bd7ad12b https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index 26b50b17a9..3e571bc2c5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23478.17 - 8.0.0-rtm.23478.17 + 8.0.0-rtm.23502.11 + 8.0.0-rtm.23502.11 8.0.0-rtm.23502.22 8.0.0-rtm.23502.22 From 78cb36fb21a5a07302b6cd2150cf420567478f0f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:15:09 +0000 Subject: [PATCH 21/82] [main] Update dependencies from dotnet/aspnetcore (#4291) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ce129e08f9..2f49df6201 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 6dae8493a54d682ff64973e67bba2bd1333047a3 - + https://github.com/dotnet/aspnetcore - f37eb785c92218e668cf98e8c50139c1395492ad + 2d2f8808674c3f5fe73ee64ce1d519d5e9e0b659 - + https://github.com/dotnet/aspnetcore - f37eb785c92218e668cf98e8c50139c1395492ad + 2d2f8808674c3f5fe73ee64ce1d519d5e9e0b659 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 3e571bc2c5..3a03472204 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23502.11 8.0.0-rtm.23502.11 - 8.0.0-rtm.23502.22 - 8.0.0-rtm.23502.22 + 8.0.0-rtm.23503.8 + 8.0.0-rtm.23503.8 8.0.100-rtm.23479.3 From f513150204315b2b7ec02b8f7e7dbc1a563098ef Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 08:55:51 -0700 Subject: [PATCH 22/82] [main] Update dependencies from dotnet/runtime (#4292) This pull request updates the following dependencies [marker]: <> (Begin:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) ## From https://github.com/dotnet/runtime - **Subscription**: e4bfb556-e13c-47f6-eb5a-08d8e4d5099b - **Build**: 20231003.15 - **Date Produced**: October 4, 2023 5:51:18 AM UTC - **Commit**: a84f8ffbf5d597b8a91e893a1f413466de017a68 - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.NETCore.App.Runtime.win-x64**: [from 8.0.0-rtm.23502.11 to 8.0.0-rtm.23503.15][1] - **VS.Redist.Common.NetCore.SharedFramework.x64.8.0**: [from 8.0.0-rtm.23502.11 to 8.0.0-rtm.23503.15][1] [1]: https://github.com/dotnet/runtime/compare/ef6283ac0a...a84f8ffbf5 [DependencyUpdate]: <> (End) [marker]: <> (End:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2f49df6201..0bd243ed52 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore 2d2f8808674c3f5fe73ee64ce1d519d5e9e0b659 - + https://github.com/dotnet/runtime - ef6283ac0a14c78d9e9fef4841545099bd7ad12b + a84f8ffbf5d597b8a91e893a1f413466de017a68 - + https://github.com/dotnet/runtime - ef6283ac0a14c78d9e9fef4841545099bd7ad12b + a84f8ffbf5d597b8a91e893a1f413466de017a68 https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index 3a03472204..8866433d90 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23502.11 - 8.0.0-rtm.23502.11 + 8.0.0-rtm.23503.15 + 8.0.0-rtm.23503.15 8.0.0-rtm.23503.8 8.0.0-rtm.23503.8 From bbf3e7b34120a30acf7734a5983d629d72fd4e83 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 4 Oct 2023 16:17:50 -0700 Subject: [PATCH 23/82] Command groups to support duplicate command names and better help support (#4285) Add extension load testing that includes duplicate command names. Add internal CommandGroup class to CommandService dotnet-dump analyze -c/--command will exit on any parsing errors or exceptions in the command command help not displayed on parsing errors/invalid options Add INIT_API_CHECK_MANAGED native SOS command macro Rename clrmodules command to assemblies and keep clrmodules as an alias Support static HelpInvoke/FilterInvoke methods Add command service testing Better command service help interface. Returns help text instead of printing it on the console directly. ICommandService.DisplayHelp() => GetHelpText(). Better help sorting. Fix some SOS scripts Return error code for command line option errors in native SOS Load next to executing assembly first when hosted under desktop Framework Test using "clrthreads" instead of "Threads" Replace testing "u" with "clru" Add more general command filter mechanism. Add FilterType property to Command attribute. Remove OS filter command flags. NativeAddressHelper service work Move Windows managed command stubs to separate file Add SOS.Hosting services to SOS.Extensions for better native command help. This requires the special ManagedOnlyCommandFilter service to prevent recursion of the C++ commands. On Windows this recursion is from "C++ command" -> "checking if managed version" -> "executing the command in SOS.Hosting" -> "C++ command". Help is now uniform across managed/native, alphabetized and filtered by the current runtime. --- diagnostics.sln | 45 +- .../AssemblyResolver.cs | 18 +- .../CommandService.cs | 704 ++++++++++++------ .../ServiceManager.cs | 4 + .../Utilities.cs | 46 +- .../CommandAttributes.cs | 47 +- .../DiagnosticsException.cs | 32 +- .../ICommandService.cs | 16 +- .../ProviderExportAttribute.cs | 2 +- .../ServiceContainer.cs | 5 +- .../AnalyzeOOMCommand.cs | 7 +- ...ModulesCommand.cs => AssembliesCommand.cs} | 19 +- .../ClrMDHelper.cs | 2 +- .../ClrMDHelperCommandBase.cs | 19 + .../ClrRuntimeCommandBase.cs | 19 + .../DumpAsyncCommand.cs | 61 +- .../DumpConcurrentDictionaryCommand.cs | 24 +- .../DumpConcurrentQueueCommand.cs | 79 +- .../DumpExceptionsCommand.cs | 5 +- .../DumpGenCommand.cs | 21 +- .../DumpHeapCommand.cs | 7 +- ...CRefsHelper.cs => DumpObjGCRefsCommand.cs} | 5 +- .../DumpRuntimeTypeCommand.cs | 7 +- .../DumpStackObjectsCommand.cs | 9 +- .../EEHeapCommand.cs | 7 +- .../ExtensionCommandBase.cs | 35 - .../ExtensionMethodHelpers.cs | 1 + .../FinalizeQueueCommand.cs | 8 +- .../FindEphemeralReferencesToLOHCommand.cs | 5 +- .../FindPointersInCommand.cs | 18 +- .../FindReferencesToEphemeralCommand.cs | 6 +- .../GCHeapStatCommand.cs | 7 +- .../GCRootCommand.cs | 10 +- .../GCToNativeCommand.cs | 22 +- .../GCWhereCommand.cs | 7 +- .../Host/ConsoleLoggingCommand.cs | 4 +- .../Host/HelpCommand.cs | 20 +- .../Host/LoggingCommand.cs | 2 +- .../Host/RegistersCommand.cs | 2 +- .../Host/SetSymbolServerCommand.cs | 99 ++- .../ListNearObjCommand.cs | 7 +- .../MAddressCommand.cs | 14 +- .../NativeAddressHelper.cs | 20 +- .../NotReachableInRangeCommand.cs | 5 +- .../ObjSizeCommand.cs | 7 +- .../ParallelStacksCommand.cs | 84 +-- .../PathToCommand.cs | 7 +- .../SimulateGCHeapCorruption.cs | 5 +- .../SizeStatsCommand.cs | 5 +- .../TaskStateCommand.cs | 34 +- .../ThreadPoolCommand.cs | 7 +- .../ThreadPoolQueueCommand.cs | 68 +- .../TimersCommand.cs | 49 +- .../TraverseHeapCommand.cs | 7 +- .../VerifyHeapCommand.cs | 7 +- .../VerifyObjectCommand.cs | 9 +- .../ConsoleService.cs | 10 +- src/Microsoft.Diagnostics.Repl/ExitCommand.cs | 2 +- .../TestHost/TestDump.cs | 11 + .../TestHost/TestHost.cs | 9 +- src/SOS/SOS.Extensions/DebuggerServices.cs | 10 +- src/SOS/SOS.Extensions/HostServices.cs | 94 +-- ...MemoryRegionServiceFromDebuggerServices.cs | 4 +- .../TargetFromFromDebuggerServices.cs | 5 + src/SOS/SOS.Hosting/Commands/SOSCommand.cs | 79 +- src/SOS/SOS.Hosting/SOSHost.cs | 77 +- src/SOS/SOS.Hosting/SOSLibrary.cs | 124 ++- .../Unix/Debugger.Tests.Config.txt | 10 + .../Windows/Debugger.Tests.Config.txt | 17 + src/SOS/SOS.UnitTests/SOS.UnitTests.csproj | 1 + src/SOS/SOS.UnitTests/SOS.cs | 12 +- src/SOS/SOS.UnitTests/SOSRunner.cs | 54 +- .../Scripts/ConcurrentDictionaries.script | 6 +- src/SOS/SOS.UnitTests/Scripts/DivZero.script | 5 - src/SOS/SOS.UnitTests/Scripts/DumpGen.script | 8 +- src/SOS/SOS.UnitTests/Scripts/GCTests.script | 2 +- src/SOS/SOS.UnitTests/Scripts/LineNums.script | 5 - .../Scripts/NestedExceptionTest.script | 5 - src/SOS/SOS.UnitTests/Scripts/Overflow.script | 5 - .../SOS.UnitTests/Scripts/Reflection.script | 5 - .../SOS.UnitTests/Scripts/SimpleThrow.script | 5 - .../Scripts/StackAndOtherTests.script | 11 +- .../Scripts/TestExtensions.script | 15 + src/SOS/SOS.UnitTests/Scripts/WebApp.script | 7 +- src/SOS/Strike/CMakeLists.txt | 1 + src/SOS/Strike/Strike.vcxproj | 1 + src/SOS/Strike/Strike.vcxproj.filters | 3 +- src/SOS/Strike/exts.cpp | 92 ++- src/SOS/Strike/exts.h | 108 +-- src/SOS/Strike/gchist.cpp | 25 +- src/SOS/Strike/managedcommands.cpp | 222 ++++++ src/SOS/Strike/sos.def | 9 +- src/SOS/Strike/sos_unixexports.src | 16 - src/SOS/Strike/sosdocs.txt | 27 +- src/SOS/Strike/sosdocsunix.txt | 56 -- src/SOS/Strike/strike.cpp | 655 +++++----------- src/SOS/Strike/util.h | 2 - src/SOS/inc/hostservices.h | 8 - src/SOS/lldbplugin/soscommand.cpp | 6 +- src/Tools/dotnet-dump/Analyzer.cs | 58 +- .../dotnet-dump/Commands/ReadMemoryCommand.cs | 2 +- src/Tools/dotnet-dump/Commands/SOSCommand.cs | 51 +- .../CommandServiceTests.cs | 132 ++++ .../DebugServicesTests.cs | 22 +- .../TestCommands.cs | 75 ++ src/tests/TestExtension/TestCommands.cs | 118 +++ src/tests/TestExtension/TestExtension.csproj | 11 + 107 files changed, 2418 insertions(+), 1671 deletions(-) rename src/Microsoft.Diagnostics.ExtensionCommands/{ClrModulesCommand.cs => AssembliesCommand.cs} (79%) create mode 100644 src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelperCommandBase.cs create mode 100644 src/Microsoft.Diagnostics.ExtensionCommands/ClrRuntimeCommandBase.cs rename src/Microsoft.Diagnostics.ExtensionCommands/{DumpObjGCRefsHelper.cs => DumpObjGCRefsCommand.cs} (96%) delete mode 100644 src/Microsoft.Diagnostics.ExtensionCommands/ExtensionCommandBase.cs create mode 100644 src/SOS/SOS.UnitTests/Scripts/TestExtensions.script create mode 100644 src/SOS/Strike/managedcommands.cpp create mode 100644 src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/CommandServiceTests.cs create mode 100644 src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/TestCommands.cs create mode 100644 src/tests/TestExtension/TestCommands.cs create mode 100644 src/tests/TestExtension/TestExtension.csproj diff --git a/diagnostics.sln b/diagnostics.sln index c53cec4f3f..23ae2ff91a 100644 --- a/diagnostics.sln +++ b/diagnostics.sln @@ -266,7 +266,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonTestRunner", "src\tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetStack.UnitTests", "src\tests\dotnet-stack\DotnetStack.UnitTests.csproj", "{E8F133F8-4D20-475D-9D16-2BA236DAB65F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Diagnostics.WebSocketServer", "src\Microsoft.Diagnostics.WebSocketServer\Microsoft.Diagnostics.WebSocketServer.csproj", "{1043FA82-37CC-4809-80DC-C1EB06A55133}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.WebSocketServer", "src\Microsoft.Diagnostics.WebSocketServer\Microsoft.Diagnostics.WebSocketServer.csproj", "{1043FA82-37CC-4809-80DC-C1EB06A55133}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestExtension", "src\tests\TestExtension\TestExtension.csproj", "{C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1907,6 +1909,46 @@ Global {1043FA82-37CC-4809-80DC-C1EB06A55133}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU {1043FA82-37CC-4809-80DC-C1EB06A55133}.RelWithDebInfo|x86.ActiveCfg = Debug|Any CPU {1043FA82-37CC-4809-80DC-C1EB06A55133}.RelWithDebInfo|x86.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|Any CPU.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|ARM.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|ARM.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|ARM64.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|ARM64.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|x64.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|x64.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|x86.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Checked|x86.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|ARM.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|ARM64.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|x64.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|x64.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|x86.ActiveCfg = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Debug|x86.Build.0 = Debug|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|Any CPU.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|ARM.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|ARM.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|ARM64.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|ARM64.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|x64.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|x64.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|x86.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.Release|x86.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E}.RelWithDebInfo|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1966,6 +2008,7 @@ Global {DFF48CB6-4504-41C6-A8F1-F4A3D316D49F} = {03479E19-3F18-49A6-910A-F5041E27E7C0} {E8F133F8-4D20-475D-9D16-2BA236DAB65F} = {03479E19-3F18-49A6-910A-F5041E27E7C0} {1043FA82-37CC-4809-80DC-C1EB06A55133} = {19FAB78C-3351-4911-8F0C-8C6056401740} + {C6EB3C21-FDFF-4CF0-BE3A-3D1A3924408E} = {03479E19-3F18-49A6-910A-F5041E27E7C0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0} diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/AssemblyResolver.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/AssemblyResolver.cs index 95eb3690dc..626b31082f 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/AssemblyResolver.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/AssemblyResolver.cs @@ -37,6 +37,15 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven string probingPath; Assembly assembly; + // Look next to the executing assembly + probingPath = Path.Combine(_defaultAssembliesPath, fileName); + Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); + if (Probe(probingPath, referenceName.Version, out assembly)) + { + Debug.WriteLine($"Matched {probingPath} based on ExecutingAssembly"); + return assembly; + } + // Look next to requesting assembly assemblyPath = args.RequestingAssembly?.Location; if (!string.IsNullOrEmpty(assemblyPath)) @@ -50,15 +59,6 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven } } - // Look next to the executing assembly - probingPath = Path.Combine(_defaultAssembliesPath, fileName); - Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); - if (Probe(probingPath, referenceName.Version, out assembly)) - { - Debug.WriteLine($"Matched {probingPath} based on ExecutingAssembly"); - return assembly; - } - return null; } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/CommandService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/CommandService.cs index 01e4cfdb61..2c87c85141 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/CommandService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/CommandService.cs @@ -9,10 +9,9 @@ using System.CommandLine.Invocation; using System.CommandLine.IO; using System.CommandLine.Parsing; -using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; +using System.Text; using System.Threading.Tasks; namespace Microsoft.Diagnostics.DebugServices.Implementation @@ -22,9 +21,8 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation /// public class CommandService : ICommandService { - private Parser _parser; - private readonly CommandLineBuilder _rootBuilder; - private readonly Dictionary _commandHandlers = new(); + private readonly List _commandGroups = new(); + private readonly string _commandPrompt; /// /// Create an instance of the command processor; @@ -32,8 +30,10 @@ public class CommandService : ICommandService /// command prompted used in help message public CommandService(string commandPrompt = null) { - _rootBuilder = new CommandLineBuilder(new Command(commandPrompt ?? ">")); - _rootBuilder.UseHelpBuilder((bindingContext) => new LocalHelpBuilder(this, bindingContext.Console, useHelpBuilder: false)); + _commandPrompt = commandPrompt ?? ">"; + + // Create default command group (should always be last in this list) + _commandGroups.Add(new CommandGroup(_commandPrompt)); } /// @@ -41,125 +41,168 @@ public CommandService(string commandPrompt = null) /// /// command line text /// services for the command - /// true success, false failure + /// true - found command, false - command not found + /// empty command line + /// other errors + /// parsing error public bool Execute(string commandLine, IServiceProvider services) { - // Parse the command line and invoke the command - ParseResult parseResult = Parser.Parse(commandLine); + string[] commandLineArray = CommandLineStringSplitter.Instance.Split(commandLine).ToArray(); + if (commandLineArray.Length <= 0) + { + throw new ArgumentException("Empty command line", nameof(commandLine)); + } + string commandName = commandLineArray[0].Trim(); + return Execute(commandName, commandLineArray, services); + } + + /// + /// Parse and execute the command. + /// + /// command name + /// command arguments/options + /// services for the command + /// true - found command, false - command not found + /// empty command name or arguments + /// other errors + /// parsing error + public bool Execute(string commandName, string commandArguments, IServiceProvider services) + { + commandName = commandName.Trim(); + string[] commandLineArray = CommandLineStringSplitter.Instance.Split(commandName + " " + (commandArguments ?? "")).ToArray(); + if (commandLineArray.Length <= 0) + { + throw new ArgumentException("Empty command name or arguments", nameof(commandArguments)); + } + return Execute(commandName, commandLineArray, services); + } - InvocationContext context = new(parseResult, new LocalConsole(services)); - if (parseResult.Errors.Count > 0) + /// + /// Find, parse and execute the command. + /// + /// command name + /// command line + /// services for the command + /// true - found command, false - command not found + /// empty command name + /// other errors + /// parsing error + private bool Execute(string commandName, string[] commandLineArray, IServiceProvider services) + { + if (string.IsNullOrEmpty(commandName)) { - context.InvocationResult = new ParseErrorResult(); + throw new ArgumentException("Empty command name", nameof(commandName)); } - else + List messages = new(); + foreach (CommandGroup group in _commandGroups) { - if (parseResult.CommandResult.Command is Command command) + if (group.TryGetCommandHandler(commandName, out CommandHandler handler)) { - if (command.Handler is CommandHandler handler) + try { - ITarget target = services.GetService(); - if (!handler.IsValidPlatform(target)) + if (handler.IsCommandSupported(group.Parser, services)) { - if (target != null) - { - context.Console.Error.WriteLine($"Command '{command.Name}' not supported on this target"); - } - else + if (group.Execute(commandLineArray, services)) { - context.Console.Error.WriteLine($"Command '{command.Name}' needs a target"); + return true; } - return false; - } - try - { - handler.Invoke(context, services); } - catch (Exception ex) + if (handler.FilterInvokeMessage != null) { - if (ex is NullReferenceException or - ArgumentException or - ArgumentNullException or - ArgumentOutOfRangeException or - NotImplementedException) - { - context.Console.Error.WriteLine(ex.ToString()); - } - else - { - context.Console.Error.WriteLine(ex.Message); - } - Trace.TraceError(ex.ToString()); - return false; + messages.Add(handler.FilterInvokeMessage); } } + catch (CommandNotFoundException ex) + { + messages.Add(ex.Message); + } } } - - context.InvocationResult?.Apply(context); - return context.ResultCode == 0; + if (messages.Count > 0) + { + throw new CommandNotFoundException(string.Concat(messages.Select(s => s + Environment.NewLine))); + } + return false; } /// /// Displays the help for a command /// - /// name of the command or alias /// service provider - /// true if success, false if command not found - public bool DisplayHelp(string commandName, IServiceProvider services) + /// command invocation and help enumeration + public IEnumerable<(string Invocation, string Help)> GetAllCommandHelp(IServiceProvider services) { - Command command = null; - if (!string.IsNullOrEmpty(commandName)) + List<(string Invocation, string Help)> help = new(); + foreach (CommandGroup group in _commandGroups) { - command = _rootBuilder.Command.Children.OfType().FirstOrDefault((cmd) => commandName == cmd.Name || cmd.Aliases.Any((alias) => commandName == alias)); - if (command == null) - { - return false; - } - if (command.Handler is CommandHandler handler) + foreach (CommandHandler handler in group.CommandHandlers) { - ITarget target = services.GetService(); - if (!handler.IsValidPlatform(target)) + try + { + if (handler.IsCommandSupported(group.Parser, services)) + { + string invocation = handler.HelpInvocation; + help.Add((invocation, handler.Help)); + } + } + catch (CommandNotFoundException) { - return false; } } } - else - { - ITarget target = services.GetService(); + return help; + } - // Create temporary builder adding only the commands that are valid for the target - CommandLineBuilder builder = new(new Command(_rootBuilder.Command.Name)); - foreach (Command cmd in _rootBuilder.Command.Children.OfType()) + /// + /// Displays the detailed help for a command + /// + /// name of the command or alias + /// service provider + /// the width to format the help or int.MaxValue + /// help text or null if not found + public string GetDetailedHelp(string commandName, IServiceProvider services, int consoleWidth) + { + if (string.IsNullOrWhiteSpace(commandName)) + { + throw new ArgumentNullException(nameof(commandName)); + } + List messages = new(); + foreach (CommandGroup group in _commandGroups) + { + if (group.TryGetCommand(commandName, out Command command)) { - if (cmd.Handler is CommandHandler handler) + if (command.Handler is CommandHandler handler) { - if (handler.IsValidPlatform(target)) + try + { + if (handler.IsCommandSupported(group.Parser, services)) + { + return group.GetDetailedHelp(command, services, consoleWidth); + } + if (handler.FilterInvokeMessage != null) + { + messages.Add(handler.FilterInvokeMessage); + } + } + catch (CommandNotFoundException ex) { - builder.AddCommand(cmd); + messages.Add(ex.Message); } } } - command = builder.Command; } - Debug.Assert(command != null); - IHelpBuilder helpBuilder = new LocalHelpBuilder(this, new LocalConsole(services), useHelpBuilder: true); - helpBuilder.Write(command); - return true; + if (messages.Count > 0) + { + return string.Concat(messages.Select(s => s + Environment.NewLine)); + } + return null; } /// - /// Does this command or alias exists? - /// - /// command or alias name - /// true if command exists - public bool IsCommand(string commandName) => _rootBuilder.Command.Children.Contains(commandName); - - /// - /// Enumerates all the command's name and help + /// Enumerates all the command's name, help and aliases /// - public IEnumerable<(string name, string help, IEnumerable aliases)> Commands => _commandHandlers.Select((keypair) => (keypair.Value.Name, keypair.Value.Help, keypair.Value.Aliases)); + public IEnumerable<(string name, string help, IEnumerable aliases)> Commands => + _commandGroups.SelectMany((group) => group.CommandHandlers).Select((handler) => (handler.Name, handler.Help, handler.Aliases)); /// /// Add the commands and aliases attributes found in the type. @@ -185,84 +228,242 @@ public void AddCommands(Type type, Func factory) CommandAttribute[] commandAttributes = (CommandAttribute[])baseType.GetCustomAttributes(typeof(CommandAttribute), inherit: true); foreach (CommandAttribute commandAttribute in commandAttributes) { - if ((commandAttribute.Flags & CommandFlags.Manual) == 0 || factory != null) + factory ??= (services) => Utilities.CreateInstance(type, services); + + bool dup = true; + foreach (CommandGroup group in _commandGroups) { - factory ??= (services) => Utilities.CreateInstance(type, services); - CreateCommand(baseType, commandAttribute, factory); + // If the group doesn't contain a duplicate command name, add it to that group + if (!group.Contains(commandAttribute.Name)) + { + group.CreateCommand(baseType, commandAttribute, factory); + dup = false; + break; + } + } + // If this is a duplicate command, create a new group and add it to the beginning. The default group must be last. + if (dup) + { + CommandGroup group = new(_commandPrompt); + _commandGroups.Insert(0, group); + group.CreateCommand(baseType, commandAttribute, factory); } } } - - // Build or re-build parser instance after all these commands and aliases are added - FlushParser(); } } - private void CreateCommand(Type type, CommandAttribute commandAttribute, Func factory) + /// + /// This groups like commands that may have the same name as another group or the default one. + /// + private sealed class CommandGroup { - Command command = new(commandAttribute.Name, commandAttribute.Help); - List<(PropertyInfo, Option)> properties = new(); - List<(PropertyInfo, Argument)> arguments = new(); + private Parser _parser; + private readonly CommandLineBuilder _rootBuilder; + private readonly Dictionary _commandHandlers = new(); - foreach (string alias in commandAttribute.Aliases) + /// + /// Create an instance of the command processor; + /// + /// command prompted used in help message + public CommandGroup(string commandPrompt = null) { - command.AddAlias(alias); + _rootBuilder = new CommandLineBuilder(new Command(commandPrompt)); } - foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite)) + /// + /// Parse and execute the command line. + /// + /// command line text + /// services for the command + /// true if command was found and executed without error + /// parsing error + internal bool Execute(IReadOnlyList commandLine, IServiceProvider services) { - ArgumentAttribute argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault(); - if (argumentAttribute != null) - { - IArgumentArity arity = property.PropertyType.IsArray ? ArgumentArity.ZeroOrMore : ArgumentArity.ZeroOrOne; + // Parse the command line and invoke the command + ParseResult parseResult = Parser.Parse(commandLine); - Argument argument = new() + if (parseResult.Errors.Count > 0) + { + StringBuilder sb = new(); + foreach (ParseError error in parseResult.Errors) { - Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(), - Description = argumentAttribute.Help, - ArgumentType = property.PropertyType, - Arity = arity - }; - command.AddArgument(argument); - arguments.Add((property, argument)); + sb.AppendLine(error.Message); + } + string helpText = GetDetailedHelp(parseResult.CommandResult.Command, services, int.MaxValue); + throw new CommandParsingException(sb.ToString(), helpText); } else { - OptionAttribute optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault(); - if (optionAttribute != null) + if (parseResult.CommandResult.Command is Command command) { - Option option = new(optionAttribute.Name ?? BuildOptionAlias(property.Name), optionAttribute.Help) + if (command.Handler is CommandHandler handler) { - Argument = new Argument { ArgumentType = property.PropertyType } - }; - command.AddOption(option); - properties.Add((property, option)); + InvocationContext context = new(parseResult, new LocalConsole(services.GetService())); + handler.Invoke(context, services); + return true; + } + } + } + return false; + } + + /// + /// Build/return parser + /// + internal Parser Parser => _parser ??= _rootBuilder.Build(); + + /// + /// Returns all the command handler instances + /// + internal IEnumerable CommandHandlers => _commandHandlers.Values; + + /// + /// Returns true if command or command alias is found + /// + internal bool Contains(string commandName) => _rootBuilder.Command.Children.Contains(commandName); + + /// + /// Returns the command handler for the command or command alias + /// + /// command or alias + /// handler instance + /// true if found + internal bool TryGetCommandHandler(string commandName, out CommandHandler handler) + { + handler = null; + if (TryGetCommand(commandName, out Command command)) + { + handler = command.Handler as CommandHandler; + } + return handler != null; + } + + /// + /// Returns the command instance for the command or command alias + /// + /// command or alias + /// command instance + /// true if found + internal bool TryGetCommand(string commandName, out Command command) + { + command = _rootBuilder.Command.Children.GetByAlias(commandName) as Command; + return command != null; + } + + /// + /// Add the commands and aliases attributes found in the type. + /// + /// Command type to search + /// function to create command instance + internal void AddCommands(Type type, Func factory) + { + for (Type baseType = type; baseType != null; baseType = baseType.BaseType) + { + if (baseType == typeof(CommandBase)) + { + break; + } + CommandAttribute[] commandAttributes = (CommandAttribute[])baseType.GetCustomAttributes(typeof(CommandAttribute), inherit: false); + foreach (CommandAttribute commandAttribute in commandAttributes) + { + factory ??= (services) => Utilities.CreateInstance(type, services); + CreateCommand(baseType, commandAttribute, factory); + } + } + + // Build or re-build parser instance after all these commands and aliases are added + FlushParser(); + } + + internal void CreateCommand(Type type, CommandAttribute commandAttribute, Func factory) + { + Command command = new(commandAttribute.Name, commandAttribute.Help); + List<(PropertyInfo, Argument)> arguments = new(); + List<(PropertyInfo, Option)> options = new(); - foreach (string alias in optionAttribute.Aliases) + foreach (string alias in commandAttribute.Aliases) + { + command.AddAlias(alias); + } + + foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite)) + { + ArgumentAttribute argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault(); + if (argumentAttribute != null) + { + IArgumentArity arity = property.PropertyType.IsArray ? ArgumentArity.ZeroOrMore : ArgumentArity.ZeroOrOne; + + Argument argument = new() + { + Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(), + Description = argumentAttribute.Help, + ArgumentType = property.PropertyType, + Arity = arity + }; + command.AddArgument(argument); + arguments.Add((property, argument)); + } + else + { + OptionAttribute optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault(); + if (optionAttribute != null) { - option.AddAlias(alias); + Option option = new(optionAttribute.Name ?? BuildOptionAlias(property.Name), optionAttribute.Help) + { + Argument = new Argument { ArgumentType = property.PropertyType } + }; + command.AddOption(option); + options.Add((property, option)); + + foreach (string alias in optionAttribute.Aliases) + { + option.AddAlias(alias); + } } } } + + CommandHandler handler = new(commandAttribute, arguments, options, type, factory); + _commandHandlers.Add(command.Name, handler); + command.Handler = handler; + _rootBuilder.AddCommand(command); + + // Build or re-build parser instance after this command is added + FlushParser(); } - CommandHandler handler = new(commandAttribute, arguments, properties, type, factory); - _commandHandlers.Add(command.Name, handler); - command.Handler = handler; - _rootBuilder.AddCommand(command); - } + internal string GetDetailedHelp(ICommand command, IServiceProvider services, int windowWidth) + { + CaptureConsole console = new(); - private Parser Parser => _parser ??= _rootBuilder.Build(); + // Get the command help + HelpBuilder helpBuilder = new(console, maxWidth: windowWidth); + helpBuilder.Write(command); - private void FlushParser() => _parser = null; + // Get the detailed help if any + if (TryGetCommandHandler(command.Name, out CommandHandler handler)) + { + string helpText = handler.GetDetailedHelp(Parser, services); + if (helpText is not null) + { + console.Out.Write(helpText); + } + } - private static string BuildOptionAlias(string parameterName) - { - if (string.IsNullOrWhiteSpace(parameterName)) + return console.ToString(); + } + + private void FlushParser() => _parser = null; + + private static string BuildOptionAlias(string parameterName) { - throw new ArgumentException("Value cannot be null or whitespace.", nameof(parameterName)); + if (string.IsNullOrWhiteSpace(parameterName)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(parameterName)); + } + return parameterName.Length > 1 ? $"--{parameterName.ToKebabCase()}" : $"-{parameterName.ToLowerInvariant()}"; } - return parameterName.Length > 1 ? $"--{parameterName.ToKebabCase()}" : $"-{parameterName.ToLowerInvariant()}"; } /// @@ -272,28 +473,68 @@ private sealed class CommandHandler : ICommandHandler { private readonly CommandAttribute _commandAttribute; private readonly IEnumerable<(PropertyInfo Property, Argument Argument)> _arguments; - private readonly IEnumerable<(PropertyInfo Property, Option Option)> _properties; + private readonly IEnumerable<(PropertyInfo Property, Option Option)> _options; private readonly Func _factory; private readonly MethodInfo _methodInfo; private readonly MethodInfo _methodInfoHelp; + private readonly MethodInfo _methodInfoFilter; + private readonly FilterInvokeAttribute _filterInvokeAttribute; public CommandHandler( CommandAttribute commandAttribute, IEnumerable<(PropertyInfo, Argument)> arguments, - IEnumerable<(PropertyInfo, Option)> properties, + IEnumerable<(PropertyInfo, Option)> options, Type type, Func factory) { _commandAttribute = commandAttribute; _arguments = arguments; - _properties = properties; + _options = options; _factory = factory; - _methodInfo = type.GetMethods().Where((methodInfo) => methodInfo.GetCustomAttribute() != null).SingleOrDefault() ?? + // Now search for the command, help and filter attributes in the command type + foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy)) + { + if (methodInfo.GetCustomAttribute() != null) + { + if (_methodInfo != null) + { + throw new ArgumentException($"Multiple CommandInvokeAttribute's found in {type}"); + } + _methodInfo = methodInfo; + } + if (methodInfo.GetCustomAttribute() != null) + { + if (_methodInfoHelp != null) + { + throw new ArgumentException($"Multiple HelpInvokeAttribute's found in {type}"); + } + if (methodInfo.ReturnType != typeof(string)) + { + throw new ArgumentException($"HelpInvokeAttribute doesn't return string in {type}"); + } + _methodInfoHelp = methodInfo; + } + FilterInvokeAttribute filterInvokeAttribute = methodInfo.GetCustomAttribute(); + if (filterInvokeAttribute != null) + { + if (_methodInfoFilter != null) + { + throw new ArgumentException($"Multiple FilterInvokeAttribute's found in {type}"); + } + if (methodInfo.ReturnType != typeof(bool)) + { + throw new ArgumentException($"FilterInvokeAttribute doesn't return bool in {type}"); + } + _filterInvokeAttribute = filterInvokeAttribute; + _methodInfoFilter = methodInfo; + } + } + if (_methodInfo == null) + { throw new ArgumentException($"No command invoke method found in {type}"); - - _methodInfoHelp = type.GetMethods().Where((methodInfo) => methodInfo.GetCustomAttribute() != null).SingleOrDefault(); + } } Task ICommandHandler.InvokeAsync(InvocationContext context) @@ -311,37 +552,25 @@ Task ICommandHandler.InvokeAsync(InvocationContext context) /// internal string Help => _commandAttribute.Help; + /// + /// Filter invoke message or null if no attribute or message + /// + internal string FilterInvokeMessage => _filterInvokeAttribute?.Message; + /// /// Returns the list of the command's aliases. /// internal IEnumerable Aliases => _commandAttribute.Aliases; /// - /// Returns true if the command should be added. + /// Returns the list of arguments /// - internal bool IsValidPlatform(ITarget target) - { - if ((_commandAttribute.Flags & CommandFlags.Global) != 0) - { - return true; - } - if (target != null) - { - if (target.OperatingSystem == OSPlatform.Windows) - { - return (_commandAttribute.Flags & CommandFlags.Windows) != 0; - } - if (target.OperatingSystem == OSPlatform.Linux) - { - return (_commandAttribute.Flags & CommandFlags.Linux) != 0; - } - if (target.OperatingSystem == OSPlatform.OSX) - { - return (_commandAttribute.Flags & CommandFlags.OSX) != 0; - } - } - return false; - } + internal IEnumerable Arguments => _arguments.Select((a) => a.Argument); + + /// + /// Returns true is the command is supported by the command filter. Calls the FilterInvokeAttribute marked method. + /// + internal bool IsCommandSupported(Parser parser, IServiceProvider services) => _methodInfoFilter == null || (bool)Invoke(_methodInfoFilter, context: null, parser, services); /// /// Execute the command synchronously. @@ -350,32 +579,56 @@ internal bool IsValidPlatform(ITarget target) /// service provider internal void Invoke(InvocationContext context, IServiceProvider services) => Invoke(_methodInfo, context, context.Parser, services); + /// + /// Return the various ways the command can be invoked. For building the help text. + /// + internal string HelpInvocation + { + get + { + IEnumerable rawAliases = new string[] { Name }.Concat(Aliases); + string invocation = string.Join(", ", rawAliases); + foreach (Argument argument in Arguments) + { + string argumentDescriptor = argument.Name; + if (!string.IsNullOrWhiteSpace(argumentDescriptor)) + { + invocation = $"{invocation} <{argumentDescriptor}>"; + } + } + return invocation; + } + } + /// /// Executes the command's help invoke function if exists /// /// parser instance /// service provider /// true help called, false no help function - internal bool InvokeHelp(Parser parser, IServiceProvider services) + internal string GetDetailedHelp(Parser parser, IServiceProvider services) { if (_methodInfoHelp == null) { - return false; + return null; } // The InvocationContext is null so the options and arguments in the // command instance created are not set. The context for the command // requesting help (either the help command or some other command using // --help) won't work for the command instance that implements it's own // help (SOS command). - Invoke(_methodInfoHelp, context: null, parser, services); - return true; + return (string)Invoke(_methodInfoHelp, context: null, parser, services); } - private void Invoke(MethodInfo methodInfo, InvocationContext context, Parser parser, IServiceProvider services) + private object Invoke(MethodInfo methodInfo, InvocationContext context, Parser parser, IServiceProvider services) { - object instance = _factory(services); - SetProperties(context, parser, instance); - Utilities.Invoke(methodInfo, instance, services); + object instance = null; + if (!methodInfo.IsStatic) + { + instance = _factory(services); + SetProperties(context, parser, instance); + } + return Utilities.Invoke(methodInfo, instance, services); } private void SetProperties(InvocationContext context, Parser parser, object instance) @@ -390,31 +643,28 @@ private void SetProperties(InvocationContext context, Parser parser, object inst } // Now initialize the option and service properties from the default and command line options - foreach ((PropertyInfo Property, Option Option) property in _properties) + foreach ((PropertyInfo Property, Option Option) option in _options) { - object value = property.Property.GetValue(instance); + object value = option.Property.GetValue(instance); - if (property.Option != null) + if (defaultParseResult != null) { - if (defaultParseResult != null) + OptionResult defaultOptionResult = defaultParseResult.FindResultFor(option.Option); + if (defaultOptionResult != null) { - OptionResult defaultOptionResult = defaultParseResult.FindResultFor(property.Option); - if (defaultOptionResult != null) - { - value = defaultOptionResult.GetValueOrDefault(); - } + value = defaultOptionResult.GetValueOrDefault(); } - if (context != null) + } + if (context != null) + { + OptionResult optionResult = context.ParseResult.FindResultFor(option.Option); + if (optionResult != null) { - OptionResult optionResult = context.ParseResult.FindResultFor(property.Option); - if (optionResult != null) - { - value = optionResult.GetValueOrDefault(); - } + value = optionResult.GetValueOrDefault(); } } - property.Property.SetValue(instance, value); + option.Property.SetValue(instance, value); } // Initialize any argument properties from the default and command line arguments @@ -463,66 +713,46 @@ private void SetProperties(InvocationContext context, Parser parser, object inst } /// - /// Local help builder that allows commands to provide more detailed help - /// text via the "InvokeHelp" function. + /// IConsole implementation that captures all the output into a string. /// - private sealed class LocalHelpBuilder : IHelpBuilder + private sealed class CaptureConsole : IConsole { - private readonly CommandService _commandService; - private readonly LocalConsole _console; - private readonly bool _useHelpBuilder; + private readonly StringBuilder _builder = new(); - public LocalHelpBuilder(CommandService commandService, IConsole console, bool useHelpBuilder) + public CaptureConsole() { - _commandService = commandService; - _console = (LocalConsole)console; - _useHelpBuilder = useHelpBuilder; + Out = Error = new StandardStreamWriter((text) => _builder.Append(text)); } - void IHelpBuilder.Write(ICommand command) - { - bool useHelpBuilder = _useHelpBuilder; - if (_commandService._commandHandlers.TryGetValue(command.Name, out CommandHandler handler)) - { - if (handler.InvokeHelp(_commandService.Parser, _console.Services)) - { - return; - } - useHelpBuilder = true; - } - if (useHelpBuilder) - { - HelpBuilder helpBuilder = new(_console, maxWidth: _console.ConsoleService.WindowWidth); - helpBuilder.Write(command); - } - } + public override string ToString() => _builder.ToString(); + + #region IConsole + + public IStandardStreamWriter Out { get; } + + bool IStandardOut.IsOutputRedirected { get { return false; } } + + public IStandardStreamWriter Error { get; } + + bool IStandardError.IsErrorRedirected { get { return false; } } + + bool IStandardIn.IsInputRedirected { get { return false; } } + + #endregion } /// - /// This class does two things: wraps the IConsoleService and provides the IConsole interface and - /// pipes through the System.CommandLine parsing allowing per command invocation data (service - /// provider and raw command line) to be passed through. + /// This class wraps the IConsoleService and provides the IConsole interface for System.CommandLine. /// private sealed class LocalConsole : IConsole { - private IConsoleService _console; + private readonly IConsoleService _consoleService; - public LocalConsole(IServiceProvider services) + public LocalConsole(IConsoleService consoleService) { - Services = services; - Out = new StandardStreamWriter(ConsoleService.Write); - Error = new StandardStreamWriter(ConsoleService.WriteError); - } - - internal readonly IServiceProvider Services; - - internal IConsoleService ConsoleService - { - get - { - _console ??= Services.GetService(); - return _console; - } + _consoleService = consoleService; + Out = new StandardStreamWriter(_consoleService.Write); + Error = new StandardStreamWriter(_consoleService.WriteError); } #region IConsole @@ -537,16 +767,16 @@ internal IConsoleService ConsoleService bool IStandardIn.IsInputRedirected { get { return false; } } - private sealed class StandardStreamWriter : IStandardStreamWriter - { - private readonly Action _write; + #endregion + } - public StandardStreamWriter(Action write) => _write = write; + private sealed class StandardStreamWriter : IStandardStreamWriter + { + private readonly Action _write; - void IStandardStreamWriter.Write(string value) => _write(value); - } + public StandardStreamWriter(Action write) => _write = write; - #endregion + void IStandardStreamWriter.Write(string value) => _write(value); } } } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs index 090270e288..19904d2f10 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs @@ -363,6 +363,7 @@ private sealed class ExtensionLoadContext : AssemblyLoadContext public ExtensionLoadContext(string extensionPath) { + Trace.TraceInformation($"ExtensionLoadContext: {extensionPath}"); _extensionPath = extensionPath; } @@ -387,12 +388,15 @@ protected override Assembly Load(AssemblyName assemblyName) { throw new InvalidOperationException($"Extension assembly reference version not supported for {assemblyName.Name} {assemblyName.Version}"); } + Trace.TraceInformation($"ExtensionLoadContext: loading SOS assembly {assembly.CodeBase}"); return assembly; } else if (_extensionPaths.TryGetValue(assemblyName.Name, out string path)) { + Trace.TraceInformation($"ExtensionLoadContext: loading from extension path {path}"); return LoadFromAssemblyPath(path); } + Trace.TraceInformation($"ExtensionLoadContext: returning null {assemblyName}"); return null; } } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs index cc69c944aa..fd231f2270 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs @@ -9,6 +9,8 @@ using System.Reflection; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; +using System.Text; +using System.Threading; using Microsoft.FileFormats; using Microsoft.FileFormats.ELF; using Microsoft.FileFormats.MachO; @@ -34,7 +36,7 @@ public static class Utilities /// /// This function is neither commutative nor associative; the hash codes must be combined in /// a deterministic order. Do not use this when hashing collections whose contents are - /// nondeterministically ordered! + /// non-deterministically ordered! /// public static int CombineHashCodes(int hashCode0, int hashCode1) { @@ -412,4 +414,46 @@ private static object[] BuildArguments(MethodBase methodBase, IServiceProvider s return arguments; } } + + public class CaptureConsoleService : IConsoleService + { + private readonly StringBuilder _builder = new(); + + public CaptureConsoleService() + { + } + + public void Clear() => _builder.Clear(); + + public override string ToString() => _builder.ToString(); + + #region IConsoleService + + public void Write(string text) + { + _builder.Append(text); + } + + public void WriteWarning(string text) + { + _builder.Append(text); + } + + public void WriteError(string text) + { + _builder.Append(text); + } + + public bool SupportsDml => false; + + public void WriteDml(string text) => throw new NotSupportedException(); + + public void WriteDmlExec(string text, string _) => throw new NotSupportedException(); + + public CancellationToken CancellationToken { get; set; } = CancellationToken.None; + + int IConsoleService.WindowWidth => int.MaxValue; + + #endregion + } } diff --git a/src/Microsoft.Diagnostics.DebugServices/CommandAttributes.cs b/src/Microsoft.Diagnostics.DebugServices/CommandAttributes.cs index 4b1df4b601..bd988f0c20 100644 --- a/src/Microsoft.Diagnostics.DebugServices/CommandAttributes.cs +++ b/src/Microsoft.Diagnostics.DebugServices/CommandAttributes.cs @@ -6,32 +6,6 @@ namespace Microsoft.Diagnostics.DebugServices { - /// - /// Command flags to filter by OS Platforms, control scope and how the command is registered. - /// - [Flags] - public enum CommandFlags : byte - { - Windows = 0x01, - Linux = 0x02, - OSX = 0x04, - - /// - /// Command is supported when there is no target - /// - Global = 0x08, - - /// - /// Command is not added through reflection, but manually with command service API. - /// - Manual = 0x10, - - /// - /// Default. All operating system, but target is required - /// - Default = Windows | Linux | OSX - } - /// /// Marks the class as a Command. /// @@ -53,11 +27,6 @@ public class CommandAttribute : Attribute /// public string[] Aliases = Array.Empty(); - /// - /// Command flags to filter by OS Platforms, control scope and how the command is registered. - /// - public CommandFlags Flags = CommandFlags.Default; - /// /// A string of options that are parsed before the command line options /// @@ -121,10 +90,24 @@ public class CommandInvokeAttribute : Attribute } /// - /// Marks the function to invoke to display alternate help for command. + /// Marks the function to invoke to return the alternate help for command. The function returns + /// a string. The Argument and Option properties of the command are not set. /// [AttributeUsage(AttributeTargets.Method)] public class HelpInvokeAttribute : Attribute { } + + /// + /// Marks the function to invoke to filter a command. The function returns a bool; true if + /// the command is supported. The Argument and Option properties of the command are not set. + /// + [AttributeUsage(AttributeTargets.Method)] + public class FilterInvokeAttribute : Attribute + { + /// + /// Message to display if the filter fails + /// + public string Message; + } } diff --git a/src/Microsoft.Diagnostics.DebugServices/DiagnosticsException.cs b/src/Microsoft.Diagnostics.DebugServices/DiagnosticsException.cs index c6ea751959..fa5d9c85cd 100644 --- a/src/Microsoft.Diagnostics.DebugServices/DiagnosticsException.cs +++ b/src/Microsoft.Diagnostics.DebugServices/DiagnosticsException.cs @@ -27,23 +27,43 @@ public DiagnosticsException(string message, Exception innerException) } /// - /// Thrown if a command is not supported on the configuration, platform or runtime + /// Thrown if a command is not found. /// - public class CommandNotSupportedException : DiagnosticsException + public class CommandNotFoundException : DiagnosticsException { - public CommandNotSupportedException() - : base() + public const string NotFoundMessage = $"Unrecognized SOS command"; + + public CommandNotFoundException(string message) + : base(message) + { + } + + public CommandNotFoundException(string message, Exception innerException) + : base(message, innerException) { } + } + + /// + /// Thrown if a command is not found. + /// + public class CommandParsingException : DiagnosticsException + { + /// + /// The detailed help of the command + /// + public string DetailedHelp { get; } - public CommandNotSupportedException(string message) + public CommandParsingException(string message, string detailedHelp) : base(message) { + DetailedHelp = detailedHelp; } - public CommandNotSupportedException(string message, Exception innerException) + public CommandParsingException(string message, string detailedHelp, Exception innerException) : base(message, innerException) { + DetailedHelp = detailedHelp; } } } diff --git a/src/Microsoft.Diagnostics.DebugServices/ICommandService.cs b/src/Microsoft.Diagnostics.DebugServices/ICommandService.cs index 2a4dbe8109..6455767449 100644 --- a/src/Microsoft.Diagnostics.DebugServices/ICommandService.cs +++ b/src/Microsoft.Diagnostics.DebugServices/ICommandService.cs @@ -12,7 +12,7 @@ namespace Microsoft.Diagnostics.DebugServices public interface ICommandService { /// - /// Enumerates all the command's name and help + /// Enumerates all the command's name, help and aliases /// IEnumerable<(string name, string help, IEnumerable aliases)> Commands { get; } @@ -23,11 +23,19 @@ public interface ICommandService void AddCommands(Type type); /// - /// Displays the help for a command + /// Gets help for all of the commands + /// + /// service provider + /// command invocation and help enumeration + public IEnumerable<(string Invocation, string Help)> GetAllCommandHelp(IServiceProvider services); + + /// + /// Displays the detailed help for a command /// /// name of the command or alias /// service provider - /// true if success, false if command not found - bool DisplayHelp(string commandName, IServiceProvider services); + /// the width to format the help or int.MaxValue + /// help text or null if not found + string GetDetailedHelp(string commandName, IServiceProvider services, int consoleWidth); } } diff --git a/src/Microsoft.Diagnostics.DebugServices/ProviderExportAttribute.cs b/src/Microsoft.Diagnostics.DebugServices/ProviderExportAttribute.cs index 8a70aff359..be60004fa1 100644 --- a/src/Microsoft.Diagnostics.DebugServices/ProviderExportAttribute.cs +++ b/src/Microsoft.Diagnostics.DebugServices/ProviderExportAttribute.cs @@ -13,7 +13,7 @@ public class ProviderExportAttribute : Attribute { /// /// The interface or type to register the provider. If null, the provider type registered will be - /// he class itself or the return type of the method. + /// the class itself or the return type of the method. /// public Type Type { get; set; } diff --git a/src/Microsoft.Diagnostics.DebugServices/ServiceContainer.cs b/src/Microsoft.Diagnostics.DebugServices/ServiceContainer.cs index 9b497c53fe..df9aa18d95 100644 --- a/src/Microsoft.Diagnostics.DebugServices/ServiceContainer.cs +++ b/src/Microsoft.Diagnostics.DebugServices/ServiceContainer.cs @@ -29,9 +29,8 @@ public class ServiceContainer : IServiceProvider /// /// search this provider if service isn't found in this instance or null /// service factories to initialize provider or null - public ServiceContainer(IServiceProvider parent, Dictionary factories) + public ServiceContainer(IServiceProvider parent, Dictionary factories = null) { - Debug.Assert(factories != null); _parent = parent; _factories = factories; _instances = new Dictionary(); @@ -88,7 +87,7 @@ public object GetService(Type type) { return service; } - if (_factories.TryGetValue(type, out ServiceFactory factory)) + if (_factories != null && _factories.TryGetValue(type, out ServiceFactory factory)) { service = factory(this); _instances.Add(type, service); diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/AnalyzeOOMCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/AnalyzeOOMCommand.cs index 35e38607a0..ad970e05bb 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/AnalyzeOOMCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/AnalyzeOOMCommand.cs @@ -7,12 +7,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "analyzeoom", Help = "Displays the info of the last OOM that occurred on an allocation request to the GC heap.")] - public class AnalyzeOOMCommand : CommandBase + [Command(Name = "analyzeoom", Aliases = new[] { "AnalyzeOOM" }, Help = "Displays the info of the last OOM that occurred on an allocation request to the GC heap.")] + public class AnalyzeOOMCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - public override void Invoke() { bool foundOne = false; diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrModulesCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/AssembliesCommand.cs similarity index 79% rename from src/Microsoft.Diagnostics.ExtensionCommands/ClrModulesCommand.cs rename to src/Microsoft.Diagnostics.ExtensionCommands/AssembliesCommand.cs index 6e19a346e4..ebeda4ed1d 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ClrModulesCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/AssembliesCommand.cs @@ -9,28 +9,21 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "clrmodules", Help = "Lists the managed modules in the process.")] - public class ClrModulesCommand : CommandBase + [Command(Name = "assemblies", Aliases = new[] { "clrmodules" }, Help = "Lists the managed assemblies in the process.")] + public class AssembliesCommand : ClrRuntimeCommandBase { - [ServiceImport(Optional = true)] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public IModuleService ModuleService { get; set; } - [Option(Name = "--name", Aliases = new string[] { "-n" }, Help = "RegEx filter on module name (path not included).")] - public string ModuleName { get; set; } + [Option(Name = "--name", Aliases = new string[] { "-n" }, Help = "RegEx filter on assembly name (path not included).")] + public string AssemblyName { get; set; } - [Option(Name = "--verbose", Aliases = new string[] { "-v" }, Help = "Displays detailed information about the modules.")] + [Option(Name = "--verbose", Aliases = new string[] { "-v" }, Help = "Displays detailed information about the assemblies.")] public bool Verbose { get; set; } public override void Invoke() { - if (Runtime == null) - { - throw new DiagnosticsException("No CLR runtime set"); - } - Regex regex = ModuleName is not null ? new Regex(ModuleName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null; + Regex regex = AssemblyName is not null ? new Regex(AssemblyName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null; foreach (ClrModule module in Runtime.EnumerateModules()) { if (regex is null || !string.IsNullOrEmpty(module.Name) && regex.IsMatch(Path.GetFileName(module.Name))) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs index 67e42bac97..1d87e10de3 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs @@ -17,7 +17,7 @@ public class ClrMDHelper private readonly ClrHeap _heap; [ServiceExport(Scope = ServiceScope.Runtime)] - public static ClrMDHelper Create([ServiceImport(Optional = true)] ClrRuntime clrRuntime) + public static ClrMDHelper TryCreate([ServiceImport(Optional = true)] ClrRuntime clrRuntime) { return clrRuntime != null ? new ClrMDHelper(clrRuntime) : null; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelperCommandBase.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelperCommandBase.cs new file mode 100644 index 0000000000..b91d528e68 --- /dev/null +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelperCommandBase.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Diagnostics.DebugServices; + +namespace Microsoft.Diagnostics.ExtensionCommands +{ + public abstract class ClrMDHelperCommandBase : CommandBase + { + /// + /// Helper bound to the current ClrRuntime that provides high level services on top of ClrMD. + /// + [ServiceImport(Optional = true)] + public ClrMDHelper Helper { get; set; } + + [FilterInvoke(Message = ClrRuntimeCommandBase.RuntimeNotFoundMessage)] + public static bool FilterInvoke([ServiceImport(Optional = true)] ClrMDHelper helper) => helper != null; + } +} diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrRuntimeCommandBase.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ClrRuntimeCommandBase.cs new file mode 100644 index 0000000000..2f139129d6 --- /dev/null +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ClrRuntimeCommandBase.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Diagnostics.DebugServices; +using Microsoft.Diagnostics.Runtime; + +namespace Microsoft.Diagnostics.ExtensionCommands +{ + public abstract class ClrRuntimeCommandBase : CommandBase + { + public const string RuntimeNotFoundMessage = "No CLR runtime found. This means that a .NET runtime module or the DAC for the runtime can not be found or downloaded."; + + [ServiceImport(Optional = true)] + public ClrRuntime Runtime { get; set; } + + [FilterInvoke(Message = RuntimeNotFoundMessage)] + public static bool FilterInvoke([ServiceImport(Optional = true)] ClrRuntime runtime) => runtime != null; + } +} diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpAsyncCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpAsyncCommand.cs index 0acbca362e..954fd6fa61 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpAsyncCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpAsyncCommand.cs @@ -14,45 +14,17 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = CommandName, Aliases = new string[] { "DumpAsync" }, Help = "Displays information about async \"stacks\" on the garbage-collected heap.")] - public sealed class DumpAsyncCommand : ExtensionCommandBase + public sealed class DumpAsyncCommand : ClrRuntimeCommandBase { /// The name of the command. private const string CommandName = "dumpasync"; /// Indent width. private const int TabWidth = 2; + /// The command invocation syntax when used in Debugger Markup Language (DML) commands. private const string DmlCommandInvoke = $"!{CommandName}"; - /// The help text to render when asked for help. - private static readonly string s_detailedHelpText = - $"Usage: {CommandName} [--stats] [--coalesce] [--address ] [--methodtable ] [--type ] [--tasks] [--completed] [--fields]" + Environment.NewLine + - Environment.NewLine + - "Displays information about async \"stacks\" on the garbage-collected heap. Stacks" + Environment.NewLine + - "are synthesized by finding all task objects (including async state machine box" + Environment.NewLine + - "objects) on the GC heap and chaining them together based on continuations." + Environment.NewLine + - Environment.NewLine + - "Options:" + Environment.NewLine + - " --stats Summarize all async frames found rather than showing detailed stacks." + Environment.NewLine + - " --coalesce Coalesce stacks and portions of stacks that are the same." + Environment.NewLine + - " --address Only show stacks that include the object with the specified address." + Environment.NewLine + - " --methodtable Only show stacks that include objects with the specified method table." + Environment.NewLine + - " --type Only show stacks that include objects whose type includes the specified name in its name." + Environment.NewLine + - " --tasks Include stacks that contain only non-state machine task objects." + Environment.NewLine + - " --completed Include completed tasks in stacks." + Environment.NewLine + - " --fields Show fields for each async stack frame." + Environment.NewLine + - Environment.NewLine + - "Examples:" + Environment.NewLine + - $"Summarize all async frames associated with a specific method table address: !{CommandName} --stats --methodtable 0x00007ffbcfbe0970" + Environment.NewLine + - $"Show all stacks coalesced by common frames: !{CommandName} --coalesce" + Environment.NewLine + - $"Show each stack that includes \"ReadAsync\": !{CommandName} --type ReadAsync" + Environment.NewLine + - $"Show each stack that includes an object at a specific address, and include fields: !{CommandName} --address 0x000001264adce778 --fields"; - - /// Gets the runtime for the process. Set by the command framework. - [ServiceImport(Optional = true)] - public ClrRuntime? Runtime { get; set; } - - /// Gets whether to only show stacks that include the object with the specified address. [Option(Name = "--address", Aliases = new string[] { "-addr" }, Help = "Only show stacks that include the object with the specified address.")] public string? ObjectAddress @@ -96,27 +68,19 @@ public string? MethodTableAddress public bool CoalesceStacks { get; set; } /// Invokes the command. - public override void ExtensionInvoke() + public override void Invoke() { - ClrRuntime? runtime = Runtime; - if (runtime is null) - { - WriteLineError("Unable to access runtime."); - return; - } - + ClrRuntime runtime = Runtime; ClrHeap heap = runtime.Heap; if (!heap.CanWalkHeap) { - WriteLineError("Unable to examine the heap."); - return; + throw new DiagnosticsException("Unable to examine the heap."); } ClrType? taskType = runtime.BaseClassLibrary.GetTypeByName("System.Threading.Tasks.Task"); if (taskType is null) { - WriteLineError("Unable to find required type."); - return; + throw new DiagnosticsException("Unable to find required type."); } ClrStaticField? taskCompletionSentinelType = taskType.GetStaticFieldByName("s_taskCompletionSentinel"); @@ -1191,7 +1155,18 @@ void Append(string s) } /// Gets detailed help for the command. - protected override string GetDetailedHelp() => s_detailedHelpText; + [HelpInvoke] + public static string GetDetailedHelp() => +@"Displays information about async ""stacks"" on the garbage-collected heap. Stacks +are synthesized by finding all task objects (including async state machine box +objects) on the GC heap and chaining them together based on continuations. + +Examples: + Summarize all async frames associated with a specific method table address: dumpasync --stats --methodtable 0x00007ffbcfbe0970 + Show all stacks coalesced by common frames: dumpasync --coalesce + Show each stack that includes ""ReadAsync"": dumpasync --type ReadAsync + Show each stack that includes an object at a specific address, and include fields: dumpasync --address 0x000001264adce778 --fields +"; /// Represents an async object to be used as a frame in an async "stack". private sealed class AsyncObject diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentDictionaryCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentDictionaryCommand.cs index b723a5d012..06bee8bc6e 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentDictionaryCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentDictionaryCommand.cs @@ -9,40 +9,36 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "dumpconcurrentdictionary", Aliases = new string[] { "dcd" }, Help = "Displays concurrent dictionary content.")] - public class DumpConcurrentDictionaryCommand : ExtensionCommandBase + public class DumpConcurrentDictionaryCommand : ClrMDHelperCommandBase { [Argument(Help = "The address of a ConcurrentDictionary object.")] public string Address { get; set; } - [ServiceImport] + [ServiceImport(Optional = true)] public ClrRuntime Runtime { get; set; } - public override void ExtensionInvoke() + public override void Invoke() { if (string.IsNullOrEmpty(Address)) { - WriteLine("Missing ConcurrentDictionary address..."); - return; + throw new DiagnosticsException("Missing ConcurrentDictionary address..."); } if (!TryParseAddress(Address, out ulong address)) { - WriteLine("Hexadecimal address expected..."); - return; + throw new DiagnosticsException("Hexadecimal address expected..."); } ClrHeap heap = Runtime.Heap; ClrType type = heap.GetObjectType(address); if (type?.Name is null) { - WriteLine($"{Address:x16} is not referencing an object..."); - return; + throw new DiagnosticsException($"{Address:x16} is not referencing an object..."); } if (!type.Name.StartsWith("System.Collections.Concurrent.ConcurrentDictionary<")) { - WriteLine($"{Address:x16} is not a ConcurrentDictionary but an instance of {type.Name}..."); - return; + throw new DiagnosticsException($"{Address:x16} is not a ConcurrentDictionary but an instance of {type.Name}..."); } WriteLine($"{type.Name}"); @@ -67,9 +63,8 @@ public override void ExtensionInvoke() WriteLine(string.Empty); } - protected override string GetDetailedHelp() - { - return + [HelpInvoke] + public static string GetDetailedHelp() => @"------------------------------------------------------------------------------- DumpConcurrentDictionary Lists all items (key/value pairs) in the given concurrent dictionary. @@ -89,7 +84,6 @@ 2 items - In case of reference types, the command to dump each object is shown (e.g. dumpobj <[item] address>). - For value types, the command to dump each value type is shown (e.g. dumpvc <[item] address>). "; - } private static string Truncate(string str, int nbMaxChars) { diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentQueueCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentQueueCommand.cs index 62c31f97f6..9252a6379e 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentQueueCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentQueueCommand.cs @@ -8,41 +8,36 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "dumpconcurrentqueue", Aliases = new string[] { "dcq" }, Help = "Displays concurrent queue content.")] - public class DumpConcurrentQueueCommand : ExtensionCommandBase + public class DumpConcurrentQueueCommand : ClrMDHelperCommandBase { [Argument(Help = "The address of a ConcurrentQueue object.")] public string Address { get; set; } - [ServiceImport] + [ServiceImport(Optional = true)] public ClrRuntime Runtime { get; set; } - public override void ExtensionInvoke() + public override void Invoke() { if (string.IsNullOrEmpty(Address)) { - WriteLine("Missing ConcurrentQueue address..."); - return; + throw new DiagnosticsException("Missing ConcurrentQueue address..."); } if (!TryParseAddress(Address, out ulong address)) { - WriteLine("Hexadecimal address expected..."); - return; + throw new DiagnosticsException("Hexadecimal address expected..."); } ClrHeap heap = Runtime.Heap; ClrType type = heap.GetObjectType(address); if (type == null) { - WriteLine($"{Address:x16} is not referencing an object..."); - return; + throw new DiagnosticsException($"{Address:x16} is not referencing an object..."); } - if (!type.Name.StartsWith("System.Collections.Concurrent.ConcurrentQueue<")) { - WriteLine($"{Address:x16} is not a ConcurrentQueue but an instance of {type.Name}..."); - return; + throw new DiagnosticsException($"{Address:x16} is not a ConcurrentQueue but an instance of {type.Name}..."); } WriteLine($"{type.Name}"); @@ -64,39 +59,33 @@ public override void ExtensionInvoke() WriteLine(""); } - protected override string GetDetailedHelp() - { - return DetailedHelpText; - } + [HelpInvoke] + public static string GetDetailedHelp() => +@"------------------------------------------------------------------------------- +DumpConcurrentQueue + +Lists all items in the given concurrent queue. + +For simple types such as numbers, boolean and string, values are shown. +> dcq 00000202a79320e8 +System.Collections.Concurrent.ConcurrentQueue + 1 - 0 + 2 - 1 + 3 - 2 + +In case of reference types, the command to dump each object is shown. +> dcq 00000202a79337f8 +System.Collections.Concurrent.ConcurrentQueue + 1 - dumpobj 0x202a7934e38 + 2 - dumpobj 0x202a7934fd0 + 3 - dumpobj 0x202a7935078 - private readonly string DetailedHelpText = - "-------------------------------------------------------------------------------" + Environment.NewLine + - "DumpConcurrentQueue" + Environment.NewLine + - Environment.NewLine + - "Lists all items in the given concurrent queue." + Environment.NewLine + - Environment.NewLine + - "For simple types such as numbers, boolean and string, values are shown." + Environment.NewLine + - "> dcq 00000202a79320e8" + Environment.NewLine + - "System.Collections.Concurrent.ConcurrentQueue" + Environment.NewLine + - " 1 - 0" + Environment.NewLine + - " 2 - 1" + Environment.NewLine + - " 3 - 2" + Environment.NewLine + - Environment.NewLine + - "In case of reference types, the command to dump each object is shown." + Environment.NewLine + - "> dcq 00000202a79337f8" + Environment.NewLine + - "System.Collections.Concurrent.ConcurrentQueue" + Environment.NewLine + - " 1 - dumpobj 0x202a7934e38" + Environment.NewLine + - " 2 - dumpobj 0x202a7934fd0" + Environment.NewLine + - " 3 - dumpobj 0x202a7935078" + Environment.NewLine + - Environment.NewLine + - "For value types, the command to dump each array segment is shown." + Environment.NewLine + - "The next step is to manually dump each element with dumpvc <[item] address>." + Environment.NewLine + - "> dcq 00000202a7933370" + Environment.NewLine + - "System.Collections.Concurrent.ConcurrentQueue" + Environment.NewLine + - " 1 - dumparray 202a79334e0" + Environment.NewLine + - " 2 - dumparray 202a7938a88" + Environment.NewLine + - Environment.NewLine + - "" - ; +For value types, the command to dump each array segment is shown. +The next step is to manually dump each element with dumpvc <[item] address>. +> dcq 00000202a7933370 +System.Collections.Concurrent.ConcurrentQueue + 1 - dumparray 202a79334e0 + 2 - dumparray 202a7938a88 +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpExceptionsCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpExceptionsCommand.cs index 4f57cf3897..b2384cfada 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpExceptionsCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpExceptionsCommand.cs @@ -14,11 +14,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "dumpexceptions", Help = "Displays a list of all managed exceptions.")] - public class DumpExceptionsCommand : CommandBase + public class DumpExceptionsCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } = null!; - [ServiceImport] public LiveObjectService LiveObjects { get; set; } = null!; diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpGenCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpGenCommand.cs index db020e7926..a3ec51e17b 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpGenCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpGenCommand.cs @@ -8,7 +8,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "dumpgen", Aliases = new string[] { "dg" }, Help = "Displays heap content for the specified generation.")] - public class DumpGenCommand : ExtensionCommandBase + public class DumpGenCommand : ClrMDHelperCommandBase { private const string statsHeader32bits = " MT Count TotalSize Class Name"; private const string statsHeader64bits = " MT Count TotalSize Class Name"; @@ -24,7 +24,7 @@ public class DumpGenCommand : ExtensionCommandBase [Option(Name = "-mt", Help = "The address pointing on a Method table.")] public string MethodTableAddress { get; set; } - public override void ExtensionInvoke() + public override void Invoke() { GCGeneration generation = ParseGenerationArgument(Generation); if (generation != GCGeneration.NotSet) @@ -43,7 +43,7 @@ public override void ExtensionInvoke() } else { - WriteLine("Hexadecimal address expected for -mt option"); + throw new DiagnosticsException("Hexadecimal address expected for -mt option"); } } WriteLine(string.Empty); @@ -88,12 +88,11 @@ private void WriteStatistics(IEnumerable dumpGenResult) WriteLine($"Total {objectsCount} objects"); } - private GCGeneration ParseGenerationArgument(string generation) + private static GCGeneration ParseGenerationArgument(string generation) { if (string.IsNullOrEmpty(generation)) { - WriteLine("Generation argument is missing"); - return GCGeneration.NotSet; + throw new DiagnosticsException("Generation argument is missing"); } string lowerString = generation.ToLowerInvariant(); GCGeneration result = lowerString switch @@ -106,17 +105,16 @@ private GCGeneration ParseGenerationArgument(string generation) "foh" => GCGeneration.FrozenObjectHeap, _ => GCGeneration.NotSet, }; + if (result == GCGeneration.NotSet) { - WriteLine($"{generation} is not a supported generation (gen0, gen1, gen2, loh, poh, foh)"); + throw new DiagnosticsException($"{generation} is not a supported generation (gen0, gen1, gen2, loh, poh, foh)"); } return result; } - - protected override string GetDetailedHelp() - { - return + [HelpInvoke] + public static string GetDetailedHelp() => @"------------------------------------------------------------------------------- DumpGen This command can be used for 2 use cases: @@ -160,6 +158,5 @@ 00000184aa23e8f0 00007ff9ea6e75b8 40 00000184aa23e918 00007ff9ea6e75b8 40 Total 3 objects "; - } } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs index 5f2faf6549..38aa12c773 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapCommand.cs @@ -9,15 +9,12 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "dumpheap", Help = "Displays a list of all managed objects.")] - public class DumpHeapCommand : CommandBase + [Command(Name = "dumpheap", Aliases = new[] { "DumpHeap" }, Help = "Displays a list of all managed objects.")] + public class DumpHeapCommand : ClrRuntimeCommandBase { [ServiceImport] public IMemoryService MemoryService { get; set; } - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public LiveObjectService LiveObjects { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpObjGCRefsHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpObjGCRefsCommand.cs similarity index 96% rename from src/Microsoft.Diagnostics.ExtensionCommands/DumpObjGCRefsHelper.cs rename to src/Microsoft.Diagnostics.ExtensionCommands/DumpObjGCRefsCommand.cs index adae249ab2..cdd53f260d 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpObjGCRefsHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpObjGCRefsCommand.cs @@ -11,13 +11,10 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "dumpobjgcrefs", Help = "A helper command to implement !dumpobj -refs")] - public sealed class DumpObjGCRefsHelper : CommandBase + public sealed class DumpObjGCRefsCommand : ClrRuntimeCommandBase { private readonly StringBuilderPool _stringBuilderPool = new(260); - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [Argument(Name = "object")] public string ObjectAddress { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpRuntimeTypeCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpRuntimeTypeCommand.cs index cfc8579e6f..8c2de445c6 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpRuntimeTypeCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpRuntimeTypeCommand.cs @@ -8,12 +8,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "dumpruntimetypes", Help = "Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too.")] - public sealed class DumpRuntimeTypeCommand : CommandBase + [Command(Name = "dumpruntimetypes", Aliases = new[] { "DumpRuntimeTypes" }, Help = "Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too.")] + public sealed class DumpRuntimeTypeCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - public override void Invoke() { Table output = null; diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs index 131450fe8b..2a211c1eb5 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs @@ -15,8 +15,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "dumpstackobjects", Aliases = new string[] { "dso" }, Help = "Displays all managed objects found within the bounds of the current stack.")] - public class DumpStackObjectsCommand : CommandBase + [Command(Name = "dumpstackobjects", Aliases = new string[] { "dso", "DumpStackObjects" }, Help = "Displays all managed objects found within the bounds of the current stack.")] + public class DumpStackObjectsCommand : ClrRuntimeCommandBase { [ServiceImport] public IMemoryService MemoryService { get; set; } @@ -27,13 +27,10 @@ public class DumpStackObjectsCommand : CommandBase [ServiceImport] public IThreadService ThreadService { get; set; } - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [Option(Name = "-verify", Help = "Verify each object and only print ones that are valid objects.")] public bool Verify { get; set; } - [Argument(Name = "StackBounds", Help = "The top and bottom of the stack (in hex).")] + [Argument(Name = "stackbounds", Help = "The top and bottom of the stack (in hex).")] public string[] Bounds { get; set; } public override void Invoke() diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs index c565148650..59fb9fc3ce 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs @@ -13,8 +13,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = CommandName, Help = "Displays information about native memory that CLR has allocated.")] - public class EEHeapCommand : CommandBase + [Command(Name = CommandName, Aliases = new[] { "EEHeap" }, Help = "Displays information about native memory that CLR has allocated.")] + public class EEHeapCommand : ClrRuntimeCommandBase { private const string CommandName = "eeheap"; @@ -23,9 +23,6 @@ public class EEHeapCommand : CommandBase // Don't use the word "Total" if we have filtered out entries private string TotalString => HeapWithFilters.HasFilters ? "Partial" : "Total"; - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public IMemoryService MemoryService { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionCommandBase.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionCommandBase.cs deleted file mode 100644 index 319b45db8d..0000000000 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionCommandBase.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Diagnostics.DebugServices; - -namespace Microsoft.Diagnostics.ExtensionCommands -{ - public abstract class ExtensionCommandBase : CommandBase - { - /// - /// Helper bound to the current ClrRuntime that provides high level services on top of ClrMD. - /// - [ServiceImport(Optional = true)] - public ClrMDHelper Helper { get; set; } - - public override void Invoke() - { - if (Helper == null) - { - throw new DiagnosticsException("No CLR runtime set"); - } - ExtensionInvoke(); - } - - public abstract void ExtensionInvoke(); - - [HelpInvoke] - public void InvokeHelp() - { - WriteLine(GetDetailedHelp()); - } - - protected abstract string GetDetailedHelp(); - } -} diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionMethodHelpers.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionMethodHelpers.cs index 348a23eb84..778aab9693 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionMethodHelpers.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ExtensionMethodHelpers.cs @@ -11,6 +11,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands internal static class ExtensionMethodHelpers { public static string ConvertToHumanReadable(this ulong totalBytes) => ConvertToHumanReadable((double)totalBytes); + public static string ConvertToHumanReadable(this long totalBytes) => ConvertToHumanReadable((double)totalBytes); public static string ConvertToHumanReadable(this double totalBytes) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs index 9d4efb2ede..f74aee474a 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs @@ -11,8 +11,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "finalizequeue", Help = "Displays all objects registered for finalization.")] - public class FinalizeQueueCommand : CommandBase + [Command(Name = "finalizequeue", Aliases = new[] { "fq", "FinalizeQueue" }, Help = "Displays all objects registered for finalization.")] + public class FinalizeQueueCommand : ClrRuntimeCommandBase { [Option(Name = "-detail", Help = "Will display extra information on any SyncBlocks that need to be cleaned up, and on any RuntimeCallableWrappers (RCWs) that await cleanup. Both of these data structures are cached and cleaned up by the finalizer thread when it gets a chance to run.")] public bool Detail { get; set; } @@ -38,9 +38,6 @@ public class FinalizeQueueCommand : CommandBase [ServiceImport] public DumpHeapService DumpHeap { get; set; } - [ServiceImport] - public ClrRuntime Runtime { get; set; } - public override void Invoke() { ulong mt = 0; @@ -87,6 +84,7 @@ public override void Invoke() DumpHeap.PrintHeap(objects, displayKind, Stat, printFragmentation: false); } + private IEnumerable EnumerateFinalizableObjects(bool allReady, ulong mt) { IEnumerable result = EnumerateValidFinalizableObjectsWithTypeFilter(mt); diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/FindEphemeralReferencesToLOHCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/FindEphemeralReferencesToLOHCommand.cs index e1715396da..221c06f502 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/FindEphemeralReferencesToLOHCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/FindEphemeralReferencesToLOHCommand.cs @@ -12,14 +12,11 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "ephtoloh", Help = "Finds ephemeral objects which reference the large object heap.")] - public class FindEphemeralReferencesToLOHCommand : CommandBase + public class FindEphemeralReferencesToLOHCommand : ClrRuntimeCommandBase { // IComparer for binary search private readonly IComparer<(ClrObject, ClrObject)> _firstObjectComparer = Comparer<(ClrObject, ClrObject)>.Create((x, y) => x.Item1.Address.CompareTo(y.Item1.Address)); - [ServiceImport] - public ClrRuntime Runtime { get; set; } - public override void Invoke() { int segments = Runtime.Heap.Segments.Count(seg => seg.Kind is not GCSegmentKind.Frozen or GCSegmentKind.Pinned); diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs index 752bb338c3..755328cf40 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs @@ -62,6 +62,9 @@ public override void Invoke() PrintPointers(!ShowAllObjects, Regions); } + [FilterInvoke(Message = "The memory region service does not exists. This command is only supported under windbg/cdb debuggers.")] + public static bool FilterInvoke([ServiceImport(Optional = true)] ClrRuntime runtime, [ServiceImport(Optional = true)] NativeAddressHelper helper) => runtime != null && helper != null; + private void PrintPointers(bool pinnedOnly, params string[] memTypes) { DescribedRegion[] allRegions = AddressHelper.EnumerateAddressSpace(tagClrMemoryRanges: true, includeReserveMemory: false, tagReserveMemoryHeuristically: false, includeHandleTableIfSlow: false).ToArray(); @@ -496,9 +499,7 @@ public bool IsPinnedObject(ulong address, out ClrObject found) } [HelpInvoke] - public void HelpInvoke() - { - WriteLine( + public static string GetDetailedHelp() => @"------------------------------------------------------------------------------- The findpointersin command will search the regions of memory given by MADDRESS_TYPE_LIST to find all pointers to other memory regions and display them. By default, pointers @@ -508,15 +509,15 @@ random pointer to the GC heap to a non-pinned object is either an old/leftover then this command print out ALL objects that are pointed to instead of collapsing them into one entry. -usage: !findpointersin [--all] MADDRESS_TYPE_LIST +usage: findpointersin [--all] MADDRESS_TYPE_LIST -Note: The MADDRESS_TYPE_LIST must be a memory type as printed by !maddress. +Note: The MADDRESS_TYPE_LIST must be a memory type as printed by maddress. -Example: ""!findpointersin PAGE_READWRITE"" will only search for regions of memory that +Example: ""findpointersin PAGE_READWRITE"" will only search for regions of memory that !maddress marks as ""PAGE_READWRITE"" and not every page of memory that's marked with PAGE_READWRITE protection. -Example: Running the command ""!findpointersin Stack PAGE_READWRITE"" will find all pointers +Example: Running the command ""findpointersin Stack PAGE_READWRITE"" will find all pointers on any ""Stack"" and ""PAGE_READWRITE"" memory segments and summarize those contents into three tables: One table for pointers to the GC heap, one table for pointers where symbols could be resolved, and one table of pointers where we couldn't resolve symbols. @@ -549,7 +550,6 @@ Microsoft.Caching.ClrMD.RawResult[] 2 14 7f063822ae58 ... --------------------------------------------------------- [ TOTALS ] ---------33,360---------72,029--------------- -"); - } +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/FindReferencesToEphemeralCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/FindReferencesToEphemeralCommand.cs index 6614438d78..571a4b97d2 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/FindReferencesToEphemeralCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/FindReferencesToEphemeralCommand.cs @@ -11,11 +11,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "ephrefs", Help = "Finds older generation objects which reference objects in the ephemeral segment.")] - public class FindReferencesToEphemeralCommand : CommandBase + public class FindReferencesToEphemeralCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - private readonly HashSet _referenced = new(); private ulong _referencedSize; @@ -71,7 +68,6 @@ group item by (item.ObjectGeneration, item.ReferenceGeneration) into g Console.WriteLine($"{objCount:n0} older generation objects referenced {_referenced.Count:n0} younger objects ({_referencedSize:n0} bytes)"); } - private IEnumerable FindObjectsWithEphemeralReferences() { foreach (ClrSegment seg in Runtime.Heap.Segments) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/GCHeapStatCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/GCHeapStatCommand.cs index 7a3da01995..3552daa85b 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/GCHeapStatCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/GCHeapStatCommand.cs @@ -11,12 +11,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "gcheapstat", DefaultOptions = "GCHeapStat", Help = "Displays various GC heap stats.")] - public class GCHeapStatCommand : CommandBase + [Command(Name = "gcheapstat", Aliases = new[] { "GCHeapStat" }, Help = "Displays various GC heap stats.")] + public class GCHeapStatCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public LiveObjectService LiveObjects { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs index 7260a22eef..1cf459013a 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs @@ -11,8 +11,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "gcroot", Help = "Displays info about references (or roots) to an object at the specified address.")] - public class GCRootCommand : CommandBase + [Command(Name = "gcroot", Aliases = new[] { "GCRoot" }, Help = "Displays info about references (or roots) to an object at the specified address.")] + public class GCRootCommand : ClrRuntimeCommandBase { private StringBuilder _lineBuilder = new(64); private ClrRoot _lastRoot; @@ -20,9 +20,6 @@ public class GCRootCommand : CommandBase [ServiceImport] public IMemoryService Memory { get; set; } - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public RootCacheService RootCache { get; set; } @@ -68,8 +65,7 @@ public override void Invoke() ClrSegment seg = Runtime.Heap.GetSegmentByAddress(address); if (seg is null) { - Console.WriteLineError($"Address {address:x} is not in the managed heap."); - return; + throw new DiagnosticsException($"Address {address:x} is not in the managed heap."); } Generation objectGen = seg.GetGeneration(address); diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/GCToNativeCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/GCToNativeCommand.cs index e5157d4d31..e3e46d6d9f 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/GCToNativeCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/GCToNativeCommand.cs @@ -24,10 +24,10 @@ public sealed class GCToNativeCommand : CommandBase public bool ShowAll { get; set; } [ServiceImport] - public ClrRuntime Runtime { get; set; } + public NativeAddressHelper AddressHelper { get; set; } [ServiceImport] - public NativeAddressHelper AddressHelper { get; set; } + public ClrRuntime Runtime { get; set; } private int Width { @@ -58,6 +58,9 @@ public override void Invoke() PrintGCPointersToMemory(ShowAll, MemoryTypes); } + [FilterInvoke(Message = "The memory region service does not exists. This command is only supported under windbg/cdb debuggers.")] + public static bool FilterInvoke([ServiceImport(Optional = true)] ClrRuntime runtime, [ServiceImport(Optional = true)] NativeAddressHelper helper) => runtime != null && helper != null; + public void PrintGCPointersToMemory(bool showAll, params string[] memoryTypes) { // Strategy: @@ -556,24 +559,22 @@ private readonly struct MemoryBlockImpl } [HelpInvoke] - public void HelpInvoke() - { - WriteLine( + public static string GetDetailedHelp() => @"------------------------------------------------------------------------------- -!gctonative searches the GC heap for pointers to native memory. This is used +gctonative searches the GC heap for pointers to native memory. This is used to help locate regions of native memory that are referenced (or possibly held alive) by objects on the GC heap. -usage: !gctonative [--all] MADDRESS_TYPE_LIST +usage: gctonative [--all] MADDRESS_TYPE_LIST -Note: The MADDRESS_TYPE_LIST must be a memory type as printed by !maddress. +Note: The MADDRESS_TYPE_LIST must be a memory type as printed by maddress. If --all is set, a full list of every pointer from the GC heap to the specified memory will be displayed instead of just a summary table. Sample Output: - 0:000> !gctonative PAGE_READWRITE + 0:000> gctonative PAGE_READWRITE Walking GC heap to find pointers... Resolving object names... ================================================ PAGE_READWRITE Regions ================================================ @@ -618,7 +619,6 @@ Resolving object names... System.Net.Sockets.SocketAsyncEngine | 1 | 7f059800edd0 Microsoft.Extensions.Caching.Memory.CacheEntry | 1 | 7f05241e0000 System.Runtime.CompilerServices.AsyncTaskMethodBuilder<...>+AsyncStateMachine... | 1 | 7f0500000004 -"); - } +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/GCWhereCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/GCWhereCommand.cs index 96a61f3783..2ae3193846 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/GCWhereCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/GCWhereCommand.cs @@ -11,12 +11,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "gcwhere", Help = "Displays the location in the GC heap of the specified address.")] - public class GCWhereCommand : CommandBase + [Command(Name = "gcwhere", Aliases = new[] { "GCWhere" }, Help = "Displays the location in the GC heap of the specified address.")] + public class GCWhereCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public IMemoryService MemoryService { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/ConsoleLoggingCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/ConsoleLoggingCommand.cs index f274ba4839..38f972e83b 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/ConsoleLoggingCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/ConsoleLoggingCommand.cs @@ -5,8 +5,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "logopen", Help = "Enables console file logging.", Flags = CommandFlags.Global)] - [Command(Name = "logclose", DefaultOptions = "--disable", Help = "Disables console file logging.", Flags = CommandFlags.Global)] + [Command(Name = "logopen", Help = "Enables console file logging.")] + [Command(Name = "logclose", DefaultOptions = "--disable", Help = "Disables console file logging.")] public class ConsoleLoggingCommand : CommandBase { [ServiceImport(Optional = true)] diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/HelpCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/HelpCommand.cs index f7c6db56ad..2770c53247 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/HelpCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/HelpCommand.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.Diagnostics.DebugServices; namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "help", Help = "Displays help for a command.", Flags = CommandFlags.Global)] + [Command(Name = "help", Aliases = new string[] { "soshelp" }, Help = "Displays help for a command.")] public class HelpCommand : CommandBase { [Argument(Help = "Command to find help.")] @@ -20,9 +22,21 @@ public class HelpCommand : CommandBase public override void Invoke() { - if (!CommandService.DisplayHelp(Command, Services)) + if (string.IsNullOrWhiteSpace(Command)) { - throw new NotSupportedException($"Help for {Command} not found"); + IEnumerable<(string Invocation, string Help)> commands = CommandService.GetAllCommandHelp(Services); + int invocationWidth = commands.Max((item) => item.Invocation.Length) + 4; + + Write(string.Concat(commands. + OrderBy(item => item.Invocation, StringComparer.OrdinalIgnoreCase). + Select((item) => $"{FormatInvocation(item.Invocation)}{item.Help}{Environment.NewLine}"))); + + string FormatInvocation(string invocation) => invocation + new string(' ', invocationWidth - invocation.Length); + } + else + { + string helpText = CommandService.GetDetailedHelp(Command, Services, Console.WindowWidth) ?? throw new DiagnosticsException($"Help for {Command} not found"); + Write(helpText); } } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/LoggingCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/LoggingCommand.cs index 18183f6d7e..de53f34e49 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/LoggingCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/LoggingCommand.cs @@ -5,7 +5,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "logging", Help = "Enables/disables internal diagnostic logging.", Flags = CommandFlags.Global)] + [Command(Name = "logging", Help = "Enables/disables internal diagnostic logging.")] public class LoggingCommand : CommandBase { [ServiceImport(Optional = true)] diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/RegistersCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/RegistersCommand.cs index 3ce4d83fbe..78d2c07b07 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/RegistersCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/RegistersCommand.cs @@ -12,7 +12,7 @@ public class RegistersCommand : CommandBase [ServiceImport] public IThreadService ThreadService { get; set; } - [ServiceImport] + [ServiceImport(Optional = true)] public IThread CurrentThread { get; set; } [Option(Name = "--verbose", Aliases = new string[] { "-v" }, Help = "Displays more details.")] diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/SetSymbolServerCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/SetSymbolServerCommand.cs index d94f035d91..fe9ebc59e7 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/SetSymbolServerCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/SetSymbolServerCommand.cs @@ -5,16 +5,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command( - Name = "setsymbolserver", - Aliases = new string[] { "SetSymbolServer" }, - Help = "Enables and sets symbol server support for symbols and module download.", - Flags = CommandFlags.Global)] - [Command( - Name = "loadsymbols", - DefaultOptions = "--loadsymbols", - Help = "Loads symbols for all modules.", - Flags = CommandFlags.Global)] + [Command(Name = "setsymbolserver", Aliases = new string[] { "SetSymbolServer" }, Help = "Enables and sets symbol server support for symbols and module download.")] + [Command(Name = "loadsymbols", DefaultOptions = "--loadsymbols", Help = "Loads symbols for all modules.")] public class SetSymbolServerCommand : CommandBase { [ServiceImport] @@ -107,5 +99,92 @@ public override void Invoke() Write(SymbolService.ToString()); } } + + [HelpInvoke] + public static string GetDetailedHelp(IHost host) + { + switch (host.HostType) + { + case HostType.DbgEng: + return s_detailedHelpTextDbgEng; + case HostType.Lldb: + return s_detailedHelpTextLLDB; + case HostType.DotnetDump: + return s_detailedHelpTextDotNetDump; + } + return null; + } + + private const string s_detailedHelpTextDbgEng = + @" +This commands enables symbol server support for portable PDBs for managed assemblies and +.NET Core native modules files (like the DAC) in SOS. If the .sympath is set, the symbol +server supported is automatically set and this command isn't necessary. +"; + + private const string s_detailedHelpTextLLDB = + @" +This commands enables symbol server support in SOS. The portable PDBs for managed assemblies +and .NET Core native symbol and module (like the DAC) files are downloaded. + +To enable downloading symbols from the Microsoft symbol server: + + (lldb) setsymbolserver -ms + +This command may take some time without any output while it attempts to download the symbol files. + +To disable downloading or clear the current SOS symbol settings allowing new symbol paths to be set: + + (lldb) setsymbolserver -disable + +To add a directory to search for symbols: + + (lldb) setsymbolserver -directory /home/mikem/symbols + +This command can be used so the module/symbol file structure does not have to match the machine +file structure that the core dump was generated. + +To clear the default cache run ""rm -r $HOME/.dotnet/symbolcache"" in a command shell. + +If you receive an error like the one below on a core dump, you need to set the .NET Core +runtime with the ""sethostruntime"" command. Type ""soshelp sethostruntime"" for more details. + + (lldb) setsymbolserver -ms + Error: Fail to initialize CoreCLR 80004005 + SetSymbolServer -ms failed + +The ""-loadsymbols"" option and the ""loadsymbol"" command alias attempts to download the native .NET +Core symbol files. It is only useful for live sessions and not core dumps. This command needs to +be run before the lldb ""bt"" (stack trace) or the ""clrstack -f"" (dumps both managed and native +stack frames). + + (lldb) loadsymbols + (lldb) bt +"; + + private const string s_detailedHelpTextDotNetDump = + @" +This commands enables symbol server support in SOS. The portable PDBs for managed assemblies +and .NET Core native module (like the DAC) files are downloaded. + +To enable downloading symbols from the Microsoft symbol server: + + > setsymbolserver -ms + +This command may take some time without any output while it attempts to download the symbol files. + +To disable downloading or clear the current SOS symbol settings allowing new symbol paths to be set: + + > setsymbolserver -disable + +To add a directory to search for symbols: + + > setsymbolserver -directory /home/mikem/symbols + +This command can be used so the module/symbol file structure does not have to match the machine +file structure that the core dump was generated. + +To clear the default cache run ""rm -r $HOME/.dotnet/symbolcache"" in a command shell. +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ListNearObjCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ListNearObjCommand.cs index 1b4411925f..afeffcbfed 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ListNearObjCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ListNearObjCommand.cs @@ -11,12 +11,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "listnearobj", Help = "Displays the object preceding and succeeding the specified address.")] - public class ListNearObjCommand : CommandBase + [Command(Name = "listnearobj", Aliases = new[] { "lno", "ListNearObj" }, Help = "Displays the object preceding and succeeding the specified address.")] + public class ListNearObjCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public IMemoryService MemoryService { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs index fa221c96ef..9a1774fdc6 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs @@ -196,15 +196,16 @@ orderby Size descending } } + [FilterInvoke(Message = "The memory region service does not exists. This command is only supported under windbg/cdb debuggers.")] + public static bool FilterInvoke([ServiceImport(Optional = true)] NativeAddressHelper helper) => helper != null; + [HelpInvoke] - public void HelpInvoke() - { - WriteLine( + public static string GetDetailedHelp() => $@"------------------------------------------------------------------------------- -maddress is a managed version of !address, which attempts to annotate all memory +!maddress is a managed version of !address, which attempts to annotate all memory with information about CLR's heaps. -usage: !sos maddress [{SummaryFlag}] [{ImagesFlag}] [{ForceHandleTableFlag}] [{ReserveFlag} [{ReserveHeuristicFlag}]] +usage: !maddress [{SummaryFlag}] [{ImagesFlag}] [{ForceHandleTableFlag}] [{ReserveFlag} [{ReserveHeuristicFlag}]] Flags: {SummaryFlag} @@ -238,7 +239,6 @@ A separated list of memory region types (as maddress defines them) to print the {BySizeFlag} Order the list of memory blocks by size (descending) when printing the list of all memory blocks instead of by address. -"); - } +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs index a8e46ed7c3..7de6a766af 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs @@ -12,22 +12,29 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [ServiceExport(Scope = ServiceScope.Target)] public sealed class NativeAddressHelper : IDisposable { private readonly IDisposable _onFlushEvent; private ((bool, bool, bool, bool) Key, DescribedRegion[] Result) _previous; - public NativeAddressHelper(ITarget target) + [ServiceExport(Scope = ServiceScope.Target)] + public static NativeAddressHelper TryCreate(ITarget target, [ServiceImport(Optional = true)] IMemoryRegionService memoryRegionService) + { + return memoryRegionService != null ? new NativeAddressHelper(target, memoryRegionService) : null; + } + + private NativeAddressHelper(ITarget target, IMemoryRegionService memoryRegionService) { Target = target; + MemoryRegionService = memoryRegionService; _onFlushEvent = target.OnFlushEvent.Register(() => _previous = default); } public void Dispose() => _onFlushEvent.Dispose(); - [ServiceImport] - public ITarget Target { get; set; } + public ITarget Target { get; } + + public IMemoryRegionService MemoryRegionService { get; } [ServiceImport] public IMemoryService MemoryService { get; set; } @@ -41,9 +48,6 @@ public NativeAddressHelper(ITarget target) [ServiceImport] public IModuleService ModuleService { get; set; } - [ServiceImport] - public IMemoryRegionService MemoryRegionService { get; set; } - [ServiceImport] public IConsoleService Console { get; set; } @@ -107,9 +111,9 @@ private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, b foreach (IRuntime runtime in RuntimeService.EnumerateRuntimes()) { ClrRuntime clrRuntime = runtime.Services.GetService(); - RootCacheService rootCache = runtime.Services.GetService(); if (clrRuntime is not null) { + RootCacheService rootCache = runtime.Services.GetService() ?? throw new DiagnosticsException("NativeAddressHelper: RootCacheService not found"); foreach ((ulong Address, ulong Size, ClrMemoryKind Kind) mem in EnumerateClrMemoryAddresses(clrRuntime, rootCache, includeHandleTableIfSlow)) { // The GCBookkeeping range is a large region of memory that the GC reserved. We'll simply mark every diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/NotReachableInRangeCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/NotReachableInRangeCommand.cs index 59e399e303..27ec0b6fe4 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/NotReachableInRangeCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/NotReachableInRangeCommand.cs @@ -14,7 +14,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands /// Prints objects and statistics for a range of object pointers. /// [Command(Name = "notreachableinrange", Help = "A helper command for !finalizerqueue")] - public class NotReachableInRangeCommand : CommandBase + public class NotReachableInRangeCommand : ClrRuntimeCommandBase { private HashSet _nonFQLiveObjects; @@ -30,9 +30,6 @@ public class NotReachableInRangeCommand : CommandBase [ServiceImport] public IMemoryService Memory { get; set; } - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [Option(Name = "-short")] public bool Short { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs index 0beb0e98f0..c65f381fa4 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ObjSizeCommand.cs @@ -8,12 +8,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "objsize", Help = "Lists the sizes of the all the objects found on managed threads.")] - public class ObjSizeCommand : CommandBase + [Command(Name = "objsize", Aliases = new[] { "ObjSize" }, Help = "Lists the sizes of the all the objects found on managed threads.")] + public class ObjSizeCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public DumpHeapService DumpHeap { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs index f61442a902..3320f11d34 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs @@ -9,17 +9,17 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "parallelstacks", Aliases = new string[] { "pstacks" }, Help = "Displays the merged threads stack similarly to the Visual Studio 'Parallel Stacks' panel.")] - public class ParallelStacksCommand : ExtensionCommandBase + public class ParallelStacksCommand : ClrMDHelperCommandBase { - [ServiceImport] + [ServiceImport(Optional = true)] public ClrRuntime Runtime { get; set; } [Option(Name = "--allthreads", Aliases = new string[] { "-a" }, Help = "Displays all threads per group instead of at most 4 by default.")] public bool AllThreads { get; set; } - public override void ExtensionInvoke() + public override void Invoke() { - ParallelStack ps = ParallelStacks.Runtime.ParallelStack.Build(Runtime); + ParallelStack ps = ParallelStack.Build(Runtime); if (ps == null) { return; @@ -41,47 +41,41 @@ public override void ExtensionInvoke() WriteLine($"==> {ps.ThreadIds.Count} threads with {ps.Stacks.Count} roots{Environment.NewLine}"); } - protected override string GetDetailedHelp() - { - return DetailedHelpText; - } + [HelpInvoke] + public static string GetDetailedHelp() => +@"------------------------------------------------------------------------------- +ParallelStacks + +pstacks groups the callstack of all running threads and shows a merged display a la Visual Studio 'Parallel Stacks' panel +By default, only 4 threads ID per frame group are listed. Use --allThreads/-a to list all threads ID. + +> pstacks +________________________________________________ +~~~~ 8f8c + 1 (dynamicClass).IL_STUB_PInvoke(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) + ... + 1 System.Console.ReadLine() + 1 NetCoreConsoleApp.Program.Main(String[]) + +________________________________________________ + ~~~~ 7034 + 1 System.Threading.Monitor.Wait(Object, Int32, Boolean) + ... + 1 System.Threading.Tasks.Task.Wait() + 1 NetCoreConsoleApp.Program+c.b__1_4(Object) + ~~~~ 9c6c,4020 + 2 System.Threading.Monitor.Wait(Object, Int32, Boolean) + ... + 2 NetCoreConsoleApp.Program+c__DisplayClass1_0.b__7() + 3 System.Threading.Tasks.Task.InnerInvoke() + 4 System.Threading.Tasks.Task+c.cctor>b__278_1(Object) + ... + 4 System.Threading.Tasks.Task.ExecuteEntryUnsafe() + 4 System.Threading.Tasks.Task.ExecuteWorkItem() + 7 System.Threading.ThreadPoolWorkQueue.Dispatch() + 7 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() - private readonly string DetailedHelpText = - "-------------------------------------------------------------------------------" + Environment.NewLine + - "ParallelStacks" + Environment.NewLine + - Environment.NewLine + - "pstacks groups the callstack of all running threads and shows a merged display a la Visual Studio 'Parallel Stacks' panel" + Environment.NewLine + - "By default, only 4 threads ID per frame group are listed. Use --allThreads/-a to list all threads ID." + Environment.NewLine + - Environment.NewLine + - "> pstacks" + Environment.NewLine + - "________________________________________________" + Environment.NewLine + - "~~~~ 8f8c" + Environment.NewLine + - " 1 (dynamicClass).IL_STUB_PInvoke(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)" + Environment.NewLine + - " ..." + Environment.NewLine + - " 1 System.Console.ReadLine()" + Environment.NewLine + - " 1 NetCoreConsoleApp.Program.Main(String[])" + Environment.NewLine + - Environment.NewLine + - "________________________________________________" + Environment.NewLine + - " ~~~~ 7034" + Environment.NewLine + - " 1 System.Threading.Monitor.Wait(Object, Int32, Boolean)" + Environment.NewLine + - " ..." + Environment.NewLine + - " 1 System.Threading.Tasks.Task.Wait()" + Environment.NewLine + - " 1 NetCoreConsoleApp.Program+c.b__1_4(Object)" + Environment.NewLine + - " ~~~~ 9c6c,4020" + Environment.NewLine + - " 2 System.Threading.Monitor.Wait(Object, Int32, Boolean)" + Environment.NewLine + - " ..." + Environment.NewLine + - " 2 NetCoreConsoleApp.Program+c__DisplayClass1_0.b__7()" + Environment.NewLine + - " 3 System.Threading.Tasks.Task.InnerInvoke()" + Environment.NewLine + - " 4 System.Threading.Tasks.Task+c.cctor>b__278_1(Object)" + Environment.NewLine + - " ..." + Environment.NewLine + - " 4 System.Threading.Tasks.Task.ExecuteEntryUnsafe()" + Environment.NewLine + - " 4 System.Threading.Tasks.Task.ExecuteWorkItem()" + Environment.NewLine + - " 7 System.Threading.ThreadPoolWorkQueue.Dispatch()" + Environment.NewLine + - " 7 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()" + Environment.NewLine + - Environment.NewLine + - "==> 8 threads with 2 roots" + Environment.NewLine + - Environment.NewLine + - "" - ; +==> 8 threads with 2 roots +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/PathToCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/PathToCommand.cs index e63940d3be..ac0975b859 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/PathToCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/PathToCommand.cs @@ -7,12 +7,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name ="pathto", Help = "Displays the GC path from to .")] - public class PathToCommand : CommandBase + [Command(Name ="pathto", Aliases = new[] { "PathTo" }, Help = "Displays the GC path from to .")] + public class PathToCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public RootCacheService RootCache { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/SimulateGCHeapCorruption.cs b/src/Microsoft.Diagnostics.ExtensionCommands/SimulateGCHeapCorruption.cs index 2105875b9d..e018cf218b 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/SimulateGCHeapCorruption.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/SimulateGCHeapCorruption.cs @@ -13,16 +13,13 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [DebugCommand(Name=nameof(SimulateGCHeapCorruption), Help = "Writes values to the GC heap in strategic places to simulate heap corruption.")] - public class SimulateGCHeapCorruption : CommandBase + public class SimulateGCHeapCorruption : ClrRuntimeCommandBase { private static readonly List _changes = new(); [ServiceImport] public IMemoryService MemoryService { get; set; } - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [Argument] public string Command { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/SizeStatsCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/SizeStatsCommand.cs index b8f67a7a5f..a1b59b07a0 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/SizeStatsCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/SizeStatsCommand.cs @@ -10,11 +10,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "sizestats", Help = "Size statistics for the GC heap.")] - public sealed class SizeStatsCommand : CommandBase + public sealed class SizeStatsCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - public override void Invoke() { SizeStats(Generation.Generation0, isFree: false); diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/TaskStateCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/TaskStateCommand.cs index 63a7e4536c..716fec0903 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/TaskStateCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/TaskStateCommand.cs @@ -7,7 +7,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "taskstate", Aliases = new string[] { "tks" }, Help = "Displays a Task state in a human readable format.")] - public class TaskStateCommand : ExtensionCommandBase + public class TaskStateCommand : ClrMDHelperCommandBase { [Argument(Help = "The Task instance address.")] public string Address { get; set; } @@ -15,7 +15,7 @@ public class TaskStateCommand : ExtensionCommandBase [Option(Name = "--value", Aliases = new string[] { "-v" }, Help = " is the value of a Task m_stateFlags field.")] public ulong? Value { get; set; } - public override void ExtensionInvoke() + public override void Invoke() { if (string.IsNullOrEmpty(Address) && !Value.HasValue) { @@ -57,23 +57,19 @@ public override void ExtensionInvoke() } - protected override string GetDetailedHelp() - { - return DetailedHelpText; - } + [HelpInvoke] + public static string GetDetailedHelp() => +@"------------------------------------------------------------------------------- +TaskState [hexa address] [-v ] + +TaskState translates a Task m_stateFlags field value into human readable format. +It supports hexadecimal address corresponding to a task instance or -v . + +> tks 000001db16cf98f0 +Running - private readonly string DetailedHelpText = - "-------------------------------------------------------------------------------" + Environment.NewLine + - "TaskState [hexa address] [-v ]" + Environment.NewLine + - Environment.NewLine + - "TaskState translates a Task m_stateFlags field value into human readable format." + Environment.NewLine + - "It supports hexadecimal address corresponding to a task instance or -v ." + Environment.NewLine + - Environment.NewLine + - "> tks 000001db16cf98f0" + Environment.NewLine + - "Running" + Environment.NewLine + - Environment.NewLine + - "> tks -v 73728" + Environment.NewLine + - "WaitingToRun" - ; +> tks -v 73728 +WaitingToRun +"; } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs index 621b6a501c..f78549ffca 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolCommand.cs @@ -11,12 +11,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "threadpool", Help = "Displays info about the runtime thread pool.")] - public sealed class ThreadPoolCommand : CommandBase + [Command(Name = "threadpool", Aliases = new[] { "ThreadPool" }, Help = "Displays info about the runtime thread pool.")] + public sealed class ThreadPoolCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [Option(Name = "-ti", Help = "Print the hill climbing log.", Aliases = new string[] { "-hc" })] public bool PrintHillClimbingLog { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolQueueCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolQueueCommand.cs index bee1b08f44..85b5d9b4ed 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolQueueCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolQueueCommand.cs @@ -9,9 +9,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "threadpoolqueue", Aliases = new string[] { "tpq" }, Help = "Displays queued ThreadPool work items.")] - public class ThreadPoolQueueCommand : ExtensionCommandBase + public class ThreadPoolQueueCommand : ClrMDHelperCommandBase { - public override void ExtensionInvoke() + public override void Invoke() { Dictionary workItems = new(); int workItemCount = 0; @@ -102,42 +102,36 @@ private static void UpdateStats(Dictionary stats, string statN wi.Count++; } - protected override string GetDetailedHelp() - { - return DetailedHelpText; - } + [HelpInvoke] + public static string GetDetailedHelp() => +@"------------------------------------------------------------------------------- +ThreadPoolQueue + +ThreadPoolQueue lists the enqueued work items in the Clr Thread Pool followed by a summary of the different tasks/work items. +The global queue is first iterated before local per-thread queues. +The name of the method to be called (on which instance if any) is also provided when available. + +> tpq + +global work item queue________________________________ +0x000002AC3C1DDBB0 Work | (ASP.global_asax)System.Web.HttpApplication.ResumeStepsWaitCallback + ... +0x000002AABEC19148 Task | System.Threading.Tasks.Dataflow.Internal.TargetCore.b__3 + +local per thread work items_____________________________________ +0x000002AE79D80A00 System.Threading.Tasks.ContinuationTaskFromTask + ... +0x000002AB7CBB84A0 Task | System.Net.Http.HttpClientHandler.StartRequest + + 7 Task System.Threading.Tasks.Dataflow.Internal.TargetCore.b__3 + ... + 84 Task System.Net.Http.HttpClientHandler.StartRequest +---- +6039 - private readonly string DetailedHelpText = - "-------------------------------------------------------------------------------" + Environment.NewLine + - "ThreadPoolQueue" + Environment.NewLine + - Environment.NewLine + - "ThreadPoolQueue lists the enqueued work items in the Clr Thread Pool followed by a summary of the different tasks/work items." + Environment.NewLine + - "The global queue is first iterated before local per-thread queues." + Environment.NewLine + - "The name of the method to be called (on which instance if any) is also provided when available." + Environment.NewLine + - Environment.NewLine + - "> tpq" + Environment.NewLine + - Environment.NewLine + - "global work item queue________________________________" + Environment.NewLine + - "0x000002AC3C1DDBB0 Work | (ASP.global_asax)System.Web.HttpApplication.ResumeStepsWaitCallback" + Environment.NewLine + - " ..." + Environment.NewLine + - "0x000002AABEC19148 Task | System.Threading.Tasks.Dataflow.Internal.TargetCore.b__3" + Environment.NewLine + - "" + Environment.NewLine + - "local per thread work items_____________________________________" + Environment.NewLine + - "0x000002AE79D80A00 System.Threading.Tasks.ContinuationTaskFromTask" + Environment.NewLine + - " ..." + Environment.NewLine + - "0x000002AB7CBB84A0 Task | System.Net.Http.HttpClientHandler.StartRequest" + Environment.NewLine + - "" + Environment.NewLine + - " 7 Task System.Threading.Tasks.Dataflow.Internal.TargetCore.b__3" + Environment.NewLine + - " ..." + Environment.NewLine + - " 84 Task System.Net.Http.HttpClientHandler.StartRequest" + Environment.NewLine + - "----" + Environment.NewLine + - "6039" + Environment.NewLine + - "" + Environment.NewLine + - "1810 Work (ASP.global_asax) System.Web.HttpApplication.ResumeStepsWaitCallback" + Environment.NewLine + - "----" + Environment.NewLine + - "1810" + Environment.NewLine + - "" - ; +1810 Work (ASP.global_asax) System.Web.HttpApplication.ResumeStepsWaitCallback +---- +1810"; private sealed class WorkInfo { diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/TimersCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/TimersCommand.cs index 6a040242a6..2c576c7193 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/TimersCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/TimersCommand.cs @@ -9,9 +9,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { [Command(Name = "timerinfo", Aliases = new string[] { "ti" }, Help = "Displays information about running timers.")] - public class TimersCommand : ExtensionCommandBase + public class TimersCommand : ClrMDHelperCommandBase { - public override void ExtensionInvoke() + public override void Invoke() { try { @@ -104,33 +104,28 @@ private static string GetTimerString(TimerInfo timer) } - protected override string GetDetailedHelp() - { - return DetailedHelpText; - } + [HelpInvoke] + public static string GetDetailedHelp() => +@"------------------------------------------------------------------------------- +TimerInfo - private readonly string DetailedHelpText = - "-------------------------------------------------------------------------------" + Environment.NewLine + - "TimerInfo" + Environment.NewLine + - Environment.NewLine + - "TimerInfo lists all the running timers followed by a summary of the different items." + Environment.NewLine + - "The name of the method to be called (on which instance if any) is also provided when available." + Environment.NewLine + - Environment.NewLine + - "> ti" + Environment.NewLine + - "0x000001E29BD45848 @ 964 ms every 1000 ms | 0x000001E29BD0C828 (Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.Heartbeat) ->" + Environment.NewLine + - "0x000001E19BD0F868 @ 1 ms every ------ ms | 0x000001E19BD0F800 (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1" + Environment.NewLine + - "0x000001E09BD09B40 @ 1 ms every ------ ms | 0x000001E09BD09AD8 (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1" + Environment.NewLine + - "0x000001E29BD58C68 @ 1 ms every ------ ms | 0x000001E29BD58C00 (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1" + Environment.NewLine + - "0x000001E29BCB1398 @ 5000 ms every ------ ms | 0x0000000000000000 () -> System.Diagnostics.Tracing.EventPipeController.PollForTracingCommand" + Environment.NewLine + - Environment.NewLine + - " 5 timers" + Environment.NewLine + - "-----------------------------------------------" + Environment.NewLine + - " 1 | @ 964 ms every 1000 ms | (Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.Heartbeat) ->" + Environment.NewLine + - " 1 | @ 5000 ms every ------ ms | () -> System.Diagnostics.Tracing.EventPipeController.PollForTracingCommand" + Environment.NewLine + - " 3 | @ 1 ms every ------ ms | (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1" - ; - } +TimerInfo lists all the running timers followed by a summary of the different items. +The name of the method to be called (on which instance if any) is also provided when available. +> ti +0x000001E29BD45848 @ 964 ms every 1000 ms | 0x000001E29BD0C828 (Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.Heartbeat) -> +0x000001E19BD0F868 @ 1 ms every ------ ms | 0x000001E19BD0F800 (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1 +0x000001E09BD09B40 @ 1 ms every ------ ms | 0x000001E09BD09AD8 (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1 +0x000001E29BD58C68 @ 1 ms every ------ ms | 0x000001E29BD58C00 (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1 +0x000001E29BCB1398 @ 5000 ms every ------ ms | 0x0000000000000000 () -> System.Diagnostics.Tracing.EventPipeController.PollForTracingCommand + + 5 timers +----------------------------------------------- + 1 | @ 964 ms every 1000 ms | (Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.Heartbeat) -> + 1 | @ 5000 ms every ------ ms | () -> System.Diagnostics.Tracing.EventPipeController.PollForTracingCommand + 3 | @ 1 ms every ------ ms | (System.Threading.Tasks.Task+DelayPromise) -> System.Threading.Tasks.Task+<>c.b__260_1 +"; + } internal sealed class TimerStat { diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/TraverseHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/TraverseHeapCommand.cs index 40d2820502..57315180bb 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/TraverseHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/TraverseHeapCommand.cs @@ -12,12 +12,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "traverseheap", Help = "Writes out heap information to a file in a format understood by the CLR Profiler.")] - public class TraverseHeapCommand : CommandBase + [Command(Name = "traverseheap", Aliases = new[] { "TraverseHeap" }, Help = "Writes out heap information to a file in a format understood by the CLR Profiler.")] + public class TraverseHeapCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public RootCacheService RootCache { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs index e5229cbf32..c7b7b19578 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/VerifyHeapCommand.cs @@ -9,16 +9,13 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = CommandName, Help = "Searches the managed heap for memory corruption..")] - public class VerifyHeapCommand : CommandBase + [Command(Name = CommandName, Aliases = new[] { "VerifyHeap" }, Help = "Searches the managed heap for memory corruption..")] + public class VerifyHeapCommand : ClrRuntimeCommandBase { private const string CommandName = "verifyheap"; private int _totalObjects; - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public IMemoryService MemoryService { get; set; } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/VerifyObjectCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/VerifyObjectCommand.cs index f952aca361..a9779d337b 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/VerifyObjectCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/VerifyObjectCommand.cs @@ -11,16 +11,13 @@ namespace Microsoft.Diagnostics.ExtensionCommands { - [Command(Name = "verifyobj", Help = "Checks the given object for signs of corruption.")] - public sealed class VerifyObjectCommand : CommandBase + [Command(Name = "verifyobj", Aliases = new[] { "VerifyObj" }, Help = "Checks the given object for signs of corruption.")] + public sealed class VerifyObjectCommand : ClrRuntimeCommandBase { - [ServiceImport] - public ClrRuntime Runtime { get; set; } - [ServiceImport] public IMemoryService Memory { get; set; } - [Argument(Name = "ObjectAddress", Help = "The object to verify.")] + [Argument(Name = "objectaddress", Help = "The object to verify.")] public string ObjectAddress { get; set; } public override void Invoke() diff --git a/src/Microsoft.Diagnostics.Repl/ConsoleService.cs b/src/Microsoft.Diagnostics.Repl/ConsoleService.cs index 06aaf7b478..4147eabb3d 100644 --- a/src/Microsoft.Diagnostics.Repl/ConsoleService.cs +++ b/src/Microsoft.Diagnostics.Repl/ConsoleService.cs @@ -508,8 +508,14 @@ private bool Dispatch(string newCommand, Action _serviceManager; + + public ServiceContainer ServiceContainer => _serviceContainer; + protected override ITarget GetTarget() { _dataTarget = DataTarget.LoadDump(DumpFile); diff --git a/src/Microsoft.Diagnostics.TestHelpers/TestHost/TestHost.cs b/src/Microsoft.Diagnostics.TestHelpers/TestHost/TestHost.cs index c86b121a63..93eea0eec2 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/TestHost/TestHost.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/TestHost/TestHost.cs @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using Microsoft.Diagnostics.DebugServices; namespace Microsoft.Diagnostics.TestHelpers { - public abstract class TestHost + public abstract class TestHost : IDisposable { private TestDataReader _testData; private ITarget _target; @@ -17,6 +18,12 @@ public TestHost(TestConfiguration config) Config = config; } + public virtual void Dispose() + { + _target?.Destroy(); + _target = null; + } + public TestDataReader TestData { get diff --git a/src/SOS/SOS.Extensions/DebuggerServices.cs b/src/SOS/SOS.Extensions/DebuggerServices.cs index 97ea031083..b5702a7a82 100644 --- a/src/SOS/SOS.Extensions/DebuggerServices.cs +++ b/src/SOS/SOS.Extensions/DebuggerServices.cs @@ -11,11 +11,12 @@ using Microsoft.Diagnostics.DebugServices; using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime.Utilities; +using SOS.Hosting; using SOS.Hosting.DbgEng.Interop; namespace SOS.Extensions { - internal sealed unsafe class DebuggerServices : CallableCOMWrapper + internal sealed unsafe class DebuggerServices : CallableCOMWrapper, SOSHost.INativeClient { internal enum OperatingSystem { @@ -40,6 +41,7 @@ internal DebuggerServices(IntPtr punk, HostType hostType) : base(new RefCountedFreeLibrary(IntPtr.Zero), IID_IDebuggerServices, punk) { _hostType = hostType; + Client = punk; // This uses COM marshalling code, so we also check that the OSPlatform is Windows. if (hostType == HostType.DbgEng && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -52,6 +54,12 @@ internal DebuggerServices(IntPtr punk, HostType hostType) } } + #region INativeClient + + public IntPtr Client { get; } + + #endregion + public HResult GetOperatingSystem(out OperatingSystem operatingSystem) { return VTable.GetOperatingSystem(Self, out operatingSystem); diff --git a/src/SOS/SOS.Extensions/HostServices.cs b/src/SOS/SOS.Extensions/HostServices.cs index 900f6f43ca..9ee85d1942 100644 --- a/src/SOS/SOS.Extensions/HostServices.cs +++ b/src/SOS/SOS.Extensions/HostServices.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; using Microsoft.Diagnostics.DebugServices; using Microsoft.Diagnostics.DebugServices.Implementation; using Microsoft.Diagnostics.ExtensionCommands; @@ -20,7 +20,7 @@ namespace SOS.Extensions /// /// The extension services Wrapper the native hosts are given /// - public sealed unsafe class HostServices : COMCallableIUnknown, IHost + public sealed unsafe class HostServices : COMCallableIUnknown, IHost, SOSLibrary.ISOSModule { private static readonly Guid IID_IHostServices = new("27B2CB8D-BDEE-4CBD-B6EF-75880D76D46F"); @@ -42,6 +42,7 @@ private delegate int InitializeCallbackDelegate( private readonly SymbolService _symbolService; private readonly HostWrapper _hostWrapper; private ServiceContainer _serviceContainer; + private ServiceContainer _servicesWithManagedOnlyFilter; private ContextServiceFromDebuggerServices _contextService; private int _targetIdFactory; private ITarget _target; @@ -101,14 +102,17 @@ public static int Initialize( return HResult.E_FAIL; } Debug.Assert(Instance == null); - Instance = new HostServices(); + Instance = new HostServices(extensionPath, extensionLibrary); return initialializeCallback(Instance.IHostServices); } - private HostServices() + private HostServices(string extensionPath, IntPtr extensionsLibrary) { + SOSPath = Path.GetDirectoryName(extensionPath); + SOSHandle = extensionsLibrary; + _serviceManager = new ServiceManager(); - _commandService = new CommandService(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ">!ext" : null); + _commandService = new CommandService(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ">!sos" : null); _serviceManager.NotifyExtensionLoad.Register(_commandService.AddCommands); _symbolService = new SymbolService(this) @@ -128,7 +132,6 @@ private HostServices() builder.AddMethod(new FlushTargetDelegate(FlushTarget)); builder.AddMethod(new DestroyTargetDelegate(DestroyTarget)); builder.AddMethod(new DispatchCommandDelegate(DispatchCommand)); - builder.AddMethod(new DisplayHelpDelegate(DisplayHelp)); builder.AddMethod(new UninitializeDelegate(Uninitialize)); IHostServices = builder.Complete(); @@ -193,16 +196,15 @@ private int RegisterDebuggerServices( FileLoggingConsoleService fileLoggingConsoleService = new(consoleService); DiagnosticLoggingService.Instance.SetConsole(consoleService, fileLoggingConsoleService); - // Don't register everything in the SOSHost assembly; just the wrappers - _serviceManager.RegisterExportedServices(typeof(TargetWrapper)); - _serviceManager.RegisterExportedServices(typeof(RuntimeWrapper)); - // Register all the services and commands in the Microsoft.Diagnostics.DebugServices.Implementation assembly _serviceManager.RegisterAssembly(typeof(Target).Assembly); // Register all the services and commands in the SOS.Extensions (this) assembly _serviceManager.RegisterAssembly(typeof(HostServices).Assembly); + // Register all the services and commands in the SOS.Hosting assembly + _serviceManager.RegisterAssembly(typeof(SOSHost).Assembly); + // Register all the services and commands in the Microsoft.Diagnostics.ExtensionCommands assembly _serviceManager.RegisterAssembly(typeof(ClrMDHelper).Assembly); @@ -220,6 +222,8 @@ private int RegisterDebuggerServices( _serviceContainer = _serviceManager.CreateServiceContainer(ServiceScope.Global, parent: null); _serviceContainer.AddService(_serviceManager); _serviceContainer.AddService(this); + _serviceContainer.AddService(this); + _serviceContainer.AddService(DebuggerServices); _serviceContainer.AddService(_commandService); _serviceContainer.AddService(_symbolService); _serviceContainer.AddService(fileLoggingConsoleService); @@ -232,6 +236,10 @@ private int RegisterDebuggerServices( ThreadUnwindServiceFromDebuggerServices threadUnwindService = new(DebuggerServices); _serviceContainer.AddService(threadUnwindService); + // Used to invoke only managed commands + _servicesWithManagedOnlyFilter = new(_contextService.Services); + _servicesWithManagedOnlyFilter.AddService(new SOSCommandBase.ManagedOnlyCommandFilter()); + // Add each extension command to the native debugger foreach ((string name, string help, IEnumerable aliases) in _commandService.Commands) { @@ -241,12 +249,6 @@ private int RegisterDebuggerServices( Trace.TraceWarning($"Cannot add extension command {hr:X8} {name} - {help}"); } } - - if (DebuggerServices.DebugClient is IDebugControl5 control) - { - MemoryRegionServiceFromDebuggerServices memRegions = new(DebuggerServices.DebugClient, control); - _serviceContainer.AddService(memRegions); - } } catch (Exception ex) { @@ -349,56 +351,27 @@ private int DispatchCommand( { return HResult.E_INVALIDARG; } - if (!_commandService.IsCommand(commandName)) - { - return HResult.E_NOTIMPL; - } try { - StringBuilder sb = new(); - sb.Append(commandName); - if (!string.IsNullOrWhiteSpace(commandArguments)) - { - sb.Append(' '); - sb.Append(commandArguments); - } - if (_commandService.Execute(sb.ToString(), _contextService.Services)) + if (_commandService.Execute(commandName, commandArguments, commandName == "help" ? _contextService.Services : _servicesWithManagedOnlyFilter)) { return HResult.S_OK; } - } - catch (CommandNotSupportedException) - { - return HResult.E_NOTIMPL; - } - catch (Exception ex) - { - Trace.TraceError(ex.ToString()); - } - return HResult.E_FAIL; - } - - private int DisplayHelp( - IntPtr self, - string commandName) - { - try - { - if (!_commandService.DisplayHelp(commandName, _contextService.Services)) + else { - return HResult.E_INVALIDARG; + // The command was not found or supported + return HResult.E_NOTIMPL; } } - catch (CommandNotSupportedException) - { - return HResult.E_NOTIMPL; - } catch (Exception ex) { Trace.TraceError(ex.ToString()); - return HResult.E_FAIL; + IConsoleService consoleService = Services.GetService(); + // TODO: when we can figure out how to deal with error messages in the scripts that are displayed on STDERROR under lldb + //consoleService.WriteLineError(ex.Message); + consoleService.WriteLine(ex.Message); } - return HResult.S_OK; + return HResult.E_FAIL; } private void Uninitialize( @@ -436,6 +409,14 @@ private void Uninitialize( #endregion + #region SOSLibrary.ISOSModule + + public string SOSPath { get; } + + public IntPtr SOSHandle { get; } + + #endregion + #region IHostServices delegates [UnmanagedFunctionPointer(CallingConvention.Winapi)] @@ -471,11 +452,6 @@ private delegate int DispatchCommandDelegate( [In, MarshalAs(UnmanagedType.LPStr)] string commandName, [In, MarshalAs(UnmanagedType.LPStr)] string commandArguments); - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - private delegate int DisplayHelpDelegate( - [In] IntPtr self, - [In, MarshalAs(UnmanagedType.LPStr)] string commandName); - [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void UninitializeDelegate( [In] IntPtr self); diff --git a/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs b/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs index 15c729c1df..403bcbc879 100644 --- a/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs @@ -15,10 +15,10 @@ internal sealed class MemoryRegionServiceFromDebuggerServices : IMemoryRegionSer private readonly IDebugClient5 _client; private readonly IDebugControl5 _control; - public MemoryRegionServiceFromDebuggerServices(IDebugClient5 client, IDebugControl5 control) + public MemoryRegionServiceFromDebuggerServices(IDebugClient5 client) { _client = client; - _control = control; + _control = (IDebugControl5)client; } public IEnumerable EnumerateRegions() diff --git a/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs b/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs index 60aea85883..e0c2bee85b 100644 --- a/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs @@ -99,6 +99,11 @@ internal TargetFromDebuggerServices(DebuggerServices debuggerServices, IHost hos _serviceContainerFactory.AddServiceFactory((services) => CreateCrashInfoService(services, debuggerServices)); OnFlushEvent.Register(() => FlushService()); + if (debuggerServices.DebugClient is not null) + { + _serviceContainerFactory.AddServiceFactory((services) => new MemoryRegionServiceFromDebuggerServices(debuggerServices.DebugClient)); + } + Finished(); } diff --git a/src/SOS/SOS.Hosting/Commands/SOSCommand.cs b/src/SOS/SOS.Hosting/Commands/SOSCommand.cs index 1190fd29ab..dfa4a32532 100644 --- a/src/SOS/SOS.Hosting/Commands/SOSCommand.cs +++ b/src/SOS/SOS.Hosting/Commands/SOSCommand.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Diagnostics; -using System.IO; using System.Linq; +using System.Runtime.InteropServices; using Microsoft.Diagnostics.DebugServices; namespace SOS.Hosting @@ -43,17 +42,45 @@ namespace SOS.Hosting [Command(Name = "ip2md", DefaultOptions = "IP2MD", Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")] [Command(Name = "name2ee", DefaultOptions = "Name2EE", Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")] [Command(Name = "printexception", DefaultOptions = "PrintException", Aliases = new string[] { "pe" }, Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")] - [Command(Name = "soshelp", DefaultOptions = "Help", Help = "Displays help for a specific SOS command.")] [Command(Name = "syncblk", DefaultOptions = "SyncBlk", Help = "Displays the SyncBlock holder info.")] [Command(Name = "threadstate", DefaultOptions = "ThreadState", Help = "Pretty prints the meaning of a threads state.")] - [Command(Name = "comstate", DefaultOptions = "COMState", Flags = CommandFlags.Windows, Help = "Lists the COM apartment model for each thread.")] - [Command(Name = "dumprcw", DefaultOptions = "DumpRCW", Flags = CommandFlags.Windows, Help = "Displays information about a Runtime Callable Wrapper.")] - [Command(Name = "dumpccw", DefaultOptions = "DumpCCW", Flags = CommandFlags.Windows, Help = "Displays information about a COM Callable Wrapper.")] - [Command(Name = "dumppermissionset", DefaultOptions = "DumpPermissionSet", Flags = CommandFlags.Windows, Help = "Displays a PermissionSet object (debug build only).")] - [Command(Name = "gchandleleaks", DefaultOptions = "GCHandleLeaks", Flags = CommandFlags.Windows, Help = "Helps in tracking down GCHandle leaks")] - [Command(Name = "watsonbuckets", DefaultOptions = "WatsonBuckets", Flags = CommandFlags.Windows, Help = "Displays the Watson buckets.")] - public class SOSCommand : CommandBase + public class SOSCommand : SOSCommandBase { + [FilterInvoke] + public static bool FilterInvoke( + [ServiceImport(Optional = true)] ManagedOnlyCommandFilter managedOnly, + [ServiceImport(Optional = true)] IRuntime runtime) => + SOSCommandBase.Filter(managedOnly, runtime); + } + + [Command(Name = "comstate", DefaultOptions = "COMState", Help = "Lists the COM apartment model for each thread.")] + [Command(Name = "dumprcw", DefaultOptions = "DumpRCW", Help = "Displays information about a Runtime Callable Wrapper.")] + [Command(Name = "dumpccw", DefaultOptions = "DumpCCW", Help = "Displays information about a COM Callable Wrapper.")] + [Command(Name = "dumppermissionset", DefaultOptions = "DumpPermissionSet", Help = "Displays a PermissionSet object (debug build only).")] + [Command(Name = "gchandleleaks", DefaultOptions = "GCHandleLeaks", Help = "Helps in tracking down GCHandle leaks.")] + [Command(Name = "watsonbuckets", DefaultOptions = "WatsonBuckets", Help = "Displays the Watson buckets.")] + public class WindowsSOSCommand : SOSCommandBase + { + /// + /// These commands are Windows only. + /// + [FilterInvoke] + public static bool FilterInvoke( + [ServiceImport(Optional = true)] ITarget target, + [ServiceImport(Optional = true)] ManagedOnlyCommandFilter managedOnly, + [ServiceImport(Optional = true)] IRuntime runtime) => + target != null && target.OperatingSystem == OSPlatform.Windows && SOSCommandBase.Filter(managedOnly, runtime); + } + + public class SOSCommandBase : CommandBase + { + /// + /// Empty service used to prevent native commands from being run + /// + public class ManagedOnlyCommandFilter + { + } + [Argument(Name = "arguments", Help = "Arguments to SOS command.")] public string[] Arguments { get; set; } @@ -62,22 +89,30 @@ public class SOSCommand : CommandBase public override void Invoke() { - try - { - Debug.Assert(Arguments != null && Arguments.Length > 0); - string arguments = string.Concat(Arguments.Skip(1).Select((arg) => arg + " ")).Trim(); - SOSHost.ExecuteCommand(Arguments[0], arguments); - } - catch (Exception ex) when (ex is FileNotFoundException or EntryPointNotFoundException or InvalidOperationException) - { - WriteLineError(ex.Message); - } + Debug.Assert(Arguments != null && Arguments.Length > 0); + string arguments = string.Concat(Arguments.Skip(1).Select((arg) => arg + " ")).Trim(); + SOSHost.ExecuteCommand(Arguments[0], arguments); } [HelpInvoke] - public void InvokeHelp() + public string GetDetailedHelp() { - SOSHost.ExecuteCommand("Help", Arguments[0]); + return SOSHost.GetHelpText(Arguments[0]); } + + /// + /// Common native SOS command filter function. + /// + /// not null means to filter out the native C++ SOS commands + /// runtime instance or null + /// + public static bool Filter(ManagedOnlyCommandFilter managedOnly, IRuntime runtime) => + // This filters out these native C++ commands if requested by host (in this case SOS.Extensions) to prevent recursion. + managedOnly == null && + // This commands require a .NET Core, Desktop Framework or .NET Core single file runtime (not a Native AOT runtime) + runtime != null && + (runtime.RuntimeType == RuntimeType.NetCore || + runtime.RuntimeType == RuntimeType.Desktop || + runtime.RuntimeType == RuntimeType.SingleFile); } } diff --git a/src/SOS/SOS.Hosting/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs index 03d011d0da..0fe569a0fa 100644 --- a/src/SOS/SOS.Hosting/SOSHost.cs +++ b/src/SOS/SOS.Hosting/SOSHost.cs @@ -21,6 +21,17 @@ namespace SOS.Hosting [ServiceExport(Scope = ServiceScope.Target)] public sealed class SOSHost : IDisposable { + /// + /// Provides the native debugger's debug client instance + /// + public interface INativeClient + { + /// + /// Native debugger client interface + /// + IntPtr Client { get; } + } + // This is what dbgeng/IDebuggerServices returns for non-PE modules that don't have a timestamp internal const uint InvalidTimeStamp = 0xFFFFFFFE; internal const uint InvalidChecksum = 0xFFFFFFFF; @@ -45,72 +56,62 @@ public sealed class SOSHost : IDisposable private readonly SOSLibrary _sosLibrary; #pragma warning restore - private readonly IntPtr _interface; + private readonly IntPtr _client; private readonly ulong _ignoreAddressBitsMask; - private bool _disposed; + private readonly bool _releaseClient; /// /// Create an instance of the hosting class. Has the lifetime of the target. /// - public SOSHost(ITarget target, IMemoryService memoryService) + public SOSHost(ITarget target, IMemoryService memoryService, [ServiceImport(Optional = true)] INativeClient client) { - Target = target ?? throw new DiagnosticsException("No target"); + Target = target; MemoryService = memoryService; _ignoreAddressBitsMask = memoryService.SignExtensionMask(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + // If running under a native debugger, use the client instance supplied by the debugger for commands + if (client != null) { - DebugClient debugClient = new(this); - _interface = debugClient.IDebugClient; + _client = client.Client; } else { - LLDBServices lldbServices = new(this); - _interface = lldbServices.ILLDBServices; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + DebugClient debugClient = new(this); + _client = debugClient.IDebugClient; + } + else + { + LLDBServices lldbServices = new(this); + _client = lldbServices.ILLDBServices; + } + _releaseClient = true; } } void IDisposable.Dispose() { - Trace.TraceInformation($"SOSHost.Dispose {_disposed}"); - if (!_disposed) + Trace.TraceInformation($"SOSHost.Dispose"); + if (_releaseClient) { - _disposed = true; - ComWrapper.ReleaseWithCheck(_interface); + ComWrapper.ReleaseWithCheck(_client); } } /// /// Execute a SOS command. /// - /// command name and arguments - public void ExecuteCommand(string commandLine) - { - string command = "Help"; - string arguments = null; - - if (commandLine != null) - { - int firstSpace = commandLine.IndexOf(' '); - command = firstSpace == -1 ? commandLine : commandLine.Substring(0, firstSpace); - arguments = firstSpace == -1 ? null : commandLine.Substring(firstSpace); - } - ExecuteCommand(command, arguments); - } + /// just the command name + /// the command arguments and options + public void ExecuteCommand(string command, string arguments) => _sosLibrary.ExecuteCommand(_client, command, arguments); /// - /// Execute a SOS command. + /// Get the detailed help text for a native SOS command. /// - /// just the command name - /// the command arguments and options - public void ExecuteCommand(string command, string arguments) - { - if (_disposed) - { - throw new ObjectDisposedException("SOSHost instance disposed"); - } - _sosLibrary.ExecuteCommand(_interface, command, arguments); - } + /// command name + /// help text or null if not found or error + public string GetHelpText(string command) => _sosLibrary.GetHelpText(command); #region Reverse PInvoke Implementations diff --git a/src/SOS/SOS.Hosting/SOSLibrary.cs b/src/SOS/SOS.Hosting/SOSLibrary.cs index d9e4f03da8..42e1f3e425 100644 --- a/src/SOS/SOS.Hosting/SOSLibrary.cs +++ b/src/SOS/SOS.Hosting/SOSLibrary.cs @@ -16,6 +16,22 @@ namespace SOS.Hosting /// public sealed class SOSLibrary : IDisposable { + /// + /// Provides the SOS module handle + /// + public interface ISOSModule + { + /// + /// The SOS module path + /// + string SOSPath { get; } + + /// + /// The SOS module handle + /// + IntPtr SOSHandle { get; } + } + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate int SOSCommandDelegate( IntPtr ILLDBServices, @@ -29,10 +45,20 @@ private delegate int SOSInitializeDelegate( [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void SOSUninitializeDelegate(); + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, EntryPoint = "FindResourceA")] + public static extern IntPtr FindResource(IntPtr hModule, string name, string type); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResource); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LockResource(IntPtr hResource); + private const string SOSInitialize = "SOSInitializeByHost"; private const string SOSUninitialize = "SOSUninitializeByHost"; private readonly HostWrapper _hostWrapper; + private readonly bool _uninitializeLibrary; private IntPtr _sosLibrary = IntPtr.Zero; /// @@ -41,12 +67,12 @@ private delegate int SOSInitializeDelegate( public string SOSPath { get; set; } [ServiceExport(Scope = ServiceScope.Global)] - public static SOSLibrary Create(IHost host) + public static SOSLibrary TryCreate(IHost host, [ServiceImport(Optional = true)] ISOSModule sosModule) { SOSLibrary sosLibrary = null; try { - sosLibrary = new SOSLibrary(host); + sosLibrary = new SOSLibrary(host, sosModule); sosLibrary.Initialize(); } catch @@ -61,10 +87,20 @@ public static SOSLibrary Create(IHost host) /// Create an instance of the hosting class /// /// target instance - private SOSLibrary(IHost host) + /// sos library info or null + private SOSLibrary(IHost host, ISOSModule sosModule) { - string rid = InstallHelper.GetRid(); - SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid); + if (sosModule is not null) + { + SOSPath = sosModule.SOSPath; + _sosLibrary = sosModule.SOSHandle; + } + else + { + string rid = InstallHelper.GetRid(); + SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid); + _uninitializeLibrary = true; + } _hostWrapper = new HostWrapper(host); } @@ -131,15 +167,15 @@ private void Initialize() /// private void Uninitialize() { - Trace.TraceInformation("SOSHost: Uninitialize"); - if (_sosLibrary != IntPtr.Zero) + Trace.TraceInformation("SOSLibrary: Uninitialize"); + if (_uninitializeLibrary && _sosLibrary != IntPtr.Zero) { SOSUninitializeDelegate uninitializeFunc = SOSHost.GetDelegateFunction(_sosLibrary, SOSUninitialize); uninitializeFunc?.Invoke(); Microsoft.Diagnostics.Runtime.DataTarget.PlatformFunctions.FreeLibrary(_sosLibrary); - _sosLibrary = IntPtr.Zero; } + _sosLibrary = IntPtr.Zero; _hostWrapper.ReleaseWithCheck(); } @@ -156,17 +192,85 @@ public void ExecuteCommand(IntPtr client, string command, string arguments) SOSCommandDelegate commandFunc = SOSHost.GetDelegateFunction(_sosLibrary, command); if (commandFunc == null) { - throw new DiagnosticsException($"SOS command not found: {command}"); + throw new CommandNotFoundException($"{CommandNotFoundException.NotFoundMessage} '{command}'"); } int result = commandFunc(client, arguments ?? ""); if (result == HResult.E_NOTIMPL) { - throw new CommandNotSupportedException($"SOS command not found: {command}"); + throw new CommandNotFoundException($"{CommandNotFoundException.NotFoundMessage} '{command}'"); } if (result != HResult.S_OK) { Trace.TraceError($"SOS command FAILED 0x{result:X8}"); + throw new DiagnosticsException(string.Empty); + } + } + + public string GetHelpText(string command) + { + string helpText; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + IntPtr hResInfo = FindResource(_sosLibrary, "DOCUMENTATION", "TEXT"); + if (hResInfo == IntPtr.Zero) + { + throw new DiagnosticsException("Can not SOS help text"); + } + IntPtr hResource = LoadResource(_sosLibrary, hResInfo); + if (hResource == IntPtr.Zero) + { + throw new DiagnosticsException("Can not SOS help text"); + } + IntPtr helpTextPtr = LockResource(hResource); + if (helpTextPtr == IntPtr.Zero) + { + throw new DiagnosticsException("Can not SOS help text"); + } + helpText = Marshal.PtrToStringAnsi(helpTextPtr); + } + else + { + string helpTextFile = Path.Combine(SOSPath, "sosdocsunix.txt"); + helpText = File.ReadAllText(helpTextFile); + } + command = command.ToLowerInvariant(); + string searchString = $"COMMAND: {command}."; + + // Search for command in help text file + int start = helpText.IndexOf(searchString); + if (start == -1) + { + throw new DiagnosticsException($"Documentation for {command} not found"); + } + + // Go to end of line + start = helpText.IndexOf('\n', start); + if (start == -1) + { + throw new DiagnosticsException($"No newline in documentation resource or file"); + } + + // Find the first occurrence of \\ followed by an \r or an \n on a line by itself. + int end = start++; + while (true) + { + end = helpText.IndexOf("\\\\", end + 1); + if (end == -1) + { + break; + } + char c = helpText[end - 1]; + if (c is '\r' or '\n') + { + break; + } + c = helpText[end + 3]; + if (c is '\r' or '\n') + { + break; + } } + return end == -1 ? helpText.Substring(start) : helpText.Substring(start, end - start); } } } diff --git a/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt b/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt index 5b869886b2..7a435c20b2 100644 --- a/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt +++ b/src/SOS/SOS.UnitTests/ConfigFiles/Unix/Debugger.Tests.Config.txt @@ -91,6 +91,16 @@ net6.0 $(RuntimeVersion60) + + diff --git a/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt b/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt index 3618f365e9..14898afadc 100644 --- a/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt +++ b/src/SOS/SOS.UnitTests/ConfigFiles/Windows/Debugger.Tests.Config.txt @@ -107,6 +107,23 @@ net6.0 $(RuntimeVersion60) + + diff --git a/src/SOS/SOS.UnitTests/SOS.UnitTests.csproj b/src/SOS/SOS.UnitTests/SOS.UnitTests.csproj index 3ce4a1bdb5..db08980770 100644 --- a/src/SOS/SOS.UnitTests/SOS.UnitTests.csproj +++ b/src/SOS/SOS.UnitTests/SOS.UnitTests.csproj @@ -31,6 +31,7 @@ + diff --git a/src/SOS/SOS.UnitTests/SOS.cs b/src/SOS/SOS.UnitTests/SOS.cs index 8849bd4a22..d7e8f6eb54 100644 --- a/src/SOS/SOS.UnitTests/SOS.cs +++ b/src/SOS/SOS.UnitTests/SOS.cs @@ -225,7 +225,6 @@ public async Task GCPOHTests(TestConfiguration config) { throw new SkipTestException("This test validates POH behavior, which was introduced in .net 5"); } - await SOSTestHelpers.RunTest( config, debuggeeName: "GCPOH", @@ -311,6 +310,17 @@ await SOSTestHelpers.RunTest( testName: "SOS.StackTests"); } + [SkippableTheory, MemberData(nameof(SOSTestHelpers.GetConfigurations), "TestName", "SOS.TestExtensions", MemberType = typeof(SOSTestHelpers))] + public async Task TestExtensions(TestConfiguration config) + { + await SOSTestHelpers.RunTest( + config, + debuggeeName: "LineNums", + scriptName: "TestExtensions.script", + Output, + testName: "SOS.TestExtensions"); + } + [SkippableTheory, MemberData(nameof(Configurations))] public async Task OtherCommands(TestConfiguration config) { diff --git a/src/SOS/SOS.UnitTests/SOSRunner.cs b/src/SOS/SOS.UnitTests/SOSRunner.cs index 3d345997a0..bee617ec9c 100644 --- a/src/SOS/SOS.UnitTests/SOSRunner.cs +++ b/src/SOS/SOS.UnitTests/SOSRunner.cs @@ -7,6 +7,7 @@ using System.IO; using System.IO.Pipes; using System.Linq; +using System.Reflection.Metadata.Ecma335; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -696,6 +697,13 @@ public static async Task StartDebugger(TestInformation information, D // Issue: https://github.com/dotnet/diagnostics/issues/3126 processRunner.WithRuntimeConfiguration("EnableWriteXorExecute", "0"); + // Setup the extension environment variable + string extensions = config.DotNetDiagnosticExtensions(); + if (!string.IsNullOrEmpty(extensions)) + { + processRunner.WithEnvironmentVariable("DOTNET_DIAGNOSTIC_EXTENSIONS", extensions); + } + DumpType? dumpType = null; if (action is DebuggerAction.LoadDump or DebuggerAction.LoadDumpWithDotNetDump) { @@ -793,6 +801,7 @@ public async Task RunScript(string scriptRelativePath) { await ContinueExecution(); } + // Adds the "!" prefix under dbgeng, nothing under lldb. Meant for SOS (native) commands. else if (line.StartsWith("SOSCOMMAND:")) { string input = line.Substring("SOSCOMMAND:".Length).TrimStart(); @@ -801,6 +810,19 @@ public async Task RunScript(string scriptRelativePath) throw new Exception($"SOS command FAILED: {input}"); } } + else if (line.StartsWith("SOSCOMMAND_FAIL:")) + { + string input = line.Substring("SOSCOMMAND_FAIL:".Length).TrimStart(); + if (await RunSosCommand(input)) + { + // The cdb runcommand extension doesn't get the execute command failures (limitation in dbgeng). + if (Debugger != NativeDebugger.Cdb) + { + throw new Exception($"SOS command did not fail: {input}"); + } + } + } + // Adds the "!sos" prefix under dbgeng, "sos " under lldb. Meant for extensions (managed) commands else if (line.StartsWith("EXTCOMMAND:")) { string input = line.Substring("EXTCOMMAND:".Length).TrimStart(); @@ -809,6 +831,19 @@ public async Task RunScript(string scriptRelativePath) throw new Exception($"Extension command FAILED: {input}"); } } + else if (line.StartsWith("EXTCOMMAND_FAIL:")) + { + string input = line.Substring("EXTCOMMAND_FAIL:".Length).TrimStart(); + if (await RunSosCommand(input, extensionCommand: true)) + { + // The cdb runcommand extension doesn't get the execute command failures (limitation in dbgeng). + if (Debugger != NativeDebugger.Cdb) + { + throw new Exception($"Extension command did not fail: {input}"); + } + } + } + // Never adds any prefix. Meant for native debugger commands. else if (line.StartsWith("COMMAND:")) { string input = line.Substring("COMMAND:".Length).TrimStart(); @@ -817,6 +852,18 @@ public async Task RunScript(string scriptRelativePath) throw new Exception($"Debugger command FAILED: {input}"); } } + else if (line.StartsWith("COMMAND_FAIL:")) + { + string input = line.Substring("COMMAND_FAIL:".Length).TrimStart(); + if (await RunCommand(input)) + { + // The cdb runcommand extension doesn't get the execute command failures (limitation in dbgeng). + if (Debugger != NativeDebugger.Cdb) + { + throw new Exception($"Debugger command did not fail: {input}"); + } + } + } else if (line.StartsWith("VERIFY:")) { string verifyLine = line.Substring("VERIFY:".Length); @@ -1060,8 +1107,6 @@ public async Task RunSosCommand(string command, bool extensionCommand = fa } break; case NativeDebugger.Lldb: - command = "sos " + command; - break; case NativeDebugger.DotNetDump: if (extensionCommand) { @@ -1681,6 +1726,11 @@ public static string DotNetDumpPath(this TestConfiguration config) return TestConfiguration.MakeCanonicalPath(dotnetDumpPath); } + public static string DotNetDiagnosticExtensions(this TestConfiguration config) + { + return TestConfiguration.MakeCanonicalPath(config.GetValue("DotNetDiagnosticExtensions")); + } + public static string SOSPath(this TestConfiguration config) { return TestConfiguration.MakeCanonicalPath(config.GetValue("SOSPath")); diff --git a/src/SOS/SOS.UnitTests/Scripts/ConcurrentDictionaries.script b/src/SOS/SOS.UnitTests/Scripts/ConcurrentDictionaries.script index 9d402ecbf3..3a156cb1e8 100644 --- a/src/SOS/SOS.UnitTests/Scripts/ConcurrentDictionaries.script +++ b/src/SOS/SOS.UnitTests/Scripts/ConcurrentDictionaries.script @@ -9,13 +9,13 @@ IFDEF:NETCORE_OR_DOTNETDUMP # Load SOS even though it doesn't actually load the sos module on dotnet-dump but it runs some initial settings/commands. LOADSOS -EXTCOMMAND: dcd +EXTCOMMAND_FAIL: dcd VERIFY: Missing ConcurrentDictionary address -EXTCOMMAND: dcd abcdefgh +EXTCOMMAND_FAIL: dcd abcdefgh VERIFY: Hexadecimal address expected -EXTCOMMAND: dcd 0000000000000001 +EXTCOMMAND_FAIL: dcd 0000000000000001 VERIFY: is not referencing an object # Checks on ConcurrentDictionary diff --git a/src/SOS/SOS.UnitTests/Scripts/DivZero.script b/src/SOS/SOS.UnitTests/Scripts/DivZero.script index 7d2095a5e1..456b10790d 100644 --- a/src/SOS/SOS.UnitTests/Scripts/DivZero.script +++ b/src/SOS/SOS.UnitTests/Scripts/DivZero.script @@ -40,12 +40,7 @@ VERIFY:\s+\s+\s+[Dd]iv[Zz]ero.*!C\.Main(\(.*\))?\+0x\s+ VERIFY:\[.*[\\|/]Debuggees[\\|/].*DivZero[\\|/]DivZero\.cs @ (57|56)\s*\]\s* # Verify that Threads (clrthreads) works -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -ENDIF:DOTNETDUMP VERIFY:\s*ThreadCount:\s+\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/DumpGen.script b/src/SOS/SOS.UnitTests/Scripts/DumpGen.script index 55bac91815..525bfb897d 100644 --- a/src/SOS/SOS.UnitTests/Scripts/DumpGen.script +++ b/src/SOS/SOS.UnitTests/Scripts/DumpGen.script @@ -9,17 +9,17 @@ IFDEF:NETCORE_OR_DOTNETDUMP # Load SOS even though it doesn't actually load the sos module on dotnet-dump but it runs some initial settings/commands. LOADSOS -EXTCOMMAND: dumpgen +EXTCOMMAND_FAIL: dumpgen VERIFY: Generation argument is missing -EXTCOMMAND: dumpgen invalid +EXTCOMMAND_FAIL: dumpgen invalid VERIFY: invalid is not a supported generation !IFDEF:LLDB -EXTCOMMAND: dumpgen gen0 -mt +EXTCOMMAND_FAIL: dumpgen gen0 -mt VERIFY: Required argument missing for option: -mt -EXTCOMMAND: dumpgen gen1 -mt zzzzz +EXTCOMMAND_FAIL: dumpgen gen1 -mt zzzzz VERIFY: Hexadecimal address expected for -mt option ENDIF:LLDB diff --git a/src/SOS/SOS.UnitTests/Scripts/GCTests.script b/src/SOS/SOS.UnitTests/Scripts/GCTests.script index 671e4e2e15..a1b172dffb 100644 --- a/src/SOS/SOS.UnitTests/Scripts/GCTests.script +++ b/src/SOS/SOS.UnitTests/Scripts/GCTests.script @@ -89,7 +89,7 @@ VERIFY:\s+\s+\s+\s+GCWhere\s+ IFDEF:WINDOWS SOSCOMMAND:DumpHeap -stat -gen xxx -VERIFY:\s*System\.ArgumentException: Unknown generation: xxx\. Only gen0, gen1, gen2, loh \(large\), poh \(pinned\) and foh \(frozen\) are supported\s+ +VERIFY:\s*Unknown generation: xxx\. Only gen0, gen1, gen2, loh \(large\), poh \(pinned\) and foh \(frozen\) are supported\s+ ENDIF:WINDOWS IFDEF:WINDOWS diff --git a/src/SOS/SOS.UnitTests/Scripts/LineNums.script b/src/SOS/SOS.UnitTests/Scripts/LineNums.script index 6f6c11a30e..39ec83258b 100644 --- a/src/SOS/SOS.UnitTests/Scripts/LineNums.script +++ b/src/SOS/SOS.UnitTests/Scripts/LineNums.script @@ -31,12 +31,7 @@ VERIFY:\s+\s+\s+LineNums.*!LineNums\.Program\.Main.*\+0x VERIFY:\[.*[\\|/]Debuggees[\\|/].*LineNums[\\|/]Program\.cs @ 13\s*\]\s* # Verify that Threads (clrthreads) works -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -ENDIF:DOTNETDUMP VERIFY:\s*ThreadCount:\s+\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/NestedExceptionTest.script b/src/SOS/SOS.UnitTests/Scripts/NestedExceptionTest.script index 8c67f036b5..d44f0d7ca9 100644 --- a/src/SOS/SOS.UnitTests/Scripts/NestedExceptionTest.script +++ b/src/SOS/SOS.UnitTests/Scripts/NestedExceptionTest.script @@ -99,12 +99,7 @@ VERIFY:HResult:\s+80131509\s+ VERIFY:There are nested exceptions on this thread. Run with -nested for details # Verify that Threads (clrthreads) works -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -ENDIF:DOTNETDUMP VERIFY:\s*ThreadCount:\s+\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/Overflow.script b/src/SOS/SOS.UnitTests/Scripts/Overflow.script index 3deaf16e46..ccc8c8d232 100644 --- a/src/SOS/SOS.UnitTests/Scripts/Overflow.script +++ b/src/SOS/SOS.UnitTests/Scripts/Overflow.script @@ -9,12 +9,7 @@ CONTINUE LOADSOS -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -managedexception -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -managedexception -ENDIF:DOTNETDUMP # 5) Verifying that PrintException gives us the right exception in the format above. SOSCOMMAND:PrintException diff --git a/src/SOS/SOS.UnitTests/Scripts/Reflection.script b/src/SOS/SOS.UnitTests/Scripts/Reflection.script index fe8db12b95..b3f34187c0 100644 --- a/src/SOS/SOS.UnitTests/Scripts/Reflection.script +++ b/src/SOS/SOS.UnitTests/Scripts/Reflection.script @@ -43,12 +43,7 @@ VERIFY:(StackTraceString: \s+)? VERIFY:HResult:\s+80131604 # Verify that Threads (clrthreads) works -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -ENDIF:DOTNETDUMP VERIFY:\s*ThreadCount:\s+\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/SimpleThrow.script b/src/SOS/SOS.UnitTests/Scripts/SimpleThrow.script index 7740c58804..8ae602b04b 100644 --- a/src/SOS/SOS.UnitTests/Scripts/SimpleThrow.script +++ b/src/SOS/SOS.UnitTests/Scripts/SimpleThrow.script @@ -46,12 +46,7 @@ VERIFY:\s+\s+\s+[Ss]imple[Tt]hrow.*!(\$0_)?Simple\.Main.*\+0x\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script b/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script index 5c31e0ef1d..ec9fed65b8 100644 --- a/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script +++ b/src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script @@ -250,20 +250,20 @@ ENDIF:DESKTOP # Verify that "u" works (depends on the IP2MD here) SOSCOMMAND:ClrStack SOSCOMMAND:IP2MD .*\s+()\s+SymbolTestApp\.Program\.Foo4.*\s+ -SOSCOMMAND:u \s*MethodDesc:\s+()\s* +SOSCOMMAND:clru \s*MethodDesc:\s+()\s* VERIFY:\s*Normal JIT generated code\s+ VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+ VERIFY:\s+Begin\s+,\s+size\s+\s+ VERIFY:\s+(?i:.*[\\|/]SymbolTestApp\.cs) @ (53|57):\s+ # Verify that "u" with no line info works -SOSCOMMAND:u -n +SOSCOMMAND:clru -n VERIFY:\s*Normal JIT generated code\s+ VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+ VERIFY:\s+Begin\s+,\s+size\s+\s+ # Verify that "u" with offsets info works -SOSCOMMAND:u -o +SOSCOMMAND:clru -o VERIFY:\s*Normal JIT generated code\s+ VERIFY:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+ VERIFY:\s+Begin\s+,\s+size\s+\s+ @@ -279,12 +279,7 @@ VERIFY:\s+Name:\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+ VERIFY:\s+JITTED Code Address:\s+\s+ # Verify that Threads (clrthreads) works -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -ENDIF:DOTNETDUMP VERIFY:\s*ThreadCount:\s+\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/TestExtensions.script b/src/SOS/SOS.UnitTests/Scripts/TestExtensions.script new file mode 100644 index 0000000000..d18a63acdd --- /dev/null +++ b/src/SOS/SOS.UnitTests/Scripts/TestExtensions.script @@ -0,0 +1,15 @@ +CONTINUE + +LOADSOS + +SOSCOMMAND:clrstack +VERIFY:Test command #1 invoked\s+ + +SOSCOMMAND:dumpheap +VERIFY:Test command #2 invoked\s+ + +SOSCOMMAND:assemblies +VERIFY:Test command #4 invoked\s+ + +SOSCOMMAND_FAIL:ip2md 0 +!VERIFY:Test command #5 invoked\s+ diff --git a/src/SOS/SOS.UnitTests/Scripts/WebApp.script b/src/SOS/SOS.UnitTests/Scripts/WebApp.script index b9a0509e0f..a304a2f001 100644 --- a/src/SOS/SOS.UnitTests/Scripts/WebApp.script +++ b/src/SOS/SOS.UnitTests/Scripts/WebApp.script @@ -101,12 +101,7 @@ VERIFY:.*\s+Child\s+SP\s+IP\s+Call Site\s+ VERIFY:.*\s+Stack walk complete.\s+ # Verify that Threads (clrthreads) works -IFDEF:DOTNETDUMP SOSCOMMAND:clrthreads -ENDIF:DOTNETDUMP -!IFDEF:DOTNETDUMP -SOSCOMMAND:Threads -ENDIF:DOTNETDUMP VERIFY:\s*ThreadCount:\s+\s+ VERIFY:\s+UnstartedThread:\s+\s+ VERIFY:\s+BackgroundThread:\s+\s+ @@ -138,7 +133,7 @@ VERIFY:\s*STACK \s* VERIFY?:\s*<< Awaiting: \s+\s+.* >>\s+ VERIFY:\s*\s+\s+\(()?\)\s+.* -SOSCOMMAND:DumpMT --stats +SOSCOMMAND:DumpMT !VERIFY:\s* is not a MethodTable SOSCOMMAND:DumpAsync --coalesce diff --git a/src/SOS/Strike/CMakeLists.txt b/src/SOS/Strike/CMakeLists.txt index 6299c27391..dcd7994672 100644 --- a/src/SOS/Strike/CMakeLists.txt +++ b/src/SOS/Strike/CMakeLists.txt @@ -89,6 +89,7 @@ if(WIN32) gchist.cpp gcroot.cpp symbols.cpp + managedcommands.cpp metadata.cpp sigparser.cpp sildasm.cpp diff --git a/src/SOS/Strike/Strike.vcxproj b/src/SOS/Strike/Strike.vcxproj index be63febcfb..29dde66bfa 100644 --- a/src/SOS/Strike/Strike.vcxproj +++ b/src/SOS/Strike/Strike.vcxproj @@ -393,6 +393,7 @@ + diff --git a/src/SOS/Strike/Strike.vcxproj.filters b/src/SOS/Strike/Strike.vcxproj.filters index ec1f21b1e9..e7b14e9eaf 100644 --- a/src/SOS/Strike/Strike.vcxproj.filters +++ b/src/SOS/Strike/Strike.vcxproj.filters @@ -32,6 +32,7 @@ platform + @@ -93,4 +94,4 @@ - + \ No newline at end of file diff --git a/src/SOS/Strike/exts.cpp b/src/SOS/Strike/exts.cpp index 3ebe83fde0..836f38b0d9 100644 --- a/src/SOS/Strike/exts.cpp +++ b/src/SOS/Strike/exts.cpp @@ -65,14 +65,9 @@ ExtQuery(PDEBUG_CLIENT client) HRESULT ExtQuery(ILLDBServices* services) { - // Initialize the PAL and extension suppport in one place and only once. - if (!g_palInitialized) + if (!InitializePAL()) { - if (PAL_InitializeDLL() != 0) - { - return E_FAIL; - } - g_palInitialized = true; + return E_FAIL; } g_ExtServices = services; @@ -196,6 +191,74 @@ ExtRelease(void) ReleaseTarget(); } +// Executes managed extension commands. Returns E_NOTIMPL if the command doesn't exists. +HRESULT +ExecuteCommand(PCSTR commandName, PCSTR args) +{ + if (commandName != nullptr && strlen(commandName) > 0) + { + IHostServices* hostServices = GetHostServices(); + if (hostServices != nullptr) + { + return hostServices->DispatchCommand(commandName, args); + } + } + return E_NOTIMPL; +} + +void +EENotLoadedMessage(HRESULT Status) +{ +#ifdef FEATURE_PAL + ExtOut("Failed to find runtime module (%s), 0x%08x\n", GetRuntimeDllName(IRuntime::Core), Status); +#else + ExtOut("Failed to find runtime module (%s or %s or %s), 0x%08x\n", GetRuntimeDllName(IRuntime::Core), GetRuntimeDllName(IRuntime::WindowsDesktop), GetRuntimeDllName(IRuntime::UnixCore), Status); +#endif + ExtOut("Extension commands need it in order to have something to do.\n"); + ExtOut("For more information see https://go.microsoft.com/fwlink/?linkid=2135652\n"); +} + +void +DACMessage(HRESULT Status) +{ + ExtOut("Failed to load data access module, 0x%08x\n", Status); + if (GetHost()->GetHostType() == IHost::HostType::DbgEng) + { + ExtOut("Verify that 1) you have a recent build of the debugger (10.0.18317.1001 or newer)\n"); + ExtOut(" 2) the file %s that matches your version of %s is\n", GetDacDllName(), GetRuntimeDllName()); + ExtOut(" in the version directory or on the symbol path\n"); + ExtOut(" 3) or, if you are debugging a dump file, verify that the file\n"); + ExtOut(" %s___.dll is on your symbol path.\n", GetDacModuleName()); + ExtOut(" 4) you are debugging on a platform and architecture that supports this\n"); + ExtOut(" the dump file. For example, an ARM dump file must be debugged\n"); + ExtOut(" on an X86 or an ARM machine; an AMD64 dump file must be\n"); + ExtOut(" debugged on an AMD64 machine.\n"); + ExtOut("\n"); + ExtOut("You can run the command '!setclrpath ' to control the load path of %s.\n", GetDacDllName()); + ExtOut("\n"); + ExtOut("Or you can also run the debugger command .cordll to control the debugger's\n"); + ExtOut("load of %s. .cordll -ve -u -l will do a verbose reload.\n", GetDacDllName()); + ExtOut("If that succeeds, the SOS command should work on retry.\n"); + ExtOut("\n"); + ExtOut("If you are debugging a minidump, you need to make sure that your executable\n"); + ExtOut("path is pointing to %s as well.\n", GetRuntimeDllName()); + } + else + { + if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS) + { + ExtOut("You can run the debugger command 'setclrpath ' to control the load of %s.\n", GetDacDllName()); + ExtOut("If that succeeds, the SOS command should work on retry.\n"); + } + else + { + ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", GetDacDllName()); + } + } + ExtOut("\n"); + ExtOut("For more information see https://go.microsoft.com/fwlink/?linkid=2135652\n"); +} + #ifndef FEATURE_PAL BOOL IsMiniDumpFileNODAC(); @@ -321,6 +384,21 @@ DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved) #else // FEATURE_PAL +BOOL +InitializePAL() +{ + // Initialize the PAL only once + if (!g_palInitialized) + { + if (PAL_InitializeDLL() != 0) + { + return false; + } + g_palInitialized = true; + } + return true; +} + HRESULT DebugClient::QueryInterface( REFIID InterfaceId, diff --git a/src/SOS/Strike/exts.h b/src/SOS/Strike/exts.h index cf29ce595b..96876879a7 100644 --- a/src/SOS/Strike/exts.h +++ b/src/SOS/Strike/exts.h @@ -223,6 +223,7 @@ IsInitializedByDbgEng(); extern ILLDBServices* g_ExtServices; extern ILLDBServices2* g_ExtServices2; +extern BOOL InitializePAL(); #define IsInitializedByDbgEng() false @@ -237,6 +238,15 @@ ArchQuery(void); void ExtRelease(void); +HRESULT +ExecuteCommand(PCSTR commandName, PCSTR args); + +void +EENotLoadedMessage(HRESULT Status); + +void +DACMessage(HRESULT Status); + extern BOOL ControlC; inline BOOL IsInterrupt() @@ -264,57 +274,6 @@ class __ExtensionCleanUp ~__ExtensionCleanUp(){ExtRelease();} }; -inline void EENotLoadedMessage(HRESULT Status) -{ -#ifdef FEATURE_PAL - ExtOut("Failed to find runtime module (%s), 0x%08x\n", GetRuntimeDllName(IRuntime::Core), Status); -#else - ExtOut("Failed to find runtime module (%s or %s or %s), 0x%08x\n", GetRuntimeDllName(IRuntime::Core), GetRuntimeDllName(IRuntime::WindowsDesktop), GetRuntimeDllName(IRuntime::UnixCore), Status); -#endif - ExtOut("Extension commands need it in order to have something to do.\n"); - ExtOut("For more information see https://go.microsoft.com/fwlink/?linkid=2135652\n"); -} - -inline void DACMessage(HRESULT Status) -{ - ExtOut("Failed to load data access module, 0x%08x\n", Status); - if (GetHost()->GetHostType() == IHost::HostType::DbgEng) - { - ExtOut("Verify that 1) you have a recent build of the debugger (10.0.18317.1001 or newer)\n"); - ExtOut(" 2) the file %s that matches your version of %s is\n", GetDacDllName(), GetRuntimeDllName()); - ExtOut(" in the version directory or on the symbol path\n"); - ExtOut(" 3) or, if you are debugging a dump file, verify that the file\n"); - ExtOut(" %s___.dll is on your symbol path.\n", GetDacModuleName()); - ExtOut(" 4) you are debugging on a platform and architecture that supports this\n"); - ExtOut(" the dump file. For example, an ARM dump file must be debugged\n"); - ExtOut(" on an X86 or an ARM machine; an AMD64 dump file must be\n"); - ExtOut(" debugged on an AMD64 machine.\n"); - ExtOut("\n"); - ExtOut("You can run the command '!setclrpath ' to control the load path of %s.\n", GetDacDllName()); - ExtOut("\n"); - ExtOut("Or you can also run the debugger command .cordll to control the debugger's\n"); - ExtOut("load of %s. .cordll -ve -u -l will do a verbose reload.\n", GetDacDllName()); - ExtOut("If that succeeds, the SOS command should work on retry.\n"); - ExtOut("\n"); - ExtOut("If you are debugging a minidump, you need to make sure that your executable\n"); - ExtOut("path is pointing to %s as well.\n", GetRuntimeDllName()); - } - else - { - if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS) - { - ExtOut("You can run the debugger command 'setclrpath ' to control the load of %s.\n", GetDacDllName()); - ExtOut("If that succeeds, the SOS command should work on retry.\n"); - } - else - { - ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", GetDacDllName()); - } - } - ExtOut("\n"); - ExtOut("For more information see https://go.microsoft.com/fwlink/?linkid=2135652\n"); -} - // The minimum initialization for a command #define INIT_API_EXT() \ HRESULT Status; \ @@ -331,6 +290,11 @@ inline void DACMessage(HRESULT Status) INIT_API_EXT() \ if ((Status = ArchQuery()) != S_OK) return Status; +#define INIT_API_NOEE_PROBE_MANAGED(name) \ + INIT_API_EXT() \ + if ((Status = ExecuteCommand(name, args)) != E_NOTIMPL) return Status; \ + if ((Status = ArchQuery()) != S_OK) return Status; + #define INIT_API_EE() \ if ((Status = GetRuntime(&g_pRuntime)) != S_OK) \ { \ @@ -342,6 +306,10 @@ inline void DACMessage(HRESULT Status) INIT_API_NOEE() \ INIT_API_EE() +#define INIT_API_NODAC_PROBE_MANAGED(name) \ + INIT_API_NOEE_PROBE_MANAGED(name) \ + INIT_API_EE() + #define INIT_API_DAC() \ if ((Status = LoadClrDebugDll()) != S_OK) \ { \ @@ -355,19 +323,27 @@ inline void DACMessage(HRESULT Status) ToRelease spISD(g_sos); \ ResetGlobals(); +#define INIT_API_PROBE_MANAGED(name) \ + INIT_API_NODAC_PROBE_MANAGED(name) \ + INIT_API_DAC() + #define INIT_API() \ INIT_API_NODAC() \ INIT_API_DAC() +#define INIT_API_EFN() \ + INIT_API_NODAC() \ + INIT_API_DAC() + // Attempt to initialize DAC and SOS globals, but do not "return" on failure. // Instead, mark the failure to initialize the DAC by setting g_bDacBroken to TRUE. // This should be used from extension commands that should work OK even when no // runtime is loaded in the debuggee, e.g. DumpLog, DumpStack. These extensions // and functions they call should test g_bDacBroken before calling any DAC enabled // feature. -#define INIT_API_NO_RET_ON_FAILURE() \ - INIT_API_NODAC() \ - if ((Status = LoadClrDebugDll()) != S_OK) \ +#define INIT_API_NO_RET_ON_FAILURE(name) \ + INIT_API_NODAC_PROBE_MANAGED(name) \ + if ((Status = LoadClrDebugDll()) != S_OK) \ { \ ExtOut("Failed to load data access module (%s), 0x%08x\n", GetDacDllName(), Status); \ ExtOut("Some functionality may be impaired\n"); \ @@ -382,6 +358,30 @@ inline void DACMessage(HRESULT Status) ToRelease spISD(g_sos); \ ToRelease spIDP(g_clrData); +#ifdef FEATURE_PAL + +#define MINIDUMP_NOT_SUPPORTED() +#define ONLY_SUPPORTED_ON_WINDOWS_TARGET() + +#else // !FEATURE_PAL + +#define MINIDUMP_NOT_SUPPORTED() \ + if (IsMiniDumpFile()) \ + { \ + ExtOut("This command is not supported in a minidump without full memory\n"); \ + ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \ + return Status; \ + } + +#define ONLY_SUPPORTED_ON_WINDOWS_TARGET() \ + if (!IsWindowsTarget()) \ + { \ + ExtOut("This command is only supported for Windows targets\n"); \ + return Status; \ + } + +#endif // FEATURE_PAL + extern BOOL g_bDacBroken; //----------------------------------------------------------------------------------------- diff --git a/src/SOS/Strike/gchist.cpp b/src/SOS/Strike/gchist.cpp index f656cb1183..82a43a9531 100644 --- a/src/SOS/Strike/gchist.cpp +++ b/src/SOS/Strike/gchist.cpp @@ -31,8 +31,8 @@ #include #include #include - #include "strike.h" + // We need to define the target address type. This will be used in the // functions that read directly from the debuggee address space, vs. using // the DAC tgo read the DAC-ized data structures. @@ -258,7 +258,7 @@ void GcHistAddLog(LPCSTR msg, StressMsg* stressMsg) DECLARE_API(HistStats) { INIT_API(); - + ExtOut ("%8s %8s %8s\n", "GCCount", "Promotes", "Relocs"); ExtOut ("-----------------------------------\n"); @@ -348,12 +348,13 @@ DECLARE_API(HistRoot) }; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) - return Status; - + { + return E_INVALIDARG; + } if (nArg != 1) { ExtOut ("!Root \n"); - return Status; + return E_INVALIDARG; } size_t Root = (size_t) GetExpression(rootstr.data); @@ -463,12 +464,13 @@ DECLARE_API(HistObjFind) }; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) - return Status; - + { + return E_INVALIDARG; + } if (nArg != 1) { ExtOut ("!ObjSearch \n"); - return Status; + return E_INVALIDARG; } size_t object = (size_t) GetExpression(objstr.data); @@ -542,12 +544,13 @@ DECLARE_API(HistObj) }; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) - return Status; - + { + return E_INVALIDARG; + } if (nArg != 1) { ExtOut ("!object \n"); - return Status; + return E_INVALIDARG; } size_t object = (size_t) GetExpression(objstr.data); diff --git a/src/SOS/Strike/managedcommands.cpp b/src/SOS/Strike/managedcommands.cpp new file mode 100644 index 0000000000..2a7b33615b --- /dev/null +++ b/src/SOS/Strike/managedcommands.cpp @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "exts.h" + +// Windows host only managed command stubs + +HRESULT ExecuteManagedOnlyCommand(PCSTR commandName, PCSTR args) +{ + HRESULT hr = ExecuteCommand(commandName, args); + if (hr == E_NOTIMPL) + { + ExtErr("Unrecognized command '%s'\n", commandName); + } + return hr; +} + +DECLARE_API(DumpStackObjects) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("dumpstackobjects", args); +} + +DECLARE_API(EEHeap) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("eeheap", args); +} + +DECLARE_API(TraverseHeap) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("traverseheap", args); +} + +DECLARE_API(DumpRuntimeTypes) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("dumpruntimetypes", args); +} + +DECLARE_API(DumpHeap) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("dumpheap", args); +} + +DECLARE_API(VerifyHeap) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("verifyheap", args); +} + +DECLARE_API(AnalyzeOOM) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("analyzeoom", args); +} + +DECLARE_API(VerifyObj) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("verifyobj", args); +} + +DECLARE_API(ListNearObj) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("listnearobj", args); +} + +DECLARE_API(GCHeapStat) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("gcheapstat", args); +} + +DECLARE_API(FinalizeQueue) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("finalizequeue", args); +} + +DECLARE_API(ThreadPool) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("threadpool", args); +} + +DECLARE_API(PathTo) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("pathto", args); +} + +DECLARE_API(GCRoot) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("gcroot", args); +} + +DECLARE_API(GCWhere) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("gcwhere", args); +} + +DECLARE_API(ObjSize) +{ + INIT_API_EXT(); + MINIDUMP_NOT_SUPPORTED(); + return ExecuteManagedOnlyCommand("objsize", args); +} + +DECLARE_API(SetSymbolServer) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("setsymbolserver", args); +} + +DECLARE_API(assemblies) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("assemblies", args); +} + +DECLARE_API(crashinfo) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("crashinfo", args); +} + +DECLARE_API(DumpAsync) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("dumpasync", args); +} + +DECLARE_API(logging) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("logging", args); +} + +DECLARE_API(maddress) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("maddress", args); +} + +DECLARE_API(dumpexceptions) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("dumpexceptions", args); +} + +DECLARE_API(dumpgen) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("dumpgen", args); +} + +DECLARE_API(sizestats) +{ + INIT_API_EXT(); + return ExecuteManagedOnlyCommand("sizestats", args); +} + +typedef HRESULT (*PFN_COMMAND)(PDEBUG_CLIENT client, PCSTR args); + +// +// Executes managed extension commands (i.e. !sos) +// +DECLARE_API(ext) +{ + INIT_API_EXT(); + + if (args == nullptr || strlen(args) <= 0) + { + args = "Help"; + } + std::string arguments(args); + size_t pos = arguments.find(' '); + std::string commandName = arguments.substr(0, pos); + if (pos != std::string::npos) + { + arguments = arguments.substr(pos + 1); + } + else + { + arguments.clear(); + } + Status = ExecuteCommand(commandName.c_str(), arguments.c_str()); + if (Status == E_NOTIMPL) + { + PFN_COMMAND commandFunc = (PFN_COMMAND)GetProcAddress(g_hInstance, commandName.c_str()); + if (commandFunc != nullptr) + { + Status = (*commandFunc)(client, arguments.c_str()); + } + else + { + ExtErr("Unrecognized command '%s'\n", commandName.c_str()); + } + } + return Status; +} + diff --git a/src/SOS/Strike/sos.def b/src/SOS/Strike/sos.def index 4b9b8fcd7d..f298a3f9fd 100644 --- a/src/SOS/Strike/sos.def +++ b/src/SOS/Strike/sos.def @@ -7,7 +7,8 @@ EXPORTS AnalyzeOOM analyzeoom=AnalyzeOOM ao=AnalyzeOOM - clrmodules + assemblies + clrmodules=assemblies ClrStack clrstack=ClrStack CLRStack=ClrStack @@ -27,6 +28,7 @@ EXPORTS dumpdelegate=DumpDelegate DumpDomain dumpdomain=DumpDomain + dumpexceptions #ifdef TRACE_GC DumpGCLog dumpgclog=DumpGCLog @@ -38,6 +40,8 @@ EXPORTS DumpGCConfigLog dumpgcconfiglog=DumpGCConfigLog dclog=DumpGCConfigLog + dumpgen + dg=dumpgen DumpHeap dumpheap=DumpHeap DumpIL @@ -121,6 +125,7 @@ EXPORTS ListNearObj listnearobj=ListNearObj lno=ListNearObj + maddress Name2EE name2ee=Name2EE ObjSize @@ -137,6 +142,7 @@ EXPORTS setsymbolserver=SetSymbolServer SetClrPath setclrpath=SetClrPath + sizestats SOSFlush sosflush=SOSFlush StopOnException @@ -161,6 +167,7 @@ EXPORTS Traverseheap=TraverseHeap u U=u + clru=u VerifyHeap verifyheap=VerifyHeap Verifyheap=VerifyHeap diff --git a/src/SOS/Strike/sos_unixexports.src b/src/SOS/Strike/sos_unixexports.src index aa8eaf23d3..cf08981f5d 100644 --- a/src/SOS/Strike/sos_unixexports.src +++ b/src/SOS/Strike/sos_unixexports.src @@ -2,7 +2,6 @@ ; The .NET Foundation licenses this file to you under the MIT license. ; See the LICENSE file in the project root for more information. -AnalyzeOOM bpmd ClrStack dbgout @@ -13,32 +12,24 @@ DumpClass DumpDelegate DumpDomain DumpGCData -DumpHeap DumpIL DumpLog DumpMD DumpModule DumpMT DumpObj -DumpRuntimeTypes DumpSig DumpSigElem DumpStack -DumpStackObjects DumpVC -EEHeap EEVersion EEStack EHInfo enummem -FinalizeQueue FindAppDomain FindRoots GCHandles -GCHeapStat GCInfo -GCRoot -GCWhere Help HistClear HistInit @@ -47,11 +38,8 @@ HistObjFind HistRoot HistStats IP2MD -ListNearObj Name2EE -ObjSize PrintException -PathTo runtimes StopOnCatch SetClrPath @@ -61,13 +49,9 @@ runtimes SuppressJitOptimization SyncBlk Threads -ThreadPool ThreadState Token2EE -TraverseHeap u -VerifyHeap -VerifyObj SOSInitializeByHost SOSUninitializeByHost diff --git a/src/SOS/Strike/sosdocs.txt b/src/SOS/Strike/sosdocs.txt index fa470d2eab..80ec24642f 100644 --- a/src/SOS/Strike/sosdocs.txt +++ b/src/SOS/Strike/sosdocs.txt @@ -19,7 +19,7 @@ COMMAND: contents. SOS is a debugger extension DLL designed to aid in the debugging of managed programs. Functions are listed by category, then roughly in order of importance. Shortcut names for popular functions are listed in parenthesis. -Type "!help " for detailed info on that function. +Type "!soshelp " for detailed info on that function. Object Inspection Examining code and stacks ----------------------------- ----------------------------- @@ -2638,26 +2638,6 @@ You can use the "dotnet --info" in a command shell to find the path of an instal .NET Core runtime. \\ -COMMAND: setsymbolserver. -!SetSymbolServer [-ms] [-mi] [-disable] [-log] [-cache ] [-directory ] [-timeout ] [-pat ] [] - --ms - Use the public Microsoft symbol server. --mi - Use the internal symweb symbol server. --disable - Disable symbol download support. --directory - Directory to search for symbols. Can be more than one. --timeout - Specify the symbol server timeout in minutes --pat - Access token to the authenticated server. --cache - Specific a symbol cache directory. The default is %%TEMP%%\SymbolCache if not specified. - - Symbol server URL. - -This commands enables symbol server support for portable PDBs in SOS. If the .sympath is set, this -symbol server support is automatically enabled. - -To disable downloading or clear the current SOS symbol settings allowing new symbol paths to be set: - - 0:000> !setsymbolserver -disable -\\ - COMMAND: sosstatus. !SOSStatus [-reset] @@ -2699,8 +2679,3 @@ runtimes [-netfx] [-netcore] List and select the .NET runtimes in the target process. \\ -COMMAND: logging. -logging [enable] [disable] - -Enables or disables the internal trace logging. -\\ diff --git a/src/SOS/Strike/sosdocsunix.txt b/src/SOS/Strike/sosdocsunix.txt index 3310726ca9..b554a9479a 100644 --- a/src/SOS/Strike/sosdocsunix.txt +++ b/src/SOS/Strike/sosdocsunix.txt @@ -2268,57 +2268,6 @@ You can use the "dotnet --info" in a command shell to find the path of an instal .NET Core runtime. \\ -COMMAND: setsymbolserver. -COMMAND: loadsymbols. -SetSymbolServer [-ms] [-disable] [-log] [-loadsymbols] [-cache ] [-directory ] [-timeout ] [-pat ] [] - --ms - Use the public Microsoft symbol server. --disable - Disable symbol download support. --directory - Directory to search for symbols. Can be more than one. --timeout - Specify the symbol server timeout in minutes --pat - Access token to the authenticated server. --cache - Specific a symbol cache directory. The default is $HOME/.dotnet/symbolcache if not specified. --loadsymbols - Attempts to download the native .NET Core symbols for the runtime - - Symbol server URL. - -This commands enables symbol server support in SOS. The portable PDBs for managed assemblies -and .NET Core native symbol files are downloaded. - -To enable downloading symbols from the Microsoft symbol server: - - (lldb) setsymbolserver -ms - -This command may take some time without any output while it attempts to download the symbol files. - -To disable downloading or clear the current SOS symbol settings allowing new symbol paths to be set: - - (lldb) setsymbolserver -disable - -To add a directory to search for symbols: - - (lldb) setsymbolserver -directory /home/mikem/symbols - -This command can be used so the module/symbol file structure does not have to match the machine -file structure that the core dump was generated. - -To clear the default cache run "rm -r $HOME/.dotnet/symbolcache" in a command shell. - -If you receive an error like the one below on a core dump, you need to set the .NET Core -runtime with the "sethostruntime" command. Type "soshelp sethostruntime" for more details. - - (lldb) setsymbolserver -ms - Error: Fail to initialize CoreCLR 80004005 - SetSymbolServer -ms failed - -The "-loadsymbols" option and the "loadsymbol" command alias attempts to download the native .NET -Core symbol files. It is only useful for live sessions and not core dumps. This command needs to -be run before the lldb "bt" (stack trace) or the "clrstack -f" (dumps both managed and native -stack frames). - - (lldb) loadsymbols - (lldb) bt -\\ - COMMAND: sosstatus. SOSStatus [-reset] @@ -2348,8 +2297,3 @@ runtimes List the .NET runtimes in the target process. \\ -COMMAND: logging. -logging [enable] [disable] - -Enables or disables the internal trace logging. -\\ diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index b6f36b5db4..0d7aad0d49 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -70,7 +70,6 @@ #include #endif // !FEATURE_PAL #include - #include "platformspecific.h" #define NOEXTAPI @@ -80,7 +79,6 @@ #undef StackTrace #include - #include #include #include @@ -222,7 +220,7 @@ extern const char* g_sosPrefix; DECLARE_API (MinidumpMode) { - INIT_API (); + INIT_API(); ONLY_SUPPORTED_ON_WINDOWS_TARGET(); DWORD_PTR Value=0; @@ -234,7 +232,7 @@ DECLARE_API (MinidumpMode) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg == 0) { @@ -269,7 +267,7 @@ DECLARE_API (MinidumpMode) \**********************************************************************/ DECLARE_API(IP2MD) { - INIT_API(); + INIT_API_PROBE_MANAGED("ip2md"); MINIDUMP_NOT_SUPPORTED(); BOOL dml = FALSE; @@ -286,14 +284,14 @@ DECLARE_API(IP2MD) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); if (IP == 0) { ExtOut("%s is not IP\n", args); - return Status; + return E_INVALIDARG; } CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP); @@ -376,25 +374,6 @@ GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames) return hr; } - -// -// Executes managed extension commands -// -HRESULT ExecuteCommand(PCSTR commandName, PCSTR args) -{ - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) - { - if (commandName != nullptr && strlen(commandName) > 0) - { - return hostServices->DispatchCommand(commandName, args); - } - } - ExtErr("Unrecognized command %s\n", commandName); - return E_NOTIMPL; -} - - /**********************************************************************\ * Routine Description: * * * @@ -457,7 +436,7 @@ void DumpStackInternal(DumpStackFlag *pDSFlag) DECLARE_API(DumpStack) { - INIT_API_NO_RET_ON_FAILURE(); + INIT_API_NO_RET_ON_FAILURE("dumpstack"); MINIDUMP_NOT_SUPPORTED(); @@ -483,7 +462,9 @@ DECLARE_API(DumpStack) }; size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) - return Status; + { + return E_INVALIDARG; + } // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options ULONG symlines = 0; @@ -537,7 +518,7 @@ DECLARE_API (EEStack) if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder enableDML(dml); @@ -617,21 +598,6 @@ DECLARE_API (EEStack) return Status; } -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the address and name of all * -* Managed Objects on the stack. * -* * -\**********************************************************************/ -DECLARE_API(DumpStackObjects) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("dumpstackobjects", args); -} - /**********************************************************************\ * Routine Description: * * * @@ -641,7 +607,7 @@ DECLARE_API(DumpStackObjects) \**********************************************************************/ DECLARE_API(DumpMD) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumpmd"); MINIDUMP_NOT_SUPPORTED(); DWORD_PTR dwStartAddr = NULL; @@ -659,7 +625,7 @@ DECLARE_API(DumpMD) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -757,7 +723,7 @@ GetILAddressResult GetILAddress(const DacpMethodDescData& MethodDescData); \**********************************************************************/ DECLARE_API(DumpIL) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumpil"); MINIDUMP_NOT_SUPPORTED(); DWORD_PTR dwStartAddr = NULL; DWORD_PTR dwDynamicMethodObj = NULL; @@ -778,7 +744,7 @@ DECLARE_API(DumpIL) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -973,12 +939,12 @@ DECLARE_API(DumpSig) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg != 2) { ExtOut("%sdumpsig \n", SOSPrefix); - return Status; + return E_INVALIDARG; } DWORD_PTR dwSigAddr = GetExpression(sigExpr.data); @@ -1020,13 +986,13 @@ DECLARE_API(DumpSigElem) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg != 2) { ExtOut("%sdumpsigelem \n", SOSPrefix); - return Status; + return E_INVALIDARG; } DWORD_PTR dwSigAddr = GetExpression(sigExpr.data); @@ -1035,7 +1001,7 @@ DECLARE_API(DumpSigElem) if (dwSigAddr == 0 || dwModuleAddr == 0) { ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data); - return Status; + return E_INVALIDARG; } DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE); @@ -1051,7 +1017,7 @@ DECLARE_API(DumpSigElem) \**********************************************************************/ DECLARE_API(DumpClass) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumpclass"); MINIDUMP_NOT_SUPPORTED(); DWORD_PTR dwStartAddr = 0; @@ -1069,13 +1035,13 @@ DECLARE_API(DumpClass) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg == 0) { ExtOut("Missing EEClass address\n"); - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -1170,7 +1136,7 @@ DECLARE_API(DumpMT) DWORD_PTR dwStartAddr=0; DWORD_PTR dwOriginalAddr; - INIT_API(); + INIT_API_PROBE_MANAGED("dumpmt"); MINIDUMP_NOT_SUPPORTED(); @@ -1189,7 +1155,7 @@ DECLARE_API(DumpMT) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -1198,7 +1164,7 @@ DECLARE_API(DumpMT) if (nArg == 0) { Print("Missing MethodTable address\n"); - return Status; + return E_INVALIDARG; } dwOriginalAddr = dwStartAddr; @@ -1207,7 +1173,7 @@ DECLARE_API(DumpMT) if (!IsMethodTable(dwStartAddr)) { Print(dwOriginalAddr, " is not a MethodTable\n"); - return Status; + return E_INVALIDARG; } DacpMethodTableData vMethTable; @@ -1216,7 +1182,7 @@ DECLARE_API(DumpMT) if (vMethTable.bIsFree) { Print("Free MethodTable\n"); - return Status; + return E_INVALIDARG; } DacpMethodTableCollectibleData vMethTableCollectible; @@ -1834,7 +1800,7 @@ HRESULT PrintPermissionSet (TADDR p_PermSet) \**********************************************************************/ DECLARE_API(DumpArray) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumparray"); DumpArrayFlags flags; @@ -1857,7 +1823,7 @@ DECLARE_API(DumpArray) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -1865,7 +1831,7 @@ DECLARE_API(DumpArray) if (p_Object == 0) { ExtOut("Invalid parameter %s\n", flags.strObject); - return Status; + return E_INVALIDARG; } if (!sos::IsObject(p_Object, true)) @@ -2052,7 +2018,7 @@ HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSe \**********************************************************************/ DECLARE_API(DumpObj) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumpobj"); MINIDUMP_NOT_SUPPORTED(); @@ -2073,7 +2039,7 @@ DECLARE_API(DumpObj) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } DWORD_PTR p_Object = GetExpression(str_Object.data); @@ -2081,7 +2047,7 @@ DECLARE_API(DumpObj) if (p_Object == 0) { ExtOut("Invalid parameter %s\n", args); - return Status; + return E_INVALIDARG; } try { @@ -2129,7 +2095,7 @@ DECLARE_API(DumpALC) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } DWORD_PTR p_Object = GetExpression(str_Object.data); @@ -2137,7 +2103,7 @@ DECLARE_API(DumpALC) if (p_Object == 0) { ExtOut("Invalid parameter %s\n", args); - return Status; + return E_INVALIDARG; } try @@ -2163,7 +2129,7 @@ DECLARE_API(DumpALC) DECLARE_API(DumpDelegate) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumpdelegate"); MINIDUMP_NOT_SUPPORTED(); try @@ -2182,12 +2148,12 @@ DECLARE_API(DumpDelegate) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg != 1) { ExtOut("Usage: %sdumpdelegate \n", SOSPrefix); - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -2879,7 +2845,7 @@ HRESULT FormatException(CLRDATA_ADDRESS taObj, BOOL bLineNumbers = FALSE) DECLARE_API(PrintException) { - INIT_API(); + INIT_API_PROBE_MANAGED("printexception"); BOOL dml = FALSE; BOOL bShowNested = FALSE; @@ -2901,7 +2867,7 @@ DECLARE_API(PrintException) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } CheckBreakingRuntimeChange(); @@ -2969,7 +2935,7 @@ DECLARE_API(PrintException) { ExtOut("Invalid exception object %s\n", args); } - return Status; + return E_INVALIDARG; } if (bCCW) @@ -2996,7 +2962,7 @@ DECLARE_API(PrintException) if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK)) { ExtOut("The current thread is unmanaged\n"); - return Status; + return E_INVALIDARG; } if (Thread.firstNestedException) @@ -3048,7 +3014,7 @@ DECLARE_API(PrintException) \**********************************************************************/ DECLARE_API(DumpVC) { - INIT_API(); + INIT_API_PROBE_MANAGED("dumpvc"); MINIDUMP_NOT_SUPPORTED(); DWORD_PTR p_MT = NULL; @@ -3067,7 +3033,7 @@ DECLARE_API(DumpVC) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -3109,7 +3075,7 @@ DECLARE_API(DumpRCW) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -3237,7 +3203,7 @@ DECLARE_API(DumpCCW) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -3493,7 +3459,7 @@ DECLARE_API(DumpPermissionSet) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg!=1) { @@ -3508,18 +3474,6 @@ DECLARE_API(DumpPermissionSet) #endif // _DEBUG #endif // FEATURE_PAL -/**********************************************************************\ -* Routine Description: * -* * -* This function dumps GC heap size. * -* * -\**********************************************************************/ -DECLARE_API(EEHeap) -{ - INIT_API_EXT(); - return ExecuteCommand("eeheap", args); -} - void PrintGCStat(HeapStat *inStat, const char* label=NULL) { if (inStat) @@ -3540,20 +3494,6 @@ void PrintGCStat(HeapStat *inStat, const char* label=NULL) } } -DECLARE_API(TraverseHeap) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - return ExecuteCommand("traverseheap", args); -} - -DECLARE_API(DumpRuntimeTypes) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - return ExecuteCommand("dumpruntimetypes", args); -} - namespace sos { class FragmentationBlock @@ -3591,52 +3531,6 @@ namespace sos }; } -DECLARE_API(DumpHeap) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - return ExecuteCommand("dumpheap", args); -} - -DECLARE_API(VerifyHeap) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - return ExecuteCommand("verifyheap", args); -} - -DECLARE_API(AnalyzeOOM) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("analyzeoom", args); -} - -DECLARE_API(VerifyObj) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("verifyobj", args); -} - -DECLARE_API(ListNearObj) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("listnearobj", args); -} - -DECLARE_API(GCHeapStat) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("gcheapstat", args); -} - /**********************************************************************\ * Routine Description: * * * @@ -3665,7 +3559,7 @@ DECLARE_API(SyncBlk) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -3915,21 +3809,6 @@ DECLARE_API(RCWCleanupList) } #endif // FEATURE_COMINTEROP -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of the finalizer * -* queue. * -* * -\**********************************************************************/ -DECLARE_API(FinalizeQueue) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("finalizequeue", args); -} - enum { // These are the values set in m_dwTransientFlags. // Note that none of these flags survive a prejit save/restore. @@ -4011,12 +3890,12 @@ DECLARE_API(DumpModule) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg != 1) { ExtOut("Usage: DumpModule [-mt] \n"); - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -4182,7 +4061,7 @@ DECLARE_API(DumpDomain) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -4303,7 +4182,7 @@ DECLARE_API(DumpAssembly) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -4849,7 +4728,7 @@ DECLARE_API(ThreadState) DECLARE_API(Threads) { - INIT_API(); + INIT_API_PROBE_MANAGED("clrthreads"); BOOL bPrintSpecialThreads = FALSE; BOOL bPrintLiveThreadsOnly = FALSE; @@ -4865,7 +4744,7 @@ DECLARE_API(Threads) }; if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) { - return Status; + return E_INVALIDARG; } if (bSwitchToManagedExceptionThread) @@ -5961,7 +5840,7 @@ DECLARE_API(SOSHandleCLRN) HRESULT HandleRuntimeLoadedNotification(IDebugClient* client) { - INIT_API(); + INIT_API_EFN(); EnableModuleLoadUnloadCallbacks(); return g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0); } @@ -5970,13 +5849,13 @@ HRESULT HandleRuntimeLoadedNotification(IDebugClient* client) HRESULT HandleExceptionNotification(ILLDBServices *client) { - INIT_API(); + INIT_API_EFN(); return HandleCLRNotificationEvent(); } HRESULT HandleRuntimeLoadedNotification(ILLDBServices *client) { - INIT_API(); + INIT_API_EFN(); EnableModuleLoadUnloadCallbacks(); return g_ExtServices->SetExceptionCallback(HandleExceptionNotification); } @@ -6027,7 +5906,7 @@ DECLARE_API(bpmd) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } bool fBadParam = false; @@ -6359,20 +6238,6 @@ DECLARE_API(bpmd) return Status; } -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the managed threadpool * -* * -\**********************************************************************/ -DECLARE_API(ThreadPool) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("threadpool", args); -} - DECLARE_API(FindAppDomain) { INIT_API(); @@ -6393,7 +6258,7 @@ DECLARE_API(FindAppDomain) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -6643,7 +6508,7 @@ BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID tok DECLARE_API(EHInfo) { - INIT_API(); + INIT_API_PROBE_MANAGED("ehinfo"); MINIDUMP_NOT_SUPPORTED(); DWORD_PTR dwStartAddr = NULL; @@ -6662,7 +6527,7 @@ DECLARE_API(EHInfo) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || (0 == nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -6724,7 +6589,7 @@ DECLARE_API(EHInfo) \**********************************************************************/ DECLARE_API(GCInfo) { - INIT_API(); + INIT_API_PROBE_MANAGED("gcinfo"); MINIDUMP_NOT_SUPPORTED(); TADDR taStartAddr = NULL; @@ -6742,7 +6607,7 @@ DECLARE_API(GCInfo) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || (0 == nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -7049,7 +6914,7 @@ DECLARE_API(u) }; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg) || (nArg < 1)) { - return Status; + return E_INVALIDARG; } // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options ULONG symlines = 0; @@ -7553,10 +7418,8 @@ HRESULT GetIntermediateLangMap(BOOL bIL, const DacpCodeHeaderData& codeHeaderDat \**********************************************************************/ DECLARE_API(DumpLog) { - INIT_API_NO_RET_ON_FAILURE(); - + INIT_API_NO_RET_ON_FAILURE("dumplog"); MINIDUMP_NOT_SUPPORTED(); - _ASSERTE(g_pRuntime != nullptr); // Not supported on desktop runtime @@ -7584,7 +7447,7 @@ DECLARE_API(DumpLog) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg > 0 && sFileName.data != NULL) { @@ -8048,7 +7911,7 @@ extern char sccsid[]; \**********************************************************************/ DECLARE_API(EEVersion) { - INIT_API_NO_RET_ON_FAILURE(); + INIT_API_NO_RET_ON_FAILURE("eeversion"); static const int fileVersionBufferSize = 1024; ArrayHolder fileVersionBuffer = new char[fileVersionBufferSize]; @@ -8139,39 +8002,31 @@ DECLARE_API(EEVersion) \**********************************************************************/ DECLARE_API(SOSStatus) { - INIT_API_NOEE(); + INIT_API_NOEE_PROBE_MANAGED("sosstatus"); - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) + BOOL bReset = FALSE; + CMDOption option[] = + { // name, vptr, type, hasValue + {"-reset", &bReset, COBOOL, FALSE}, + {"--reset", &bReset, COBOOL, FALSE}, + {"-r", &bReset, COBOOL, FALSE}, + }; + if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) { - Status = hostServices->DispatchCommand("sosstatus", args); + return E_INVALIDARG; } - else + if (bReset) { - BOOL bReset = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-reset", &bReset, COBOOL, FALSE}, - {"--reset", &bReset, COBOOL, FALSE}, - {"-r", &bReset, COBOOL, FALSE}, - }; - if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) - { - return Status; - } - if (bReset) + ITarget* target = GetTarget(); + if (target != nullptr) { - ITarget* target = GetTarget(); - if (target != nullptr) - { - target->Flush(); - } - ExtOut("Internal cached state reset\n"); - return S_OK; + target->Flush(); } - Target::DisplayStatus(); + ExtOut("Internal cached state reset\n"); + return S_OK; } - return Status; + Target::DisplayStatus(); + return S_OK; } #ifndef FEATURE_PAL @@ -8485,13 +8340,13 @@ DECLARE_API(Token2EE) size_t nArg; if (!GetCMDOption(args,option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg!=2) { ExtOut("Usage: %stoken2ee module_name mdToken\n", SOSPrefix); ExtOut(" You can pass * for module_name to search all modules.\n"); - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -8557,7 +8412,7 @@ DECLARE_API(Token2EE) \**********************************************************************/ DECLARE_API(Name2EE) { - INIT_API(); + INIT_API_PROBE_MANAGED("name2ee"); MINIDUMP_NOT_SUPPORTED(); StringHolder DllName, TypeName; @@ -8577,7 +8432,7 @@ DECLARE_API(Name2EE) if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -8620,7 +8475,7 @@ DECLARE_API(Name2EE) ExtOut(" use * for module_name to search all loaded modules\n"); ExtOut("Examples: %sname2ee mscorlib.dll System.String.ToString\n", SOSPrefix); ExtOut(" %sname2ee *!System.String\n", SOSPrefix); - return Status; + return E_INVALIDARG; } int numModule; @@ -8675,43 +8530,9 @@ DECLARE_API(Name2EE) return Status; } - -DECLARE_API(PathTo) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("pathto", args); -} - - -/**********************************************************************\ -* Routine Description: * -* * -* This function finds all roots (on stack or in handles) for a * -* given object. * -* * -\**********************************************************************/ -DECLARE_API(GCRoot) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("gcroot", args); -} - -DECLARE_API(GCWhere) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("gcwhere", args); -} - - DECLARE_API(FindRoots) { - INIT_API_EXT(); + INIT_API(); MINIDUMP_NOT_SUPPORTED(); if (IsDumpFile()) @@ -8737,7 +8558,7 @@ DECLARE_API(FindRoots) }; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -8939,8 +8760,10 @@ class GCHandlesImpl {"/d", &mDML, COBOOL, FALSE}, }; - if (!GetCMDOption(args,option,ARRAY_SIZE(option),NULL,0,NULL)) + if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) + { sos::Throw("Failed to parse command line arguments."); + } if (type != NULL) { if (_stricmp(type, "Pinned") == 0) @@ -9326,7 +9149,7 @@ DECLARE_API(GetCodeTypeFlags) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } size_t preg = 1; // by default @@ -9336,7 +9159,7 @@ DECLARE_API(GetCodeTypeFlags) if (preg > 19) { ExtOut("Pseudo-register number must be between 0 and 19\n"); - return Status; + return E_INVALIDARG; } } @@ -9434,19 +9257,19 @@ DECLARE_API(StopOnException) size_t nArg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (IsDumpFile()) { ExtOut("Live debugging session required\n"); - return Status; + return E_INVALIDARG; } if (nArg < 1 || nArg > 2) { ExtOut("usage: StopOnException [-derived] [-create | -create2] \n"); ExtOut(" []\n"); ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n"); - return Status; + return E_INVALIDARG; } size_t preg = 1; // by default @@ -9456,7 +9279,7 @@ DECLARE_API(StopOnException) if (preg > 19) { ExtOut("Pseudo-register number must be between 0 and 19\n"); - return Status; + return E_INVALIDARG; } } @@ -9541,20 +9364,6 @@ DECLARE_API(StopOnException) return Status; } -/**********************************************************************\ -* Routine Description: * -* * -* This function finds the size of an object or all roots. * -* * -\**********************************************************************/ -DECLARE_API(ObjSize) -{ - INIT_API_EXT(); - MINIDUMP_NOT_SUPPORTED(); - - return ExecuteCommand("objsize", args); -} - #ifndef FEATURE_PAL // For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO? DECLARE_API(GCHandleLeaks) @@ -9580,7 +9389,7 @@ DECLARE_API(GCHandleLeaks) if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -11582,7 +11391,7 @@ DECLARE_API(Watch) }; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if(addExpression.data != NULL || aExpression.data != NULL) @@ -11671,7 +11480,7 @@ DECLARE_API(Watch) DECLARE_API(ClrStack) { - INIT_API(); + INIT_API_PROBE_MANAGED("clrstack"); BOOL bAll = FALSE; BOOL bParams = FALSE; @@ -11715,7 +11524,7 @@ DECLARE_API(ClrStack) }; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } EnableDMLHolder dmlHolder(dml); @@ -11785,7 +11594,7 @@ BOOL IsMemoryInfoAvailable() return TRUE; } -DECLARE_API( VMMap ) +DECLARE_API(VMMap) { INIT_API(); @@ -11799,30 +11608,21 @@ DECLARE_API( VMMap ) } return Status; -} // DECLARE_API( vmmap ) +} #endif // FEATURE_PAL DECLARE_API(SOSFlush) { - INIT_API_NOEE(); + INIT_API_NOEE_PROBE_MANAGED("sosflush"); - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) + ITarget* target = GetTarget(); + if (target != nullptr) { - Status = hostServices->DispatchCommand("sosflush", args); + target->Flush(); } - else - { - ITarget* target = GetTarget(); - if (target != nullptr) - { - target->Flush(); - } - ExtOut("Internal cached state reset\n"); - return S_OK; - } - return Status; + ExtOut("Internal cached state reset\n"); + return S_OK; } #ifndef FEATURE_PAL @@ -11867,16 +11667,16 @@ DECLARE_API(SaveModule) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } if (nArg != 2) { ExtOut("Usage: SaveModule
\n"); - return Status; + return E_INVALIDARG; } if (moduleAddr == 0) { ExtOut ("Invalid arg\n"); - return Status; + return E_INVALIDARG; } char* ptr = Location.data; @@ -12057,7 +11857,7 @@ DECLARE_API(dbgout) if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) { - return Status; + return E_INVALIDARG; } Output::SetDebugOutputEnabled(!bOff); @@ -12532,7 +12332,7 @@ HRESULT CALLBACK _EFN_StackTrace( size_t uiSizeOfContext, DWORD Flags) { - INIT_API(); + INIT_API_EFN(); Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength, pTransitionContexts, puiTransitionContextCount, @@ -12830,7 +12630,7 @@ DECLARE_API(VerifyStackTrace) if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL,0,NULL)) { - return Status; + return E_INVALIDARG; } if (bVerifyManagedExcepStack) @@ -13036,7 +12836,7 @@ DECLARE_API(SaveState) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return E_FAIL; + return E_INVALIDARG; } if(nArg == 0) @@ -13076,7 +12876,7 @@ DECLARE_API(SuppressJitOptimization) size_t nArg; if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return E_FAIL; + return E_INVALIDARG; } if (nArg == 1 && (_stricmp(onOff.data, "On") == 0)) @@ -13405,7 +13205,7 @@ _EFN_GetManagedExcepStack( ULONG cbString ) { - INIT_API(); + INIT_API_EFN(); ArrayHolder tmpStr = new NOTHROW WCHAR[cbString]; if (tmpStr == NULL) @@ -13436,7 +13236,7 @@ _EFN_GetManagedExcepStackW( ULONG cchString ) { - INIT_API(); + INIT_API_EFN(); return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString); } @@ -13452,7 +13252,7 @@ _EFN_GetManagedObjectName( ULONG cbName ) { - INIT_API (); + INIT_API_EFN(); if (!sos::IsObject(objAddr, false)) { @@ -13481,7 +13281,7 @@ _EFN_GetManagedObjectFieldInfo( PULONG pOffset ) { - INIT_API(); + INIT_API_EFN(); DacpObjectData objData; LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR)); @@ -13540,7 +13340,7 @@ DECLARE_API(VerifyGMT) if (!GetCMDOption(args, NULL, 0, arg, ARRAY_SIZE(arg), &nArg)) { - return Status; + return E_INVALIDARG; } } ULONG64 managedThread; @@ -13565,7 +13365,7 @@ _EFN_GetManagedThread( ULONG osThreadId, PULONG64 pManagedThread) { - INIT_API(); + INIT_API_EFN(); _ASSERTE(pManagedThread != nullptr); *pManagedThread = 0; @@ -13623,7 +13423,7 @@ DECLARE_API(SetHostRuntime) size_t narg; if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &narg)) { - return E_FAIL; + return E_INVALIDARG; } if (narg > 0 || bNetCore || bNetFx || bNone) { @@ -13689,41 +13489,31 @@ DECLARE_API(SetHostRuntime) // DECLARE_API(SetClrPath) { - INIT_API_NOEE(); + INIT_API_NODAC_PROBE_MANAGED("setclrpath"); - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) + StringHolder runtimeModulePath; + CMDValue arg[] = + { + {&runtimeModulePath.data, COSTRING}, + }; + size_t narg; + if (!GetCMDOption(args, nullptr, 0, arg, ARRAY_SIZE(arg), &narg)) { - return hostServices->DispatchCommand("setclrpath", args); + return E_FAIL; } - else + if (narg > 0) { - INIT_API_EE(); - - StringHolder runtimeModulePath; - CMDValue arg[] = - { - {&runtimeModulePath.data, COSTRING}, - }; - size_t narg; - if (!GetCMDOption(args, nullptr, 0, arg, ARRAY_SIZE(arg), &narg)) + std::string fullPath; + if (!GetAbsolutePath(runtimeModulePath.data, fullPath)) { + ExtErr("Invalid runtime directory %s\n", fullPath.c_str()); return E_FAIL; } - if (narg > 0) - { - std::string fullPath; - if (!GetAbsolutePath(runtimeModulePath.data, fullPath)) - { - ExtErr("Invalid runtime directory %s\n", fullPath.c_str()); - return E_FAIL; - } - g_pRuntime->SetRuntimeDirectory(fullPath.c_str()); - } - const char* runtimeDirectory = g_pRuntime->GetRuntimeDirectory(); - if (runtimeDirectory != nullptr) { - ExtOut("Runtime module directory: %s\n", runtimeDirectory); - } + g_pRuntime->SetRuntimeDirectory(fullPath.c_str()); + } + const char* runtimeDirectory = g_pRuntime->GetRuntimeDirectory(); + if (runtimeDirectory != nullptr) { + ExtOut("Runtime module directory: %s\n", runtimeDirectory); } return S_OK; } @@ -13733,138 +13523,46 @@ DECLARE_API(SetClrPath) // DECLARE_API(runtimes) { - INIT_API_NOEE(); + INIT_API_NOEE_PROBE_MANAGED("runtimes"); - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) + BOOL bNetFx = FALSE; + BOOL bNetCore = FALSE; + CMDOption option[] = + { // name, vptr, type, hasValue + {"-netfx", &bNetFx, COBOOL, FALSE}, + {"-netcore", &bNetCore, COBOOL, FALSE}, + }; + if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) { - Status = hostServices->DispatchCommand("runtimes", args); + return E_INVALIDARG; } - else + if (bNetCore || bNetFx) { - BOOL bNetFx = FALSE; - BOOL bNetCore = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-netfx", &bNetFx, COBOOL, FALSE}, - {"-netcore", &bNetCore, COBOOL, FALSE}, - }; - if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL)) - { - return Status; - } - if (bNetCore || bNetFx) - { #ifndef FEATURE_PAL - if (IsWindowsTarget()) - { - PCSTR name = bNetFx ? "desktop .NET Framework" : ".NET Core"; - if (!Target::SwitchRuntime(bNetFx)) - { - ExtErr("The %s runtime is not loaded\n", name); - return E_FAIL; - } - ExtOut("Switched to %s runtime successfully\n", name); - } - else -#endif + if (IsWindowsTarget()) + { + PCSTR name = bNetFx ? "desktop .NET Framework" : ".NET Core"; + if (!Target::SwitchRuntime(bNetFx)) { - ExtErr("The '-netfx' and '-netcore' options are only supported on Windows targets\n"); - return E_FAIL; + ExtErr("The %s runtime is not loaded\n", name); + return E_INVALIDARG; } + ExtOut("Switched to %s runtime successfully\n", name); } else +#endif { - Target::DisplayStatus(); + ExtErr("The '-netfx' and '-netcore' options are only supported on Windows targets\n"); + return E_INVALIDARG; } } - return Status; -} - -#ifdef HOST_WINDOWS -// -// Sets the symbol server path. -// -DECLARE_API(SetSymbolServer) -{ - INIT_API_EXT(); - return ExecuteCommand("setsymbolserver", args); -} - -// -// Dumps the managed assemblies -// -DECLARE_API(clrmodules) -{ - INIT_API_EXT(); - return ExecuteCommand("clrmodules", args); -} - -// -// Dumps the Native AOT crash info -// -DECLARE_API(crashinfo) -{ - INIT_API_EXT(); - return ExecuteCommand("crashinfo", args); -} - -// -// Dumps async stacks -// -DECLARE_API(DumpAsync) -{ - INIT_API_EXT(); - return ExecuteCommand("dumpasync", args); -} - -// -// Enables and disables managed extension logging -// -DECLARE_API(logging) -{ - INIT_API_EXT(); - return ExecuteCommand("logging", args); -} - -typedef HRESULT (*PFN_COMMAND)(PDEBUG_CLIENT client, PCSTR args); - -// -// Executes managed extension commands -// -DECLARE_API(ext) -{ - INIT_API_EXT(); - - if (args == nullptr || strlen(args) <= 0) - { - args = "Help"; - } - std::string arguments(args); - size_t pos = arguments.find(' '); - std::string commandName = arguments.substr(0, pos); - if (pos != std::string::npos) - { - arguments = arguments.substr(pos + 1); - } else { - arguments.clear(); - } - Status = ExecuteCommand(commandName.c_str(), arguments.c_str()); - if (Status == E_NOTIMPL) - { - PFN_COMMAND commandFunc = (PFN_COMMAND)GetProcAddress(g_hInstance, commandName.c_str()); - if (commandFunc != nullptr) - { - Status = (*commandFunc)(client, arguments.c_str()); - } + Target::DisplayStatus(); } return Status; } -#endif // HOST_WINDOWS - void PrintHelp (__in_z LPCSTR pszCmdName) { static LPSTR pText = NULL; @@ -13969,7 +13667,7 @@ void PrintHelp (__in_z LPCSTR pszCmdName) \**********************************************************************/ DECLARE_API(Help) { - INIT_API_EXT(); + INIT_API_NOEE_PROBE_MANAGED("help"); StringHolder commandName; CMDValue arg[] = @@ -13986,15 +13684,6 @@ DECLARE_API(Help) if (nArg == 1) { - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) - { - if (hostServices->DisplayHelp(commandName.data) == S_OK) - { - return S_OK; - } - } - // Convert commandName to lower-case LPSTR curChar = commandName.data; while (*curChar != '\0') @@ -14016,12 +13705,6 @@ DECLARE_API(Help) else { PrintHelp ("contents"); - IHostServices* hostServices = GetHostServices(); - if (hostServices != nullptr) - { - ExtOut("\n"); - hostServices->DisplayHelp(nullptr); - } } return S_OK; diff --git a/src/SOS/Strike/util.h b/src/SOS/Strike/util.h index 50467dd8ad..8eb3f60e52 100644 --- a/src/SOS/Strike/util.h +++ b/src/SOS/Strike/util.h @@ -1778,8 +1778,6 @@ CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr); BOOL IsMTForFreeObj(DWORD_PTR pMT); -HRESULT ExecuteCommand(PCSTR commandName, PCSTR args); - enum ARGTYPE {COBOOL,COSIZE_T,COHEX,COSTRING}; struct CMDOption { diff --git a/src/SOS/inc/hostservices.h b/src/SOS/inc/hostservices.h index 788f6f3538..74f1815609 100644 --- a/src/SOS/inc/hostservices.h +++ b/src/SOS/inc/hostservices.h @@ -82,14 +82,6 @@ IHostServices : public IUnknown PCSTR commandName, PCSTR arguments) = 0; - /// - /// Displays the help for a managed extension command - /// - /// - /// error code - virtual HRESULT STDMETHODCALLTYPE DisplayHelp( - PCSTR commandName) = 0; - /// /// Uninitialize the extension infrastructure /// diff --git a/src/SOS/lldbplugin/soscommand.cpp b/src/SOS/lldbplugin/soscommand.cpp index cdb5c55376..95847b9bce 100644 --- a/src/SOS/lldbplugin/soscommand.cpp +++ b/src/SOS/lldbplugin/soscommand.cpp @@ -157,6 +157,7 @@ sosCommandInitialize(lldb::SBDebugger debugger) g_services->AddCommand("ext", new sosCommand(nullptr), "Executes various coreclr debugging commands. Use the syntax 'sos '. For more information, see 'soshelp'."); g_services->AddManagedCommand("analyzeoom", "Provides a stack trace of managed code only."); g_services->AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module."); + g_services->AddManagedCommand("assemblies", "Lists the managed modules in the process."); g_services->AddManagedCommand("clrmodules", "Lists the managed modules in the process."); g_services->AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only."); g_services->AddCommand("clrthreads", new sosCommand("Threads"), "Lists the managed threads running."); @@ -204,12 +205,12 @@ sosCommandInitialize(lldb::SBDebugger debugger) g_services->AddCommand("histroot", new sosCommand("HistRoot"), "Displays information related to both promotions and relocations of the specified root."); g_services->AddCommand("histstats", new sosCommand("HistStats"), "Displays stress log stats."); g_services->AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled."); - g_services->AddCommand("listnearobj", new sosCommand("ListNearObj"), "Displays the object preceding and succeeding the specified address."); + g_services->AddManagedCommand("listnearobj", "Displays the object preceding and succeeding the specified address."); g_services->AddManagedCommand("loadsymbols", "Loads the .NET Core native module symbols."); g_services->AddManagedCommand("logging", "Enables/disables internal SOS logging."); g_services->AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module."); g_services->AddManagedCommand("objsize", "Displays the size of the specified object."); - g_services->AddCommand("pathto", new sosCommand("PathTo"), "Displays the GC path from to ."); + g_services->AddManagedCommand("pathto", "Displays the GC path from to ."); g_services->AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address."); g_services->AddCommand("printexception", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address."); g_services->AddCommand("runtimes", new sosCommand("runtimes"), "Lists the runtimes in the target or change the default runtime."); @@ -225,5 +226,6 @@ sosCommandInitialize(lldb::SBDebugger debugger) g_services->AddCommand("token2ee", new sosCommand("token2ee"), "Displays the MethodTable structure and MethodDesc structure for the specified token and module."); g_services->AddManagedCommand("verifyheap", "Checks the GC heap for signs of corruption."); g_services->AddManagedCommand("verifyobj", "Checks the object that is passed as an argument for signs of corruption."); + g_services->AddManagedCommand("traverseheap", "Writes out heap information to a file in a format understood by the CLR Profiler."); return true; } diff --git a/src/Tools/dotnet-dump/Analyzer.cs b/src/Tools/dotnet-dump/Analyzer.cs index 6718677d65..2a2b5defcf 100644 --- a/src/Tools/dotnet-dump/Analyzer.cs +++ b/src/Tools/dotnet-dump/Analyzer.cs @@ -63,11 +63,11 @@ public Task Analyze(FileInfo dump_path, string[] command) _consoleService.AddCommandHistory(history); } catch (Exception ex) when - (ex is IOException or - ArgumentNullException or - UnauthorizedAccessException or - NotSupportedException or - SecurityException) + (ex is IOException + or ArgumentNullException + or UnauthorizedAccessException + or NotSupportedException + or SecurityException) { } @@ -86,9 +86,6 @@ NotSupportedException or // Add the specially handled exit command _commandService.AddCommands(typeof(ExitCommand), (services) => new ExitCommand(_consoleService.Stop)); - // Add "sos" command manually - _commandService.AddCommands(typeof(SOSCommand), (services) => new SOSCommand(_commandService, services)); - // Display any extension assembly loads on console _serviceManager.NotifyExtensionLoad.Register((Assembly assembly) => _fileLoggingConsoleService.WriteLine($"Loading extension {assembly.Location}")); _serviceManager.NotifyExtensionLoadFailure.Register((Exception ex) => _fileLoggingConsoleService.WriteLine(ex.Message)); @@ -107,6 +104,7 @@ NotSupportedException or _serviceContainer.AddService(_fileLoggingConsoleService); _serviceContainer.AddService(DiagnosticLoggingService.Instance); _serviceContainer.AddService(_commandService); + _serviceContainer.AddService(_commandService); SymbolService symbolService = new(this); _serviceContainer.AddService(symbolService); @@ -133,18 +131,24 @@ NotSupportedException or symbolService.AddCachePath(symbolService.DefaultSymbolCache); symbolService.AddDirectoryPath(Path.GetDirectoryName(dump_path.FullName)); - // Run the commands from the dotnet-dump command line + // Run the commands from the dotnet-dump command line. Any errors/exceptions from the + // command execution will be displayed and dotnet-dump exited. if (command != null) { - foreach (string cmd in command) + foreach (string commandLine in command) { - _commandService.Execute(cmd, contextService.Services); + if (!_commandService.Execute(commandLine, contextService.Services)) + { + throw new CommandNotFoundException($"{CommandNotFoundException.NotFoundMessage} '{commandLine}'"); + } if (_consoleService.Shutdown) { break; } } } + + // Now start the REPL command loop if the console isn't redirected if (!_consoleService.Shutdown && (!Console.IsOutputRedirected || Console.IsInputRedirected)) { // Start interactive command line processing @@ -153,21 +157,25 @@ NotSupportedException or _consoleService.Start((string prompt, string commandLine, CancellationToken cancellation) => { _fileLoggingConsoleService.WriteLine("{0}{1}", prompt, commandLine); - _commandService.Execute(commandLine, contextService.Services); + if (!_commandService.Execute(commandLine, contextService.Services)) + { + throw new CommandNotFoundException($"{CommandNotFoundException.NotFoundMessage} '{commandLine}'"); + } }); } } catch (Exception ex) when - (ex is ClrDiagnosticsException or - FileNotFoundException or - DirectoryNotFoundException or - UnauthorizedAccessException or - PlatformNotSupportedException or - InvalidDataException or - InvalidOperationException or - NotSupportedException) + (ex is ClrDiagnosticsException + or DiagnosticsException + or FileNotFoundException + or DirectoryNotFoundException + or UnauthorizedAccessException + or PlatformNotSupportedException + or InvalidDataException + or InvalidOperationException + or NotSupportedException) { - _fileLoggingConsoleService.WriteError($"{ex.Message}"); + _fileLoggingConsoleService.WriteLineError($"{ex.Message}"); return Task.FromResult(1); } finally @@ -186,10 +194,10 @@ InvalidOperationException or File.WriteAllLines(historyFileName, _consoleService.GetCommandHistory()); } catch (Exception ex) when - (ex is IOException or - UnauthorizedAccessException or - NotSupportedException or - SecurityException) + (ex is IOException + or UnauthorizedAccessException + or NotSupportedException + or SecurityException) { } } diff --git a/src/Tools/dotnet-dump/Commands/ReadMemoryCommand.cs b/src/Tools/dotnet-dump/Commands/ReadMemoryCommand.cs index f4017f7b47..21f6475de8 100644 --- a/src/Tools/dotnet-dump/Commands/ReadMemoryCommand.cs +++ b/src/Tools/dotnet-dump/Commands/ReadMemoryCommand.cs @@ -7,7 +7,7 @@ namespace Microsoft.Diagnostics.Tools.Dump { - [Command(Name = "readmemory", Aliases = new string[] { "d" }, Help = "Dumps memory contents.")] + [Command(Name = "d", Aliases = new string[] { "readmemory" }, Help = "Dumps memory contents.")] [Command(Name = "db", DefaultOptions = "--ascii:true --unicode:false --ascii-string:false --unicode-string:false -c:128 -l:1 -w:16", Help = "Dumps memory as bytes.")] [Command(Name = "dc", DefaultOptions = "--ascii:false --unicode:true --ascii-string:false --unicode-string:false -c:64 -l:2 -w:8", Help = "Dumps memory as chars.")] [Command(Name = "da", DefaultOptions = "--ascii:false --unicode:false --ascii-string:true --unicode-string:false -c:128 -l:1 -w:0", Help = "Dumps memory as zero-terminated byte strings.")] diff --git a/src/Tools/dotnet-dump/Commands/SOSCommand.cs b/src/Tools/dotnet-dump/Commands/SOSCommand.cs index a3c15fc79e..a2045070c4 100644 --- a/src/Tools/dotnet-dump/Commands/SOSCommand.cs +++ b/src/Tools/dotnet-dump/Commands/SOSCommand.cs @@ -9,55 +9,48 @@ namespace Microsoft.Diagnostics.Tools.Dump { - [Command(Name = "sos", Aliases = new string[] { "ext" }, Help = "Executes various SOS debugging commands.", Flags = CommandFlags.Global | CommandFlags.Manual)] + [Command(Name = "sos", Aliases = new string[] { "ext" }, Help = "Executes various SOS debugging commands.")] public class SOSCommand : CommandBase { - private readonly CommandService _commandService; - private readonly IServiceProvider _services; - private SOSHost _sosHost; + [ServiceImport] + public CommandService CommandService { get; set; } - [Argument(Name = "arguments", Help = "SOS command and arguments.")] + [ServiceImport] + public IServiceProvider Services { get; set; } + + [ServiceImport(Optional = true)] + public SOSHost SOSHost { get; set; } + + [Argument(Name = "command_and_arguments", Help = "SOS command and arguments.")] public string[] Arguments { get; set; } - public SOSCommand(CommandService commandService, IServiceProvider services) + public SOSCommand() { - _commandService = commandService; - _services = services; } public override void Invoke() { - string commandLine; - string commandName; + string command; + string arguments; if (Arguments != null && Arguments.Length > 0) { - commandLine = string.Concat(Arguments.Select((arg) => arg + " ")).Trim(); - commandName = Arguments[0]; + command = Arguments[0]; + arguments = string.Concat(Arguments.Skip(1).Select((arg) => arg + " ")).Trim(); } else { - commandLine = commandName = "help"; + command = "help"; + arguments = null; } - if (_commandService.IsCommand(commandName)) + if (CommandService.Execute(command, arguments, Services)) { - try - { - _commandService.Execute(commandLine, _services); - return; - } - catch (CommandNotSupportedException) - { - } + return; } - if (_sosHost is null) + if (SOSHost is null) { - _sosHost = _services.GetService(); - if (_sosHost is null) - { - throw new DiagnosticsException($"'{commandName}' command not found"); - } + throw new CommandNotFoundException($"{CommandNotFoundException.NotFoundMessage} '{command}'"); } - _sosHost.ExecuteCommand(commandLine); + SOSHost.ExecuteCommand(command, arguments); } } } diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/CommandServiceTests.cs b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/CommandServiceTests.cs new file mode 100644 index 0000000000..34f018693f --- /dev/null +++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/CommandServiceTests.cs @@ -0,0 +1,132 @@ +using Microsoft.Diagnostics.DebugServices.Implementation; +using Microsoft.Diagnostics.TestHelpers; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; + +[assembly: SuppressMessage("Performance", "CA1825:Avoid zero-length array allocations.", Justification = "")] + +namespace Microsoft.Diagnostics.DebugServices.UnitTests +{ + public class CommandServiceTests : IDisposable + { + private const string ListenerName = "CommandServiceTests"; + + private static IEnumerable _configurations; + + /// + /// Get the first test asset dump. It doesn't matter which one. + /// + /// + public static IEnumerable GetConfiguration() + { + return _configurations ??= TestRunConfiguration.Instance.Configurations + .Where((config) => config.AllSettings.ContainsKey("DumpFile")) + .Take(1) + .Select(c => new[] { c }) + .ToImmutableArray(); + } + + ITestOutputHelper Output { get; set; } + + public CommandServiceTests(ITestOutputHelper output) + { + Output = output; + LoggingListener.EnableListener(output, ListenerName); + } + + void IDisposable.Dispose() => Trace.Listeners.Remove(ListenerName); + + [SkippableTheory, MemberData(nameof(GetConfiguration))] + public void CommandServiceTest1(TestConfiguration config) + { + using TestDump testDump = new(config); + + CaptureConsoleService consoleService = new(); + testDump.ServiceContainer.AddService(consoleService); + + CommandService commandService = new(); + testDump.ServiceContainer.AddService(commandService); + + // Add all the test commands + commandService.AddCommands(typeof(TestCommand1).Assembly); + + // See if the test commands exists + Assert.Contains(commandService.Commands, ((string name, string help, IEnumerable aliases) cmd) => cmd.name == "testcommand"); + + // Invoke only TestCommand1 + TestCommand1.FilterValue = true; + TestCommand1.Invoked = false; + TestCommand2.FilterValue = false; + TestCommand2.Invoked = false; + TestCommand3.FilterValue = false; + TestCommand3.Invoked = false; + Assert.True(commandService.Execute("testcommand", testDump.Target.Services)); + Assert.True(TestCommand1.Invoked); + Assert.False(TestCommand2.Invoked); + Assert.False(TestCommand3.Invoked); + + // Check for TestCommand1 help + string help1 = commandService.GetDetailedHelp("testcommand", testDump.Target.Services, consoleWidth: int.MaxValue); + Assert.NotNull(help1); + Output.WriteLine(help1); + Assert.Contains("Test command #1", help1); + + // Invoke only TestCommand2 + TestCommand1.FilterValue = false; + TestCommand1.Invoked = false; + TestCommand2.FilterValue = true; + TestCommand2.Invoked = false; + TestCommand3.FilterValue = false; + TestCommand3.Invoked = false; + Assert.True(commandService.Execute("testcommand", testDump.Target.Services)); + Assert.False(TestCommand1.Invoked); + Assert.True(TestCommand2.Invoked); + Assert.False(TestCommand3.Invoked); + + // Invoke only TestCommand3 + + TestCommand1.FilterValue = false; + TestCommand1.Invoked = false; + TestCommand2.FilterValue = false; + TestCommand2.Invoked = false; + TestCommand3.FilterValue = true; + TestCommand3.Invoked = false; + Assert.True(commandService.Execute("testcommand", "--foo 23", testDump.Target.Services)); + Assert.False(TestCommand1.Invoked); + Assert.False(TestCommand2.Invoked); + Assert.True(TestCommand3.Invoked); + + // Check for TestCommand3 help + string help3 = commandService.GetDetailedHelp("testcommand", testDump.Target.Services, consoleWidth: int.MaxValue); + Assert.NotNull(help3); + Output.WriteLine(help3); + Assert.Contains("Test command #3", help3); + + // Invoke none of the test commands + TestCommand1.FilterValue = false; + TestCommand1.Invoked = false; + TestCommand2.FilterValue = false; + TestCommand2.Invoked = false; + TestCommand3.FilterValue = false; + TestCommand3.Invoked = false; + try + { + Assert.False(commandService.Execute("testcommand", testDump.Target.Services)); + } + catch (DiagnosticsException ex) + { + Assert.Matches("Test command #2 filter", ex.Message); + } + Assert.False(TestCommand1.Invoked); + Assert.False(TestCommand2.Invoked); + Assert.False(TestCommand3.Invoked); + } + } +} diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/DebugServicesTests.cs b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/DebugServicesTests.cs index 040ff1d8aa..fb7ba82b99 100644 --- a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/DebugServicesTests.cs +++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/DebugServicesTests.cs @@ -30,11 +30,11 @@ public class DebugServicesTests : IDisposable public static IEnumerable GetConfigurations() { - _configurations ??= TestRunConfiguration.Instance.Configurations + return _configurations ??= TestRunConfiguration.Instance.Configurations .Where((config) => config.AllSettings.ContainsKey("DumpFile")) - .Select((config) => CreateHost(config)) - .Select((host) => new[] { host }).ToImmutableArray(); - return _configurations; + .Select(CreateHost) + .Select((host) => new[] { host }) + .ToImmutableArray(); } private static TestHost CreateHost(TestConfiguration config) @@ -116,6 +116,15 @@ public void ModuleTests(TestHost host) } Assert.NotNull(module); + if (OS.Kind != OSKind.Windows) + { + // Skip managed modules when running on Linux/OSX because of the 6.0 injection activation issue in the DAC + if (moduleData.TryGetValue("IsManaged", out bool isManaged) && isManaged) + { + continue; + } + } + if (host.Target.Host.HostType != HostType.Lldb) { // Check that the resulting module matches the test data @@ -264,6 +273,11 @@ public void RuntimeTests(TestHost host) { throw new SkipTestException("Not supported on Alpine Linux"); } + // Disable running on Linux/OSX because of the 6.0 injection activation issue in the DAC + if (OS.Kind != OSKind.Windows) + { + throw new SkipTestException("Not supported on Linux"); + } IRuntimeService runtimeService = host.Target.Services.GetService(); Assert.NotNull(runtimeService); diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/TestCommands.cs b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/TestCommands.cs new file mode 100644 index 0000000000..920190d72d --- /dev/null +++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/TestCommands.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Xunit; + +namespace Microsoft.Diagnostics.DebugServices.UnitTests +{ + [Command(Name = "testcommand", Help = "Test command #1")] + public class TestCommand1 : CommandBase + { + public static bool FilterValue; + public static bool Invoked; + + [ServiceImport] + public ITarget Target { get; set; } + + [Argument(Name = "FileName", Help = "Test argument.")] + public string FileName { get; set; } + + public override void Invoke() + { + Assert.NotNull(Target); + Invoked = true; + } + + [FilterInvoke] + public bool FilterInvoke() => FilterValue; + } + + [Command(Name = "testcommand", Help = "Test command #2")] + public class TestCommand2 : CommandBase + { + public static bool FilterValue; + public static bool Invoked; + + [ServiceImport] + public ITarget Target { get; set; } + + [Option(Name = "--foo", Help = "Test option.")] + public int Foo { get; set; } + + public override void Invoke() + { + Assert.NotNull(Target); + Invoked = true; + } + + [FilterInvoke(Message = "Test command #2 filter")] + public bool FilterInvoke() => FilterValue; + } + + [Command(Name = "testcommand", Help = "Test command #3")] + public class TestCommand3 : CommandBase + { + public static bool FilterValue; + public static bool Invoked; + + [ServiceImport] + public ITarget Target { get; set; } + + [Option(Name = "--foo", Help = "Test option.")] + public int Foo { get; set; } + + public override void Invoke() + { + Assert.NotNull(Target); + Invoked = true; + } + + [FilterInvoke] + public static bool FilterInvoke() => FilterValue; + } +} diff --git a/src/tests/TestExtension/TestCommands.cs b/src/tests/TestExtension/TestCommands.cs new file mode 100644 index 0000000000..9e7d6b0a41 --- /dev/null +++ b/src/tests/TestExtension/TestCommands.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Diagnostics.DebugServices; + +namespace TestExtension +{ + [Command(Name = "clrstack", Help = "Test command #1")] + public class TestCommand1 : CommandBase + { + [ServiceImport] + public ITarget Target { get; set; } + + [Argument(Name = "FileName", Help = "Test argument.")] + public string FileName { get; set; } + + public override void Invoke() + { + if (Target is null) + { + throw new ArgumentNullException(nameof(Target)); + } + WriteLine("Test command #1 invoked"); + } + + [FilterInvoke] + public bool FilterInvoke() => true; + } + + [Command(Name = "dumpheap", Help = "Test command #2")] + public class TestCommand2 : CommandBase + { + [ServiceImport] + public ITarget Target { get; set; } + + [Option(Name = "--foo", Help = "Test option.")] + public int Foo { get; set; } + + public override void Invoke() + { + if (Target is null) + { + throw new ArgumentNullException(nameof(Target)); + } + WriteLine("Test command #2 invoked"); + } + + [FilterInvoke] + public bool FilterInvoke() => true; + } + + [Command(Name = "dumpheap", Help = "Test command #3")] + public class TestCommand3 : CommandBase + { + [ServiceImport] + public ITarget Target { get; set; } + + [Option(Name = "--foo", Help = "Test option.")] + public int Foo { get; set; } + + public override void Invoke() + { + if (Target is null) + { + throw new ArgumentNullException(nameof(Target)); + } + WriteLine("Test command #3 invoked"); + } + + [FilterInvoke] + public bool FilterInvoke() => false; + } + + [Command(Name = "assemblies", Help = "Test command #4")] + public class TestCommand4 : CommandBase + { + [ServiceImport] + public ITarget Target { get; set; } + + [Option(Name = "--foo", Help = "Test option.")] + public int Foo { get; set; } + + public override void Invoke() + { + if (Target is null) + { + throw new ArgumentNullException(nameof(Target)); + } + WriteLine("Test command #4 invoked"); + } + + [FilterInvoke] + public static bool FilterInvoke() => true; + } + + [Command(Name = "ip2md", Help = "Test command #5")] + public class TestCommand5 : CommandBase + { + [ServiceImport] + public ITarget Target { get; set; } + + [Option(Name = "--bar", Help = "Test option #5.")] + public int Foo { get; set; } + + public override void Invoke() + { + if (Target is null) + { + throw new ArgumentNullException(nameof(Target)); + } + WriteLine("Test command #5 invoked"); + } + + [FilterInvoke] + public static bool FilterInvoke() => false; + } +} diff --git a/src/tests/TestExtension/TestExtension.csproj b/src/tests/TestExtension/TestExtension.csproj new file mode 100644 index 0000000000..3db8fe9c15 --- /dev/null +++ b/src/tests/TestExtension/TestExtension.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + From 6896e43b7c98f77023a76d0d4d4d10af9f42c270 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:12:05 -0700 Subject: [PATCH 24/82] [main] Update dependencies from microsoft/clrmd (#4287) This pull request updates the following dependencies [marker]: <> (Begin:cb58fe07-ae24-4e73-0e84-08d8e40a189f) ## From https://github.com/microsoft/clrmd - **Subscription**: cb58fe07-ae24-4e73-0e84-08d8e40a189f - **Build**: 20231004.2 - **Date Produced**: October 4, 2023 10:57:18 PM UTC - **Commit**: 10828baeeb7a144414259d9c00a9923c2fcb61e7 - **Branch**: refs/heads/main [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.Diagnostics.Runtime**: [from 3.0.450101 to 3.1.450402][3] - **Microsoft.Diagnostics.Runtime.Utilities**: [from 3.0.450101 to 3.1.450402][3] [3]: https://github.com/microsoft/clrmd/compare/1c8f0f18ea...10828baeeb [DependencyUpdate]: <> (End) [marker]: <> (End:cb58fe07-ae24-4e73-0e84-08d8e40a189f) --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0bd243ed52..56888b99a1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 8cc6f03fdbd9c79f0bf9ffbe0a788dca1a81348a - + https://github.com/microsoft/clrmd - 1c8f0f18ea71856142b99cfd64ec2f25e6294dc3 + 10828baeeb7a144414259d9c00a9923c2fcb61e7 - + https://github.com/microsoft/clrmd - 1c8f0f18ea71856142b99cfd64ec2f25e6294dc3 + 10828baeeb7a144414259d9c00a9923c2fcb61e7 diff --git a/eng/Versions.props b/eng/Versions.props index 8866433d90..83d8e88d63 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.0.450101 + 3.1.450402 16.11.27-beta1.23180.1 3.0.7 6.0.0 From 45ab14f1b083330e5b6c85105d43ac47b909ac82 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:12:16 -0700 Subject: [PATCH 25/82] [main] Update dependencies from dotnet/runtime (#4294) This pull request updates the following dependencies [marker]: <> (Begin:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) ## From https://github.com/dotnet/runtime - **Subscription**: e4bfb556-e13c-47f6-eb5a-08d8e4d5099b - **Build**: 20231004.8 - **Date Produced**: October 4, 2023 10:14:40 PM UTC - **Commit**: 22b8a5665f5725a2c7bb09cfbe88f2cdc9847c1a - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.NETCore.App.Runtime.win-x64**: [from 8.0.0-rtm.23503.15 to 8.0.0-rtm.23504.8][1] - **VS.Redist.Common.NetCore.SharedFramework.x64.8.0**: [from 8.0.0-rtm.23503.15 to 8.0.0-rtm.23504.8][1] [1]: https://github.com/dotnet/runtime/compare/a84f8ffbf5...22b8a5665f [DependencyUpdate]: <> (End) [marker]: <> (End:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 56888b99a1..12f01bf6d9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore 2d2f8808674c3f5fe73ee64ce1d519d5e9e0b659 - + https://github.com/dotnet/runtime - a84f8ffbf5d597b8a91e893a1f413466de017a68 + 22b8a5665f5725a2c7bb09cfbe88f2cdc9847c1a - + https://github.com/dotnet/runtime - a84f8ffbf5d597b8a91e893a1f413466de017a68 + 22b8a5665f5725a2c7bb09cfbe88f2cdc9847c1a https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index 83d8e88d63..dd44104662 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23503.15 - 8.0.0-rtm.23503.15 + 8.0.0-rtm.23504.8 + 8.0.0-rtm.23504.8 8.0.0-rtm.23503.8 8.0.0-rtm.23503.8 From 1b6bcd6113a28e06faf28d861d9189ef8801a70f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:12:36 -0700 Subject: [PATCH 26/82] [main] Update dependencies from dotnet/aspnetcore (#4293) This pull request updates the following dependencies [marker]: <> (Begin:319094f3-ed78-47c4-53e7-08d8e409d87d) ## From https://github.com/dotnet/aspnetcore - **Subscription**: 319094f3-ed78-47c4-53e7-08d8e409d87d - **Build**: 20231004.12 - **Date Produced**: October 4, 2023 8:08:32 PM UTC - **Commit**: 91314b6bde6e9823f848fb37100aea5df4e44c99 - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.AspNetCore.App.Ref**: [from 8.0.0-rtm.23503.8 to 8.0.0-rtm.23504.12][1] - **Microsoft.AspNetCore.App.Ref.Internal**: [from 8.0.0-rtm.23503.8 to 8.0.0-rtm.23504.12][1] [1]: https://github.com/dotnet/aspnetcore/compare/2d2f880867...91314b6bde [DependencyUpdate]: <> (End) [marker]: <> (End:319094f3-ed78-47c4-53e7-08d8e409d87d) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 12f01bf6d9..e37f944488 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 6dae8493a54d682ff64973e67bba2bd1333047a3 - + https://github.com/dotnet/aspnetcore - 2d2f8808674c3f5fe73ee64ce1d519d5e9e0b659 + 91314b6bde6e9823f848fb37100aea5df4e44c99 - + https://github.com/dotnet/aspnetcore - 2d2f8808674c3f5fe73ee64ce1d519d5e9e0b659 + 91314b6bde6e9823f848fb37100aea5df4e44c99 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index dd44104662..5521066718 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23504.8 8.0.0-rtm.23504.8 - 8.0.0-rtm.23503.8 - 8.0.0-rtm.23503.8 + 8.0.0-rtm.23504.12 + 8.0.0-rtm.23504.12 8.0.100-rtm.23479.3 From 74357d8b1becc312e5ec6baee4c06177c1ff43ff Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 5 Oct 2023 16:39:39 -0700 Subject: [PATCH 27/82] Use new ModuleInfo.TryCreateResourceRoot in DataReader impl (#4295) Some test fixes to make them more reliable. --- .../DataReader.cs | 9 ++++++--- .../Microsoft.Diagnostics.TestHelpers.csproj | 1 - .../EventPipeSessionTests.cs | 4 +++- src/tests/eventpipe/ContentionEvents.cs | 4 ++-- src/tests/eventpipe/LoaderEvents.cs | 9 +++++---- src/tests/eventpipe/MethodEvents.cs | 20 ++++++++++--------- src/tests/eventpipe/ThreadPoolEvents.cs | 4 ++-- 7 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs index 86e6120208..826eaecdf1 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/DataReader.cs @@ -48,7 +48,7 @@ public DataReader(ITarget target) int IDataReader.ProcessId => unchecked((int)_target.ProcessId.GetValueOrDefault()); - IEnumerable IDataReader.EnumerateModules() => _modules ??= ModuleService.EnumerateModules().Select((module) => new DataReaderModule(module)).ToList(); + IEnumerable IDataReader.EnumerateModules() => _modules ??= ModuleService.EnumerateModules().Select((module) => new DataReaderModule(this, module)).ToList(); bool IDataReader.GetThreadContext(uint threadId, uint contextFlags, Span context) { @@ -114,11 +114,14 @@ ulong IMemoryReader.ReadPointer(ulong address) private sealed class DataReaderModule : ModuleInfo { + private readonly IDataReader _reader; private readonly IModule _module; + private IResourceNode _resourceRoot; - public DataReaderModule(IModule module) + public DataReaderModule(IDataReader reader, IModule module) : base(module.ImageBase, module.FileName) { + _reader = reader; _module = module; } @@ -202,7 +205,7 @@ public override ulong GetExportSymbolAddress(string symbol) return 0; } - public override IResourceNode ResourceRoot => base.ResourceRoot; + public override IResourceNode ResourceRoot => _resourceRoot ??= ModuleInfo.TryCreateResourceRoot(_reader, _module.ImageBase, _module.ImageSize, _module.IsFileLayout.GetValueOrDefault(false)); } } } diff --git a/src/Microsoft.Diagnostics.TestHelpers/Microsoft.Diagnostics.TestHelpers.csproj b/src/Microsoft.Diagnostics.TestHelpers/Microsoft.Diagnostics.TestHelpers.csproj index d1316d1f77..0057d575f5 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/Microsoft.Diagnostics.TestHelpers.csproj +++ b/src/Microsoft.Diagnostics.TestHelpers/Microsoft.Diagnostics.TestHelpers.csproj @@ -21,6 +21,5 @@ - diff --git a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs index 89d29b8b64..9bfeb9c9ec 100644 --- a/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs +++ b/src/tests/Microsoft.Diagnostics.NETCore.Client/EventPipeSessionTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Tracing; +using System.Threading; using System.Threading.Tasks; using Microsoft.Diagnostics.TestHelpers; using Microsoft.Diagnostics.Tracing; @@ -92,7 +93,7 @@ private async Task EventPipeSessionStreamTestCore(TestConfiguration config, bool EventPipeEventSource source = new(session.EventStream); source.Dynamic.All += (TraceEvent obj) => { runner.WriteLine("Got an event"); - evntCnt += 1; + Interlocked.Increment(ref evntCnt); }; try { @@ -111,6 +112,7 @@ private async Task EventPipeSessionStreamTestCore(TestConfiguration config, bool runner.WriteLine("Waiting for stream Task"); streamTask.Wait(10000); runner.WriteLine("Done waiting for stream Task"); + Assert.True(evntCnt > 0); } } diff --git a/src/tests/eventpipe/ContentionEvents.cs b/src/tests/eventpipe/ContentionEvents.cs index 7aa13645b3..4d5faa1f73 100644 --- a/src/tests/eventpipe/ContentionEvents.cs +++ b/src/tests/eventpipe/ContentionEvents.cs @@ -70,9 +70,9 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => { Func> _DoesTraceContainEvents = (source) => { int ContentionStartEvents = 0; - source.Clr.ContentionStart += (eventData) => ContentionStartEvents += 1; + source.Clr.ContentionStart += (eventData) => Interlocked.Increment(ref ContentionStartEvents); int ContentionStopEvents = 0; - source.Clr.ContentionStop += (eventData) => ContentionStopEvents += 1; + source.Clr.ContentionStop += (eventData) => Interlocked.Increment(ref ContentionStopEvents); return () => { Logger.logger.Log("Event counts validation"); Logger.logger.Log("ContentionStartEvents: " + ContentionStartEvents); diff --git a/src/tests/eventpipe/LoaderEvents.cs b/src/tests/eventpipe/LoaderEvents.cs index 86656eb99b..7297391a7e 100644 --- a/src/tests/eventpipe/LoaderEvents.cs +++ b/src/tests/eventpipe/LoaderEvents.cs @@ -7,6 +7,7 @@ using System.IO; using System.Reflection; using System.Runtime.Loader; +using System.Threading; using EventPipe.UnitTests.Common; using Microsoft.Diagnostics.NETCore.Client; using Microsoft.Diagnostics.Tracing; @@ -79,13 +80,13 @@ void GetAssemblyPath() Func> _DoesTraceContainEvents = (source) => { int LoaderAssemblyLoadEvents = 0; int LoaderAssemblyUnloadEvents = 0; - source.Clr.LoaderAssemblyLoad += (eventData) => LoaderAssemblyLoadEvents += 1; - source.Clr.LoaderAssemblyUnload += (eventData) => LoaderAssemblyUnloadEvents += 1; + source.Clr.LoaderAssemblyLoad += (eventData) => Interlocked.Increment(ref LoaderAssemblyLoadEvents); + source.Clr.LoaderAssemblyUnload += (eventData) => Interlocked.Increment(ref LoaderAssemblyUnloadEvents); int LoaderModuleLoadEvents = 0; int LoaderModuleUnloadEvents = 0; - source.Clr.LoaderModuleLoad += (eventData) => LoaderModuleLoadEvents += 1; - source.Clr.LoaderModuleUnload += (eventData) => LoaderModuleUnloadEvents += 1; + source.Clr.LoaderModuleLoad += (eventData) => Interlocked.Increment(ref LoaderModuleLoadEvents); + source.Clr.LoaderModuleUnload += (eventData) => Interlocked.Increment(ref LoaderModuleUnloadEvents); return () => { Logger.logger.Log("Event counts validation"); diff --git a/src/tests/eventpipe/MethodEvents.cs b/src/tests/eventpipe/MethodEvents.cs index ee47168e4d..7e78fe717c 100644 --- a/src/tests/eventpipe/MethodEvents.cs +++ b/src/tests/eventpipe/MethodEvents.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Tracing; +using System.Threading; using EventPipe.UnitTests.Common; using Microsoft.Diagnostics.NETCore.Client; using Microsoft.Diagnostics.Tracing; @@ -66,21 +67,22 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => { } }; - Func> _DoesTraceContainEvents = (source) => { - int MethodLoadVerboseEvents = 0; - int MethodUnloadVerboseEvents = 0; - source.Clr.MethodLoadVerbose += (eventData) => MethodLoadVerboseEvents += 1; - source.Clr.MethodUnloadVerbose += (eventData) => MethodUnloadVerboseEvents += 1; + int MethodLoadVerboseEvents = 0; + int MethodUnloadVerboseEvents = 0; + int MethodJittingStartedEvents = 0; - int MethodJittingStartedEvents = 0; - source.Clr.MethodJittingStarted += (eventData) => MethodJittingStartedEvents += 1; + Func> _DoesTraceContainEvents = (source) => { + source.Clr.MethodLoadVerbose += (eventData) => Interlocked.Increment(ref MethodUnloadVerboseEvents); + source.Clr.MethodUnloadVerbose += (eventData) => Interlocked.Increment(ref MethodUnloadVerboseEvents); + source.Clr.MethodJittingStarted += (eventData) => Interlocked.Increment(ref MethodJittingStartedEvents); return () => { Logger.logger.Log("Event counts validation"); Logger.logger.Log("MethodLoadVerboseEvents: " + MethodLoadVerboseEvents); Logger.logger.Log("MethodUnloadVerboseEvents: " + MethodUnloadVerboseEvents); - //MethodUnloadVerboseEvents not stable, ignore the verification - bool MethodVerboseResult = MethodLoadVerboseEvents >= 1 && MethodUnloadVerboseEvents >= 0; + //MethodLoadVerboseEvents doesn't seem to ever get incremented, ignore the verification + //bool MethodVerboseResult = MethodLoadVerboseEvents >= 1 && MethodUnloadVerboseEvents >= 1; + bool MethodVerboseResult = MethodUnloadVerboseEvents >= 1; Logger.logger.Log("MethodVerboseResult check: " + MethodVerboseResult); Logger.logger.Log("MethodJittingStartedEvents: " + MethodJittingStartedEvents); diff --git a/src/tests/eventpipe/ThreadPoolEvents.cs b/src/tests/eventpipe/ThreadPoolEvents.cs index 4b97bf06c4..2160984ac9 100644 --- a/src/tests/eventpipe/ThreadPoolEvents.cs +++ b/src/tests/eventpipe/ThreadPoolEvents.cs @@ -64,8 +64,8 @@ void TestTask() int ThreadPoolWorkerThreadAdjustmentSampleEvents = 0; int ThreadPoolWorkerThreadAdjustmentAdjustmentEvents = 0; - source.Clr.ThreadPoolWorkerThreadAdjustmentSample += (eventData) => ThreadPoolWorkerThreadAdjustmentSampleEvents += 1; - source.Clr.ThreadPoolWorkerThreadAdjustmentAdjustment += (eventData) => ThreadPoolWorkerThreadAdjustmentAdjustmentEvents += 1; + source.Clr.ThreadPoolWorkerThreadAdjustmentSample += (eventData) => Interlocked.Increment(ref ThreadPoolWorkerThreadAdjustmentSampleEvents); + source.Clr.ThreadPoolWorkerThreadAdjustmentAdjustment += (eventData) => Interlocked.Increment(ref ThreadPoolWorkerThreadAdjustmentAdjustmentEvents); return () => { Logger.logger.Log("Event counts validation"); From fea2845c25142ba74e85998a15bbce49c531365a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 13:23:01 +0000 Subject: [PATCH 28/82] [main] Update dependencies from dotnet/aspnetcore (#4297) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e37f944488..1938c3c2d5 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 6dae8493a54d682ff64973e67bba2bd1333047a3 - + https://github.com/dotnet/aspnetcore - 91314b6bde6e9823f848fb37100aea5df4e44c99 + 51f80c73e5a9bfb21962db8daa9bb174b9af5942 - + https://github.com/dotnet/aspnetcore - 91314b6bde6e9823f848fb37100aea5df4e44c99 + 51f80c73e5a9bfb21962db8daa9bb174b9af5942 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 5521066718..f12ffaa73f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23504.8 8.0.0-rtm.23504.8 - 8.0.0-rtm.23504.12 - 8.0.0-rtm.23504.12 + 8.0.0-rtm.23505.1 + 8.0.0-rtm.23505.1 8.0.100-rtm.23479.3 From 5f0ad651c995307bdfcba01c05951add1496d039 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:17:34 -0700 Subject: [PATCH 29/82] [main] Update dependencies from dotnet/runtime (#4299) This pull request updates the following dependencies [marker]: <> (Begin:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) ## From https://github.com/dotnet/runtime - **Subscription**: e4bfb556-e13c-47f6-eb5a-08d8e4d5099b - **Build**: 20231006.12 - **Date Produced**: October 7, 2023 3:40:26 AM UTC - **Commit**: b17a34c818bd5e01fdb9827fea64727a5fc51025 - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.NETCore.App.Runtime.win-x64**: [from 8.0.0-rtm.23504.8 to 8.0.0-rtm.23506.12][1] - **VS.Redist.Common.NetCore.SharedFramework.x64.8.0**: [from 8.0.0-rtm.23504.8 to 8.0.0-rtm.23506.12][1] [1]: https://github.com/dotnet/runtime/compare/22b8a5665f...b17a34c818 [DependencyUpdate]: <> (End) [marker]: <> (End:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1938c3c2d5..82011089fa 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore 51f80c73e5a9bfb21962db8daa9bb174b9af5942 - + https://github.com/dotnet/runtime - 22b8a5665f5725a2c7bb09cfbe88f2cdc9847c1a + b17a34c818bd5e01fdb9827fea64727a5fc51025 - + https://github.com/dotnet/runtime - 22b8a5665f5725a2c7bb09cfbe88f2cdc9847c1a + b17a34c818bd5e01fdb9827fea64727a5fc51025 https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index f12ffaa73f..94ec683413 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23504.8 - 8.0.0-rtm.23504.8 + 8.0.0-rtm.23506.12 + 8.0.0-rtm.23506.12 8.0.0-rtm.23505.1 8.0.0-rtm.23505.1 From 7ff20ba07347596e46e1e39219030a9fb3a6fe1d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:17:46 -0700 Subject: [PATCH 30/82] [main] Update dependencies from dotnet/aspnetcore (#4298) This pull request updates the following dependencies [marker]: <> (Begin:319094f3-ed78-47c4-53e7-08d8e409d87d) ## From https://github.com/dotnet/aspnetcore - **Subscription**: 319094f3-ed78-47c4-53e7-08d8e409d87d - **Build**: 20231006.5 - **Date Produced**: October 7, 2023 1:42:18 AM UTC - **Commit**: 6bd37340c54e3b9690102886afca6198a461cb3e - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.AspNetCore.App.Ref**: [from 8.0.0-rtm.23505.1 to 8.0.0-rtm.23506.5][1] - **Microsoft.AspNetCore.App.Ref.Internal**: [from 8.0.0-rtm.23505.1 to 8.0.0-rtm.23506.5][1] [1]: https://github.com/dotnet/aspnetcore/compare/51f80c73e5...6bd37340c5 [DependencyUpdate]: <> (End) [marker]: <> (End:319094f3-ed78-47c4-53e7-08d8e409d87d) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 82011089fa..9444b904b1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 6dae8493a54d682ff64973e67bba2bd1333047a3 - + https://github.com/dotnet/aspnetcore - 51f80c73e5a9bfb21962db8daa9bb174b9af5942 + 6bd37340c54e3b9690102886afca6198a461cb3e - + https://github.com/dotnet/aspnetcore - 51f80c73e5a9bfb21962db8daa9bb174b9af5942 + 6bd37340c54e3b9690102886afca6198a461cb3e https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 94ec683413..ce5e6f2f75 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23506.12 8.0.0-rtm.23506.12 - 8.0.0-rtm.23505.1 - 8.0.0-rtm.23505.1 + 8.0.0-rtm.23506.5 + 8.0.0-rtm.23506.5 8.0.100-rtm.23479.3 From 14bcb465b23221b8e4c9e6ca2a885265d169d13a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:40:37 +0000 Subject: [PATCH 31/82] [main] Update dependencies from dotnet/arcade (#4300) [main] Update dependencies from dotnet/arcade - Fix build from new analysis checks --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- eng/common/sdk-task.ps1 | 2 +- eng/common/tools.ps1 | 6 +++--- global.json | 4 ++-- .../GlobMatcherTests.cs | 4 ++-- src/tests/eventpipe/ThreadPoolEvents.cs | 2 ++ 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9444b904b1..7d86b74268 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,14 +14,14 @@ - + https://github.com/dotnet/arcade - 1d451c32dda2314c721adbf8829e1c0cd4e681ff + 822f095b8c815dd7b9161140a9ff8151de593f82 - + https://github.com/dotnet/arcade - 1d451c32dda2314c721adbf8829e1c0cd4e681ff + 822f095b8c815dd7b9161140a9ff8151de593f82 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index ce5e6f2f75..e97cecda81 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -62,7 +62,7 @@ 6.0.0 6.0.8 2.0.3 - 8.0.0-beta.23463.1 + 9.0.0-beta.23508.1 1.2.0-beta.406 7.0.0-beta.22316.2 10.0.18362 diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index 6c4ac6fec1..91f8196cc8 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.6.0-2" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.7.2-1" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index aa74ab4a81..84cfe7cd9c 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -379,13 +379,13 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } # Minimum VS version to require. - $vsMinVersionReqdStr = '17.6' + $vsMinVersionReqdStr = '17.7' $vsMinVersionReqd = [Version]::new($vsMinVersionReqdStr) # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.6.0-2 - $defaultXCopyMSBuildVersion = '17.6.0-2' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.7.2-1 + $defaultXCopyMSBuildVersion = '17.7.2-1' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { diff --git a/global.json b/global.json index 9b5157cc31..92fde626a4 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "8.0.100-preview.7.23376.3", + "dotnet": "8.0.100-rc.1.23455.8", "runtimes": { "dotnet": [ "$(MicrosoftNETCoreApp60Version)", @@ -16,6 +16,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.5.0", - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23463.1" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23508.1" } } diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/GlobMatcherTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/GlobMatcherTests.cs index 5e013b9776..88317be47b 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/GlobMatcherTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/GlobMatcherTests.cs @@ -50,7 +50,7 @@ public void TestGlobs() { if (!matcher.Match(value)) { - Assert.True(false, $"Expected {value} to match pattern {keyValuePair.Key}"); + Assert.Fail($"Expected {value} to match pattern {keyValuePair.Key}"); }; } @@ -58,7 +58,7 @@ public void TestGlobs() { if (matcher.Match(value)) { - Assert.False(true, $"Expected {value} to not match pattern {keyValuePair.Key}"); + Assert.Fail($"Expected {value} to not match pattern {keyValuePair.Key}"); } } } diff --git a/src/tests/eventpipe/ThreadPoolEvents.cs b/src/tests/eventpipe/ThreadPoolEvents.cs index 2160984ac9..c1a1676196 100644 --- a/src/tests/eventpipe/ThreadPoolEvents.cs +++ b/src/tests/eventpipe/ThreadPoolEvents.cs @@ -50,7 +50,9 @@ await RemoteTestExecutorHelper.RunTestCaseAsync(() => { taskArray[i] = Task.Run(() => TestTask()); } +#pragma warning disable xUnit1031 // Do not use blocking task operations in test method Task.WaitAll(taskArray); +#pragma warning restore xUnit1031 // Do not use blocking task operations in test method }; void TestTask() From f884da88b980cb0cdc5cc6aecd0e3eec64857802 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:29:39 -0700 Subject: [PATCH 32/82] [main] Update dependencies from dotnet/installer (#4301) This pull request updates the following dependencies [marker]: <> (Begin:638f1194-0c1a-4d47-eb59-08d8e4d5099b) ## From https://github.com/dotnet/installer - **Subscription**: 638f1194-0c1a-4d47-eb59-08d8e4d5099b - **Build**: 20231006.1 - **Date Produced**: October 6, 2023 9:36:48 PM UTC - **Commit**: 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - **Branch**: refs/heads/release/8.0.1xx [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.Dotnet.Sdk.Internal**: [from 8.0.100-rtm.23479.3 to 8.0.100-rtm.23506.1][1] [1]: https://github.com/dotnet/installer/compare/6dae8493a5...0ffc9fdc93 [DependencyUpdate]: <> (End) [marker]: <> (End:638f1194-0c1a-4d47-eb59-08d8e4d5099b) --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Mike McLaughlin --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7d86b74268..b7f4bd4b6a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,9 +27,9 @@ https://github.com/dotnet/arcade ccfe6da198c5f05534863bbb1bff66e830e0c6ab - + https://github.com/dotnet/installer - 6dae8493a54d682ff64973e67bba2bd1333047a3 + 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 https://github.com/dotnet/aspnetcore diff --git a/eng/Versions.props b/eng/Versions.props index e97cecda81..b4ad1a9807 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -24,7 +24,7 @@ 8.0.0-rtm.23506.5 8.0.0-rtm.23506.5 - 8.0.100-rtm.23479.3 + 8.0.100-rtm.23506.1 @@ -35,7 +35,7 @@ $(MicrosoftNETCoreApp60Version) $(MicrosoftNETCoreApp70Version) - 8.0.0-rtm.23477.9 + 8.0.0-rtm.23504.8 From f7186f03f7d2d7e5c0720c73adffc50e56053f9b Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 9 Oct 2023 17:43:52 -0700 Subject: [PATCH 33/82] Remove SOS's CRT invalid parameter handler (#4303) SOS does get built dynamically linking to the C++ CRT in release so the invalid parameter handler that gets installed affects all the code in the process. Fixes issue: https://github.com/dotnet/diagnostics/issues/4070 --- .../Debuggees/DesktopClrHost/CMakeLists.txt | 1 - src/SOS/Strike/CMakeLists.txt | 29 +++++++++---------- src/SOS/Strike/exts.cpp | 20 ------------- src/SOS/extensions/CMakeLists.txt | 4 --- src/SOS/runcommand/CMakeLists.txt | 3 -- 5 files changed, 13 insertions(+), 44 deletions(-) diff --git a/src/SOS/SOS.UnitTests/Debuggees/DesktopClrHost/CMakeLists.txt b/src/SOS/SOS.UnitTests/Debuggees/DesktopClrHost/CMakeLists.txt index 3d06e95d2c..d27bd7a780 100644 --- a/src/SOS/SOS.UnitTests/Debuggees/DesktopClrHost/CMakeLists.txt +++ b/src/SOS/SOS.UnitTests/Debuggees/DesktopClrHost/CMakeLists.txt @@ -6,7 +6,6 @@ include_directories(inc) include_directories("$ENV{VSInstallDir}/DIA SDK/include") add_definitions(-DUSE_STL) -add_definitions(-MT) set(DESKTOPCLRHOST_SOURCES DesktopClrHost.cpp diff --git a/src/SOS/Strike/CMakeLists.txt b/src/SOS/Strike/CMakeLists.txt index dcd7994672..d72a5aa6b5 100644 --- a/src/SOS/Strike/CMakeLists.txt +++ b/src/SOS/Strike/CMakeLists.txt @@ -32,9 +32,9 @@ if(CLR_CMAKE_HOST_ARCH_AMD64) add_definitions(-D_TARGET_WIN64_=1) add_definitions(-DDBG_TARGET_64BIT) add_definitions(-DDBG_TARGET_WIN64=1) - if(WIN32) + if (CLR_CMAKE_HOST_WIN32) add_definitions(-DSOS_TARGET_ARM64=1) - endif(WIN32) + endif(CLR_CMAKE_HOST_WIN32) remove_definitions(-D_TARGET_ARM64_=1) add_definitions(-D_TARGET_AMD64_) add_definitions(-DDBG_TARGET_AMD64) @@ -44,9 +44,9 @@ elseif(CLR_CMAKE_HOST_ARCH_I386) add_definitions(-D_TARGET_X86_=1) add_definitions(-DTARGET_X86) add_definitions(-DDBG_TARGET_32BIT) - if(WIN32) + if (CLR_CMAKE_HOST_WIN32) add_definitions(-DSOS_TARGET_ARM=1) - endif(WIN32) + endif(CLR_CMAKE_HOST_WIN32) elseif(CLR_CMAKE_HOST_ARCH_ARM) message(STATUS "CLR_CMAKE_HOST_ARCH_ARM") add_definitions(-DSOS_TARGET_ARM=1) @@ -73,12 +73,9 @@ include_directories(${ROOT_DIR}/src/SOS/extensions) include_directories(${CLR_SHARED_DIR}/gcdump) include_directories(platform) -if(WIN32) +if (CLR_CMAKE_HOST_WIN32) add_definitions(-DUSE_STL) - #use static crt - add_definitions(-MT) - set(SOS_SOURCES disasm.cpp dllsext.cpp @@ -138,7 +135,7 @@ if(WIN32) mscoree.lib) endif(NOT CLR_CMAKE_HOST_ARCH_ARM64 AND NOT CLR_CMAKE_HOST_ARCH_ARM) -else(WIN32) +else(CLR_CMAKE_HOST_WIN32) add_definitions(-DFEATURE_ENABLE_HARDWARE_EXCEPTIONS) add_definitions(-DPAL_STDCPP_COMPAT=1) add_compile_options(-Wno-null-arithmetic) @@ -187,28 +184,28 @@ else(WIN32) coreclrpal ) -endif(WIN32) +endif(CLR_CMAKE_HOST_WIN32) if(CLR_CMAKE_HOST_ARCH_AMD64) set(SOS_SOURCES_ARCH disasmX86.cpp ) - if(WIN32) + if (CLR_CMAKE_HOST_WIN32) list(APPEND SOS_SOURCES_ARCH disasmARM64.cpp ) - endif(WIN32) + endif(CLR_CMAKE_HOST_WIN32) elseif(CLR_CMAKE_HOST_ARCH_I386) set(SOS_SOURCES_ARCH disasmX86.cpp ) - if(WIN32) + if (CLR_CMAKE_HOST_WIN32) list(APPEND SOS_SOURCES_ARCH disasmARM.cpp ) - endif(WIN32) + endif(CLR_CMAKE_HOST_WIN32) elseif(CLR_CMAKE_HOST_ARCH_ARM) set(SOS_SOURCES_ARCH disasmARM.cpp @@ -246,6 +243,6 @@ target_link_libraries(sos ${SOS_LIBRARY}) # add the install targets install_clr(TARGETS sos DESTINATIONS .) -if(NOT WIN32) +if(NOT CLR_CMAKE_HOST_WIN32) install(FILES sosdocsunix.txt DESTINATION .) -endif(NOT WIN32) +endif(NOT CLR_CMAKE_HOST_WIN32) diff --git a/src/SOS/Strike/exts.cpp b/src/SOS/Strike/exts.cpp index 836f38b0d9..112e42bd98 100644 --- a/src/SOS/Strike/exts.cpp +++ b/src/SOS/Strike/exts.cpp @@ -264,20 +264,6 @@ DACMessage(HRESULT Status) BOOL IsMiniDumpFileNODAC(); extern HMODULE g_hInstance; -// This function throws an exception that can be caught by the debugger, -// instead of allowing the default CRT behavior of invoking Watson to failfast. -void __cdecl _SOS_invalid_parameter( - const WCHAR * expression, - const WCHAR * function, - const WCHAR * file, - unsigned int line, - uintptr_t pReserved -) -{ - ExtErr("\nSOS failure!\n"); - throw "SOS failure"; -} - bool g_Initialized = false; const char* g_sosPrefix = ""; @@ -345,12 +331,6 @@ DebugExtensionInitialize(PULONG Version, PULONG Flags) } ExtRelease(); -#ifndef _ARM_ - // Make sure we do not tear down the debugger when a security function fails - // Since we link statically against CRT this will only affect the SOS module. - _set_invalid_parameter_handler(_SOS_invalid_parameter); -#endif - return S_OK; } diff --git a/src/SOS/extensions/CMakeLists.txt b/src/SOS/extensions/CMakeLists.txt index faff991739..bb4f7bf47f 100644 --- a/src/SOS/extensions/CMakeLists.txt +++ b/src/SOS/extensions/CMakeLists.txt @@ -2,10 +2,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include(configure.cmake) -if(WIN32) - add_definitions(-MT) -endif(WIN32) - add_definitions(-DPAL_STDCPP_COMPAT) include_directories(${ROOT_DIR}/src/SOS/inc) diff --git a/src/SOS/runcommand/CMakeLists.txt b/src/SOS/runcommand/CMakeLists.txt index 469d802405..c9b492de8a 100644 --- a/src/SOS/runcommand/CMakeLists.txt +++ b/src/SOS/runcommand/CMakeLists.txt @@ -7,9 +7,6 @@ include_directories("$ENV{VSInstallDir}/DIA SDK/include") add_definitions(-DUSE_STL) -#use static crt -add_definitions(-MT) - set(RUNCOMMAND_SOURCES runcommand.cpp ) From 02f1957ecc02d3908c835911cbe61de72652abd2 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 9 Oct 2023 21:49:51 -0700 Subject: [PATCH 34/82] Re-enable dumpmt tests in OtherCommands.script (#4304) Addresses issue #https://github.com/dotnet/diagnostics/issues/3516 --- src/SOS/SOS.UnitTests/Scripts/OtherCommands.script | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/SOS/SOS.UnitTests/Scripts/OtherCommands.script b/src/SOS/SOS.UnitTests/Scripts/OtherCommands.script index 8274219dcd..b3473d5b36 100644 --- a/src/SOS/SOS.UnitTests/Scripts/OtherCommands.script +++ b/src/SOS/SOS.UnitTests/Scripts/OtherCommands.script @@ -109,18 +109,9 @@ VERIFY:\s*Class Name:\s+SymbolTestApp.Program\s+ VERIFY:\s*File:\s+.*SymbolTestApp\.(dll|exe)\s+ # Verify DumpMT -!IFDEF:MAJOR_RUNTIME_VERSION_GE_7 -# https://github.com/dotnet/diagnostics/issues/3516 SOSCOMMAND:DumpMT \s*Method Table:\s+()\s+ VERIFY:\s*Name:\s+SymbolTestApp.Program\s+ VERIFY:\s*File:\s+.*SymbolTestApp\.(dll|exe)\s+ -ENDIF:MAJOR_RUNTIME_VERSION_GE_7 - -IFDEF:MAJOR_RUNTIME_VERSION_GE_8 -SOSCOMMAND:DumpMT \s*Method Table:\s+()\s+ -VERIFY:\s*Name:\s+SymbolTestApp.Program\s+ -VERIFY:\s*File:\s+.*SymbolTestApp\.(dll|exe)\s+ -ENDIF:MAJOR_RUNTIME_VERSION_GE_8 SOSCOMMAND:FinalizeQueue VERIFY:\s*SyncBlocks to be cleaned up:\s+\s+ From 30c367910d6c835d8eea0bed6188b6237554f2cf Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:20:52 +0000 Subject: [PATCH 35/82] [main] Update dependencies from microsoft/clrmd (#4306) [main] Update dependencies from microsoft/clrmd --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b7f4bd4b6a..f83e48699b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 8cc6f03fdbd9c79f0bf9ffbe0a788dca1a81348a - + https://github.com/microsoft/clrmd - 10828baeeb7a144414259d9c00a9923c2fcb61e7 + 00fee4217d957d9d89caee5ad7414ac779d9f383 - + https://github.com/microsoft/clrmd - 10828baeeb7a144414259d9c00a9923c2fcb61e7 + 00fee4217d957d9d89caee5ad7414ac779d9f383 diff --git a/eng/Versions.props b/eng/Versions.props index b4ad1a9807..a4a2e0a18a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.1.450402 + 3.1.450904 16.11.27-beta1.23180.1 3.0.7 6.0.0 From 622b8137b6b01c1ca4b9d528d8ec39ba5c53ff1e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:25:09 +0000 Subject: [PATCH 36/82] [main] Update dependencies from dotnet/aspnetcore (#4305) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f83e48699b..84a57e1a7b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index a4a2e0a18a..badaa1be90 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23506.12 8.0.0-rtm.23506.12 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 8.0.100-rtm.23506.1 From 25b04195f78bbc15d0f7e453d7655f8c3b7891bd Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:26:51 +0000 Subject: [PATCH 37/82] [main] Update dependencies from dotnet/runtime (#4308) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 84a57e1a7b..2b126fd39c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/runtime - b17a34c818bd5e01fdb9827fea64727a5fc51025 + a9cc3c80fe43d19a38cacda4c1aecc51fb6eabb1 - + https://github.com/dotnet/runtime - b17a34c818bd5e01fdb9827fea64727a5fc51025 + a9cc3c80fe43d19a38cacda4c1aecc51fb6eabb1 https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index badaa1be90..f3c6e0add2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.446801 - 8.0.0-rtm.23506.12 - 8.0.0-rtm.23506.12 + 8.0.0-rtm.23509.5 + 8.0.0-rtm.23509.5 8.0.0-rtm.23509.3 8.0.0-rtm.23509.3 From bd5dd2264494255fb25d3e15edd9d2b6232909b6 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:37:26 -0700 Subject: [PATCH 38/82] [main] Update dependencies from dotnet/symstore (#4307) This pull request updates the following dependencies [marker]: <> (Begin:678f7c5b-6647-4e77-0d75-08d8e40a4c7c) ## From https://github.com/dotnet/symstore - **Subscription**: 678f7c5b-6647-4e77-0d75-08d8e40a4c7c - **Build**: 20231009.1 - **Date Produced**: October 9, 2023 4:51:26 PM UTC - **Commit**: a3b341f9e61c8d8e832c4acfeb5b3a2305e51bcc - **Branch**: refs/heads/main [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.SymbolStore**: [from 1.0.446801 to 1.0.450901][1] [1]: https://github.com/dotnet/symstore/compare/8cc6f03fdb...a3b341f9e6 [DependencyUpdate]: <> (End) [marker]: <> (End:678f7c5b-6647-4e77-0d75-08d8e40a4c7c) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b126fd39c..e8721df2ce 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/symstore - 8cc6f03fdbd9c79f0bf9ffbe0a788dca1a81348a + a3b341f9e61c8d8e832c4acfeb5b3a2305e51bcc https://github.com/microsoft/clrmd diff --git a/eng/Versions.props b/eng/Versions.props index f3c6e0add2..bbc3da60b4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,7 +16,7 @@ - 1.0.446801 + 1.0.450901 8.0.0-rtm.23509.5 8.0.0-rtm.23509.5 From 3557d580619793cde2d3e76075b9019e3bce264c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:38:43 -0700 Subject: [PATCH 39/82] [main] Update dependencies from dotnet/source-build-reference-packages (#4309) This pull request updates the following dependencies [marker]: <> (Begin:8fefa124-13dd-4c66-7dae-08d9c02d7834) ## From https://github.com/dotnet/source-build-reference-packages - **Subscription**: 8fefa124-13dd-4c66-7dae-08d9c02d7834 - **Build**: 20231009.2 - **Date Produced**: October 9, 2023 1:15:04 PM UTC - **Commit**: 8a2f652b1f23d493fcce31b73e829de56df38d5f - **Branch**: refs/heads/main [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.SourceBuild.Intermediate.source-build-reference-packages**: [from 9.0.0-alpha.1.23502.1 to 9.0.0-alpha.1.23509.2][1] [1]: https://github.com/dotnet/source-build-reference-packages/compare/05ffbf9df6...8a2f652b1f [DependencyUpdate]: <> (End) [marker]: <> (End:8fefa124-13dd-4c66-7dae-08d9c02d7834) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e8721df2ce..cd70ea1001 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -47,9 +47,9 @@ https://github.com/dotnet/runtime a9cc3c80fe43d19a38cacda4c1aecc51fb6eabb1 - + https://github.com/dotnet/source-build-reference-packages - 05ffbf9df6c1dc621665ee1864874c4fe6de874c + 8a2f652b1f23d493fcce31b73e829de56df38d5f diff --git a/eng/Versions.props b/eng/Versions.props index bbc3da60b4..8f04c22d0f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -67,7 +67,7 @@ 7.0.0-beta.22316.2 10.0.18362 13.0.1 - 9.0.0-alpha.1.23502.1 + 9.0.0-alpha.1.23509.2 3.11.0 From 3122b0322217bb25f7063383e36c66f51baf01f1 Mon Sep 17 00:00:00 2001 From: Maoni Stephens Date: Tue, 10 Oct 2023 19:11:08 -0700 Subject: [PATCH 40/82] fix doc for fq -allReady (#4310) fixing some incorrect info for the explaination of the -allReady arg for the !fq cmd. --- src/SOS/Strike/sosdocs.txt | 14 ++++++++++---- src/SOS/Strike/sosdocsunix.txt | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/SOS/Strike/sosdocs.txt b/src/SOS/Strike/sosdocs.txt index 80ec24642f..0d424296be 100644 --- a/src/SOS/Strike/sosdocs.txt +++ b/src/SOS/Strike/sosdocs.txt @@ -682,10 +682,16 @@ The arguments in detail: -allReady Specifying this argument will allow for the display of all objects that are ready for finalization, whether they are already marked by - the GC as such, or whether the next GC will. The objects that are - not in the "Ready for finalization" list are finalizable objects that - are no longer rooted. This option can be very expensive, as it - verifies whether all the objects in the finalizable queues are still + the GC as such or not. The former means GC already put them in the + "Ready for finalization" list and their finalizers are ready to run + but haven't run yet. The latter means there is nothing holding onto + these objects but GC hasn't noticed it yet because a GC that collects + the generation this object lives in has not happened yet. When that + GC happens, this object will be moved to the "Ready for finalization" + list. For example, if a finalizable object lives in gen2 and a gen2 GC + has not happened, even if it's displayed by -allReady it's not actually + ready for finalization. This option can be very expensive, as it + verifies whether all the objects in the finalizable queues are still rooted or not. -short Limits the output to just the address of each object. If used in conjunction with -allReady it enumerates all objects that have a diff --git a/src/SOS/Strike/sosdocsunix.txt b/src/SOS/Strike/sosdocsunix.txt index b554a9479a..d8cb6c2116 100644 --- a/src/SOS/Strike/sosdocsunix.txt +++ b/src/SOS/Strike/sosdocsunix.txt @@ -547,10 +547,16 @@ The arguments in detail: -allReady Specifying this argument will allow for the display of all objects that are ready for finalization, whether they are already marked by - the GC as such, or whether the next GC will. The objects that are - not in the "Ready for finalization" list are finalizable objects that - are no longer rooted. This option can be very expensive, as it - verifies whether all the objects in the finalizable queues are still + the GC as such or not. The former means GC already put them in the + "Ready for finalization" list and their finalizers are ready to run + but haven't run yet. The latter means there is nothing holding onto + these objects but GC hasn't noticed it yet because a GC that collects + the generation this object lives in has not happened yet. When that + GC happens, this object will be moved to the "Ready for finalization" + list. For example, if a finalizable object lives in gen2 and a gen2 GC + has not happened, even if it's displayed by -allReady it's not actually + ready for finalization. This option can be very expensive, as it + verifies whether all the objects in the finalizable queues are still rooted or not. -short Limits the output to just the address of each object. If used in conjunction with -allReady it enumerates all objects that have a From 21f1504886cd61a304808980de3ece59eff68e81 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:23:39 +0000 Subject: [PATCH 41/82] [main] Update dependencies from microsoft/clrmd (#4312) [main] Update dependencies from microsoft/clrmd --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index cd70ea1001..4dbc5cd726 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore a3b341f9e61c8d8e832c4acfeb5b3a2305e51bcc - + https://github.com/microsoft/clrmd - 00fee4217d957d9d89caee5ad7414ac779d9f383 + 903207ffe9dbac775a2a70d54980fc03abad4cb1 - + https://github.com/microsoft/clrmd - 00fee4217d957d9d89caee5ad7414ac779d9f383 + 903207ffe9dbac775a2a70d54980fc03abad4cb1 diff --git a/eng/Versions.props b/eng/Versions.props index 8f04c22d0f..a2471d4a16 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.1.450904 + 3.1.451001 16.11.27-beta1.23180.1 3.0.7 6.0.0 From 6bc2f7ab37da107abda94ec839f37fae2b1eadb5 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:28:01 +0000 Subject: [PATCH 42/82] [main] Update dependencies from dotnet/aspnetcore (#4311) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4dbc5cd726..2d1e159d53 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - + https://github.com/dotnet/aspnetcore - cfc7ac66d89f8e38def01cc19c6d15f4c010c630 + 7ffeb436ad029d1e1012372b7bb345ad22770f09 - + https://github.com/dotnet/aspnetcore - cfc7ac66d89f8e38def01cc19c6d15f4c010c630 + 7ffeb436ad029d1e1012372b7bb345ad22770f09 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index a2471d4a16..820eb77528 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23509.5 8.0.0-rtm.23509.5 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 8.0.100-rtm.23506.1 From 1b012b188d241a184338e52637baa529c3305a47 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:34:58 +0000 Subject: [PATCH 43/82] [main] Update dependencies from dotnet/source-build-reference-packages (#4313) [main] Update dependencies from dotnet/source-build-reference-packages --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2d1e159d53..45ac8876e1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -47,9 +47,9 @@ https://github.com/dotnet/runtime a9cc3c80fe43d19a38cacda4c1aecc51fb6eabb1 - + https://github.com/dotnet/source-build-reference-packages - 8a2f652b1f23d493fcce31b73e829de56df38d5f + 5d89368fe132c3f6210d661e18087db782b74f2d diff --git a/eng/Versions.props b/eng/Versions.props index 820eb77528..84939e6d25 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -67,7 +67,7 @@ 7.0.0-beta.22316.2 10.0.18362 13.0.1 - 9.0.0-alpha.1.23509.2 + 9.0.0-alpha.1.23510.3 3.11.0 From 289a46a2c9fc87b977217d769b0aed45e2b37bcf Mon Sep 17 00:00:00 2001 From: kkeirstead <85592574+kkeirstead@users.noreply.github.com> Date: Wed, 11 Oct 2023 07:10:55 -0700 Subject: [PATCH 44/82] Dotnet Counters + Dotnet Monitor Unification - Automated `Collect` Testing (#4253) --- .../CommonTestRunner/TestRunnerUtilities.cs | 59 +++ src/tests/EventPipeTracee/CustomMetrics.cs | 38 ++ .../EventPipeTracee/EventPipeTracee.csproj | 6 +- src/tests/EventPipeTracee/Program.cs | 31 ++ .../PipelineTestUtilities.cs | 24 +- src/tests/dotnet-counters/CSVExporterTests.cs | 46 +-- .../CounterMonitorPayloadTests.cs | 336 ++++++++++++++++++ .../DotnetCounters.UnitTests.csproj | 2 + src/tests/dotnet-counters/TestConstants.cs | 25 ++ 9 files changed, 516 insertions(+), 51 deletions(-) create mode 100644 src/tests/CommonTestRunner/TestRunnerUtilities.cs create mode 100644 src/tests/EventPipeTracee/CustomMetrics.cs create mode 100644 src/tests/dotnet-counters/CounterMonitorPayloadTests.cs create mode 100644 src/tests/dotnet-counters/TestConstants.cs diff --git a/src/tests/CommonTestRunner/TestRunnerUtilities.cs b/src/tests/CommonTestRunner/TestRunnerUtilities.cs new file mode 100644 index 0000000000..7cbc88b264 --- /dev/null +++ b/src/tests/CommonTestRunner/TestRunnerUtilities.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Diagnostics.TestHelpers; +using Xunit.Abstractions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; + +namespace CommonTestRunner +{ + public static class TestRunnerUtilities + { + public static async Task StartProcess(TestConfiguration config, string testArguments, ITestOutputHelper outputHelper, int testProcessTimeout = 60_000) + { + TestRunner runner = await TestRunner.Create(config, outputHelper, "EventPipeTracee", testArguments).ConfigureAwait(true); + await runner.Start(testProcessTimeout).ConfigureAwait(true); + return runner; + } + + public static async Task ExecuteCollection( + Func executeCollection, + TestRunner testRunner, + CancellationToken token) + { + Task collectionTask = executeCollection(token); + await ExecuteCollection(collectionTask, testRunner, token).ConfigureAwait(false); + } + + public static async Task ExecuteCollection( + Task collectionTask, + TestRunner testRunner, + CancellationToken token, + Func waitForPipeline = null) + { + // Begin event production + testRunner.WakeupTracee(); + + // Wait for event production to be done + testRunner.WaitForSignal(); + + try + { + if (waitForPipeline != null) + { + await waitForPipeline(token).ConfigureAwait(false); + } + + await collectionTask.ConfigureAwait(true); + } + finally + { + // Signal for debuggee that it's ok to end/move on. + testRunner.WakeupTracee(); + } + } + } +} diff --git a/src/tests/EventPipeTracee/CustomMetrics.cs b/src/tests/EventPipeTracee/CustomMetrics.cs new file mode 100644 index 0000000000..d16b036f8a --- /dev/null +++ b/src/tests/EventPipeTracee/CustomMetrics.cs @@ -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. + +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using Constants = DotnetCounters.UnitTests.TestConstants; + +namespace EventPipeTracee +{ + internal sealed class CustomMetrics : IDisposable + { + private Meter _meter; + private Counter _counter; + private Histogram _histogram; + + public CustomMetrics() + { + _meter = new(Constants.TestMeterName); + _counter = _meter.CreateCounter(Constants.TestCounter, "dollars"); + _histogram = _meter.CreateHistogram(Constants.TestHistogram, "feet"); + // consider adding gauge/etc. here + } + + public void IncrementCounter(int v = 1) + { + _counter.Add(v); + } + + public void RecordHistogram(float v = 10.0f) + { + KeyValuePair tags = new(Constants.TagKey, Constants.TagValue); + _histogram.Record(v, tags); + } + + public void Dispose() => _meter?.Dispose(); + } +} diff --git a/src/tests/EventPipeTracee/EventPipeTracee.csproj b/src/tests/EventPipeTracee/EventPipeTracee.csproj index b94bb2b3cd..f61b62ceda 100644 --- a/src/tests/EventPipeTracee/EventPipeTracee.csproj +++ b/src/tests/EventPipeTracee/EventPipeTracee.csproj @@ -1,10 +1,14 @@ - + Exe $(BuildProjectFramework) net6.0;net7.0;net8.0 + + + + diff --git a/src/tests/EventPipeTracee/Program.cs b/src/tests/EventPipeTracee/Program.cs index 0ede793dff..0d379b97d7 100644 --- a/src/tests/EventPipeTracee/Program.cs +++ b/src/tests/EventPipeTracee/Program.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using System.IO.Pipes; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -29,6 +31,10 @@ public static int Main(string[] args) bool spinWait10 = args.Length > 2 && "SpinWait10".Equals(args[2], StringComparison.Ordinal); string loggerCategory = args[1]; + bool diagMetrics = args.Any("DiagMetrics".Equals); + + Console.WriteLine($"{pid} EventPipeTracee: DiagMetrics {diagMetrics}"); + Console.WriteLine($"{pid} EventPipeTracee: start process"); Console.Out.Flush(); @@ -54,12 +60,35 @@ public static int Main(string[] args) Console.WriteLine($"{pid} EventPipeTracee: {DateTime.UtcNow} Awaiting start"); Console.Out.Flush(); + using CustomMetrics metrics = diagMetrics ? new CustomMetrics() : null; + // Wait for server to send something int input = pipeStream.ReadByte(); Console.WriteLine($"{pid} {DateTime.UtcNow} Starting test body '{input}'"); Console.Out.Flush(); + CancellationTokenSource recordMetricsCancellationTokenSource = new(); + + if (diagMetrics) + { + _ = Task.Run(async () => { + + // Recording a single value appeared to cause test flakiness due to a race + // condition with the timing of when dotnet-counters starts collecting and + // when these values are published. Publishing values repeatedly bypasses this problem. + while (!recordMetricsCancellationTokenSource.Token.IsCancellationRequested) + { + recordMetricsCancellationTokenSource.Token.ThrowIfCancellationRequested(); + + metrics.IncrementCounter(); + metrics.RecordHistogram(10.0f); + await Task.Delay(1000).ConfigureAwait(true); + } + + }).ConfigureAwait(true); + } + TestBodyCore(customCategoryLogger, appCategoryLogger); Console.WriteLine($"{pid} EventPipeTracee: signal end of test data"); @@ -87,6 +116,8 @@ public static int Main(string[] args) // Wait for server to send something input = pipeStream.ReadByte(); + recordMetricsCancellationTokenSource.Cancel(); + Console.WriteLine($"{pid} EventPipeTracee {DateTime.UtcNow} Ending remote test process '{input}'"); return 0; } diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs index 70f72ecb49..ffd52880e4 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/PipelineTestUtilities.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using CommonTestRunner; using Microsoft.Diagnostics.TestHelpers; using Xunit.Abstractions; using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; @@ -16,9 +17,7 @@ internal static class PipelineTestUtilities public static async Task StartProcess(TestConfiguration config, string testArguments, ITestOutputHelper outputHelper, int testProcessTimeout = 60_000) { - TestRunner runner = await TestRunner.Create(config, outputHelper, "EventPipeTracee", testArguments); - await runner.Start(testProcessTimeout); - return runner; + return await TestRunnerUtilities.StartProcess(config, testArguments, outputHelper, testProcessTimeout); } public static async Task ExecutePipelineWithTracee( @@ -75,14 +74,7 @@ private static async Task ExecutePipelineWithTracee( { Task runTask = await startPipelineAsync(pipeline, token); - // Begin event production - testRunner.WakeupTracee(); - - // Wait for event production to be done - testRunner.WaitForSignal(); - - try - { + Func waitForPipeline = async (cancellationToken) => { // Optionally wait on caller before allowing the pipeline to stop. if (null != waitTaskSource) { @@ -96,15 +88,9 @@ private static async Task ExecutePipelineWithTracee( //Signal for the pipeline to stop await pipeline.StopAsync(token); + }; - //After a pipeline is stopped, we should expect the RunTask to eventually finish - await runTask; - } - finally - { - // Signal for debugee that's ok to end/move on. - testRunner.WakeupTracee(); - } + await TestRunnerUtilities.ExecuteCollection(runTask, testRunner, token, waitForPipeline); } } } diff --git a/src/tests/dotnet-counters/CSVExporterTests.cs b/src/tests/dotnet-counters/CSVExporterTests.cs index fca3d71c75..7c283ff4e3 100644 --- a/src/tests/dotnet-counters/CSVExporterTests.cs +++ b/src/tests/dotnet-counters/CSVExporterTests.cs @@ -36,11 +36,7 @@ public void IncrementingCounterTest() List lines = File.ReadLines(fileName).ToList(); Assert.Equal(101, lines.Count); // should be 101 including the headers - string[] headerTokens = lines[0].Split(','); - Assert.Equal("Provider", headerTokens[1]); - Assert.Equal("Counter Name", headerTokens[2]); - Assert.Equal("Counter Type", headerTokens[3]); - Assert.Equal("Mean/Increment", headerTokens[4]); + ValidateHeaderTokens(lines[0]); for (int i = 1; i < lines.Count; i++) { @@ -78,12 +74,7 @@ public void CounterTest() List lines = File.ReadLines(fileName).ToList(); Assert.Equal(11, lines.Count); // should be 11 including the headers - string[] headerTokens = lines[0].Split(','); - Assert.Equal("Provider", headerTokens[1]); - Assert.Equal("Counter Name", headerTokens[2]); - Assert.Equal("Counter Type", headerTokens[3]); - Assert.Equal("Mean/Increment", headerTokens[4]); - + ValidateHeaderTokens(lines[0]); for (int i = 1; i < lines.Count; i++) { @@ -121,11 +112,7 @@ public void DifferentDisplayRateTest() List lines = File.ReadLines(fileName).ToList(); Assert.Equal(101, lines.Count); // should be 101 including the headers - string[] headerTokens = lines[0].Split(','); - Assert.Equal("Provider", headerTokens[1]); - Assert.Equal("Counter Name", headerTokens[2]); - Assert.Equal("Counter Type", headerTokens[3]); - Assert.Equal("Mean/Increment", headerTokens[4]); + ValidateHeaderTokens(lines[0]); for (int i = 1; i < lines.Count; i++) { @@ -163,11 +150,7 @@ public void DisplayUnitsTest() List lines = File.ReadLines(fileName).ToList(); Assert.Equal(101, lines.Count); // should be 101 including the headers - string[] headerTokens = lines[0].Split(','); - Assert.Equal("Provider", headerTokens[1]); - Assert.Equal("Counter Name", headerTokens[2]); - Assert.Equal("Counter Type", headerTokens[3]); - Assert.Equal("Mean/Increment", headerTokens[4]); + ValidateHeaderTokens(lines[0]); for (int i = 1; i < lines.Count; i++) { @@ -205,11 +188,7 @@ public void TagsTest() List lines = File.ReadLines(fileName).ToList(); Assert.Equal(101, lines.Count); // should be 101 including the headers - string[] headerTokens = lines[0].Split(','); - Assert.Equal("Provider", headerTokens[1]); - Assert.Equal("Counter Name", headerTokens[2]); - Assert.Equal("Counter Type", headerTokens[3]); - Assert.Equal("Mean/Increment", headerTokens[4]); + ValidateHeaderTokens(lines[0]); for (int i = 1; i < lines.Count; i++) { @@ -247,11 +226,7 @@ public void PercentilesTest() List lines = File.ReadLines(fileName).ToList(); Assert.Equal(101, lines.Count); // should be 101 including the headers - string[] headerTokens = lines[0].Split(','); - Assert.Equal("Provider", headerTokens[1]); - Assert.Equal("Counter Name", headerTokens[2]); - Assert.Equal("Counter Type", headerTokens[3]); - Assert.Equal("Mean/Increment", headerTokens[4]); + ValidateHeaderTokens(lines[0]); for (int i = 1; i < lines.Count; i++) { @@ -268,5 +243,14 @@ public void PercentilesTest() File.Delete(fileName); } } + + internal static void ValidateHeaderTokens(string headerLine) + { + string[] headerTokens = headerLine.Split(','); + Assert.Equal("Provider", headerTokens[TestConstants.ProviderIndex]); + Assert.Equal("Counter Name", headerTokens[TestConstants.CounterNameIndex]); + Assert.Equal("Counter Type", headerTokens[TestConstants.CounterTypeIndex]); + Assert.Equal("Mean/Increment", headerTokens[TestConstants.ValueIndex]); + } } } diff --git a/src/tests/dotnet-counters/CounterMonitorPayloadTests.cs b/src/tests/dotnet-counters/CounterMonitorPayloadTests.cs new file mode 100644 index 0000000000..b2c38cbd4c --- /dev/null +++ b/src/tests/dotnet-counters/CounterMonitorPayloadTests.cs @@ -0,0 +1,336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.IO; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using CommonTestRunner; +using Microsoft.Diagnostics.TestHelpers; +using Microsoft.Diagnostics.Tools.Counters; +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using TestRunner = Microsoft.Diagnostics.CommonTestRunner.TestRunner; +using Constants = DotnetCounters.UnitTests.TestConstants; + +namespace DotnetCounters.UnitTests +{ + /// + /// Tests the behavior of CounterMonitor's Collect command. + /// + public class CounterMonitorPayloadTests + { + private enum CounterTypes + { + Metric, Rate + } + + private ITestOutputHelper _outputHelper; + private static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(2); + private static readonly string SystemRuntimeName = "System.Runtime"; + private static readonly string TagStart = "["; + + private static HashSet ExpectedCounterTypes = new() { CounterTypes.Metric, CounterTypes.Rate }; + + public CounterMonitorPayloadTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + } + + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestCounterMonitorCustomMetricsJSON(TestConfiguration configuration) + { + CheckRuntimeOS(); + CheckFramework(configuration); + + List metricComponents = await GetCounterTraceJSON(configuration, new List { Constants.TestMeterName }); + + ValidateCustomMetrics(metricComponents, CountersExportFormat.json); + } + + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestCounterMonitorCustomMetricsCSV(TestConfiguration configuration) + { + CheckRuntimeOS(); + CheckFramework(configuration); + + List metricComponents = await GetCounterTraceCSV(configuration, new List { Constants.TestMeterName }); + + ValidateCustomMetrics(metricComponents, CountersExportFormat.csv); + } + + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestCounterMonitorSystemRuntimeMetricsJSON(TestConfiguration configuration) + { + CheckRuntimeOS(); + + List metricComponents = await GetCounterTraceJSON(configuration, new List { SystemRuntimeName }); + + ValidateSystemRuntimeMetrics(metricComponents); + } + + [SkippableTheory, MemberData(nameof(Configurations))] + public async Task TestCounterMonitorSystemRuntimeMetricsCSV(TestConfiguration configuration) + { + CheckRuntimeOS(); + + List metricComponents = await GetCounterTraceCSV(configuration, new List { SystemRuntimeName }); + + ValidateSystemRuntimeMetrics(metricComponents); + } + + private void ValidateSystemRuntimeMetrics(List metricComponents) + { + string[] ExpectedProviders = { "System.Runtime" }; + Assert.Equal(ExpectedProviders, metricComponents.Select(c => c.ProviderName).ToHashSet()); + + // Could also just check the number of counter names + HashSet expectedCounterNames = new() + { + "CPU Usage (%)", + "Working Set (MB)", + "GC Heap Size (MB)", + "Gen 0 GC Count (Count / 1 sec)", + "Gen 1 GC Count (Count / 1 sec)", + "Gen 2 GC Count (Count / 1 sec)", + "ThreadPool Thread Count", + "Monitor Lock Contention Count (Count / 1 sec)", + "ThreadPool Queue Length", + "ThreadPool Completed Work Item Count (Count / 1 sec)", + "Allocation Rate (B / 1 sec)", + "Number of Active Timers", + "GC Fragmentation (%)", + "GC Committed Bytes (MB)", + "Exception Count (Count / 1 sec)", + "% Time in GC since last GC (%)", + "Gen 0 Size (B)", + "Gen 1 Size (B)", + "Gen 2 Size (B)", + "LOH Size (B)", + "POH (Pinned Object Heap) Size (B)", + "Number of Assemblies Loaded", + "IL Bytes Jitted (B)", + "Number of Methods Jitted", + "Time spent in JIT (ms / 1 sec)" + }; + + Assert.Subset(metricComponents.Select(c => c.CounterName).ToHashSet(), expectedCounterNames); + + Assert.Equal(ExpectedCounterTypes, metricComponents.Select(c => c.CounterType).ToHashSet()); + } + + private async Task> GetCounterTraceJSON(TestConfiguration configuration, List counterList) + { + string path = Path.ChangeExtension(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()), "json"); + + Func> createMetricComponents = () => + { + using FileStream metricsFile = File.OpenRead(path); + JSONCounterTrace trace = JsonSerializer.Deserialize(metricsFile, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + var providers = trace.events.Select(e => e.provider).ToList(); + var counterNames = trace.events.Select(e => e.name).ToList(); + var counterTypes = trace.events.Select(e => e.counterType).ToList(); + var tags = trace.events.Select(e => e.tags).ToList(); + var values = trace.events.Select(e => e.value).ToList(); + + return CreateMetricComponents(providers, counterNames, counterTypes, tags, values); + }; + + return await GetCounterTrace(configuration, counterList, path, CountersExportFormat.json, createMetricComponents); + } + + private async Task> GetCounterTraceCSV(TestConfiguration configuration, List counterList) + { + string path = Path.ChangeExtension(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()), "csv"); + + Func> createMetricComponents = () => + { + List lines = File.ReadLines(path).ToList(); + CSVExporterTests.ValidateHeaderTokens(lines[0]); + lines.RemoveAt(0); // Trim the header + + IEnumerable splitLines = lines.Select(l => l.Split(",")); + + var providers = splitLines.Select(line => line[Constants.ProviderIndex]).ToList(); + var countersList = splitLines.Select(line => line[Constants.CounterNameIndex]).ToList(); + var counterNames = countersList.Select(counter => counter.Split(TagStart)[0]).ToList(); + var counterTypes = splitLines.Select(line => line[Constants.CounterTypeIndex]).ToList(); + var tags = GetCSVTags(countersList); + var values = GetCSVValues(splitLines); + + return CreateMetricComponents(providers, counterNames, counterTypes, tags, values); + }; + + return await GetCounterTrace(configuration, counterList, path, CountersExportFormat.csv, createMetricComponents); + } + + private List CreateMetricComponents(List providers, List counterNames, List counterTypes, List tags, List values) + { + List metricComponents = new(providers.Count()); + + for (int index = 0; index < providers.Count(); ++index) + { + CounterTypes type; + Enum.TryParse(counterTypes[index], out type); + + metricComponents.Add(new MetricComponents() + { + ProviderName = providers[index], + CounterName = counterNames[index], + CounterType = type, + Tags = tags[index], + Value = values[index] + }); + } + + return metricComponents; + } + + private async Task> GetCounterTrace(TestConfiguration configuration, List counterList, string path, CountersExportFormat exportFormat, Func> CreateMetricComponents) + { + try + { + CounterMonitor monitor = new CounterMonitor(); + + using CancellationTokenSource source = new CancellationTokenSource(DefaultTimeout); + + await using var testRunner = await TestRunnerUtilities.StartProcess(configuration, "TestCounterMonitor DiagMetrics", _outputHelper); + + await TestRunnerUtilities.ExecuteCollection((ct) => { + return Task.Run(async () => + await monitor.Collect( + ct: ct, + counter_list: counterList, + counters: null, + console: new TestConsole(), + processId: testRunner.Pid, + refreshInterval: 1, + format: exportFormat, + output: path, + name: null, + diagnosticPort: null, + resumeRuntime: false, + maxHistograms: 10, + maxTimeSeries: 1000, + duration: TimeSpan.FromSeconds(10))); + }, testRunner, source.Token); + + return CreateMetricComponents(); + } + finally + { + try + { + File.Delete(path); + } + catch { } + } + } + + private void ValidateCustomMetrics(List metricComponents, CountersExportFormat format) + { + // Currently not validating timestamp due to https://github.com/dotnet/diagnostics/issues/3905 + + HashSet expectedProviders = new() { Constants.TestMeterName }; + Assert.Equal(expectedProviders, metricComponents.Select(c => c.ProviderName).ToHashSet()); + + HashSet expectedCounterNames = new() { Constants.TestHistogramName, Constants.TestCounterName }; + Assert.Equal(expectedCounterNames, metricComponents.Select(c => c.CounterName).ToHashSet()); + + Assert.Equal(ExpectedCounterTypes, metricComponents.Select(c => c.CounterType).ToHashSet()); + + string tagSeparator = format == CountersExportFormat.csv ? ";" : ","; + string tag = Constants.TagKey + "=" + Constants.TagValue + tagSeparator + Constants.PercentileKey + "="; + HashSet expectedTags = new() { $"{tag}{Constants.Quantile50}", $"{tag}{Constants.Quantile95}", $"{tag}{Constants.Quantile99}" }; + Assert.Equal(expectedTags, metricComponents.Where(c => c.CounterName == Constants.TestHistogramName).Select(c => c.Tags).Distinct()); + Assert.Empty(metricComponents.Where(c => c.CounterName == Constants.TestCounterName).Where(c => c.Tags != string.Empty)); + + var actualCounterValues = metricComponents.Where(c => c.CounterName == Constants.TestCounterName).Select(c => c.Value); + + Assert.NotEmpty(actualCounterValues); + double histogramValue = Assert.Single(metricComponents.Where(c => c.CounterName == Constants.TestHistogramName).Select(c => c.Value).Distinct()); + Assert.Equal(10, histogramValue); + } + + private List GetCSVTags(List countersList) + { + var tags = countersList.Select(counter => { + var split = counter.Split(TagStart); + return split.Length > 1 ? split[1].Remove(split[1].Length - 1) : string.Empty; + }).ToList(); + + return tags; + } + + private List GetCSVValues(IEnumerable splitLines) + { + return splitLines.Select(line => { + return double.TryParse(line[Constants.ValueIndex], out double val) ? val : -1; + }).ToList(); + } + + private void CheckFramework(TestConfiguration configuration) + { + if (configuration.RuntimeFrameworkVersionMajor < 8) + { + throw new SkipTestException("Not supported on < .NET 8.0"); + } + } + + private void CheckRuntimeOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + throw new SkipTestException("Test instability on OSX"); + } + } + + public static IEnumerable Configurations => TestRunner.Configurations; + + private sealed class MetricComponents + { + public string ProviderName { get; set; } + public string CounterName { get; set; } + public double Value { get; set; } + public string Tags { get; set; } + public CounterTypes CounterType { get; set; } + } + + private sealed class TestConsole : IConsole + { + private readonly TestStandardStreamWriter _outWriter; + private readonly TestStandardStreamWriter _errorWriter; + + private sealed class TestStandardStreamWriter : IStandardStreamWriter + { + private StringWriter _writer = new(); + public void Write(string value) => _writer.Write(value); + public void WriteLine(string value) => _writer.WriteLine(value); + } + + public TestConsole() + { + _outWriter = new TestStandardStreamWriter(); + _errorWriter = new TestStandardStreamWriter(); + } + + public IStandardStreamWriter Out => _outWriter; + + public bool IsOutputRedirected => true; + + public IStandardStreamWriter Error => _errorWriter; + + public bool IsErrorRedirected => true; + + public bool IsInputRedirected => false; + } + } +} diff --git a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj index 06c1878141..00f6e91c0d 100644 --- a/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj +++ b/src/tests/dotnet-counters/DotnetCounters.UnitTests.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/tests/dotnet-counters/TestConstants.cs b/src/tests/dotnet-counters/TestConstants.cs new file mode 100644 index 0000000000..4e9912d2e6 --- /dev/null +++ b/src/tests/dotnet-counters/TestConstants.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace DotnetCounters.UnitTests +{ + public static class TestConstants + { + public const string TestCounter = "TestCounter"; + public const string TestCounterName = TestCounter + " (dollars / 1 sec)"; + public const string TestHistogram = "TestHistogram"; + public const string TestHistogramName = TestHistogram + " (feet)"; + public const string PercentileKey = "Percentile"; + public const string TagKey = "TestTag"; + public const string TagValue = "5"; + public const string TestMeterName = "TestMeter"; + public const string Quantile50 = "50"; + public const string Quantile95 = "95"; + public const string Quantile99 = "99"; + + public const int ProviderIndex = 1; + public const int CounterNameIndex = 2; + public const int CounterTypeIndex = 3; + public const int ValueIndex = 4; + } +} From fa95cb7ac68cbe9a889b1dff72c8e535a5b13dd8 Mon Sep 17 00:00:00 2001 From: kkeirstead <85592574+kkeirstead@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:11:49 -0700 Subject: [PATCH 45/82] Dotnet Counters + Dotnet Monitor Unification (#4254) --- .../Counters/CounterPayload.cs | 181 +++-- .../Counters/CounterPayloadExtensions.cs | 21 - .../Counters/ICounterPayload.cs | 6 +- .../Counters/MetricsPipeline.cs | 11 +- .../Counters/TraceEventExtensions.cs | 151 ++-- .../DiagnosticsEventPipeProcessor.cs | 4 +- .../EventPipeStreamProvider.cs | 13 +- .../EventSourcePipeline.cs | 2 +- .../EventSourcePipelineSettings.cs | 2 + ...ft.Diagnostics.Monitoring.EventPipe.csproj | 1 + .../Trace/EventTracePipeline.cs | 2 +- .../EventCounter/EventCounterTrigger.cs | 5 +- .../SystemDiagnosticsMetricsTrigger.cs | 7 +- .../SystemDiagnosticsMetricsTriggerImpl.cs | 10 +- .../Microsoft.Diagnostics.Monitoring.csproj | 2 + src/Tools/dotnet-counters/CounterMonitor.cs | 704 +++++------------- src/Tools/dotnet-counters/CounterPayload.cs | 63 -- .../CounterPayloadExtensions.cs | 28 + .../dotnet-counters/Exporters/CSVExporter.cs | 9 +- .../Exporters/ConsoleWriter.cs | 13 +- .../Exporters/ICounterRenderer.cs | 4 +- .../dotnet-counters/Exporters/JSONExporter.cs | 9 +- src/Tools/dotnet-counters/Program.cs | 4 +- .../EventCounterPipelineUnitTests.cs | 6 +- .../EventCounterTriggerTests.cs | 3 +- src/tests/dotnet-counters/CSVExporterTests.cs | 9 +- .../dotnet-counters/JSONExporterTests.cs | 12 +- 27 files changed, 535 insertions(+), 747 deletions(-) delete mode 100644 src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs delete mode 100644 src/Tools/dotnet-counters/CounterPayload.cs create mode 100644 src/Tools/dotnet-counters/CounterPayloadExtensions.cs diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs index 99e3030081..850949b087 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs @@ -7,12 +7,9 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe { - /// - /// TODO This is currently a duplication of the src\Tools\dotnet-counters\CounterPayload.cs stack. The two will be unified in a separate change. - /// - internal class CounterPayload : ICounterPayload + internal abstract class CounterPayload : ICounterPayload { - public CounterPayload(DateTime timestamp, + protected CounterPayload(DateTime timestamp, string provider, string name, string displayName, @@ -20,7 +17,9 @@ public CounterPayload(DateTime timestamp, double value, CounterType counterType, float interval, - string metadata) + int series, + string metadata, + EventType eventType) { Timestamp = timestamp; Name = name; @@ -30,30 +29,11 @@ public CounterPayload(DateTime timestamp, CounterType = counterType; Provider = provider; Interval = interval; + Series = series; Metadata = metadata; - EventType = EventType.Gauge; - } - - // Copied from dotnet-counters - public CounterPayload(string providerName, - string name, - string metadata, - double value, - DateTime timestamp, - string type, - EventType eventType) - { - Provider = providerName; - Name = name; - Metadata = metadata; - Value = value; - Timestamp = timestamp; - CounterType = (CounterType)Enum.Parse(typeof(CounterType), type); EventType = eventType; } - public string Namespace { get; } - public string Name { get; } public string DisplayName { get; protected set; } @@ -73,12 +53,50 @@ public CounterPayload(string providerName, public string Metadata { get; } public EventType EventType { get; set; } + + public virtual bool IsMeter => false; + + public int Series { get; } + } + + internal sealed class EventCounterPayload : CounterPayload + { + public EventCounterPayload(DateTime timestamp, + string provider, + string name, + string displayName, + string unit, + double value, + CounterType counterType, + float interval, + int series, + string metadata) : base(timestamp, provider, name, displayName, unit, value, counterType, interval, series, metadata, EventType.Gauge) + { + } + } + + internal abstract class MeterPayload : CounterPayload + { + protected MeterPayload(DateTime timestamp, + string provider, + string name, + string displayName, + string unit, + double value, + CounterType counterType, + string metadata, + EventType eventType) + : base(timestamp, provider, name, displayName, unit, value, counterType, 0.0f, 0, metadata, eventType) + { + } + + public override bool IsMeter => true; } - internal class GaugePayload : CounterPayload + internal sealed class GaugePayload : MeterPayload { public GaugePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) : - base(providerName, name, metadata, value, timestamp, "Metric", EventType.Gauge) + base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Metric, metadata, EventType.Gauge) { // In case these properties are not provided, set them to appropriate values. string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; @@ -86,10 +104,10 @@ public GaugePayload(string providerName, string name, string displayName, string } } - internal class UpDownCounterPayload : CounterPayload + internal class UpDownCounterPayload : MeterPayload { public UpDownCounterPayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) : - base(providerName, name, metadata, value, timestamp, "Metric", EventType.UpDownCounter) + base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Metric, metadata, EventType.UpDownCounter) { // In case these properties are not provided, set them to appropriate values. string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; @@ -97,19 +115,26 @@ public UpDownCounterPayload(string providerName, string name, string displayName } } - internal class CounterEndedPayload : CounterPayload + internal sealed class BeginInstrumentReportingPayload : MeterPayload { - public CounterEndedPayload(string providerName, string name, DateTime timestamp) - : base(providerName, name, null, 0.0, timestamp, "Metric", EventType.CounterEnded) + public BeginInstrumentReportingPayload(string providerName, string name, DateTime timestamp) + : base(timestamp, providerName, name, string.Empty, string.Empty, 0.0, CounterType.Metric, null, EventType.BeginInstrumentReporting) { + } + } + internal sealed class CounterEndedPayload : MeterPayload + { + public CounterEndedPayload(string providerName, string name, DateTime timestamp) + : base(timestamp, providerName, name, string.Empty, string.Empty, 0.0, CounterType.Metric, null, EventType.CounterEnded) + { } } - internal class RatePayload : CounterPayload + internal sealed class RatePayload : MeterPayload { public RatePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, double intervalSecs, DateTime timestamp) : - base(providerName, name, metadata, value, timestamp, "Rate", EventType.Rate) + base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Rate, metadata, EventType.Rate) { // In case these properties are not provided, set them to appropriate values. string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; @@ -119,35 +144,46 @@ public RatePayload(string providerName, string name, string displayName, string } } - internal class PercentilePayload : CounterPayload + internal record struct Quantile(double Percentage, double Value); + + internal sealed class PercentilePayload : MeterPayload { - public PercentilePayload(string providerName, string name, string displayName, string displayUnits, string metadata, IEnumerable quantiles, DateTime timestamp) : - base(providerName, name, metadata, 0.0, timestamp, "Metric", EventType.Histogram) + public PercentilePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) : + base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Metric, metadata, EventType.Histogram) { // In case these properties are not provided, set them to appropriate values. string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName; - Quantiles = quantiles.ToArray(); } - - public Quantile[] Quantiles { get; } } - internal record struct Quantile(double Percentage, double Value); - - internal class ErrorPayload : CounterPayload + // Dotnet-monitor and dotnet-counters previously had incompatible PercentilePayload implementations before being unified - + // Dotnet-monitor created a single payload that contained all of the quantiles to keep them together, whereas + // dotnet-counters created a separate payload for each quantile (multiple payloads per TraceEvent). + // AggregatePercentilePayload allows dotnet-monitor to construct a PercentilePayload for individual quantiles + // like dotnet-counters, while still keeping the quantiles together as a unit. + internal sealed class AggregatePercentilePayload : MeterPayload { - public ErrorPayload(string errorMessage) : this(errorMessage, DateTime.UtcNow) + public AggregatePercentilePayload(string providerName, string name, string displayName, string displayUnits, string metadata, IEnumerable quantiles, DateTime timestamp) : + base(timestamp, providerName, name, displayName, displayUnits, 0.0, CounterType.Metric, metadata, EventType.Histogram) { + //string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; + //DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName; + Quantiles = quantiles.ToArray(); } - public ErrorPayload(string errorMessage, DateTime timestamp) : - base(string.Empty, string.Empty, null, 0.0, timestamp, "Metric", EventType.Error) + public Quantile[] Quantiles { get; } + } + + internal sealed class ErrorPayload : MeterPayload + { + public ErrorPayload(string errorMessage, DateTime timestamp, EventType eventType) + : base(timestamp, string.Empty, string.Empty, string.Empty, string.Empty, 0.0, CounterType.Metric, null, eventType) { ErrorMessage = errorMessage; } - public string ErrorMessage { get; private set; } + public string ErrorMessage { get; } } internal enum EventType : int @@ -156,7 +192,52 @@ internal enum EventType : int Gauge, Histogram, UpDownCounter, - Error, - CounterEnded + BeginInstrumentReporting, + CounterEnded, + HistogramLimitError, + TimeSeriesLimitError, + ErrorTargetProcess, + MultipleSessionsNotSupportedError, + MultipleSessionsConfiguredIncorrectlyError, + ObservableInstrumentCallbackError + } + + internal static class EventTypeExtensions + { + public static bool IsValuePublishedEvent(this EventType eventType) + { + return eventType is EventType.Gauge + || eventType is EventType.Rate + || eventType is EventType.Histogram + || eventType is EventType.UpDownCounter; + } + + public static bool IsError(this EventType eventType) + { + return eventType is EventType.HistogramLimitError + || eventType is EventType.TimeSeriesLimitError + || eventType is EventType.ErrorTargetProcess + || eventType is EventType.MultipleSessionsNotSupportedError + || eventType is EventType.MultipleSessionsConfiguredIncorrectlyError + || eventType is EventType.ObservableInstrumentCallbackError; + } + + public static bool IsNonFatalError(this EventType eventType) + { + return IsError(eventType) + && !IsTracingError(eventType) + && !IsSessionStartupError(eventType); + } + + public static bool IsTracingError(this EventType eventType) + { + return eventType is EventType.ErrorTargetProcess; + } + + public static bool IsSessionStartupError(this EventType eventType) + { + return eventType is EventType.MultipleSessionsNotSupportedError + || eventType is EventType.MultipleSessionsConfiguredIncorrectlyError; + } } } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs deleted file mode 100644 index 12d4b862e4..0000000000 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.Diagnostics.Monitoring.EventPipe -{ - internal static class CounterPayloadExtensions - { - public static string GetDisplay(this ICounterPayload counterPayload) - { - if (counterPayload.CounterType == CounterType.Rate) - { - return $"{counterPayload.DisplayName} ({counterPayload.Unit} / {counterPayload.Interval} sec)"; - } - if (!string.IsNullOrEmpty(counterPayload.Unit)) - { - return $"{counterPayload.DisplayName} ({counterPayload.Unit})"; - } - return $"{counterPayload.DisplayName}"; - } - } -} diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs index 4e3ce06f74..4cc5fdb043 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs @@ -41,6 +41,10 @@ internal interface ICounterPayload /// string Metadata { get; } - EventType EventType { get; set; } + EventType EventType { get; } + + bool IsMeter { get; } + + int Series { get; } } } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs index 1c3a29ab8a..9f8f34c150 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs @@ -17,6 +17,7 @@ internal class MetricsPipeline : EventSourcePipeline private readonly CounterFilter _filter; private string _clientId; private string _sessionId; + private CounterConfiguration _counterConfiguration; public MetricsPipeline(DiagnosticsClient client, MetricsPipelineSettings settings, @@ -51,6 +52,14 @@ protected override MonitoringSourceConfiguration CreateConfiguration() _clientId = config.ClientId; _sessionId = config.SessionId; + _counterConfiguration = new CounterConfiguration(_filter) + { + SessionId = _sessionId, + ClientId = _clientId, + MaxHistograms = Settings.MaxHistograms, + MaxTimeseries = Settings.MaxTimeSeries + }; + return config; } @@ -61,7 +70,7 @@ protected override async Task OnEventSourceAvailable(EventPipeEventSource eventS eventSource.Dynamic.All += traceEvent => { try { - if (traceEvent.TryGetCounterPayload(_filter, _sessionId, _clientId, out ICounterPayload counterPayload)) + if (traceEvent.TryGetCounterPayload(_counterConfiguration, out ICounterPayload counterPayload)) { ExecuteCounterLoggerAction((metricLogger) => metricLogger.Log(counterPayload)); } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs index 942a7ebddf..06472cf3f3 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs @@ -9,11 +9,29 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe { + internal class CounterConfiguration + { + public CounterConfiguration(CounterFilter filter) + { + CounterFilter = filter ?? throw new ArgumentNullException(nameof(filter)); + } + + public CounterFilter CounterFilter { get; } + + public string SessionId { get; set; } + + public string ClientId { get; set; } + + public int MaxHistograms { get; set; } + + public int MaxTimeseries { get; set; } + } + internal static class TraceEventExtensions { private static HashSet inactiveSharedSessions = new(StringComparer.OrdinalIgnoreCase); - public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilter filter, string sessionId, string clientId, out ICounterPayload payload) + public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload) { payload = null; @@ -27,12 +45,12 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte string counterName = payloadFields["Name"].ToString(); string metadata = payloadFields["Metadata"].ToString(); - + int seriesValue = GetInterval(series); //CONSIDER //Concurrent counter sessions do not each get a separate interval. Instead the payload //for _all_ the counters changes the Series to be the lowest specified interval, on a per provider basis. //Currently the CounterFilter will remove any data whose Series doesn't match the requested interval. - if (!filter.IsIncluded(traceEvent.ProviderName, counterName, GetInterval(series))) + if (!counterConfiguration.CounterFilter.IsIncluded(traceEvent.ProviderName, counterName, seriesValue)) { return false; } @@ -61,7 +79,7 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte // Note that dimensional data such as pod and namespace are automatically added in prometheus and azure monitor scenarios. // We no longer added it here. - payload = new CounterPayload( + payload = new EventCounterPayload( traceEvent.TimeStamp, traceEvent.ProviderName, counterName, displayName, @@ -69,57 +87,57 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte value, counterType, intervalSec, + seriesValue / 1000, metadata); return true; } - if (clientId != null && !inactiveSharedSessions.Contains(clientId) && MonitoringSourceConfiguration.SystemDiagnosticsMetricsProviderName.Equals(traceEvent.ProviderName)) + if (counterConfiguration.ClientId != null && !inactiveSharedSessions.Contains(counterConfiguration.ClientId) && MonitoringSourceConfiguration.SystemDiagnosticsMetricsProviderName.Equals(traceEvent.ProviderName)) { if (traceEvent.EventName == "BeginInstrumentReporting") { - // Do we want to log something for this? - //HandleBeginInstrumentReporting(traceEvent); + HandleBeginInstrumentReporting(traceEvent, counterConfiguration, out payload); } if (traceEvent.EventName == "HistogramValuePublished") { - HandleHistogram(traceEvent, filter, sessionId, out payload); + HandleHistogram(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "GaugeValuePublished") { - HandleGauge(traceEvent, filter, sessionId, out payload); + HandleGauge(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "CounterRateValuePublished") { - HandleCounterRate(traceEvent, filter, sessionId, out payload); + HandleCounterRate(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "UpDownCounterRateValuePublished") { - HandleUpDownCounterValue(traceEvent, filter, sessionId, out payload); + HandleUpDownCounterValue(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "TimeSeriesLimitReached") { - HandleTimeSeriesLimitReached(traceEvent, sessionId, out payload); + HandleTimeSeriesLimitReached(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "HistogramLimitReached") { - HandleHistogramLimitReached(traceEvent, sessionId, out payload); + HandleHistogramLimitReached(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "Error") { - HandleError(traceEvent, sessionId, out payload); + HandleError(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "ObservableInstrumentCallbackError") { - HandleObservableInstrumentCallbackError(traceEvent, sessionId, out payload); + HandleObservableInstrumentCallbackError(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "MultipleSessionsNotSupportedError") { - HandleMultipleSessionsNotSupportedError(traceEvent, sessionId, out payload); + HandleMultipleSessionsNotSupportedError(traceEvent, counterConfiguration, out payload); } else if (traceEvent.EventName == "MultipleSessionsConfiguredIncorrectlyError") { - HandleMultipleSessionsConfiguredIncorrectlyError(traceEvent, clientId, out payload); + HandleMultipleSessionsConfiguredIncorrectlyError(traceEvent, counterConfiguration.ClientId, out payload); } return payload != null; @@ -128,13 +146,13 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte return false; } - private static void HandleGauge(TraceEvent obj, CounterFilter filter, string sessionId, out ICounterPayload payload) + private static void HandleGauge(TraceEvent obj, CounterConfiguration counterConfiguration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); - if (payloadSessionId != sessionId) + if (payloadSessionId != counterConfiguration.SessionId) { return; } @@ -146,7 +164,7 @@ private static void HandleGauge(TraceEvent obj, CounterFilter filter, string ses string tags = (string)obj.PayloadValue(5); string lastValueText = (string)obj.PayloadValue(6); - if (!filter.IsIncluded(meterName, instrumentName)) + if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName)) { return; } @@ -164,13 +182,36 @@ private static void HandleGauge(TraceEvent obj, CounterFilter filter, string ses } } - private static void HandleCounterRate(TraceEvent traceEvent, CounterFilter filter, string sessionId, out ICounterPayload payload) + private static void HandleBeginInstrumentReporting(TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload) + { + payload = null; + + string payloadSessionId = (string)traceEvent.PayloadValue(0); + if (payloadSessionId != counterConfiguration.SessionId) + { + return; + } + + string meterName = (string)traceEvent.PayloadValue(1); + //string meterVersion = (string)obj.PayloadValue(2); + string instrumentName = (string)traceEvent.PayloadValue(3); + + if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName)) + { + return; + } + + + payload = new BeginInstrumentReportingPayload(meterName, instrumentName, traceEvent.TimeStamp); + } + + private static void HandleCounterRate(TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)traceEvent.PayloadValue(0); - if (payloadSessionId != sessionId) + if (payloadSessionId != counterConfiguration.SessionId) { return; } @@ -182,14 +223,14 @@ private static void HandleCounterRate(TraceEvent traceEvent, CounterFilter filte string tags = (string)traceEvent.PayloadValue(5); string rateText = (string)traceEvent.PayloadValue(6); - if (!filter.IsIncluded(meterName, instrumentName)) + if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName)) { return; } - if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double rate)) + if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value)) { - payload = new RatePayload(meterName, instrumentName, null, unit, tags, rate, filter.DefaultIntervalSeconds, traceEvent.TimeStamp); + payload = new RatePayload(meterName, instrumentName, null, unit, tags, value, counterConfiguration.CounterFilter.DefaultIntervalSeconds, traceEvent.TimeStamp); } else { @@ -200,13 +241,13 @@ private static void HandleCounterRate(TraceEvent traceEvent, CounterFilter filte } } - private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterFilter filter, string sessionId, out ICounterPayload payload) + private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)traceEvent.PayloadValue(0); - if (payloadSessionId != sessionId || traceEvent.Version < 1) // Version 1 added the value field. + if (payloadSessionId != configuration.SessionId || traceEvent.Version < 1) // Version 1 added the value field. { return; } @@ -219,7 +260,7 @@ private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterFilte //string rateText = (string)traceEvent.PayloadValue(6); // Not currently using rate for UpDownCounters. string valueText = (string)traceEvent.PayloadValue(7); - if (!filter.IsIncluded(meterName, instrumentName)) + if (!configuration.CounterFilter.IsIncluded(meterName, instrumentName)) { return; } @@ -239,12 +280,13 @@ private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterFilte } } - private static void HandleHistogram(TraceEvent obj, CounterFilter filter, string sessionId, out ICounterPayload payload) + private static void HandleHistogram(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); - if (payloadSessionId != sessionId) + + if (payloadSessionId != configuration.SessionId) { return; } @@ -256,72 +298,71 @@ private static void HandleHistogram(TraceEvent obj, CounterFilter filter, string string tags = (string)obj.PayloadValue(5); string quantilesText = (string)obj.PayloadValue(6); - if (!filter.IsIncluded(meterName, instrumentName)) + if (!configuration.CounterFilter.IsIncluded(meterName, instrumentName)) { return; } //Note quantiles can be empty. IList quantiles = ParseQuantiles(quantilesText); - payload = new PercentilePayload(meterName, instrumentName, null, unit, tags, quantiles, obj.TimeStamp); - } - + payload = new AggregatePercentilePayload(meterName, instrumentName, null, unit, tags, quantiles, obj.TimeStamp); + } - private static void HandleHistogramLimitReached(TraceEvent obj, string sessionId, out ICounterPayload payload) + private static void HandleHistogramLimitReached(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); - if (payloadSessionId != sessionId) + if (payloadSessionId != configuration.SessionId) { return; } - string errorMessage = $"Warning: Histogram tracking limit reached. Not all data is being shown. The limit can be changed with maxHistograms but will use more memory in the target process."; + string errorMessage = $"Warning: Histogram tracking limit ({configuration.MaxHistograms}) reached. Not all data is being shown. The limit can be changed but will use more memory in the target process."; - payload = new ErrorPayload(errorMessage); + payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.HistogramLimitError); } - private static void HandleTimeSeriesLimitReached(TraceEvent obj, string sessionId, out ICounterPayload payload) + private static void HandleTimeSeriesLimitReached(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); - if (payloadSessionId != sessionId) + if (payloadSessionId != configuration.SessionId) { return; } - string errorMessage = "Warning: Time series tracking limit reached. Not all data is being shown. The limit can be changed with maxTimeSeries but will use more memory in the target process."; + string errorMessage = $"Warning: Time series tracking limit ({configuration.MaxTimeseries}) reached. Not all data is being shown. The limit can be changed but will use more memory in the target process."; - payload = new ErrorPayload(errorMessage, obj.TimeStamp); + payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.TimeSeriesLimitError); } - private static void HandleError(TraceEvent obj, string sessionId, out ICounterPayload payload) + private static void HandleError(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); string error = (string)obj.PayloadValue(1); - if (payloadSessionId != sessionId) + if (configuration.SessionId != payloadSessionId) { return; } string errorMessage = "Error reported from target process:" + Environment.NewLine + error; - payload = new ErrorPayload(errorMessage, obj.TimeStamp); + payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.ErrorTargetProcess); } - private static void HandleMultipleSessionsNotSupportedError(TraceEvent obj, string sessionId, out ICounterPayload payload) + private static void HandleMultipleSessionsNotSupportedError(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); - if (payloadSessionId == sessionId) + if (payloadSessionId == configuration.SessionId) { // If our session is the one that is running then the error is not for us, // it is for some other session that came later @@ -332,7 +373,7 @@ private static void HandleMultipleSessionsNotSupportedError(TraceEvent obj, stri string errorMessage = "Error: Another metrics collection session is already in progress for the target process." + Environment.NewLine + "Concurrent sessions are not supported."; - payload = new ErrorPayload(errorMessage, obj.TimeStamp); + payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.MultipleSessionsNotSupportedError); } } @@ -383,20 +424,20 @@ private static void HandleMultipleSessionsConfiguredIncorrectlyError(TraceEvent if (TryCreateSharedSessionConfiguredIncorrectlyMessage(obj, clientId, out string message)) { - payload = new ErrorPayload(message.ToString(), obj.TimeStamp); + payload = new ErrorPayload(message.ToString(), obj.TimeStamp, EventType.MultipleSessionsConfiguredIncorrectlyError); inactiveSharedSessions.Add(clientId); } } - private static void HandleObservableInstrumentCallbackError(TraceEvent obj, string sessionId, out ICounterPayload payload) + private static void HandleObservableInstrumentCallbackError(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload) { payload = null; string payloadSessionId = (string)obj.PayloadValue(0); string error = (string)obj.PayloadValue(1); - if (payloadSessionId != sessionId) + if (payloadSessionId != configuration.SessionId) { return; } @@ -404,10 +445,10 @@ private static void HandleObservableInstrumentCallbackError(TraceEvent obj, stri string errorMessage = "Exception thrown from an observable instrument callback in the target process:" + Environment.NewLine + error; - payload = new ErrorPayload(errorMessage, obj.TimeStamp); + payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.ObservableInstrumentCallbackError); } - private static IList ParseQuantiles(string quantileList) + private static List ParseQuantiles(string quantileList) { string[] quantileParts = quantileList.Split(';', StringSplitOptions.RemoveEmptyEntries); List quantiles = new(); @@ -418,11 +459,11 @@ private static IList ParseQuantiles(string quantileList) { continue; } - if (!double.TryParse(keyValParts[0], out double key)) + if (!double.TryParse(keyValParts[0], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double key)) { continue; } - if (!double.TryParse(keyValParts[1], out double val)) + if (!double.TryParse(keyValParts[1], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double val)) { continue; } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs index 7ebc6409b3..cff749c126 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs @@ -39,7 +39,7 @@ Func, CancellationToken, Task> onEventSourceAva _sessionStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } - public async Task Process(DiagnosticsClient client, TimeSpan duration, CancellationToken token) + public async Task Process(DiagnosticsClient client, TimeSpan duration, bool resumeRuntime, CancellationToken token) { //No need to guard against reentrancy here, since the calling pipeline does this already. IDisposable registration = token.Register(() => TryCancelCompletionSources(token)); @@ -53,7 +53,7 @@ public async Task Process(DiagnosticsClient client, TimeSpan duration, Cancellat // Allows the event handling routines to stop processing before the duration expires. Func stopFunc = () => Task.Run(() => { streamProvider.StopProcessing(); }); - Stream sessionStream = await streamProvider.ProcessEvents(client, duration, token).ConfigureAwait(false); + Stream sessionStream = await streamProvider.ProcessEvents(client, duration, resumeRuntime, token).ConfigureAwait(false); if (!_sessionStarted.TrySetResult(true)) { diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs index 45cf97be43..23bb51b28f 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs @@ -21,7 +21,7 @@ public EventPipeStreamProvider(MonitoringSourceConfiguration sourceConfig) _stopProcessingSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } - public async Task ProcessEvents(DiagnosticsClient client, TimeSpan duration, CancellationToken cancellationToken) + public async Task ProcessEvents(DiagnosticsClient client, TimeSpan duration, bool resumeRuntime, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -29,6 +29,17 @@ public async Task ProcessEvents(DiagnosticsClient client, TimeSpan durat try { session = await client.StartEventPipeSessionAsync(_sourceConfig.GetProviders(), _sourceConfig.RequestRundown, _sourceConfig.BufferSizeInMB, cancellationToken).ConfigureAwait(false); + if (resumeRuntime) + { + try + { + await client.ResumeRuntimeAsync(cancellationToken).ConfigureAwait(false); + } + catch (UnsupportedCommandException) + { + // Noop if the command is unknown since the target process is most likely a 3.1 app. + } + } } catch (EndOfStreamException e) { diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipeline.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipeline.cs index 7da5c54ea6..1b36f04e52 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipeline.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipeline.cs @@ -35,7 +35,7 @@ protected override Task OnRun(CancellationToken token) { try { - return _processor.Value.Process(Client, Settings.Duration, token); + return _processor.Value.Process(Client, Settings.Duration, Settings.ResumeRuntime, token); } catch (InvalidOperationException e) { diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipelineSettings.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipelineSettings.cs index ece20a2368..1c35878eb3 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipelineSettings.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventSourcePipelineSettings.cs @@ -8,5 +8,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe internal class EventSourcePipelineSettings { public TimeSpan Duration { get; set; } + + public bool ResumeRuntime { get; set; } } } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj index 0682848f12..c104003875 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj @@ -40,6 +40,7 @@ + diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Trace/EventTracePipeline.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Trace/EventTracePipeline.cs index 68f1760364..bb4d1fc3af 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Trace/EventTracePipeline.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Trace/EventTracePipeline.cs @@ -31,7 +31,7 @@ protected override async Task OnRun(CancellationToken token) { //It is important that the underlying stream be completely read, or disposed. //If rundown is enabled, the underlying stream must be drained or disposed, or the app hangs. - using Stream eventStream = await _provider.Value.ProcessEvents(Client, Settings.Duration, token).ConfigureAwait(false); + using Stream eventStream = await _provider.Value.ProcessEvents(Client, Settings.Duration, Settings.ResumeRuntime, token).ConfigureAwait(false); await _onStreamAvailable(eventStream, token).ConfigureAwait(false); } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/EventCounter/EventCounterTrigger.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/EventCounter/EventCounterTrigger.cs index 6350d43038..a1b96bf004 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/EventCounter/EventCounterTrigger.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/EventCounter/EventCounterTrigger.cs @@ -32,6 +32,7 @@ internal sealed class EventCounterTrigger : private readonly CounterFilter _filter; private readonly EventCounterTriggerImpl _impl; private readonly string _providerName; + private CounterConfiguration _counterConfiguration; public EventCounterTrigger(EventCounterTriggerSettings settings) { @@ -45,6 +46,8 @@ public EventCounterTrigger(EventCounterTriggerSettings settings) _filter = new CounterFilter(settings.CounterIntervalSeconds); _filter.AddFilter(settings.ProviderName, new string[] { settings.CounterName }); + _counterConfiguration = new CounterConfiguration(_filter); + _impl = new EventCounterTriggerImpl(settings); _providerName = settings.ProviderName; @@ -58,7 +61,7 @@ public IReadOnlyDictionary> GetProviderEvent public bool HasSatisfiedCondition(TraceEvent traceEvent) { // Filter to the counter of interest before forwarding to the implementation - if (traceEvent.TryGetCounterPayload(_filter, null, null, out ICounterPayload payload)) + if (traceEvent.TryGetCounterPayload(_counterConfiguration, out ICounterPayload payload)) { return _impl.HasSatisfiedCondition(payload); } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTrigger.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTrigger.cs index 378e389065..d9e9a1267a 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTrigger.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTrigger.cs @@ -29,6 +29,7 @@ internal sealed class SystemDiagnosticsMetricsTrigger : private readonly string _meterName; private readonly string _clientId; private readonly string _sessionId; + private CounterConfiguration _counterConfiguration; public SystemDiagnosticsMetricsTrigger(SystemDiagnosticsMetricsTriggerSettings settings) { @@ -49,6 +50,10 @@ public SystemDiagnosticsMetricsTrigger(SystemDiagnosticsMetricsTriggerSettings s _clientId = settings.ClientId; _sessionId = settings.SessionId; + + _clientId = settings.ClientId; + + _counterConfiguration = new CounterConfiguration(_filter) { SessionId = _sessionId, ClientId = _clientId }; } public IReadOnlyDictionary> GetProviderEventMap() @@ -59,7 +64,7 @@ public IReadOnlyDictionary> GetProviderEvent public bool HasSatisfiedCondition(TraceEvent traceEvent) { // Filter to the counter of interest before forwarding to the implementation - if (traceEvent.TryGetCounterPayload(_filter, _sessionId, _clientId, out ICounterPayload payload)) + if (traceEvent.TryGetCounterPayload(_counterConfiguration, out ICounterPayload payload)) { return _impl.HasSatisfiedCondition(payload); } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs index cb60f10320..d0e4e28087 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs @@ -55,7 +55,7 @@ public bool HasSatisfiedCondition(ICounterPayload payload) { EventType eventType = payload.EventType; - if (eventType == EventType.Error || eventType == EventType.CounterEnded) + if (eventType.IsError() || eventType == EventType.CounterEnded) { // not currently logging the error messages @@ -63,17 +63,17 @@ public bool HasSatisfiedCondition(ICounterPayload payload) } else { - bool passesValueFilter = (payload is PercentilePayload percentilePayload) ? - _valueFilterHistogram(CreatePayloadDictionary(percentilePayload)) : + bool passesValueFilter = (payload is AggregatePercentilePayload aggregatePercentilePayload) ? + _valueFilterHistogram(CreatePayloadDictionary(aggregatePercentilePayload)) : _valueFilterDefault(payload.Value); return SharedTriggerImplHelper.HasSatisfiedCondition(ref _latestTicks, ref _targetTicks, _windowTicks, _intervalTicks, payload, passesValueFilter); } } - private static Dictionary CreatePayloadDictionary(PercentilePayload percentilePayload) + private static Dictionary CreatePayloadDictionary(AggregatePercentilePayload aggregatePercentilePayload) { - return percentilePayload.Quantiles.ToDictionary(keySelector: p => CounterUtilities.CreatePercentile(p.Percentage), elementSelector: p => p.Value); + return aggregatePercentilePayload.Quantiles.ToDictionary(keySelector: q => CounterUtilities.CreatePercentile(q.Percentage), elementSelector: q => q.Value); } } } diff --git a/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj b/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj index 975476fa62..a4fff0796f 100644 --- a/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj +++ b/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj @@ -26,6 +26,8 @@ + + diff --git a/src/Tools/dotnet-counters/CounterMonitor.cs b/src/Tools/dotnet-counters/CounterMonitor.cs index a2a623d0a6..04159229ab 100644 --- a/src/Tools/dotnet-counters/CounterMonitor.cs +++ b/src/Tools/dotnet-counters/CounterMonitor.cs @@ -6,45 +6,31 @@ using System.CommandLine; using System.CommandLine.IO; using System.CommandLine.Rendering; +using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.Tracing; -using System.Globalization; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Diagnostics.Monitoring; using Microsoft.Diagnostics.Monitoring.EventPipe; using Microsoft.Diagnostics.NETCore.Client; using Microsoft.Diagnostics.Tools.Counters.Exporters; -using Microsoft.Diagnostics.Tracing; using Microsoft.Internal.Common.Utils; namespace Microsoft.Diagnostics.Tools.Counters { - public class CounterMonitor + internal class CounterMonitor : ICountersLogger { private const int BufferDelaySecs = 1; - private const string SharedSessionId = "SHARED"; // This should be identical to the one used by dotnet-monitor in MetricSourceConfiguration.cs - private static HashSet inactiveSharedSessions = new(StringComparer.OrdinalIgnoreCase); - - private string _sessionId; private int _processId; - private int _interval; private CounterSet _counterList; - private CancellationToken _ct; private IConsole _console; private ICounterRenderer _renderer; private string _output; private bool _pauseCmdSet; - private readonly TaskCompletionSource _shouldExit; - private bool _resumeRuntime; + private readonly TaskCompletionSource _shouldExit; private DiagnosticsClient _diagnosticsClient; - private EventPipeSession _session; - private readonly string _clientId; - private int _maxTimeSeries; - private int _maxHistograms; - private TimeSpan _duration; + private MetricsPipelineSettings _settings; private class ProviderEventState { @@ -57,77 +43,7 @@ private class ProviderEventState public CounterMonitor() { _pauseCmdSet = false; - _clientId = Guid.NewGuid().ToString(); - - _shouldExit = new TaskCompletionSource(); - } - - private void DynamicAllMonitor(TraceEvent obj) - { - if (_shouldExit.Task.IsCompleted) - { - return; - } - - lock (this) - { - // If we are paused, ignore the event. - // There's a potential race here between the two tasks but not a huge deal if we miss by one event. - _renderer.ToggleStatus(_pauseCmdSet); - - // If a session received a MultipleSessionsConfiguredIncorrectlyError, ignore future shared events - if (obj.ProviderName == "System.Diagnostics.Metrics" && !inactiveSharedSessions.Contains(_clientId)) - { - if (obj.EventName == "BeginInstrumentReporting") - { - HandleBeginInstrumentReporting(obj); - } - if (obj.EventName == "HistogramValuePublished") - { - HandleHistogram(obj); - } - else if (obj.EventName == "GaugeValuePublished") - { - HandleGauge(obj); - } - else if (obj.EventName == "CounterRateValuePublished") - { - HandleCounterRate(obj); - } - else if (obj.EventName == "UpDownCounterRateValuePublished") - { - HandleUpDownCounterValue(obj); - } - else if (obj.EventName == "TimeSeriesLimitReached") - { - HandleTimeSeriesLimitReached(obj); - } - else if (obj.EventName == "HistogramLimitReached") - { - HandleHistogramLimitReached(obj); - } - else if (obj.EventName == "Error") - { - HandleError(obj); - } - else if (obj.EventName == "ObservableInstrumentCallbackError") - { - HandleObservableInstrumentCallbackError(obj); - } - else if (obj.EventName == "MultipleSessionsNotSupportedError") - { - HandleMultipleSessionsNotSupportedError(obj); - } - else if (obj.EventName == "MultipleSessionsConfiguredIncorrectlyError") - { - HandleMultipleSessionsConfiguredIncorrectlyError(obj); - } - } - else if (obj.EventName == "EventCounters") - { - HandleDiagnosticCounter(obj); - } - } + _shouldExit = new TaskCompletionSource(); } private void MeterInstrumentEventObserved(string meterName, DateTime timestamp) @@ -147,255 +63,16 @@ private void MeterInstrumentEventObserved(string meterName, DateTime timestamp) } } - private void HandleBeginInstrumentReporting(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - string meterName = (string)obj.PayloadValue(1); - // string instrumentName = (string)obj.PayloadValue(3); - if (sessionId != _sessionId) - { - return; - } - MeterInstrumentEventObserved(meterName, obj.TimeStamp); - } - - private void HandleCounterRate(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - string meterName = (string)obj.PayloadValue(1); - //string meterVersion = (string)obj.PayloadValue(2); - string instrumentName = (string)obj.PayloadValue(3); - string unit = (string)obj.PayloadValue(4); - string tags = (string)obj.PayloadValue(5); - string rateText = (string)obj.PayloadValue(6); - if (sessionId != _sessionId || !Filter(meterName, instrumentName)) - { - return; - } - MeterInstrumentEventObserved(meterName, obj.TimeStamp); - - // the value might be an empty string indicating no measurement was provided this collection interval - if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double rate)) - { - CounterPayload payload = new RatePayload(meterName, instrumentName, null, unit, tags, rate, _interval, obj.TimeStamp); - _renderer.CounterPayloadReceived(payload, _pauseCmdSet); - } - - } - - private void HandleGauge(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - string meterName = (string)obj.PayloadValue(1); - //string meterVersion = (string)obj.PayloadValue(2); - string instrumentName = (string)obj.PayloadValue(3); - string unit = (string)obj.PayloadValue(4); - string tags = (string)obj.PayloadValue(5); - string lastValueText = (string)obj.PayloadValue(6); - if (sessionId != _sessionId || !Filter(meterName, instrumentName)) - { - return; - } - MeterInstrumentEventObserved(meterName, obj.TimeStamp); - - // the value might be an empty string indicating no measurement was provided this collection interval - if (double.TryParse(lastValueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double lastValue)) - { - CounterPayload payload = new GaugePayload(meterName, instrumentName, null, unit, tags, lastValue, obj.TimeStamp); - _renderer.CounterPayloadReceived(payload, _pauseCmdSet); - } - else - { - // for observable instruments we assume the lack of data is meaningful and remove it from the UI - CounterPayload payload = new RatePayload(meterName, instrumentName, null, unit, tags, 0, _interval, obj.TimeStamp); - _renderer.CounterStopped(payload); - } - } - - private void HandleUpDownCounterValue(TraceEvent obj) - { - if (obj.Version < 1) // Version 1 added the value field. - { - return; - } - - string sessionId = (string)obj.PayloadValue(0); - string meterName = (string)obj.PayloadValue(1); - //string meterVersion = (string)obj.PayloadValue(2); - string instrumentName = (string)obj.PayloadValue(3); - string unit = (string)obj.PayloadValue(4); - string tags = (string)obj.PayloadValue(5); - //string rateText = (string)obj.PayloadValue(6); // Not currently using rate for UpDownCounters. - string valueText = (string)obj.PayloadValue(7); - if (sessionId != _sessionId || !Filter(meterName, instrumentName)) - { - return; - } - MeterInstrumentEventObserved(meterName, obj.TimeStamp); - - // the value might be an empty string indicating no measurement was provided this collection interval - if (double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value)) - { - // UpDownCounter reports the value, not the rate - this is different than how Counter behaves, and is thus treated as a gauge. - CounterPayload payload = new GaugePayload(meterName, instrumentName, null, unit, tags, value, obj.TimeStamp); - _renderer.CounterPayloadReceived(payload, _pauseCmdSet); - } - else - { - // for observable instruments we assume the lack of data is meaningful and remove it from the UI - CounterPayload payload = new RatePayload(meterName, instrumentName, null, unit, tags, 0, _interval, obj.TimeStamp); - _renderer.CounterStopped(payload); - } - } - - private void HandleHistogram(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - string meterName = (string)obj.PayloadValue(1); - //string meterVersion = (string)obj.PayloadValue(2); - string instrumentName = (string)obj.PayloadValue(3); - string unit = (string)obj.PayloadValue(4); - string tags = (string)obj.PayloadValue(5); - string quantilesText = (string)obj.PayloadValue(6); - if (sessionId != _sessionId || !Filter(meterName, instrumentName)) - { - return; - } - MeterInstrumentEventObserved(meterName, obj.TimeStamp); - KeyValuePair[] quantiles = ParseQuantiles(quantilesText); - foreach ((double key, double val) in quantiles) - { - CounterPayload payload = new PercentilePayload(meterName, instrumentName, null, unit, AppendQuantile(tags, $"Percentile={key * 100}"), val, obj.TimeStamp); - _renderer.CounterPayloadReceived(payload, _pauseCmdSet); - } - } - - private void HandleHistogramLimitReached(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - if (sessionId != _clientId) - { - return; - } - _renderer.SetErrorText( - $"Warning: Histogram tracking limit ({_maxHistograms}) reached. Not all data is being shown." + Environment.NewLine + - "The limit can be changed with --maxHistograms but will use more memory in the target process." - ); - } - - private void HandleTimeSeriesLimitReached(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - if (sessionId != _sessionId) - { - return; - } - _renderer.SetErrorText( - $"Warning: Time series tracking limit ({_maxTimeSeries}) reached. Not all data is being shown." + Environment.NewLine + - "The limit can be changed with --maxTimeSeries but will use more memory in the target process." - ); - } - - private void HandleError(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - string error = (string)obj.PayloadValue(1); - if (sessionId != _sessionId) - { - return; - } - _renderer.SetErrorText( - "Error reported from target process:" + Environment.NewLine + - error - ); - _shouldExit.TrySetResult((int)ReturnCode.TracingError); - } - - private void HandleObservableInstrumentCallbackError(TraceEvent obj) - { - string sessionId = (string)obj.PayloadValue(0); - string error = (string)obj.PayloadValue(1); - if (sessionId != _sessionId) - { - return; - } - _renderer.SetErrorText( - "Exception thrown from an observable instrument callback in the target process:" + Environment.NewLine + - error - ); - } - - private void HandleMultipleSessionsNotSupportedError(TraceEvent obj) - { - string runningSessionId = (string)obj.PayloadValue(0); - if (runningSessionId == _sessionId) - { - // If our session is the one that is running then the error is not for us, - // it is for some other session that came later - return; - } - _renderer.SetErrorText( - "Error: Another metrics collection session is already in progress for the target process." + Environment.NewLine + - "Concurrent sessions are not supported."); - _shouldExit.TrySetResult((int)ReturnCode.SessionCreationError); - } - - private void HandleMultipleSessionsConfiguredIncorrectlyError(TraceEvent obj) - { - if (TraceEventExtensions.TryCreateSharedSessionConfiguredIncorrectlyMessage(obj, _clientId, out string message)) - { - _renderer.SetErrorText(message); - inactiveSharedSessions.Add(_clientId); - _shouldExit.TrySetResult((int)ReturnCode.SessionCreationError); - } - } - - private static KeyValuePair[] ParseQuantiles(string quantileList) - { - string[] quantileParts = quantileList.Split(';', StringSplitOptions.RemoveEmptyEntries); - List> quantiles = new(); - foreach (string quantile in quantileParts) - { - string[] keyValParts = quantile.Split('=', StringSplitOptions.RemoveEmptyEntries); - if (keyValParts.Length != 2) - { - continue; - } - if (!double.TryParse(keyValParts[0], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double key)) - { - continue; - } - if (!double.TryParse(keyValParts[1], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double val)) - { - continue; - } - quantiles.Add(new KeyValuePair(key, val)); - } - return quantiles.ToArray(); - } - - private static string AppendQuantile(string tags, string quantile) => string.IsNullOrEmpty(tags) ? quantile : $"{tags},{quantile}"; - - private void HandleDiagnosticCounter(TraceEvent obj) + private void HandleDiagnosticCounter(ICounterPayload payload) { - IDictionary payloadVal = (IDictionary)(obj.PayloadValue(0)); - IDictionary payloadFields = (IDictionary)(payloadVal["Payload"]); - - // If it's not a counter we asked for, ignore it. - string name = payloadFields["Name"].ToString(); - if (!_counterList.Contains(obj.ProviderName, name)) - { - return; - } - // init providerEventState if this is the first time we've seen an event from this provider - if (!_providerEventStates.TryGetValue(obj.ProviderName, out ProviderEventState providerState)) + if (!_providerEventStates.TryGetValue(payload.Provider, out ProviderEventState providerState)) { providerState = new ProviderEventState() { - FirstReceiveTimestamp = obj.TimeStamp + FirstReceiveTimestamp = payload.Timestamp }; - _providerEventStates.Add(obj.ProviderName, providerState); + _providerEventStates.Add(payload.Provider, providerState); } // we give precedence to instrument events over diagnostic counter events. If we are seeing @@ -405,42 +82,35 @@ private void HandleDiagnosticCounter(TraceEvent obj) return; } - CounterPayload payload; - if (payloadFields["CounterType"].Equals("Sum")) - { - payload = new RatePayload( - obj.ProviderName, - name, - payloadFields["DisplayName"].ToString(), - payloadFields["DisplayUnits"].ToString(), - null, - (double)payloadFields["Increment"], - _interval, - obj.TimeStamp); - } - else - { - payload = new GaugePayload( - obj.ProviderName, - name, - payloadFields["DisplayName"].ToString(), - payloadFields["DisplayUnits"].ToString(), - null, - (double)payloadFields["Mean"], - obj.TimeStamp); - } - // If we saw the first event for this provider recently then a duplicate instrument event may still be // coming. We'll buffer this event for a while and then render it if it remains unduplicated for // a while. // This is all best effort, if we do show the DiagnosticCounter event and then an instrument event shows up - // later the renderer may obsserve some odd behavior like changes in the counter metadata, oddly timed reporting + // later the renderer may observe some odd behavior like changes in the counter metadata, oddly timed reporting // intervals, or counters that stop reporting. // I'm gambling this is good enough that the behavior will never be seen in practice, but if it is we could // either adjust the time delay or try to improve how the renderers handle it. - if (providerState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) >= obj.TimeStamp) + if (providerState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) >= payload.Timestamp) + { + _bufferedEvents.Enqueue((CounterPayload)payload); + } + else { - _bufferedEvents.Enqueue(payload); + CounterPayloadReceived((CounterPayload)payload); + } + } + + private void CounterPayloadReceived(CounterPayload payload) + { + if (payload is AggregatePercentilePayload aggregatePayload) + { + foreach (Quantile quantile in aggregatePayload.Quantiles) + { + (double key, double val) = quantile; + PercentilePayload percentilePayload = new(payload.Provider, payload.Name, payload.DisplayName, payload.Unit, AppendQuantile(payload.Metadata, $"Percentile={key * 100}"), val, payload.Timestamp); + _renderer.CounterPayloadReceived(percentilePayload, _pauseCmdSet); + } + } else { @@ -448,6 +118,8 @@ private void HandleDiagnosticCounter(TraceEvent obj) } } + private static string AppendQuantile(string tags, string quantile) => string.IsNullOrEmpty(tags) ? quantile : $"{tags},{quantile}"; + // when receiving DiagnosticCounter events we may have buffered them to wait for // duplicate instrument events. If we've waited long enough then we should remove // them from the buffer and render them. @@ -459,7 +131,7 @@ private void HandleBufferedEvents() while (_bufferedEvents.Count != 0) { CounterPayload payload = _bufferedEvents.Peek(); - ProviderEventState providerEventState = _providerEventStates[payload.ProviderName]; + ProviderEventState providerEventState = _providerEventStates[payload.Provider]; if (providerEventState.InstrumentEventObserved) { _bufferedEvents.Dequeue(); @@ -467,7 +139,7 @@ private void HandleBufferedEvents() else if (providerEventState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) < now) { _bufferedEvents.Dequeue(); - _renderer.CounterPayloadReceived(payload, _pauseCmdSet); + CounterPayloadReceived((CounterPayload)payload); } else { @@ -481,37 +153,7 @@ private void HandleBufferedEvents() } } - private void StopMonitor() - { - try - { - _session?.Stop(); - } - catch (EndOfStreamException ex) - { - // If the app we're monitoring exits abruptly, this may throw in which case we just swallow the exception and exit gracefully. - Debug.WriteLine($"[ERROR] {ex}"); - } - // We may time out if the process ended before we sent StopTracing command. We can just exit in that case. - catch (TimeoutException) - { - } - // On Unix platforms, we may actually get a PNSE since the pipe is gone with the process, and Runtime Client Library - // does not know how to distinguish a situation where there is no pipe to begin with, or where the process has exited - // before dotnet-counters and got rid of a pipe that once existed. - // Since we are catching this in StopMonitor() we know that the pipe once existed (otherwise the exception would've - // been thrown in StartMonitor directly) - catch (PlatformNotSupportedException) - { - } - // On non-abrupt exits, the socket may be already closed by the runtime and we won't be able to send a stop request through it. - catch (ServerNotAvailableException) - { - } - _renderer.Stop(); - } - - public async Task Monitor( + public async Task Monitor( CancellationToken ct, List counter_list, string counters, @@ -535,7 +177,7 @@ public async Task Monitor( ValidateNonNegative(maxTimeSeries, nameof(maxTimeSeries)); if (!ProcessLauncher.Launcher.HasChildProc && !CommandUtils.ValidateArgumentsForAttach(processId, name, diagnosticPort, out _processId)) { - return (int)ReturnCode.ArgumentError; + return ReturnCode.ArgumentError; } ct.Register(() => _shouldExit.TrySetResult((int)ReturnCode.Ok)); @@ -546,7 +188,7 @@ public async Task Monitor( bool useAnsi = vTerm.IsEnabled; if (holder == null) { - return (int)ReturnCode.Ok; + return ReturnCode.Ok; } try { @@ -554,38 +196,48 @@ public async Task Monitor( // the launch command may misinterpret app arguments as the old space separated // provider list so we need to ignore it in that case _counterList = ConfigureCounters(counters, _processId != 0 ? counter_list : null); - _ct = ct; - _interval = refreshInterval; - _maxHistograms = maxHistograms; - _maxTimeSeries = maxTimeSeries; _renderer = new ConsoleWriter(useAnsi); _diagnosticsClient = holder.Client; - _resumeRuntime = resumeRuntime; - _duration = duration; - int ret = await Start().ConfigureAwait(false); + _settings = new MetricsPipelineSettings(); + _settings.Duration = duration == TimeSpan.Zero ? Timeout.InfiniteTimeSpan : duration; + _settings.MaxHistograms = maxHistograms; + _settings.MaxTimeSeries = maxTimeSeries; + _settings.CounterIntervalSeconds = refreshInterval; + _settings.ResumeRuntime = resumeRuntime; + _settings.CounterGroups = GetEventPipeProviders(); + + bool useSharedSession = false; + if (_diagnosticsClient.GetProcessInfo().TryGetProcessClrVersion(out Version version)) + { + useSharedSession = version.Major >= 8 ? true : false; + } + _settings.UseSharedSession = useSharedSession; + + ReturnCode ret; + MetricsPipeline eventCounterPipeline = new(holder.Client, _settings, new[] { this }); + await using (eventCounterPipeline.ConfigureAwait(false)) + { + ret = await Start(eventCounterPipeline, ct).ConfigureAwait(false); + } ProcessLauncher.Launcher.Cleanup(); return ret; } catch (OperationCanceledException) { - try - { - _session.Stop(); - } - catch (Exception) { } // Swallow all exceptions for now. + //Cancellation token should automatically stop the session console.Out.WriteLine($"Complete"); - return (int)ReturnCode.Ok; + return ReturnCode.Ok; } } } catch (CommandLineErrorException e) { console.Error.WriteLine(e.Message); - return (int)ReturnCode.ArgumentError; + return ReturnCode.ArgumentError; } } - public async Task Collect( + public async Task Collect( CancellationToken ct, List counter_list, string counters, @@ -611,7 +263,7 @@ public async Task Collect( ValidateNonNegative(maxTimeSeries, nameof(maxTimeSeries)); if (!ProcessLauncher.Launcher.HasChildProc && !CommandUtils.ValidateArgumentsForAttach(processId, name, diagnosticPort, out _processId)) { - return (int)ReturnCode.ArgumentError; + return ReturnCode.ArgumentError; } ct.Register(() => _shouldExit.TrySetResult((int)ReturnCode.Ok)); @@ -629,17 +281,19 @@ public async Task Collect( // the launch command may misinterpret app arguments as the old space separated // provider list so we need to ignore it in that case _counterList = ConfigureCounters(counters, _processId != 0 ? counter_list : null); - _ct = ct; - _interval = refreshInterval; - _maxHistograms = maxHistograms; - _maxTimeSeries = maxTimeSeries; + _settings = new MetricsPipelineSettings(); + _settings.Duration = duration == TimeSpan.Zero ? Timeout.InfiniteTimeSpan : duration; + _settings.MaxHistograms = maxHistograms; + _settings.MaxTimeSeries = maxTimeSeries; + _settings.CounterIntervalSeconds = refreshInterval; + _settings.ResumeRuntime = resumeRuntime; + _settings.CounterGroups = GetEventPipeProviders(); _output = output; _diagnosticsClient = holder.Client; - _duration = duration; if (_output.Length == 0) { _console.Error.WriteLine("Output cannot be an empty string"); - return (int)ReturnCode.ArgumentError; + return ReturnCode.ArgumentError; } if (format == CountersExportFormat.csv) { @@ -663,27 +317,29 @@ public async Task Collect( else { _console.Error.WriteLine($"The output format {format} is not a valid output format."); - return (int)ReturnCode.ArgumentError; + return ReturnCode.ArgumentError; } - _resumeRuntime = resumeRuntime; - int ret = await Start().ConfigureAwait(false); + + ReturnCode ret; + MetricsPipeline eventCounterPipeline = new(holder.Client, _settings, new[] { this }); + await using (eventCounterPipeline.ConfigureAwait(false)) + { + ret = await Start(pipeline: eventCounterPipeline, ct).ConfigureAwait(false); + } + return ret; } catch (OperationCanceledException) { - try - { - _session.Stop(); - } - catch (Exception) { } // session.Stop() can throw if target application already stopped before we send the stop command. - return (int)ReturnCode.Ok; + //Cancellation token should automatically stop the session + return ReturnCode.Ok; } } } catch (CommandLineErrorException e) { console.Error.WriteLine(e.Message); - return (int)ReturnCode.ArgumentError; + return ReturnCode.ArgumentError; } } @@ -833,85 +489,21 @@ private static void ParseCounterProvider(string providerText, CounterSet counter } } - private EventPipeProvider[] GetEventPipeProviders() - { - // EventSources support EventCounter based metrics directly - IEnumerable eventCounterProviders = _counterList.Providers.Select( - providerName => new EventPipeProvider(providerName, EventLevel.Error, 0, new Dictionary() - {{ "EventCounterIntervalSec", _interval.ToString() }})); - - //System.Diagnostics.Metrics EventSource supports the new Meter/Instrument APIs - const long TimeSeriesValues = 0x2; - StringBuilder metrics = new(); - foreach (string provider in _counterList.Providers) + private EventPipeCounterGroup[] GetEventPipeProviders() => + _counterList.Providers.Select(provider => new EventPipeCounterGroup { - if (metrics.Length != 0) - { - metrics.Append(','); - } - if (_counterList.IncludesAllCounters(provider)) - { - metrics.Append(provider); - } - else - { - string[] providerCounters = _counterList.GetCounters(provider).Select(counter => $"{provider}\\{counter}").ToArray(); - metrics.Append(string.Join(',', providerCounters)); - } - } + ProviderName = provider, + CounterNames = _counterList.GetCounters(provider).ToArray() + }).ToArray(); - // Shared Session Id was added in 8.0 - older runtimes will not properly support it. - _sessionId = Guid.NewGuid().ToString(); - if (_diagnosticsClient.GetProcessInfo().TryGetProcessClrVersion(out Version version)) - { - _sessionId = version.Major >= 8 ? SharedSessionId : _sessionId; - } - - EventPipeProvider metricsEventSourceProvider = - new("System.Diagnostics.Metrics", EventLevel.Informational, TimeSeriesValues, - new Dictionary() - { - { "SessionId", _sessionId }, - { "Metrics", metrics.ToString() }, - { "RefreshInterval", _interval.ToString() }, - { "MaxTimeSeries", _maxTimeSeries.ToString() }, - { "MaxHistograms", _maxHistograms.ToString() }, - { "ClientId", _clientId } - } - ); - - return eventCounterProviders.Append(metricsEventSourceProvider).ToArray(); - } - - private bool Filter(string meterName, string instrumentName) - { - return _counterList.GetCounters(meterName).Contains(instrumentName) || _counterList.IncludesAllCounters(meterName); - } - - private Task Start() + private async Task Start(MetricsPipeline pipeline, CancellationToken token) { - EventPipeProvider[] providers = GetEventPipeProviders(); _renderer.Initialize(); - - Task monitorTask = new(() => { + Task monitorTask = new(async () => { try { - _session = _diagnosticsClient.StartEventPipeSession(providers, false, 10); - if (_resumeRuntime) - { - try - { - _diagnosticsClient.ResumeRuntime(); - } - catch (UnsupportedCommandException) - { - // Noop if the command is unknown since the target process is most likely a 3.1 app. - } - } - EventPipeEventSource source = new(_session.EventStream); - source.Dynamic.All += DynamicAllMonitor; - _renderer.EventPipeSourceConnected(); - source.Process(); + Task runAsyncTask = await pipeline.StartAsync(token).ConfigureAwait(false); + await runAsyncTask.ConfigureAwait(false); } catch (DiagnosticsClientException ex) { @@ -928,15 +520,8 @@ private Task Start() }); monitorTask.Start(); - bool shouldStopAfterDuration = _duration != default(TimeSpan); - Stopwatch durationStopwatch = null; - if (shouldStopAfterDuration) - { - durationStopwatch = Stopwatch.StartNew(); - } - - while (!_shouldExit.Task.Wait(250)) + while (!_shouldExit.Task.Wait(250, token)) { HandleBufferedEvents(); if (!Console.IsInputRedirected && Console.KeyAvailable) @@ -955,16 +540,107 @@ private Task Start() _pauseCmdSet = false; } } + } + + try + { + await pipeline.StopAsync(token).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + } + catch (PipelineException) + { + } + + return await _shouldExit.Task.ConfigureAwait(false); + } + + void ICountersLogger.Log(ICounterPayload payload) + { + if (_shouldExit.Task.IsCompleted) + { + return; + } - if (shouldStopAfterDuration && durationStopwatch.Elapsed >= _duration) + lock (this) + { + // If we are paused, ignore the event. + // There's a potential race here between the two tasks but not a huge deal if we miss by one event. + _renderer.ToggleStatus(_pauseCmdSet); + if (payload is ErrorPayload errorPayload) { - durationStopwatch.Stop(); - break; + // Several of the error messages used by Dotnet are specific to the tool; + // the error messages found in errorPayload.ErrorMessage are not tool-specific. + // This replaces the generic error messages with specific ones as-needed. + string errorMessage = string.Empty; + switch (errorPayload.EventType) + { + case EventType.HistogramLimitError: + errorMessage = $"Warning: Histogram tracking limit ({_settings.MaxHistograms}) reached. Not all data is being shown." + Environment.NewLine + + "The limit can be changed with --maxHistograms but will use more memory in the target process."; + break; + case EventType.TimeSeriesLimitError: + errorMessage = $"Warning: Time series tracking limit ({_settings.MaxTimeSeries}) reached. Not all data is being shown." + Environment.NewLine + + "The limit can be changed with --maxTimeSeries but will use more memory in the target process."; + break; + case EventType.ErrorTargetProcess: + case EventType.MultipleSessionsNotSupportedError: + case EventType.MultipleSessionsConfiguredIncorrectlyError: + case EventType.ObservableInstrumentCallbackError: + default: + errorMessage = errorPayload.ErrorMessage; + break; + } + + _renderer.SetErrorText(errorMessage); + + if (errorPayload.EventType.IsSessionStartupError()) + { + _shouldExit.TrySetResult(ReturnCode.SessionCreationError); + } + else if (errorPayload.EventType.IsTracingError()) + { + _shouldExit.TrySetResult(ReturnCode.TracingError); + } + else if (errorPayload.EventType.IsNonFatalError()) + { + // Don't need to exit for NonFatalError + } + else + { + _shouldExit.TrySetResult(ReturnCode.UnknownError); + } + } + else if (payload is CounterEndedPayload counterEnded) + { + _renderer.CounterStopped(counterEnded); + } + else if (payload.IsMeter) + { + MeterInstrumentEventObserved(payload.Provider, payload.Timestamp); + if (payload.EventType.IsValuePublishedEvent()) + { + CounterPayloadReceived((CounterPayload)payload); + } + } + else + { + HandleDiagnosticCounter(payload); } } + } + + public Task PipelineStarted(CancellationToken token) + { + _renderer.EventPipeSourceConnected(); + return Task.CompletedTask; + } - StopMonitor(); - return _shouldExit.Task; + public Task PipelineStopped(CancellationToken token) + { + _renderer.Stop(); + return Task.CompletedTask; } } } diff --git a/src/Tools/dotnet-counters/CounterPayload.cs b/src/Tools/dotnet-counters/CounterPayload.cs deleted file mode 100644 index a7863bd143..0000000000 --- a/src/Tools/dotnet-counters/CounterPayload.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Diagnostics.Tools.Counters -{ - public class CounterPayload - { - public CounterPayload(string providerName, string name, string displayName, string displayUnits, string tags, double value, DateTime timestamp, string type) - { - ProviderName = providerName; - Name = name; - Tags = tags; - Value = value; - Timestamp = timestamp; - CounterType = type; - } - - public string ProviderName { get; private set; } - public string Name { get; private set; } - public double Value { get; private set; } - public virtual string DisplayName { get; protected set; } - public string CounterType { get; private set; } - public DateTime Timestamp { get; private set; } - public string Tags { get; private set; } - } - - internal class GaugePayload : CounterPayload - { - public GaugePayload(string providerName, string name, string displayName, string displayUnits, string tags, double value, DateTime timestamp) : - base(providerName, name, displayName, displayUnits, tags, value, timestamp, "Metric") - { - // In case these properties are not provided, set them to appropriate values. - string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; - DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName; - } - } - - internal class RatePayload : CounterPayload - { - public RatePayload(string providerName, string name, string displayName, string displayUnits, string tags, double value, double intervalSecs, DateTime timestamp) : - base(providerName, name, displayName, displayUnits, tags, value, timestamp, "Rate") - { - // In case these properties are not provided, set them to appropriate values. - string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; - string unitsName = string.IsNullOrEmpty(displayUnits) ? "Count" : displayUnits; - string intervalName = intervalSecs.ToString() + " sec"; - DisplayName = $"{counterName} ({unitsName} / {intervalName})"; - } - } - - internal class PercentilePayload : CounterPayload - { - public PercentilePayload(string providerName, string name, string displayName, string displayUnits, string tags, double val, DateTime timestamp) : - base(providerName, name, displayName, displayUnits, tags, val, timestamp, "Metric") - { - // In case these properties are not provided, set them to appropriate values. - string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; - DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName; - } - } -} diff --git a/src/Tools/dotnet-counters/CounterPayloadExtensions.cs b/src/Tools/dotnet-counters/CounterPayloadExtensions.cs new file mode 100644 index 0000000000..974e054c96 --- /dev/null +++ b/src/Tools/dotnet-counters/CounterPayloadExtensions.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Diagnostics.Monitoring.EventPipe; + +namespace Microsoft.Diagnostics.Tools.Counters +{ + internal static class CounterPayloadExtensions + { + public static string GetDisplay(this ICounterPayload counterPayload) + { + if (!counterPayload.IsMeter) + { + string unit = counterPayload.Unit == "count" ? "Count" : counterPayload.Unit; + if (counterPayload.CounterType == CounterType.Rate) + { + return $"{counterPayload.DisplayName} ({unit} / {counterPayload.Series} sec)"; + } + if (!string.IsNullOrEmpty(counterPayload.Unit)) + { + return $"{counterPayload.DisplayName} ({unit})"; + } + } + + return $"{counterPayload.DisplayName}"; + } + } +} diff --git a/src/Tools/dotnet-counters/Exporters/CSVExporter.cs b/src/Tools/dotnet-counters/Exporters/CSVExporter.cs index cdf760edb4..eb4399de96 100644 --- a/src/Tools/dotnet-counters/Exporters/CSVExporter.cs +++ b/src/Tools/dotnet-counters/Exporters/CSVExporter.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Text; +using Microsoft.Diagnostics.Monitoring.EventPipe; namespace Microsoft.Diagnostics.Tools.Counters.Exporters { @@ -70,11 +71,11 @@ public void CounterPayloadReceived(CounterPayload payload, bool _) builder .Append(payload.Timestamp.ToString()).Append(',') - .Append(payload.ProviderName).Append(',') - .Append(payload.DisplayName); - if (!string.IsNullOrEmpty(payload.Tags)) + .Append(payload.Provider).Append(',') + .Append(payload.GetDisplay()); + if (!string.IsNullOrEmpty(payload.Metadata)) { - builder.Append('[').Append(payload.Tags.Replace(',', ';')).Append(']'); + builder.Append('[').Append(payload.Metadata.Replace(',', ';')).Append(']'); } builder.Append(',') .Append(payload.CounterType).Append(',') diff --git a/src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs b/src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs index 76f06931f7..8795ba4e73 100644 --- a/src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs +++ b/src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Microsoft.Diagnostics.Monitoring.EventPipe; namespace Microsoft.Diagnostics.Tools.Counters.Exporters { @@ -12,7 +13,7 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters /// ConsoleWriter is an implementation of ICounterRenderer for rendering the counter values in real-time /// to the console. This is the renderer for the `dotnet-counters monitor` command. /// - public class ConsoleWriter : ICounterRenderer + internal class ConsoleWriter : ICounterRenderer { /// Information about an observed provider. private class ObservedProvider @@ -257,9 +258,9 @@ public void CounterPayloadReceived(CounterPayload payload, bool pauseCmdSet) return; } - string providerName = payload.ProviderName; + string providerName = payload.Provider; string name = payload.Name; - string tags = payload.Tags; + string tags = payload.Metadata; bool redraw = false; if (!_providers.TryGetValue(providerName, out ObservedProvider provider)) @@ -270,7 +271,7 @@ public void CounterPayloadReceived(CounterPayload payload, bool pauseCmdSet) if (!provider.Counters.TryGetValue(name, out ObservedCounter counter)) { - string displayName = payload.DisplayName; + string displayName = payload.GetDisplay(); provider.Counters[name] = counter = new ObservedCounter(displayName); _maxNameLength = Math.Max(_maxNameLength, displayName.Length); if (tags != null) @@ -313,9 +314,9 @@ public void CounterStopped(CounterPayload payload) { lock (_lock) { - string providerName = payload.ProviderName; + string providerName = payload.Provider; string counterName = payload.Name; - string tags = payload.Tags; + string tags = payload.Metadata; if (!_providers.TryGetValue(providerName, out ObservedProvider provider)) { diff --git a/src/Tools/dotnet-counters/Exporters/ICounterRenderer.cs b/src/Tools/dotnet-counters/Exporters/ICounterRenderer.cs index d8389b2ec7..2bd4ca254f 100644 --- a/src/Tools/dotnet-counters/Exporters/ICounterRenderer.cs +++ b/src/Tools/dotnet-counters/Exporters/ICounterRenderer.cs @@ -1,9 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Diagnostics.Monitoring.EventPipe; + namespace Microsoft.Diagnostics.Tools.Counters.Exporters { - public interface ICounterRenderer + internal interface ICounterRenderer { void Initialize(); void EventPipeSourceConnected(); diff --git a/src/Tools/dotnet-counters/Exporters/JSONExporter.cs b/src/Tools/dotnet-counters/Exporters/JSONExporter.cs index 8abadbbeb7..ec571ad7ba 100644 --- a/src/Tools/dotnet-counters/Exporters/JSONExporter.cs +++ b/src/Tools/dotnet-counters/Exporters/JSONExporter.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Text; +using Microsoft.Diagnostics.Monitoring.EventPipe; namespace Microsoft.Diagnostics.Tools.Counters.Exporters { @@ -72,10 +73,10 @@ public void CounterPayloadReceived(CounterPayload payload, bool _) } builder .Append("{ \"timestamp\": \"").Append(DateTime.Now.ToString("u")).Append("\", ") - .Append(" \"provider\": \"").Append(JsonEscape(payload.ProviderName)).Append("\", ") - .Append(" \"name\": \"").Append(JsonEscape(payload.DisplayName)).Append("\", ") - .Append(" \"tags\": \"").Append(JsonEscape(payload.Tags)).Append("\", ") - .Append(" \"counterType\": \"").Append(JsonEscape(payload.CounterType)).Append("\", ") + .Append(" \"provider\": \"").Append(JsonEscape(payload.Provider)).Append("\", ") + .Append(" \"name\": \"").Append(JsonEscape(payload.GetDisplay())).Append("\", ") + .Append(" \"tags\": \"").Append(JsonEscape(payload.Metadata)).Append("\", ") + .Append(" \"counterType\": \"").Append(JsonEscape(payload.CounterType.ToString())).Append("\", ") .Append(" \"value\": ").Append(payload.Value.ToString(CultureInfo.InvariantCulture)).Append(" },"); } } diff --git a/src/Tools/dotnet-counters/Program.cs b/src/Tools/dotnet-counters/Program.cs index ec79a2e6ef..d1fde42e6f 100644 --- a/src/Tools/dotnet-counters/Program.cs +++ b/src/Tools/dotnet-counters/Program.cs @@ -21,7 +21,7 @@ public enum CountersExportFormat { csv, json }; internal static class Program { - private delegate Task CollectDelegate( + private delegate Task CollectDelegate( CancellationToken ct, List counter_list, string counters, @@ -37,7 +37,7 @@ private delegate Task CollectDelegate( int maxTimeSeries, TimeSpan duration); - private delegate Task MonitorDelegate( + private delegate Task MonitorDelegate( CancellationToken ct, List counter_list, string counters, diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs index 46ae594632..e7b4ae3752 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs @@ -57,11 +57,11 @@ public TestMetricsLogger(IDictionary> expectedCounte public IEnumerable Metrics => _metrics.Values; - public void Log(ICounterPayload metric) + public void Log(ICounterPayload payload) { - string key = CreateKey(metric); + string key = CreateKey(payload); - _metrics[key] = metric; + _metrics[key] = payload; // Complete the task source if the last expected key was removed. if (_expectedCounters.Remove(key) && _expectedCounters.Count == 0) diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs index 9634999aa0..b55ed5a64a 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs @@ -477,7 +477,7 @@ public ICounterPayload CreateNext(double value) // Add some variance between -5 and 5 milliseconds to simulate "real" timestamp _lastTimestamp = _lastTimestamp.Value.AddMilliseconds((10 * _random.NextDouble()) - 5); - return new CounterPayload( + return new EventCounterPayload( _lastTimestamp.Value, EventCounterConstants.RuntimeProviderName, EventCounterConstants.CpuUsageCounterName, @@ -486,6 +486,7 @@ public ICounterPayload CreateNext(double value) value, CounterType.Metric, actualInterval, + (int)_intervalSeconds, null); } } diff --git a/src/tests/dotnet-counters/CSVExporterTests.cs b/src/tests/dotnet-counters/CSVExporterTests.cs index 7c283ff4e3..342d367480 100644 --- a/src/tests/dotnet-counters/CSVExporterTests.cs +++ b/src/tests/dotnet-counters/CSVExporterTests.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.Diagnostics.Tools.Counters; using Microsoft.Diagnostics.Tools.Counters.Exporters; +using Microsoft.Diagnostics.Monitoring.EventPipe; using Xunit; namespace DotnetCounters.UnitTests @@ -25,7 +26,7 @@ public void IncrementingCounterTest() DateTime start = DateTime.Now; for (int i = 0; i < 100; i++) { - exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", "", "", i, 1, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", string.Empty, string.Empty, i, 1, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -63,7 +64,7 @@ public void CounterTest() DateTime start = DateTime.Now; for (int i = 0; i < 10; i++) { - exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", "", "", i, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", string.Empty, null, i, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -101,7 +102,7 @@ public void DifferentDisplayRateTest() DateTime start = DateTime.Now; for (int i = 0; i < 100; i++) { - exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", "", "", i, 60, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", string.Empty, null, i, 60, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -139,7 +140,7 @@ public void DisplayUnitsTest() DateTime start = DateTime.Now; for (int i = 0; i < 100; i++) { - exporter.CounterPayloadReceived(new RatePayload("myProvider", "allocRateGen", "Allocation Rate Gen", "MB", "", i, 60, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new RatePayload("myProvider", "allocRateGen", "Allocation Rate Gen", "MB", string.Empty, i, 60, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); diff --git a/src/tests/dotnet-counters/JSONExporterTests.cs b/src/tests/dotnet-counters/JSONExporterTests.cs index 379f699be3..d4daadbbcb 100644 --- a/src/tests/dotnet-counters/JSONExporterTests.cs +++ b/src/tests/dotnet-counters/JSONExporterTests.cs @@ -6,7 +6,9 @@ using Microsoft.Diagnostics.Tools.Counters; using Microsoft.Diagnostics.Tools.Counters.Exporters; using Newtonsoft.Json; +using Microsoft.Diagnostics.Monitoring.EventPipe; using Xunit; +using System.Collections.Generic; #pragma warning disable CA1507 // Use nameof to express symbol names @@ -26,7 +28,7 @@ public void IncrementingCounterTest() DateTime start = DateTime.Now; for (int i = 0; i < 10; i++) { - exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", "", "", 1, 1, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", string.Empty, string.Empty, 1, 1, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -57,7 +59,7 @@ public void CounterTest() DateTime start = DateTime.Now; for (int i = 0; i < 10; i++) { - exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", "", "", 1, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", string.Empty, string.Empty, 1, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -88,7 +90,7 @@ public void DisplayUnitsTest() DateTime start = DateTime.Now; for (int i = 0; i < 20; i++) { - exporter.CounterPayloadReceived(new GaugePayload("myProvider", "heapSize", "Heap Size", "MB", "", i, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new GaugePayload("myProvider", "heapSize", "Heap Size", "MB", string.Empty, i, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -122,7 +124,7 @@ public void ValidJSONFormatTest() DateTime start = DateTime.Now; for (int i = 0; i < 20; i++) { - exporter.CounterPayloadReceived(new RatePayload("myProvider", "heapSize", "Heap Size", "MB", "", 0, 60, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new RatePayload("myProvider", "heapSize", "Heap Size", "MB", string.Empty, 0, 60, start + TimeSpan.FromSeconds(i)), false); } exporter.Stop(); @@ -210,7 +212,7 @@ public void PercentilesTest() DateTime start = DateTime.Now; for (int i = 0; i < 10; i++) { - exporter.CounterPayloadReceived(new PercentilePayload("myProvider", "counterOne", "Counter One", "", "f=abc,Percentile=50", 1, start + TimeSpan.FromSeconds(i)), false); + exporter.CounterPayloadReceived(new PercentilePayload("myProvider", "counterOne", "Counter One", "", "f=abc,Percentile=50", 1, start + TimeSpan.FromSeconds(1)), false); } exporter.Stop(); From a14d42d1d6dcd08fd1d57c417e6f7d8592b8e1ee Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 13:21:58 +0000 Subject: [PATCH 46/82] [main] Update dependencies from dotnet/aspnetcore (#4316) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 45ac8876e1..21f77f42b2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - + https://github.com/dotnet/aspnetcore - 7ffeb436ad029d1e1012372b7bb345ad22770f09 + d4e5261cb47fd4f9b5e7a144108c9bb3f02c57d4 - + https://github.com/dotnet/aspnetcore - 7ffeb436ad029d1e1012372b7bb345ad22770f09 + d4e5261cb47fd4f9b5e7a144108c9bb3f02c57d4 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 84939e6d25..f658110aa9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23509.5 8.0.0-rtm.23509.5 - 8.0.0-rtm.23510.7 - 8.0.0-rtm.23510.7 + 8.0.0-rtm.23512.1 + 8.0.0-rtm.23512.1 8.0.100-rtm.23506.1 From e71971ee792f0797389b3fd5c51a06adf2feb048 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 13:23:18 +0000 Subject: [PATCH 47/82] [main] Update dependencies from dotnet/runtime (#4317) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 21f77f42b2..dd59d6f3f1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore d4e5261cb47fd4f9b5e7a144108c9bb3f02c57d4 - + https://github.com/dotnet/runtime - a9cc3c80fe43d19a38cacda4c1aecc51fb6eabb1 + 256bf22a3ddf920516752701da2b95e1847ff708 - + https://github.com/dotnet/runtime - a9cc3c80fe43d19a38cacda4c1aecc51fb6eabb1 + 256bf22a3ddf920516752701da2b95e1847ff708 https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index f658110aa9..c58b33bc94 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.450901 - 8.0.0-rtm.23509.5 - 8.0.0-rtm.23509.5 + 8.0.0-rtm.23511.16 + 8.0.0-rtm.23511.16 8.0.0-rtm.23512.1 8.0.0-rtm.23512.1 From f0fc5f8e2ce471f4c5e4afc41d2920a5f5ccd1b0 Mon Sep 17 00:00:00 2001 From: Lee Culver Date: Thu, 12 Oct 2023 11:59:35 -0700 Subject: [PATCH 48/82] !maddress misattributes memory to the GC (#4318) Fixes an issue reported by the GC team. On windows, VirtualQuery information (bounds of allocated segments) is very accurate in dumps because we have an entire segment of the dump format which describes it. On Linux, it seems like we smash together regions which came from two different calls of VirtualAlloc (well, the Linux equivalent). So, only use a heuristic of attributing regions of allocated memory on Windows. Additionally, we are 100% accurate in reporting every byte of memory that the GC allocates as of .Net 8. We shouldn't be ever using this heuristic to attribute any memory to the GC. --- .../NativeAddressHelper.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs index 7de6a766af..409c85bcf2 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs @@ -196,7 +196,25 @@ private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, b // by the other region kind. if (region.ClrMemoryKind == ClrMemoryKind.None) { - region.ClrMemoryKind = mem.Kind; + // On platforms other than Windows, we may not have accurate region begin/end + // locations. For example, with Windows dumps, we will get two distinct regions + // when one call to virtual alloc maps 0x10000-0x20000 and a different call to + // virtual alloc maps 0x20000-0x30000. On Linux, we seem to only get one region + // defined (0x10000-0x30000) even if that came from two different calls to + // the Linux equivalent of VirtualAlloc. Therefore, we only use this heuristic + // to tag memory on Windows to avoid accidently over-attributing memory to CLR. + // + // Finally, we actually get very accurate data about GC structures, so never + // use this heuristic to tag memory as belonging to the GC because we know it + // doesn't. + if (Target.OperatingSystem == OSPlatform.Windows + && mem.Kind != ClrMemoryKind.GCHeap + && mem.Kind != ClrMemoryKind.GCHeapReserve + && mem.Kind != ClrMemoryKind.GCBookkeeping + && mem.Kind != ClrMemoryKind.GCHeapToBeFreed) + { + region.ClrMemoryKind = mem.Kind; + } } DescribedRegion middleRegion = new(region) From 0483c7f0cf898efdeb929b611e8cb2363005c170 Mon Sep 17 00:00:00 2001 From: Joe Schmitt <1146681+schmittjoseph@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:25:15 -0700 Subject: [PATCH 49/82] Handle log messages with null state (#4319) This PR addresses an issue were certain log messages could be dropped: - A log message with placeholders can have an entirely null state (e.g. `_logger.LogInformational("Test {A}", args: null)`). In this scenario, the event source args payload will only contain the original format string, not the null args. - The current code assumes all placeholders will exist in the event source args payload, which results in an exception being thrown and the log message being dropped. --- .../Logs/EventLogsPipeline.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs index eec6facb48..063bd342fb 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs @@ -137,7 +137,10 @@ protected override Task OnEventSourceAvailable(EventPipeEventSource eventSource, object[] args = new object[formatter.ValueNames.Count]; for (int i = 0; i < args.Length; i++) { - args[i] = message.GetProperty(formatter.ValueNames[i]).GetString(); + if (message.TryGetProperty(formatter.ValueNames[i], out JsonElement value)) + { + args[i] = value.GetString(); + } } //We want to propagate the timestamp to the underlying logger, but that's not part of the ILogger interface. From 303b277ccea7ed6e55ef4b1adb1b5dcb715df283 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 13:27:22 +0000 Subject: [PATCH 50/82] [main] Update dependencies from dotnet/aspnetcore (#4320) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index dd59d6f3f1..9efc520a51 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - + https://github.com/dotnet/aspnetcore - d4e5261cb47fd4f9b5e7a144108c9bb3f02c57d4 + 775bf48a1d908496d1947db71ec98012d664dc54 - + https://github.com/dotnet/aspnetcore - d4e5261cb47fd4f9b5e7a144108c9bb3f02c57d4 + 775bf48a1d908496d1947db71ec98012d664dc54 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index c58b33bc94..f484890e52 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23511.16 8.0.0-rtm.23511.16 - 8.0.0-rtm.23512.1 - 8.0.0-rtm.23512.1 + 8.0.0-rtm.23512.20 + 8.0.0-rtm.23512.20 8.0.100-rtm.23506.1 From 851fc024da3797c5a3c3942f619bfd7a878fe273 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 13:28:10 +0000 Subject: [PATCH 51/82] [main] Update dependencies from microsoft/clrmd (#4321) [main] Update dependencies from microsoft/clrmd --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9efc520a51..f7fbceb29b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore a3b341f9e61c8d8e832c4acfeb5b3a2305e51bcc - + https://github.com/microsoft/clrmd - 903207ffe9dbac775a2a70d54980fc03abad4cb1 + 6eae6255dfa3bdc3a855fc6e578a3a192c1eb2ab - + https://github.com/microsoft/clrmd - 903207ffe9dbac775a2a70d54980fc03abad4cb1 + 6eae6255dfa3bdc3a855fc6e578a3a192c1eb2ab diff --git a/eng/Versions.props b/eng/Versions.props index f484890e52..7124884cf0 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.1.451001 + 3.1.451202 16.11.27-beta1.23180.1 3.0.7 6.0.0 From 7f54d71b4989e535b980306d0a8169defefc2873 Mon Sep 17 00:00:00 2001 From: Mikhail Kurinnoi Date: Fri, 13 Oct 2023 21:12:25 +0300 Subject: [PATCH 52/82] Initial Patch for RISC-V support. (#4322) Based on CoreCLR changes. CC @clamp03 @wscho77 @HJLeee @JongHeonChoi @t-mustafin @gbalykov --- CMakeLists.txt | 13 + src/SOS/Strike/CMakeLists.txt | 5 + src/SOS/Strike/platform/datatarget.cpp | 2 +- src/SOS/extensions/hostcoreclr.cpp | 4 + src/SOS/lldbplugin/CMakeLists.txt | 7 + src/dbgshim/debugshim.cpp | 2 + src/shared/dbgutil/elfreader.cpp | 2 + src/shared/gcdump/gcinfodecoder.cpp | 4 +- src/shared/inc/clrnt.h | 57 +++ src/shared/inc/crosscomp.h | 143 ++++++++ src/shared/inc/dbgtargetcontext.h | 70 ++++ src/shared/inc/gcinfodecoder.h | 10 +- src/shared/inc/gcinfotypes.h | 58 ++- src/shared/inc/pedecoder.h | 2 + src/shared/inc/regdisp.h | 68 +++- src/shared/inc/switches.h | 2 +- src/shared/inc/volatile.h | 6 +- src/shared/pal/inc/pal.h | 167 ++++++++- src/shared/pal/inc/rt/intsafe.h | 2 +- src/shared/pal/inc/rt/ntimage.h | 7 + src/shared/pal/inc/rt/palrt.h | 2 +- src/shared/pal/inc/unixasmmacros.inc | 2 + src/shared/pal/inc/unixasmmacrosriscv64.inc | 341 ++++++++++++++++++ src/shared/pal/prebuilt/inc/cordebug.h | 71 +++- src/shared/pal/src/CMakeLists.txt | 2 + .../pal/src/arch/riscv64/asmconstants.h | 135 +++++++ src/shared/pal/src/arch/riscv64/context2.S | 242 +++++++++++++ src/shared/pal/src/arch/riscv64/debugbreak.S | 8 + src/shared/pal/src/arch/riscv64/processor.cpp | 21 ++ src/shared/pal/src/misc/sysinfo.cpp | 9 +- 30 files changed, 1429 insertions(+), 35 deletions(-) create mode 100644 src/shared/pal/inc/unixasmmacrosriscv64.inc create mode 100644 src/shared/pal/src/arch/riscv64/asmconstants.h create mode 100644 src/shared/pal/src/arch/riscv64/context2.S create mode 100644 src/shared/pal/src/arch/riscv64/debugbreak.S create mode 100644 src/shared/pal/src/arch/riscv64/processor.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 488392bd33..c93eba23ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,11 @@ elseif (CLR_CMAKE_HOST_ARCH_MIPS64) add_definitions(-DMIPS64) add_definitions(-D_WIN64) add_definitions(-DBIT64=1) +elseif (CLR_CMAKE_HOST_ARCH_RISCV64) + add_definitions(-D_RISCV64_) + add_definitions(-DRISCV64) + add_definitions(-D_WIN64) + add_definitions(-DBIT64=1) else () clr_unknown_arch() endif () @@ -143,6 +148,14 @@ elseif (CLR_CMAKE_TARGET_ARCH_MIPS64) add_definitions(-DDBG_TARGET_64BIT=1) add_definitions(-DDBG_TARGET_WIN64=1) add_definitions(-DFEATURE_MULTIREG_RETURN) +elseif (CLR_CMAKE_TARGET_ARCH_RISCV64) + add_definitions(-DDBG_TARGET_RISCV64_UNIX) + add_definitions(-D_TARGET_RISCV64_=1) + add_definitions(-D_TARGET_64BIT_=1) + add_definitions(-DDBG_TARGET_RISCV64=1) + add_definitions(-DDBG_TARGET_64BIT=1) + add_definitions(-DDBG_TARGET_WIN64=1) + add_definitions(-DFEATURE_MULTIREG_RETURN) else () clr_unknown_arch() endif (CLR_CMAKE_TARGET_ARCH_AMD64) diff --git a/src/SOS/Strike/CMakeLists.txt b/src/SOS/Strike/CMakeLists.txt index d72a5aa6b5..52456d7387 100644 --- a/src/SOS/Strike/CMakeLists.txt +++ b/src/SOS/Strike/CMakeLists.txt @@ -64,6 +64,11 @@ elseif(CLR_CMAKE_HOST_ARCH_MIPS64) add_definitions(-D_TARGET_WIN64_=1) add_definitions(-DDBG_TARGET_64BIT) add_definitions(-DDBG_TARGET_WIN64=1) +elseif(CLR_CMAKE_HOST_ARCH_RISCV64) + add_definitions(-DSOS_TARGET_RISCV64=1) + add_definitions(-D_TARGET_WIN64_=1) + add_definitions(-DDBG_TARGET_64BIT) + add_definitions(-DDBG_TARGET_WIN64=1) endif() add_definitions(-DSTRIKE) diff --git a/src/SOS/Strike/platform/datatarget.cpp b/src/SOS/Strike/platform/datatarget.cpp index e575121ad3..eb22b9397b 100644 --- a/src/SOS/Strike/platform/datatarget.cpp +++ b/src/SOS/Strike/platform/datatarget.cpp @@ -99,7 +99,7 @@ HRESULT STDMETHODCALLTYPE DataTarget::GetPointerSize( /* [out] */ ULONG32 *size) { -#if defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64) || defined(SOS_TARGET_MIPS64) +#if defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64) || defined(SOS_TARGET_MIPS64) || defined(SOS_TARGET_RISCV64) *size = 8; #elif defined(SOS_TARGET_ARM) || defined(SOS_TARGET_X86) *size = 4; diff --git a/src/SOS/extensions/hostcoreclr.cpp b/src/SOS/extensions/hostcoreclr.cpp index db940bbf5b..025d6a3f9e 100644 --- a/src/SOS/extensions/hostcoreclr.cpp +++ b/src/SOS/extensions/hostcoreclr.cpp @@ -85,6 +85,8 @@ namespace RuntimeHostingConstants "DOTNET_ROOT_ARM"; #elif defined(HOST_ARM64) "DOTNET_ROOT_ARM64"; +#elif defined(HOST_RISCV64) + "DOTNET_ROOT_RISCV64"; #else "Error"; #error Hosting layer doesn't support target arch @@ -105,6 +107,8 @@ namespace RuntimeHostingConstants "/etc/dotnet/install_location_arm"; #elif defined(HOST_ARM64) "/etc/dotnet/install_location_arm64"; +#elif defined(HOST_RISCV64) + "/etc/dotnet/install_location_riscv64"; #else "ERROR"; #error Hosting layer doesn't support target arch diff --git a/src/SOS/lldbplugin/CMakeLists.txt b/src/SOS/lldbplugin/CMakeLists.txt index 8dd487fd97..49936d30b4 100644 --- a/src/SOS/lldbplugin/CMakeLists.txt +++ b/src/SOS/lldbplugin/CMakeLists.txt @@ -50,6 +50,13 @@ elseif(CLR_CMAKE_HOST_ARCH_MIPS64) add_definitions(-DDBG_TARGET_WIN64=1) add_definitions(-DBIT64) SET(REQUIRE_LLDBPLUGIN false) +elseif(CLR_CMAKE_HOST_ARCH_RISCV64) + add_definitions(-D_TARGET_RISCV64_=1) + add_definitions(-DDBG_TARGET_64BIT=1) + add_definitions(-DDBG_TARGET_RISCV64=1) + add_definitions(-DDBG_TARGET_WIN64=1) + add_definitions(-DBIT64) + SET(REQUIRE_LLDBPLUGIN false) endif() if(NOT $ENV{LLVM_HOME} STREQUAL "") diff --git a/src/dbgshim/debugshim.cpp b/src/dbgshim/debugshim.cpp index efddfaed60..bc480ee803 100644 --- a/src/dbgshim/debugshim.cpp +++ b/src/dbgshim/debugshim.cpp @@ -756,6 +756,8 @@ HRESULT CLRDebuggingImpl::FormatLongDacModuleName(_Inout_updates_z_(cchBuffer) W const WCHAR* pHostArch = W("arm"); #elif defined(HOST_ARM64) const WCHAR* pHostArch = W("arm64"); +#elif defined(HOST_RISCV64) + const WCHAR* pHostArch = W("riscv64"); #else _ASSERTE(!"Unknown host arch"); return E_NOTIMPL; diff --git a/src/shared/dbgutil/elfreader.cpp b/src/shared/dbgutil/elfreader.cpp index 99bf785a4d..4b5f7347e3 100644 --- a/src/shared/dbgutil/elfreader.cpp +++ b/src/shared/dbgutil/elfreader.cpp @@ -846,6 +846,8 @@ Elf64_Ehdr::Elf64_Ehdr() e_machine = EM_AARCH64; #elif defined(TARGET_LOONGARCH64) e_machine = EM_LOONGARCH; +#elif defined(TARGET_RISCV64) + e_machine = EM_RISCV; #endif e_flags = 0; e_version = 1; diff --git a/src/shared/gcdump/gcinfodecoder.cpp b/src/shared/gcdump/gcinfodecoder.cpp index 6833af4daa..c8e1bf3a39 100644 --- a/src/shared/gcdump/gcinfodecoder.cpp +++ b/src/shared/gcdump/gcinfodecoder.cpp @@ -520,13 +520,13 @@ bool GcInfoDecoder::GetIsVarArg() return m_IsVarArg; } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) bool GcInfoDecoder::HasTailCalls() { _ASSERTE( m_Flags & DECODE_HAS_TAILCALLS ); return m_HasTailCalls; } -#endif // TARGET_ARM || TARGET_ARM64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_RISCV64 bool GcInfoDecoder::WantsReportOnlyLeaf() { diff --git a/src/shared/inc/clrnt.h b/src/shared/inc/clrnt.h index e12c588700..bd541d4284 100644 --- a/src/shared/inc/clrnt.h +++ b/src/shared/inc/clrnt.h @@ -1029,4 +1029,61 @@ RtlVirtualUnwind( #endif +#ifdef TARGET_RISCV64 +#include "daccess.h" + +#define UNW_FLAG_NHANDLER 0x0 /* any handler */ +#define UNW_FLAG_EHANDLER 0x1 /* filter handler */ +#define UNW_FLAG_UHANDLER 0x2 /* unwind handler */ + +// This function returns the RVA of the end of the function (exclusive, so one byte after the actual end) +// using the unwind info on ARM64. (see ExternalAPIs\Win9CoreSystem\inc\winnt.h) +FORCEINLINE +ULONG64 +RtlpGetFunctionEndAddress ( + _In_ PT_RUNTIME_FUNCTION FunctionEntry, + _In_ ULONG64 ImageBase + ) +{ + ULONG64 FunctionLength; + + FunctionLength = FunctionEntry->UnwindData; + if ((FunctionLength & 3) != 0) { + FunctionLength = (FunctionLength >> 2) & 0x7ff; + } else { + memcpy(&FunctionLength, (void*)(ImageBase + FunctionLength), sizeof(UINT32)); + FunctionLength &= 0x3ffff; + } + + return FunctionEntry->BeginAddress + 4 * FunctionLength; +} + +#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ((FunctionEntry)->BeginAddress) +#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = (address)) + +#define RUNTIME_FUNCTION__EndAddress(FunctionEntry, ImageBase) (RtlpGetFunctionEndAddress(FunctionEntry, (ULONG64)(ImageBase))) + +#define RUNTIME_FUNCTION__SetUnwindInfoAddress(prf,address) do { (prf)->UnwindData = (address); } while (0) + +typedef struct _UNWIND_INFO { + // dummy +} UNWIND_INFO, *PUNWIND_INFO; + +EXTERN_C +NTSYSAPI +PEXCEPTION_ROUTINE +NTAPI +RtlVirtualUnwind( + IN ULONG HandlerType, + IN ULONG64 ImageBase, + IN ULONG64 ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PVOID *HandlerData, + OUT PULONG64 EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ); + +#endif // TARGET_RISCV64 + #endif // CLRNT_H_ diff --git a/src/shared/inc/crosscomp.h b/src/shared/inc/crosscomp.h index 84b6f81606..01ec1ff928 100644 --- a/src/shared/inc/crosscomp.h +++ b/src/shared/inc/crosscomp.h @@ -382,6 +382,147 @@ enum #endif // TARGET_ARM64 && !HOST_ARM64 +#elif defined(HOST_AMD64) && defined(TARGET_RISCV64) // Host amd64 managing RISCV64 related code + +#ifndef CROSS_COMPILE +#define CROSS_COMPILE +#endif + +// +// Specify the number of breakpoints and watchpoints that the OS +// will track. +// + +#define RISCV64_MAX_BREAKPOINTS 8 +#define RISCV64_MAX_WATCHPOINTS 2 + +#define CONTEXT_UNWOUND_TO_CALL 0x20000000 + +typedef struct DECLSPEC_ALIGN(16) _T_CONTEXT { + + // + // Control flags. + // + + /* +0x000 */ DWORD ContextFlags; + + // + // Integer registers + // + DWORD64 R0; + DWORD64 Ra; + DWORD64 Sp; + DWORD64 Gp; + DWORD64 Tp; + DWORD64 T0; + DWORD64 T1; + DWORD64 T2; + DWORD64 Fp; + DWORD64 S1; + DWORD64 A0; + DWORD64 A1; + DWORD64 A2; + DWORD64 A3; + DWORD64 A4; + DWORD64 A5; + DWORD64 A6; + DWORD64 A7; + DWORD64 S2; + DWORD64 S3; + DWORD64 S4; + DWORD64 S5; + DWORD64 S6; + DWORD64 S7; + DWORD64 S8; + DWORD64 S9; + DWORD64 S10; + DWORD64 S11; + DWORD64 T3; + DWORD64 T4; + DWORD64 T5; + DWORD64 T6; + DWORD64 Pc; + + // + // Floating Point Registers + // + //TODO-RISCV64: support the SIMD. + ULONGLONG F[32]; + DWORD Fcsr; +} T_CONTEXT, *PT_CONTEXT; + +// _IMAGE_RISCV64_RUNTIME_FUNCTION_ENTRY (see ExternalAPIs\Win9CoreSystem\inc\winnt.h) +typedef struct _T_RUNTIME_FUNCTION { + DWORD BeginAddress; + union { + DWORD UnwindData; + struct { + DWORD Flag : 2; + DWORD FunctionLength : 11; + DWORD RegF : 3; + DWORD RegI : 4; + DWORD H : 1; + DWORD CR : 2; + DWORD FrameSize : 9; + } PackedUnwindData; + }; +} T_RUNTIME_FUNCTION, *PT_RUNTIME_FUNCTION; + +// +// Define exception dispatch context structure. +// + +typedef struct _T_DISPATCHER_CONTEXT { + DWORD64 ControlPc; + DWORD64 ImageBase; + PT_RUNTIME_FUNCTION FunctionEntry; + DWORD64 EstablisherFrame; + DWORD64 TargetPc; + PCONTEXT ContextRecord; + PEXCEPTION_ROUTINE LanguageHandler; + PVOID HandlerData; + PVOID HistoryTable; + DWORD ScopeIndex; + BOOLEAN ControlPcIsUnwound; + PBYTE NonVolatileRegisters; +} T_DISPATCHER_CONTEXT, *PT_DISPATCHER_CONTEXT; + +// +// Nonvolatile context pointer record. +// + +typedef struct _T_KNONVOLATILE_CONTEXT_POINTERS { + + PDWORD64 S1; + PDWORD64 S2; + PDWORD64 S3; + PDWORD64 S4; + PDWORD64 S5; + PDWORD64 S6; + PDWORD64 S7; + PDWORD64 S8; + PDWORD64 S9; + PDWORD64 S10; + PDWORD64 S11; + PDWORD64 Fp; + PDWORD64 Gp; + PDWORD64 Tp; + PDWORD64 Ra; + + PDWORD64 F8; + PDWORD64 F9; + PDWORD64 F18; + PDWORD64 F19; + PDWORD64 F20; + PDWORD64 F21; + PDWORD64 F22; + PDWORD64 F23; + PDWORD64 F24; + PDWORD64 F25; + PDWORD64 F26; + PDWORD64 F27; +} T_KNONVOLATILE_CONTEXT_POINTERS, *PT_KNONVOLATILE_CONTEXT_POINTERS; + #else #define T_CONTEXT CONTEXT @@ -426,6 +567,8 @@ enum #define DAC_CS_NATIVE_DATA_SIZE 96 #elif defined(TARGET_LINUX) && defined(TARGET_S390X) #define DAC_CS_NATIVE_DATA_SIZE 96 +#elif defined(TARGET_LINUX) && defined(TARGET_RISCV64) +#define DAC_CS_NATIVE_DATA_SIZE 96 #elif defined(TARGET_NETBSD) && defined(TARGET_AMD64) #define DAC_CS_NATIVE_DATA_SIZE 96 #elif defined(TARGET_NETBSD) && defined(TARGET_ARM) diff --git a/src/shared/inc/dbgtargetcontext.h b/src/shared/inc/dbgtargetcontext.h index e4fd6e4bf5..4970caed05 100644 --- a/src/shared/inc/dbgtargetcontext.h +++ b/src/shared/inc/dbgtargetcontext.h @@ -48,6 +48,8 @@ #define DTCONTEXT_IS_ARM #elif defined (TARGET_ARM64) #define DTCONTEXT_IS_ARM64 +#elif defined (TARGET_RISCV64) +#define DTCONTEXT_IS_RISCV64 #endif #if defined(DTCONTEXT_IS_X86) @@ -447,6 +449,74 @@ typedef DECLSPEC_ALIGN(16) struct { } DT_CONTEXT; +#elif defined(DTCONTEXT_IS_RISCV64) + +#define DT_CONTEXT_RISCV64 0x01000000L + +#define DT_CONTEXT_CONTROL (DT_CONTEXT_RISCV64 | 0x1L) +#define DT_CONTEXT_INTEGER (DT_CONTEXT_RISCV64 | 0x2L) +#define DT_CONTEXT_FLOATING_POINT (DT_CONTEXT_RISCV64 | 0x4L) +#define DT_CONTEXT_DEBUG_REGISTERS (DT_CONTEXT_RISCV64 | 0x8L) + +#define DT_CONTEXT_FULL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT) +#define DT_CONTEXT_ALL (DT_CONTEXT_CONTROL | DT_CONTEXT_INTEGER | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_DEBUG_REGISTERS) + +#define DT_RISCV64_MAX_BREAKPOINTS 8 +#define DT_RISCV64_MAX_WATCHPOINTS 2 + +typedef struct DECLSPEC_ALIGN(16) { + // + // Control flags. + // + + /* +0x000 */ DWORD ContextFlags; + + // + // Integer registers + // + DWORD64 R0; + DWORD64 Ra; + DWORD64 Sp; + DWORD64 Gp; + DWORD64 Tp; + DWORD64 T0; + DWORD64 T1; + DWORD64 T2; + DWORD64 Fp; + DWORD64 S1; + DWORD64 A0; + DWORD64 A1; + DWORD64 A2; + DWORD64 A3; + DWORD64 A4; + DWORD64 A5; + DWORD64 A6; + DWORD64 A7; + DWORD64 S2; + DWORD64 S3; + DWORD64 S4; + DWORD64 S5; + DWORD64 S6; + DWORD64 S7; + DWORD64 S8; + DWORD64 S9; + DWORD64 S10; + DWORD64 S11; + DWORD64 T3; + DWORD64 T4; + DWORD64 T5; + DWORD64 T6; + DWORD64 Pc; + + // + // Floating Point Registers + // + ULONGLONG F[32]; + DWORD Fcsr; +} DT_CONTEXT; + +static_assert(sizeof(DT_CONTEXT) == sizeof(T_CONTEXT), "DT_CONTEXT size must equal the T_CONTEXT size"); + #else #error Unsupported platform #endif diff --git a/src/shared/inc/gcinfodecoder.h b/src/shared/inc/gcinfodecoder.h index 13b9575630..6ccdbe4aae 100644 --- a/src/shared/inc/gcinfodecoder.h +++ b/src/shared/inc/gcinfodecoder.h @@ -212,7 +212,7 @@ enum GcInfoDecoderFlags DECODE_EDIT_AND_CONTINUE = 0x800, DECODE_REVERSE_PINVOKE_VAR = 0x1000, DECODE_RETURN_KIND = 0x2000, -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) DECODE_HAS_TAILCALLS = 0x4000, #endif // TARGET_ARM || TARGET_ARM64 }; @@ -231,7 +231,7 @@ enum GcInfoHeaderFlags GC_INFO_HAS_STACK_BASE_REGISTER = 0x40, #ifdef TARGET_AMD64 GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80, -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) GC_INFO_HAS_TAILCALLS = 0x80, #endif // TARGET_AMD64 GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS = 0x100, @@ -536,9 +536,9 @@ class GcInfoDecoder bool HasMethodTableGenericsInstContext(); bool GetIsVarArg(); bool WantsReportOnlyLeaf(); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) bool HasTailCalls(); -#endif // TARGET_ARM || TARGET_ARM64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_RISCV64 ReturnKind GetReturnKind(); UINT32 GetCodeLength(); UINT32 GetStackBaseRegister(); @@ -561,7 +561,7 @@ class GcInfoDecoder bool m_GenericSecretParamIsMT; #ifdef TARGET_AMD64 bool m_WantsReportOnlyLeaf; -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) bool m_HasTailCalls; #endif // TARGET_AMD64 INT32 m_SecurityObjectStackSlot; diff --git a/src/shared/inc/gcinfotypes.h b/src/shared/inc/gcinfotypes.h index 1dd9372900..fc409d3700 100644 --- a/src/shared/inc/gcinfotypes.h +++ b/src/shared/inc/gcinfotypes.h @@ -155,7 +155,7 @@ struct GcStackSlot // 10 RT_ByRef // 11 RT_Unset -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) // Slim Header: @@ -772,6 +772,62 @@ void FASTCALL decodeCallPattern(int pattern, #define LIVESTATE_RLE_RUN_ENCBASE 2 #define LIVESTATE_RLE_SKIP_ENCBASE 4 +#elif defined(TARGET_RISCV64) +#ifndef TARGET_POINTER_SIZE +#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target +#endif +#define NUM_NORM_CODE_OFFSETS_PER_CHUNK (64) +#define NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2 (6) +#define NORMALIZE_STACK_SLOT(x) ((x)>>3) // GC Pointers are 8-bytes aligned +#define DENORMALIZE_STACK_SLOT(x) ((x)<<3) +#define NORMALIZE_CODE_LENGTH(x) ((x)>>2) // All Instructions are 4 bytes long +#define DENORMALIZE_CODE_LENGTH(x) ((x)<<2) +#define NORMALIZE_STACK_BASE_REGISTER(x) ((x)^8) // Encode Frame pointer X8 as zero +#define DENORMALIZE_STACK_BASE_REGISTER(x) ((x)^8) +#define NORMALIZE_SIZE_OF_STACK_AREA(x) ((x)>>3) +#define DENORMALIZE_SIZE_OF_STACK_AREA(x) ((x)<<3) +#define CODE_OFFSETS_NEED_NORMALIZATION 0 +#define NORMALIZE_CODE_OFFSET(x) (x) // Instructions are 4 bytes long, but the safe-point +#define DENORMALIZE_CODE_OFFSET(x) (x) // offsets are encoded with a -1 adjustment. +#define NORMALIZE_REGISTER(x) (x) +#define DENORMALIZE_REGISTER(x) (x) +#define NORMALIZE_NUM_SAFE_POINTS(x) (x) +#define DENORMALIZE_NUM_SAFE_POINTS(x) (x) +#define NORMALIZE_NUM_INTERRUPTIBLE_RANGES(x) (x) +#define DENORMALIZE_NUM_INTERRUPTIBLE_RANGES(x) (x) + +#define PSP_SYM_STACK_SLOT_ENCBASE 6 +#define GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE 6 +#define SECURITY_OBJECT_STACK_SLOT_ENCBASE 6 +#define GS_COOKIE_STACK_SLOT_ENCBASE 6 +#define CODE_LENGTH_ENCBASE 8 +#define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 +#define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 4 +#define STACK_BASE_REGISTER_ENCBASE 2 +// FP encoded as 0, SP as 2?? +#define SIZE_OF_STACK_AREA_ENCBASE 3 +#define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 4 +#define SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE 4 +#define REVERSE_PINVOKE_FRAME_ENCBASE 6 +#define NUM_REGISTERS_ENCBASE 3 +#define NUM_STACK_SLOTS_ENCBASE 2 +#define NUM_UNTRACKED_SLOTS_ENCBASE 1 +#define NORM_PROLOG_SIZE_ENCBASE 5 +#define NORM_EPILOG_SIZE_ENCBASE 3 +#define NORM_CODE_OFFSET_DELTA_ENCBASE 3 +#define INTERRUPTIBLE_RANGE_DELTA1_ENCBASE 6 +#define INTERRUPTIBLE_RANGE_DELTA2_ENCBASE 6 +#define REGISTER_ENCBASE 3 +#define REGISTER_DELTA_ENCBASE 2 +#define STACK_SLOT_ENCBASE 6 +#define STACK_SLOT_DELTA_ENCBASE 4 +#define NUM_SAFE_POINTS_ENCBASE 3 +#define NUM_INTERRUPTIBLE_RANGES_ENCBASE 1 +#define NUM_EH_CLAUSES_ENCBASE 2 +#define POINTER_SIZE_ENCBASE 3 +#define LIVESTATE_RLE_RUN_ENCBASE 2 +#define LIVESTATE_RLE_SKIP_ENCBASE 4 + #else #ifndef TARGET_X86 diff --git a/src/shared/inc/pedecoder.h b/src/shared/inc/pedecoder.h index bb4ea867db..769c0b0116 100644 --- a/src/shared/inc/pedecoder.h +++ b/src/shared/inc/pedecoder.h @@ -81,6 +81,8 @@ inline CHECK CheckOverflow(RVA value1, COUNT_T value2) #define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_ARM64 #elif defined(TARGET_S390X) #define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_UNKNOWN +#elif defined(TARGET_RISCV64) +#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_RISCV64 #else #error "port me" #endif diff --git a/src/shared/inc/regdisp.h b/src/shared/inc/regdisp.h index b031b0a80b..02e67dbdd4 100644 --- a/src/shared/inc/regdisp.h +++ b/src/shared/inc/regdisp.h @@ -184,11 +184,38 @@ typedef struct _Arm64VolatileContextPointer }; } Arm64VolatileContextPointer; #endif //TARGET_ARM64 + +#if defined(TARGET_RISCV64) +typedef struct _RiscV64VolatileContextPointer +{ + PDWORD64 R0; + PDWORD64 A0; + PDWORD64 A1; + PDWORD64 A2; + PDWORD64 A3; + PDWORD64 A4; + PDWORD64 A5; + PDWORD64 A6; + PDWORD64 A7; + PDWORD64 T0; + PDWORD64 T1; + PDWORD64 T2; + PDWORD64 T3; + PDWORD64 T4; + PDWORD64 T5; + PDWORD64 T6; +} RiscV64VolatileContextPointer; +#endif + struct REGDISPLAY : public REGDISPLAY_BASE { #ifdef TARGET_ARM64 Arm64VolatileContextPointer volatileCurrContextPointers; #endif +#ifdef TARGET_RISCV64 + RiscV64VolatileContextPointer volatileCurrContextPointers; +#endif + REGDISPLAY() { // Initialize @@ -297,6 +324,8 @@ inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display) return (LPVOID)((TADDR)display->pCurrentContext->R0); #elif defined(TARGET_X86) return (LPVOID)display->pCurrentContext->Eax; +#elif defined(TARGET_RISCV64) + return (LPVOID)display->pCurrentContext->A0; #else PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)"); return NULL; @@ -352,7 +381,23 @@ inline void FillContextPointers(PT_KNONVOLATILE_CONTEXT_POINTERS pCtxPtrs, PT_CO { *(&pCtxPtrs->Edi + i) = (&pCtx->Edi + i); } -#else // TARGET_X86 +#elif defined(TARGET_RISCV64) // TARGET_X86 + *(&pCtxPtrs->S1) = &pCtx->S1; + *(&pCtxPtrs->S2) = &pCtx->S2; + *(&pCtxPtrs->S3) = &pCtx->S3; + *(&pCtxPtrs->S4) = &pCtx->S4; + *(&pCtxPtrs->S5) = &pCtx->S5; + *(&pCtxPtrs->S6) = &pCtx->S6; + *(&pCtxPtrs->S7) = &pCtx->S7; + *(&pCtxPtrs->S8) = &pCtx->S8; + *(&pCtxPtrs->S9) = &pCtx->S9; + *(&pCtxPtrs->S10) = &pCtx->S10; + *(&pCtxPtrs->S11) = &pCtx->S11; + *(&pCtxPtrs->Gp) = &pCtx->Gp; + *(&pCtxPtrs->Tp) = &pCtx->Tp; + *(&pCtxPtrs->Fp) = &pCtx->Fp; + *(&pCtxPtrs->Ra) = &pCtx->Ra; +#else // TARGET_RISCV64 PORTABILITY_ASSERT("FillContextPointers"); #endif // _TARGET_???_ (ELSE) } @@ -424,7 +469,23 @@ inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pC // Fill volatile context pointers. They can be used by GC in the case of the leaf frame for (int i=0; i < 18; i++) pRD->volatileCurrContextPointers.X[i] = &pctx->X[i]; -#endif // TARGET_ARM64 +#elif defined(TARGET_RISCV64) // TARGET_ARM64 + pRD->volatileCurrContextPointers.A0 = &pctx->A0; + pRD->volatileCurrContextPointers.A1 = &pctx->A1; + pRD->volatileCurrContextPointers.A2 = &pctx->A2; + pRD->volatileCurrContextPointers.A3 = &pctx->A3; + pRD->volatileCurrContextPointers.A4 = &pctx->A4; + pRD->volatileCurrContextPointers.A5 = &pctx->A5; + pRD->volatileCurrContextPointers.A6 = &pctx->A6; + pRD->volatileCurrContextPointers.A7 = &pctx->A7; + pRD->volatileCurrContextPointers.T0 = &pctx->T0; + pRD->volatileCurrContextPointers.T1 = &pctx->T1; + pRD->volatileCurrContextPointers.T2 = &pctx->T2; + pRD->volatileCurrContextPointers.T3 = &pctx->T3; + pRD->volatileCurrContextPointers.T4 = &pctx->T4; + pRD->volatileCurrContextPointers.T5 = &pctx->T5; + pRD->volatileCurrContextPointers.T6 = &pctx->T6; +#endif // TARGET_RISCV64 #ifdef DEBUG_REGDISPLAY pRD->_pThread = NULL; @@ -504,6 +565,9 @@ inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs) #elif defined(TARGET_ARM64) _ASSERTE(regNum < 31); return (size_t *)®s->X0 + regNum; +#elif defined(TARGET_RISCV64) + _ASSERTE(regNum < 32); + return (size_t *)®s->R0 + regNum; #else _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)"); #endif diff --git a/src/shared/inc/switches.h b/src/shared/inc/switches.h index 072d37d69b..6a4f6d6371 100644 --- a/src/shared/inc/switches.h +++ b/src/shared/inc/switches.h @@ -31,7 +31,7 @@ #if defined(TARGET_X86) || defined(TARGET_ARM) #define USE_LAZY_PREFERRED_RANGE 0 -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_S390X) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_S390X) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #if defined(HOST_UNIX) // In PAL we have a smechanism that reserves memory on start up that is diff --git a/src/shared/inc/volatile.h b/src/shared/inc/volatile.h index e127fe25f9..9a811309ed 100644 --- a/src/shared/inc/volatile.h +++ b/src/shared/inc/volatile.h @@ -68,8 +68,8 @@ #error The Volatile type is currently only defined for Visual C++ and GNU C++ #endif -#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_S390X) -#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, or S390X CPUs +#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_RISCV64) && !defined(HOST_S390X) +#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, RISCV64, or S390X CPUs #endif #if defined(__GNUC__) @@ -99,6 +99,8 @@ // currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it // turns out to be a performance issue for the uni-proc case. #define VOLATILE_MEMORY_BARRIER() MemoryBarrier() +#elif defined(HOST_RISCV64) +#define VOLATILE_MEMORY_BARRIER() asm volatile ("fence rw,rw" : : : "memory") #else // // On VC++, reorderings at the compiler and machine level are prevented by the use of the diff --git a/src/shared/pal/inc/pal.h b/src/shared/pal/inc/pal.h index 01e0851cc5..cbdc93434b 100644 --- a/src/shared/pal/inc/pal.h +++ b/src/shared/pal/inc/pal.h @@ -96,6 +96,8 @@ typedef PVOID NATIVE_LIBRARY_HANDLE; #define _M_ARM64 1 #elif defined(__s390x__) && !defined(_M_S390X) #define _M_S390X 1 +#elif defined(__riscv) && (__riscv_xlen == 64) && !defined(_M_RISCV64) +#define _M_RISCV64 1 #endif #if defined(_M_IX86) && !defined(HOST_X86) @@ -108,6 +110,8 @@ typedef PVOID NATIVE_LIBRARY_HANDLE; #define HOST_ARM64 #elif defined(_M_S390X) && !defined(HOST_S390X) #define HOST_S390X +#elif defined(_M_RISCV64) && !defined(HOST_RISCV64) +#define HOST_RISCV64 #endif #endif // !_MSC_VER @@ -1762,6 +1766,134 @@ typedef struct _KNONVOLATILE_CONTEXT_POINTERS { } KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS; +#elif defined(HOST_RISCV64) + +// Please refer to src/coreclr/pal/src/arch/riscv64/asmconstants.h +#define CONTEXT_RISCV64 0x01000000L + +#define CONTEXT_CONTROL (CONTEXT_RISCV64 | 0x1) +#define CONTEXT_INTEGER (CONTEXT_RISCV64 | 0x2) +#define CONTEXT_FLOATING_POINT (CONTEXT_RISCV64 | 0x4) +#define CONTEXT_DEBUG_REGISTERS (CONTEXT_RISCV64 | 0x8) + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) + +#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS) + +#define CONTEXT_EXCEPTION_ACTIVE 0x8000000 +#define CONTEXT_SERVICE_ACTIVE 0x10000000 +#define CONTEXT_EXCEPTION_REQUEST 0x40000000 +#define CONTEXT_EXCEPTION_REPORTING 0x80000000 + +// +// This flag is set by the unwinder if it has unwound to a call +// site, and cleared whenever it unwinds through a trap frame. +// It is used by language-specific exception handlers to help +// differentiate exception scopes during dispatching. +// + +#define CONTEXT_UNWOUND_TO_CALL 0x20000000 + +// begin_ntoshvp + +// +// Specify the number of breakpoints and watchpoints that the OS +// will track. Architecturally, RISCV64 supports up to 16. In practice, +// however, almost no one implements more than 4 of each. +// + +#define RISCV64_MAX_BREAKPOINTS 8 +#define RISCV64_MAX_WATCHPOINTS 2 + +typedef struct DECLSPEC_ALIGN(16) _CONTEXT { + + // + // Control flags. + // + + /* +0x000 */ DWORD ContextFlags; + + // + // Integer registers. + // + DWORD64 R0; + DWORD64 Ra; + DWORD64 Sp; + DWORD64 Gp; + DWORD64 Tp; + DWORD64 T0; + DWORD64 T1; + DWORD64 T2; + DWORD64 Fp; + DWORD64 S1; + DWORD64 A0; + DWORD64 A1; + DWORD64 A2; + DWORD64 A3; + DWORD64 A4; + DWORD64 A5; + DWORD64 A6; + DWORD64 A7; + DWORD64 S2; + DWORD64 S3; + DWORD64 S4; + DWORD64 S5; + DWORD64 S6; + DWORD64 S7; + DWORD64 S8; + DWORD64 S9; + DWORD64 S10; + DWORD64 S11; + DWORD64 T3; + DWORD64 T4; + DWORD64 T5; + DWORD64 T6; + DWORD64 Pc; + + // + // Floating Point Registers + // + // TODO-RISCV64: support the SIMD. + ULONGLONG F[32]; + DWORD Fcsr; +} CONTEXT, *PCONTEXT, *LPCONTEXT; + +// +// Nonvolatile context pointer record. +// + +typedef struct _KNONVOLATILE_CONTEXT_POINTERS { + + PDWORD64 S1; + PDWORD64 S2; + PDWORD64 S3; + PDWORD64 S4; + PDWORD64 S5; + PDWORD64 S6; + PDWORD64 S7; + PDWORD64 S8; + PDWORD64 S9; + PDWORD64 S10; + PDWORD64 S11; + PDWORD64 Fp; + PDWORD64 Gp; + PDWORD64 Tp; + PDWORD64 Ra; + + PDWORD64 F8; + PDWORD64 F9; + PDWORD64 F18; + PDWORD64 F19; + PDWORD64 F20; + PDWORD64 F21; + PDWORD64 F22; + PDWORD64 F23; + PDWORD64 F24; + PDWORD64 F25; + PDWORD64 F26; + PDWORD64 F27; +} KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS; + #elif defined(HOST_S390X) // There is no context for s390x defined in winnt.h, @@ -1912,6 +2044,8 @@ GetThreadTimes( #define PAL_CS_NATIVE_DATA_SIZE 56 #elif defined(__sun) && defined(__x86_64__) #define PAL_CS_NATIVE_DATA_SIZE 48 +#elif defined(__linux__) && defined(__riscv) && __riscv_xlen == 64 +#define PAL_CS_NATIVE_DATA_SIZE 96 #else #warning #error PAL_CS_NATIVE_DATA_SIZE is not defined for this architecture @@ -2548,9 +2682,9 @@ BitScanReverse64( return qwMask != 0; } -FORCEINLINE void PAL_ArmInterlockedOperationBarrier() +FORCEINLINE void PAL_InterlockedOperationBarrier() { -#ifdef HOST_ARM64 +#if defined(HOST_ARM64) || defined(HOST_RISCV64) // On arm64, most of the __sync* functions generate a code sequence like: // loop: // ldaxr (load acquire exclusive) @@ -2563,7 +2697,7 @@ FORCEINLINE void PAL_ArmInterlockedOperationBarrier() // require the load to occur after the store. This memory barrier should be used following a call to a __sync* function to // prevent that reordering. Code generated for arm32 includes a 'dmb' after 'cbnz', so no issue there at the moment. __sync_synchronize(); -#endif // HOST_ARM64 +#endif } /*++ @@ -2594,7 +2728,7 @@ InterlockedIncrement( IN OUT LONG volatile *lpAddend) { LONG result = __sync_add_and_fetch(lpAddend, (LONG)1); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2607,7 +2741,7 @@ InterlockedIncrement64( IN OUT LONGLONG volatile *lpAddend) { LONGLONG result = __sync_add_and_fetch(lpAddend, (LONGLONG)1); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2639,7 +2773,7 @@ InterlockedDecrement( IN OUT LONG volatile *lpAddend) { LONG result = __sync_sub_and_fetch(lpAddend, (LONG)1); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2655,7 +2789,7 @@ InterlockedDecrement64( IN OUT LONGLONG volatile *lpAddend) { LONGLONG result = __sync_sub_and_fetch(lpAddend, (LONGLONG)1); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2690,7 +2824,7 @@ InterlockedExchange( IN LONG Value) { LONG result = __atomic_exchange_n(Target, Value, __ATOMIC_ACQ_REL); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2704,7 +2838,7 @@ InterlockedExchange64( IN LONGLONG Value) { LONGLONG result = __atomic_exchange_n(Target, Value, __ATOMIC_ACQ_REL); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2746,7 +2880,7 @@ InterlockedCompareExchange( Destination, /* The pointer to a variable whose value is to be compared with. */ Comperand, /* The value to be compared */ Exchange /* The value to be stored */); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2769,7 +2903,7 @@ InterlockedCompareExchange64( Destination, /* The pointer to a variable whose value is to be compared with. */ Comperand, /* The value to be compared */ Exchange /* The value to be stored */); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2800,7 +2934,7 @@ InterlockedExchangeAdd( IN LONG Value) { LONG result = __sync_fetch_and_add(Addend, Value); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2814,7 +2948,7 @@ InterlockedExchangeAdd64( IN LONGLONG Value) { LONGLONG result = __sync_fetch_and_add(Addend, Value); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2828,7 +2962,7 @@ InterlockedAnd( IN LONG Value) { LONG result = __sync_fetch_and_and(Destination, Value); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2842,7 +2976,7 @@ InterlockedOr( IN LONG Value) { LONG result = __sync_fetch_and_or(Destination, Value); - PAL_ArmInterlockedOperationBarrier(); + PAL_InterlockedOperationBarrier(); return result; } @@ -2914,6 +3048,9 @@ YieldProcessor() "nop"); #elif defined(HOST_ARM) || defined(HOST_ARM64) __asm__ __volatile__( "yield"); +#elif defined(HOST_RISCV64) + // TODO-RISCV64-CQ: When Zihintpause is supported, replace with `pause` instruction. + __asm__ __volatile__(".word 0x0100000f"); #else return; #endif diff --git a/src/shared/pal/inc/rt/intsafe.h b/src/shared/pal/inc/rt/intsafe.h index 2b607e3a31..23e8969cf8 100644 --- a/src/shared/pal/inc/rt/intsafe.h +++ b/src/shared/pal/inc/rt/intsafe.h @@ -31,7 +31,7 @@ #define LODWORD(_qw) ((ULONG)(_qw)) #if defined(MIDL_PASS) || defined(RC_INVOKED) || defined(_M_CEE_PURE) \ - || defined(_M_AMD64) || defined(__ARM_ARCH) || defined(_M_S390X) + || defined(_M_AMD64) || defined(__ARM_ARCH) || defined(_M_S390X) || defined(_M_RISCV64) #ifndef UInt32x32To64 #define UInt32x32To64(a, b) ((unsigned __int64)((ULONG)(a)) * (unsigned __int64)((ULONG)(b))) diff --git a/src/shared/pal/inc/rt/ntimage.h b/src/shared/pal/inc/rt/ntimage.h index f722da0e4b..a1ac7c2f99 100644 --- a/src/shared/pal/inc/rt/ntimage.h +++ b/src/shared/pal/inc/rt/ntimage.h @@ -244,6 +244,7 @@ typedef struct _IMAGE_FILE_HEADER { #define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian #define IMAGE_FILE_MACHINE_CEE 0xC0EE #define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264 // LOONGARCH64. +#define IMAGE_FILE_MACHINE_RISCV64 0x5064 // RISCV64 // // Directory format. @@ -1036,6 +1037,12 @@ typedef IMAGE_RELOCATION UNALIGNED *PIMAGE_RELOCATION; #define IMAGE_REL_LOONGARCH64_PC 0x0003 #define IMAGE_REL_LOONGARCH64_JIR 0x0004 +// +// RISCV64 relocation types +// +#define IMAGE_REL_RISCV64_PC 0x0003 +#define IMAGE_REL_RISCV64_JALR 0x0004 + // // CEF relocation types. // diff --git a/src/shared/pal/inc/rt/palrt.h b/src/shared/pal/inc/rt/palrt.h index 0855f5991c..033e523c35 100644 --- a/src/shared/pal/inc/rt/palrt.h +++ b/src/shared/pal/inc/rt/palrt.h @@ -1167,7 +1167,7 @@ typedef struct _DISPATCHER_CONTEXT { DWORD Reserved; } DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT; -#elif defined(HOST_ARM64) +#elif defined(HOST_ARM64) || defined(HOST_RISCV64) typedef struct _DISPATCHER_CONTEXT { ULONG64 ControlPc; diff --git a/src/shared/pal/inc/unixasmmacros.inc b/src/shared/pal/inc/unixasmmacros.inc index a1bad6300f..593d6089a4 100644 --- a/src/shared/pal/inc/unixasmmacros.inc +++ b/src/shared/pal/inc/unixasmmacros.inc @@ -43,6 +43,8 @@ #include "unixasmmacrosarm.inc" #elif defined(HOST_ARM64) #include "unixasmmacrosarm64.inc" +#elif defined(HOST_RISCV64) +#include "unixasmmacrosriscv64.inc" #elif defined(HOST_S390X) #include "unixasmmacross390x.inc" #endif diff --git a/src/shared/pal/inc/unixasmmacrosriscv64.inc b/src/shared/pal/inc/unixasmmacrosriscv64.inc new file mode 100644 index 0000000000..56da9af150 --- /dev/null +++ b/src/shared/pal/inc/unixasmmacrosriscv64.inc @@ -0,0 +1,341 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.macro NESTED_ENTRY Name, Section, Handler + LEAF_ENTRY \Name, \Section + .ifnc \Handler, NoHandler + .cfi_personality 0x1B, C_FUNC(\Handler) // 0x1B == DW_EH_PE_pcrel | DW_EH_PE_sdata4 + .endif +.endm + +.macro NESTED_END Name, Section + LEAF_END \Name, \Section +.endm + +.macro PATCH_LABEL Name + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro LEAF_ENTRY Name, Section + .global C_FUNC(\Name) + .type \Name, %function +C_FUNC(\Name): + .cfi_startproc +.endm + +.macro LEAF_END Name, Section + .size \Name, .-\Name + .cfi_endproc +.endm + +.macro LEAF_END_MARKED Name, Section +C_FUNC(\Name\()_End): + .global C_FUNC(\Name\()_End) + LEAF_END \Name, \Section + // make sure this symbol gets its own address + nop +.endm + +.macro PREPARE_EXTERNAL_VAR Name, HelperReg + lla \HelperReg, \Name +.endm + +.macro PROLOG_STACK_ALLOC Size + addi sp, sp, -\Size + .cfi_adjust_cfa_offset \Size +.endm + +.macro EPILOG_STACK_FREE Size + addi sp, sp, \Size + .cfi_adjust_cfa_offset -\Size +.endm + +.macro EPILOG_STACK_RESTORE + ori sp, fp, 0 + .cfi_restore sp +.endm + +.macro PROLOG_SAVE_REG reg, ofs + sd \reg, \ofs(sp) + .cfi_rel_offset \reg, \ofs +.endm + +.macro PROLOG_SAVE_REG_PAIR reg1, reg2, ofs, __def_cfa_save=0 + sd \reg1, \ofs(sp) + sd \reg2, (\ofs+8)(sp) + .cfi_rel_offset \reg1, \ofs + .cfi_rel_offset \reg2, \ofs + 8 + .if (\__def_cfa_save == 1) + addi fp, sp, 0 + .cfi_def_cfa_register fp + .endif +.endm + +.macro PROLOG_SAVE_REG_PAIR_INDEXED reg1, reg2, ssize, __def_cfa_save=1 + addi sp, sp, -\ssize + .cfi_adjust_cfa_offset \ssize + + sd \reg1, 0(sp) + sd \reg2, 8(sp) + + .cfi_rel_offset \reg1, 0 + .cfi_rel_offset \reg2, 8 + .if (\__def_cfa_save == 1) + addi fp, sp, 0 + .cfi_def_cfa_register fp + .endif +.endm + +.macro EPILOG_RESTORE_REG reg, ofs + ld \reg, (\ofs)(sp) + .cfi_restore \reg +.endm + +.macro EPILOG_RESTORE_REG_PAIR reg1, reg2, ofs + ld \reg2, (\ofs+8)(sp) + ld \reg1, (\ofs)(sp) + .cfi_restore \reg2 + .cfi_restore \reg1 +.endm + +.macro EPILOG_RESTORE_REG_PAIR_INDEXED reg1, reg2, ssize + ld \reg2, 8(sp) + ld \reg1, 0(sp) + .cfi_restore \reg2 + .cfi_restore \reg1 + + addi sp, sp, \ssize + .cfi_adjust_cfa_offset -\ssize +.endm + +.macro EPILOG_RETURN + ret +.endm + +.macro EMIT_BREAKPOINT + ebreak +.endm + +.macro EPILOG_BRANCH Target + j \Target +.endm + +.macro EPILOG_BRANCH_REG reg + jr \reg +.endm + +//----------------------------------------------------------------------------- +// The Following sets of SAVE_*_REGISTERS expect the memory to be reserved and +// base address to be passed in $reg +// + +// Reserve 64 bytes of memory before calling SAVE_CALLEESAVED_REGISTERS +.macro SAVE_CALLEESAVED_REGISTERS reg, ofs + PROLOG_SAVE_REG_PAIR s1, s2, \ofs + 16 + PROLOG_SAVE_REG_PAIR s3, s4, \ofs + 32 + PROLOG_SAVE_REG_PAIR s5, s6, \ofs + 48 + PROLOG_SAVE_REG_PAIR s7, s8, \ofs + 64 + PROLOG_SAVE_REG_PAIR s9, s10, \ofs + 80 + PROLOG_SAVE_REG_PAIR s11, tp \ofs + 96 + PROLOG_SAVE_REG gp, \ofs + 112 +.endm + +// Reserve 64 bytes of memory before calling SAVE_ARGUMENT_REGISTERS +.macro SAVE_ARGUMENT_REGISTERS reg, ofs + sd a0, (\ofs)(\reg) + sd a1, (\ofs + 8)(\reg) + sd a2, (\ofs + 16)(\reg) + sd a3, (\ofs + 24)(\reg) + sd a4, (\ofs + 32)(\reg) + sd a5, (\ofs + 40)(\reg) + sd a6, (\ofs + 48)(\reg) + sd a7, (\ofs + 56)(\reg) +.endm + +// Reserve 64 bytes of memory before calling SAVE_FLOAT_ARGUMENT_REGISTERS +.macro SAVE_FLOAT_ARGUMENT_REGISTERS reg, ofs + fsd fa0, (\ofs)(\reg) + fsd fa1, (\ofs + 8)(\reg) + fsd fa2, (\ofs + 16)(\reg) + fsd fa3, (\ofs + 24)(\reg) + fsd fa4, (\ofs + 32)(\reg) + fsd fa5, (\ofs + 40)(\reg) + fsd fa6, (\ofs + 48)(\reg) + fsd fa7, (\ofs + 56)(\reg) +.endm + +// Reserve 64 bytes of memory before calling SAVE_FLOAT_CALLEESAVED_REGISTERS +.macro SAVE_FLOAT_CALLEESAVED_REGISTERS reg, ofs +// TODO RISCV NYI + sw ra, 0(zero) +.endm + +.macro RESTORE_CALLEESAVED_REGISTERS reg, ofs + EPILOG_RESTORE_REG gp \ofs + 112 + EPILOG_RESTORE_REG_PAIR s11, tp \ofs + 96 + EPILOG_RESTORE_REG_PAIR s9, s10, \ofs + 80 + EPILOG_RESTORE_REG_PAIR s7, s8, \ofs + 64 + EPILOG_RESTORE_REG_PAIR s5, s6, \ofs + 48 + EPILOG_RESTORE_REG_PAIR s3, s4, \ofs + 32 + EPILOG_RESTORE_REG_PAIR s1, s2, \ofs + 16 +.endm + +.macro RESTORE_ARGUMENT_REGISTERS reg, ofs + ld a0, (\ofs)(\reg) + ld a1, (\ofs + 8)(\reg) + ld a2, (\ofs + 16)(\reg) + ld a3, (\ofs + 24)(\reg) + ld a4, (\ofs + 32)(\reg) + ld a5, (\ofs + 40)(\reg) + ld a6, (\ofs + 48)(\reg) + ld a7, (\ofs + 56)(\reg) +.endm + +.macro RESTORE_FLOAT_ARGUMENT_REGISTERS reg, ofs + fld fa0, (\ofs)(\reg) + fld fa1, (\ofs + 8)(\reg) + fld fa2, (\ofs + 16)(\reg) + fld fa3, (\ofs + 24)(\reg) + fld fa4, (\ofs + 32)(\reg) + fld fa5, (\ofs + 40)(\reg) + fld fa6, (\ofs + 48)(\reg) + fld fa7, (\ofs + 56)(\reg) +.endm + +.macro RESTORE_FLOAT_CALLEESAVED_REGISTERS reg, ofs +// TODO RISCV NYI + sw ra, 0(zero) +.endm + +//----------------------------------------------------------------------------- +// Define the prolog for a TransitionBlock-based method. This macro should be called first in the method and +// comprises the entire prolog.The locals must be 8 byte aligned +// +// Save_argument_registers: +// GPR_a7 +// GPR_a6 +// GPR_a5 +// GPR_a4 +// GPR_a3 +// GPR_a2 +// GPR_a1 +// GPR_a0 +// +// General Registers: +// GPR_tp +// GPR_s8 +// GPR_s7 +// GPR_s6 +// GPR_s5 +// GPR_s4 +// GPR_s3 +// GPR_s2 +// GPR_s1 +// GPR_s0 +// GPR_ra +// GPR_fp +// +// Float Point: +// FPR_f27 / fs11 +// FPR_f26 / fs10 +// FPR_f25 / fs9 +// FPR_f24 / fs8 +// FPR_f23 / fs7 +// FPR_f22 / fs6 +// FPR_f21 / fs5 +// FPR_f20 / fs4 +// FPR_f19 / fs3 +// FPR_f18 / fs2 +// FPR_f9 / fs1 +// FPR_f8 / fs0 +// Extra: +// +.macro PROLOG_WITH_TRANSITION_BLOCK extraParameters = 0, extraLocals = 0, SaveFPRegs = 1 + __PWTB_SaveFPArgs = \SaveFPRegs + + __PWTB_FloatArgumentRegisters = \extraLocals + + .if ((__PWTB_FloatArgumentRegisters % 16) != 0) + __PWTB_FloatArgumentRegisters = __PWTB_FloatArgumentRegisters + 8 + .endif + + __PWTB_TransitionBlock = __PWTB_FloatArgumentRegisters + + .if (__PWTB_SaveFPArgs == 1) + __PWTB_TransitionBlock = __PWTB_TransitionBlock + SIZEOF__FloatArgumentRegisters + .endif + + + __PWTB_CalleeSavedRegisters = __PWTB_TransitionBlock + __PWTB_ArgumentRegisters = __PWTB_TransitionBlock + 120 + + // Including fp, ra, s1-s11, tp, gp, and (a0-a7)arguments. (1+1+11+1+1)*8 + 8*8. + __PWTB_StackAlloc = __PWTB_TransitionBlock + 120 + 64 + PROLOG_STACK_ALLOC __PWTB_StackAlloc + PROLOG_SAVE_REG_PAIR fp, ra, __PWTB_CalleeSavedRegisters, 1 + + // First, Spill argument registers. + SAVE_ARGUMENT_REGISTERS sp, __PWTB_ArgumentRegisters + + // Then, Spill callee saved registers. sp=r2. + SAVE_CALLEESAVED_REGISTERS sp, __PWTB_CalleeSavedRegisters + + // saving is f10-17. + .if (__PWTB_SaveFPArgs == 1) + SAVE_FLOAT_ARGUMENT_REGISTERS sp, __PWTB_FloatArgumentRegisters + .endif + +.endm + +.macro EPILOG_WITH_TRANSITION_BLOCK_RETURN +// TODO RISCV NYI + sw ra, 0(zero) +.endm + + +//----------------------------------------------------------------------------- +// Provides a matching epilog to PROLOG_WITH_TRANSITION_BLOCK and ends by preparing for tail-calling. +// Since this is a tail call argument registers are restored. +// +.macro EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .if (__PWTB_SaveFPArgs == 1) + RESTORE_FLOAT_ARGUMENT_REGISTERS sp, __PWTB_FloatArgumentRegisters + .endif + + RESTORE_CALLEESAVED_REGISTERS sp, __PWTB_CalleeSavedRegisters + + RESTORE_ARGUMENT_REGISTERS sp, __PWTB_ArgumentRegisters + + EPILOG_RESTORE_REG_PAIR fp, ra, __PWTB_CalleeSavedRegisters + + EPILOG_STACK_FREE __PWTB_StackAlloc +.endm + +// ------------------------------------------------------------------ +// Macro to generate Redirection Stubs +// +// $reason : reason for redirection +// Eg. GCThreadControl +// NOTE: If you edit this macro, make sure you update GetCONTEXTFromRedirectedStubStackFrame. +// This function is used by both the personality routine and the debugger to retrieve the original CONTEXT. +.macro GenerateRedirectedHandledJITCaseStub reason +// TODO RISCV NYI + sw ra, 0(zero) +.endm + +//----------------------------------------------------------------------------- +// Macro used to check (in debug builds only) whether the stack is 16-bytes aligned (a requirement before calling +// out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly +// before a call (if you have a frame pointer and a dynamic stack). A breakpoint will be invoked if the stack +// is misaligned. +// +.macro CHECK_STACK_ALIGNMENT + +#ifdef _DEBUG + andi t4, sp, 0xf + beq t4, zero, 0f + EMIT_BREAKPOINT +0: +#endif +.endm diff --git a/src/shared/pal/prebuilt/inc/cordebug.h b/src/shared/pal/prebuilt/inc/cordebug.h index df87698471..5feeaff766 100644 --- a/src/shared/pal/prebuilt/inc/cordebug.h +++ b/src/shared/pal/prebuilt/inc/cordebug.h @@ -1475,7 +1475,9 @@ enum CorDebugPlatform CORDB_PLATFORM_POSIX_AMD64 = ( CORDB_PLATFORM_WINDOWS_ARM64 + 1 ) , CORDB_PLATFORM_POSIX_X86 = ( CORDB_PLATFORM_POSIX_AMD64 + 1 ) , CORDB_PLATFORM_POSIX_ARM = ( CORDB_PLATFORM_POSIX_X86 + 1 ) , - CORDB_PLATFORM_POSIX_ARM64 = ( CORDB_PLATFORM_POSIX_ARM + 1 ) + CORDB_PLATFORM_POSIX_ARM64 = ( CORDB_PLATFORM_POSIX_ARM + 1 ) , + CORDB_PLATFORM_POSIX_LOONGARCH64 = ( CORDB_PLATFORM_POSIX_ARM64 + 1 ) , + CORDB_PLATFORM_POSIX_RISCV64 = ( CORDB_PLATFORM_POSIX_LOONGARCH64 + 1 ) } CorDebugPlatform; @@ -9191,7 +9193,72 @@ enum CorDebugRegister REGISTER_ARM64_V28 = ( REGISTER_ARM64_V27 + 1 ) , REGISTER_ARM64_V29 = ( REGISTER_ARM64_V28 + 1 ) , REGISTER_ARM64_V30 = ( REGISTER_ARM64_V29 + 1 ) , - REGISTER_ARM64_V31 = ( REGISTER_ARM64_V30 + 1 ) + REGISTER_ARM64_V31 = ( REGISTER_ARM64_V30 + 1 ) , + REGISTER_RISCV64_PC = 0, + REGISTER_RISCV64_RA = ( REGISTER_RISCV64_PC + 1), + REGISTER_RISCV64_SP = ( REGISTER_RISCV64_RA + 1), + REGISTER_RISCV64_GP = ( REGISTER_RISCV64_SP + 1), + REGISTER_RISCV64_TP = ( REGISTER_RISCV64_GP + 1 ), + REGISTER_RISCV64_T0 = ( REGISTER_RISCV64_TP + 1 ), + REGISTER_RISCV64_T1 = ( REGISTER_RISCV64_T0 + 1 ), + REGISTER_RISCV64_T2 = ( REGISTER_RISCV64_T1 + 1 ), + REGISTER_RISCV64_FP = ( REGISTER_RISCV64_T2 + 1 ), + REGISTER_RISCV64_S1 = ( REGISTER_RISCV64_FP + 1 ), + REGISTER_RISCV64_A0 = ( REGISTER_RISCV64_S1 + 1 ), + REGISTER_RISCV64_A1 = ( REGISTER_RISCV64_A0 + 1 ), + REGISTER_RISCV64_A2 = ( REGISTER_RISCV64_A1 + 1 ), + REGISTER_RISCV64_A3 = ( REGISTER_RISCV64_A2 + 1 ), + REGISTER_RISCV64_A4 = ( REGISTER_RISCV64_A3 + 1 ), + REGISTER_RISCV64_A5 = ( REGISTER_RISCV64_A4 + 1 ), + REGISTER_RISCV64_A6 = ( REGISTER_RISCV64_A5 + 1 ), + REGISTER_RISCV64_A7 = ( REGISTER_RISCV64_A6 + 1 ), + REGISTER_RISCV64_S2 = ( REGISTER_RISCV64_A7 + 1 ), + REGISTER_RISCV64_S3 = ( REGISTER_RISCV64_S2 + 1 ), + REGISTER_RISCV64_S4 = ( REGISTER_RISCV64_S3 + 1 ), + REGISTER_RISCV64_S5 = ( REGISTER_RISCV64_S4 + 1 ), + REGISTER_RISCV64_S6 = ( REGISTER_RISCV64_S5 + 1 ), + REGISTER_RISCV64_S7 = ( REGISTER_RISCV64_S6 + 1 ), + REGISTER_RISCV64_S8 = ( REGISTER_RISCV64_S7 + 1 ), + REGISTER_RISCV64_S9 = ( REGISTER_RISCV64_S8 + 1 ), + REGISTER_RISCV64_S10 = ( REGISTER_RISCV64_S9 + 1 ), + REGISTER_RISCV64_S11 = ( REGISTER_RISCV64_S10 + 1 ), + REGISTER_RISCV64_T3 = ( REGISTER_RISCV64_S11 + 1 ), + REGISTER_RISCV64_T4 = ( REGISTER_RISCV64_T3 + 1 ), + REGISTER_RISCV64_T5 = ( REGISTER_RISCV64_T4 + 1 ), + REGISTER_RISCV64_T6 = ( REGISTER_RISCV64_T5 + 1 ), + REGISTER_RISCV64_F0 = ( REGISTER_RISCV64_T6 + 1 ), + REGISTER_RISCV64_F1 = ( REGISTER_RISCV64_F0 + 1 ), + REGISTER_RISCV64_F2 = ( REGISTER_RISCV64_F1 + 1 ), + REGISTER_RISCV64_F3 = ( REGISTER_RISCV64_F2 + 1 ), + REGISTER_RISCV64_F4 = ( REGISTER_RISCV64_F3 + 1 ), + REGISTER_RISCV64_F5 = ( REGISTER_RISCV64_F4 + 1 ), + REGISTER_RISCV64_F6 = ( REGISTER_RISCV64_F5 + 1 ), + REGISTER_RISCV64_F7 = ( REGISTER_RISCV64_F6 + 1 ), + REGISTER_RISCV64_F8 = ( REGISTER_RISCV64_F7 + 1 ), + REGISTER_RISCV64_F9 = ( REGISTER_RISCV64_F8 + 1 ), + REGISTER_RISCV64_F10 = ( REGISTER_RISCV64_F9 + 1 ), + REGISTER_RISCV64_F11 = ( REGISTER_RISCV64_F10 + 1 ), + REGISTER_RISCV64_F12 = ( REGISTER_RISCV64_F11 + 1 ), + REGISTER_RISCV64_F13 = ( REGISTER_RISCV64_F12 + 1 ), + REGISTER_RISCV64_F14 = ( REGISTER_RISCV64_F13 + 1 ), + REGISTER_RISCV64_F15 = ( REGISTER_RISCV64_F14 + 1 ), + REGISTER_RISCV64_F16 = ( REGISTER_RISCV64_F15 + 1 ), + REGISTER_RISCV64_F17 = ( REGISTER_RISCV64_F16 + 1 ), + REGISTER_RISCV64_F18 = ( REGISTER_RISCV64_F17 + 1 ), + REGISTER_RISCV64_F19 = ( REGISTER_RISCV64_F18 + 1 ), + REGISTER_RISCV64_F20 = ( REGISTER_RISCV64_F19 + 1 ), + REGISTER_RISCV64_F21 = ( REGISTER_RISCV64_F20 + 1 ), + REGISTER_RISCV64_F22 = ( REGISTER_RISCV64_F21 + 1 ), + REGISTER_RISCV64_F23 = ( REGISTER_RISCV64_F22 + 1 ), + REGISTER_RISCV64_F24 = ( REGISTER_RISCV64_F23 + 1 ), + REGISTER_RISCV64_F25 = ( REGISTER_RISCV64_F24 + 1 ), + REGISTER_RISCV64_F26 = ( REGISTER_RISCV64_F25 + 1 ), + REGISTER_RISCV64_F27 = ( REGISTER_RISCV64_F26 + 1 ), + REGISTER_RISCV64_F28 = ( REGISTER_RISCV64_F27 + 1 ), + REGISTER_RISCV64_F29 = ( REGISTER_RISCV64_F28 + 1 ), + REGISTER_RISCV64_F30 = ( REGISTER_RISCV64_F29 + 1 ), + REGISTER_RISCV64_F31 = ( REGISTER_RISCV64_F30 + 1 ), + REGISTER_RISCV64_X0 = ( REGISTER_RISCV64_F31 + 1 ), // TODO-RISCV64-CQ: Add X0 for an use in debug. Need to check. } CorDebugRegister; diff --git a/src/shared/pal/src/CMakeLists.txt b/src/shared/pal/src/CMakeLists.txt index 453433e13b..7ae9cb393d 100644 --- a/src/shared/pal/src/CMakeLists.txt +++ b/src/shared/pal/src/CMakeLists.txt @@ -42,6 +42,8 @@ elseif(CLR_CMAKE_HOST_ARCH_I386) set(PAL_ARCH_SOURCES_DIR i386) elseif(CLR_CMAKE_HOST_ARCH_S390X) set(PAL_ARCH_SOURCES_DIR s390x) +elseif(CLR_CMAKE_HOST_ARCH_RISCV64) + set(PAL_ARCH_SOURCES_DIR riscv64) endif() if(CLR_CMAKE_TARGET_OSX) diff --git a/src/shared/pal/src/arch/riscv64/asmconstants.h b/src/shared/pal/src/arch/riscv64/asmconstants.h new file mode 100644 index 0000000000..97ae37f0c3 --- /dev/null +++ b/src/shared/pal/src/arch/riscv64/asmconstants.h @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __PAL_RISCV64_ASMCONSTANTS_H__ +#define __PAL_RISCV64_ASMCONSTANTS_H__ + +// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/2d865a2964fe06bfc569ab00c74e152b582ed764/riscv-dwarf.adoc +// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/2d865a2964fe06bfc569ab00c74e152b582ed764/riscv-cc.adoc + +#define CONTEXT_RISCV64 0x01000000L + +#define CONTEXT_CONTROL_BIT (0) +#define CONTEXT_INTEGER_BIT (1) +#define CONTEXT_FLOATING_POINT_BIT (2) +#define CONTEXT_DEBUG_REGISTERS_BIT (3) + +#define CONTEXT_CONTROL (CONTEXT_RISCV64 | (1L << CONTEXT_CONTROL_BIT)) +#define CONTEXT_INTEGER (CONTEXT_RISCV64 | (1 << CONTEXT_INTEGER_BIT)) +#define CONTEXT_FLOATING_POINT (CONTEXT_RISCV64 | (1 << CONTEXT_FLOATING_POINT_BIT)) +#define CONTEXT_DEBUG_REGISTERS (CONTEXT_RISCV64 | (1 << CONTEXT_DEBUG_REGISTERS_BIT)) + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) + +#define SIZEOF_RISCV64_GPR 8 +#define SIZEOF_RISCV64_FPR 8 + +#define CONTEXT_ContextFlags 0 +#define CONTEXT_X0 CONTEXT_ContextFlags + SIZEOF_RISCV64_GPR // hardwired zero +#define CONTEXT_X1 CONTEXT_X0 + SIZEOF_RISCV64_GPR +#define CONTEXT_X2 CONTEXT_X1 + SIZEOF_RISCV64_GPR +#define CONTEXT_X3 CONTEXT_X2 + SIZEOF_RISCV64_GPR +#define CONTEXT_X4 CONTEXT_X3 + SIZEOF_RISCV64_GPR +#define CONTEXT_X5 CONTEXT_X4 + SIZEOF_RISCV64_GPR +#define CONTEXT_X6 CONTEXT_X5 + SIZEOF_RISCV64_GPR +#define CONTEXT_X7 CONTEXT_X6 + SIZEOF_RISCV64_GPR +#define CONTEXT_X8 CONTEXT_X7 + SIZEOF_RISCV64_GPR +#define CONTEXT_X9 CONTEXT_X8 + SIZEOF_RISCV64_GPR +#define CONTEXT_X10 CONTEXT_X9 + SIZEOF_RISCV64_GPR +#define CONTEXT_X11 CONTEXT_X10 + SIZEOF_RISCV64_GPR +#define CONTEXT_X12 CONTEXT_X11 + SIZEOF_RISCV64_GPR +#define CONTEXT_X13 CONTEXT_X12 + SIZEOF_RISCV64_GPR +#define CONTEXT_X14 CONTEXT_X13 + SIZEOF_RISCV64_GPR +#define CONTEXT_X15 CONTEXT_X14 + SIZEOF_RISCV64_GPR +#define CONTEXT_X16 CONTEXT_X15 + SIZEOF_RISCV64_GPR +#define CONTEXT_X17 CONTEXT_X16 + SIZEOF_RISCV64_GPR +#define CONTEXT_X18 CONTEXT_X17 + SIZEOF_RISCV64_GPR +#define CONTEXT_X19 CONTEXT_X18 + SIZEOF_RISCV64_GPR +#define CONTEXT_X20 CONTEXT_X19 + SIZEOF_RISCV64_GPR +#define CONTEXT_X21 CONTEXT_X20 + SIZEOF_RISCV64_GPR +#define CONTEXT_X22 CONTEXT_X21 + SIZEOF_RISCV64_GPR +#define CONTEXT_X23 CONTEXT_X22 + SIZEOF_RISCV64_GPR +#define CONTEXT_X24 CONTEXT_X23 + SIZEOF_RISCV64_GPR +#define CONTEXT_X25 CONTEXT_X24 + SIZEOF_RISCV64_GPR +#define CONTEXT_X26 CONTEXT_X25 + SIZEOF_RISCV64_GPR +#define CONTEXT_X27 CONTEXT_X26 + SIZEOF_RISCV64_GPR +#define CONTEXT_X28 CONTEXT_X27 + SIZEOF_RISCV64_GPR +#define CONTEXT_X29 CONTEXT_X28 + SIZEOF_RISCV64_GPR +#define CONTEXT_X30 CONTEXT_X29 + SIZEOF_RISCV64_GPR +#define CONTEXT_X31 CONTEXT_X30 + SIZEOF_RISCV64_GPR + +#define CONTEXT_Pc CONTEXT_X31 + SIZEOF_RISCV64_GPR +#define CONTEXT_FPU_OFFSET CONTEXT_Pc + SIZEOF_RISCV64_GPR +#define CONTEXT_Ra CONTEXT_X1 +#define CONTEXT_Sp CONTEXT_X2 +#define CONTEXT_Gp CONTEXT_X3 +#define CONTEXT_Tp CONTEXT_X4 +#define CONTEXT_Fp CONTEXT_X8 + +#define CONTEXT_S0 CONTEXT_X8 +#define CONTEXT_S1 CONTEXT_X9 +#define CONTEXT_S2 CONTEXT_X18 +#define CONTEXT_S3 CONTEXT_X19 +#define CONTEXT_S4 CONTEXT_X20 +#define CONTEXT_S5 CONTEXT_X21 +#define CONTEXT_S6 CONTEXT_X22 +#define CONTEXT_S7 CONTEXT_X23 +#define CONTEXT_S8 CONTEXT_X24 +#define CONTEXT_S9 CONTEXT_X25 +#define CONTEXT_S10 CONTEXT_X26 +#define CONTEXT_S11 CONTEXT_X27 + +#define CONTEXT_A0 CONTEXT_X10 +#define CONTEXT_A1 CONTEXT_X11 +#define CONTEXT_A2 CONTEXT_X12 +#define CONTEXT_A3 CONTEXT_X13 +#define CONTEXT_A4 CONTEXT_X14 +#define CONTEXT_A5 CONTEXT_X15 +#define CONTEXT_A6 CONTEXT_X16 +#define CONTEXT_A7 CONTEXT_X17 + +#define CONTEXT_T0 CONTEXT_X5 +#define CONTEXT_T1 CONTEXT_X6 +#define CONTEXT_T2 CONTEXT_X7 +#define CONTEXT_T3 CONTEXT_X28 +#define CONTEXT_T4 CONTEXT_X29 +#define CONTEXT_T5 CONTEXT_X30 +#define CONTEXT_T6 CONTEXT_X31 + +#define CONTEXT_F0 0 +#define CONTEXT_F1 CONTEXT_F0 + SIZEOF_RISCV64_FPR +#define CONTEXT_F2 CONTEXT_F1 + SIZEOF_RISCV64_FPR +#define CONTEXT_F3 CONTEXT_F2 + SIZEOF_RISCV64_FPR +#define CONTEXT_F4 CONTEXT_F3 + SIZEOF_RISCV64_FPR +#define CONTEXT_F5 CONTEXT_F4 + SIZEOF_RISCV64_FPR +#define CONTEXT_F6 CONTEXT_F5 + SIZEOF_RISCV64_FPR +#define CONTEXT_F7 CONTEXT_F6 + SIZEOF_RISCV64_FPR +#define CONTEXT_F8 CONTEXT_F7 + SIZEOF_RISCV64_FPR +#define CONTEXT_F9 CONTEXT_F8 + SIZEOF_RISCV64_FPR +#define CONTEXT_F10 CONTEXT_F9 + SIZEOF_RISCV64_FPR +#define CONTEXT_F11 CONTEXT_F10 + SIZEOF_RISCV64_FPR +#define CONTEXT_F12 CONTEXT_F11 + SIZEOF_RISCV64_FPR +#define CONTEXT_F13 CONTEXT_F12 + SIZEOF_RISCV64_FPR +#define CONTEXT_F14 CONTEXT_F13 + SIZEOF_RISCV64_FPR +#define CONTEXT_F15 CONTEXT_F14 + SIZEOF_RISCV64_FPR +#define CONTEXT_F16 CONTEXT_F15 + SIZEOF_RISCV64_FPR +#define CONTEXT_F17 CONTEXT_F16 + SIZEOF_RISCV64_FPR +#define CONTEXT_F18 CONTEXT_F17 + SIZEOF_RISCV64_FPR +#define CONTEXT_F19 CONTEXT_F18 + SIZEOF_RISCV64_FPR +#define CONTEXT_F20 CONTEXT_F19 + SIZEOF_RISCV64_FPR +#define CONTEXT_F21 CONTEXT_F20 + SIZEOF_RISCV64_FPR +#define CONTEXT_F22 CONTEXT_F21 + SIZEOF_RISCV64_FPR +#define CONTEXT_F23 CONTEXT_F22 + SIZEOF_RISCV64_FPR +#define CONTEXT_F24 CONTEXT_F23 + SIZEOF_RISCV64_FPR +#define CONTEXT_F25 CONTEXT_F24 + SIZEOF_RISCV64_FPR +#define CONTEXT_F26 CONTEXT_F25 + SIZEOF_RISCV64_FPR +#define CONTEXT_F27 CONTEXT_F26 + SIZEOF_RISCV64_FPR +#define CONTEXT_F28 CONTEXT_F27 + SIZEOF_RISCV64_FPR +#define CONTEXT_F29 CONTEXT_F28 + SIZEOF_RISCV64_FPR +#define CONTEXT_F30 CONTEXT_F29 + SIZEOF_RISCV64_FPR +#define CONTEXT_F31 CONTEXT_F30 + SIZEOF_RISCV64_FPR + +#define CONTEXT_FLOAT_CONTROL_OFFSET CONTEXT_F31+SIZEOF_RISCV64_FPR +#define CONTEXT_Size ((CONTEXT_FPU_OFFSET + 8 + 8 + 0xf) & ~0xf) + +#endif diff --git a/src/shared/pal/src/arch/riscv64/context2.S b/src/shared/pal/src/arch/riscv64/context2.S new file mode 100644 index 0000000000..6bc3970cb7 --- /dev/null +++ b/src/shared/pal/src/arch/riscv64/context2.S @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Implementation of _CONTEXT_CaptureContext for the RISCV64 platform. +// This function is processor dependent. It is used by exception handling, +// and is always apply to the current thread. +// + +#include "unixasmmacros.inc" +#include "asmconstants.h" + +// Incoming: +// a0: Context* +// a1: Exception* +// +LEAF_ENTRY RtlRestoreContext, _TEXT +#ifdef HAS_ASAN +#pragma error("TODO-RISCV64-CQ: unimplemented on RISCV64 yet") +#endif + + mv t4, a0 + lw t1, CONTEXT_ContextFlags(t4) + andi t1, t1, 0x1 << CONTEXT_FLOATING_POINT_BIT + beqz t1, LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT) + + //64-bits FPR. + addi t0, t4, CONTEXT_FPU_OFFSET + + fld f0, (CONTEXT_F0)(t0) + fld f1, (CONTEXT_F1)(t0) + fld f2, (CONTEXT_F2)(t0) + fld f3, (CONTEXT_F3)(t0) + fld f4, (CONTEXT_F4)(t0) + fld f5, (CONTEXT_F5)(t0) + fld f6, (CONTEXT_F6)(t0) + fld f7, (CONTEXT_F7)(t0) + fld f8, (CONTEXT_F8)(t0) + fld f9, (CONTEXT_F9)(t0) + fld f10, (CONTEXT_F10)(t0) + fld f11, (CONTEXT_F11)(t0) + fld f12, (CONTEXT_F12)(t0) + fld f13, (CONTEXT_F13)(t0) + fld f14, (CONTEXT_F14)(t0) + fld f15, (CONTEXT_F15)(t0) + fld f16, (CONTEXT_F16)(t0) + fld f17, (CONTEXT_F17)(t0) + fld f18, (CONTEXT_F18)(t0) + fld f19, (CONTEXT_F19)(t0) + fld f20, (CONTEXT_F20)(t0) + fld f21, (CONTEXT_F21)(t0) + fld f22, (CONTEXT_F22)(t0) + fld f23, (CONTEXT_F23)(t0) + fld f24, (CONTEXT_F24)(t0) + fld f25, (CONTEXT_F25)(t0) + fld f26, (CONTEXT_F26)(t0) + fld f27, (CONTEXT_F27)(t0) + fld f28, (CONTEXT_F28)(t0) + fld f29, (CONTEXT_F29)(t0) + fld f30, (CONTEXT_F30)(t0) + fld f31, (CONTEXT_F31)(t0) + + lw t1, (CONTEXT_FLOAT_CONTROL_OFFSET)(t0) + fscsr x0, t1 + +LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT): + + lw t1, CONTEXT_ContextFlags(t4) + andi t1, t1, 0x1 << CONTEXT_INTEGER_BIT + beqz t1, LOCAL_LABEL(No_Restore_CONTEXT_INTEGER) + + ld tp, (CONTEXT_Tp)(a0) + ld gp, (CONTEXT_Gp)(a0) + ld a1, (CONTEXT_A1)(a0) + ld a2, (CONTEXT_A2)(a0) + ld a3, (CONTEXT_A3)(a0) + ld a4, (CONTEXT_A4)(a0) + ld a5, (CONTEXT_A5)(a0) + ld a6, (CONTEXT_A6)(a0) + ld a7, (CONTEXT_A7)(a0) + ld t0, (CONTEXT_T0)(a0) + ld t1, (CONTEXT_T1)(a0) + ld t2, (CONTEXT_T2)(a0) + ld t3, (CONTEXT_T3)(a0) + ld t5, (CONTEXT_T5)(a0) + ld t6, (CONTEXT_T6)(a0) + + ld s1, (CONTEXT_S1)(a0) + ld s2, (CONTEXT_S2)(a0) + ld s3, (CONTEXT_S3)(a0) + ld s4, (CONTEXT_S4)(a0) + ld s5, (CONTEXT_S5)(a0) + ld s6, (CONTEXT_S6)(a0) + ld s7, (CONTEXT_S7)(a0) + ld s8, (CONTEXT_S8)(a0) + ld s9, (CONTEXT_S9)(a0) + ld s10, (CONTEXT_S10)(a0) + ld s11, (CONTEXT_S11)(a0) + + ld a0, (CONTEXT_A0)(a0) + +LOCAL_LABEL(No_Restore_CONTEXT_INTEGER): + + lw t1, CONTEXT_ContextFlags(t4) + andi t1, t1, 0x1 << CONTEXT_CONTROL_BIT + beqz t1, LOCAL_LABEL(No_Restore_CONTEXT_CONTROL) + + ld ra, (CONTEXT_Ra)(t4) + ld fp, (CONTEXT_Fp)(t4) + ld sp, (CONTEXT_Sp)(t4) + ld t1, (CONTEXT_Pc)(t4) // Since we cannot control $pc directly, we're going to corrupt t1 + ld t4, (CONTEXT_T4)(t4) + jr t1 + +LOCAL_LABEL(No_Restore_CONTEXT_CONTROL): + ld t4, (CONTEXT_T4)(t4) + ret +LEAF_END RtlRestoreContext, _TEXT + +// Incoming: +// a0: Context* + +LEAF_ENTRY RtlCaptureContext, _TEXT + PROLOG_STACK_ALLOC 16 + sd t1, 0(sp) + li t1, CONTEXT_FULL + sw t1, CONTEXT_ContextFlags(a0) + ld t1, 0(sp) + EPILOG_STACK_FREE 16 + tail CONTEXT_CaptureContext +LEAF_END RtlCaptureContext, _TEXT + +// Incoming: +// a0: Context* +// + +LEAF_ENTRY CONTEXT_CaptureContext, _TEXT + PROLOG_STACK_ALLOC 24 + sd t0, 0(sp) + sd t1, 8(sp) + sd t3, 16(sp) + + lw t1, CONTEXT_ContextFlags(a0) + li t0, CONTEXT_CONTROL + and t3, t1, t0 + bne t3, t0, LOCAL_LABEL(Done_CONTEXT_CONTROL) + + addi t0, sp, 24 + sd fp, CONTEXT_Fp(a0) + sd t0, CONTEXT_Sp(a0) + sd ra, CONTEXT_Ra(a0) + sd ra, CONTEXT_Pc(a0) + +LOCAL_LABEL(Done_CONTEXT_CONTROL): + + li t0, CONTEXT_INTEGER + and t3, t1, t0 + bne t3, t0, LOCAL_LABEL(Done_CONTEXT_INTEGER) + + ld t0, 0(sp) + ld t1, 8(sp) + ld t3, 16(sp) + + sd tp, (CONTEXT_Tp)(a0) + sd gp, (CONTEXT_Gp)(a0) + sd a0, (CONTEXT_A0)(a0) + sd a1, (CONTEXT_A1)(a0) + sd a2, (CONTEXT_A2)(a0) + sd a3, (CONTEXT_A3)(a0) + sd a4, (CONTEXT_A4)(a0) + sd a5, (CONTEXT_A5)(a0) + sd a6, (CONTEXT_A6)(a0) + sd a7, (CONTEXT_A7)(a0) + sd t0, (CONTEXT_T0)(a0) + sd t1, (CONTEXT_T1)(a0) + sd t2, (CONTEXT_T2)(a0) + sd t3, (CONTEXT_T3)(a0) + sd t4, (CONTEXT_T4)(a0) + sd t5, (CONTEXT_T5)(a0) + sd t6, (CONTEXT_T6)(a0) + + sd s1, (CONTEXT_S1)(a0) + sd s2, (CONTEXT_S2)(a0) + sd s3, (CONTEXT_S3)(a0) + sd s4, (CONTEXT_S4)(a0) + sd s5, (CONTEXT_S5)(a0) + sd s6, (CONTEXT_S6)(a0) + sd s7, (CONTEXT_S7)(a0) + sd s8, (CONTEXT_S8)(a0) + sd s9, (CONTEXT_S9)(a0) + sd s10, (CONTEXT_S10)(a0) + sd s11, (CONTEXT_S11)(a0) + +LOCAL_LABEL(Done_CONTEXT_INTEGER): + lw t1, CONTEXT_ContextFlags(a0) + + li t0, CONTEXT_FLOATING_POINT + and t3, t1, t0 + bne t3, t0, LOCAL_LABEL(Done_CONTEXT_FLOATING_POINT) + + addi a0, a0, CONTEXT_FPU_OFFSET + + fsd f0, (CONTEXT_F0)(a0) + fsd f1, (CONTEXT_F1)(a0) + fsd f2, (CONTEXT_F2)(a0) + fsd f3, (CONTEXT_F3)(a0) + fsd f4, (CONTEXT_F4)(a0) + fsd f5, (CONTEXT_F5)(a0) + fsd f6, (CONTEXT_F6)(a0) + fsd f7, (CONTEXT_F7)(a0) + fsd f8, (CONTEXT_F8)(a0) + fsd f9, (CONTEXT_F9)(a0) + fsd f10, (CONTEXT_F10)(a0) + fsd f11, (CONTEXT_F11)(a0) + fsd f12, (CONTEXT_F12)(a0) + fsd f13, (CONTEXT_F13)(a0) + fsd f14, (CONTEXT_F14)(a0) + fsd f15, (CONTEXT_F15)(a0) + fsd f16, (CONTEXT_F16)(a0) + fsd f17, (CONTEXT_F17)(a0) + fsd f18, (CONTEXT_F18)(a0) + fsd f19, (CONTEXT_F19)(a0) + fsd f20, (CONTEXT_F20)(a0) + fsd f21, (CONTEXT_F21)(a0) + fsd f22, (CONTEXT_F22)(a0) + fsd f23, (CONTEXT_F23)(a0) + fsd f24, (CONTEXT_F24)(a0) + fsd f25, (CONTEXT_F25)(a0) + fsd f26, (CONTEXT_F26)(a0) + fsd f27, (CONTEXT_F27)(a0) + fsd f28, (CONTEXT_F28)(a0) + fsd f29, (CONTEXT_F29)(a0) + fsd f30, (CONTEXT_F30)(a0) + fsd f31, (CONTEXT_F31)(a0) + + frcsr t0 + sd t0, (CONTEXT_FLOAT_CONTROL_OFFSET)(a0) + +LOCAL_LABEL(Done_CONTEXT_FLOATING_POINT): + + EPILOG_STACK_FREE 24 + ret +LEAF_END CONTEXT_CaptureContext, _TEXT diff --git a/src/shared/pal/src/arch/riscv64/debugbreak.S b/src/shared/pal/src/arch/riscv64/debugbreak.S new file mode 100644 index 0000000000..69cb82b86c --- /dev/null +++ b/src/shared/pal/src/arch/riscv64/debugbreak.S @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "unixasmmacros.inc" + +LEAF_ENTRY DBG_DebugBreak, _TEXT + EMIT_BREAKPOINT +LEAF_END_MARKED DBG_DebugBreak, _TEXT diff --git a/src/shared/pal/src/arch/riscv64/processor.cpp b/src/shared/pal/src/arch/riscv64/processor.cpp new file mode 100644 index 0000000000..ab4b84febd --- /dev/null +++ b/src/shared/pal/src/arch/riscv64/processor.cpp @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*++ + + + +Module Name: + + processor.cpp + +Abstract: + + Implementation of processor related functions for the ARM64 + platform. These functions are processor dependent. + + + +--*/ + +#include "pal/palinternal.h" diff --git a/src/shared/pal/src/misc/sysinfo.cpp b/src/shared/pal/src/misc/sysinfo.cpp index 19f9c86fd4..4e08089d96 100644 --- a/src/shared/pal/src/misc/sysinfo.cpp +++ b/src/shared/pal/src/misc/sysinfo.cpp @@ -470,12 +470,17 @@ GlobalMemoryStatusEx( #endif // __APPLE__ } +#ifndef TARGET_RISCV64 // There is no API to get the total virtual address space size on // Unix, so we use a constant value representing 128TB, which is // the approximate size of total user virtual address space on // the currently supported Unix systems. - static const UINT64 _128TB = (1ull << 47); - lpBuffer->ullTotalVirtual = _128TB; + static const UINT64 VMSize = (1ull << 47); +#else // TARGET_RISCV64 + // For RISC-V Linux Kernel SV39 virtual memory limit is 256gb. + static const UINT64 VMSize = (1ull << 38); +#endif // TARGET_RISCV64 + lpBuffer->ullTotalVirtual = VMSize; lpBuffer->ullAvailVirtual = lpBuffer->ullAvailPhys; LOGEXIT("GlobalMemoryStatusEx returns %d\n", fRetVal); From 6fff22a8444ce2b1f145ed78ccab2e023ae34c01 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:16:29 +0000 Subject: [PATCH 53/82] [main] Update dependencies from dotnet/aspnetcore (#4326) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f7fbceb29b..ff274bb9e8 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - + https://github.com/dotnet/aspnetcore - 775bf48a1d908496d1947db71ec98012d664dc54 + cbfc558b6dd2676b47c8009e2453ddce5725c8d7 - + https://github.com/dotnet/aspnetcore - 775bf48a1d908496d1947db71ec98012d664dc54 + cbfc558b6dd2676b47c8009e2453ddce5725c8d7 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 7124884cf0..74a5740218 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23511.16 8.0.0-rtm.23511.16 - 8.0.0-rtm.23512.20 - 8.0.0-rtm.23512.20 + 8.0.0-rtm.23513.13 + 8.0.0-rtm.23513.13 8.0.100-rtm.23506.1 From d99aecd5b81ca40f2f12a67b9cbd73db1017966f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:17:09 +0000 Subject: [PATCH 54/82] [main] Update dependencies from microsoft/clrmd (#4327) [main] Update dependencies from microsoft/clrmd --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ff274bb9e8..7c95e6271f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore a3b341f9e61c8d8e832c4acfeb5b3a2305e51bcc - + https://github.com/microsoft/clrmd - 6eae6255dfa3bdc3a855fc6e578a3a192c1eb2ab + 70994fd922058fb99403b4a96944286dafd26ea0 - + https://github.com/microsoft/clrmd - 6eae6255dfa3bdc3a855fc6e578a3a192c1eb2ab + 70994fd922058fb99403b4a96944286dafd26ea0 diff --git a/eng/Versions.props b/eng/Versions.props index 74a5740218..6a182deace 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.1.451202 + 3.1.451301 16.11.27-beta1.23180.1 3.0.7 6.0.0 From b885e5bf4da1a80ad17ff40ae199ad8e3ebd191a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:22:52 +0000 Subject: [PATCH 55/82] [main] Update dependencies from dotnet/runtime (#4328) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7c95e6271f..ce5678556d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore cbfc558b6dd2676b47c8009e2453ddce5725c8d7 - + https://github.com/dotnet/runtime - 256bf22a3ddf920516752701da2b95e1847ff708 + 60b77a63df30362ed1c66a834fcb8f8956ea113b - + https://github.com/dotnet/runtime - 256bf22a3ddf920516752701da2b95e1847ff708 + 60b77a63df30362ed1c66a834fcb8f8956ea113b https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index 6a182deace..081aa58555 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.450901 - 8.0.0-rtm.23511.16 - 8.0.0-rtm.23511.16 + 8.0.0-rtm.23513.17 + 8.0.0-rtm.23513.17 8.0.0-rtm.23513.13 8.0.0-rtm.23513.13 From 9f493c1dcc8e88fdcb495e718a694fc876a3fdce Mon Sep 17 00:00:00 2001 From: Lee Culver Date: Sat, 14 Oct 2023 09:46:32 -0700 Subject: [PATCH 56/82] Additional !maddress fixes (#4325) More issues found by the GC team. With this fix the output of !maddress now exactly lines of with GC data structures. --- .../NativeAddressHelper.cs | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs index 409c85bcf2..0ae5cde836 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs @@ -196,25 +196,7 @@ private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, b // by the other region kind. if (region.ClrMemoryKind == ClrMemoryKind.None) { - // On platforms other than Windows, we may not have accurate region begin/end - // locations. For example, with Windows dumps, we will get two distinct regions - // when one call to virtual alloc maps 0x10000-0x20000 and a different call to - // virtual alloc maps 0x20000-0x30000. On Linux, we seem to only get one region - // defined (0x10000-0x30000) even if that came from two different calls to - // the Linux equivalent of VirtualAlloc. Therefore, we only use this heuristic - // to tag memory on Windows to avoid accidently over-attributing memory to CLR. - // - // Finally, we actually get very accurate data about GC structures, so never - // use this heuristic to tag memory as belonging to the GC because we know it - // doesn't. - if (Target.OperatingSystem == OSPlatform.Windows - && mem.Kind != ClrMemoryKind.GCHeap - && mem.Kind != ClrMemoryKind.GCHeapReserve - && mem.Kind != ClrMemoryKind.GCBookkeeping - && mem.Kind != ClrMemoryKind.GCHeapToBeFreed) - { - region.ClrMemoryKind = mem.Kind; - } + AssignKindIfAppropriate(mem, region); } DescribedRegion middleRegion = new(region) @@ -281,8 +263,10 @@ private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, b region.Start = newRange.End; if (region.ClrMemoryKind == ClrMemoryKind.None) // see note above { - region.ClrMemoryKind = mem.Kind; + AssignKindIfAppropriate(mem, region); } + + rangeList.Add(newRange); } else { @@ -296,7 +280,6 @@ private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, b } DescribedRegion[] ranges = rangeList.OrderBy(r => r.Start).ToArray(); - if (tagReserveMemoryHeuristically) { foreach (DescribedRegion mem in ranges) @@ -318,6 +301,29 @@ private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, b return ranges; } + private void AssignKindIfAppropriate((ulong Address, ulong Size, ClrMemoryKind Kind) mem, DescribedRegion region) + { + // On platforms other than Windows, we may not have accurate region begin/end + // locations. For example, with Windows dumps, we will get two distinct regions + // when one call to virtual alloc maps 0x10000-0x20000 and a different call to + // virtual alloc maps 0x20000-0x30000. On Linux, we seem to only get one region + // defined (0x10000-0x30000) even if that came from two different calls to + // the Linux equivalent of VirtualAlloc. Therefore, we only use this heuristic + // to tag memory on Windows to avoid accidently over-attributing memory to CLR. + // + // Finally, we actually get very accurate data about GC structures, so never + // use this heuristic to tag memory as belonging to the GC because we know it + // doesn't. + if (Target.OperatingSystem == OSPlatform.Windows + && mem.Kind != ClrMemoryKind.GCHeap + && mem.Kind != ClrMemoryKind.GCHeapReserve + && mem.Kind != ClrMemoryKind.GCBookkeeping + && mem.Kind != ClrMemoryKind.GCHeapToBeFreed) + { + region.ClrMemoryKind = mem.Kind; + } + } + /// /// Enumerates pointers to various CLR heaps in memory. /// From 3be39972c247af44368f0f777cbc23fbd0aa900b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:10:32 +0000 Subject: [PATCH 57/82] [main] Update dependencies from dotnet/aspnetcore (#4329) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ce5678556d..a510a9cd49 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 - + https://github.com/dotnet/aspnetcore - cbfc558b6dd2676b47c8009e2453ddce5725c8d7 + faf594a06685882b29522bede329f94db94bb3c1 - + https://github.com/dotnet/aspnetcore - cbfc558b6dd2676b47c8009e2453ddce5725c8d7 + faf594a06685882b29522bede329f94db94bb3c1 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 081aa58555..6467497ab5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23513.17 8.0.0-rtm.23513.17 - 8.0.0-rtm.23513.13 - 8.0.0-rtm.23513.13 + 8.0.0-rtm.23514.1 + 8.0.0-rtm.23514.1 8.0.100-rtm.23506.1 From 51a9fa3826d018d2ea9c27922da756317d1e512a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:24:40 +0000 Subject: [PATCH 58/82] [main] Update dependencies from dotnet/arcade (#4330) [main] Update dependencies from dotnet/arcade --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- eng/common/post-build/check-channel-consistency.ps1 | 2 +- global.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a510a9cd49..f0cd3ac216 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,14 +14,14 @@ - + https://github.com/dotnet/arcade - 822f095b8c815dd7b9161140a9ff8151de593f82 + 0e89c8d7ddbb54510d9d0a757fda5126021b79f0 - + https://github.com/dotnet/arcade - 822f095b8c815dd7b9161140a9ff8151de593f82 + 0e89c8d7ddbb54510d9d0a757fda5126021b79f0 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index 6467497ab5..1af45a3262 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -62,7 +62,7 @@ 6.0.0 6.0.8 2.0.3 - 9.0.0-beta.23508.1 + 9.0.0-beta.23510.4 1.2.0-beta.406 7.0.0-beta.22316.2 10.0.18362 diff --git a/eng/common/post-build/check-channel-consistency.ps1 b/eng/common/post-build/check-channel-consistency.ps1 index 63f3464c98..1728f035a9 100644 --- a/eng/common/post-build/check-channel-consistency.ps1 +++ b/eng/common/post-build/check-channel-consistency.ps1 @@ -7,7 +7,7 @@ try { . $PSScriptRoot\post-build-utils.ps1 if ($PromoteToChannels -eq "") { - Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." + Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/main/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." ExitWithExitCode 0 } diff --git a/global.json b/global.json index 92fde626a4..1f3c7c8e18 100644 --- a/global.json +++ b/global.json @@ -16,6 +16,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.5.0", - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23508.1" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23510.4" } } From 1cb3ee38b9dbcb4b8fdb6c2eb8aa48666cac1b00 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:16:41 +0000 Subject: [PATCH 59/82] [main] Update dependencies from dotnet/installer (#4331) [main] Update dependencies from dotnet/installer - Update Versions.props --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f0cd3ac216..dd68f52ade 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,9 +27,9 @@ https://github.com/dotnet/arcade ccfe6da198c5f05534863bbb1bff66e830e0c6ab - + https://github.com/dotnet/installer - 0ffc9fdc93e578268a09b0dccdc4c3527f4697f3 + c65382f00216756e0fb192cc2fd6513448d2679d https://github.com/dotnet/aspnetcore diff --git a/eng/Versions.props b/eng/Versions.props index 1af45a3262..1703fa1e9c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -24,7 +24,7 @@ 8.0.0-rtm.23514.1 8.0.0-rtm.23514.1 - 8.0.100-rtm.23506.1 + 8.0.100-rtm.23513.4 @@ -35,7 +35,7 @@ $(MicrosoftNETCoreApp60Version) $(MicrosoftNETCoreApp70Version) - 8.0.0-rtm.23504.8 + 8.0.0-rtm.23511.16 From 8411fb36dd382cc367b3dbefcad0a206bc36e122 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:24:38 +0000 Subject: [PATCH 60/82] [main] Update dependencies from dotnet/symstore (#4334) [main] Update dependencies from dotnet/symstore --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index dd68f52ade..79d7c0b51f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/symstore - a3b341f9e61c8d8e832c4acfeb5b3a2305e51bcc + 21508f9ab057e19d4060855647edeb376ac7d59d https://github.com/microsoft/clrmd diff --git a/eng/Versions.props b/eng/Versions.props index 1703fa1e9c..4b19cd65d4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,7 +16,7 @@ - 1.0.450901 + 1.0.451601 8.0.0-rtm.23513.17 8.0.0-rtm.23513.17 From aeb0414258597b2f264f95aa4d0e2d0e4bac7637 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:24:49 +0000 Subject: [PATCH 61/82] [main] Update dependencies from dotnet/runtime (#4335) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 79d7c0b51f..d0d174a483 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore faf594a06685882b29522bede329f94db94bb3c1 - + https://github.com/dotnet/runtime - 60b77a63df30362ed1c66a834fcb8f8956ea113b + 567f81e719091bd1ba3c751d49bacee2c6a45e66 - + https://github.com/dotnet/runtime - 60b77a63df30362ed1c66a834fcb8f8956ea113b + 567f81e719091bd1ba3c751d49bacee2c6a45e66 https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index 4b19cd65d4..dac142af08 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.451601 - 8.0.0-rtm.23513.17 - 8.0.0-rtm.23513.17 + 8.0.0-rtm.23516.15 + 8.0.0-rtm.23516.15 8.0.0-rtm.23514.1 8.0.0-rtm.23514.1 From 83ecc1495ab29dd162389774692be22942aecb6f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:45:57 +0000 Subject: [PATCH 62/82] [main] Update dependencies from dotnet/source-build-reference-packages (#4336) [main] Update dependencies from dotnet/source-build-reference-packages --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d0d174a483..7a7e0ba0da 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -47,9 +47,9 @@ https://github.com/dotnet/runtime 567f81e719091bd1ba3c751d49bacee2c6a45e66 - + https://github.com/dotnet/source-build-reference-packages - 5d89368fe132c3f6210d661e18087db782b74f2d + b35b2b088257f17a7a6d836b22aea06a124e5303 diff --git a/eng/Versions.props b/eng/Versions.props index dac142af08..3305913534 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -67,7 +67,7 @@ 7.0.0-beta.22316.2 10.0.18362 13.0.1 - 9.0.0-alpha.1.23510.3 + 9.0.0-alpha.1.23516.3 3.11.0 From 6728584bcec88cd5d53de8e07b7628fbe1bb86b1 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:33:59 -0700 Subject: [PATCH 63/82] [main] Update dependencies from dotnet/aspnetcore (#4333) This pull request updates the following dependencies [marker]: <> (Begin:319094f3-ed78-47c4-53e7-08d8e409d87d) ## From https://github.com/dotnet/aspnetcore - **Subscription**: 319094f3-ed78-47c4-53e7-08d8e409d87d - **Build**: 20231016.6 - **Date Produced**: October 16, 2023 8:00:36 PM UTC - **Commit**: 02bdf7077b8d96039bd39d6189a1abdefc41e65e - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.AspNetCore.App.Ref**: [from 8.0.0-rtm.23514.1 to 8.0.0-rtm.23516.6][1] - **Microsoft.AspNetCore.App.Ref.Internal**: [from 8.0.0-rtm.23514.1 to 8.0.0-rtm.23516.6][1] [1]: https://github.com/dotnet/aspnetcore/compare/faf594a066...02bdf7077b [DependencyUpdate]: <> (End) [marker]: <> (End:319094f3-ed78-47c4-53e7-08d8e409d87d) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7a7e0ba0da..bd3cff390c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer c65382f00216756e0fb192cc2fd6513448d2679d - + https://github.com/dotnet/aspnetcore - faf594a06685882b29522bede329f94db94bb3c1 + 02bdf7077b8d96039bd39d6189a1abdefc41e65e - + https://github.com/dotnet/aspnetcore - faf594a06685882b29522bede329f94db94bb3c1 + 02bdf7077b8d96039bd39d6189a1abdefc41e65e https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 3305913534..05f30c7c63 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23516.15 8.0.0-rtm.23516.15 - 8.0.0-rtm.23514.1 - 8.0.0-rtm.23514.1 + 8.0.0-rtm.23516.6 + 8.0.0-rtm.23516.6 8.0.100-rtm.23513.4 From d9ee004b45e35e833917a3b5aa18f5484c832417 Mon Sep 17 00:00:00 2001 From: kkeirstead <85592574+kkeirstead@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:59:18 -0700 Subject: [PATCH 64/82] [Dotnet Monitor] Small Fix For SDM Triggers (#4332) Small bug fix on the dotnet-monitor side from the unification work - we previously didn't create a payload for `BeginInstrumentReporting`, and thus only checked that a payload wasn't an error or the end of a counter. The code now leverages the newly-added `IsValuePublishedEvent` from the unification to only apply to the correct payloads. --- .../SystemDiagnosticsMetricsTriggerImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs index d0e4e28087..c10d87f1a8 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/SystemDiagnosticsMetricsTrigger/SystemDiagnosticsMetricsTriggerImpl.cs @@ -55,7 +55,7 @@ public bool HasSatisfiedCondition(ICounterPayload payload) { EventType eventType = payload.EventType; - if (eventType.IsError() || eventType == EventType.CounterEnded) + if (!eventType.IsValuePublishedEvent()) { // not currently logging the error messages From eac849b91757712fe9ee73945e1bd450da33689c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:23:05 +0000 Subject: [PATCH 65/82] [main] Update dependencies from microsoft/clrmd (#4339) [main] Update dependencies from microsoft/clrmd --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index bd3cff390c..a44739dde2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 21508f9ab057e19d4060855647edeb376ac7d59d - + https://github.com/microsoft/clrmd - 70994fd922058fb99403b4a96944286dafd26ea0 + b64f583396784300c7ded11d22c65053197e958a - + https://github.com/microsoft/clrmd - 70994fd922058fb99403b4a96944286dafd26ea0 + b64f583396784300c7ded11d22c65053197e958a diff --git a/eng/Versions.props b/eng/Versions.props index 05f30c7c63..f0e6e869b4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0 6.0.0 - 3.1.451301 + 3.1.451701 16.11.27-beta1.23180.1 3.0.7 6.0.0 From f7d94a9cf90da3e22d5bf8ebbdcc783f8ff7122f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:28:42 +0000 Subject: [PATCH 66/82] [main] Update dependencies from dotnet/runtime (#4340) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a44739dde2..db59f889ea 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore 02bdf7077b8d96039bd39d6189a1abdefc41e65e - + https://github.com/dotnet/runtime - 567f81e719091bd1ba3c751d49bacee2c6a45e66 + 6f7af556d2761b0c93299cb88c61e4b747d6176a - + https://github.com/dotnet/runtime - 567f81e719091bd1ba3c751d49bacee2c6a45e66 + 6f7af556d2761b0c93299cb88c61e4b747d6176a https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index f0e6e869b4..0c40fbf8ec 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.451601 - 8.0.0-rtm.23516.15 - 8.0.0-rtm.23516.15 + 8.0.0-rtm.23517.16 + 8.0.0-rtm.23517.16 8.0.0-rtm.23516.6 8.0.0-rtm.23516.6 From c61893e8b2d33215527c11921ee7976435393957 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:49:10 -0700 Subject: [PATCH 67/82] [main] Update dependencies from dotnet/aspnetcore (#4338) This pull request updates the following dependencies [marker]: <> (Begin:319094f3-ed78-47c4-53e7-08d8e409d87d) ## From https://github.com/dotnet/aspnetcore - **Subscription**: 319094f3-ed78-47c4-53e7-08d8e409d87d - **Build**: 20231017.6 - **Date Produced**: October 17, 2023 5:56:46 PM UTC - **Commit**: e3aba084cde27f8e718b188e3e9f7239e63d5816 - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.AspNetCore.App.Ref**: [from 8.0.0-rtm.23516.6 to 8.0.0-rtm.23517.6][1] - **Microsoft.AspNetCore.App.Ref.Internal**: [from 8.0.0-rtm.23516.6 to 8.0.0-rtm.23517.6][1] [1]: https://github.com/dotnet/aspnetcore/compare/02bdf7077b...e3aba084cd [DependencyUpdate]: <> (End) [marker]: <> (End:319094f3-ed78-47c4-53e7-08d8e409d87d) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index db59f889ea..e01b40cb14 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer c65382f00216756e0fb192cc2fd6513448d2679d - + https://github.com/dotnet/aspnetcore - 02bdf7077b8d96039bd39d6189a1abdefc41e65e + e3aba084cde27f8e718b188e3e9f7239e63d5816 - + https://github.com/dotnet/aspnetcore - 02bdf7077b8d96039bd39d6189a1abdefc41e65e + e3aba084cde27f8e718b188e3e9f7239e63d5816 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 0c40fbf8ec..f8671bf3b4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23517.16 8.0.0-rtm.23517.16 - 8.0.0-rtm.23516.6 - 8.0.0-rtm.23516.6 + 8.0.0-rtm.23517.6 + 8.0.0-rtm.23517.6 8.0.100-rtm.23513.4 From 8bf59d7b0bd50ca3bda60ab67bbd09afe6b7731f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:10:46 -0700 Subject: [PATCH 68/82] [main] Update dependencies from dotnet/runtime (#4344) This pull request updates the following dependencies [marker]: <> (Begin:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) ## From https://github.com/dotnet/runtime - **Subscription**: e4bfb556-e13c-47f6-eb5a-08d8e4d5099b - **Build**: 20231018.26 - **Date Produced**: October 19, 2023 7:20:11 AM UTC - **Commit**: c6e7ebdcb1b99c72990ae9f5ff95a75d5bb0f3ce - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.NETCore.App.Runtime.win-x64**: [from 8.0.0-rtm.23517.16 to 8.0.0-rtm.23518.26][1] - **VS.Redist.Common.NetCore.SharedFramework.x64.8.0**: [from 8.0.0-rtm.23517.16 to 8.0.0-rtm.23518.26][1] [1]: https://github.com/dotnet/runtime/compare/6f7af556d2...c6e7ebdcb1 [DependencyUpdate]: <> (End) [marker]: <> (End:e4bfb556-e13c-47f6-eb5a-08d8e4d5099b) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e01b40cb14..6272be3971 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore e3aba084cde27f8e718b188e3e9f7239e63d5816 - + https://github.com/dotnet/runtime - 6f7af556d2761b0c93299cb88c61e4b747d6176a + c6e7ebdcb1b99c72990ae9f5ff95a75d5bb0f3ce - + https://github.com/dotnet/runtime - 6f7af556d2761b0c93299cb88c61e4b747d6176a + c6e7ebdcb1b99c72990ae9f5ff95a75d5bb0f3ce https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index f8671bf3b4..e69ce2118f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.451601 - 8.0.0-rtm.23517.16 - 8.0.0-rtm.23517.16 + 8.0.0-rtm.23518.26 + 8.0.0-rtm.23518.26 8.0.0-rtm.23517.6 8.0.0-rtm.23517.6 From 1aff876483fd309585e11524156a09dc824ecb09 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:10:58 -0700 Subject: [PATCH 69/82] [main] Update dependencies from dotnet/aspnetcore (#4343) This pull request updates the following dependencies [marker]: <> (Begin:319094f3-ed78-47c4-53e7-08d8e409d87d) ## From https://github.com/dotnet/aspnetcore - **Subscription**: 319094f3-ed78-47c4-53e7-08d8e409d87d - **Build**: 20231018.6 - **Date Produced**: October 18, 2023 11:05:03 PM UTC - **Commit**: 198857c8a2211931739e6e08facfbab52f8dd023 - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.AspNetCore.App.Ref**: [from 8.0.0-rtm.23517.6 to 8.0.0-rtm.23518.6][1] - **Microsoft.AspNetCore.App.Ref.Internal**: [from 8.0.0-rtm.23517.6 to 8.0.0-rtm.23518.6][1] [1]: https://github.com/dotnet/aspnetcore/compare/e3aba084cd...198857c8a2 [DependencyUpdate]: <> (End) [marker]: <> (End:319094f3-ed78-47c4-53e7-08d8e409d87d) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 6272be3971..2e782c0153 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer c65382f00216756e0fb192cc2fd6513448d2679d - + https://github.com/dotnet/aspnetcore - e3aba084cde27f8e718b188e3e9f7239e63d5816 + 198857c8a2211931739e6e08facfbab52f8dd023 - + https://github.com/dotnet/aspnetcore - e3aba084cde27f8e718b188e3e9f7239e63d5816 + 198857c8a2211931739e6e08facfbab52f8dd023 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index e69ce2118f..c0786a0899 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23518.26 8.0.0-rtm.23518.26 - 8.0.0-rtm.23517.6 - 8.0.0-rtm.23517.6 + 8.0.0-rtm.23518.6 + 8.0.0-rtm.23518.6 8.0.100-rtm.23513.4 From 12f8ef371df096f56f71ac16b7fe96cbb92344f6 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Thu, 19 Oct 2023 18:55:27 +0200 Subject: [PATCH 70/82] Fix dotnet-dsrouter Android port reverse local/remote ports. (#4342) The use of automatic port forwarding on Android device used the same port as local and remote, that should be possible, but turns out there is some issue with `adb` around that configuration when running against a physical Android device (works when using port forwarding/reverse against Android emulator). This PR change the port defaults and align to Xamarin Android documentation as well as using different ports for local and remove when using -`tcps` or -`tcpc` arguments together with `--forward-port android`. When running using the "Android" connect profile, `dotnet-dsrouter android` we will default to 9001 as local/host and 9000 as remote/device port. When running with -`tcps` or -`tcpc` we will use passed port as local/host port and then set the remote/device port to local/host port - 1 in call to `adb reverse|forward`. Fixes https://github.com/dotnet/diagnostics/issues/4337 --- .../dotnet-dsrouter/ADBTcpRouterFactory.cs | 67 ++++++++++++------- .../DiagnosticsServerRouterCommands.cs | 10 +-- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs b/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs index 95c3abf28f..4cb9284ead 100644 --- a/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs +++ b/src/Tools/dotnet-dsrouter/ADBTcpRouterFactory.cs @@ -13,52 +13,52 @@ namespace Microsoft.Diagnostics.Tools.DiagnosticsServerRouter { internal static class ADBCommandExec { - public static bool AdbAddPortForward(int port, bool rethrow, ILogger logger) + public static bool AdbAddPortForward(int localPort, int remotePort, bool rethrow, ILogger logger) { bool ownsPortForward = false; - if (!RunAdbCommandInternal($"forward --list", $"tcp:{port}", 0, rethrow, logger)) + if (!RunAdbCommandInternal($"forward --list", $"tcp:{localPort}", 0, rethrow, logger)) { - ownsPortForward = RunAdbCommandInternal($"forward tcp:{port} tcp:{port}", "", 0, rethrow, logger); + ownsPortForward = RunAdbCommandInternal($"forward tcp:{localPort} tcp:{remotePort}", "", 0, rethrow, logger); if (!ownsPortForward) { - logger?.LogError($"Failed setting up port forward for tcp:{port}."); + logger?.LogError($"Failed setting up port forward for host tcp:{localPort} <-> device tcp:{remotePort}."); } } return ownsPortForward; } - public static bool AdbAddPortReverse(int port, bool rethrow, ILogger logger) + public static bool AdbAddPortReverse(int localPort, int remotePort, bool rethrow, ILogger logger) { bool ownsPortForward = false; - if (!RunAdbCommandInternal($"reverse --list", $"tcp:{port}", 0, rethrow, logger)) + if (!RunAdbCommandInternal($"reverse --list", $"tcp:{remotePort}", 0, rethrow, logger)) { - ownsPortForward = RunAdbCommandInternal($"reverse tcp:{port} tcp:{port}", "", 0, rethrow, logger); + ownsPortForward = RunAdbCommandInternal($"reverse tcp:{remotePort} tcp:{localPort}", "", 0, rethrow, logger); if (!ownsPortForward) { - logger?.LogError($"Failed setting up port forward for tcp:{port}."); + logger?.LogError($"Failed setting up port forward for host tcp:{localPort} <-> device tcp:{remotePort}."); } } return ownsPortForward; } - public static void AdbRemovePortForward(int port, bool ownsPortForward, bool rethrow, ILogger logger) + public static void AdbRemovePortForward(int localPort, int remotePort, bool ownsPortForward, bool rethrow, ILogger logger) { if (ownsPortForward) { - if (!RunAdbCommandInternal($"forward --remove tcp:{port}", "", 0, rethrow, logger)) + if (!RunAdbCommandInternal($"forward --remove tcp:{localPort}", "", 0, rethrow, logger)) { - logger?.LogError($"Failed removing port forward for tcp:{port}."); + logger?.LogError($"Failed setting up port forward for host tcp:{localPort} <-> device tcp:{remotePort}."); } } } - public static void AdbRemovePortReverse(int port, bool ownsPortForward, bool rethrow, ILogger logger) + public static void AdbRemovePortReverse(int localPort, int remotePort, bool ownsPortForward, bool rethrow, ILogger logger) { if (ownsPortForward) { - if (!RunAdbCommandInternal($"reverse --remove tcp:{port}", "", 0, rethrow, logger)) + if (!RunAdbCommandInternal($"reverse --remove tcp:{remotePort}", "", 0, rethrow, logger)) { - logger?.LogError($"Failed removing port forward for tcp:{port}."); + logger?.LogError($"Failed setting up port forward for host tcp:{localPort} <-> device tcp:{remotePort}."); } } } @@ -131,7 +131,8 @@ public static bool RunAdbCommandInternal(string command, string expectedOutput, internal sealed class ADBTcpServerRouterFactory : TcpServerRouterFactory { - private readonly int _port; + private readonly int _localPort; + private readonly int _remotePort; private bool _ownsPortReverse; private Task _portReverseTask; private CancellationTokenSource _portReverseTaskCancelToken; @@ -144,7 +145,13 @@ public static TcpServerRouterFactory CreateADBInstance(string tcpServer, int run public ADBTcpServerRouterFactory(string tcpServer, int runtimeTimeoutMs, ILogger logger) : base(tcpServer, runtimeTimeoutMs, logger) { - _port = new IpcTcpSocketEndPoint(tcpServer).EndPoint.Port; + _localPort = new IpcTcpSocketEndPoint(tcpServer).EndPoint.Port; + _remotePort = _localPort - 1; + + if (_remotePort <= 0) + { + throw new ArgumentException($"Invalid local/remote TCP endpoint ports {_localPort}/{_remotePort}."); + } } public override void Start() @@ -152,7 +159,7 @@ public override void Start() // Enable port reverse. try { - _ownsPortReverse = ADBCommandExec.AdbAddPortReverse(_port, true, Logger); + _ownsPortReverse = ADBCommandExec.AdbAddPortReverse(_localPort, _remotePort, true, Logger); } catch { @@ -160,7 +167,8 @@ public override void Start() Logger.LogError("Failed setting up adb port reverse." + " This might lead to problems communicating with Android application." + " Make sure env variable ANDROID_SDK_ROOT is set and points to an Android SDK." + - $" Executing with unknown adb status for port {_port}."); + $" Executing with unknown adb status for port {_localPort}."); + base.Start(); return; } @@ -170,7 +178,7 @@ public override void Start() while (await timer.WaitForNextTickAsync(_portReverseTaskCancelToken.Token).ConfigureAwait(false) && !_portReverseTaskCancelToken.Token.IsCancellationRequested) { // Make sure reverse port configuration is still active. - if (ADBCommandExec.AdbAddPortReverse(_port, false, Logger) && !_ownsPortReverse) + if (ADBCommandExec.AdbAddPortReverse(_localPort, _remotePort, false, Logger) && !_ownsPortReverse) { _ownsPortReverse = true; } @@ -192,14 +200,15 @@ public override async Task Stop() catch { } // Disable port reverse. - ADBCommandExec.AdbRemovePortReverse(_port, _ownsPortReverse, false, Logger); + ADBCommandExec.AdbRemovePortReverse(_localPort, _remotePort, _ownsPortReverse, false, Logger); _ownsPortReverse = false; } } internal sealed class ADBTcpClientRouterFactory : TcpClientRouterFactory { - private readonly int _port; + private readonly int _localPort; + private readonly int _remotePort; private bool _ownsPortForward; private Task _portForwardTask; private CancellationTokenSource _portForwardTaskCancelToken; @@ -212,7 +221,13 @@ public static TcpClientRouterFactory CreateADBInstance(string tcpClient, int run public ADBTcpClientRouterFactory(string tcpClient, int runtimeTimeoutMs, ILogger logger) : base(tcpClient, runtimeTimeoutMs, logger) { - _port = new IpcTcpSocketEndPoint(tcpClient).EndPoint.Port; + _localPort = new IpcTcpSocketEndPoint(tcpClient).EndPoint.Port; + _remotePort = _localPort - 1; + + if (_remotePort <= 0) + { + throw new ArgumentException($"Invalid local/remote TCP endpoint ports {_localPort}/{_remotePort}."); + } } public override void Start() @@ -220,7 +235,7 @@ public override void Start() // Enable port forwarding. try { - _ownsPortForward = ADBCommandExec.AdbAddPortForward(_port, true, Logger); + _ownsPortForward = ADBCommandExec.AdbAddPortForward(_localPort, _remotePort, true, Logger); } catch { @@ -228,7 +243,7 @@ public override void Start() Logger.LogError("Failed setting up adb port forward." + " This might lead to problems communicating with Android application." + " Make sure env variable ANDROID_SDK_ROOT is set and points to an Android SDK." + - $" Executing with unknown adb status for port {_port}."); + $" Executing with unknown adb status for port {_localPort}."); return; } @@ -238,7 +253,7 @@ public override void Start() while (await timer.WaitForNextTickAsync(_portForwardTaskCancelToken.Token).ConfigureAwait(false) && !_portForwardTaskCancelToken.Token.IsCancellationRequested) { // Make sure forward port configuration is still active. - if (ADBCommandExec.AdbAddPortForward(_port, false, Logger) && !_ownsPortForward) + if (ADBCommandExec.AdbAddPortForward(_localPort, _remotePort, false, Logger) && !_ownsPortForward) { _ownsPortForward = true; } @@ -256,7 +271,7 @@ public override void Stop() catch { } // Disable port forwarding. - ADBCommandExec.AdbRemovePortForward(_port, _ownsPortForward, false, Logger); + ADBCommandExec.AdbRemovePortForward(_localPort, _remotePort, _ownsPortForward, false, Logger); _ownsPortForward = false; } } diff --git a/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs b/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs index 20fd5b312b..213c801990 100644 --- a/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs +++ b/src/Tools/dotnet-dsrouter/DiagnosticsServerRouterCommands.cs @@ -336,7 +336,7 @@ public async Task RunIpcClientWebSocketServerRouter(CancellationToken token public async Task RunIpcServerIOSSimulatorRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - if (info) + if (info || ParseLogLevel(verbose) <= LogLevel.Information) { logRouterUsageInfo("ios simulator", "127.0.0.1:9000", true); } @@ -346,7 +346,7 @@ public async Task RunIpcServerIOSSimulatorRouter(CancellationToken token, i public async Task RunIpcServerIOSRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - if (info) + if (info || ParseLogLevel(verbose) <= LogLevel.Information) { logRouterUsageInfo("ios device", "127.0.0.1:9000", true); } @@ -356,7 +356,7 @@ public async Task RunIpcServerIOSRouter(CancellationToken token, int runtim public async Task RunIpcServerAndroidEmulatorRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - if (info) + if (info || ParseLogLevel(verbose) <= LogLevel.Information) { logRouterUsageInfo("android emulator", "10.0.2.2:9000", false); } @@ -366,12 +366,12 @@ public async Task RunIpcServerAndroidEmulatorRouter(CancellationToken token public async Task RunIpcServerAndroidRouter(CancellationToken token, int runtimeTimeout, string verbose, bool info) { - if (info) + if (info || ParseLogLevel(verbose) <= LogLevel.Information) { logRouterUsageInfo("android device", "127.0.0.1:9000", false); } - return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9000", runtimeTimeout, verbose, "Android").ConfigureAwait(false); + return await RunIpcServerTcpServerRouter(token, "", "127.0.0.1:9001", runtimeTimeout, verbose, "Android").ConfigureAwait(false); } private static string GetDefaultIpcServerPath(ILogger logger) From 6e5d61e7bc2f713685c5ffbc1776d81011895798 Mon Sep 17 00:00:00 2001 From: Joe Schmitt <1146681+schmittjoseph@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:32:17 -0700 Subject: [PATCH 71/82] Fix log scope construction (#4323) This PR reworks how log scopes are reconstructed. The current implementation has a several issues that results in it only working in single-threaded contexts. Once multiple threads are involved, scopes will be incorrectly associated to log messages in addition to scopes being deleted early while they are still active. I've described how the new approach works in the source code so it doesn't get lost after this PR is merged. I've also left a comment with a visual representation of edge cases with concurrent traces. /cc @dotnet/dotnet-monitor --------- Co-authored-by: Wiktor Kopec --- .../LoggingSourceConfiguration.cs | 19 +++- .../Logs/EventLogsPipeline.cs | 104 +++++++++++------- .../Logs/EventLogsPipelineSettings.cs | 4 + src/tests/EventPipeTracee/Program.cs | 48 ++++++-- .../EventLogsPipelineUnitTests.cs | 12 ++ 5 files changed, 138 insertions(+), 49 deletions(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LoggingSourceConfiguration.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LoggingSourceConfiguration.cs index 82dae4f329..d65acdee05 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LoggingSourceConfiguration.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LoggingSourceConfiguration.cs @@ -6,6 +6,7 @@ using System.Diagnostics.Tracing; using System.Text; using Microsoft.Diagnostics.NETCore.Client; +using Microsoft.Diagnostics.Tracing.Parsers; using Microsoft.Extensions.Logging; namespace Microsoft.Diagnostics.Monitoring.EventPipe @@ -15,21 +16,24 @@ public class LoggingSourceConfiguration : MonitoringSourceConfiguration private readonly string _filterSpecs; private readonly long _keywords; private readonly EventLevel _level; + private readonly bool _collectScopes; /// /// Creates a new logging source configuration. /// - public LoggingSourceConfiguration(LogLevel level, LogMessageType messageType, IDictionary filterSpecs, bool useAppFilters) + public LoggingSourceConfiguration(LogLevel level, LogMessageType messageType, IDictionary filterSpecs, bool useAppFilters, + bool collectScopes) { RequestRundown = false; _filterSpecs = ToFilterSpecsString(filterSpecs, useAppFilters); _keywords = (long)ToKeywords(messageType); _level = ToEventLevel(level); + _collectScopes = collectScopes; } public override IList GetProviders() { - return new List() + List providers = new() { new EventPipeProvider( MicrosoftExtensionsLoggingProviderName, @@ -41,6 +45,17 @@ public override IList GetProviders() } ) }; + + if (_collectScopes) + { + // Activity correlation + providers.Add(new EventPipeProvider( + TplEventSource, + EventLevel.Informational, + (long)TplEtwProviderTraceEventParser.Keywords.TasksFlowActivityIds)); + } + + return providers; } private static string ToFilterSpecsString(IDictionary filterSpecs, bool useAppFilters) diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs index 063bd342fb..0b6f0824f6 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs @@ -30,7 +30,8 @@ protected override MonitoringSourceConfiguration CreateConfiguration() Settings.LogLevel, LogMessageType.FormattedMessage | LogMessageType.JsonMessage, Settings.FilterSpecs, - Settings.UseAppFilters); + Settings.UseAppFilters, + Settings.CollectScopes); } catch (NotSupportedException ex) { @@ -42,45 +43,55 @@ protected override Task OnEventSourceAvailable(EventPipeEventSource eventSource, { string lastFormattedMessage = string.Empty; - Dictionary logActivities = new(); - Stack stack = new(); + // + // We enable TplEventSource's TasksFlowActivityIds as part of our configuration to enable activity correlation. + // This means that each time an event start occurs the current ActivityId will branch creating a new one with a RelatedActivityId equal to where it branched from. + // Combining this with the fact that scopes are handled as ActivityJson/{Start,Stop} means the ActivityId will branch each time a scope starts. + // When a log message occurs, it'll have an ActivityId equal to the latest applicable scope. + // + // By maintaining a tree with the branching data, we can construct the full scope for a log message: + // - Each time the ActivityId branches, create a node in the tree with it's parent being the node corresponding to the RelatedActivityId. + // - Each node has corresponding scope data. + // - When a log message occurs, grab the node with the corresponding ActivityId and backtrack to the root of the tree. Each node visited is included as part of the log's scope. + // + // NOTE: There are edge cases with concurrent traces, this is described in greater detail above our backtracking code. + // + Dictionary activityIdToScope = new(); + + if (Settings.CollectScopes) + { + eventSource.Dynamic.AddCallbackForProviderEvent(LoggingSourceConfiguration.MicrosoftExtensionsLoggingProviderName, "ActivityJson/Start", (traceEvent) => { + if (traceEvent.ActivityID == Guid.Empty) + { + // Unexpected + return; + } - eventSource.Dynamic.AddCallbackForProviderEvent(LoggingSourceConfiguration.MicrosoftExtensionsLoggingProviderName, "ActivityJson/Start", (traceEvent) => { - int factoryId = (int)traceEvent.PayloadByName("FactoryID"); - string categoryName = (string)traceEvent.PayloadByName("LoggerName"); - string argsJson = (string)traceEvent.PayloadByName("ArgumentsJson"); + string argsJson = (string)traceEvent.PayloadByName("ArgumentsJson"); - // TODO: Store this information by logger factory id - LogActivityItem item = new() - { - ActivityID = traceEvent.ActivityID, - ScopedObject = new LogObject(JsonDocument.Parse(argsJson).RootElement), - }; + // TODO: Store this information by logger factory id + LogScopeItem item = new() + { + ActivityID = traceEvent.ActivityID, + ScopedObject = new LogObject(JsonDocument.Parse(argsJson).RootElement), + }; - if (stack.Count > 0) - { - Guid parentId = stack.Peek(); - if (logActivities.TryGetValue(parentId, out LogActivityItem parentItem)) + if (activityIdToScope.TryGetValue(traceEvent.RelatedActivityID, out LogScopeItem parentItem)) { item.Parent = parentItem; } - } - stack.Push(traceEvent.ActivityID); - logActivities[traceEvent.ActivityID] = item; - }); - - eventSource.Dynamic.AddCallbackForProviderEvent(LoggingSourceConfiguration.MicrosoftExtensionsLoggingProviderName, "ActivityJson/Stop", (traceEvent) => { - int factoryId = (int)traceEvent.PayloadByName("FactoryID"); - string categoryName = (string)traceEvent.PayloadByName("LoggerName"); + if (activityIdToScope.Count < Settings.ScopeLimit || activityIdToScope.ContainsKey(traceEvent.ActivityID)) + { + activityIdToScope[traceEvent.ActivityID] = item; + } + }); - //If we begin collection in the middle of a request, we can receive a stop without having a start. - if (stack.Count > 0) - { - stack.Pop(); - logActivities.Remove(traceEvent.ActivityID); - } - }); + eventSource.Dynamic.AddCallbackForProviderEvent(LoggingSourceConfiguration.MicrosoftExtensionsLoggingProviderName, "ActivityJson/Stop", (traceEvent) => { + // Not all stopped event ActivityIds will exist in our tree since there may be scopes already active when we start the trace session. + _ = activityIdToScope.Remove(traceEvent.ActivityID); + }); + } eventSource.Dynamic.AddCallbackForProviderEvent(LoggingSourceConfiguration.MicrosoftExtensionsLoggingProviderName, "MessageJson", (traceEvent) => { // Level, FactoryID, LoggerName, EventID, EventName, ExceptionJson, ArgumentsJson @@ -110,14 +121,29 @@ protected override Task OnEventSourceAvailable(EventPipeEventSource eventSource, ILogger logger = _factory.CreateLogger(categoryName); List scopes = new(); - if (logActivities.TryGetValue(traceEvent.ActivityID, out LogActivityItem logActivityItem)) + // + // The MessageJson event will occur with an ActivityId equal to the most relevant activity branch and we can backtrack to the root of the tree + // to grab all applicable scopes (where each node we visit is an applicable scope). + // + // Ideally the ActivityId will always exist in our tree, however if another trace is ongoing that is interested in an event start + // within the same async context as our log message then there will be nodes+edges that our tree is unaware of. + // This is because TplEventSource's TasksFlowActivityIds is a singleton implementation that is shared for all traces, + // regardless of if the other traces have TasksFlowActivityIds enabled. + // + // In this scenario there's still a chance that only a single branch has occurred and we're the first event logged with the newly branched ActivityId. + // In which case we can use the RelatedActivityId to find our way back onto the tree. + // + // If not then we will be operating on a subtree without a way of getting back to the root node and will only have a subset (if any) of the + // applicable scopes. + // + if (activityIdToScope.TryGetValue(traceEvent.ActivityID, out LogScopeItem scopeItem) || + activityIdToScope.TryGetValue(traceEvent.RelatedActivityID, out scopeItem)) { - // REVIEW: Does order matter here? We're combining everything anyways. - while (logActivityItem != null) + while (scopeItem != null) { - scopes.Add(logger.BeginScope(logActivityItem.ScopedObject)); + scopes.Add(logger.BeginScope(scopeItem.ScopedObject)); - logActivityItem = logActivityItem.Parent; + scopeItem = scopeItem.Parent; } } @@ -190,13 +216,13 @@ private static string MessageFormatter(object state, Exception error) return state.ToString(); } - private class LogActivityItem + private class LogScopeItem { public Guid ActivityID { get; set; } public LogObject ScopedObject { get; set; } - public LogActivityItem Parent { get; set; } + public LogScopeItem Parent { get; set; } } } } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipelineSettings.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipelineSettings.cs index c505b81954..f8133f53a1 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipelineSettings.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipelineSettings.cs @@ -16,5 +16,9 @@ internal class EventLogsPipelineSettings : EventSourcePipelineSettings // This setting will collect logs for the application-defined categories and levels. public bool UseAppFilters { get; set; } = true; + + public bool CollectScopes { get; set; } = true; + + public int ScopeLimit { get; set; } = 10_000; } } diff --git a/src/tests/EventPipeTracee/Program.cs b/src/tests/EventPipeTracee/Program.cs index 0d379b97d7..1decd119a0 100644 --- a/src/tests/EventPipeTracee/Program.cs +++ b/src/tests/EventPipeTracee/Program.cs @@ -17,7 +17,7 @@ internal static class Program { private const string AppLoggerCategoryName = "AppLoggerCategory"; - public static int Main(string[] args) + public static async Task Main(string[] args) { int pid = Process.GetCurrentProcess().Id; string pipeServerName = args.Length > 0 ? args[0] : null; @@ -89,7 +89,7 @@ public static int Main(string[] args) }).ConfigureAwait(true); } - TestBodyCore(customCategoryLogger, appCategoryLogger); + await TestBodyCore(customCategoryLogger, appCategoryLogger).ConfigureAwait(false); Console.WriteLine($"{pid} EventPipeTracee: signal end of test data"); Console.Out.Flush(); @@ -123,17 +123,49 @@ public static int Main(string[] args) } // TODO At some point we may want parameters to choose different test bodies. - private static void TestBodyCore(ILogger customCategoryLogger, ILogger appCategoryLogger) + private static async Task TestBodyCore(ILogger customCategoryLogger, ILogger appCategoryLogger) { - //Json data is always converted to strings for ActivityStart events. - using (IDisposable scope = customCategoryLogger.BeginScope(new Dictionary { + TaskCompletionSource secondSetScopes = new(TaskCreationOptions.RunContinuationsAsynchronously); + TaskCompletionSource firstFinishedLogging = new(TaskCreationOptions.RunContinuationsAsynchronously); + TaskCompletionSource secondFinishedLogging = new(TaskCreationOptions.RunContinuationsAsynchronously); + + Task firstTask = Task.Run(async () => { + using (IDisposable scope = customCategoryLogger.BeginScope(new Dictionary { { "IntValue", "5" }, { "BoolValue", "true" }, { "StringValue", "test" } }.ToList())) - { - customCategoryLogger.LogInformation("Some warning message with {Arg}", 6); - } + { + // Await for the other task to add its scopes. + await secondSetScopes.Task.ConfigureAwait(false); + + customCategoryLogger.LogInformation("Some warning message with {Arg}", 6); + + // Signal other task to log + firstFinishedLogging.SetResult(); + + // Do not dispose scopes until the other task is done + await secondFinishedLogging.Task.ConfigureAwait(false); + } + }); + + Task secondTask = Task.Run(async () => { + using (IDisposable scope = customCategoryLogger.BeginScope(new Dictionary { + { "IntValue", "6" }, + { "BoolValue", "false" }, + { "StringValue", "string" } }.ToList())) + { + // Signal that we added our scopes and wait for the other task to log + secondSetScopes.SetResult(); + await firstFinishedLogging.Task.ConfigureAwait(false); + customCategoryLogger.LogInformation("Some other message with {Arg}", 7); + secondFinishedLogging.SetResult(); + } + }); + await firstTask.ConfigureAwait(false); + await secondTask.ConfigureAwait(false); + + //Json data is always converted to strings for ActivityStart events. customCategoryLogger.LogWarning(new EventId(7, "AnotherEventId"), "Another message"); appCategoryLogger.LogInformation("Information message."); diff --git a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs index dfe8753b1f..c449efc5d1 100644 --- a/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs +++ b/src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs @@ -263,6 +263,18 @@ private static void ValidateLoggerRemoteCategoryInformationMessage(StreamReader Assert.Equal(string.Empty, result.EventName); Validate(result.Scopes, ("BoolValue", "true"), ("StringValue", "test"), ("IntValue", "5")); Validate(result.Arguments, ("Arg", "6")); + + message = reader.ReadLine(); + Assert.NotNull(message); + + result = JsonSerializer.Deserialize(message); + Assert.Equal("Some other message with 7", result.Message); + Assert.Equal(LoggerRemoteTestName, result.Category); + Assert.Equal("Information", result.LogLevel); + Assert.Equal(0, result.EventId); + Assert.Equal(string.Empty, result.EventName); + Validate(result.Scopes, ("BoolValue", "false"), ("StringValue", "string"), ("IntValue", "6")); + Validate(result.Arguments, ("Arg", "7")); } private static void ValidateLoggerRemoteCategoryWarningMessage(StreamReader reader) From a6630008a9026386d4e7a5af7c06e62e8ff417fb Mon Sep 17 00:00:00 2001 From: mikelle-rogers <45022607+mikelle-rogers@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:49:51 -0700 Subject: [PATCH 72/82] Retire Duplicate Documentation (#4341) Retire duplicate documentation by redirecting to docs that will be updated. fixes #515 --------- Co-authored-by: Noah Falk --- documentation/debugging-coredump.md | 2 ++ documentation/design-docs/dotnet-tools.md | 3 ++- documentation/design-docs/eventcounters.md | 4 +++- documentation/dotnet-counters-instructions.md | 4 +++- documentation/dotnet-dump-instructions.md | 2 ++ documentation/dotnet-gcdump-instructions.md | 2 ++ documentation/dotnet-trace-instructions.md | 2 ++ documentation/installing-sos-instructions.md | 2 ++ documentation/installing-sos-windows-instructions.md | 2 ++ documentation/sos.md | 2 ++ .../tutorial/app_is_leaking_memory_eventual_crash.md | 2 ++ documentation/tutorial/app_running_slow_highcpu.md | 2 ++ documentation/tutorial/hung_app.md | 2 ++ documentation/tutorial/installing_the_diagnostics_tools.md | 2 ++ 14 files changed, 30 insertions(+), 3 deletions(-) diff --git a/documentation/debugging-coredump.md b/documentation/debugging-coredump.md index eb43cee166..4cfb0e33e9 100644 --- a/documentation/debugging-coredump.md +++ b/documentation/debugging-coredump.md @@ -1,3 +1,5 @@ +This documentation is now located at [debug-linux-dumps](https://learn.microsoft.com/dotnet/core/diagnostics/debug-linux-dumps) This doc is no longer being updated. + Debugging Linux or MacOS Core Dump ================================== diff --git a/documentation/design-docs/dotnet-tools.md b/documentation/design-docs/dotnet-tools.md index 0af1b58b44..bd36698d01 100644 --- a/documentation/design-docs/dotnet-tools.md +++ b/documentation/design-docs/dotnet-tools.md @@ -1,4 +1,5 @@ -# Dotnet Diagnostic Tools CLI Design +For the latest public documentation on using dotnet-* diagnostic tools see [dotnet-trace](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace), [dotnet-counters](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-counters), [dotnet-dump](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dump), [dotnet-gcdump](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-gcdump), [dotnet-dsrouter](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter), [dotnet-monitor](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-monitor), [dotnet-symbol](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-symbol), [dotnet-sos](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). +# Dotnet Diagnostic Tools CLI Design ## User workflows diff --git a/documentation/design-docs/eventcounters.md b/documentation/design-docs/eventcounters.md index 2815eca2c9..0cbd98fe2e 100644 --- a/documentation/design-docs/eventcounters.md +++ b/documentation/design-docs/eventcounters.md @@ -1,3 +1,5 @@ +The newest documentation is now maintained at [eventcounters](https://learn.microsoft.com/dotnet/core/diagnostics/event-counters). This doc is no longer being updated. + # EventCounters in .NET Core 3.0 ## Introduction @@ -332,4 +334,4 @@ The official dotnet-trace documentation contains a [section](https://github.com/ TraceEvent is a managed library that makes it easy to consume ETW and EventPipe events. For more information, refer to the [TraceEvent Library Programmers Guide](https://github.com/Microsoft/perfview/blob/main/documentation/TraceEvent/TraceEventProgrammersGuide.md). -For some more detailed code samples, you can also try reading [Criteo Labs blog](https://medium.com/criteo-labs/net-core-counters-internals-how-to-integrate-counters-in-your-monitoring-pipeline-5354cd61b42e) on how to do this. \ No newline at end of file +For some more detailed code samples, you can also try reading [Criteo Labs blog](https://medium.com/criteo-labs/net-core-counters-internals-how-to-integrate-counters-in-your-monitoring-pipeline-5354cd61b42e) on how to do this. diff --git a/documentation/dotnet-counters-instructions.md b/documentation/dotnet-counters-instructions.md index 4faf0868b4..86beeba17c 100644 --- a/documentation/dotnet-counters-instructions.md +++ b/documentation/dotnet-counters-instructions.md @@ -1,3 +1,5 @@ +This documentation is now being maintained here: [dotnet-counters](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-counters). This doc is no longer being updated. + # dotnet-counters NOTE: This documentation page may contain information on some features that are still work-in-progress. For most up-to-date documentation on released version of `dotnet-counters`, please refer to [its official documentation](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) page. @@ -209,4 +211,4 @@ $ dotnet-counters monitor --counters System.Runtime[assembly-count] -- my-aspnet provider and counter names, use the list command. -- (for target applications running .NET 5.0 or later only) - After the collection configuration parameters, the user can append `--` followed by a command to start a .NET application with at least a 5.0 runtime. `dotnet-counters` will launch a process with the provided command and collect the requested metrics. \ No newline at end of file + After the collection configuration parameters, the user can append `--` followed by a command to start a .NET application with at least a 5.0 runtime. `dotnet-counters` will launch a process with the provided command and collect the requested metrics. diff --git a/documentation/dotnet-dump-instructions.md b/documentation/dotnet-dump-instructions.md index c75ac8a606..b3ea9ab8fc 100644 --- a/documentation/dotnet-dump-instructions.md +++ b/documentation/dotnet-dump-instructions.md @@ -1,3 +1,5 @@ +This documentation is now being maintained at [dotnet-dump](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dump). This doc is no longer being updated. + Dump collection and analysis utility (dotnet-dump) ================================================== diff --git a/documentation/dotnet-gcdump-instructions.md b/documentation/dotnet-gcdump-instructions.md index 078785bc83..7ecbc75e58 100644 --- a/documentation/dotnet-gcdump-instructions.md +++ b/documentation/dotnet-gcdump-instructions.md @@ -1,3 +1,5 @@ +This documentation is now being maintained at [dotnet-gcdump](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-gcdump). This doc is no longer being updated. + # Heap Analysis Tool (dotnet-gcdump) NOTE: This documentation page may contain information on some features that are still work-in-progress. For most up-to-date documentation on released version of `dotnet-gcdump`, please refer to [its official documentation](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-gcdump) page. diff --git a/documentation/dotnet-trace-instructions.md b/documentation/dotnet-trace-instructions.md index 154996a927..4ad26dfe64 100644 --- a/documentation/dotnet-trace-instructions.md +++ b/documentation/dotnet-trace-instructions.md @@ -1,3 +1,5 @@ +This documentation is now being maintained at [dotnet-trace](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace). This doc is no longer being updated. + # Trace for performance analysis utility (dotnet-trace) NOTE: This documentation page may contain information on some features that are still work-in-progress. For most up-to-date documentation on released version of `dotnet-trace`, please refer to [its official documentation](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) page. diff --git a/documentation/installing-sos-instructions.md b/documentation/installing-sos-instructions.md index 4db1420e32..620e5c9cc5 100644 --- a/documentation/installing-sos-instructions.md +++ b/documentation/installing-sos-instructions.md @@ -1,3 +1,5 @@ +This documentation is now being maintained at [dotnet-sos](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). This doc is no longer being updated. + Installing SOS on Linux and MacOS ================================= diff --git a/documentation/installing-sos-windows-instructions.md b/documentation/installing-sos-windows-instructions.md index cbde73e79e..923f29004c 100644 --- a/documentation/installing-sos-windows-instructions.md +++ b/documentation/installing-sos-windows-instructions.md @@ -1,3 +1,5 @@ +This documentation is now being maintained at [dotnet-sos](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). This doc is no longer being updated. + Installing SOS on Windows ========================= diff --git a/documentation/sos.md b/documentation/sos.md index e4de62587d..45d83740c6 100644 --- a/documentation/sos.md +++ b/documentation/sos.md @@ -1,3 +1,5 @@ +This documentation is now being maintained at [dotnet-sos](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). This doc is no longer being updated. + SOS === diff --git a/documentation/tutorial/app_is_leaking_memory_eventual_crash.md b/documentation/tutorial/app_is_leaking_memory_eventual_crash.md index dc29ecf6f9..bb97c26f01 100644 --- a/documentation/tutorial/app_is_leaking_memory_eventual_crash.md +++ b/documentation/tutorial/app_is_leaking_memory_eventual_crash.md @@ -1,3 +1,5 @@ +The newest documentation is now being maintained at [debug-memory-leak](https://learn.microsoft.com/dotnet/core/diagnostics/debug-memory-leak). This documentation is no longer being updated. + # App is leaking memory (eventual crash/stops responding) http://localhost:5000/api/diagscenario/memleak/{kb} diff --git a/documentation/tutorial/app_running_slow_highcpu.md b/documentation/tutorial/app_running_slow_highcpu.md index aa56ea3558..2fd8fef245 100644 --- a/documentation/tutorial/app_running_slow_highcpu.md +++ b/documentation/tutorial/app_running_slow_highcpu.md @@ -1,3 +1,5 @@ +The newest documentation is now being maintained at [debug-highcpu](https://learn.microsoft.com/dotnet/core/diagnostics/debug-highcpu). This documentation is no longer being updated. + # App is running slow (due to high CPU) http://localhost:5000/api/diagscenario/highcpu/{milliseconds} diff --git a/documentation/tutorial/hung_app.md b/documentation/tutorial/hung_app.md index a4fa89f390..3e05005629 100644 --- a/documentation/tutorial/hung_app.md +++ b/documentation/tutorial/hung_app.md @@ -1,3 +1,5 @@ +The newest documentation is now being maintained at [debug-deadlock](https://learn.microsoft.com/dotnet/core/diagnostics/debug-deadlock). This documentation is no longer being updated. + # App stops responding **IMPORTANT: This tutorial uses API/methods available in dotnet core preview 5. These API/methods are _subject to change._** diff --git a/documentation/tutorial/installing_the_diagnostics_tools.md b/documentation/tutorial/installing_the_diagnostics_tools.md index e3efd27750..56bf5e78c8 100644 --- a/documentation/tutorial/installing_the_diagnostics_tools.md +++ b/documentation/tutorial/installing_the_diagnostics_tools.md @@ -1,3 +1,5 @@ +The newest documentation is now maintained at [dotnet-trace](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace), [dotnet-counters](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-counters), [dotnet-dump](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dump), [dotnet-gcdump](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-gcdump), [dotnet-dsrouter](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter), [dotnet-monitor](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-monitor), [dotnet-symbol](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-symbol), [dotnet-sos](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). This documentation is no longer being updated. + # Installing the diagnostics tools Depending on the diagnostics scenario you will use one or more of the tools below to get to root cause. By default, these tools are installed to ~/.dotnet/tools. From a6920fefeb98df4a20c1ab07d8bb48007788664b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 13:43:29 +0000 Subject: [PATCH 73/82] [main] Update dependencies from dotnet/source-build-reference-packages (#4347) [main] Update dependencies from dotnet/source-build-reference-packages --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2e782c0153..e064654464 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -47,9 +47,9 @@ https://github.com/dotnet/runtime c6e7ebdcb1b99c72990ae9f5ff95a75d5bb0f3ce - + https://github.com/dotnet/source-build-reference-packages - b35b2b088257f17a7a6d836b22aea06a124e5303 + d3fbf3c3d4c4f142ea12efceaa6efece9ad2e6b5 diff --git a/eng/Versions.props b/eng/Versions.props index c0786a0899..04533743c6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -67,7 +67,7 @@ 7.0.0-beta.22316.2 10.0.18362 13.0.1 - 9.0.0-alpha.1.23516.3 + 9.0.0-alpha.1.23519.2 3.11.0 From 0539a730d337da7b0626e1e001de5cfe99042276 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:20:43 -0700 Subject: [PATCH 74/82] [main] Update dependencies from dotnet/aspnetcore (#4346) This pull request updates the following dependencies [marker]: <> (Begin:319094f3-ed78-47c4-53e7-08d8e409d87d) ## From https://github.com/dotnet/aspnetcore - **Subscription**: 319094f3-ed78-47c4-53e7-08d8e409d87d - **Build**: 20231019.14 - **Date Produced**: October 20, 2023 5:00:06 AM UTC - **Commit**: e4ba445101d9bdae24baa6b332d19a11ac8922be - **Branch**: refs/heads/release/8.0 [DependencyUpdate]: <> (Begin) - **Updates**: - **Microsoft.AspNetCore.App.Ref**: [from 8.0.0-rtm.23518.6 to 8.0.0-rtm.23519.14][1] - **Microsoft.AspNetCore.App.Ref.Internal**: [from 8.0.0-rtm.23518.6 to 8.0.0-rtm.23519.14][1] [1]: https://github.com/dotnet/aspnetcore/compare/198857c8a2...e4ba445101 [DependencyUpdate]: <> (End) [marker]: <> (End:319094f3-ed78-47c4-53e7-08d8e409d87d) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e064654464..e9c485de6a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer c65382f00216756e0fb192cc2fd6513448d2679d - + https://github.com/dotnet/aspnetcore - 198857c8a2211931739e6e08facfbab52f8dd023 + e4ba445101d9bdae24baa6b332d19a11ac8922be - + https://github.com/dotnet/aspnetcore - 198857c8a2211931739e6e08facfbab52f8dd023 + e4ba445101d9bdae24baa6b332d19a11ac8922be https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 04533743c6..987343bd6e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23518.26 8.0.0-rtm.23518.26 - 8.0.0-rtm.23518.6 - 8.0.0-rtm.23518.6 + 8.0.0-rtm.23519.14 + 8.0.0-rtm.23519.14 8.0.100-rtm.23513.4 From d3a55f24fcbfe6994e32a23ee041dca1606212ec Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 13:09:38 +0000 Subject: [PATCH 75/82] [main] Update dependencies from dotnet/runtime (#4350) [main] Update dependencies from dotnet/runtime --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e9c485de6a..53ed4ad916 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -39,13 +39,13 @@ https://github.com/dotnet/aspnetcore e4ba445101d9bdae24baa6b332d19a11ac8922be - + https://github.com/dotnet/runtime - c6e7ebdcb1b99c72990ae9f5ff95a75d5bb0f3ce + 11ad607efb2b31c5e1b906303fcd70341e9d5206 - + https://github.com/dotnet/runtime - c6e7ebdcb1b99c72990ae9f5ff95a75d5bb0f3ce + 11ad607efb2b31c5e1b906303fcd70341e9d5206 https://github.com/dotnet/source-build-reference-packages diff --git a/eng/Versions.props b/eng/Versions.props index 987343bd6e..f23cb55399 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,8 +18,8 @@ 1.0.451601 - 8.0.0-rtm.23518.26 - 8.0.0-rtm.23518.26 + 8.0.0-rtm.23520.16 + 8.0.0-rtm.23520.16 8.0.0-rtm.23519.14 8.0.0-rtm.23519.14 From 3f9c251b6b2671648916383077865b09bfe5a63c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 13:13:37 +0000 Subject: [PATCH 76/82] [main] Update dependencies from dotnet/aspnetcore (#4349) [main] Update dependencies from dotnet/aspnetcore --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 53ed4ad916..a778f62f33 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,13 +31,13 @@ https://github.com/dotnet/installer c65382f00216756e0fb192cc2fd6513448d2679d - + https://github.com/dotnet/aspnetcore - e4ba445101d9bdae24baa6b332d19a11ac8922be + c9fa5f3a34605c93bffd1459a5e39e6bc63f50cc - + https://github.com/dotnet/aspnetcore - e4ba445101d9bdae24baa6b332d19a11ac8922be + c9fa5f3a34605c93bffd1459a5e39e6bc63f50cc https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index f23cb55399..9d25d708a1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,8 +21,8 @@ 8.0.0-rtm.23520.16 8.0.0-rtm.23520.16 - 8.0.0-rtm.23519.14 - 8.0.0-rtm.23519.14 + 8.0.0-rtm.23520.10 + 8.0.0-rtm.23520.10 8.0.100-rtm.23513.4 From 6d3c1bbf0b940c47715cbfefb7fcc526cbf73599 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:24:08 +0000 Subject: [PATCH 77/82] [main] Update dependencies from dotnet/arcade (#4352) [main] Update dependencies from dotnet/arcade --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- eng/common/build.sh | 2 +- eng/common/cross/build-rootfs.sh | 2 +- eng/common/cross/riscv64/tizen/tizen.patch | 9 +++++++++ eng/common/cross/tizen-build-rootfs.sh | 21 +++++++++++++++++++++ eng/common/cross/tizen-fetch.sh | 17 ++++++++++++++--- eng/common/cross/toolchain.cmake | 19 +++++++++++++------ eng/common/sdk-task.ps1 | 2 +- eng/common/templates/job/job.yml | 2 +- eng/common/tools.ps1 | 4 ++-- global.json | 4 ++-- 12 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 eng/common/cross/riscv64/tizen/tizen.patch diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a778f62f33..b9ab5e0fb5 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,14 +14,14 @@ - + https://github.com/dotnet/arcade - 0e89c8d7ddbb54510d9d0a757fda5126021b79f0 + 6a5ca678aef84f34a1fccc46f76623f41da56553 - + https://github.com/dotnet/arcade - 0e89c8d7ddbb54510d9d0a757fda5126021b79f0 + 6a5ca678aef84f34a1fccc46f76623f41da56553 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index 9d25d708a1..96c7eeb4a8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -62,7 +62,7 @@ 6.0.0 6.0.8 2.0.3 - 9.0.0-beta.23510.4 + 9.0.0-beta.23518.2 1.2.0-beta.406 7.0.0-beta.22316.2 10.0.18362 diff --git a/eng/common/build.sh b/eng/common/build.sh index 50af40cdd2..2c17ba529b 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -105,7 +105,7 @@ while [[ $# > 0 ]]; do -binarylog|-bl) binary_log=true ;; - -excludeCIBinarylog|-nobl) + -excludecibinarylog|-nobl) exclude_ci_binary_log=true ;; -pipelineslog|-pl) diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 9caf9b021d..4228f202e5 100644 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -487,7 +487,7 @@ if [[ "$__CodeName" == "alpine" ]]; then -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" \ - search 'llvm*-libs' | sort | tail -1 | sed 's/-[^-]*//2g')" + search 'llvm*-libs' | grep -E '^llvm' | sort | tail -1 | sed 's/-[^-]*//2g')" fi # install all packages in one go diff --git a/eng/common/cross/riscv64/tizen/tizen.patch b/eng/common/cross/riscv64/tizen/tizen.patch new file mode 100644 index 0000000000..eb6d1c0747 --- /dev/null +++ b/eng/common/cross/riscv64/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib64/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib64/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf64-littleriscv) +-GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-riscv64-lp64d.so.1 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-riscv64-lp64d.so.1 ) ) diff --git a/eng/common/cross/tizen-build-rootfs.sh b/eng/common/cross/tizen-build-rootfs.sh index ac84173d44..ba31c93285 100644 --- a/eng/common/cross/tizen-build-rootfs.sh +++ b/eng/common/cross/tizen-build-rootfs.sh @@ -22,6 +22,10 @@ case "$ARCH" in TIZEN_ARCH="x86_64" LINK_ARCH="x86" ;; + riscv64) + TIZEN_ARCH="riscv64" + LINK_ARCH="riscv" + ;; *) echo "Unsupported architecture for tizen: $ARCH" exit 1 @@ -58,4 +62,21 @@ rm -rf $TIZEN_TMP_DIR echo ">>Start configuring Tizen rootfs" ln -sfn asm-${LINK_ARCH} ./usr/include/asm patch -p1 < $__TIZEN_CROSSDIR/tizen.patch +if [[ "$TIZEN_ARCH" == "riscv64" ]]; then + echo "Fixing broken symlinks in $PWD" + rm ./usr/lib64/libresolv.so + ln -s ../../lib64/libresolv.so.2 ./usr/lib64/libresolv.so + rm ./usr/lib64/libpthread.so + ln -s ../../lib64/libpthread.so.0 ./usr/lib64/libpthread.so + rm ./usr/lib64/libdl.so + ln -s ../../lib64/libdl.so.2 ./usr/lib64/libdl.so + rm ./usr/lib64/libutil.so + ln -s ../../lib64/libutil.so.1 ./usr/lib64/libutil.so + rm ./usr/lib64/libm.so + ln -s ../../lib64/libm.so.6 ./usr/lib64/libm.so + rm ./usr/lib64/librt.so + ln -s ../../lib64/librt.so.1 ./usr/lib64/librt.so + rm ./lib/ld-linux-riscv64-lp64d.so.1 + ln -s ../lib64/ld-linux-riscv64-lp64d.so.1 ./lib/ld-linux-riscv64-lp64d.so.1 +fi echo "< Date: Mon, 23 Oct 2023 10:31:13 -0700 Subject: [PATCH 78/82] Update Native AOT crash info with module base address (#4351) --- .../CrashInfoService.cs | 17 ++++++++++++----- .../ICrashInfoService.cs | 5 +++++ .../CrashInfoCommand.cs | 1 + 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs index a10c4001a3..2f7282e0ae 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/CrashInfoService.cs @@ -30,12 +30,16 @@ public sealed class CrashInfoJson [JsonPropertyName("reason")] public int Reason { get; set; } - [JsonPropertyName("runtime")] - public string Runtime { get; set; } - [JsonPropertyName("runtime_type")] public int RuntimeType { get; set; } + [JsonPropertyName("runtime_base")] + [JsonConverter(typeof(HexUInt64Converter))] + public ulong RuntimeBaseAddress { get; set; } + + [JsonPropertyName("runtime_version")] + public string RuntimeVersion { get; set; } + [JsonPropertyName("thread")] [JsonConverter(typeof(HexUInt32Converter))] public uint Thread { get; set; } @@ -165,7 +169,8 @@ private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo) ThreadId = threadId; HResult = hresult; CrashReason = (CrashReason)crashInfo.Reason; - RuntimeVersion = crashInfo.Runtime; + RuntimeBaseAddress = crashInfo.RuntimeBaseAddress; + RuntimeVersion = crashInfo.RuntimeVersion; RuntimeType = (RuntimeType)crashInfo.RuntimeType; Message = crashInfo.Message; Exception = crashInfo.Exception; @@ -179,10 +184,12 @@ private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo) public CrashReason CrashReason { get; } - public string RuntimeVersion { get; } + public ulong RuntimeBaseAddress { get; } public RuntimeType RuntimeType { get; } + public string RuntimeVersion { get; } + public string Message { get; } public IManagedException Exception { get; } diff --git a/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs b/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs index 797fb75fd1..1d04cab520 100644 --- a/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs +++ b/src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs @@ -41,6 +41,11 @@ public interface ICrashInfoService /// RuntimeType RuntimeType { get; } + /// + /// The module base address that contains the runtime + /// + ulong RuntimeBaseAddress { get; } + /// /// Runtime version and possible commit id /// diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs index 994522dd2a..67c69b3aed 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/CrashInfoCommand.cs @@ -30,6 +30,7 @@ public override void Invoke() WriteLine($"ThreadId: {CrashInfo.ThreadId:X4}"); WriteLine($"HResult: {CrashInfo.HResult:X4}"); WriteLine($"RuntimeType: {CrashInfo.RuntimeType}"); + WriteLine($"RuntimeBaseAddress: {CrashInfo.RuntimeBaseAddress:X16}"); WriteLine($"RuntimeVersion: {CrashInfo.RuntimeVersion}"); WriteLine($"Message: {CrashInfo.Message}"); From 609ce19b33abfccc71c92456ed3c0fe186c125d0 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 20:08:02 +0000 Subject: [PATCH 79/82] [main] Update dependencies from dotnet/installer (#4353) [main] Update dependencies from dotnet/installer - Update Versions.props --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b9ab5e0fb5..355fddd00f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,9 +27,9 @@ https://github.com/dotnet/arcade ccfe6da198c5f05534863bbb1bff66e830e0c6ab - + https://github.com/dotnet/installer - c65382f00216756e0fb192cc2fd6513448d2679d + 30d7d24a6592aa0c97f81ac36b563fab2b451b14 https://github.com/dotnet/aspnetcore diff --git a/eng/Versions.props b/eng/Versions.props index 96c7eeb4a8..43aaa74c1d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -24,7 +24,7 @@ 8.0.0-rtm.23520.10 8.0.0-rtm.23520.10 - 8.0.100-rtm.23513.4 + 8.0.100-rtm.23522.1 @@ -35,7 +35,7 @@ $(MicrosoftNETCoreApp60Version) $(MicrosoftNETCoreApp70Version) - 8.0.0-rtm.23511.16 + 8.0.0-rtm.23519.13 From 093a0ea50d7c04c520d18ced9fab4849d2c7ff9e Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 24 Oct 2023 01:46:55 +0200 Subject: [PATCH 80/82] Fix ModuleLoadUnloadTraceData.ModuleID cast to be unchecked since its really a ulong member. (#4354) The ModuleLoadUnload events ModuleID is typed as a uint64 in the EventPipe manifest and emitted as a uint64 in the event payload. However, the parsing logic in ModuleLoadUnloadTraceDataevent in trace event: https://github.com/microsoft/perfview/blob/a6c87911fe1aef8f59c9ce54aa4e16a1be6db91e/src/TraceEvent/Parsers/ClrEtwAll.cs.base#L4963 handles it as a long. Android devices running arm64 can use pointer tagging meaning that high bits can be set in 64-bit addresses and since module id is a memory address, it will be returned as a negative long from ModuleLoadUnload event and since all diagnostic tooling is build with overflow checking enabled by default, casting it to a ulong will trigger and overflow exception when high bit is set. Fix is to do an unchecked cast in this case since the value should be threated as a ulong in first place. Fixes https://github.com/dotnet/diagnostics/issues/4348. --- .../DotNetHeapDump/DotNetHeapDumpGraphReader.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Tools/dotnet-gcdump/DotNetHeapDump/DotNetHeapDumpGraphReader.cs b/src/Tools/dotnet-gcdump/DotNetHeapDump/DotNetHeapDumpGraphReader.cs index c26488fcef..328febb54d 100644 --- a/src/Tools/dotnet-gcdump/DotNetHeapDump/DotNetHeapDumpGraphReader.cs +++ b/src/Tools/dotnet-gcdump/DotNetHeapDump/DotNetHeapDumpGraphReader.cs @@ -117,12 +117,13 @@ internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher sourc return; } - if (!m_moduleID2Name.ContainsKey((ulong)data.ModuleID)) + ulong moduleID = unchecked((ulong)data.ModuleID); + if (!m_moduleID2Name.ContainsKey(moduleID)) { - m_moduleID2Name[(ulong)data.ModuleID] = data.ModuleILPath; + m_moduleID2Name[moduleID] = data.ModuleILPath; } - m_log.WriteLine("Found Module {0} ID 0x{1:x}", data.ModuleILFileName, (ulong)data.ModuleID); + m_log.WriteLine("Found Module {0} ID 0x{1:x}", data.ModuleILFileName, moduleID); }; source.Clr.AddCallbackForEvents(moduleCallback); // Get module events for clr provider // TODO should not be needed if we use CAPTURE_STATE when collecting. From 6f9f639970a8cb373c8b8b1974d29005d3d33da1 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 24 Oct 2023 01:48:53 +0200 Subject: [PATCH 81/82] Handle sessionId as ulong instead of long in EventPipeSession.cs. (#4345) Session id is a uint64 in runtime as well as specified as a uint64 in IPC protocol, https://github.com/dotnet/diagnostics/blob/main/documentation/design-docs/ipc-protocol.md#returns-as-an-ipc-message-payload. EventPipeSession.cs however handled it as a long instead of an ulong, currently that doesn't affect release builds since it doesn't do much with the id except passing it back to stop the session, but in debug builds there is an assert that validates that session id > 0. On Android physical devices it is not uncommon to get code and memory allocated at high addresses, including having the high order bit set and when that happens, EventPipeSession.cs will see a negative session id and assert on debug builds. Fix adjust session id as ulong inline with IPC protocol, it also makes sure keyword serialized when starting a session is handled according to IPC specification, but only when serialized into the payload, it will still be typed as long inside EventPipeProvider since it is a public property. --- .../DiagnosticsClient/EventPipeSession.cs | 6 +++--- .../DiagnosticsClient/EventPipeSessionConfiguration.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs index 041502f6f7..d61aace345 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs @@ -13,13 +13,13 @@ namespace Microsoft.Diagnostics.NETCore.Client { public class EventPipeSession : IDisposable { - private long _sessionId; + private ulong _sessionId; private IpcEndpoint _endpoint; private bool _disposedValue; // To detect redundant calls private bool _stopped; // To detect redundant calls private readonly IpcResponse _response; - private EventPipeSession(IpcEndpoint endpoint, IpcResponse response, long sessionId) + private EventPipeSession(IpcEndpoint endpoint, IpcResponse response, ulong sessionId) { _endpoint = endpoint; _response = response; @@ -93,7 +93,7 @@ private static EventPipeSession CreateSessionFromResponse(IpcEndpoint endpoint, { DiagnosticsClient.ValidateResponseMessage(response.Value.Message, operationName); - long sessionId = BinaryPrimitives.ReadInt64LittleEndian(new ReadOnlySpan(response.Value.Message.Payload, 0, 8)); + ulong sessionId = BinaryPrimitives.ReadUInt64LittleEndian(new ReadOnlySpan(response.Value.Message.Payload, 0, 8)); EventPipeSession session = new(endpoint, response.Value, sessionId); response = null; diff --git a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs index 25f813d8ff..0e837d908c 100644 --- a/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs +++ b/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSessionConfiguration.cs @@ -59,7 +59,7 @@ public byte[] SerializeV2() writer.Write(Providers.Count); foreach (EventPipeProvider provider in Providers) { - writer.Write(provider.Keywords); + writer.Write(unchecked((ulong)provider.Keywords)); writer.Write((uint)provider.EventLevel); writer.WriteString(provider.Name); From 8e31e1b7d19e3c058514111347ede52898f41f67 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 23 Oct 2023 19:05:57 -0700 Subject: [PATCH 82/82] Update tools major version to 8.0 --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 8fdae5dba1..6a38791823 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -2,7 +2,7 @@ https://github.com/dotnet/diagnostics - 7.0.0 + 8.0.0 true true