From 4915c15310e5eefb41b53d49295a48a192084e9d Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Fri, 7 Feb 2025 15:12:27 +0800 Subject: [PATCH 01/10] make TraceCollector work on Linux --- .../GC.Infrastructure.Core.csproj | 6 +- .../TraceCollection/TraceCollector.cs | 156 +++++++++++++----- .../Commands/GCPerfSim/GCPerfSimCommand.cs | 28 +++- .../Microbenchmark/MicrobenchmarkCommand.cs | 21 ++- .../GC.Infrastructure/Program.cs | 5 +- 5 files changed, 160 insertions(+), 56 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj index 3c3ad5c0fba..3cb61ccfac9 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj @@ -5,7 +5,11 @@ enable enable false - false + false + true + + + Windows diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index 752657610b5..e4872158f87 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using Microsoft.Diagnostics.Utilities; +using System.Diagnostics; using System.Net; namespace GC.Infrastructure.Core.TraceCollection @@ -19,12 +20,10 @@ public sealed class TraceCollector : IDisposable { private bool disposedValue; - private static readonly Lazy _client = new(); - - // TODO: Make this URL configurable. - private const string PERFVIEW_URL = "https://github.com/microsoft/perfview/releases/download/v3.0.0/PerfView.exe"; + private static readonly string DependenciesFolder = "./dependencies"; + + public string Name { get; init; } - private readonly string ALWAYS_ARGS = @$" /AcceptEULA /NoGUI /Merge:true"; internal static readonly Dictionary WindowsCollectTypeMap = new() { { CollectType.gc, "/GCCollectOnly" }, @@ -38,9 +37,9 @@ public sealed class TraceCollector : IDisposable internal static readonly Dictionary LinuxCollectTypeMap = new() { - { CollectType.gc, "gcCollectOnly" }, - { CollectType.cpu, "collect_cpu" }, - { CollectType.threadtime, "collect_threadTime" }, + { CollectType.gc, "--clrevents gc" }, + { CollectType.cpu, "--clrevents gc+stack --clreventlevel informational" }, + { CollectType.threadtime, "--clrevents gc --clreventlevel Verbose" }, }; internal static readonly Dictionary StringToCollectTypeMap = new(StringComparer.OrdinalIgnoreCase) @@ -55,54 +54,122 @@ public sealed class TraceCollector : IDisposable { "none", CollectType.none } }; + private readonly CollectType _collectType; + private readonly Process _traceProcess; private readonly string arguments; + private readonly string _collectorPath; +#if Windows private readonly Guid _sessionName; - private readonly Process _traceProcess; - private readonly CollectType _collectType; +#endif - public TraceCollector(string name, string collectType, string outputPath) + private static void InstallTraceCollector(string dependenciesFolder) { - // Get Perfview if it doesn't exist. - if (!Directory.Exists("./dependencies")) +#if Windows + string perfviewPath = Path.Combine(DependenciesFolder, "Perfview.exe"); + if (File.Exists(perfviewPath)) { - Directory.CreateDirectory("./dependencies"); + return; } + + // TODO: Make this URL configurable. + const string perfviewUrl = "https://github.com/microsoft/perfview/releases/download/v3.0.0/PerfView.exe"; + using (HttpClient client = new()) + { + HttpResponseMessage response = client.GetAsync(perfviewUrl).Result; + response.EnsureSuccessStatusCode(); - if (!File.Exists(Path.Combine("./dependencies", "PerfView.exe"))) + using (FileStream writer = File.OpenWrite(perfviewPath)) + { + response.Content.ReadAsStream().CopyTo(writer); + } + } +#else + string dotNetTracePath = Path.Combine(DependenciesFolder, "dotnet-trace"); + if (File.Exists(dotNetTracePath)) { - _client.Value.DownloadFile(PERFVIEW_URL, Path.Combine("./dependencies", "PerfView.exe")); + return; } - _collectType = StringToCollectTypeMap[collectType]; + using (Process dotNetTraceInstaller = new()) + { + dotNetTraceInstaller.StartInfo.FileName = "dotnet"; + dotNetTraceInstaller.StartInfo.Arguments = $"tool install dotnet-trace --tool-path {dependenciesFolder}"; + dotNetTraceInstaller.StartInfo.UseShellExecute = false; + dotNetTraceInstaller.StartInfo.CreateNoWindow = true; + dotNetTraceInstaller.StartInfo.RedirectStandardError = true; + dotNetTraceInstaller.StartInfo.RedirectStandardOutput = true; + dotNetTraceInstaller.Start(); + dotNetTraceInstaller.WaitForExit(); + } +#endif + } - if (_collectType != CollectType.none) + public TraceCollector(string name, string collectType, string outputPath, int? pid = null) + { + if (!Directory.Exists(DependenciesFolder)) { - _sessionName = Guid.NewGuid(); - foreach (var invalid in Path.GetInvalidPathChars()) - { - name = name.Replace(invalid.ToString(), string.Empty); - } + Directory.CreateDirectory(DependenciesFolder); + } - name = name.Replace("<", ""); - name = name.Replace(">", ""); + InstallTraceCollector(DependenciesFolder); - Name = Path.Combine(outputPath, $"{name}.etl"); + _collectType = StringToCollectTypeMap[collectType]; - arguments = $"{ALWAYS_ARGS} /sessionName:{_sessionName} {WindowsCollectTypeMap[_collectType]} /LogFile:{Path.Combine(outputPath, name)}.txt /DataFile:{Path.Combine(outputPath, $"{name}.etl")}"; - string command = $"start {arguments}"; + if (_collectType == CollectType.none) + { + return; + } - _traceProcess = new(); - _traceProcess.StartInfo.FileName = "./dependencies/PerfView.exe"; - _traceProcess.StartInfo.Arguments = command; - _traceProcess.StartInfo.UseShellExecute = false; - _traceProcess.StartInfo.CreateNoWindow = true; - _traceProcess.StartInfo.RedirectStandardError = true; - _traceProcess.StartInfo.RedirectStandardOutput = true; - _traceProcess.Start(); + foreach (var invalid in Path.GetInvalidPathChars()) + { + name = name.Replace(invalid.ToString(), string.Empty); + } - // Give PerfView about a second to get started. - Thread.Sleep(1000); + name = name.Replace("<", ""); + name = name.Replace(">", ""); + +#if Windows + _collectorPath = Path.Combine(DependenciesFolder, "Perfview.exe"); + _sessionName = Guid.NewGuid(); + + Name = Path.Combine(outputPath, $"{name}.etl"); + string ALWAYS_ARGS = @$" /AcceptEULA /NoGUI /Merge:true"; + arguments = $"{ALWAYS_ARGS} /sessionName:{_sessionName} {WindowsCollectTypeMap[_collectType]} /LogFile:{Path.Combine(outputPath, name)}.txt /DataFile:{Name}"; + string command = $"start {arguments}"; + + _traceProcess = new(); + _traceProcess.StartInfo.FileName = _collectorPath; + _traceProcess.StartInfo.Arguments = command; + _traceProcess.StartInfo.UseShellExecute = false; + _traceProcess.StartInfo.CreateNoWindow = true; + _traceProcess.StartInfo.RedirectStandardError = true; + _traceProcess.StartInfo.RedirectStandardOutput = true; + _traceProcess.Start(); + + // Give PerfView about a second to get started. + Thread.Sleep(1000); + +#else + if (pid == null) + { + throw new Exception($"{nameof(TraceCollector)}: Must provide prcoess id in Linux case"); } + + _collectorPath = Path.Combine(DependenciesFolder, "dotnet-trace"); + + Name = Path.Combine(outputPath, $"{name}.nettrace"); + arguments = $"-p {pid} -o {Name} {LinuxCollectTypeMap[_collectType]}"; + string command = $"collect {arguments}"; + + _traceProcess = new(); + _traceProcess.StartInfo.FileName = _collectorPath; + _traceProcess.StartInfo.Arguments = command; + _traceProcess.StartInfo.UseShellExecute = false; + _traceProcess.StartInfo.CreateNoWindow = true; + _traceProcess.StartInfo.RedirectStandardError = true; + _traceProcess.StartInfo.RedirectStandardOutput = true; + _traceProcess.Start(); +#endif } private void Dispose(bool disposing) @@ -115,11 +182,12 @@ private void Dispose(bool disposing) // TODO: Parameterize the wait for exit time. +#if Windows if (!disposedValue) { using (Process stopProcess = new()) { - stopProcess.StartInfo.FileName = Path.Combine("./dependencies", "PerfView.exe"); + stopProcess.StartInfo.FileName = _collectorPath; string command = $"stop {arguments}"; stopProcess.StartInfo.Arguments = command; stopProcess.StartInfo.UseShellExecute = false; @@ -160,9 +228,15 @@ private void Dispose(bool disposing) disposedValue = true; } +#else + if (!disposedValue) + { + _traceProcess.WaitForExit(); + _traceProcess.Dispose(); + } +#endif } - public string Name { get; init; } ~TraceCollector() { diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs index 65f3ca62a8d..21c0d4a3de2 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs @@ -176,15 +176,31 @@ internal static Dictionary ExecuteLocally(GCPer string key = $"{runInfo.RunDetails.Key}.{runInfo.CorerunDetails.Key}.{iterationIdx}"; string traceName = $"{runInfo.RunDetails.Key}.{runInfo.CorerunDetails.Key}.{iterationIdx}"; - using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, outputPath)) + + if (OperatingSystem.IsWindows()) + { + using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, outputPath)) + { + gcperfsimProcess.Start(); + output = gcperfsimProcess.StandardOutput.ReadToEnd(); + error = gcperfsimProcess.StandardError.ReadToEnd(); + gcperfsimProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); + File.WriteAllText(Path.Combine(outputPath, key + ".txt"), "Standard Out: \n" + output + "\n Standard Error: \n" + error); + } + } + else { gcperfsimProcess.Start(); - output = gcperfsimProcess.StandardOutput.ReadToEnd(); - error = gcperfsimProcess.StandardError.ReadToEnd(); - gcperfsimProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); - File.WriteAllText(Path.Combine(outputPath, key + ".txt"), "Standard Out: \n" + output + "\n Standard Error: \n" + error); + using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, outputPath, gcperfsimProcess.Id)) + { + output = gcperfsimProcess.StandardOutput.ReadToEnd(); + error = gcperfsimProcess.StandardError.ReadToEnd(); + gcperfsimProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); + File.WriteAllText(Path.Combine(outputPath, key + ".txt"), "Standard Out: \n" + output + "\n Standard Error: \n" + error); + } } - + + // If a trace is requested, ensure the file exists. If not, there is was an error and alert the user. if (configuration.TraceConfigurations?.Type != "none") { diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs index 536933590fe..c8c5385aeef 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs @@ -177,12 +177,25 @@ public static MicrobenchmarkOutputResults RunMicrobenchmarks(MicrobenchmarkConfi }; string traceName = $"{benchmarkCleanedName}_{index}"; - using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath)) + if (OperatingSystem.IsWindows()) + { + using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath)) + { + bdnProcess.Start(); + bdnProcess.BeginOutputReadLine(); + bdnProcess.BeginErrorReadLine(); + bdnProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); + } + } + else { bdnProcess.Start(); - bdnProcess.BeginOutputReadLine(); - bdnProcess.BeginErrorReadLine(); - bdnProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); + using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath, bdnProcess.Id)) + { + bdnProcess.BeginOutputReadLine(); + bdnProcess.BeginErrorReadLine(); + bdnProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); + } } string processDetailsKey = $"{run.Key}_{benchmark}_{index}"; diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Program.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Program.cs index 0a505869860..29ba80d0a45 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Program.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Program.cs @@ -20,6 +20,7 @@ internal static void Main(string[] args) if (OperatingSystem.IsWindows()) { + bool IsAdministrator = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); if (!IsAdministrator) { AnsiConsole.WriteLine("Not running in admin mode - please elevate privileges to run this process."); @@ -79,9 +80,5 @@ internal static void Main(string[] args) } } } - - [SupportedOSPlatform("windows")] - internal static bool IsAdministrator => - new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); } } \ No newline at end of file From 7456e69276d536445594336fa45dd93622d428c7 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Sat, 8 Feb 2025 15:29:43 +0800 Subject: [PATCH 02/10] make sure nettrace exists if required on Linux --- .../GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs index 21c0d4a3de2..0803f4c6468 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs @@ -205,7 +205,7 @@ internal static Dictionary ExecuteLocally(GCPer if (configuration.TraceConfigurations?.Type != "none") { // Not checking Linux here since the local run only allows for Windows. - if (!File.Exists(Path.Combine(outputPath, traceName + ".etl.zip"))) + if (!File.Exists(Path.Combine(outputPath, traceName + ".etl.zip")) && !File.Exists(Path.Combine(outputPath, traceName + ".nettrace"))) { AnsiConsole.MarkupLine($"[yellow bold] ({DateTime.Now}) The trace for the run wasn't successfully captured. Please check the log file for more details: {Markup.Escape(output)} Full run details: {Path.GetFileNameWithoutExtension(configuration.Name)}: {runInfo.CorerunDetails.Key} for {runInfo.RunDetails.Key} [/]"); } From 97687bf7f8581ec7efb0d3bd29df0ab81fee0788 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Wed, 12 Feb 2025 12:18:16 +0800 Subject: [PATCH 03/10] enable trace collection for microbenchmarks on Linux --- .../MicrobenchmarkResultsAnalyzer.cs | 17 ++--------------- .../Microbenchmark.CommandBuilder.cs | 2 +- .../Microbenchmark/MicrobenchmarkCommand.cs | 13 ++++++++++++- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs index 4ad9158e74c..e26e433364c 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs @@ -48,23 +48,10 @@ public static IReadOnlyDictionary allPertinentProcesses = analyzer.Value.GetProcessGCData("dotnet"); - List corerunProcesses = analyzer.Value.GetProcessGCData("corerun"); - allPertinentProcesses.AddRange(corerunProcesses); foreach (var benchmark in runsToResults[run.Value]) { - GCProcessData? benchmarkGCData = null; - foreach (var process in allPertinentProcesses) - { - string commandLine = process.CommandLine.Replace("\"", "").Replace("\\", ""); - string runCleaned = benchmark.Key.Replace("\"", "").Replace("\\", ""); - if (commandLine.Contains(runCleaned) && commandLine.Contains("--benchmarkName")) - { - benchmarkGCData = process; - break; - } - } - + GCProcessData? benchmarkGCData = analyzer.Value.GetProcessGCData("MicroBenchmarks").FirstOrDefault(); + if (benchmarkGCData != null) { int processID = benchmarkGCData.ProcessID; diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs index 90b90d04691..e959a015685 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs @@ -11,7 +11,7 @@ public static (string, string) Build(MicrobenchmarkConfiguration configuration, string filter = benchmark ?? configuration.MicrobenchmarkConfigurations.Filter; // Base command: Add mandatory commands. - string command = $"run -f {frameworkVersion} --filter \"{filter}\" -c Release --noOverwrite --no-build"; + string command = $"run -f {frameworkVersion} --filter \"{filter}\" -c Release --inProcess --noOverwrite --no-build"; // [Optional] Add corerun. if (!string.IsNullOrEmpty(run.Value.corerun)) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs index c8c5385aeef..ef13027bd77 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs @@ -189,8 +189,19 @@ public static MicrobenchmarkOutputResults RunMicrobenchmarks(MicrobenchmarkConfi } else { + Process microBenchmarksProcess = new(); bdnProcess.Start(); - using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath, bdnProcess.Id)) + // wait for Microbenchmark to start + while (true) + { + Process[] microBenchmarksProcessList = Process.GetProcessesByName("MicroBenchmarks"); + if (microBenchmarksProcessList.Count() != 0) + { + microBenchmarksProcess = microBenchmarksProcessList.First(); + break; + } + } + using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath, microBenchmarksProcess.Id)) { bdnProcess.BeginOutputReadLine(); bdnProcess.BeginErrorReadLine(); From ab9190fdf173c721c16ef0e56e05dbdd43f71a94 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Wed, 19 Feb 2025 11:02:36 +0800 Subject: [PATCH 04/10] Create new CollectTypeMap to separate between the crank and local runs --- .../TraceCollection/TraceCollector.cs | 9 ++++++++- .../Commands/GCPerfSim/GCPerfSimCommand.cs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index e4872158f87..860154dce93 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -36,6 +36,13 @@ public sealed class TraceCollector : IDisposable }; internal static readonly Dictionary LinuxCollectTypeMap = new() + { + { CollectType.gc, "gcCollectOnly" }, + { CollectType.cpu, "collect_cpu" }, + { CollectType.threadtime, "collect_threadTime" } + }; + + internal static readonly Dictionary LinuxLocalRunCollectTypeMap = new() { { CollectType.gc, "--clrevents gc" }, { CollectType.cpu, "--clrevents gc+stack --clreventlevel informational" }, @@ -158,7 +165,7 @@ public TraceCollector(string name, string collectType, string outputPath, int? p _collectorPath = Path.Combine(DependenciesFolder, "dotnet-trace"); Name = Path.Combine(outputPath, $"{name}.nettrace"); - arguments = $"-p {pid} -o {Name} {LinuxCollectTypeMap[_collectType]}"; + arguments = $"-p {pid} -o {Name} {LinuxLocalRunCollectTypeMap[_collectType]}"; string command = $"collect {arguments}"; _traceProcess = new(); diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs index 0803f4c6468..be77f167f8c 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimCommand.cs @@ -204,7 +204,7 @@ internal static Dictionary ExecuteLocally(GCPer // If a trace is requested, ensure the file exists. If not, there is was an error and alert the user. if (configuration.TraceConfigurations?.Type != "none") { - // Not checking Linux here since the local run only allows for Windows. + // On Windows, the trace file path ends with ".etl.zip"; On Linux, it ends with ".nettrace". if (!File.Exists(Path.Combine(outputPath, traceName + ".etl.zip")) && !File.Exists(Path.Combine(outputPath, traceName + ".nettrace"))) { AnsiConsole.MarkupLine($"[yellow bold] ({DateTime.Now}) The trace for the run wasn't successfully captured. Please check the log file for more details: {Markup.Escape(output)} Full run details: {Path.GetFileNameWithoutExtension(configuration.Name)}: {runInfo.CorerunDetails.Key} for {runInfo.RunDetails.Key} [/]"); From 648256f59471f9340102e6049a81c1f673b890bb Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Tue, 4 Mar 2025 09:48:42 +0800 Subject: [PATCH 05/10] update CollectTypeMap for Linux --- .../CommandBuilders/GCPerfSim.CommandBuilder.cs | 2 +- .../TraceCollection/TraceCollector.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs index 7643f48711b..af4af5ec718 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs @@ -104,7 +104,7 @@ public static (string, string) BuildForServer(GCPerfSimConfiguration configurati if (configuration.TraceConfigurations != null && !string.Equals(configuration.TraceConfigurations.Type, "none", StringComparison.OrdinalIgnoreCase)) { CollectType collectType = TraceCollector.StringToCollectTypeMap[configuration.TraceConfigurations.Type]; - string collectionCommand = os == OS.Windows ? TraceCollector.WindowsCollectTypeMap[collectType] : TraceCollector.LinuxCollectTypeMap[collectType]; + string collectionCommand = os == OS.Windows ? TraceCollector.WindowsCollectTypeMap[collectType] : TraceCollector.LinuxServerRunCollectTypeMap[collectType]; collectionCommand = collectionCommand.Replace(" ", ";").Replace("/", ""); diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index 860154dce93..013f2ac7f8b 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -35,7 +35,7 @@ public sealed class TraceCollector : IDisposable { CollectType.join, " /BufferSizeMB:4096 /CircularMB:4096 /KernelEvents:Process+Thread+ImageLoad /ClrEvents:GC+Threading /ClrEventLevel=Verbose " }, }; - internal static readonly Dictionary LinuxCollectTypeMap = new() + internal static readonly Dictionary LinuxServerRunCollectTypeMap = new() { { CollectType.gc, "gcCollectOnly" }, { CollectType.cpu, "collect_cpu" }, @@ -44,9 +44,8 @@ public sealed class TraceCollector : IDisposable internal static readonly Dictionary LinuxLocalRunCollectTypeMap = new() { - { CollectType.gc, "--clrevents gc" }, - { CollectType.cpu, "--clrevents gc+stack --clreventlevel informational" }, - { CollectType.threadtime, "--clrevents gc --clreventlevel Verbose" }, + { CollectType.gc, "--profile gc-collect" }, + { CollectType.cpu, "--clrevents gc+stack --clreventlevel informational" } }; internal static readonly Dictionary StringToCollectTypeMap = new(StringComparer.OrdinalIgnoreCase) From 7cd598b9eff9d0e7a343bcf9347248bb30073e39 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Tue, 4 Mar 2025 11:00:34 +0800 Subject: [PATCH 06/10] disable linux trace for microbenchmark --- .../MicrobenchmarkResultsAnalyzer.cs | 17 ++++++++-- .../Microbenchmark.CommandBuilder.cs | 2 +- .../Microbenchmark/MicrobenchmarkCommand.cs | 32 +++---------------- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs index e26e433364c..4ad9158e74c 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/Microbenchmarks/MicrobenchmarkResultsAnalyzer.cs @@ -48,10 +48,23 @@ public static IReadOnlyDictionary allPertinentProcesses = analyzer.Value.GetProcessGCData("dotnet"); + List corerunProcesses = analyzer.Value.GetProcessGCData("corerun"); + allPertinentProcesses.AddRange(corerunProcesses); foreach (var benchmark in runsToResults[run.Value]) { - GCProcessData? benchmarkGCData = analyzer.Value.GetProcessGCData("MicroBenchmarks").FirstOrDefault(); - + GCProcessData? benchmarkGCData = null; + foreach (var process in allPertinentProcesses) + { + string commandLine = process.CommandLine.Replace("\"", "").Replace("\\", ""); + string runCleaned = benchmark.Key.Replace("\"", "").Replace("\\", ""); + if (commandLine.Contains(runCleaned) && commandLine.Contains("--benchmarkName")) + { + benchmarkGCData = process; + break; + } + } + if (benchmarkGCData != null) { int processID = benchmarkGCData.ProcessID; diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs index e959a015685..90b90d04691 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/Microbenchmark.CommandBuilder.cs @@ -11,7 +11,7 @@ public static (string, string) Build(MicrobenchmarkConfiguration configuration, string filter = benchmark ?? configuration.MicrobenchmarkConfigurations.Filter; // Base command: Add mandatory commands. - string command = $"run -f {frameworkVersion} --filter \"{filter}\" -c Release --inProcess --noOverwrite --no-build"; + string command = $"run -f {frameworkVersion} --filter \"{filter}\" -c Release --noOverwrite --no-build"; // [Optional] Add corerun. if (!string.IsNullOrEmpty(run.Value.corerun)) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs index ef13027bd77..536933590fe 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/Microbenchmark/MicrobenchmarkCommand.cs @@ -177,36 +177,12 @@ public static MicrobenchmarkOutputResults RunMicrobenchmarks(MicrobenchmarkConfi }; string traceName = $"{benchmarkCleanedName}_{index}"; - if (OperatingSystem.IsWindows()) + using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath)) { - using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath)) - { - bdnProcess.Start(); - bdnProcess.BeginOutputReadLine(); - bdnProcess.BeginErrorReadLine(); - bdnProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); - } - } - else - { - Process microBenchmarksProcess = new(); bdnProcess.Start(); - // wait for Microbenchmark to start - while (true) - { - Process[] microBenchmarksProcessList = Process.GetProcessesByName("MicroBenchmarks"); - if (microBenchmarksProcessList.Count() != 0) - { - microBenchmarksProcess = microBenchmarksProcessList.First(); - break; - } - } - using (TraceCollector traceCollector = new TraceCollector(traceName, collectType, runPath, microBenchmarksProcess.Id)) - { - bdnProcess.BeginOutputReadLine(); - bdnProcess.BeginErrorReadLine(); - bdnProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); - } + bdnProcess.BeginOutputReadLine(); + bdnProcess.BeginErrorReadLine(); + bdnProcess.WaitForExit((int)configuration.Environment.default_max_seconds * 1000); } string processDetailsKey = $"{run.Key}_{benchmark}_{index}"; From 5f396c06a2a51b1ecc7cb3cdfdcba0f16c24a503 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Tue, 4 Mar 2025 16:41:27 +0800 Subject: [PATCH 07/10] Traces for each config type --- .../CommandBuilders/GCPerfSim.CommandBuilder.cs | 4 ++++ .../TraceCollection/TraceCollector.cs | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs index af4af5ec718..2195f099b2d 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/CommandBuilders/GCPerfSim.CommandBuilder.cs @@ -104,6 +104,10 @@ public static (string, string) BuildForServer(GCPerfSimConfiguration configurati if (configuration.TraceConfigurations != null && !string.Equals(configuration.TraceConfigurations.Type, "none", StringComparison.OrdinalIgnoreCase)) { CollectType collectType = TraceCollector.StringToCollectTypeMap[configuration.TraceConfigurations.Type]; + if (os == OS.Linux && !TraceCollector.LinuxServerRunCollectTypeMap.Keys.Contains(collectType)) + { + throw new Exception($"{nameof(GCPerfSimCommandBuilder)}: Trace collect type {configuration.TraceConfigurations.Type} is not supported for GCPerfsim Linux server run."); + } string collectionCommand = os == OS.Windows ? TraceCollector.WindowsCollectTypeMap[collectType] : TraceCollector.LinuxServerRunCollectTypeMap[collectType]; collectionCommand = collectionCommand.Replace(" ", ";").Replace("/", ""); diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index 013f2ac7f8b..6987cfb43ce 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -44,8 +44,7 @@ public sealed class TraceCollector : IDisposable internal static readonly Dictionary LinuxLocalRunCollectTypeMap = new() { - { CollectType.gc, "--profile gc-collect" }, - { CollectType.cpu, "--clrevents gc+stack --clreventlevel informational" } + { CollectType.gc, "--profile gc-collect" } }; internal static readonly Dictionary StringToCollectTypeMap = new(StringComparer.OrdinalIgnoreCase) @@ -154,13 +153,18 @@ public TraceCollector(string name, string collectType, string outputPath, int? p // Give PerfView about a second to get started. Thread.Sleep(1000); - + #else if (pid == null) { throw new Exception($"{nameof(TraceCollector)}: Must provide prcoess id in Linux case"); } + if (_collectType != CollectType.none && !LinuxLocalRunCollectTypeMap.Keys.Contains(_collectType)) + { + throw new Exception($"{nameof(TraceCollector)}: Trace collect type {collectType} is not supported for Linux local run."); + } + _collectorPath = Path.Combine(DependenciesFolder, "dotnet-trace"); Name = Path.Combine(outputPath, $"{name}.nettrace"); From 8177216a8d8d190b5972a700d372725371e08925 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Thu, 20 Mar 2025 14:22:34 +0800 Subject: [PATCH 08/10] add cpu & verbose config type for linux tracing --- .../GC.Infrastructure.Core/TraceCollection/TraceCollector.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index 6987cfb43ce..b2cb7ee6a34 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -44,7 +44,9 @@ public sealed class TraceCollector : IDisposable internal static readonly Dictionary LinuxLocalRunCollectTypeMap = new() { - { CollectType.gc, "--profile gc-collect" } + { CollectType.gc, "--profile gc-collect" }, + { CollectType.cpu, "" }, + { CollectType.verbose, "--clrevents gc+stack --clreventlevel verbose" } }; internal static readonly Dictionary StringToCollectTypeMap = new(StringComparer.OrdinalIgnoreCase) From 7136b1aa4e315e46575af307a06d3b7ba2e1ec0e Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Mon, 24 Mar 2025 09:55:37 +0800 Subject: [PATCH 09/10] remove conditional compilation --- .../GC.Infrastructure.Core.csproj | 6 +- .../TraceCollection/TraceCollector.cs | 254 +++++++++--------- 2 files changed, 132 insertions(+), 128 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj index 3cb61ccfac9..cda42b8cc22 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/GC.Infrastructure.Core.csproj @@ -6,11 +6,7 @@ enable false false - true - - - Windows - + diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index b2cb7ee6a34..226951dc045 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -45,7 +45,7 @@ public sealed class TraceCollector : IDisposable internal static readonly Dictionary LinuxLocalRunCollectTypeMap = new() { { CollectType.gc, "--profile gc-collect" }, - { CollectType.cpu, "" }, + { CollectType.cpu, "--profile cpu-sampling" }, { CollectType.verbose, "--clrevents gc+stack --clreventlevel verbose" } }; @@ -65,50 +65,52 @@ public sealed class TraceCollector : IDisposable private readonly Process _traceProcess; private readonly string arguments; private readonly string _collectorPath; -#if Windows private readonly Guid _sessionName; -#endif private static void InstallTraceCollector(string dependenciesFolder) { -#if Windows - string perfviewPath = Path.Combine(DependenciesFolder, "Perfview.exe"); - if (File.Exists(perfviewPath)) + if (OperatingSystem.IsWindows()) { - return; - } - - // TODO: Make this URL configurable. - const string perfviewUrl = "https://github.com/microsoft/perfview/releases/download/v3.0.0/PerfView.exe"; - using (HttpClient client = new()) - { - HttpResponseMessage response = client.GetAsync(perfviewUrl).Result; - response.EnsureSuccessStatusCode(); + string perfviewPath = Path.Combine(DependenciesFolder, "Perfview.exe"); + if (File.Exists(perfviewPath)) + { + return; + } - using (FileStream writer = File.OpenWrite(perfviewPath)) + // TODO: Make this URL configurable. + const string perfviewUrl = "https://github.com/microsoft/perfview/releases/download/v3.0.0/PerfView.exe"; + using (HttpClient client = new()) { - response.Content.ReadAsStream().CopyTo(writer); + HttpResponseMessage response = client.GetAsync(perfviewUrl).Result; + response.EnsureSuccessStatusCode(); + + using (FileStream writer = File.OpenWrite(perfviewPath)) + { + response.Content.ReadAsStream().CopyTo(writer); + } } } -#else - string dotNetTracePath = Path.Combine(DependenciesFolder, "dotnet-trace"); - if (File.Exists(dotNetTracePath)) + if (OperatingSystem.IsLinux()) { - return; - } + string dotNetTracePath = Path.Combine(DependenciesFolder, "dotnet-trace"); + if (File.Exists(dotNetTracePath)) + { + return; + } - using (Process dotNetTraceInstaller = new()) - { - dotNetTraceInstaller.StartInfo.FileName = "dotnet"; - dotNetTraceInstaller.StartInfo.Arguments = $"tool install dotnet-trace --tool-path {dependenciesFolder}"; - dotNetTraceInstaller.StartInfo.UseShellExecute = false; - dotNetTraceInstaller.StartInfo.CreateNoWindow = true; - dotNetTraceInstaller.StartInfo.RedirectStandardError = true; - dotNetTraceInstaller.StartInfo.RedirectStandardOutput = true; - dotNetTraceInstaller.Start(); - dotNetTraceInstaller.WaitForExit(); + using (Process dotNetTraceInstaller = new()) + { + dotNetTraceInstaller.StartInfo.FileName = "dotnet"; + dotNetTraceInstaller.StartInfo.Arguments = $"tool install dotnet-trace --tool-path {dependenciesFolder}"; + dotNetTraceInstaller.StartInfo.UseShellExecute = false; + dotNetTraceInstaller.StartInfo.CreateNoWindow = true; + dotNetTraceInstaller.StartInfo.RedirectStandardError = true; + dotNetTraceInstaller.StartInfo.RedirectStandardOutput = true; + dotNetTraceInstaller.Start(); + Console.WriteLine("install dotnet-trace"); + dotNetTraceInstaller.WaitForExit(); + } } -#endif } public TraceCollector(string name, string collectType, string outputPath, int? pid = null) @@ -135,53 +137,56 @@ public TraceCollector(string name, string collectType, string outputPath, int? p name = name.Replace("<", ""); name = name.Replace(">", ""); -#if Windows - _collectorPath = Path.Combine(DependenciesFolder, "Perfview.exe"); - _sessionName = Guid.NewGuid(); - - Name = Path.Combine(outputPath, $"{name}.etl"); - string ALWAYS_ARGS = @$" /AcceptEULA /NoGUI /Merge:true"; - arguments = $"{ALWAYS_ARGS} /sessionName:{_sessionName} {WindowsCollectTypeMap[_collectType]} /LogFile:{Path.Combine(outputPath, name)}.txt /DataFile:{Name}"; - string command = $"start {arguments}"; - - _traceProcess = new(); - _traceProcess.StartInfo.FileName = _collectorPath; - _traceProcess.StartInfo.Arguments = command; - _traceProcess.StartInfo.UseShellExecute = false; - _traceProcess.StartInfo.CreateNoWindow = true; - _traceProcess.StartInfo.RedirectStandardError = true; - _traceProcess.StartInfo.RedirectStandardOutput = true; - _traceProcess.Start(); - - // Give PerfView about a second to get started. - Thread.Sleep(1000); - -#else - if (pid == null) + if (OperatingSystem.IsWindows()) { - throw new Exception($"{nameof(TraceCollector)}: Must provide prcoess id in Linux case"); + _collectorPath = Path.Combine(DependenciesFolder, "Perfview.exe"); + _sessionName = Guid.NewGuid(); + + Name = Path.Combine(outputPath, $"{name}.etl"); + string ALWAYS_ARGS = @$" /AcceptEULA /NoGUI /Merge:true"; + arguments = $"{ALWAYS_ARGS} /sessionName:{_sessionName} {WindowsCollectTypeMap[_collectType]} /LogFile:{Path.Combine(outputPath, name)}.txt /DataFile:{Name}"; + string command = $"start {arguments}"; + + _traceProcess = new(); + _traceProcess.StartInfo.FileName = _collectorPath; + _traceProcess.StartInfo.Arguments = command; + _traceProcess.StartInfo.UseShellExecute = false; + _traceProcess.StartInfo.CreateNoWindow = true; + _traceProcess.StartInfo.RedirectStandardError = true; + _traceProcess.StartInfo.RedirectStandardOutput = true; + _traceProcess.Start(); + + // Give PerfView about a second to get started. + Thread.Sleep(1000); } - - if (_collectType != CollectType.none && !LinuxLocalRunCollectTypeMap.Keys.Contains(_collectType)) + + if (OperatingSystem.IsLinux()) { - throw new Exception($"{nameof(TraceCollector)}: Trace collect type {collectType} is not supported for Linux local run."); - } + if (pid == null) + { + throw new Exception($"{nameof(TraceCollector)}: Must provide prcoess id in Linux case"); + } + + if (_collectType != CollectType.none && !LinuxLocalRunCollectTypeMap.Keys.Contains(_collectType)) + { + throw new Exception($"{nameof(TraceCollector)}: Trace collect type {collectType} is not supported for Linux local run."); + } + + _collectorPath = Path.Combine(DependenciesFolder, "dotnet-trace"); - _collectorPath = Path.Combine(DependenciesFolder, "dotnet-trace"); - - Name = Path.Combine(outputPath, $"{name}.nettrace"); - arguments = $"-p {pid} -o {Name} {LinuxLocalRunCollectTypeMap[_collectType]}"; - string command = $"collect {arguments}"; - - _traceProcess = new(); - _traceProcess.StartInfo.FileName = _collectorPath; - _traceProcess.StartInfo.Arguments = command; - _traceProcess.StartInfo.UseShellExecute = false; - _traceProcess.StartInfo.CreateNoWindow = true; - _traceProcess.StartInfo.RedirectStandardError = true; - _traceProcess.StartInfo.RedirectStandardOutput = true; - _traceProcess.Start(); -#endif + Name = Path.Combine(outputPath, $"{name}.nettrace"); + arguments = $"-p {pid} -o {Name} {LinuxLocalRunCollectTypeMap[_collectType]}"; + string command = $"collect {arguments}"; + + _traceProcess = new(); + _traceProcess.StartInfo.FileName = _collectorPath; + _traceProcess.StartInfo.Arguments = command; + _traceProcess.StartInfo.UseShellExecute = false; + _traceProcess.StartInfo.CreateNoWindow = true; + _traceProcess.StartInfo.RedirectStandardError = true; + _traceProcess.StartInfo.RedirectStandardOutput = true; + _traceProcess.Start(); + } } private void Dispose(bool disposing) @@ -193,60 +198,63 @@ private void Dispose(bool disposing) } // TODO: Parameterize the wait for exit time. - -#if Windows - if (!disposedValue) + if (OperatingSystem.IsWindows()) { - using (Process stopProcess = new()) + if (!disposedValue) { - stopProcess.StartInfo.FileName = _collectorPath; - string command = $"stop {arguments}"; - stopProcess.StartInfo.Arguments = command; - stopProcess.StartInfo.UseShellExecute = false; - stopProcess.StartInfo.CreateNoWindow = true; - stopProcess.StartInfo.RedirectStandardInput = true; - stopProcess.StartInfo.RedirectStandardError = true; - stopProcess.Start(); - stopProcess.WaitForExit(200_000); - _traceProcess?.Dispose(); - } - - // Clean up any dangling ETW sessions for both the Kernel and the session. - using (Process stopLogmanKernelProcess = new()) - { - stopLogmanKernelProcess.StartInfo.FileName = "logman"; - string etsStopCommand = $"-ets stop {_sessionName}Kernel"; - stopLogmanKernelProcess.StartInfo.Arguments = etsStopCommand; - stopLogmanKernelProcess.StartInfo.UseShellExecute = false; - stopLogmanKernelProcess.StartInfo.RedirectStandardOutput = false; - stopLogmanKernelProcess.StartInfo.RedirectStandardError = false; - stopLogmanKernelProcess.StartInfo.CreateNoWindow = true; - stopLogmanKernelProcess.Start(); - stopLogmanKernelProcess.WaitForExit(5_000); + using (Process stopProcess = new()) + { + stopProcess.StartInfo.FileName = _collectorPath; + string command = $"stop {arguments}"; + stopProcess.StartInfo.Arguments = command; + stopProcess.StartInfo.UseShellExecute = false; + stopProcess.StartInfo.CreateNoWindow = true; + stopProcess.StartInfo.RedirectStandardInput = true; + stopProcess.StartInfo.RedirectStandardError = true; + stopProcess.Start(); + stopProcess.WaitForExit(200_000); + _traceProcess?.Dispose(); + } + + // Clean up any dangling ETW sessions for both the Kernel and the session. + using (Process stopLogmanKernelProcess = new()) + { + stopLogmanKernelProcess.StartInfo.FileName = "logman"; + string etsStopCommand = $"-ets stop {_sessionName}Kernel"; + stopLogmanKernelProcess.StartInfo.Arguments = etsStopCommand; + stopLogmanKernelProcess.StartInfo.UseShellExecute = false; + stopLogmanKernelProcess.StartInfo.RedirectStandardOutput = false; + stopLogmanKernelProcess.StartInfo.RedirectStandardError = false; + stopLogmanKernelProcess.StartInfo.CreateNoWindow = true; + stopLogmanKernelProcess.Start(); + stopLogmanKernelProcess.WaitForExit(5_000); + } + + using (Process stopLogmanProcess = new()) + { + stopLogmanProcess.StartInfo.FileName = "logman"; + string etsStopCommand = $"-ets stop {_sessionName}"; + stopLogmanProcess.StartInfo.Arguments = etsStopCommand; + stopLogmanProcess.StartInfo.UseShellExecute = false; + stopLogmanProcess.StartInfo.RedirectStandardOutput = false; + stopLogmanProcess.StartInfo.RedirectStandardError = false; + stopLogmanProcess.StartInfo.CreateNoWindow = true; + stopLogmanProcess.Start(); + stopLogmanProcess.WaitForExit(5_000); + } + + disposedValue = true; } + } - using (Process stopLogmanProcess = new()) + if (OperatingSystem.IsLinux()) + { + if (!disposedValue) { - stopLogmanProcess.StartInfo.FileName = "logman"; - string etsStopCommand = $"-ets stop {_sessionName}"; - stopLogmanProcess.StartInfo.Arguments = etsStopCommand; - stopLogmanProcess.StartInfo.UseShellExecute = false; - stopLogmanProcess.StartInfo.RedirectStandardOutput = false; - stopLogmanProcess.StartInfo.RedirectStandardError = false; - stopLogmanProcess.StartInfo.CreateNoWindow = true; - stopLogmanProcess.Start(); - stopLogmanProcess.WaitForExit(5_000); + _traceProcess.WaitForExit(); + _traceProcess.Dispose(); } - - disposedValue = true; - } -#else - if (!disposedValue) - { - _traceProcess.WaitForExit(); - _traceProcess.Dispose(); } -#endif } From 402e722eb12791e6bdc9ec120dae2e93a61b1daf Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Mon, 31 Mar 2025 09:15:02 +0800 Subject: [PATCH 10/10] use default tracing arguments to collect cpu type trace --- .../GC.Infrastructure.Core/TraceCollection/TraceCollector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs index 226951dc045..dee739f21e8 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/TraceCollection/TraceCollector.cs @@ -45,7 +45,7 @@ public sealed class TraceCollector : IDisposable internal static readonly Dictionary LinuxLocalRunCollectTypeMap = new() { { CollectType.gc, "--profile gc-collect" }, - { CollectType.cpu, "--profile cpu-sampling" }, + { CollectType.cpu, "" }, { CollectType.verbose, "--clrevents gc+stack --clreventlevel verbose" } };