From 9dba210726b0ea3a07ca88c7111140bae92b90e3 Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Wed, 5 Nov 2025 12:39:40 +0100 Subject: [PATCH 1/7] Add a lock to avoid exceptions --- .../ProfilerHelper.cs | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index 6c0d4ab4f061..6734e41d9df7 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -19,6 +19,8 @@ public class ProfilerHelper .Concat(new[] { "dotnet", "iisexpress" }) .ToArray(); + private static readonly object CorFlagsLock = new object(); + private static string _corFlagsExe; public static async Task StartProcessWithProfiler( @@ -89,53 +91,58 @@ public static async Task StartProcessWithProfiler( public static void SetCorFlags(string executable, ITestOutputHelper output, bool require32Bit) { - var corFlagsExe = _corFlagsExe; - var setBit = require32Bit ? "/32BITREQ+" : "/32BITREQ-"; - if (string.IsNullOrEmpty(corFlagsExe)) + // Use a lock to prevent parallel tests from modifying the same executable concurrently + // which can cause "Access is denied" errors in CI + lock (CorFlagsLock) { - var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); - var dotnetWindowsSdkToolsFolder = Path.Combine(programFiles, "Microsoft SDKs", "Windows", "v10.0A", "bin"); - - output?.WriteLine($"Searching for CorFlags.exe in {dotnetWindowsSdkToolsFolder}"); - if (Directory.Exists(dotnetWindowsSdkToolsFolder)) + var corFlagsExe = _corFlagsExe; + var setBit = require32Bit ? "/32BITREQ+" : "/32BITREQ-"; + if (string.IsNullOrEmpty(corFlagsExe)) { - // get sub directories, e.g. - // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools", - // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools", - foreach (var folder in Directory.EnumerateDirectories(dotnetWindowsSdkToolsFolder)) + var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + var dotnetWindowsSdkToolsFolder = Path.Combine(programFiles, "Microsoft SDKs", "Windows", "v10.0A", "bin"); + + output?.WriteLine($"Searching for CorFlags.exe in {dotnetWindowsSdkToolsFolder}"); + if (Directory.Exists(dotnetWindowsSdkToolsFolder)) { - var exe = Path.Combine(folder, "x64", "CorFlags.exe"); - if (File.Exists(exe)) + // get sub directories, e.g. + // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools", + // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools", + foreach (var folder in Directory.EnumerateDirectories(dotnetWindowsSdkToolsFolder)) { - corFlagsExe = exe; - break; + var exe = Path.Combine(folder, "x64", "CorFlags.exe"); + if (File.Exists(exe)) + { + corFlagsExe = exe; + break; + } } } - } - if (!string.IsNullOrEmpty(corFlagsExe)) - { - Interlocked.Exchange(ref _corFlagsExe, corFlagsExe); - output?.WriteLine($"CorFlags.exe found at {corFlagsExe}"); - } - else - { - throw new Exception($"Could not find CorFlags.exe so unable to set {setBit}"); + if (!string.IsNullOrEmpty(corFlagsExe)) + { + Interlocked.Exchange(ref _corFlagsExe, corFlagsExe); + output?.WriteLine($"CorFlags.exe found at {corFlagsExe}"); + } + else + { + throw new Exception($"Could not find CorFlags.exe so unable to set {setBit}"); + } } - } - output?.WriteLine($"Updating {Path.GetFileName(executable)} using {setBit}"); - var opts = new ProcessStartInfo(corFlagsExe, $"\"{executable}\" {setBit} /force") - { - CreateNoWindow = true, - UseShellExecute = false - }; + output?.WriteLine($"Updating {Path.GetFileName(executable)} using {setBit}"); + var opts = new ProcessStartInfo(corFlagsExe, $"\"{executable}\" {setBit} /force") + { + CreateNoWindow = true, + UseShellExecute = false + }; - var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); + var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); - if (!executedSuccessfully) - { - throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + if (!executedSuccessfully) + { + throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + } } } } From 6db0aa8bf9d955c05696707a4f781b880ea98a97 Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Wed, 5 Nov 2025 15:24:30 +0100 Subject: [PATCH 2/7] Move lock to ConsoleTestHelper --- .../ProfilerHelper.cs | 79 +++++++++---------- .../ConsoleTestHelper.cs | 15 +++- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index 6734e41d9df7..6c0d4ab4f061 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -19,8 +19,6 @@ public class ProfilerHelper .Concat(new[] { "dotnet", "iisexpress" }) .ToArray(); - private static readonly object CorFlagsLock = new object(); - private static string _corFlagsExe; public static async Task StartProcessWithProfiler( @@ -91,59 +89,54 @@ public static async Task StartProcessWithProfiler( public static void SetCorFlags(string executable, ITestOutputHelper output, bool require32Bit) { - // Use a lock to prevent parallel tests from modifying the same executable concurrently - // which can cause "Access is denied" errors in CI - lock (CorFlagsLock) + var corFlagsExe = _corFlagsExe; + var setBit = require32Bit ? "/32BITREQ+" : "/32BITREQ-"; + if (string.IsNullOrEmpty(corFlagsExe)) { - var corFlagsExe = _corFlagsExe; - var setBit = require32Bit ? "/32BITREQ+" : "/32BITREQ-"; - if (string.IsNullOrEmpty(corFlagsExe)) - { - var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); - var dotnetWindowsSdkToolsFolder = Path.Combine(programFiles, "Microsoft SDKs", "Windows", "v10.0A", "bin"); + var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + var dotnetWindowsSdkToolsFolder = Path.Combine(programFiles, "Microsoft SDKs", "Windows", "v10.0A", "bin"); - output?.WriteLine($"Searching for CorFlags.exe in {dotnetWindowsSdkToolsFolder}"); - if (Directory.Exists(dotnetWindowsSdkToolsFolder)) + output?.WriteLine($"Searching for CorFlags.exe in {dotnetWindowsSdkToolsFolder}"); + if (Directory.Exists(dotnetWindowsSdkToolsFolder)) + { + // get sub directories, e.g. + // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools", + // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools", + foreach (var folder in Directory.EnumerateDirectories(dotnetWindowsSdkToolsFolder)) { - // get sub directories, e.g. - // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools", - // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools", - foreach (var folder in Directory.EnumerateDirectories(dotnetWindowsSdkToolsFolder)) + var exe = Path.Combine(folder, "x64", "CorFlags.exe"); + if (File.Exists(exe)) { - var exe = Path.Combine(folder, "x64", "CorFlags.exe"); - if (File.Exists(exe)) - { - corFlagsExe = exe; - break; - } + corFlagsExe = exe; + break; } } - - if (!string.IsNullOrEmpty(corFlagsExe)) - { - Interlocked.Exchange(ref _corFlagsExe, corFlagsExe); - output?.WriteLine($"CorFlags.exe found at {corFlagsExe}"); - } - else - { - throw new Exception($"Could not find CorFlags.exe so unable to set {setBit}"); - } } - output?.WriteLine($"Updating {Path.GetFileName(executable)} using {setBit}"); - var opts = new ProcessStartInfo(corFlagsExe, $"\"{executable}\" {setBit} /force") + if (!string.IsNullOrEmpty(corFlagsExe)) { - CreateNoWindow = true, - UseShellExecute = false - }; - - var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); - - if (!executedSuccessfully) + Interlocked.Exchange(ref _corFlagsExe, corFlagsExe); + output?.WriteLine($"CorFlags.exe found at {corFlagsExe}"); + } + else { - throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + throw new Exception($"Could not find CorFlags.exe so unable to set {setBit}"); } } + + output?.WriteLine($"Updating {Path.GetFileName(executable)} using {setBit}"); + var opts = new ProcessStartInfo(corFlagsExe, $"\"{executable}\" {setBit} /force") + { + CreateNoWindow = true, + UseShellExecute = false + }; + + var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); + + if (!executedSuccessfully) + { + throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + } } } } diff --git a/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs b/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs index 3af63b6b0665..f8b192854449 100644 --- a/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs +++ b/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs @@ -16,6 +16,8 @@ namespace Datadog.Trace.Tools.dd_dotnet.ArtifactTests; public abstract class ConsoleTestHelper : ToolTestHelper { + private static readonly object CorFlagsLock = new object(); + protected ConsoleTestHelper(ITestOutputHelper output) : base("Console", output) { @@ -43,7 +45,10 @@ protected Task StartConsoleWithArgs(string args, bool enabl && !EnvironmentHelper.IsCoreClr() && !EnvironmentTools.IsTestTarget64BitProcess()) { - ProfilerHelper.SetCorFlags(executable, Output, !EnvironmentTools.IsTestTarget64BitProcess()); + lock (CorFlagsLock) + { + ProfilerHelper.SetCorFlags(executable, Output, !EnvironmentTools.IsTestTarget64BitProcess()); + } } return (executable, args); @@ -96,7 +101,13 @@ protected async Task StartConsole(string executable, string } }; - var helper = new CustomProcessHelper(agent, Process.Start(processStart)!, callback); + // Lock around Process.Start to prevent concurrent CorFlags modification + // from another thread causing "Access is denied" errors + CustomProcessHelper helper; + lock (CorFlagsLock) + { + helper = new CustomProcessHelper(agent, Process.Start(processStart)!, callback); + } var completed = await Task.WhenAny( helper.Task, From ed6ac5ad8b0584007c87c961d2d6b4f6f0474480 Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Thu, 6 Nov 2025 10:37:28 +0100 Subject: [PATCH 3/7] Apply corflags only once --- .../ProfilerHelper.cs | 90 ++++++++++++------- .../ConsoleTestHelper.cs | 15 +--- 2 files changed, 58 insertions(+), 47 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index 6c0d4ab4f061..3a91e727fe6d 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -4,6 +4,7 @@ // using System; +using System.Collections.Concurrent; using System.Diagnostics; using System.IO; using System.Linq; @@ -19,6 +20,8 @@ public class ProfilerHelper .Concat(new[] { "dotnet", "iisexpress" }) .ToArray(); + private static readonly ConcurrentDictionary _corFlagsLocks = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _corFlagsApplied = new ConcurrentDictionary(); private static string _corFlagsExe; public static async Task StartProcessWithProfiler( @@ -89,53 +92,72 @@ public static async Task StartProcessWithProfiler( public static void SetCorFlags(string executable, ITestOutputHelper output, bool require32Bit) { - var corFlagsExe = _corFlagsExe; - var setBit = require32Bit ? "/32BITREQ+" : "/32BITREQ-"; - if (string.IsNullOrEmpty(corFlagsExe)) + // Use the absolute path as the key to track which executables have been modified + var executablePath = Path.GetFullPath(executable); + + // Get or create a lock object for this specific executable + var lockObj = _corFlagsLocks.GetOrAdd(executablePath, _ => new object()); + + lock (lockObj) { - var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); - var dotnetWindowsSdkToolsFolder = Path.Combine(programFiles, "Microsoft SDKs", "Windows", "v10.0A", "bin"); + // Check if we've already applied CorFlags to this executable + if (_corFlagsApplied.TryGetValue(executablePath, out _)) + { + output?.WriteLine($"CorFlags already applied to {Path.GetFileName(executable)}, skipping"); + return; + } - output?.WriteLine($"Searching for CorFlags.exe in {dotnetWindowsSdkToolsFolder}"); - if (Directory.Exists(dotnetWindowsSdkToolsFolder)) + var corFlagsExe = _corFlagsExe; + var setBit = require32Bit ? "/32BITREQ+" : "/32BITREQ-"; + if (string.IsNullOrEmpty(corFlagsExe)) { - // get sub directories, e.g. - // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools", - // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools", - foreach (var folder in Directory.EnumerateDirectories(dotnetWindowsSdkToolsFolder)) + var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + var dotnetWindowsSdkToolsFolder = Path.Combine(programFiles, "Microsoft SDKs", "Windows", "v10.0A", "bin"); + + output?.WriteLine($"Searching for CorFlags.exe in {dotnetWindowsSdkToolsFolder}"); + if (Directory.Exists(dotnetWindowsSdkToolsFolder)) { - var exe = Path.Combine(folder, "x64", "CorFlags.exe"); - if (File.Exists(exe)) + // get sub directories, e.g. + // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools", + // @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools", + foreach (var folder in Directory.EnumerateDirectories(dotnetWindowsSdkToolsFolder)) { - corFlagsExe = exe; - break; + var exe = Path.Combine(folder, "x64", "CorFlags.exe"); + if (File.Exists(exe)) + { + corFlagsExe = exe; + break; + } } } - } - if (!string.IsNullOrEmpty(corFlagsExe)) - { - Interlocked.Exchange(ref _corFlagsExe, corFlagsExe); - output?.WriteLine($"CorFlags.exe found at {corFlagsExe}"); + if (!string.IsNullOrEmpty(corFlagsExe)) + { + Interlocked.Exchange(ref _corFlagsExe, corFlagsExe); + output?.WriteLine($"CorFlags.exe found at {corFlagsExe}"); + } + else + { + throw new Exception($"Could not find CorFlags.exe so unable to set {setBit}"); + } } - else + + output?.WriteLine($"Updating {Path.GetFileName(executable)} using {setBit}"); + var opts = new ProcessStartInfo(corFlagsExe, $"\"{executable}\" {setBit} /force") { - throw new Exception($"Could not find CorFlags.exe so unable to set {setBit}"); - } - } + CreateNoWindow = true, + UseShellExecute = false + }; - output?.WriteLine($"Updating {Path.GetFileName(executable)} using {setBit}"); - var opts = new ProcessStartInfo(corFlagsExe, $"\"{executable}\" {setBit} /force") - { - CreateNoWindow = true, - UseShellExecute = false - }; + var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); - var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); + if (!executedSuccessfully) + { + throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + } - if (!executedSuccessfully) - { - throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + // Mark this executable as having been modified + _corFlagsApplied.TryAdd(executablePath, true); } } } diff --git a/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs b/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs index f8b192854449..3af63b6b0665 100644 --- a/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs +++ b/tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/ConsoleTestHelper.cs @@ -16,8 +16,6 @@ namespace Datadog.Trace.Tools.dd_dotnet.ArtifactTests; public abstract class ConsoleTestHelper : ToolTestHelper { - private static readonly object CorFlagsLock = new object(); - protected ConsoleTestHelper(ITestOutputHelper output) : base("Console", output) { @@ -45,10 +43,7 @@ protected Task StartConsoleWithArgs(string args, bool enabl && !EnvironmentHelper.IsCoreClr() && !EnvironmentTools.IsTestTarget64BitProcess()) { - lock (CorFlagsLock) - { - ProfilerHelper.SetCorFlags(executable, Output, !EnvironmentTools.IsTestTarget64BitProcess()); - } + ProfilerHelper.SetCorFlags(executable, Output, !EnvironmentTools.IsTestTarget64BitProcess()); } return (executable, args); @@ -101,13 +96,7 @@ protected async Task StartConsole(string executable, string } }; - // Lock around Process.Start to prevent concurrent CorFlags modification - // from another thread causing "Access is denied" errors - CustomProcessHelper helper; - lock (CorFlagsLock) - { - helper = new CustomProcessHelper(agent, Process.Start(processStart)!, callback); - } + var helper = new CustomProcessHelper(agent, Process.Start(processStart)!, callback); var completed = await Task.WhenAny( helper.Task, From e59735cb7b8be2165e885117c2e1b3342768ba8f Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Thu, 6 Nov 2025 10:53:28 +0100 Subject: [PATCH 4/7] Make dictionary case insensitive --- tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index 3a91e727fe6d..657ef5144d0a 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -20,8 +20,8 @@ public class ProfilerHelper .Concat(new[] { "dotnet", "iisexpress" }) .ToArray(); - private static readonly ConcurrentDictionary _corFlagsLocks = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary _corFlagsApplied = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _corFlagsLocks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static readonly ConcurrentDictionary _corFlagsApplied = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private static string _corFlagsExe; public static async Task StartProcessWithProfiler( From bb295b8b560ece184a49fef88c6a80706a7832b9 Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Thu, 6 Nov 2025 11:22:14 +0100 Subject: [PATCH 5/7] Remove redundant comments --- tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index 657ef5144d0a..ce54574a9bd2 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -94,13 +94,10 @@ public static void SetCorFlags(string executable, ITestOutputHelper output, bool { // Use the absolute path as the key to track which executables have been modified var executablePath = Path.GetFullPath(executable); - - // Get or create a lock object for this specific executable var lockObj = _corFlagsLocks.GetOrAdd(executablePath, _ => new object()); lock (lockObj) { - // Check if we've already applied CorFlags to this executable if (_corFlagsApplied.TryGetValue(executablePath, out _)) { output?.WriteLine($"CorFlags already applied to {Path.GetFileName(executable)}, skipping"); @@ -156,7 +153,6 @@ public static void SetCorFlags(string executable, ITestOutputHelper output, bool throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); } - // Mark this executable as having been modified _corFlagsApplied.TryAdd(executablePath, true); } } From 2db74e520f8e9fbf0211a563c0f143d0fe6e292a Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Thu, 6 Nov 2025 12:02:17 +0100 Subject: [PATCH 6/7] Single lock --- .../test/Datadog.Trace.TestHelpers/ProfilerHelper.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index ce54574a9bd2..ff3dc862aff2 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -20,7 +20,7 @@ public class ProfilerHelper .Concat(new[] { "dotnet", "iisexpress" }) .ToArray(); - private static readonly ConcurrentDictionary _corFlagsLocks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static readonly object _lockObj = new(); private static readonly ConcurrentDictionary _corFlagsApplied = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private static string _corFlagsExe; @@ -94,13 +94,17 @@ public static void SetCorFlags(string executable, ITestOutputHelper output, bool { // Use the absolute path as the key to track which executables have been modified var executablePath = Path.GetFullPath(executable); - var lockObj = _corFlagsLocks.GetOrAdd(executablePath, _ => new object()); - lock (lockObj) + if (_corFlagsApplied.TryGetValue(executablePath, out _)) { + return; + } + + lock (_lockObj) + { + // check again to be safe if (_corFlagsApplied.TryGetValue(executablePath, out _)) { - output?.WriteLine($"CorFlags already applied to {Path.GetFileName(executable)}, skipping"); return; } From d166e4fd208c22cbf2d79cc0b0f040ce4333a1ea Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Tue, 11 Nov 2025 14:31:40 +0100 Subject: [PATCH 7/7] Dispose process --- .../Datadog.Trace.TestHelpers/ProfilerHelper.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs index ff3dc862aff2..125977a5b669 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/ProfilerHelper.cs @@ -150,11 +150,18 @@ public static void SetCorFlags(string executable, ITestOutputHelper output, bool UseShellExecute = false }; - var executedSuccessfully = Process.Start(opts).WaitForExit(20_000); - - if (!executedSuccessfully) + using (var process = Process.Start(opts)) { - throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + if (process == null) + { + throw new Exception("Failed to start CorFlags process."); + } + + var exited = process.WaitForExit(20_000); + if (!exited) + { + throw new Exception($"Error setting CorFlags.exe {Path.GetFileName(executable)} {setBit}"); + } } _corFlagsApplied.TryAdd(executablePath, true);