singrdk/base/Applications/Tests/MemStress/MemStress.cs

385 lines
12 KiB
C#
Raw Permalink Normal View History

2008-11-17 18:29:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// 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<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[Endpoint]
public readonly TRef<DirectoryServiceContract.Imp:Start> 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;
}
}
}