1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import java.lang.reflect.*; 18 import java.util.ArrayList; 19 import java.util.Arrays; 20 import java.util.Collections; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.Map; 24 25 // Run on host with: 26 // javac ThreadTest.java && java ThreadStress && rm *.class 27 public class Main implements Runnable { 28 29 public static final boolean DEBUG = false; 30 31 enum Operation { 32 OOM(1), 33 SIGQUIT(19), 34 ALLOC(60), 35 STACKTRACE(20), 36 EXIT(50), 37 38 SLEEP(25), 39 TIMED_WAIT(10), 40 WAIT(15); 41 42 private final int frequency; Operation(int frequency)43 Operation(int frequency) { 44 this.frequency = frequency; 45 } 46 } 47 main(String[] args)48 public static void main(String[] args) throws Exception { 49 50 final int numberOfThreads = 5; 51 final int totalOperations = 1000; 52 final int operationsPerThread = totalOperations/numberOfThreads; 53 54 // Lock used to notify threads performing Operation.WAIT 55 final Object lock = new Object(); 56 57 // Each thread is going to do operationsPerThread 58 // operations. The distribution of operations is determined by 59 // the Operation.frequency values. We fill out an Operation[] 60 // for each thread with the operations it is to perform. The 61 // Operation[] is shuffled so that there is more random 62 // interactions between the threads. 63 64 // The simple-minded filling in of Operation[] based on 65 // Operation.frequency below won't have even have close to a 66 // reasonable distribution if the count of Operation 67 // frequencies is greater than the total number of 68 // operations. So here we do a quick sanity check in case 69 // people tweak the constants above. 70 int operationCount = 0; 71 for (Operation op : Operation.values()) { 72 operationCount += op.frequency; 73 } 74 if (operationCount > operationsPerThread) { 75 throw new AssertionError(operationCount + " > " + operationsPerThread); 76 } 77 78 // Fill in the Operation[] array for each thread by laying 79 // down references to operation according to their desired 80 // frequency. 81 final Main[] threadStresses = new Main[numberOfThreads]; 82 for (int t = 0; t < threadStresses.length; t++) { 83 Operation[] operations = new Operation[operationsPerThread]; 84 int o = 0; 85 LOOP: 86 while (true) { 87 for (Operation op : Operation.values()) { 88 for (int f = 0; f < op.frequency; f++) { 89 if (o == operations.length) { 90 break LOOP; 91 } 92 operations[o] = op; 93 o++; 94 } 95 } 96 } 97 // Randomize the oepration order 98 Collections.shuffle(Arrays.asList(operations)); 99 threadStresses[t] = new Main(lock, t, operations); 100 } 101 102 // Enable to dump operation counds per thread to make sure its 103 // sane compared to Operation.frequency 104 if (DEBUG) { 105 for (int t = 0; t < threadStresses.length; t++) { 106 Operation[] operations = new Operation[operationsPerThread]; 107 Map<Operation, Integer> distribution = new HashMap<Operation, Integer>(); 108 for (Operation operation : operations) { 109 Integer ops = distribution.get(operation); 110 if (ops == null) { 111 ops = 1; 112 } else { 113 ops++; 114 } 115 distribution.put(operation, ops); 116 } 117 System.out.println("Distribution for " + t); 118 for (Operation op : Operation.values()) { 119 System.out.println(op + " = " + distribution.get(op)); 120 } 121 } 122 } 123 124 // Create the runners for each thread. The runner Thread 125 // ensures that thread that exit due to Operation.EXIT will be 126 // restarted until they reach their desired 127 // operationsPerThread. 128 Thread[] runners = new Thread[numberOfThreads]; 129 for (int r = 0; r < runners.length; r++) { 130 final Main ts = threadStresses[r]; 131 runners[r] = new Thread("Runner thread " + r) { 132 final Main threadStress = ts; 133 public void run() { 134 int id = threadStress.id; 135 System.out.println("Starting worker for " + id); 136 while (threadStress.nextOperation < operationsPerThread) { 137 Thread thread = new Thread(ts, "Worker thread " + id); 138 thread.start(); 139 try { 140 thread.join(); 141 } catch (InterruptedException e) { 142 } 143 System.out.println("Thread exited for " + id + " with " 144 + (operationsPerThread - threadStress.nextOperation) 145 + " operations remaining."); 146 } 147 System.out.println("Finishing worker"); 148 } 149 }; 150 } 151 152 // The notifier thread is a daemon just loops forever to wake 153 // up threads in Operation.WAIT 154 Thread notifier = new Thread("Notifier") { 155 public void run() { 156 while (true) { 157 synchronized (lock) { 158 lock.notifyAll(); 159 } 160 } 161 } 162 }; 163 notifier.setDaemon(true); 164 notifier.start(); 165 166 for (int r = 0; r < runners.length; r++) { 167 runners[r].start(); 168 } 169 for (int r = 0; r < runners.length; r++) { 170 runners[r].join(); 171 } 172 } 173 174 private final Operation[] operations; 175 private final Object lock; 176 private final int id; 177 178 private int nextOperation; 179 Main(Object lock, int id, Operation[] operations)180 private Main(Object lock, int id, Operation[] operations) { 181 this.lock = lock; 182 this.id = id; 183 this.operations = operations; 184 } 185 run()186 public void run() { 187 try { 188 if (DEBUG) { 189 System.out.println("Starting ThreadStress " + id); 190 } 191 while (nextOperation < operations.length) { 192 Operation operation = operations[nextOperation]; 193 if (DEBUG) { 194 System.out.println("ThreadStress " + id 195 + " operation " + nextOperation 196 + " is " + operation); 197 } 198 nextOperation++; 199 switch (operation) { 200 case EXIT: { 201 return; 202 } 203 case SIGQUIT: { 204 try { 205 SIGQUIT(); 206 } catch (Exception ex) { 207 } 208 } 209 case SLEEP: { 210 try { 211 Thread.sleep(100); 212 } catch (InterruptedException ignored) { 213 } 214 } 215 case TIMED_WAIT: { 216 synchronized (lock) { 217 try { 218 lock.wait(100, 0); 219 } catch (InterruptedException ignored) { 220 } 221 } 222 break; 223 } 224 case WAIT: { 225 synchronized (lock) { 226 try { 227 lock.wait(); 228 } catch (InterruptedException ignored) { 229 } 230 } 231 break; 232 } 233 case OOM: { 234 try { 235 List<byte[]> l = new ArrayList<byte[]>(); 236 while (true) { 237 l.add(new byte[1024]); 238 } 239 } catch (OutOfMemoryError e) { 240 } 241 break; 242 } 243 case ALLOC: { 244 try { 245 List<byte[]> l = new ArrayList<byte[]>(); 246 for (int i = 0; i < 1024; i++) { 247 l.add(new byte[1024]); 248 } 249 } catch (OutOfMemoryError e) { 250 } 251 break; 252 } 253 case STACKTRACE: { 254 Thread.currentThread().getStackTrace(); 255 break; 256 } 257 default: { 258 throw new AssertionError(operation.toString()); 259 } 260 } 261 } 262 } finally { 263 if (DEBUG) { 264 System.out.println("Finishing ThreadStress for " + id); 265 } 266 } 267 } 268 SIGQUIT()269 private static void SIGQUIT() throws Exception { 270 Class<?> osClass = Class.forName("android.system.Os"); 271 Method getpid = osClass.getDeclaredMethod("getpid"); 272 int pid = (Integer)getpid.invoke(null); 273 274 Class<?> osConstants = Class.forName("android.system.OsConstants"); 275 Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); 276 int sigquit = (Integer)sigquitField.get(null); 277 278 Method kill = osClass.getDeclaredMethod("kill", int.class, int.class); 279 kill.invoke(null, pid, sigquit); 280 } 281 } 282