For a win32 executable (x86) we can set the LargeAddressAware flag so it can access a virtual address space of 4 GB (instead of just 2 GB) when running on x64 Windows.
This looks very appealing. However, there are risks involved.
For example see: Drawbacks of using /LARGEADDRESSAWARE for 32 bit Windows executables?
So let's go ahead and configure the system that is executing some unit tests with the system-wide registry switch AllocationPreference
set to MEM_TOP_DOWN
.
That should do, shouldn't it?
It doesn't!
The problem is that the x86 "test runner" (execution engine) of Visual Studio is not LAA enabled itself.
This parent process will only see the "lower" 2 GB of VAS, and so will our modules to be tested.
Examples from VS2013.1
mstest.exe
spawnsQTAgent32.exe
vstest.console.exe
spawnsvstest.executionengine.x86.exe
All of them are not LAA enabled!
So what is the recommended way to use a x86 test runner that is LAA enabled?
here's a little code snippet (VS unit test, csharp) to check for LAA execution environment.
unless it succeeds your test environment is not suitable to have your set of unit tests (also) cover the compatibility with LAA:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestCheckEnv32LAA
{
[TestClass]
public class CheckEnv32LAA
{
#region [Native DLL import]
[Flags()]
public enum AllocationType : uint
{
COMMIT = 0x1000,
RESERVE = 0x2000,
RESET = 0x80000,
LARGE_PAGES = 0x20000000,
PHYSICAL = 0x400000,
TOP_DOWN = 0x100000,
WRITE_WATCH = 0x200000
}
[Flags()]
public enum MemoryProtection : uint
{
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
GUARD_Modifierflag = 0x100,
NOCACHE_Modifierflag = 0x200,
WRITECOMBINE_Modifierflag = 0x400
}
[StructLayout(LayoutKind.Sequential)]
struct MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
[DllImport("kernel32.dll")]
extern static void GlobalMemoryStatusEx(ref MEMORYSTATUSEX status);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern UIntPtr VirtualAlloc(UIntPtr lpAddress, UIntPtr dwSize,
AllocationType flAllocationType, MemoryProtection flProtect);
#endregion
public CheckEnv32LAA()
{
}
[TestMethod]
public void CheckEnvironment32LAA()
{
// check for a suitable environment to test modules for compatibility with LargeAddressAware (LAA):
// 1) OS must be x64
// 2) test runner must be x86
// 3) test runner must be LAA enabled itself
// 4) memory allocation (with manual TopDown flag) must happen beyond the 2 GB boundary
// 5) memory allocation (with default settings) must happen beyond the 2 GB boundary
//
// RE 3) this requirement is true for "regular" unit tests (to test DLL modules). it does not apply
// for any tests spawning the application (EXE) to be tested as a separate process.
//
// RE 5) a failure indicates the following registry switch has not been set:
// [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]
// "AllocationPreference"=dword:00100000
//
// see:
// https://stackoverflow.com/questions/2288728/
String sParentProcName = Process.GetCurrentProcess().MainModule.FileName;
//CHECK_1
Assert.IsTrue(Environment.Is64BitOperatingSystem, "Test is not executing on x64 OS");
//CHECK_2
Assert.IsFalse(Environment.Is64BitProcess, "Test runner is not x86: " + sParentProcName);
//CHECK_3
MEMORYSTATUSEX tmpStatus = new MEMORYSTATUSEX();
tmpStatus.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
tmpStatus.ullTotalPhys = 0;
GlobalMemoryStatusEx(ref tmpStatus);
ulong uVM = tmpStatus.ullTotalVirtual;
Assert.IsTrue(uVM > 0x80000000, "Test runner is not LAA enabled (max: " + uVM / (1024 * 1024) + "): " + sParentProcName);
Assert.IsTrue(uVM <= 0x100000000, "Test runner is not x86 (max: " + uVM / (1024 * 1024) + "): " + sParentProcName);
//CHECK_4
UIntPtr pMem = UIntPtr.Zero;
ulong uAddress = 0;
pMem = VirtualAlloc(UIntPtr.Zero, (UIntPtr)1024, AllocationType.RESERVE | AllocationType.TOP_DOWN, MemoryProtection.READWRITE);
uAddress = (ulong)pMem;
Assert.IsTrue(uAddress > 0x80000000, "Test runner is not LAA enabled (highest: " + uAddress / (1024 * 1024) + "): " + sParentProcName);
//CHECK_5
pMem = VirtualAlloc(UIntPtr.Zero, (UIntPtr)1024, AllocationType.RESERVE, MemoryProtection.READWRITE);
uAddress = (ulong)pMem;
Assert.IsTrue(uAddress > 0x80000000, "System-wide MEM_TOP_DOWN is not set (allocated at: " + uAddress / (1024 * 1024) + ")");
}
}
}
So far I have only come across suggestions to mess with the Microsoft binaries listed in the question (i.e. use
editbin.exe
to manually "patch" them). But this has the following disadvantages:It seems a proper long-term solution would have to be implemented by microsoft?:
http://visualstudio.uservoice.com/forums/196039-microsoft-test-tools/suggestions/5781437