1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package nsk.monitoring.stress.thread; 25 26 import java.lang.management.*; 27 import java.io.*; 28 import nsk.share.*; 29 import nsk.monitoring.share.*; 30 31 public class strace001 { 32 public final static String LIB_NAME = "StackTraceController"; 33 private final static String THREAD_NAME 34 = "nsk.monitoring.stress.thread.RunningThread"; 35 private final static int ITERATIONS = 50; 36 37 public static volatile boolean finish; 38 public static volatile boolean testFailed = false; 39 public static Object common = new Object(); 40 public static Integer activeThreads; 41 private static Log log; 42 private static int depth; 43 private static int threadCount; 44 private static String[] expectedTrace; 45 private static ThreadMonitor monitor; 46 private static ThreadController controller; 47 48 public static void main(String[] argv) { 49 System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); 50 } 51 52 public static int run(String[] argv, PrintStream out) { 53 ArgumentHandler argHandler = new ArgumentHandler(argv); 54 log = new Log(out, argHandler); 55 monitor = Monitor.getThreadMonitor(log, argHandler); 56 threadCount = argHandler.getThreadCount(); 57 depth = argHandler.getThreadDepth(); 58 controller = new ThreadController(log, threadCount, depth, 59 argHandler.getInvocationType()); 60 RunningThread threads[] = new RunningThread[threadCount]; 61 62 // Fill expectedTrace array according to invocation type that is set in 63 // test options 64 if ( !fillTrace() ) { 65 log.complain("Unknown invocation type: " 66 + controller.getInvocationType()); 67 return Consts.TEST_FAILED; 68 } 69 70 for (int i = 0; i < ITERATIONS; i++) { 71 log.display("\nIteration: " + i); 72 activeThreads = new Integer(0); 73 finish = false; 74 75 // Start all threads. Half of them are user threads, 76 // others - deamon 77 for (int j = 0; j < threadCount; j++) { 78 threads[j] = new RunningThread(j, controller, log, depth); 79 threads[j].setDaemon(i % 2 == 0); 80 threads[j].start(); 81 } 82 83 // Wait for all threads to start 84 while (activeThreads.intValue() < threadCount) 85 Thread.currentThread().yield(); 86 log.display("All threads started: " + activeThreads); 87 88 // Make a snapshot of stack trace for all threads and check it 89 for (int j = 0; j < threadCount; j++) { 90 boolean isAlive = threads[j].isAlive(); 91 ThreadInfo info = monitor.getThreadInfo(threads[j].getId(), Integer.MAX_VALUE); 92 93 // A thread may be dead because of OutOfMemoryError or 94 // StackOverflowError 95 if (isAlive) { 96 if (info == null) { 97 log.complain("ThreadInfo for thread " + j + " is null, " 98 + "but Thread.isAlive() returned true."); 99 testFailed = true; 100 continue; 101 } 102 103 StackTraceElement[] snapshot = info.getStackTrace(); 104 if ( !checkTrace(snapshot) ) { 105 log.display("\nSnapshot of thread: " + j); 106 printStackTrace(snapshot); 107 testFailed = true; 108 } 109 } else { 110 log.display("Thread " + j + " is dead, skipping it."); 111 } 112 } 113 114 // Let all threads to complete their job 115 finish = true; 116 117 // Wait for all threads to be dead 118 for (int j = 0; j < threadCount; j++) 119 try { 120 threads[j].join(); 121 } catch (InterruptedException e) { 122 log.complain("Unexpected exception while joining thread " 123 + j); 124 e.printStackTrace(log.getOutStream()); 125 testFailed = true; 126 } 127 log.display("All threads have died."); 128 } // for i 129 130 if (testFailed) 131 log.complain("TEST FAILED."); 132 133 return (testFailed) ? Consts.TEST_FAILED : Consts.TEST_PASSED; 134 } 135 136 // Fill expectedTrace array according to the invocation type that is set in 137 // test options 138 private static boolean fillTrace() { 139 switch (controller.getInvocationType()) { 140 case ThreadController.JAVA_TYPE: 141 expectedTrace = new String[] { 142 "java.lang.Thread.sleep" 143 , "java.lang.Thread.yield" 144 , THREAD_NAME + ".waitForSign" 145 , THREAD_NAME + ".recursionJava" 146 , THREAD_NAME + ".run" 147 }; 148 break; 149 150 case ThreadController.NATIVE_TYPE: 151 expectedTrace = new String[] { 152 "java.lang.Thread.sleep" 153 , "java.lang.Thread.yield" 154 , THREAD_NAME + ".waitForSign" 155 , THREAD_NAME + ".recursionNative" 156 , THREAD_NAME + ".run" 157 }; 158 break; 159 160 case ThreadController.MIXED_TYPE: 161 expectedTrace = new String[] { 162 "java.lang.Thread.sleep" 163 , "java.lang.Thread.yield" 164 , THREAD_NAME + ".waitForSign" 165 , THREAD_NAME + ".recursionNative" 166 , THREAD_NAME + ".recursionJava" 167 , THREAD_NAME + ".run" 168 }; 169 break; 170 171 default: 172 return false; 173 } 174 175 return true; 176 } 177 178 // The method prints stack trace in style JVM does 179 private static void printStackTrace(StackTraceElement[] elements) { 180 for (int i = 0; i < elements.length; i++) { 181 String s = "\t " + i + ": " + elements[i].getClassName() + "." 182 + elements[i].getMethodName(); 183 184 if (elements[i].isNativeMethod()) 185 s = s + "(Native Method)"; 186 else 187 s = s + "(" + elements[i].getFileName() + ":" 188 + elements[i].getLineNumber() + ")"; 189 log.display(s); 190 } 191 } 192 193 // The method performs checks of the stack trace 194 private static boolean checkTrace(StackTraceElement[] elements) { 195 int length = elements.length; 196 int expectedLength = depth +3; 197 boolean result = true; 198 199 // Check the length of the trace. It must not be greater than 200 // expectedLength. Number of recursionJava() or recursionNative() 201 // methods must not ne greater than depth, also one Object.wait() or 202 // Thread.yield() method, one run( ) and one waitForSign(). 203 if (length > expectedLength) { 204 log.complain("Length of the stack trace is " + length + ", but " 205 + "expected to be not greater than " + expectedLength); 206 result = false; 207 } 208 209 // Check each element of the snapshot 210 for (int i = 0; i < elements.length; i++) { 211 if (i == elements.length - 1) { 212 213 // The latest method of the snapshot must be RunningThread.run() 214 if ( !checkLastElement(elements[i]) ) 215 result = false; 216 } else { 217 218 // getClassName() and getMethodName() must return correct values 219 // for each element 220 if ( !checkElement(i, elements[i]) ) 221 result = false; 222 } 223 } 224 return result; 225 } 226 227 // The method checks that StackTraceElement.getClassName() and 228 // StackTraceElement.getMethodName() return expected values 229 private static boolean checkElement(int n, StackTraceElement element) { 230 String name = element.getClassName() + "." + element.getMethodName(); 231 232 // The latest element is not checked, since it must be "run()" 233 for (int i = 0; i < expectedTrace.length - 1; i++) { 234 if (expectedTrace[i].equals(name)) 235 return true; 236 } 237 238 log.complain("Unexpected " + n + " element of the stack trace:\n\t" 239 + name); 240 return false; 241 } 242 243 // The method checks that StackTraceElement.getClassName() returns 244 // "RunningThread" and StackTraceElement.getMethodName() returns "run" 245 // for the latest element of the snapshot 246 private static boolean checkLastElement(StackTraceElement element) { 247 String name = element.getClassName() + "." + element.getMethodName(); 248 String last = expectedTrace[expectedTrace.length - 1]; 249 250 if (!last.equals(name)) { 251 log.complain("Unexpected last element of the stack trace:\n\t" 252 + name + "\nexpected:\n\t" + last); 253 return false; 254 } 255 return true; 256 } 257 } 258 259 // This thread starts a recursion until it reaches specified depth. Then the 260 // thread waits until it gets a notification from main thread. Pure java 261 // and native methods are used in the thread. So, the thread is definitly in 262 // "running" state when main thread performs its checks. 263 class RunningThread extends Thread { 264 private int num; 265 private static ThreadController controller; 266 private Log log; 267 private int depth; 268 private boolean mixed = false; 269 native int recursionNative(int maxDepth, int currentDepth, boolean returnToJava); 270 271 static { 272 try { 273 System.loadLibrary(strace001.LIB_NAME); 274 } catch (UnsatisfiedLinkError e) { 275 System.err.println("Cannot load library " + strace001.LIB_NAME); 276 System.err.println("java.library.path: " 277 + System.getProperty("java.library.path")); 278 throw e; 279 } 280 } 281 282 RunningThread(int num, ThreadController controller, Log log, int depth) { 283 this.num = num; 284 this.controller = controller; 285 this.log = log; 286 this.depth = depth; 287 } 288 289 public void run() { 290 int result = 0; 291 int invocationType = controller.getInvocationType(); 292 293 // This instance of the thread is alive 294 synchronized (strace001.common) { 295 synchronized (strace001.activeThreads) { 296 strace001.activeThreads 297 = new Integer(strace001.activeThreads.intValue() + 1); 298 } 299 } 300 301 // Choose a method (native or java) to continue recursion 302 try { 303 switch (invocationType) { 304 case ThreadController.JAVA_TYPE: 305 recursionJava(depth, 0); 306 break; 307 case ThreadController.NATIVE_TYPE: 308 result = recursionNative(depth, 0, false); 309 310 if (result == 1) { 311 log.display("Fatal error (OutOfMemoryError or " 312 + "StackOverflow) is thrown in native method of " 313 + " thread " + num); 314 return; 315 } else if (result == 2) { 316 log.complain("Unexpected exception is thrown in native " 317 + "method of thread " + num); 318 strace001.testFailed = true; 319 return; 320 } 321 break; 322 case ThreadController.MIXED_TYPE: 323 mixed = true; 324 result = recursionNative(depth, 0, true); 325 326 if (result == 1) { 327 log.display("Fatal error (OutOfMemoryError or " 328 + "StackOverflow) is thrown in native method of " 329 + " thread " + num); 330 return; 331 } else if (result == 2) { 332 log.complain("Unexpected exception is thrown in native " 333 + "method of thread " + num); 334 strace001.testFailed = true; 335 return; 336 } 337 break; 338 default: 339 log.complain("Unknown invocation type: " 340 + controller.getInvocationType()); 341 strace001.testFailed = true; 342 } 343 } catch (OutOfMemoryError e) { 344 // Recursion is too deep, so exit peacefully 345 log.display("OutOfMemoryError is thrown in thread " + num); 346 } catch (StackOverflowError e) { 347 // Recursion is too deep, so exit peacefully 348 log.display("StackOverflowError is thrown in thread " + num); 349 } 350 } // run() 351 352 private void recursionJava(int maxDepth, int currentDepth) { 353 // A short delay. Otherwise the method will reach the specified depth 354 // almost instantly 355 try { 356 sleep(1); 357 } catch (InterruptedException e) { 358 log.complain("Unexpected exception"); 359 e.printStackTrace(log.getOutStream()); 360 strace001.testFailed = true; 361 } 362 363 currentDepth++; 364 if (maxDepth > currentDepth) { 365 yield(); 366 if (mixed) { 367 int result = recursionNative(maxDepth, currentDepth, true); 368 369 if (result == 1) { 370 log.display("Fatal error (OutOfMemoryError or " 371 + "StackOverflow) is thrown in native method of " 372 + " thread " + num); 373 return; 374 } else if (result == 2) { 375 log.complain("Unexpected exception is thrown in native " 376 + "method of thread " + num); 377 strace001.testFailed = true; 378 return; 379 } 380 } else 381 recursionJava(maxDepth, currentDepth); 382 } 383 384 waitForSign(); 385 } // recursionJava() 386 387 private void waitForSign() { 388 // When the depth is reached, wait for a notification from main thread 389 while (!strace001.finish) { 390 try { 391 sleep(1); 392 } catch (InterruptedException e) { 393 log.complain("Unexpected exception"); 394 e.printStackTrace(log.getOutStream()); 395 strace001.testFailed = true; 396 break; 397 } 398 } 399 } // waitForSign() 400 } // class RunningThread