diff options
author | Brian Carlstrom <bdc@google.com> | 2011-10-21 12:05:06 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2011-10-21 12:35:43 -0700 |
commit | 7c6deaa3382f1f4fb0f591af206f03045c6e9004 (patch) | |
tree | 7f0a4e2956aae97b6d813eee620b486222b1333c | |
parent | 4f20aef512500525b2255d4b0e6984fe3e4b5229 (diff) | |
download | art-7c6deaa3382f1f4fb0f591af206f03045c6e9004.zip art-7c6deaa3382f1f4fb0f591af206f03045c6e9004.tar.gz art-7c6deaa3382f1f4fb0f591af206f03045c6e9004.tar.bz2 |
Add ThreadStress test to try and exercise corner cases
Change-Id: I8d151e9ba935b8c76406483c6c276cc26896aabf
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | build/Android.common.mk | 1 | ||||
-rw-r--r-- | build/Android.oattest.mk | 3 | ||||
-rw-r--r-- | src/runtime.cc | 3 | ||||
-rw-r--r-- | test/ThreadStress/ThreadStress.java | 235 |
5 files changed, 242 insertions, 2 deletions
@@ -139,8 +139,6 @@ test-art-target-oat-process: test-art-target-oat-process-am # test-art-target-oa $(eval $(call build-art-cache-oat,system/framework/am.jar)) $(eval $(call build-art-cache-oat,system/app/Calculator.apk)) -$(eval $(call build-art-cache-oat,system/app/SettingsProvider.apk)) -$(eval $(call build-art-cache-oat,system/app/SystemUI.apk)) .PHONY: test-art-target-oat-process-am test-art-target-oat-process-am: $(call art-cache-oat,system/framework/am.jar) test-art-target-sync diff --git a/build/Android.common.mk b/build/Android.common.mk index b9d6109..9c7238a 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -257,6 +257,7 @@ TEST_DEX_DIRECTORIES := \ Statics \ StaticsFromCode \ SystemMethods \ + ThreadStress \ Invoke \ XandY diff --git a/build/Android.oattest.mk b/build/Android.oattest.mk index 2761404..db9c6b0 100644 --- a/build/Android.oattest.mk +++ b/build/Android.oattest.mk @@ -85,4 +85,7 @@ $(eval $(call declare-test-test-target,SystemMethods,)) # TODO: Enable when the StackWalk2 tests are passing # $(eval $(call declare-test-test-target,StackWalk2,)) +# TODO: Enable when ThreadStress passes +# $(eval $(call declare-test-test-target,ThreadStress,)) + ######################################################################## diff --git a/src/runtime.cc b/src/runtime.cc index 64823fd..08a1d9f 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -307,7 +307,10 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b } else if (option.starts_with("-Xlockprofthreshold:")) { parsed->lock_profiling_threshold_ = ParseIntegerOrDie(option); } else if (option.starts_with("-Xstacktracefile:")) { +// always show stack traces in debug builds +#ifdef NDEBUG parsed->stack_trace_file_ = option.substr(strlen("-Xstacktracefile:")).data(); +#endif } else if (option == "sensitiveThread") { parsed->hook_is_sensitive_thread_ = reinterpret_cast<bool (*)()>(options[i].second); } else if (option == "vfprintf") { diff --git a/test/ThreadStress/ThreadStress.java b/test/ThreadStress/ThreadStress.java new file mode 100644 index 0000000..94089dc --- /dev/null +++ b/test/ThreadStress/ThreadStress.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// Run on host with: +// javac ThreadTest.java && java ThreadStress && rm *.class +class ThreadStress implements Runnable { + + public static final boolean DEBUG = true; + + enum Operation { + OOM(1), + ALLOC(99), + EXIT(50), + WAIT(50); + + private final int frequency; + Operation(int frequency) { + this.frequency = frequency; + } + } + + public static void main(String[] args) throws Exception { + + final int numberOfThreads = 5; + final int totalOperations = 1000; + final int operationsPerThread = totalOperations/numberOfThreads; + + // Lock used to notify threads performin Operation.WAIT + final Object lock = new Object(); + + // Each thread is going to do operationsPerThread + // operations. The distribution of operations is determined by + // the Operation.frequency values. We fill out an Operation[] + // for each thread with the operations it is to perform. The + // 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. + final ThreadStress[] threadStresses = new ThreadStress[numberOfThreads]; + for (int t = 0; t < threadStresses.length; t++) { + Operation[] operations = new Operation[operationsPerThread]; + int o = 0; + LOOP: + while (true) { + for (Operation op : Operation.values()) { + for (int f = 0; f < op.frequency; f++) { + if (o == operations.length) { + break LOOP; + } + operations[o] = op; + o++; + } + } + } + // Randomize the oepration order + Collections.shuffle(Arrays.asList(operations)); + threadStresses[t] = new ThreadStress(lock, t, operations); + } + + // Enable to dump operation counds 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]; + Map<Operation, Integer> distribution = new HashMap<Operation, Integer>(); + for (Operation operation : operations) { + Integer ops = distribution.get(operation); + if (ops == null) { + ops = 1; + } else { + ops++; + } + distribution.put(operation, ops); + } + System.out.println("Distribution for " + t); + for (Operation op : Operation.values()) { + System.out.println(op + " = " + distribution.get(op)); + } + } + } + + // Create the runners for each thread. The runner Thread + // ensures that thread that exit due to Operation.EXIT will be + // restarted until they reach their desired + // operationsPerThread. + Thread[] runners = new Thread[numberOfThreads]; + for (int r = 0; r < runners.length; r++) { + final ThreadStress ts = threadStresses[r]; + runners[r] = new Thread() { + final ThreadStress threadStress = ts; + public void run() { + int id = threadStress.id; + System.out.println("Starting runner for " + id); + while (threadStress.nextOperation < operationsPerThread) { + Thread thread = new Thread(ts); + thread.start(); + try { + thread.join(); + } catch (InterruptedException e) { + } + System.out.println("Thread exited for " + id + " with " + + (operationsPerThread - threadStress.nextOperation) + + " operations remaining."); + } + System.out.println("Finishing runner for " + id); + } + }; + } + + // The notifier thread is a daemon just loops forever to wake + // up threads in Operation.WAIT + Thread notifier = new Thread() { + public void run() { + while (true) { + synchronized (lock) { + lock.notifyAll(); + } + } + } + }; + notifier.setDaemon(true); + notifier.start(); + + for (int r = 0; r < runners.length; r++) { + runners[r].start(); + } + for (int r = 0; r < runners.length; r++) { + runners[r].join(); + } + } + + private final Operation[] operations; + private final Object lock; + private final int id; + + private int nextOperation; + + private ThreadStress(Object lock, int id, Operation[] operations) { + this.lock = lock; + this.id = id; + this.operations = operations; + } + + public void run() { + try { + if (DEBUG) { + System.out.println("Starting ThreadStress " + id); + } + while (nextOperation < operations.length) { + Operation operation = operations[nextOperation]; + if (DEBUG) { + System.out.println("ThreadStress " + id + + " operation " + nextOperation + + " is " + operation); + } + nextOperation++; + switch (operation) { + case EXIT: { + return; + } + case WAIT: { + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException e) { + } + } + break; + } + case OOM: { + try { + List<byte[]> l = new ArrayList<byte[]>(); + while (true) { + l.add(new byte[1024]); + } + } catch (OutOfMemoryError e) { + } + break; + } + case ALLOC: { + List<byte[]> l = new ArrayList<byte[]>(); + for (int i = 0; i < 1024; i++) { + l.add(new byte[1024]); + } + break; + } + default: { + throw new AssertionError(operation.toString()); + } + } + } + } + finally { + if (DEBUG) { + System.out.println("Finishing ThreadStress for " + id); + } + } + } +} |