diff options
author | Andreas Gampe <agampe@google.com> | 2015-02-13 23:38:25 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-02-13 23:38:25 +0000 |
commit | e5f5953e744060fde3b4489cea4d934d529e3e32 (patch) | |
tree | e7791dd6820d3f41c073c0c2fc7565e857d4c3b1 | |
parent | 6bf6ce19df0b165122d5e9a593943e3bfb97ad4d (diff) | |
parent | f2fdc7368c5fd5d9cbb4bd1d962b887e87f0654c (diff) | |
download | art-e5f5953e744060fde3b4489cea4d934d529e3e32.zip art-e5f5953e744060fde3b4489cea4d934d529e3e32.tar.gz art-e5f5953e744060fde3b4489cea4d934d529e3e32.tar.bz2 |
Merge "ART: Rewrite ThreadStress for easier extensibility"
-rw-r--r-- | test/004-ThreadStress/src/Main.java | 461 | ||||
-rwxr-xr-x | test/etc/run-test-jar | 1 |
2 files changed, 344 insertions, 118 deletions
diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java index 0c1c97d..6e7d5b6 100644 --- a/test/004-ThreadStress/src/Main.java +++ b/test/004-ThreadStress/src/Main.java @@ -19,41 +19,348 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; // Run on host with: // javac ThreadTest.java && java ThreadStress && rm *.class +// Through run-test: +// test/run-test {run-test-args} 004-ThreadStress [Main {ThreadStress-args}] +// (It is important to pass Main if you want to give parameters...) +// +// ThreadStress command line parameters: +// -n X ............ number of threads +// -o X ............ number of overall operations +// -t X ............ number of operations per thread +// --dumpmap ....... print the frequency map +// -oom:X .......... frequency of OOM (double) +// -alloc:X ........ frequency of Alloc +// -stacktrace:X ... frequency of StackTrace +// -exit:X ......... frequency of Exit +// -sleep:X ........ frequency of Sleep +// -wait:X ......... frequency of Wait +// -timedwait:X .... frequency of TimedWait + public class Main implements Runnable { public static final boolean DEBUG = false; - enum Operation { - OOM(1), - SIGQUIT(19), - ALLOC(60), - STACKTRACE(20), - EXIT(50), + private static abstract class Operation { + /** + * Perform the action represented by this operation. Returns true if the thread should + * continue. + */ + public abstract boolean perform(); + } + + private final static class OOM extends Operation { + @Override + public boolean perform() { + try { + List<byte[]> l = new ArrayList<byte[]>(); + while (true) { + l.add(new byte[1024]); + } + } catch (OutOfMemoryError e) { + } + return true; + } + } + + private final static class SigQuit extends Operation { + private final static int sigquit; + private final static Method kill; + private final static int pid; + + static { + int pidTemp = -1; + int sigquitTemp = -1; + Method killTemp = null; + + try { + Class<?> osClass = Class.forName("android.system.Os"); + Method getpid = osClass.getDeclaredMethod("getpid"); + pidTemp = (Integer)getpid.invoke(null); + + Class<?> osConstants = Class.forName("android.system.OsConstants"); + Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); + sigquitTemp = (Integer)sigquitField.get(null); + + killTemp = osClass.getDeclaredMethod("kill", int.class, int.class); + } catch (Exception e) { + if (!e.getClass().getName().equals("ErrnoException")) { + e.printStackTrace(System.out); + } + } + + pid = pidTemp; + sigquit = sigquitTemp; + kill = killTemp; + } + + @Override + public boolean perform() { + try { + kill.invoke(null, pid, sigquit); + } catch (Exception e) { + if (!e.getClass().getName().equals("ErrnoException")) { + e.printStackTrace(System.out); + } + } + return true; + } + } + + private final static class Alloc extends Operation { + @Override + public boolean perform() { + try { + List<byte[]> l = new ArrayList<byte[]>(); + for (int i = 0; i < 1024; i++) { + l.add(new byte[1024]); + } + } catch (OutOfMemoryError e) { + } + return true; + } + } + + private final static class StackTrace extends Operation { + @Override + public boolean perform() { + Thread.currentThread().getStackTrace(); + return true; + } + } + + private final static class Exit extends Operation { + @Override + public boolean perform() { + return false; + } + } + + private final static class Sleep extends Operation { + @Override + public boolean perform() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + return true; + } + } - SLEEP(25), - TIMED_WAIT(10), - WAIT(15); + private final static class TimedWait extends Operation { + private final Object lock; - private final int frequency; - Operation(int frequency) { - this.frequency = frequency; + public TimedWait(Object lock) { + this.lock = lock; + } + + @Override + public boolean perform() { + synchronized (lock) { + try { + lock.wait(100, 0); + } catch (InterruptedException ignored) { + } + } + return true; } } + private final static class Wait extends Operation { + private final Object lock; + + public Wait(Object lock) { + this.lock = lock; + } + + @Override + public boolean perform() { + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException ignored) { + } + } + return true; + } + } + + private final static class SyncAndWork extends Operation { + private final Object lock; + + public SyncAndWork(Object lock) { + this.lock = lock; + } + + @Override + public boolean perform() { + synchronized (lock) { + try { + Thread.sleep((int)(Math.random()*10)); + } catch (InterruptedException ignored) { + } + } + return true; + } + } + + private final static Map<Operation, Double> createDefaultFrequencyMap(Object lock) { + Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>(); + frequencyMap.put(new OOM(), 0.005); // 1/200 + frequencyMap.put(new SigQuit(), 0.095); // 19/200 + frequencyMap.put(new Alloc(), 0.3); // 60/200 + frequencyMap.put(new StackTrace(), 0.1); // 20/200 + frequencyMap.put(new Exit(), 0.25); // 50/200 + frequencyMap.put(new Sleep(), 0.125); // 25/200 + frequencyMap.put(new TimedWait(lock), 0.05); // 10/200 + frequencyMap.put(new Wait(lock), 0.075); // 15/200 + + return frequencyMap; + } + + private final static Map<Operation, Double> createLockFrequencyMap(Object lock) { + Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>(); + frequencyMap.put(new Sleep(), 0.2); + frequencyMap.put(new TimedWait(lock), 0.2); + frequencyMap.put(new Wait(lock), 0.2); + frequencyMap.put(new SyncAndWork(lock), 0.4); + + return frequencyMap; + } + public static void main(String[] args) throws Exception { + parseAndRun(args); + } - final int numberOfThreads = 5; - final int totalOperations = 1000; - final int operationsPerThread = totalOperations/numberOfThreads; + private static Map<Operation, Double> updateFrequencyMap(Map<Operation, Double> in, + Object lock, String arg) { + String split[] = arg.split(":"); + if (split.length != 2) { + throw new IllegalArgumentException("Can't split argument " + arg); + } + double d; + try { + d = Double.parseDouble(split[1]); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + if (d < 0) { + throw new IllegalArgumentException(arg + ": value must be >= 0."); + } + Operation op = null; + if (split[0].equals("-oom")) { + op = new OOM(); + } else if (split[0].equals("-sigquit")) { + op = new SigQuit(); + } else if (split[0].equals("-alloc")) { + op = new Alloc(); + } else if (split[0].equals("-stacktrace")) { + op = new StackTrace(); + } else if (split[0].equals("-exit")) { + op = new Exit(); + } else if (split[0].equals("-sleep")) { + op = new Sleep(); + } else if (split[0].equals("-wait")) { + op = new Wait(lock); + } else if (split[0].equals("-timedwait")) { + op = new TimedWait(lock); + } else { + throw new IllegalArgumentException("Unknown arg " + arg); + } + + if (in == null) { + in = new HashMap<Operation, Double>(); + } + in.put(op, d); - // Lock used to notify threads performing Operation.WAIT - final Object lock = new Object(); + return in; + } + + private static void normalize(Map<Operation, Double> map) { + double sum = 0; + for (Double d : map.values()) { + sum += d; + } + if (sum == 0) { + throw new RuntimeException("No elements!"); + } + if (sum != 1.0) { + // Avoid ConcurrentModificationException. + Set<Operation> tmp = new HashSet<>(map.keySet()); + for (Operation op : tmp) { + map.put(op, map.get(op) / sum); + } + } + } + + public static void parseAndRun(String[] args) throws Exception { + int numberOfThreads = -1; + int totalOperations = -1; + int operationsPerThread = -1; + Object lock = new Object(); + Map<Operation, Double> frequencyMap = null; + boolean dumpMap = false; + + if (args != null) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-n")) { + i++; + numberOfThreads = Integer.parseInt(args[i]); + } else if (args[i].equals("-o")) { + i++; + totalOperations = Integer.parseInt(args[i]); + } else if (args[i].equals("-t")) { + i++; + operationsPerThread = Integer.parseInt(args[i]); + } else if (args[i].equals("--locks-only")) { + lock = new Object(); + frequencyMap = createLockFrequencyMap(lock); + } else if (args[i].equals("--dumpmap")) { + dumpMap = true; + } else { + frequencyMap = updateFrequencyMap(frequencyMap, lock, args[i]); + } + } + } + if (totalOperations != -1 && operationsPerThread != -1) { + throw new IllegalArgumentException( + "Specified both totalOperations and operationsPerThread"); + } + + if (numberOfThreads == -1) { + numberOfThreads = 5; + } + + if (totalOperations == -1) { + totalOperations = 1000; + } + + if (operationsPerThread == -1) { + operationsPerThread = totalOperations/numberOfThreads; + } + + if (frequencyMap == null) { + frequencyMap = createDefaultFrequencyMap(lock); + } + normalize(frequencyMap); + + if (dumpMap) { + System.out.println(frequencyMap); + } + + runTest(numberOfThreads, operationsPerThread, lock, frequencyMap); + } + + public static void runTest(final int numberOfThreads, final int operationsPerThread, + final Object lock, Map<Operation, Double> frequencyMap) + throws Exception { // Each thread is going to do operationsPerThread // operations. The distribution of operations is determined by // the Operation.frequency values. We fill out an Operation[] @@ -61,20 +368,6 @@ public class Main implements Runnable { // Operation[] is shuffled so that there is more random // interactions between the threads. - // The simple-minded filling in of Operation[] based on - // Operation.frequency below won't have even have close to a - // reasonable distribution if the count of Operation - // frequencies is greater than the total number of - // operations. So here we do a quick sanity check in case - // people tweak the constants above. - int operationCount = 0; - for (Operation op : Operation.values()) { - operationCount += op.frequency; - } - if (operationCount > operationsPerThread) { - throw new AssertionError(operationCount + " > " + operationsPerThread); - } - // Fill in the Operation[] array for each thread by laying // down references to operation according to their desired // frequency. @@ -84,8 +377,9 @@ public class Main implements Runnable { int o = 0; LOOP: while (true) { - for (Operation op : Operation.values()) { - for (int f = 0; f < op.frequency; f++) { + for (Operation op : frequencyMap.keySet()) { + int freq = (int)(frequencyMap.get(op) * operationsPerThread); + for (int f = 0; f < freq; f++) { if (o == operations.length) { break LOOP; } @@ -99,11 +393,11 @@ public class Main implements Runnable { threadStresses[t] = new Main(lock, t, operations); } - // Enable to dump operation counds per thread to make sure its + // Enable to dump operation counts per thread to make sure its // sane compared to Operation.frequency if (DEBUG) { for (int t = 0; t < threadStresses.length; t++) { - Operation[] operations = new Operation[operationsPerThread]; + Operation[] operations = threadStresses[t].operations; Map<Operation, Integer> distribution = new HashMap<Operation, Integer>(); for (Operation operation : operations) { Integer ops = distribution.get(operation); @@ -115,7 +409,7 @@ public class Main implements Runnable { distribution.put(operation, ops); } System.out.println("Distribution for " + t); - for (Operation op : Operation.values()) { + for (Operation op : frequencyMap.keySet()) { System.out.println(op + " = " + distribution.get(op)); } } @@ -151,17 +445,19 @@ public class Main implements Runnable { // The notifier thread is a daemon just loops forever to wake // up threads in Operation.WAIT - Thread notifier = new Thread("Notifier") { - public void run() { - while (true) { - synchronized (lock) { - lock.notifyAll(); + if (lock != null) { + Thread notifier = new Thread("Notifier") { + public void run() { + while (true) { + synchronized (lock) { + lock.notifyAll(); + } } } - } - }; - notifier.setDaemon(true); - notifier.start(); + }; + notifier.setDaemon(true); + notifier.start(); + } for (int r = 0; r < runners.length; r++) { runners[r].start(); @@ -196,67 +492,8 @@ public class Main implements Runnable { + " is " + operation); } nextOperation++; - switch (operation) { - case EXIT: { - return; - } - case SIGQUIT: { - try { - SIGQUIT(); - } catch (Exception ex) { - } - } - case SLEEP: { - try { - Thread.sleep(100); - } catch (InterruptedException ignored) { - } - } - case TIMED_WAIT: { - synchronized (lock) { - try { - lock.wait(100, 0); - } catch (InterruptedException ignored) { - } - } - break; - } - case WAIT: { - synchronized (lock) { - try { - lock.wait(); - } catch (InterruptedException ignored) { - } - } - break; - } - case OOM: { - try { - List<byte[]> l = new ArrayList<byte[]>(); - while (true) { - l.add(new byte[1024]); - } - } catch (OutOfMemoryError e) { - } - break; - } - case ALLOC: { - try { - List<byte[]> l = new ArrayList<byte[]>(); - for (int i = 0; i < 1024; i++) { - l.add(new byte[1024]); - } - } catch (OutOfMemoryError e) { - } - break; - } - case STACKTRACE: { - Thread.currentThread().getStackTrace(); - break; - } - default: { - throw new AssertionError(operation.toString()); - } + if (!operation.perform()) { + return; } } } finally { @@ -266,16 +503,4 @@ public class Main implements Runnable { } } - private static void SIGQUIT() throws Exception { - Class<?> osClass = Class.forName("android.system.Os"); - Method getpid = osClass.getDeclaredMethod("getpid"); - int pid = (Integer)getpid.invoke(null); - - Class<?> osConstants = Class.forName("android.system.OsConstants"); - Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); - int sigquit = (Integer)sigquitField.get(null); - - Method kill = osClass.getDeclaredMethod("kill", int.class, int.class); - kill.invoke(null, pid, sigquit); - } } diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 907218a..04eea4e 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -175,6 +175,7 @@ if [ "x$1" = "x" ] ; then MAIN="Main" else MAIN="$1" + shift fi if [ "$ZYGOTE" = "" ]; then |