/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // Note: Perform short (bvt) and long running stress of OutOfMemory // behavior of the system. // // // Tests to support: // // - Overcommit of heap memory within a SIP // // Create lots of managed array objects until the OS can no // longer satisfy the request and terminates the SIP. // // // - Overcommit of stack memory within a SIP // // Implement a recursive function that results in stack overflow // and termination of the SIP. // // - Overcommit of heap memory within the kernel // // Since we can't directly allocate kernel heap memory, // we do this by creating lots of kernel objects that occupy // the heap (such as new threads) // // - Overcommit kernel heap memory from within the kernel in multiple // threads to find races in any resource reservation strategies that // may be timing dependent. // using System; using System.Threading; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.UnitTest; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Applications; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] namespace Microsoft.Singularity.Applications { [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] public readonly TRef Stdin; [OutputEndpoint("data")] public readonly TRef Stdout; [Endpoint] public readonly TRef nsRef; [BoolParameter( "sip_so", Default=false, HelpMessage="Create Stack Overflow in SIP")] internal bool sipSo; [BoolParameter( "sip_oom", Default=false, HelpMessage="Create Out Of Memory in SIP")] internal bool sipOom; [BoolParameter( "kernel_oom", Default=false, HelpMessage="Create Kernel Out Of Memory")] internal bool kernelOom; [BoolParameter( "kernel_oom_stress", Default=false, HelpMessage="Create Kernel Out Of Memory Stress")] internal bool kernelOomStress; // // The noProcess parameter runs the test without creating a sub-process. // // Since success for this test is running out of memory and having the SIP // die, there is no way to return a success status to a top level test script. // The script thinks that the SIP failure due to OOM is a failure of the test. // // So by default, the tests are run in a sub-process which we can then monitor // and return a proper result to the top level test script. // // This parameter allows the same executable to be used for both parts // of the test. // [BoolParameter( "noprocess", Default=false, HelpMessage="Run directly without a sub-process")] internal bool noProcess; reflective internal Parameters(); internal int AppMain() { return MemStress.AppMain(this); } } public class MemStress { internal static int AppMain(Parameters! config) { #if PAGING if (true) { Console.WriteLine("MemStress does not work on PAGING builds"); return -1; } #endif DumpMemInfo(); SipMemoryStresser sipms = new SipMemoryStresser(); if (!config.noProcess) { if (config.sipSo) { sipms.RunTestAsProcess(config, "-sip_so"); } else if (config.sipOom) { sipms.RunTestAsProcess(config, "-sip_oom"); } else if (config.kernelOom) { sipms.RunTestAsProcess(config, "-kernel_oom"); } else if (config.kernelOomStress) { sipms.RunTestAsProcess(config, "-kernel_oom_stress"); } else { Console.WriteLine("Usage: MemStress [-sip_oom] [-sip_so] [-kernel_oom] [-kernel_oom_stress]"); Console.WriteLine("Must pick one option"); } } else { if (config.sipSo) { sipms.RunSOTest(); } else if (config.sipOom) { sipms.RunOOMTest(); } else if (config.kernelOom) { sipms.KernelOOMTest(); } else if (config.kernelOomStress) { sipms.KernelOOMStress(); } else { Console.WriteLine("Usage: MemStress [-sip_oom] [-sip_so] [-kernel_oom] [-kernel_oom_stress]"); Console.WriteLine("Must pick one option"); } } return 0; } public static void DumpMemInfo() { int result; ulong totalMemoryFree = 0; ulong totalMemoryInUse = 0; ulong kernelHeapInUse = 0; ulong kernelStackInUse = 0; ulong totalSIPHeapInUse = 0; ulong totalSIPStackInUse = 0; ulong kernelStackReservation = 0; ulong kernelHeapReservation = 0; result = MemoryInfoService.MemoryUsageInfo( out totalMemoryFree, out totalMemoryInUse, out kernelHeapInUse, out kernelStackInUse, out totalSIPHeapInUse, out totalSIPStackInUse, out kernelStackReservation, out kernelHeapReservation ); // TODO: Use standard ErrorCode's if (result != 0) { Console.WriteLine("Error {0} retrieving MemoryUsageInfo"); } else { Console.WriteLine("TotalMemoryFree 0x{0:x8}, TotalMemoryInUse {1:x8}", totalMemoryFree, totalMemoryInUse); Console.WriteLine("KernelHeapInUse {0:x8}, KernelStackInUse {1:x8}", kernelHeapInUse, kernelStackInUse); Console.WriteLine("TotalSIPHeapInUse {0:x8}, TotalSIPStackInUse {1:x8}", totalSIPHeapInUse, totalSIPStackInUse); Console.WriteLine("KernelStackReservation {0:x8}, KernelHeapReservation {1:x8}", kernelStackReservation, kernelHeapReservation); Console.WriteLine(""); } } } public class SipMemoryStresser { // // Run out of memory test, eventually terminates the SIP due // to fail fast. // internal void RunTestAsProcess(Parameters! config, string test) { Process p = null; // // Create a subprocess run of "memstress -testparameter" // string[] args = new string[3]; args[0] = "memstress"; args[1] = test; args[2] = "-noprocess"; Console.WriteLine("Creating subprocess memstress {0} -noprocess", test); try { p = ProcessLauncher.CreateSubProcess(config, args); } catch (Exception e) { Console.WriteLine("Exception from CreateSubProcess: " + e.Message); return; } if (p == null) { Console.WriteLine("Error creating process"); return; } Console.WriteLine("Process returned with ExitCode={0}", p.ExitCode); } public void RunOOMTest() { object[] TopLevel; object[] tmp; TopLevel = new Object[1]; // // Currrently the test handles the OOM exception showing // that this works. Additional testing should create multiple // threads and have OOM failures at random places in the runtime // without try/except handlers so we can test the case when // the exception bubbles up to the top of the stack. // try { for (int count = 65535;; count *= 2) { tmp = new object[count]; tmp[0] = TopLevel; TopLevel = tmp; for (int i = 1; i < count; i++) { TopLevel[i] = new object[count]; } MemStress.DumpMemInfo(); } } catch (OutOfMemoryException e) { e=e; //Thread.CurrentProcess.Stop(1); return; } } // Run stack overflow test, terminates the SIP public void RunSOTest() { // Test the fail fast path directly //Microsoft.Singularity.V1.Services.StackService.StackOverflow(); RunSOTest(); } // // Invoke a kernel heap out of memory condition by creating // lots of kernel objects // public void KernelOOMTest() { IdleThread[] threads; int threadCount = 1000; while (true) { System.Console.WriteLine("Creating {0} threads", threadCount); threads = new IdleThread[threadCount]; if (threads == null) { System.Console.WriteLine("Error allocating Thread[]"); return; } for (int i = 0; i < threadCount; i++) { threads[i] = new IdleThread(i); if (threads[i] == null) { System.Console.WriteLine("Error creating thread"); return; } } // Start them running for (int i = 0; i < threadCount; i++) { ((!)threads[i]).RunThread(); } } } // // Use multiple threads to stress the system and induce // kernel heap OOM to test the ability for the system // to remain running. // public void KernelOOMStress() { Console.WriteLine("Not implemented yet"); return; } } // Generic class that creates the thread public class ThreadRunner { private Thread thread; public ThreadRunner() { } public Thread Thread { get { return thread; } } // Creates thread, returns with it running public Thread RunThread() { thread = new Thread(new ThreadStart(ThreadMain)); thread.Start(); return thread; } public virtual void ThreadMain() { // This exits the thread return; } } // // Implementation classes represent different thread creation // parameters, and actions // // // This creates an idle thread that just waits. // // The goal is to create lots of kernel objects and use up // memory, not jam the system with many threads until // it stalls. // public class IdleThread : ThreadRunner { private long argValue; public IdleThread(long arg) : base() { argValue = arg; } public override void ThreadMain() { // Wait for ever while (true) { Thread.Sleep(60*60*1000); } // This exits the thread return; } } }