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