diff --git a/src/Microsoft.TestPlatform.Execution.Shared/Microsoft.TestPlatform.Execution.Shared.projitems b/src/Microsoft.TestPlatform.Execution.Shared/Microsoft.TestPlatform.Execution.Shared.projitems
index de0ddc3ab4..2269ef03d4 100644
--- a/src/Microsoft.TestPlatform.Execution.Shared/Microsoft.TestPlatform.Execution.Shared.projitems
+++ b/src/Microsoft.TestPlatform.Execution.Shared/Microsoft.TestPlatform.Execution.Shared.projitems
@@ -10,6 +10,7 @@
+
diff --git a/src/Microsoft.TestPlatform.Execution.Shared/ProcDumpExecutableHelper.cs b/src/Microsoft.TestPlatform.Execution.Shared/ProcDumpExecutableHelper.cs
new file mode 100644
index 0000000000..a81ff801c2
--- /dev/null
+++ b/src/Microsoft.TestPlatform.Execution.Shared/ProcDumpExecutableHelper.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.IO;
+
+using Microsoft.VisualStudio.TestPlatform.CoreUtilities;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
+using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+
+namespace Microsoft.VisualStudio.TestPlatform.Execution;
+
+internal class ProcDumpExecutableHelper
+{
+ private const string ProcdumpUnixProcess = "procdump";
+
+ private readonly IProcessHelper _processHelper;
+ private readonly IFileHelper _fileHelper;
+ private readonly IEnvironment _environment;
+ private readonly IEnvironmentVariableHelper _environmentVariableHelper;
+
+ public ProcDumpExecutableHelper(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment, IEnvironmentVariableHelper environmentVariableHelper)
+ {
+ _processHelper = processHelper;
+ _fileHelper = fileHelper;
+ _environment = environment;
+ _environmentVariableHelper = environmentVariableHelper;
+ }
+
+ public static string ProcDumpFileName(PlatformArchitecture architecture) =>
+ architecture switch
+ {
+ PlatformArchitecture.X86 => "procdump.exe",
+ PlatformArchitecture.ARM64 => "procdump64a.exe",
+ _ => "procdump64.exe",
+ };
+
+ public bool TryGetProcDumpExecutable(out string path)
+ {
+ // Use machine architecture
+ var targetProcessArchitecture = _environment.Architecture;
+ return TryGetProcDumpExecutable(targetProcessArchitecture, out path);
+ }
+
+ public bool TryGetProcDumpExecutable(int processId, out string path)
+ {
+ // Launch proc dump according to process architecture
+ var targetProcessArchitecture = _processHelper.GetProcessArchitecture(processId);
+ return TryGetProcDumpExecutable(targetProcessArchitecture, out path);
+ }
+
+ public bool TryGetProcDumpExecutable(PlatformArchitecture architecture, out string path)
+ {
+ var procdumpDirectory = _environmentVariableHelper.GetEnvironmentVariable("PROCDUMP_PATH");
+ var searchPath = false;
+ if (procdumpDirectory.IsNullOrWhiteSpace())
+ {
+ EqtTrace.Verbose("ProcDumpExecutableHelper.GetProcDumpExecutable: PROCDUMP_PATH env variable is empty will try to run ProcDump from PATH.");
+ searchPath = true;
+ }
+ else if (!_fileHelper.DirectoryExists(procdumpDirectory))
+ {
+ EqtTrace.Verbose($"ProcDumpExecutableHelper.GetProcDumpExecutable: PROCDUMP_PATH env variable '{procdumpDirectory}' is not a directory, or the directory does not exist. Will try to run ProcDump from PATH.");
+ searchPath = true;
+ }
+
+ string filename = _environment.OperatingSystem == PlatformOperatingSystem.Windows
+ ? ProcDumpFileName(architecture)
+ : _environment.OperatingSystem is PlatformOperatingSystem.Unix or PlatformOperatingSystem.OSX
+ ? ProcdumpUnixProcess
+ : throw new NotSupportedException($"Not supported platform {_environment.OperatingSystem}");
+
+ if (!searchPath)
+ {
+ var candidatePath = Path.Combine(procdumpDirectory!, filename);
+ if (_fileHelper.Exists(candidatePath))
+ {
+ EqtTrace.Verbose($"ProcDumpExecutableHelper.GetProcDumpExecutable: Path to ProcDump '{candidatePath}' exists, using that.");
+ path = candidatePath;
+ return true;
+ }
+
+ EqtTrace.Verbose($"ProcDumpExecutableHelper.GetProcDumpExecutable: Path '{candidatePath}' does not exist will try to run {filename} from PATH.");
+ }
+
+ if (TryGetExecutablePath(filename, out var p))
+ {
+ EqtTrace.Verbose($"ProcDumpExecutableHelper.GetProcDumpExecutable: Resolved {filename} to {p} from PATH.");
+ path = p;
+ return true;
+ }
+
+ EqtTrace.Verbose($"ProcDumpExecutableHelper.GetProcDumpExecutable: Could not find {filename} on PATH.");
+ path = filename;
+ return false;
+ }
+
+ private bool TryGetExecutablePath(string executable, out string executablePath)
+ {
+ executablePath = string.Empty;
+ var pathString = _environmentVariableHelper.GetEnvironmentVariable("PATH") ?? string.Empty;
+ foreach (string path in pathString.Split(Path.PathSeparator))
+ {
+ string exeFullPath = Path.Combine(path.Trim(), executable);
+ if (_fileHelper.Exists(exeFullPath))
+ {
+ executablePath = exeFullPath;
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs
index 0752e9a95a..4436f79ff1 100644
--- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs
+++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs
@@ -9,6 +9,8 @@
using System.IO;
using System.Linq;
+using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers;
+using Microsoft.VisualStudio.TestPlatform.Execution;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
@@ -29,6 +31,7 @@ public class ProcDumpDumper : ICrashDumper, IHangDumper
private readonly IProcessHelper _processHelper;
private readonly IFileHelper _fileHelper;
private readonly IEnvironment _environment;
+ private readonly IEnvironmentVariableHelper _environmentVariableHelper;
private Process? _procDumpProcess;
private string? _tempDirectory;
private string? _dumpFileName;
@@ -36,19 +39,30 @@ public class ProcDumpDumper : ICrashDumper, IHangDumper
private string? _outputDirectory;
private Process? _process;
private string? _outputFilePrefix;
+ private readonly ProcDumpExecutableHelper _procDumpExecutableHelper;
public ProcDumpDumper()
- : this(new ProcessHelper(), new FileHelper(), new PlatformEnvironment())
+ : this(new ProcessHelper(), new FileHelper(), new PlatformEnvironment(), new EnvironmentVariableHelper())
{
}
- public ProcDumpDumper(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment)
+ public ProcDumpDumper(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment) :
+ this(processHelper, fileHelper, environment, new EnvironmentVariableHelper())
{
_processHelper = processHelper;
_fileHelper = fileHelper;
_environment = environment;
}
+ internal ProcDumpDumper(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment, IEnvironmentVariableHelper environmentVariableHelper)
+ {
+ _processHelper = processHelper;
+ _fileHelper = fileHelper;
+ _environment = environment;
+ _environmentVariableHelper = environmentVariableHelper;
+ _procDumpExecutableHelper = new ProcDumpExecutableHelper(processHelper, fileHelper, environment, environmentVariableHelper);
+ }
+
[SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of the public API")]
protected Action