Skip to content

Commit

Permalink
Fix get process architecture (#3726)
Browse files Browse the repository at this point in the history
* Fix get process architecture

* Fix getting process architecture, when it is not the current process

* Remove extra class

* One more fallback to x64

* Apply review comments

* Fix
  • Loading branch information
nohwnd authored Jun 8, 2022
1 parent 850cb2c commit 87cfa7a
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 73 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ public class ProcDumpDumper : ICrashDumper, IHangDumper
private string? _outputFilePrefix;

public ProcDumpDumper()
: this(new ProcessHelper(), new FileHelper(), new PlatformEnvironment(), new NativeMethodsHelper())
: this(new ProcessHelper(), new FileHelper(), new PlatformEnvironment())
{
}

public ProcDumpDumper(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment, INativeMethodsHelper? nativeMethodsHelper)
public ProcDumpDumper(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment)
{
_processHelper = processHelper;
_fileHelper = fileHelper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.DetachF
Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.GetDumpFiles(bool warnOnNoDumpFiles, bool processCrashed) -> System.Collections.Generic.IEnumerable<string!>!
Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.StartHangBasedProcessDump(int processId, string! testResultsDirectory, bool isFullDump, string! targetFramework, System.Action<string!>? logWarning = null) -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.StartTriggerBasedProcessDump(int processId, string! testResultsDirectory, bool isFullDump, string! targetFramework, bool collectAlways, System.Action<string!>! logWarning) -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.NativeMethodsHelper
Microsoft.TestPlatform.Extensions.BlameDataCollector.NativeMethodsHelper.Is64Bit(System.IntPtr processHandle) -> bool
Microsoft.TestPlatform.Extensions.BlameDataCollector.NativeMethodsHelper.NativeMethodsHelper() -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpArgsBuilder
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpArgsBuilder.BuildHangBasedProcDumpArgs(int processId, string! filename, bool isFullDump) -> string!
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpArgsBuilder.BuildTriggerBasedProcDumpArgs(int processId, string! filename, System.Collections.Generic.IEnumerable<string!>! procDumpExceptionsList, bool isFullDump) -> string!
Expand All @@ -74,7 +71,7 @@ Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.Dump(int pro
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.GetDumpFiles(bool processCrashed) -> System.Collections.Generic.IEnumerable<string!>!
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.OutputReceivedCallback.get -> System.Action<object?, string!>!
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.ProcDumpDumper() -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.ProcDumpDumper(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IProcessHelper! processHelper, Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper! fileHelper, Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IEnvironment! environment, Microsoft.TestPlatform.Extensions.BlameDataCollector.INativeMethodsHelper? nativeMethodsHelper) -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.ProcDumpDumper(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IProcessHelper! processHelper, Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper! fileHelper, Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IEnvironment! environment) -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.WaitForDumpToFinish() -> void
Microsoft.TestPlatform.Extensions.BlameDataCollector.Win32NamedEvent
Microsoft.TestPlatform.Extensions.BlameDataCollector.Win32NamedEvent.Set() -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ internal class NativeMethods
public const ushort IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
public const ushort IMAGE_FILE_MACHINE_UNKNOWN = 0;

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool IsWow64Process2(IntPtr process, out ushort processMachine, out ushort nativeMachine);
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWow64Process2([In] IntPtr process, [Out] out ushort processMachine, [Out] out ushort nativeMachine);

// A pointer to a value that is set to TRUE if the process is running under WOW64.
// If the process is running under 32-bit Windows, the value is set to FALSE.
// If the process is a 64-bit application running under 64-bit Windows, the value is also set to FALSE.
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool IsWow64Process([In] IntPtr process, [Out] out bool wow64Process);
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
public partial class ProcessHelper : IProcessHelper
{
private static readonly string Arm = "arm";
private readonly Process _currentProcess = Process.GetCurrentProcess();

/// <inheritdoc/>
public object LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary<string, string>? envVariables, Action<object?, string>? errorCallback, Action<object>? exitCallBack, Action<object?, string>? outputCallBack)
Expand Down Expand Up @@ -137,7 +138,7 @@ void InitializeAndStart()
/// <inheritdoc/>
public string GetCurrentProcessFileName()
{
return Process.GetCurrentProcess().MainModule.FileName;
return _currentProcess.MainModule.FileName;
}

/// <inheritdoc/>
Expand All @@ -149,12 +150,17 @@ public string GetTestEngineDirectory()
/// <inheritdoc/>
public int GetCurrentProcessId()
{
return Process.GetCurrentProcess().Id;
return _currentProcess.Id;
}

/// <inheritdoc/>
public string GetProcessName(int processId)
{
if (processId == _currentProcess.Id)
{
return _currentProcess.ProcessName;
}

return Process.GetProcessById(processId).ProcessName;
}

Expand Down Expand Up @@ -182,7 +188,7 @@ public void SetExitCallback(int processId, Action<object?>? callbackAction)
{
try
{
var process = Process.GetProcessById(processId);
var process = processId == _currentProcess.Id ? _currentProcess : Process.GetProcessById(processId);
process.EnableRaisingEvents = true;
process.Exited += (sender, args) => callbackAction?.Invoke(sender);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@ namespace Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
/// <inheritdoc />
public class PlatformEnvironment : IEnvironment
{
private PlatformArchitecture? _architecture;

/// <inheritdoc />
public PlatformArchitecture Architecture
=> Environment.Is64BitOperatingSystem
? IsArm64()
? PlatformArchitecture.ARM64
: PlatformArchitecture.X64
: PlatformArchitecture.X86;
{
get
{
return _architecture ??= Environment.Is64BitOperatingSystem
? IsArm64()
? PlatformArchitecture.ARM64
: PlatformArchitecture.X64
: PlatformArchitecture.X86;
}
}

private static bool IsArm64()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,69 @@ public string GetCurrentProcessLocation()
=> Path.GetDirectoryName(GetCurrentProcessFileName());

/// <inheritdoc/>
public IntPtr GetProcessHandle(int processId)
=> Process.GetProcessById(processId).Handle;
public IntPtr GetProcessHandle(int processId) =>
processId == _currentProcess.Id
? _currentProcess.Handle
: Process.GetProcessById(processId).Handle;

/// <inheritdoc/>
public PlatformArchitecture GetCurrentProcessArchitecture()
=> _currentProcessArchitecture ??= GetProcessArchitecture(Process.GetCurrentProcess().Id);

{
_currentProcessArchitecture ??= GetProcessArchitecture(_currentProcess.Id);
return _currentProcessArchitecture.Value;
}

public PlatformArchitecture GetProcessArchitecture(int processId)
=> IntPtr.Size == 8
? IsArm64(processId)
? PlatformArchitecture.ARM64
: PlatformArchitecture.X64
: PlatformArchitecture.X86;

private static bool IsArm64(int processId)
{
if (_currentProcess.Id == processId)
{
// If we already cached the current process architecture, no need to figure it out again.
if (_currentProcessArchitecture is not null)
{
return _currentProcessArchitecture.Value;
}

// When this is current process, we can just check if IntPointer size to get if we are 64-bit or 32-bit.
// When it is 32-bit we can just return, if it is 64-bit we need to clarify if x64 or arm64.
if (IntPtr.Size == 4)
{
return PlatformArchitecture.X86;
}
}

// If the current process is 64-bit, or this is any remote process, we need to query it via native api.
var process = processId == _currentProcess.Id ? _currentProcess : Process.GetProcessById(processId);
try
{
var process = Process.GetProcessById(processId);
if (!NativeMethods.IsWow64Process2(process.Handle, out ushort processMachine, out ushort nativeMachine))
{
throw new Win32Exception();
}

// If processMachine is IMAGE_FILE_MACHINE_UNKNOWN mean that we're not running using WOW64 x86 emulation.
if (processMachine != NativeMethods.IMAGE_FILE_MACHINE_UNKNOWN)
{
// The process is running using WOW64, which suggests it is 32-bit (or any of the other machines, that we cannot
// handle, so we just assume x86).
return PlatformArchitecture.X86;
}

// If processMachine is IMAGE_FILE_MACHINE_UNKNOWN mean that we're not running using WOW64 emulation.
// If nativeMachine is IMAGE_FILE_MACHINE_ARM64 mean that we're running on ARM64 architecture device.
if (processMachine == NativeMethods.IMAGE_FILE_MACHINE_UNKNOWN && nativeMachine == NativeMethods.IMAGE_FILE_MACHINE_ARM64)
{
// To distinguish between ARM64 and x64 emulated on ARM64 we check the PE header of the current running executable.
return IsArm64Executable(process.MainModule.FileName);
if (IsArm64Executable(process.MainModule.FileName))
{
return PlatformArchitecture.ARM64;
}
else
{
return PlatformArchitecture.X64;
}
}
else
{
return PlatformArchitecture.X64;
}
}
catch
Expand All @@ -62,9 +94,34 @@ private static bool IsArm64(int processId)
// we loaded runner version of Microsoft.TestPlatform.PlatformAbstractions but newer version Microsoft.TestPlatform.ObjectModel(the one close
// to the test container) and the old PlatformAbstractions doesn't contain the methods expected by the new ObjectModel throwing
// a MissedMethodException.
}

return false;
if (!Environment.Is64BitOperatingSystem)
{
// When we know this is not 64-bit operating system, then all processes are running as 32-bit, both
// the current process and other processes.
return PlatformArchitecture.X86;
}

try
{
var isWow64Process = NativeMethods.IsWow64Process(process.Handle, out var isWow64);
if (!isWow64Process)
{
// Do nothing we cannot log errors here.
}

// The process is running using WOW64, which suggests it is 32-bit (or any of the other machines, that we cannot
// handle, so we just assume x86). If it is not wow, we assume x64, because we failed the call to more advanced api
// that can tell us if this is arm64, so we are probably on older version of OS which is x64.
// We could call PlatformArchitecture.Architecture, but that uses the same api that we just failed to invoke.
return isWow64 ? PlatformArchitecture.X86 : PlatformArchitecture.X64;
}
catch
{
// We are on 64-bit system, let's assume x64 when we fail to determine the value.
return PlatformArchitecture.X64;
}
}
}

private static bool IsArm64Executable(string path)
Expand Down

0 comments on commit 87cfa7a

Please sign in to comment.