1/*
2 * Copyright (c) 2015, 2017, 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 */
35import common.ToolResults;
36import java.util.Iterator;
37import utils.*;
38
39class 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
85public 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().pid());
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}
207