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

Fix get process architecture #3726

Merged
merged 7 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can add CallingConvention = CallingConvention.Winapi and the return annotation also above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.

[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.
}
Evangelink marked this conversation as resolved.
Show resolved Hide resolved

// 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