1 /*
   2  * Copyright (c) 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  * @summary Create a thread which stops in methods a(), a()->b(), a()->b()->c(),
  27  *          synchronizing on one monitor inside of each method.
  28  *          After checking that lock info is correct invoke another method
  29  *          and get the lock again. Repeat this action.
  30  * @modules java.base/jdk.internal.misc
  31  * @library /test/lib/share/classes
  32  * @library ../share
  33  * @build common.*
  34  *
  35  * @run main/othervm -XX:+UsePerfData SpreadLockTest
  36  */
  37 import common.ToolResults;
  38 import java.util.Iterator;
  39 import utils.*;
  40 
  41 class SpreadLockDebuggee extends Thread {
  42 
  43     static final String THREAD_NAME = "MyThread";
  44 
  45     SpreadLockDebuggee() {
  46         setName(THREAD_NAME);
  47     }
  48 
  49     Object monitor = new Object();
  50 
  51     public void c() {
  52         synchronized (monitor) {
  53             Utils.sleep();
  54         }
  55     }
  56 
  57     public void b() {
  58         synchronized (monitor) {
  59             try {
  60                 while (true) {
  61                     Thread.sleep(Long.MAX_VALUE);
  62                 }
  63             } catch (InterruptedException e) {
  64                 c();
  65             }
  66         }
  67     }
  68 
  69     public void a() {
  70         synchronized (monitor) {
  71             try {
  72                 while (true) {
  73                     Thread.sleep(Long.MAX_VALUE);
  74                 }
  75             } catch (InterruptedException e) {
  76                 b();
  77             }
  78         }
  79     }
  80 
  81     @Override
  82     public void run() {
  83         a();
  84     }
  85 
  86 }
  87 
  88 public class SpreadLockTest {
  89 
  90     public static void main(String[] args) throws Exception {
  91         new SpreadLockTest().doTest();
  92     }
  93 
  94     private void doTest() throws Exception {
  95         SpreadLockDebuggee debuggee = new SpreadLockDebuggee();
  96 
  97         // Start in method a()
  98         debuggee.start();
  99 
 100         // Collect output from the jstack tool
 101         JstackTool jstackTool = new JstackTool(ProcessHandle.current().getPid());
 102         ToolResults results1 = jstackTool.measure();
 103 
 104         // Go to method b()
 105         debuggee.interrupt();
 106 
 107         // Collect output from the jstack tool
 108         ToolResults results2 = jstackTool.measure();
 109 
 110         // Go to method c()
 111         debuggee.interrupt();
 112 
 113         // Collect output from the jstack tool
 114         ToolResults results3 = jstackTool.measure();
 115 
 116         analyse(results1.getStdoutString(), results2.getStdoutString(), results3.getStdoutString());
 117     }
 118 
 119     // Analyzing the outputs from the 3 jstack runs
 120     public void analyse(String result1, String result2, String result3) {
 121         String jstackStr1 = result1;
 122         String jstackStr2 = result2;
 123         String jstackStr3 = result3;
 124 
 125         if (jstackStr1 == null) {
 126             throw new RuntimeException("First jstack output is empty");
 127         }
 128         if (jstackStr2 == null) {
 129             throw new RuntimeException("Second jstack output is empty");
 130         }
 131         if (jstackStr3 == null) {
 132             throw new RuntimeException("Third jstack output is empty");
 133         }
 134 
 135         Format format = new DefaultFormat();
 136         JStack jstack1 = format.parse(jstackStr1);
 137         JStack jstack2 = format.parse(jstackStr2);
 138         JStack jstack3 = format.parse(jstackStr3);
 139 
 140         ThreadStack ts1 = jstack1.getThreadStack(SpreadLockDebuggee.THREAD_NAME);
 141         ThreadStack ts2 = jstack2.getThreadStack(SpreadLockDebuggee.THREAD_NAME);
 142         ThreadStack ts3 = jstack3.getThreadStack(SpreadLockDebuggee.THREAD_NAME);
 143 
 144         if (ts1 == null || ts2 == null || ts3 == null) {
 145             throw new RuntimeException(
 146                     "One of thread stack trace is null in the first jstack output : "
 147                     + ts1 + ", " + ts2 + ", " + ts3);
 148         }
 149 
 150         MonitorInfo[] monitorInfo = new MonitorInfo[6];
 151         int counter = 0;
 152 
 153         Iterator<MethodInfo> it = ts1.getStack().iterator();
 154         while (it.hasNext()) {
 155             MethodInfo mi = it.next();
 156             if (mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".a")) {
 157                 monitorInfo[counter++] = haveToHaveOneLock(mi);
 158             }
 159         }
 160 
 161         it = ts2.getStack().iterator();
 162         while (it.hasNext()) {
 163             MethodInfo mi = it.next();
 164             if (mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".a")
 165                     || mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".b")) {
 166                 monitorInfo[counter++] = haveToHaveOneLock(mi);
 167             }
 168         }
 169 
 170         it = ts3.getStack().iterator();
 171         while (it.hasNext()) {
 172             MethodInfo mi = it.next();
 173             if (mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".a")
 174                     || mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".b")
 175                     || mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".c")) {
 176                 monitorInfo[counter++] = haveToHaveOneLock(mi);
 177             }
 178         }
 179 
 180         System.out.println("All monitors found - passed");
 181 
 182     }
 183 
 184     private MonitorInfo haveToHaveOneLock(MethodInfo mi) {
 185         if (mi.getLocks().size() == 1) {
 186             System.out.println("Method \"" + mi.getName()
 187                     + "\" contain 1 lock - correct");
 188             return mi.getLocks().getFirst();
 189         } else {
 190             throw new RuntimeException("Lock count ("
 191                     + mi.getLocks().size() + ") is incorrect in method \""
 192                     + mi.getName() + "\"");
 193         }
 194     }
 195 
 196 }