1 /*
2  * Copyright (c) 2007, 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 package nsk.monitoring.share.thread;
24 
25 import java.lang.management.ThreadMXBean;
26 import java.lang.management.ThreadInfo;
27 import java.lang.management.MonitorInfo;
28 import java.lang.management.LockInfo;
29 import nsk.share.log.Log;
30 import nsk.share.log.LogAware;
31 import nsk.share.TestFailure;
32 import java.util.Map;
33 import java.util.HashMap;
34 import java.util.concurrent.locks.ReentrantLock;
35 import java.util.concurrent.locks.Lock;
36 
37 /**
38  * Base class for all threads that are used in monitoring testing.
39  */
40 public abstract class ThreadMonitoringScenarioBase implements LogAware, ThreadMonitoringScenario {
41         protected static boolean lockedMonitorsAvailable = true;
42         protected static boolean lockedSynchronizersAvailable = true;
43         protected Log log;
44 
ThreadMonitoringScenarioBase(Log log)45         public ThreadMonitoringScenarioBase(Log log) {
46                 setLog(log);
47         }
48 
begin()49         public abstract void begin();
50 
waitState()51         public abstract void waitState();
52 
finish()53         public abstract void finish();
54 
end()55         public abstract void end();
56 
printThreadInfo(ThreadInfo info)57         protected void printThreadInfo(ThreadInfo info) {
58                 //ThreadUtils.threadDump(log, threadMXBean.dumpAllThreads(true, true));
59                 ThreadUtils.threadInfo(log, info);
60         }
61 
62 
63         /**
64          * Check that there are no unexpected elements in stack trace.
65          */
checkStackTrace(StackTraceElement[] elements)66         protected boolean checkStackTrace(StackTraceElement[] elements) {
67                 boolean unexpected = false;
68                 for (StackTraceElement element : elements)
69                         if (!isStackTraceElementExpected(element)) {
70                                 if (!unexpected) {
71                                         log.info("Unexpected stack trace elements for: " + this);
72                                         unexpected = true;
73                                 }
74                                 log.info(ThreadUtils.INDENT + "at " + element);
75                         }
76                 return !unexpected;
77         }
78 
79         /**
80          * Verifies that given stack trace element from stack trace is expected
81          * in pre-defined state. This method will be called by checkStackTrace
82          * for each element.
83          *
84          * @param element stack trace element
85          * @return true if element is expected, false otherwise
86          */
isStackTraceElementExpected(StackTraceElement element)87         protected boolean isStackTraceElementExpected(StackTraceElement element) {
88                 return false;
89         }
90 
91         /**
92          * Check that stack trace element is expected.
93          */
checkStackTraceElement(StackTraceElement element, String[] expectedMethods)94         protected boolean checkStackTraceElement(StackTraceElement element, String[] expectedMethods) {
95                 String name = element.getClassName() + "." + element.getMethodName();
96                 for (String method : expectedMethods)
97                         if (method.equals(name))
98                                 return true;
99                 return false;
100         }
101 
102         /**
103          * Check that lock info matches given lock object.
104          *
105          * @param lockInfo lock info
106          * @param lock lock object
107          */
checkLockInfo(LockInfo lockInfo, Object lock)108         protected void checkLockInfo(LockInfo lockInfo, Object lock) {
109                 if (lock != null) {
110                         verify(lockInfo.getClassName().equals(lock.getClass().getName()), "LockInfo.getClassName() = " + lockInfo.getClassName() + " differs from lock.getClass().getName() = " + lock.getClass().getName());
111                         verify(lockInfo.getIdentityHashCode() == System.identityHashCode(lock), "LockInfo.getIdentityHashCode() = " + lockInfo.getIdentityHashCode() + " differs from System.identityHashCode(lock) = " + System.identityHashCode(lock));
112                         String expectedToString = lock.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(lock));
113                         verify(lockInfo.toString().equals(expectedToString), "LockInfo.toString() = " + lockInfo.toString() + " differs from expected toString() = " + expectedToString);
114                 } else
115                         verify(lockInfo == null, "Unexpected ThreadInfo.getLockInfo(): " + ThreadUtils.strLockInfo(lockInfo));
116         }
117 
118         /**
119          * Check that given MonitorInfo matches given lock object and method name.
120          *
121          * @param monitorInfo monitor info
122          * @param lock lock object
123          * @param methodName method name
124          */
checkMonitorInfo(MonitorInfo monitorInfo, Object lock, String methodName)125         protected void checkMonitorInfo(MonitorInfo monitorInfo, Object lock, String methodName) {
126                 checkLockInfo(monitorInfo, lock);
127                 StackTraceElement element = monitorInfo.getLockedStackFrame();
128                 String expectedMethodName = element.getClassName() + '.' + element.getMethodName();
129                 verify(expectedMethodName.equals(methodName), "Unexpected method name in " + ThreadUtils.strMonitorInfo(monitorInfo) + " expected: " + methodName);
130         }
131 
132         /**
133          * Check that monitor info for all given method names and locks is present.
134          *
135          * @param monitorInfo array of monitor info to check
136          * @param lockMap map with method names as keys and locks as values
137          */
checkMonitorInfo(MonitorInfo[] monitorInfos, Map<String, Object[]> lockMap)138         protected void checkMonitorInfo(MonitorInfo[] monitorInfos, Map<String, Object[]> lockMap) {
139                 try {
140                         if (lockMap == null || !lockedMonitorsAvailable) {
141                                 verify(monitorInfos.length == 0, "Unexpected MonitorInfo[] objects: " + ThreadUtils.strMonitorInfoArr(monitorInfos));
142                         } else {
143                                 int n = 0;
144                                 // Check that each entry in the map has corresponding monitorInfo
145                                 for (Map.Entry<String, Object[]> entry : lockMap.entrySet()) {
146                                         String methodName = entry.getKey();
147                                         Object[] locks = entry.getValue();
148                                         n += locks.length;
149                                         for (Object lock : locks)
150                                                 checkMonitorInfo(monitorInfos, methodName, lock);
151                                 }
152                                 // Check that each monitorInfo entry corresponds to entry in lockMap
153                                 for (MonitorInfo monitorInfo : monitorInfos) {
154                                         StackTraceElement element = monitorInfo.getLockedStackFrame();
155                                         if (element == null)
156                                                 continue;
157                                         Object[] locks = lockMap.get(element.getMethodName());
158                                         checkMonitorInfo(monitorInfo, element.getMethodName(), locks);
159                                 }
160                                 verify(n == monitorInfos.length, "Unexpected monitor info array length: " + monitorInfos.length + " expected: " + n);
161                         }
162                 } catch (TestFailure t) {
163                         log.info("Expected monitor info for locks:");
164                         for (Map.Entry<String, Object[]> entry : lockMap.entrySet()) {
165                                 for (Object lock : entry.getValue()) {
166                                         String s = "";
167                                         s +=  "methodName: " + entry.getKey();
168                                         s += " className: " + lock.getClass().getName();
169                                         s += " identityHashCode: " + System.identityHashCode(lock);
170                                         log.info(s);
171                                 }
172                         }
173                         throw t;
174                 }
175         }
176 
177         /**
178          * Check that monitor info for given method name and lock is present.
179          *
180          * @param monitorInfos monitor info array
181          * @param methodName method name
182          * @param lock lock object
183          */
checkMonitorInfo(MonitorInfo[] monitorInfos, String methodName, Object lock)184         protected void checkMonitorInfo(MonitorInfo[] monitorInfos, String methodName, Object lock) {
185                 String className = lock.getClass().getName();
186                 int hashCode = System.identityHashCode(lock);
187                 for (MonitorInfo monitorInfo : monitorInfos) {
188                         if (className.equals(monitorInfo.getClassName()) &&
189                             hashCode == monitorInfo.getIdentityHashCode()) {
190                                 if (monitorInfo.getLockedStackFrame() == null)
191                                         return;
192                                 verify(methodName.equals(monitorInfo.getLockedStackFrame().getMethodName()), "Invalid method name: " + monitorInfo.getLockedStackFrame().getMethodName() + " expected: " + methodName);
193                                 return;
194                         }
195                 }
196                 throw new TestFailure("Expected monitor not found: methodName: " + methodName + " lock: " + lock);
197         }
198 
199         /**
200          * Check that monitor info for given method name corresponds to one of locks.
201          *
202          * @param monitorInfo monitor info
203          * @param methodName method name
204          * @param locks lock array
205          */
checkMonitorInfo(MonitorInfo monitorInfo, String methodName, Object[] locks)206         protected void checkMonitorInfo(MonitorInfo monitorInfo, String methodName, Object[] locks) {
207                 for (Object lock : locks) {
208                         String className = lock.getClass().getName();
209                         int hashCode = System.identityHashCode(lock);
210                         if (className.equals(monitorInfo.getClassName()) &&
211                             hashCode == monitorInfo.getIdentityHashCode() &&
212                             methodName.equals(monitorInfo.getLockedStackFrame().getMethodName()))
213                                 return;
214                 }
215                 throw new TestFailure("Lock for MonitorInfo not found: " + ThreadUtils.strMonitorInfo(monitorInfo));
216         }
217 
218         /**
219          * Check that lock info corresponds to given locks.
220          *
221          * We can only check number of items here.
222          *
223          * @param lockInfos lock info array
224          * @param lockMap lock map
225          */
checkSynchronizers(LockInfo[] lockInfos, Map<String, Lock[]> lockMap)226         protected void checkSynchronizers(LockInfo[] lockInfos, Map<String, Lock[]> lockMap) {
227                 if (lockMap == null || !lockedSynchronizersAvailable)
228                         verify(lockInfos.length == 0, "Unexpected LockInfo[] objects: " + ThreadUtils.strLockInfoArr(lockInfos));
229                 else {
230                         // Only check length
231                         int n = 0;
232                         for (Map.Entry<String, Lock[]> entry : lockMap.entrySet()) {
233                                 Lock[] locks = entry.getValue();
234                                 n += locks.length;
235                         }
236                         verify(lockInfos.length == n, "Unexpected LockInfo[] length: " + lockInfos.length + " expected: " + n);
237                 }
238         }
239 
240         /**
241          * Obtain full method name for given stack trace element.
242          *
243          * @param element stack trace element
244          * @return full method name, i.e. className.methodName
245          */
getMethodName(StackTraceElement element)246         protected String getMethodName(StackTraceElement element) {
247                 return element.getClassName() + '.' + element.getMethodName();
248         }
249 
250         /**
251          * Verify condition and throw TestFailure if it does not hold.
252          *
253          * @param condition boolean condition
254          * @param message TestFailure message
255          */
verify(boolean condition, String message)256         protected void verify(boolean condition, String message) {
257                 if (!condition)
258                         throw new TestFailure(message + " in: " + this);
259         }
260 
setLog(Log log)261         public final void setLog(Log log) {
262                 this.log = log;
263         }
264 }
265