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 invoke another method
29 *          and get the lock again. Repeat this action.
30 * @modules java.base/jdk.internal.misc
31 * @library /test/lib
32 * @library ../share
33 * @run main/othervm -XX:+UsePerfData SpreadLockTest
34 */
35import common.ToolResults;
36import java.util.Iterator;
37import utils.*;
38
39class SpreadLockDebuggee extends Thread {
40
41    static final String THREAD_NAME = "MyThread";
42
43    SpreadLockDebuggee() {
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        synchronized (monitor) {
57            try {
58                while (true) {
59                    Thread.sleep(Long.MAX_VALUE);
60                }
61            } catch (InterruptedException e) {
62                c();
63            }
64        }
65    }
66
67    public void a() {
68        synchronized (monitor) {
69            try {
70                while (true) {
71                    Thread.sleep(Long.MAX_VALUE);
72                }
73            } catch (InterruptedException e) {
74                b();
75            }
76        }
77    }
78
79    @Override
80    public void run() {
81        a();
82    }
83
84}
85
86public class SpreadLockTest {
87
88    public static void main(String[] args) throws Exception {
89        new SpreadLockTest().doTest();
90    }
91
92    private void doTest() throws Exception {
93        SpreadLockDebuggee debuggee = new SpreadLockDebuggee();
94
95        // Start in method a()
96        debuggee.start();
97
98        // Collect output from the jstack tool
99        JstackTool jstackTool = new JstackTool(ProcessHandle.current().pid());
100        ToolResults results1 = jstackTool.measure();
101
102        // Go to method b()
103        debuggee.interrupt();
104
105        // Collect output from the jstack tool
106        ToolResults results2 = jstackTool.measure();
107
108        // Go to method c()
109        debuggee.interrupt();
110
111        // Collect output from the jstack tool
112        ToolResults results3 = jstackTool.measure();
113
114        analyse(results1.getStdoutString(), results2.getStdoutString(), results3.getStdoutString());
115    }
116
117    // Analyzing the outputs from the 3 jstack runs
118    public void analyse(String result1, String result2, String result3) {
119        String jstackStr1 = result1;
120        String jstackStr2 = result2;
121        String jstackStr3 = result3;
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(SpreadLockDebuggee.THREAD_NAME);
139        ThreadStack ts2 = jstack2.getThreadStack(SpreadLockDebuggee.THREAD_NAME);
140        ThreadStack ts3 = jstack3.getThreadStack(SpreadLockDebuggee.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[] monitorInfo = new MonitorInfo[6];
149        int counter = 0;
150
151        Iterator<MethodInfo> it = ts1.getStack().iterator();
152        while (it.hasNext()) {
153            MethodInfo mi = it.next();
154            if (mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".a")) {
155                monitorInfo[counter++] = haveToHaveOneLock(mi);
156            }
157        }
158
159        it = ts2.getStack().iterator();
160        while (it.hasNext()) {
161            MethodInfo mi = it.next();
162            if (mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".a")
163                    || mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".b")) {
164                monitorInfo[counter++] = haveToHaveOneLock(mi);
165            }
166        }
167
168        it = ts3.getStack().iterator();
169        while (it.hasNext()) {
170            MethodInfo mi = it.next();
171            if (mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".a")
172                    || mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".b")
173                    || mi.getName().startsWith(SpreadLockDebuggee.class.getName() + ".c")) {
174                monitorInfo[counter++] = haveToHaveOneLock(mi);
175            }
176        }
177
178        System.out.println("All monitors found - passed");
179
180    }
181
182    private MonitorInfo haveToHaveOneLock(MethodInfo mi) {
183        if (mi.getLocks().size() == 1) {
184            System.out.println("Method \"" + mi.getName()
185                    + "\" contain 1 lock - correct");
186            return mi.getLocks().getFirst();
187        } else {
188            throw new RuntimeException("Lock count ("
189                    + mi.getLocks().size() + ") is incorrect in method \""
190                    + mi.getName() + "\"");
191        }
192    }
193
194}
195