Igor Ignatyev 08855df46a 8199643: [TESTBUG] Open source common VM testbase code
Reviewed-by: vlivanov, erikj, mseledtsov, gthornbr
2018-04-30 18:10:24 -07:00

399 lines
19 KiB
Java

/*
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package nsk.share.gc.gp;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import nsk.share.gc.gp.array.*;
import nsk.share.gc.gp.string.*;
import nsk.share.gc.gp.list.*;
import nsk.share.gc.gp.tree.*;
import nsk.share.gc.gp.misc.*;
import nsk.share.gc.gp.classload.*;
import nsk.share.gc.Memory;
import nsk.share.TestBug;
import nsk.share.test.*;
/**
* Utility methods for garbage producers.
*/
public final class GarbageUtils {
private static final int ALLOCATION_LIMIT = 50000000; //50 Mb
private static GarbageProducers garbageProducers;
private static List<GarbageProducer> primitiveArrayProducers;
private static List<GarbageProducer> arrayProducers;
private static final GarbageProducer byteArrayProducer = new ByteArrayProducer();
public static enum OOM_TYPE {
ANY (),
HEAP("Java heap space"),
METASPACE("Metaspace", "Compressed class space");
private final String[] expectedStrings;
OOM_TYPE(String... expectedStrings) {
this.expectedStrings = expectedStrings;
}
/**
* Returns true if the given error message matches
* one of expected strings.
*/
public boolean accept(String errorMessage) {
if (expectedStrings == null || expectedStrings.length == 0 || errorMessage == null) {
return true;
}
for (String s: expectedStrings) {
if (errorMessage.indexOf(s) != -1) {
return true;
}
}
return false;
}
};
// Force loading of OOM_TYPE and calling of enum contrusctors when loading GarbageUtils class.
public static final Object[] thisIsGarbageArray_theOnlyPurposeForCreatingItAndDeclaringItPublicIsToInitializeIntancesOfOOMEnumberation = new Object[] { OOM_TYPE.ANY, OOM_TYPE.HEAP, OOM_TYPE.METASPACE };
// Force early loading of classes that might otherwise unexpectedly fail
// class loading during testing due to high memory pressure.
public static final StringWriter preloadStringWriter = new StringWriter(1);
public static final PrintWriter preloadPrintWriter = new PrintWriter(preloadStringWriter);
private GarbageUtils() {
}
/**
* Eat memory using execution controller that waits for 2 minutes.
* @return number of OOME occured
*/
public static int eatMemory() {
return eatMemory(2 * 60 * 1000);
}
/**
* Eat memory using execution controller that waits for timeout.
* @return number of OOME occured
*/
public static int eatMemory(final long timeout) {
return eatMemory(new ExecutionController() {
final long initialTime = System.currentTimeMillis();
@Override
public void start(long stdIterations) {}
@Override
public boolean iteration() {return false;}
@Override
public boolean continueExecution() {
return System.currentTimeMillis() - initialTime < timeout;
}
@Override
public long getIteration() {return 0;}
@Override
public void finish() {}
});
}
/**
* Eat memory using given execution controller and garbage producer.
*
* @param stresser execution controller
* @param gp garbage producer
* @return number of OOME occured
*/
public static int eatMemory(ExecutionController stresser) {
return eatMemory(stresser, byteArrayProducer, 50, 100, 2, OOM_TYPE.ANY);
}
/**
* Eat memory using given execution controller and garbage producer.
*
* @param stresser execution controller
* @param gp garbage producer
* @return number of OOME occured
*/
public static int eatMemory(ExecutionController stresser, GarbageProducer gp) {
return eatMemory(stresser, gp, 50, 100, 2, OOM_TYPE.ANY);
}
/**
* Eat memory using given garbage producer and given factor.
*
* @param gp garbage producer
* @param factor factor to divide the array size by
* @return number of OOME occured
*/
public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long factor) {
return eatMemory(stresser, gp, 50, 100, factor, OOM_TYPE.ANY);
}
/**
* Eat memory using default(byte[]) garbage producer.
*
* Note that this method can throw Failure if any exception
* is thrown while eating memory. To avoid OOM while allocating
* exception we preallocate it before the lunch starts. It means
* that exception stack trace does not correspond to the place
* where exception is thrown, but points at start of the method.
*
* @param stresser stresser
* @param initialFactor determines which portion of initial memory initial chunk will be
* @param minMemoryChunk determines when to stop
* @param factor factor to divide the array size by
* @return number of OOME occured
*/
public static int eatMemory(ExecutionController stresser,long initialFactor, long minMemoryChunk, long factor) {
return eatMemory(stresser, byteArrayProducer, initialFactor, minMemoryChunk, factor, OOM_TYPE.ANY);
}
/**
* Eat memory using given garbage producer.
*
* Note that this method can throw Failure if any exception
* is thrown while eating memory. To avoid OOM while allocating
* exception we preallocate it before the lunch starts. It means
* that exception stack trace does not correspond to the place
* where exception is thrown, but points at start of the method.
*
* @param stresser stresser to use
* @param gp garbage producer
* @param initialFactor determines which portion of initial memory initial chunk will be
* @param minMemoryChunk determines when to stop
* @param factor factor to divide the array size by. A value of 0 means that method returns after first OOME
* @param type of OutOfMemory Exception: Java heap space or Metadata space
* @return number of OOME occured
*/
public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor) {
return eatMemory(stresser, gp, initialFactor, minMemoryChunk, factor, OOM_TYPE.ANY);
}
/**
* Eat memory using given garbage producer.
*
* Note that this method can throw Failure if any exception
* is thrown while eating memory. To avoid OOM while allocating
* exception we preallocate it before the lunch starts. It means
* that exception stack trace does not correspond to the place
* where exception is thrown, but points at start of the method.
*
* @param stresser stresser to use
* @param gp garbage producer
* @param initialFactor determines which portion of initial memory initial chunk will be
* @param minMemoryChunk determines when to stop
* @param factor factor to divide the array size by. A value of 0 means that method returns after first OOME
* @param type of OutOfMemory Exception: Java heap space or Metadata space
* @return number of OOME occured
*/
public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor, OOM_TYPE type) {
int numberOfOOMEs = 0;
try {
StringWriter sw = new StringWriter(10000);
PrintWriter pw = new PrintWriter(sw);
byte[] someMemory = new byte[200000]; //200 Kb
try {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long maxMemoryChunk = maxMemory / initialFactor;
long chunk = maxMemoryChunk;
chunk = chunk > ALLOCATION_LIMIT ? ALLOCATION_LIMIT : chunk;
int allocations = 0;
List<Object> storage = new ArrayList<Object>();
while (chunk > minMemoryChunk && stresser.continueExecution()) {
try {
storage.add(gp.create(chunk));
if (Thread.currentThread().isInterrupted()) {
return numberOfOOMEs;
}
// if we are able to eat chunk*factor let
// try to increase size of chunk
if (chunk * factor < maxMemoryChunk
&& factor != 0 && allocations++ == factor + 1) {
chunk = chunk * factor;
allocations = 0;
}
} catch (OutOfMemoryError e) {
someMemory = null;
if (type != OOM_TYPE.ANY) {
e.printStackTrace(pw);
pw.close();
if (type.accept(sw.toString())) {
numberOfOOMEs++;
} else {
// Trying to catch situation when Java generates OOM different type that test trying to catch
throw new TestBug("Test throw OOM of unexpected type." + sw.toString());
}
} else {
numberOfOOMEs++;
}
allocations = 0;
if (factor == 0) {
return numberOfOOMEs;
} else {
chunk = chunk / factor;
}
}
}
} catch (OutOfMemoryError e) {
someMemory = null;
if (type != OOM_TYPE.ANY) {
e.printStackTrace(pw);
pw.close();
if (type.accept(sw.toString())) {
numberOfOOMEs++;
} else {
// Trying to catch situation when Java generates OOM different type that test trying to catch
throw new TestBug("Test throw OOM of unexpected type." + sw.toString());
}
} else {
numberOfOOMEs++;
}
// all memory is eaten now even before we start, just return
}
} catch (OutOfMemoryError e) {
numberOfOOMEs++;
}
return numberOfOOMEs;
}
/**
* Get all primitive array producers.
*/
public static List<GarbageProducer> getPrimitiveArrayProducers() {
return getGarbageProducers().getPrimitiveArrayProducers();
}
/**
* Get all array producers.
*/
public static List<GarbageProducer> getArrayProducers() {
return getGarbageProducers().getArrayProducers();
}
/**
* Determine size of each object in array which will occupy given
* memory with distribution determined by given memory strategy.
*/
public static long getArraySize(long memory, MemoryStrategy memoryStrategy) {
return memoryStrategy.getSize(memory - Memory.getArrayExtraSize(), Memory.getReferenceSize());
}
/**
* Determine object count in array which will occupy given
* memory with distribution determined by given memory strategy.
*/
public static int getArrayCount(long memory, MemoryStrategy memoryStrategy) {
return memoryStrategy.getCount(memory - Memory.getArrayExtraSize(), Memory.getReferenceSize());
}
/**
* Get garbage producer by identifier.
*
* @param id identifier
* @return garbage producer for this identifier
*/
public static GarbageProducer getGarbageProducer(String id) {
if (id == null || id.equals("byteArr"))
return new ByteArrayProducer();
else if (id.equals("booleanArr"))
return new BooleanArrayProducer();
else if (id.equals("shortArr"))
return new ShortArrayProducer();
else if (id.equals("charArr"))
return new CharArrayProducer();
else if (id.equals("intArr"))
return new IntArrayProducer();
else if (id.equals("longArr"))
return new LongArrayProducer();
else if (id.equals("floatArr"))
return new FloatArrayProducer();
else if (id.equals("doubleArr"))
return new DoubleArrayProducer();
else if (id.equals("objectArr"))
return new ObjectArrayProducer();
else if (id.equals("randomString"))
return new RandomStringProducer();
else if (id.equals("simpleString"))
return new SimpleStringProducer();
else if (id.startsWith("interned("))
return new InternedStringProducer(getGarbageProducer(getInBrackets(id)));
else if (id.startsWith("linearList("))
return new LinearListProducer(MemoryStrategy.fromString(getInBrackets(id)));
else if (id.startsWith("circularList("))
return new CircularListProducer(MemoryStrategy.fromString(getInBrackets(id)));
else if (id.startsWith("nonbranchyTree("))
return new NonbranchyTreeProducer(MemoryStrategy.fromString(getInBrackets(id)));
else if (id.equals("class"))
return new GeneratedClassProducer();
else if (id.startsWith("hashed("))
return new HashedGarbageProducer(getGarbageProducer(getInBrackets(id)));
else if (id.startsWith("random("))
return new RandomProducer(getGarbageProducerList(getInBrackets(id)));
else if (id.startsWith("twofields("))
return new TwoFieldsObjectProducer(getGarbageProducer(getInBrackets(id)));
else if (id.startsWith("arrayof("))
return new ArrayOfProducer(getGarbageProducer(getInBrackets(id)));
else if (id.startsWith("trace("))
return new TraceProducer(getGarbageProducer(getInBrackets(id)));
else
throw new TestBug("Invalid garbage producer identifier: " + id);
}
private static String getInBrackets(String s) {
int n1 = s.indexOf('(');
if (n1 == -1)
throw new TestBug("Opening bracket not found: " + s);
int n2 = s.lastIndexOf(')');
if (n2 == -1)
throw new TestBug("Closing bracket not found: " + s);
return s.substring(n1 + 1, n2);
}
private static List<GarbageProducer> getGarbageProducerList(String s) {
if (s.equals("primitiveArrays"))
return getPrimitiveArrayProducers();
else if (s.equals("arrays"))
return getArrayProducers();
else {
String[] ids = s.split(",");
List<GarbageProducer> garbageProducers = new ArrayList<GarbageProducer>(ids.length);
for (int i = 0; i < ids.length; ++i)
garbageProducers.add(getGarbageProducer(ids[i]));
return garbageProducers;
//throw new TestBug("Invalid id for list of garbage producers: " + id);
}
}
public static GarbageProducers getGarbageProducers() {
if (garbageProducers == null)
garbageProducers = new GarbageProducers();
return garbageProducers;
}
}