Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use environment variables for AeDebugger mode #4049

Merged
merged 6 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)DebuggerBreakpoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ProcDumpExecutableHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UILanguageOverride.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WellKnownDebugEnvironmentVariables.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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;

MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -38,17 +41,26 @@ public class ProcDumpDumper : ICrashDumper, IHangDumper
private string? _outputFilePrefix;

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;
}

[SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of the public API")]
protected Action<object?, string?> OutputReceivedCallback => (process, data) =>
// useful for visibility when debugging this tool
Expand Down Expand Up @@ -86,7 +98,7 @@ public void AttachToTargetProcess(int processId, string outputDirectory, DumpTyp
throw new InvalidOperationException("Procdump crash dump file must end with .dmp extension.");
}

if (!TryGetProcDumpExecutable(processId, out var procDumpPath))
if (!new ProcDumpExecutableHelper(_processHelper, _fileHelper, _environment, _environmentVariableHelper).TryGetProcDumpExecutable(processId, out var procDumpPath))
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
{
var procdumpNotFound = string.Format(CultureInfo.CurrentCulture, Resources.Resources.ProcDumpNotFound, procDumpPath);
logWarning(procdumpNotFound);
Expand Down Expand Up @@ -205,7 +217,7 @@ public void Dump(int processId, string outputDirectory, DumpTypeOption dumpType)
throw new InvalidOperationException("Procdump crash dump file must end with .dmp extension.");
}

if (!TryGetProcDumpExecutable(processId, out var procDumpPath))
if (!new ProcDumpExecutableHelper(_processHelper, _fileHelper, _environment, _environmentVariableHelper).TryGetProcDumpExecutable(processId, out var procDumpPath))
{
var err = $"{procDumpPath} could not be found, please set PROCDUMP_PATH environment variable to a directory that contains {procDumpPath} executable, or make sure that the executable is available on PATH.";
ConsoleOutput.Instance.Warning(false, err);
Expand Down Expand Up @@ -237,90 +249,4 @@ public void Dump(int processId, string outputDirectory, DumpTypeOption dumpType)

EqtTrace.Info($"ProcDumpDumper.Dump: ProcDump finished hang dumping process with id '{processId}'.");
}

/// <summary>
/// Try get proc dump executable path from env variable or PATH, if it does not success the result is false, and the name of the exe we tried to find.
/// </summary>
/// <param name="processId">
/// Process Id to determine the bittness
/// </param>
/// <param name="path">
/// Path to procdump or the name of the executable we tried to resolve when we don't find it
/// </param>
/// <returns>proc dump executable path</returns>
private bool TryGetProcDumpExecutable(int processId, out string path)
{
var procdumpDirectory = Environment.GetEnvironmentVariable("PROCDUMP_PATH");
var searchPath = false;
if (procdumpDirectory.IsNullOrWhiteSpace())
{
EqtTrace.Verbose("ProcDumpDumper.GetProcDumpExecutable: PROCDUMP_PATH env variable is empty will try to run ProcDump from PATH.");
searchPath = true;
}
else if (!Directory.Exists(procdumpDirectory))
{
EqtTrace.Verbose($"ProcDumpDumper.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;
if (_environment.OperatingSystem == PlatformOperatingSystem.Windows)
{
// Launch proc dump according to process architecture
var targetProcessArchitecture = _processHelper.GetProcessArchitecture(processId);
filename = targetProcessArchitecture switch
{
PlatformArchitecture.X86 => "procdump.exe",
PlatformArchitecture.ARM64 => "procdump64a.exe",
_ => "procdump64.exe",
};
}
else
{
filename = _environment.OperatingSystem is PlatformOperatingSystem.Unix or PlatformOperatingSystem.OSX
? Constants.ProcdumpUnixProcess
: throw new NotSupportedException($"Not supported platform {_environment.OperatingSystem}");
}

if (!searchPath)
{
var candidatePath = Path.Combine(procdumpDirectory!, filename);
if (File.Exists(candidatePath))
{
EqtTrace.Verbose($"ProcDumpDumper.GetProcDumpExecutable: Path to ProcDump '{candidatePath}' exists, using that.");
path = candidatePath;
return true;
}

EqtTrace.Verbose($"ProcDumpDumper.GetProcDumpExecutable: Path '{candidatePath}' does not exist will try to run {filename} from PATH.");
}

if (TryGetExecutablePath(filename, out var p))
{
EqtTrace.Verbose($"ProcDumpDumper.GetProcDumpExecutable: Resolved {filename} to {p} from PATH.");
path = p;
return true;
}

EqtTrace.Verbose($"ProcDumpDumper.GetProcDumpExecutable: Could not find {filename} on PATH.");
path = filename;
return false;
}

private bool TryGetExecutablePath(string executable, out string executablePath)
{
executablePath = string.Empty;
var pathString = Environment.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;
}
}