summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2011-10-21 12:05:06 -0700
committerBrian Carlstrom <bdc@google.com>2011-10-21 12:35:43 -0700
commit7c6deaa3382f1f4fb0f591af206f03045c6e9004 (patch)
tree7f0a4e2956aae97b6d813eee620b486222b1333c
parent4f20aef512500525b2255d4b0e6984fe3e4b5229 (diff)
downloadart-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.mk2
-rw-r--r--build/Android.common.mk1
-rw-r--r--build/Android.oattest.mk3
-rw-r--r--src/runtime.cc3
-rw-r--r--test/ThreadStress/ThreadStress.java235
5 files changed, 242 insertions, 2 deletions
diff --git a/Android.mk b/Android.mk
index 59a4f50..6de4cfb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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);
+ }
+ }
+ }
+}