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