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