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