1 /* 2 * Copyright (c) 2014, 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 /* 25 * @test 26 * @bug 8036823 27 * @bug 8046287 28 * @summary Creates two threads contending for the same lock and checks 29 * whether jstack reports "locked" by more than one thread. 30 * 31 * @library /testlibrary 32 * @build com.oracle.java.testlibrary.* 33 * @run main/othervm TestThreadDumpMonitorContention 34 */ 35 36 import java.io.BufferedReader; 37 import java.io.InputStreamReader; 38 import java.lang.management.ManagementFactory; 39 import java.lang.management.RuntimeMXBean; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 45 import com.oracle.java.testlibrary.*; 46 47 public class TestThreadDumpMonitorContention { 48 // jstack tends to be closely bound to the VM that we are running 49 // so use getTestJDKTool() instead of getCompileJDKTool() or even 50 // getJDKTool() which can fall back to "compile.jdk". 51 final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack"); 52 final static String PID = getPid(); 53 54 // looking for header lines with these patterns: 55 // "ContendingThread-1" #19 prio=5 os_prio=64 tid=0x000000000079c000 nid=0x23 runnable [0xffff80ffb8b87000] 56 // "ContendingThread-2" #21 prio=5 os_prio=64 tid=0x0000000000780000 nid=0x2f waiting for monitor entry [0xfffffd7fc1111000] 57 // "ContendingThread-2" #24 prio=5 os_prio=64 tid=0x0000000000ec8800 nid=0x31 waiting on condition [0xfffffd7bbfffe000] 58 final static Pattern HEADER_PREFIX_PATTERN = Pattern.compile( 59 "^\"ContendingThread-.*"); 60 final static Pattern HEADER_WAITING_PATTERN1 = Pattern.compile( 61 "^\"ContendingThread-.* waiting for monitor entry .*"); 62 final static Pattern HEADER_WAITING_PATTERN2 = Pattern.compile( 63 "^\"ContendingThread-.* waiting on condition .*"); 64 final static Pattern HEADER_RUNNABLE_PATTERN = Pattern.compile( 65 "^\"ContendingThread-.* runnable .*"); 66 67 // looking for thread state lines with these patterns: 68 // java.lang.Thread.State: RUNNABLE 69 // java.lang.Thread.State: BLOCKED (on object monitor) 70 final static Pattern THREAD_STATE_PREFIX_PATTERN = Pattern.compile( 71 " *java\\.lang\\.Thread\\.State: .*"); 72 final static Pattern THREAD_STATE_BLOCKED_PATTERN = Pattern.compile( 73 " *java\\.lang\\.Thread\\.State: BLOCKED \\(on object monitor\\)"); 74 final static Pattern THREAD_STATE_RUNNABLE_PATTERN = Pattern.compile( 75 " *java\\.lang\\.Thread\\.State: RUNNABLE"); 76 77 // looking for duplicates of this pattern: 78 // - locked <0x000000076ac59e20> (a TestThreadDumpMonitorContention$1) 79 final static Pattern LOCK_PATTERN = Pattern.compile( 80 ".* locked \\<.*\\(a TestThreadDumpMonitorContention.*"); 81 82 // sanity checking header and thread state lines associated 83 // with this pattern: 84 // - waiting to lock <0x000000076ac59e20> (a TestThreadDumpMonitorContention$1) 85 final static Pattern WAITING_PATTERN = Pattern.compile( 86 ".* waiting to lock \\<.*\\(a TestThreadDumpMonitorContention.*"); 87 88 final static Object barrier = new Object(); 89 volatile static boolean done = false; 90 91 static int barrier_cnt = 0; 92 static int blank_line_match_cnt = 0; 93 static int error_cnt = 0; 94 static boolean have_header_line = false; 95 static boolean have_thread_state_line = false; 96 static String header_line = null; 97 static int header_prefix_match_cnt = 0; 98 static int locked_line_match_cnt = 0; 99 static String[] locked_match_list = new String[2]; 100 static int n_samples = 15; 101 static int sum_both_running_cnt = 0; 102 static int sum_both_waiting_cnt = 0; 103 static int sum_contended_cnt = 0; 104 static int sum_locked_hdr_runnable_cnt = 0; 105 static int sum_locked_hdr_waiting1_cnt = 0; 106 static int sum_locked_hdr_waiting2_cnt = 0; 107 static int sum_locked_thr_state_blocked_cnt = 0; 108 static int sum_locked_thr_state_runnable_cnt = 0; 109 static int sum_one_waiting_cnt = 0; 110 static int sum_uncontended_cnt = 0; 111 static int sum_waiting_hdr_waiting1_cnt = 0; 112 static int sum_waiting_thr_state_blocked_cnt = 0; 113 static String thread_state_line = null; 114 static boolean verbose = false; 115 static int waiting_line_match_cnt = 0; 116 117 public static void main(String[] args) throws Exception { 118 if (args.length != 0) { 119 int arg_i = 0; 120 if (args[arg_i].equals("-v")) { 121 verbose = true; 122 arg_i++; 123 } 124 125 try { 126 n_samples = Integer.parseInt(args[arg_i]); 127 } catch (NumberFormatException nfe) { 128 System.err.println(nfe); 129 usage(); 130 } 131 } 132 133 Runnable runnable = new Runnable() { 134 public void run() { 135 synchronized (barrier) { 136 // let the main thread know we're running 137 barrier_cnt++; 138 barrier.notify(); 139 } 140 while (!done) { 141 synchronized (this) { } 142 } 143 } 144 }; 145 Thread[] thread_list = new Thread[2]; 146 thread_list[0] = new Thread(runnable, "ContendingThread-1"); 147 thread_list[1] = new Thread(runnable, "ContendingThread-2"); 148 synchronized (barrier) { 149 thread_list[0].start(); 150 thread_list[1].start(); 151 152 // Wait until the contending threads are running so that 153 // we don't sample any thread init states. 154 while (barrier_cnt < 2) { 155 barrier.wait(); 156 } 157 } 158 159 doSamples(); 160 161 done = true; 162 163 thread_list[0].join(); 164 thread_list[1].join(); 165 166 if (error_cnt == 0) { 167 System.out.println("Test PASSED."); 168 } else { 169 System.out.println("Test FAILED."); 170 throw new AssertionError("error_cnt=" + error_cnt); 171 } 172 } 173 174 // Reached a blank line which is the end of the 175 // stack trace without matching either LOCK_PATTERN 176 // or WAITING_PATTERN. Rare, but it's not an error. 177 // 178 // Example: 179 // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f runnable [0xfffffd7fc1111000] 180 // java.lang.Thread.State: RUNNABLE 181 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) 182 // at java.lang.Thread.run(Thread.java:745) 183 // 184 static boolean checkBlankLine(String line) { 185 if (line.length() == 0) { 186 blank_line_match_cnt++; 187 have_header_line = false; 188 have_thread_state_line = false; 189 return true; 190 } 191 192 return false; 193 } 194 195 // Process the locked line here if we found one. 196 // 197 // Example 1: 198 // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f runnable [0xfffffd7fc1111000] 199 // java.lang.Thread.State: RUNNABLE 200 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) 201 // - locked <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1) 202 // at java.lang.Thread.run(Thread.java:745) 203 // 204 // Example 2: 205 // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f waiting for monitor entry [0xfffffd7fc1111000] 206 // java.lang.Thread.State: BLOCKED (on object monitor) 207 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) 208 // - locked <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1) 209 // at java.lang.Thread.run(Thread.java:745) 210 // 211 // Example 3: 212 // "ContendingThread-2" #24 prio=5 os_prio=64 tid=0x0000000000ec8800 nid=0x31 waiting on condition [0xfffffd7bbfffe000] 213 // java.lang.Thread.State: RUNNABLE 214 // JavaThread state: _thread_blocked 215 // Thread: 0x0000000000ec8800 [0x31] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0 216 // JavaThread state: _thread_blocked 217 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) 218 // - locked <0xfffffd7e6d03eb28> (a TestThreadDumpMonitorContention$1) 219 // at java.lang.Thread.run(Thread.java:745) 220 // 221 static boolean checkLockedLine(String line) { 222 Matcher matcher = LOCK_PATTERN.matcher(line); 223 if (matcher.matches()) { 224 if (verbose) { 225 System.out.println("locked_line='" + line + "'"); 226 } 227 locked_match_list[locked_line_match_cnt] = new String(line); 228 locked_line_match_cnt++; 229 230 matcher = HEADER_RUNNABLE_PATTERN.matcher(header_line); 231 if (matcher.matches()) { 232 sum_locked_hdr_runnable_cnt++; 233 } else { 234 // It's strange, but a locked line can also 235 // match the HEADER_WAITING_PATTERN{1,2}. 236 matcher = HEADER_WAITING_PATTERN1.matcher(header_line); 237 if (matcher.matches()) { 238 sum_locked_hdr_waiting1_cnt++; 239 } else { 240 matcher = HEADER_WAITING_PATTERN2.matcher(header_line); 241 if (matcher.matches()) { 242 sum_locked_hdr_waiting2_cnt++; 243 } else { 244 System.err.println(); 245 System.err.println("ERROR: header line does " + 246 "not match runnable or waiting patterns."); 247 System.err.println("ERROR: header_line='" + 248 header_line + "'"); 249 System.err.println("ERROR: locked_line='" + line + 250 "'"); 251 error_cnt++; 252 } 253 } 254 } 255 256 matcher = THREAD_STATE_RUNNABLE_PATTERN.matcher(thread_state_line); 257 if (matcher.matches()) { 258 sum_locked_thr_state_runnable_cnt++; 259 } else { 260 // It's strange, but a locked line can also 261 // match the THREAD_STATE_BLOCKED_PATTERN. 262 matcher = THREAD_STATE_BLOCKED_PATTERN.matcher( 263 thread_state_line); 264 if (matcher.matches()) { 265 sum_locked_thr_state_blocked_cnt++; 266 } else { 267 System.err.println(); 268 System.err.println("ERROR: thread state line does not " + 269 "match runnable or waiting patterns."); 270 System.err.println("ERROR: " + "thread_state_line='" + 271 thread_state_line + "'"); 272 System.err.println("ERROR: locked_line='" + line + "'"); 273 error_cnt++; 274 } 275 } 276 277 // Have everything we need from this thread stack 278 // that matches the LOCK_PATTERN. 279 have_header_line = false; 280 have_thread_state_line = false; 281 return true; 282 } 283 284 return false; 285 } 286 287 // Process the waiting line here if we found one. 288 // 289 // Example: 290 // "ContendingThread-2" #22 prio=5 os_prio=64 tid=0x00000000007b9800 nid=0x30 waiting for monitor entry [0xfffffd7fc1010000] 291 // java.lang.Thread.State: BLOCKED (on object monitor) 292 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) 293 // - waiting to lock <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1) 294 // at java.lang.Thread.run(Thread.java:745) 295 // 296 static boolean checkWaitingLine(String line) { 297 Matcher matcher = WAITING_PATTERN.matcher(line); 298 if (matcher.matches()) { 299 waiting_line_match_cnt++; 300 if (verbose) { 301 System.out.println("waiting_line='" + line + "'"); 302 } 303 304 matcher = HEADER_WAITING_PATTERN1.matcher(header_line); 305 if (matcher.matches()) { 306 sum_waiting_hdr_waiting1_cnt++; 307 } else { 308 System.err.println(); 309 System.err.println("ERROR: header line does " + 310 "not match a waiting pattern."); 311 System.err.println("ERROR: header_line='" + header_line + "'"); 312 System.err.println("ERROR: waiting_line='" + line + "'"); 313 error_cnt++; 314 } 315 316 matcher = THREAD_STATE_BLOCKED_PATTERN.matcher(thread_state_line); 317 if (matcher.matches()) { 318 sum_waiting_thr_state_blocked_cnt++; 319 } else { 320 System.err.println(); 321 System.err.println("ERROR: thread state line " + 322 "does not match a waiting pattern."); 323 System.err.println("ERROR: thread_state_line='" + 324 thread_state_line + "'"); 325 System.err.println("ERROR: waiting_line='" + line + "'"); 326 error_cnt++; 327 } 328 329 // Have everything we need from this thread stack 330 // that matches the WAITING_PATTERN. 331 have_header_line = false; 332 have_thread_state_line = false; 333 return true; 334 } 335 336 return false; 337 } 338 339 static void doSamples() throws Exception { 340 for (int count = 0; count < n_samples; count++) { 341 blank_line_match_cnt = 0; 342 header_prefix_match_cnt = 0; 343 locked_line_match_cnt = 0; 344 waiting_line_match_cnt = 0; 345 // verbose mode or an error has a lot of output so add more space 346 if (verbose || error_cnt > 0) System.out.println(); 347 System.out.println("Sample #" + count); 348 349 // We don't use the ProcessTools, OutputBuffer or 350 // OutputAnalyzer classes from the testlibrary because 351 // we have a complicated multi-line parse to perform 352 // on a narrow subset of the JSTACK output. 353 // 354 // - we only care about stack traces that match 355 // HEADER_PREFIX_PATTERN; only two should match 356 // - we care about at most three lines from each stack trace 357 // - if both stack traces match LOCKED_PATTERN, then that's 358 // a failure and we report it 359 // - for a stack trace that matches LOCKED_PATTERN, we verify: 360 // - the header line matches HEADER_RUNNABLE_PATTERN 361 // or HEADER_WAITING_PATTERN{1,2} 362 // - the thread state line matches THREAD_STATE_BLOCKED_PATTERN 363 // or THREAD_STATE_RUNNABLE_PATTERN 364 // - we report any mismatches as failures 365 // - for a stack trace that matches WAITING_PATTERN, we verify: 366 // - the header line matches HEADER_WAITING_PATTERN1 367 // - the thread state line matches THREAD_STATE_BLOCKED_PATTERN 368 // - we report any mismatches as failures 369 // - the stack traces that match HEADER_PREFIX_PATTERN may 370 // not match either LOCKED_PATTERN or WAITING_PATTERN 371 // because we might observe the thread outside of 372 // monitor operations; this is not considered a failure 373 // 374 // When we do observe LOCKED_PATTERN or WAITING_PATTERN, 375 // then we are checking the header and thread state patterns 376 // that occurred earlier in the current stack trace that 377 // matched HEADER_PREFIX_PATTERN. We don't use data from 378 // stack traces that don't match HEADER_PREFIX_PATTERN and 379 // we don't mix data between the two stack traces that do 380 // match HEADER_PREFIX_PATTERN. 381 // 382 Process process = new ProcessBuilder(JSTACK, PID) 383 .redirectErrorStream(true).start(); 384 385 BufferedReader reader = new BufferedReader(new InputStreamReader( 386 process.getInputStream())); 387 String line; 388 while ((line = reader.readLine()) != null) { 389 Matcher matcher = null; 390 391 // process the header line here 392 if (!have_header_line) { 393 matcher = HEADER_PREFIX_PATTERN.matcher(line); 394 if (matcher.matches()) { 395 header_prefix_match_cnt++; 396 if (verbose) { 397 System.out.println(); 398 System.out.println("header='" + line + "'"); 399 } 400 header_line = new String(line); 401 have_header_line = true; 402 continue; 403 } 404 continue; // skip until have a header line 405 } 406 407 // process the thread state line here 408 if (!have_thread_state_line) { 409 matcher = THREAD_STATE_PREFIX_PATTERN.matcher(line); 410 if (matcher.matches()) { 411 if (verbose) { 412 System.out.println("thread_state='" + line + "'"); 413 } 414 thread_state_line = new String(line); 415 have_thread_state_line = true; 416 continue; 417 } 418 continue; // skip until we have a thread state line 419 } 420 421 // process the locked line here if we find one 422 if (checkLockedLine(line)) { 423 continue; 424 } 425 426 // process the waiting line here if we find one 427 if (checkWaitingLine(line)) { 428 continue; 429 } 430 431 // process the blank line here if we find one 432 if (checkBlankLine(line)) { 433 continue; 434 } 435 } 436 process.waitFor(); 437 438 if (header_prefix_match_cnt != 2) { 439 System.err.println(); 440 System.err.println("ERROR: should match exactly two headers."); 441 System.err.println("ERROR: header_prefix_match_cnt=" + 442 header_prefix_match_cnt); 443 error_cnt++; 444 } 445 446 if (locked_line_match_cnt == 2) { 447 if (locked_match_list[0].equals(locked_match_list[1])) { 448 System.err.println(); 449 System.err.println("ERROR: matching lock lines:"); 450 System.err.println("ERROR: line[0]'" + 451 locked_match_list[0] + "'"); 452 System.err.println("ERROR: line[1]'" + 453 locked_match_list[1] + "'"); 454 error_cnt++; 455 } 456 } 457 458 if (locked_line_match_cnt == 1) { 459 // one thread has the lock 460 if (waiting_line_match_cnt == 1) { 461 // and the other contended for it 462 sum_contended_cnt++; 463 } else { 464 // and the other is just running 465 sum_uncontended_cnt++; 466 } 467 } else if (waiting_line_match_cnt == 1) { 468 // one thread is waiting 469 sum_one_waiting_cnt++; 470 } else if (waiting_line_match_cnt == 2) { 471 // both threads are waiting 472 sum_both_waiting_cnt++; 473 } else { 474 // both threads are running 475 sum_both_running_cnt++; 476 } 477 478 // slight delay between jstack launches 479 Thread.sleep(500); 480 } 481 482 if (error_cnt != 0) { 483 // skip summary info since there were errors 484 return; 485 } 486 487 System.out.println("INFO: Summary for all samples:"); 488 System.out.println("INFO: both_running_cnt=" + sum_both_running_cnt); 489 System.out.println("INFO: both_waiting_cnt=" + sum_both_waiting_cnt); 490 System.out.println("INFO: contended_cnt=" + sum_contended_cnt); 491 System.out.println("INFO: one_waiting_cnt=" + sum_one_waiting_cnt); 492 System.out.println("INFO: uncontended_cnt=" + sum_uncontended_cnt); 493 System.out.println("INFO: locked_hdr_runnable_cnt=" + 494 sum_locked_hdr_runnable_cnt); 495 System.out.println("INFO: locked_hdr_waiting1_cnt=" + 496 sum_locked_hdr_waiting1_cnt); 497 System.out.println("INFO: locked_hdr_waiting2_cnt=" + 498 sum_locked_hdr_waiting2_cnt); 499 System.out.println("INFO: locked_thr_state_blocked_cnt=" + 500 sum_locked_thr_state_blocked_cnt); 501 System.out.println("INFO: locked_thr_state_runnable_cnt=" + 502 sum_locked_thr_state_runnable_cnt); 503 System.out.println("INFO: waiting_hdr_waiting1_cnt=" + 504 sum_waiting_hdr_waiting1_cnt); 505 System.out.println("INFO: waiting_thr_state_blocked_cnt=" + 506 sum_waiting_thr_state_blocked_cnt); 507 508 if (sum_contended_cnt == 0) { 509 System.err.println("WARNING: the primary scenario for 8036823" + 510 " has not been exercised by this test run."); 511 } 512 } 513 514 // This helper relies on RuntimeMXBean.getName() returning a string 515 // that looks like this: 5436@mt-haku 516 // 517 // The testlibrary has tryFindJvmPid(), but that uses a separate 518 // process which is much more expensive for finding out your own PID. 519 // 520 static String getPid() { 521 RuntimeMXBean runtimebean = ManagementFactory.getRuntimeMXBean(); 522 String vmname = runtimebean.getName(); 523 int i = vmname.indexOf('@'); 524 if (i != -1) { 525 vmname = vmname.substring(0, i); 526 } 527 return vmname; 528 } 529 530 static void usage() { 531 System.err.println("Usage: " + 532 "java TestThreadDumpMonitorContention [-v] [n_samples]"); 533 System.exit(1); 534 } 535 }