Unit Testing for x86 LargeAddressAware compatibility

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 spawns QTAgent32.exe
  • vstest.console.exe spawns vstest.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_MACHINESYSTEMCurrentControlSetControlSession ManagerMemory 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 (ie use editbin.exe to manually "patch" them). But this has the following disadvantages:

  • I need to repeat the patching once any future service pack is installed for Visual Studio
  • I can no longer test in parallel: "regular" x86 and "extended" x86 with LAA
  • 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

    链接地址: http://www.djcxy.com/p/20034.html

    上一篇: 如何使基金会5关闭

    下一篇: x86 LargeAddressAware兼容性的单元测试