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