1 /*
2  * Copyright (c) 2005, 2018, 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  * @bug     5086470 6358247 7193302 8048215
27  * @summary Test type conversion when invoking ThreadMXBean.dumpAllThreads
28  *          through proxy.
29  * @author  Mandy Chung
30  *
31  * @run main ThreadMXBeanProxy
32  */
33 
34 import static java.lang.management.ManagementFactory.*;
35 import java.lang.management.*;
36 import java.util.*;
37 import java.util.concurrent.locks.*;
38 import java.util.concurrent.TimeUnit;
39 import java.io.*;
40 import javax.management.*;
41 
42 public class ThreadMXBeanProxy {
43     private static MBeanServer server =
44         ManagementFactory.getPlatformMBeanServer();
45     private static ThreadMXBean mbean;
46     static Mutex mutex = new Mutex();
47     static Object lock = new Object();
48     static Object waiter = new Object();
49     static MyThread thread = new MyThread();
main(String[] argv)50     public static void main(String[] argv) throws Exception {
51         mbean = newPlatformMXBeanProxy(server,
52                                        THREAD_MXBEAN_NAME,
53                                        ThreadMXBean.class);
54 
55         if (!mbean.isSynchronizerUsageSupported()) {
56             System.out.println("Monitoring of synchronizer usage not supported");
57             return;
58         }
59 
60         thread.setDaemon(true);
61         thread.start();
62 
63         // wait until myThread acquires mutex and lock owner is set.
64         while (!(mutex.isLocked() && mutex.getLockOwner() == thread)) {
65            try {
66                Thread.sleep(100);
67            } catch (InterruptedException e) {
68                throw new RuntimeException(e);
69            }
70         }
71 
72         // 'thread' holds the mutex, which means it must also have the monitor of
73         // 'waiter' at least until it does the wait(). So we acquire the monitor of
74         // 'waiter' here, which ensures that 'thread' must be in wait()
75         synchronized(waiter) {
76         }
77 
78         long[] ids = new long[] { thread.getId() };
79 
80         // validate the local access
81         ThreadInfo[] infos = getThreadMXBean().getThreadInfo(ids, true, true);
82         if (infos.length != 1) {
83             throw new RuntimeException("Returned ThreadInfo[] of length=" +
84                 infos.length + ". Expected to be 1.");
85         }
86         thread.checkThreadInfo(infos[0]);
87 
88         // validate the remote access
89         infos = mbean.getThreadInfo(ids, true, true);
90         if (infos.length != 1) {
91             throw new RuntimeException("Returned ThreadInfo[] of length=" +
92                 infos.length + ". Expected to be 1.");
93         }
94         thread.checkThreadInfo(infos[0]);
95 
96         boolean found = false;
97         infos = mbean.dumpAllThreads(true, true);
98         for (ThreadInfo ti : infos) {
99             if (ti.getThreadId() == thread.getId()) {
100                 thread.checkThreadInfo(ti);
101                 found = true;
102             }
103         }
104 
105         if (!found) {
106             throw new RuntimeException("No ThreadInfo found for MyThread");
107         }
108 
109         System.out.println("Test passed");
110     }
111 
112     static class MyThread extends Thread {
MyThread()113         public MyThread() {
114             super("MyThread");
115         }
run()116         public void run() {
117             synchronized (lock) {
118                 synchronized(waiter) {
119                     mutex.lock();
120                     try {
121                         waiter.wait();
122                     } catch (InterruptedException e) {
123                         throw new RuntimeException(e);
124                     }
125                 }
126             }
127         }
128 
129         int OWNED_MONITORS = 1;
130         int OWNED_SYNCS = 1;
checkThreadInfo(ThreadInfo info)131         void checkThreadInfo(ThreadInfo info) {
132             if (!getName().equals(info.getThreadName())) {
133                 throw new RuntimeException("Name: " + info.getThreadName() +
134                     " not matched. Expected: " + getName());
135             }
136 
137             MonitorInfo[] monitors = info.getLockedMonitors();
138             if (monitors.length != OWNED_MONITORS) {
139                 throw new RuntimeException("Number of locked monitors = " +
140                     monitors.length +
141                     " not matched. Expected: " + OWNED_MONITORS);
142             }
143             MonitorInfo m = monitors[0];
144             StackTraceElement ste = m.getLockedStackFrame();
145             int depth = m.getLockedStackDepth();
146             StackTraceElement[] stacktrace = info.getStackTrace();
147             if (!ste.equals(stacktrace[depth])) {
148                 System.out.println("LockedStackFrame:- " + ste);
149                 System.out.println("StackTrace at " + depth + " :-" +
150                     stacktrace[depth]);
151                 throw new RuntimeException("LockedStackFrame does not match " +
152                     "stack frame in ThreadInfo.getStackTrace");
153            }
154 
155            String className = lock.getClass().getName();
156            int hcode = System.identityHashCode(lock);
157            if (!className.equals(m.getClassName()) ||
158                    hcode != m.getIdentityHashCode() ||
159                    !m.getLockedStackFrame().getMethodName().equals("run")) {
160                 System.out.println(info);
161                 throw new RuntimeException("MonitorInfo " + m +
162                    " doesn't match.");
163             }
164 
165             LockInfo[] syncs = info.getLockedSynchronizers();
166             if (syncs.length != OWNED_SYNCS) {
167                 throw new RuntimeException("Number of locked syncs = " +
168                         syncs.length + " not matched. Expected: " + OWNED_SYNCS);
169             }
170             AbstractOwnableSynchronizer s = mutex.getSync();
171             String lockName = s.getClass().getName();
172             hcode = System.identityHashCode(s);
173             if (!lockName.equals(syncs[0].getClassName())) {
174                 throw new RuntimeException("LockInfo : " + syncs[0] +
175                     " class name not matched. Expected: " + lockName);
176             }
177             if (hcode != syncs[0].getIdentityHashCode()) {
178                 throw new RuntimeException("LockInfo: " + syncs[0] +
179                     " IdentityHashCode not matched. Expected: " + hcode);
180             }
181             LockInfo li = info.getLockInfo();
182             if (li == null) {
183                 throw new RuntimeException("Expected non-null LockInfo");
184             }
185         }
186     }
187     static class Mutex implements Lock, java.io.Serializable {
188 
189         // Our internal helper class
190         class Sync extends AbstractQueuedSynchronizer {
191             // Report whether in locked state
isHeldExclusively()192             protected boolean isHeldExclusively() {
193                 return getState() == 1;
194             }
195 
196             // Acquire the lock if state is zero
tryAcquire(int acquires)197             public boolean tryAcquire(int acquires) {
198                 assert acquires == 1; // Otherwise unused
199                 if (compareAndSetState(0, 1)) {
200                     setExclusiveOwnerThread(Thread.currentThread());
201                     return true;
202                 }
203                 return false;
204             }
205 
206             // Release the lock by setting state to zero
tryRelease(int releases)207             protected boolean tryRelease(int releases) {
208                 assert releases == 1; // Otherwise unused
209                 if (getState() == 0) throw new IllegalMonitorStateException();
210                 setExclusiveOwnerThread(null);
211                 setState(0);
212                 return true;
213             }
214 
215             // Provide a Condition
newCondition()216             Condition newCondition() { return new ConditionObject(); }
217 
218             // Deserialize properly
readObject(ObjectInputStream s)219             private void readObject(ObjectInputStream s)
220                 throws IOException, ClassNotFoundException {
221                 s.defaultReadObject();
222                 setState(0); // reset to unlocked state
223             }
224 
getLockOwner()225             protected Thread getLockOwner() {
226                 return getExclusiveOwnerThread();
227             }
228         }
229 
230         // The sync object does all the hard work. We just forward to it.
231         private final Sync sync = new Sync();
232 
lock()233         public void lock()                { sync.acquire(1); }
tryLock()234         public boolean tryLock()          { return sync.tryAcquire(1); }
unlock()235         public void unlock()              { sync.release(1); }
newCondition()236         public Condition newCondition()   { return sync.newCondition(); }
isLocked()237         public boolean isLocked()         { return sync.isHeldExclusively(); }
hasQueuedThreads()238         public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
lockInterruptibly()239         public void lockInterruptibly() throws InterruptedException {
240             sync.acquireInterruptibly(1);
241         }
tryLock(long timeout, TimeUnit unit)242         public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
243             return sync.tryAcquireNanos(1, unit.toNanos(timeout));
244         }
245 
getLockOwner()246         public Thread getLockOwner()     { return sync.getLockOwner(); }
247 
getSync()248         public AbstractOwnableSynchronizer getSync() { return sync; }
249     }
250 }
251