diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NativeMethodsHelper.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NativeMethodsHelper.cs deleted file mode 100644 index dc0d78fd81..0000000000 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NativeMethodsHelper.cs +++ /dev/null @@ -1,41 +0,0 @@ -// 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.Runtime.InteropServices; - -using Microsoft.VisualStudio.TestPlatform.ObjectModel; - -namespace Microsoft.TestPlatform.Extensions.BlameDataCollector; - -public class NativeMethodsHelper : INativeMethodsHelper -{ - /// - /// Returns if a process is 64 bit process - /// - /// Process Handle - /// Bool for Is64Bit - public bool Is64Bit(IntPtr processHandle) - { - // WOW64 is the x86 emulator that allows 32 bit Windows - based applications to run seamlessly on 64 bit Windows. - - // If the function succeeds, the return value is a nonzero value. - var isWow64Process = IsWow64Process(processHandle, out var isWow64); - if (!isWow64Process) - { - EqtTrace.Verbose("NativeMethodsHelper: The call to IsWow64Process failed."); - } - - var is64Bit = !isWow64; - EqtTrace.Verbose($"NativeMethodsHelper: is Wow64Process: {isWow64Process} is 64bit: {is64Bit}."); - - return is64Bit; - } - - // 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); -} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs index f5767f1af8..b10f64cc79 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpDumper.cs @@ -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; diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/PublicAPI/PublicAPI.Shipped.txt index c9f6f04366..a61100a66f 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/PublicAPI/PublicAPI.Shipped.txt @@ -60,9 +60,6 @@ Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.DetachF Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.GetDumpFiles(bool warnOnNoDumpFiles, bool processCrashed) -> System.Collections.Generic.IEnumerable! Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.StartHangBasedProcessDump(int processId, string! testResultsDirectory, bool isFullDump, string! targetFramework, System.Action? logWarning = null) -> void Microsoft.TestPlatform.Extensions.BlameDataCollector.IProcessDumpUtility.StartTriggerBasedProcessDump(int processId, string! testResultsDirectory, bool isFullDump, string! targetFramework, bool collectAlways, System.Action! 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! procDumpExceptionsList, bool isFullDump) -> string! @@ -74,7 +71,7 @@ Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.Dump(int pro Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.GetDumpFiles(bool processCrashed) -> System.Collections.Generic.IEnumerable! Microsoft.TestPlatform.Extensions.BlameDataCollector.ProcDumpDumper.OutputReceivedCallback.get -> System.Action! 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 diff --git a/src/Microsoft.TestPlatform.PlatformAbstractions/common/NativeMethods.cs b/src/Microsoft.TestPlatform.PlatformAbstractions/common/NativeMethods.cs index 6ffd54aa10..680aae6d57 100644 --- a/src/Microsoft.TestPlatform.PlatformAbstractions/common/NativeMethods.cs +++ b/src/Microsoft.TestPlatform.PlatformAbstractions/common/NativeMethods.cs @@ -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 diff --git a/src/Microsoft.TestPlatform.PlatformAbstractions/common/System/ProcessHelper.cs b/src/Microsoft.TestPlatform.PlatformAbstractions/common/System/ProcessHelper.cs index 032817b66d..290b971610 100644 --- a/src/Microsoft.TestPlatform.PlatformAbstractions/common/System/ProcessHelper.cs +++ b/src/Microsoft.TestPlatform.PlatformAbstractions/common/System/ProcessHelper.cs @@ -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(); /// public object LaunchProcess(string processPath, string arguments, string workingDirectory, IDictionary? envVariables, Action? errorCallback, Action? exitCallBack, Action? outputCallBack) @@ -137,7 +138,7 @@ void InitializeAndStart() /// public string GetCurrentProcessFileName() { - return Process.GetCurrentProcess().MainModule.FileName; + return _currentProcess.MainModule.FileName; } /// @@ -149,12 +150,17 @@ public string GetTestEngineDirectory() /// public int GetCurrentProcessId() { - return Process.GetCurrentProcess().Id; + return _currentProcess.Id; } /// public string GetProcessName(int processId) { + if (processId == _currentProcess.Id) + { + return _currentProcess.ProcessName; + } + return Process.GetProcessById(processId).ProcessName; } @@ -182,7 +188,7 @@ public void SetExitCallback(int processId, Action? 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); } diff --git a/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/PlatformEnvironment.cs b/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/PlatformEnvironment.cs index 96b6df5ffc..202263d19f 100644 --- a/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/PlatformEnvironment.cs +++ b/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/PlatformEnvironment.cs @@ -14,13 +14,20 @@ namespace Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; /// public class PlatformEnvironment : IEnvironment { + private PlatformArchitecture? _architecture; + /// 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() { diff --git a/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/ProcessHelper.cs b/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/ProcessHelper.cs index a69b5e105a..e0553562cb 100644 --- a/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/ProcessHelper.cs +++ b/src/Microsoft.TestPlatform.PlatformAbstractions/net451/System/ProcessHelper.cs @@ -21,37 +21,69 @@ public string GetCurrentProcessLocation() => Path.GetDirectoryName(GetCurrentProcessFileName()); /// - public IntPtr GetProcessHandle(int processId) - => Process.GetProcessById(processId).Handle; + public IntPtr GetProcessHandle(int processId) => + processId == _currentProcess.Id + ? _currentProcess.Handle + : Process.GetProcessById(processId).Handle; /// 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 @@ -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)