test/runtime/Thread/TestThreadDumpMonitorContention.java

Wed, 04 Jun 2014 06:25:53 -0700

author
dcubed
date
Wed, 04 Jun 2014 06:25:53 -0700
changeset 6708
4a1062dc52d1
child 6727
22146594cd5a
permissions
-rw-r--r--

8036823: Stack trace sometimes shows 'locked' instead of 'waiting to lock'
Summary: Add a !owner check for 'waiting to lock' to catch current_pending_monitor corner cases.
Reviewed-by: dholmes, sspitsyn, kmo, zgu
Contributed-by: rednaxelafx@gmail.com, zhengyu.gu@oracle.com, daniel.daugherty@oracle.com

dcubed@6708 1 /*
dcubed@6708 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
dcubed@6708 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
dcubed@6708 4 *
dcubed@6708 5 * This code is free software; you can redistribute it and/or modify it
dcubed@6708 6 * under the terms of the GNU General Public License version 2 only, as
dcubed@6708 7 * published by the Free Software Foundation.
dcubed@6708 8 *
dcubed@6708 9 * This code is distributed in the hope that it will be useful, but WITHOUT
dcubed@6708 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
dcubed@6708 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
dcubed@6708 12 * version 2 for more details (a copy is included in the LICENSE file that
dcubed@6708 13 * accompanied this code).
dcubed@6708 14 *
dcubed@6708 15 * You should have received a copy of the GNU General Public License version
dcubed@6708 16 * 2 along with this work; if not, write to the Free Software Foundation,
dcubed@6708 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
dcubed@6708 18 *
dcubed@6708 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
dcubed@6708 20 * or visit www.oracle.com if you need additional information or have any
dcubed@6708 21 * questions.
dcubed@6708 22 */
dcubed@6708 23
dcubed@6708 24 /*
dcubed@6708 25 * @test
dcubed@6708 26 * @bug 8036823
dcubed@6708 27 * @summary Creates two threads contending for the same lock and checks
dcubed@6708 28 * whether jstack reports "locked" by more than one thread.
dcubed@6708 29 *
dcubed@6708 30 * @library /testlibrary
dcubed@6708 31 * @run main/othervm TestThreadDumpMonitorContention
dcubed@6708 32 */
dcubed@6708 33
dcubed@6708 34 import java.io.BufferedReader;
dcubed@6708 35 import java.io.InputStreamReader;
dcubed@6708 36 import java.lang.management.ManagementFactory;
dcubed@6708 37 import java.lang.management.RuntimeMXBean;
dcubed@6708 38 import java.util.ArrayList;
dcubed@6708 39 import java.util.List;
dcubed@6708 40 import java.util.regex.Matcher;
dcubed@6708 41 import java.util.regex.Pattern;
dcubed@6708 42
dcubed@6708 43 import com.oracle.java.testlibrary.*;
dcubed@6708 44
dcubed@6708 45 public class TestThreadDumpMonitorContention {
dcubed@6708 46 // jstack tends to be closely bound to the VM that we are running
dcubed@6708 47 // so use getTestJDKTool() instead of getCompileJDKTool() or even
dcubed@6708 48 // getJDKTool() which can fall back to "compile.jdk".
dcubed@6708 49 final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack");
dcubed@6708 50 final static String PID = getPid();
dcubed@6708 51
dcubed@6708 52 // looking for header lines with these patterns:
dcubed@6708 53 // "ContendingThread-1" #19 prio=5 os_prio=64 tid=0x000000000079c000 nid=0x23 runnable [0xffff80ffb8b87000]
dcubed@6708 54 // "ContendingThread-2" #21 prio=5 os_prio=64 tid=0x0000000000780000 nid=0x2f waiting for monitor entry [0xfffffd7fc1111000]
dcubed@6708 55 final static Pattern HEADER_PREFIX_PATTERN = Pattern.compile(
dcubed@6708 56 "^\"ContendingThread-.*");
dcubed@6708 57 final static Pattern HEADER_WAITING_PATTERN = Pattern.compile(
dcubed@6708 58 "^\"ContendingThread-.* waiting for monitor entry .*");
dcubed@6708 59 final static Pattern HEADER_RUNNABLE_PATTERN = Pattern.compile(
dcubed@6708 60 "^\"ContendingThread-.* runnable .*");
dcubed@6708 61
dcubed@6708 62 // looking for thread state lines with these patterns:
dcubed@6708 63 // java.lang.Thread.State: RUNNABLE
dcubed@6708 64 // java.lang.Thread.State: BLOCKED (on object monitor)
dcubed@6708 65 final static Pattern THREAD_STATE_PREFIX_PATTERN = Pattern.compile(
dcubed@6708 66 " *java\\.lang\\.Thread\\.State: .*");
dcubed@6708 67 final static Pattern THREAD_STATE_BLOCKED_PATTERN = Pattern.compile(
dcubed@6708 68 " *java\\.lang\\.Thread\\.State: BLOCKED \\(on object monitor\\)");
dcubed@6708 69 final static Pattern THREAD_STATE_RUNNABLE_PATTERN = Pattern.compile(
dcubed@6708 70 " *java\\.lang\\.Thread\\.State: RUNNABLE");
dcubed@6708 71
dcubed@6708 72 // looking for duplicates of this pattern:
dcubed@6708 73 // - locked <0x000000076ac59e20> (a TestThreadDumpMonitorContention$1)
dcubed@6708 74 final static Pattern LOCK_PATTERN = Pattern.compile(
dcubed@6708 75 ".* locked \\<.*\\(a TestThreadDumpMonitorContention.*");
dcubed@6708 76
dcubed@6708 77 // sanity checking header and thread state lines associated
dcubed@6708 78 // with this pattern:
dcubed@6708 79 // - waiting to lock <0x000000076ac59e20> (a TestThreadDumpMonitorContention$1)
dcubed@6708 80 final static Pattern WAITING_PATTERN = Pattern.compile(
dcubed@6708 81 ".* waiting to lock \\<.*\\(a TestThreadDumpMonitorContention.*");
dcubed@6708 82
dcubed@6708 83 volatile static boolean done = false;
dcubed@6708 84
dcubed@6708 85 static int error_cnt = 0;
dcubed@6708 86 static String header_line = null;
dcubed@6708 87 static boolean have_header_line = false;
dcubed@6708 88 static boolean have_thread_state_line = false;
dcubed@6708 89 static int match_cnt = 0;
dcubed@6708 90 static String[] match_list = new String[2];
dcubed@6708 91 static int n_samples = 15;
dcubed@6708 92 static String thread_state_line = null;
dcubed@6708 93 static boolean verbose = false;
dcubed@6708 94
dcubed@6708 95 public static void main(String[] args) throws Exception {
dcubed@6708 96 if (args.length != 0) {
dcubed@6708 97 int arg_i = 0;
dcubed@6708 98 if (args[arg_i].equals("-v")) {
dcubed@6708 99 verbose = true;
dcubed@6708 100 arg_i++;
dcubed@6708 101 }
dcubed@6708 102
dcubed@6708 103 try {
dcubed@6708 104 n_samples = Integer.parseInt(args[arg_i]);
dcubed@6708 105 } catch (NumberFormatException nfe) {
dcubed@6708 106 System.err.println(nfe);
dcubed@6708 107 usage();
dcubed@6708 108 }
dcubed@6708 109 }
dcubed@6708 110
dcubed@6708 111 Runnable runnable = new Runnable() {
dcubed@6708 112 public void run() {
dcubed@6708 113 while (!done) {
dcubed@6708 114 synchronized (this) { }
dcubed@6708 115 }
dcubed@6708 116 }
dcubed@6708 117 };
dcubed@6708 118 Thread[] thread_list = new Thread[2];
dcubed@6708 119 thread_list[0] = new Thread(runnable, "ContendingThread-1");
dcubed@6708 120 thread_list[1] = new Thread(runnable, "ContendingThread-2");
dcubed@6708 121 thread_list[0].start();
dcubed@6708 122 thread_list[1].start();
dcubed@6708 123
dcubed@6708 124 doSamples();
dcubed@6708 125
dcubed@6708 126 done = true;
dcubed@6708 127
dcubed@6708 128 thread_list[0].join();
dcubed@6708 129 thread_list[1].join();
dcubed@6708 130
dcubed@6708 131 if (error_cnt == 0) {
dcubed@6708 132 System.out.println("Test PASSED.");
dcubed@6708 133 } else {
dcubed@6708 134 System.out.println("Test FAILED.");
dcubed@6708 135 throw new AssertionError("error_cnt=" + error_cnt);
dcubed@6708 136 }
dcubed@6708 137 }
dcubed@6708 138
dcubed@6708 139 // Reached a blank line which is the end of the
dcubed@6708 140 // stack trace without matching either LOCK_PATTERN
dcubed@6708 141 // or WAITING_PATTERN. Rare, but it's not an error.
dcubed@6708 142 //
dcubed@6708 143 // Example:
dcubed@6708 144 // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f runnable [0xfffffd7fc1111000]
dcubed@6708 145 // java.lang.Thread.State: RUNNABLE
dcubed@6708 146 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:67)
dcubed@6708 147 // at java.lang.Thread.run(Thread.java:745)
dcubed@6708 148 //
dcubed@6708 149 static boolean checkBlankLine(String line) {
dcubed@6708 150 if (line.length() == 0) {
dcubed@6708 151 have_header_line = false;
dcubed@6708 152 have_thread_state_line = false;
dcubed@6708 153 return true;
dcubed@6708 154 }
dcubed@6708 155
dcubed@6708 156 return false;
dcubed@6708 157 }
dcubed@6708 158
dcubed@6708 159 // Process the locked line here if we found one.
dcubed@6708 160 //
dcubed@6708 161 // Example 1:
dcubed@6708 162 // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f runnable [0xfffffd7fc1111000]
dcubed@6708 163 // java.lang.Thread.State: RUNNABLE
dcubed@6708 164 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:67)
dcubed@6708 165 // - locked <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1)
dcubed@6708 166 // at java.lang.Thread.run(Thread.java:745)
dcubed@6708 167 //
dcubed@6708 168 // Example 2:
dcubed@6708 169 // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f waiting for monitor entry [0xfffffd7fc1111000]
dcubed@6708 170 // java.lang.Thread.State: BLOCKED (on object monitor)
dcubed@6708 171 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:67)
dcubed@6708 172 // - locked <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1)
dcubed@6708 173 // at java.lang.Thread.run(Thread.java:745)
dcubed@6708 174 //
dcubed@6708 175 static boolean checkLockedLine(String line) {
dcubed@6708 176 Matcher matcher = LOCK_PATTERN.matcher(line);
dcubed@6708 177 if (matcher.matches()) {
dcubed@6708 178 if (verbose) {
dcubed@6708 179 System.out.println("locked_line='" + line + "'");
dcubed@6708 180 }
dcubed@6708 181 match_list[match_cnt] = new String(line);
dcubed@6708 182 match_cnt++;
dcubed@6708 183
dcubed@6708 184 matcher = HEADER_RUNNABLE_PATTERN.matcher(header_line);
dcubed@6708 185 if (!matcher.matches()) {
dcubed@6708 186 // It's strange, but a locked line can also
dcubed@6708 187 // match the HEADER_WAITING_PATTERN.
dcubed@6708 188 matcher = HEADER_WAITING_PATTERN.matcher(header_line);
dcubed@6708 189 if (!matcher.matches()) {
dcubed@6708 190 System.err.println();
dcubed@6708 191 System.err.println("ERROR: header line does " +
dcubed@6708 192 "not match runnable or waiting patterns.");
dcubed@6708 193 System.err.println("ERROR: header_line='" +
dcubed@6708 194 header_line + "'");
dcubed@6708 195 System.err.println("ERROR: locked_line='" + line + "'");
dcubed@6708 196 error_cnt++;
dcubed@6708 197 }
dcubed@6708 198 }
dcubed@6708 199
dcubed@6708 200 matcher = THREAD_STATE_RUNNABLE_PATTERN.matcher(thread_state_line);
dcubed@6708 201 if (!matcher.matches()) {
dcubed@6708 202 // It's strange, but a locked line can also
dcubed@6708 203 // match the THREAD_STATE_BLOCKED_PATTERN.
dcubed@6708 204 matcher = THREAD_STATE_BLOCKED_PATTERN.matcher(
dcubed@6708 205 thread_state_line);
dcubed@6708 206 if (!matcher.matches()) {
dcubed@6708 207 System.err.println();
dcubed@6708 208 System.err.println("ERROR: thread state line does not " +
dcubed@6708 209 "match runnable or waiting patterns.");
dcubed@6708 210 System.err.println("ERROR: " + "thread_state_line='" +
dcubed@6708 211 thread_state_line + "'");
dcubed@6708 212 System.err.println("ERROR: locked_line='" + line + "'");
dcubed@6708 213 error_cnt++;
dcubed@6708 214 }
dcubed@6708 215 }
dcubed@6708 216
dcubed@6708 217 // Have everything we need from this thread stack
dcubed@6708 218 // that matches the LOCK_PATTERN.
dcubed@6708 219 have_header_line = false;
dcubed@6708 220 have_thread_state_line = false;
dcubed@6708 221 return true;
dcubed@6708 222 }
dcubed@6708 223
dcubed@6708 224 return false;
dcubed@6708 225 }
dcubed@6708 226
dcubed@6708 227 // Process the waiting line here if we found one.
dcubed@6708 228 //
dcubed@6708 229 // Example:
dcubed@6708 230 // "ContendingThread-2" #22 prio=5 os_prio=64 tid=0x00000000007b9800 nid=0x30 waiting for monitor entry [0xfffffd7fc1010000]
dcubed@6708 231 // java.lang.Thread.State: BLOCKED (on object monitor)
dcubed@6708 232 // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:67)
dcubed@6708 233 // - waiting to lock <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1)
dcubed@6708 234 // at java.lang.Thread.run(Thread.java:745)
dcubed@6708 235 //
dcubed@6708 236 static boolean checkWaitingLine(String line) {
dcubed@6708 237 Matcher matcher = WAITING_PATTERN.matcher(line);
dcubed@6708 238 if (matcher.matches()) {
dcubed@6708 239 if (verbose) {
dcubed@6708 240 System.out.println("waiting_line='" + line + "'");
dcubed@6708 241 }
dcubed@6708 242
dcubed@6708 243 matcher = HEADER_WAITING_PATTERN.matcher(header_line);
dcubed@6708 244 if (!matcher.matches()) {
dcubed@6708 245 System.err.println();
dcubed@6708 246 System.err.println("ERROR: header line does " +
dcubed@6708 247 "not match a waiting pattern.");
dcubed@6708 248 System.err.println("ERROR: header_line='" + header_line + "'");
dcubed@6708 249 System.err.println("ERROR: waiting_line='" + line + "'");
dcubed@6708 250 error_cnt++;
dcubed@6708 251 }
dcubed@6708 252
dcubed@6708 253 matcher = THREAD_STATE_BLOCKED_PATTERN.matcher(thread_state_line);
dcubed@6708 254 if (!matcher.matches()) {
dcubed@6708 255 System.err.println();
dcubed@6708 256 System.err.println("ERROR: thread state line " +
dcubed@6708 257 "does not match a waiting pattern.");
dcubed@6708 258 System.err.println("ERROR: thread_state_line='" +
dcubed@6708 259 thread_state_line + "'");
dcubed@6708 260 System.err.println("ERROR: waiting_line='" + line + "'");
dcubed@6708 261 error_cnt++;
dcubed@6708 262 }
dcubed@6708 263
dcubed@6708 264 // Have everything we need from this thread stack
dcubed@6708 265 // that matches the WAITING_PATTERN.
dcubed@6708 266 have_header_line = false;
dcubed@6708 267 have_thread_state_line = false;
dcubed@6708 268 return true;
dcubed@6708 269 }
dcubed@6708 270
dcubed@6708 271 return false;
dcubed@6708 272 }
dcubed@6708 273
dcubed@6708 274 static void doSamples() throws Exception {
dcubed@6708 275 for (int count = 0; count < n_samples; count++) {
dcubed@6708 276 match_cnt = 0;
dcubed@6708 277 // verbose mode or an error has a lot of output so add more space
dcubed@6708 278 if (verbose || error_cnt > 0) System.out.println();
dcubed@6708 279 System.out.println("Sample #" + count);
dcubed@6708 280
dcubed@6708 281 // We don't use the ProcessTools, OutputBuffer or
dcubed@6708 282 // OutputAnalyzer classes from the testlibrary because
dcubed@6708 283 // we have a complicated multi-line parse to perform
dcubed@6708 284 // on a narrow subset of the JSTACK output.
dcubed@6708 285 //
dcubed@6708 286 // - we only care about stack traces that match
dcubed@6708 287 // HEADER_PREFIX_PATTERN; only two should match
dcubed@6708 288 // - we care about at most three lines from each stack trace
dcubed@6708 289 // - if both stack traces match LOCKED_PATTERN, then that's
dcubed@6708 290 // a failure and we report it
dcubed@6708 291 // - for a stack trace that matches LOCKED_PATTERN, we verify:
dcubed@6708 292 // - the header line matches HEADER_RUNNABLE_PATTERN
dcubed@6708 293 // or HEADER_WAITING_PATTERN
dcubed@6708 294 // - the thread state line matches THREAD_STATE_BLOCKED_PATTERN
dcubed@6708 295 // or THREAD_STATE_RUNNABLE_PATTERN
dcubed@6708 296 // - we report any mismatches as failures
dcubed@6708 297 // - for a stack trace that matches WAITING_PATTERN, we verify:
dcubed@6708 298 // - the header line matches HEADER_WAITING_PATTERN
dcubed@6708 299 // - the thread state line matches THREAD_STATE_BLOCKED_PATTERN
dcubed@6708 300 // - we report any mismatches as failures
dcubed@6708 301 // - the stack traces that match HEADER_PREFIX_PATTERN may
dcubed@6708 302 // not match either LOCKED_PATTERN or WAITING_PATTERN
dcubed@6708 303 // because we might observe the thread outside of
dcubed@6708 304 // monitor operations; this is not considered a failure
dcubed@6708 305 //
dcubed@6708 306 // When we do observe LOCKED_PATTERN or WAITING_PATTERN,
dcubed@6708 307 // then we are checking the header and thread state patterns
dcubed@6708 308 // that occurred earlier in the current stack trace that
dcubed@6708 309 // matched HEADER_PREFIX_PATTERN. We don't use data from
dcubed@6708 310 // stack traces that don't match HEADER_PREFIX_PATTERN and
dcubed@6708 311 // we don't mix data between the two stack traces that do
dcubed@6708 312 // match HEADER_PREFIX_PATTERN.
dcubed@6708 313 //
dcubed@6708 314 Process process = new ProcessBuilder(JSTACK, PID)
dcubed@6708 315 .redirectErrorStream(true).start();
dcubed@6708 316
dcubed@6708 317 BufferedReader reader = new BufferedReader(new InputStreamReader(
dcubed@6708 318 process.getInputStream()));
dcubed@6708 319 String line;
dcubed@6708 320 while ((line = reader.readLine()) != null) {
dcubed@6708 321 Matcher matcher = null;
dcubed@6708 322
dcubed@6708 323 // process the header line here
dcubed@6708 324 if (!have_header_line) {
dcubed@6708 325 matcher = HEADER_PREFIX_PATTERN.matcher(line);
dcubed@6708 326 if (matcher.matches()) {
dcubed@6708 327 if (verbose) {
dcubed@6708 328 System.out.println();
dcubed@6708 329 System.out.println("header='" + line + "'");
dcubed@6708 330 }
dcubed@6708 331 header_line = new String(line);
dcubed@6708 332 have_header_line = true;
dcubed@6708 333 continue;
dcubed@6708 334 }
dcubed@6708 335 continue; // skip until have a header line
dcubed@6708 336 }
dcubed@6708 337
dcubed@6708 338 // process the thread state line here
dcubed@6708 339 if (!have_thread_state_line) {
dcubed@6708 340 matcher = THREAD_STATE_PREFIX_PATTERN.matcher(line);
dcubed@6708 341 if (matcher.matches()) {
dcubed@6708 342 if (verbose) {
dcubed@6708 343 System.out.println("thread_state='" + line + "'");
dcubed@6708 344 }
dcubed@6708 345 thread_state_line = new String(line);
dcubed@6708 346 have_thread_state_line = true;
dcubed@6708 347 continue;
dcubed@6708 348 }
dcubed@6708 349 continue; // skip until we have a thread state line
dcubed@6708 350 }
dcubed@6708 351
dcubed@6708 352 // process the locked line here if we find one
dcubed@6708 353 if (checkLockedLine(line)) {
dcubed@6708 354 continue;
dcubed@6708 355 }
dcubed@6708 356
dcubed@6708 357 // process the waiting line here if we find one
dcubed@6708 358 if (checkWaitingLine(line)) {
dcubed@6708 359 continue;
dcubed@6708 360 }
dcubed@6708 361
dcubed@6708 362 // process the blank line here if we find one
dcubed@6708 363 if (checkBlankLine(line)) {
dcubed@6708 364 continue;
dcubed@6708 365 }
dcubed@6708 366 }
dcubed@6708 367 process.waitFor();
dcubed@6708 368
dcubed@6708 369 if (match_cnt == 2) {
dcubed@6708 370 if (match_list[0].equals(match_list[1])) {
dcubed@6708 371 System.err.println();
dcubed@6708 372 System.err.println("ERROR: matching lock lines:");
dcubed@6708 373 System.err.println("ERROR: line[0]'" + match_list[0] + "'");
dcubed@6708 374 System.err.println("ERROR: line[1]'" + match_list[1] + "'");
dcubed@6708 375 error_cnt++;
dcubed@6708 376 }
dcubed@6708 377 }
dcubed@6708 378
dcubed@6708 379 // slight delay between jstack launches
dcubed@6708 380 Thread.sleep(500);
dcubed@6708 381 }
dcubed@6708 382 }
dcubed@6708 383
dcubed@6708 384 // This helper relies on RuntimeMXBean.getName() returning a string
dcubed@6708 385 // that looks like this: 5436@mt-haku
dcubed@6708 386 //
dcubed@6708 387 // The testlibrary has tryFindJvmPid(), but that uses a separate
dcubed@6708 388 // process which is much more expensive for finding out your own PID.
dcubed@6708 389 //
dcubed@6708 390 static String getPid() {
dcubed@6708 391 RuntimeMXBean runtimebean = ManagementFactory.getRuntimeMXBean();
dcubed@6708 392 String vmname = runtimebean.getName();
dcubed@6708 393 int i = vmname.indexOf('@');
dcubed@6708 394 if (i != -1) {
dcubed@6708 395 vmname = vmname.substring(0, i);
dcubed@6708 396 }
dcubed@6708 397 return vmname;
dcubed@6708 398 }
dcubed@6708 399
dcubed@6708 400 static void usage() {
dcubed@6708 401 System.err.println("Usage: " +
dcubed@6708 402 "java TestThreadDumpMonitorContention [-v] [n_samples]");
dcubed@6708 403 System.exit(1);
dcubed@6708 404 }
dcubed@6708 405 }

mercurial