1 /*
  2  * Copyright (c) 2006, 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.share.jdi;
 24 
 25 import java.io.*;
 26 import java.util.*;
 27 import com.sun.jdi.*;
 28 
 29 import nsk.share.Consts;
 30 
 31 /*
 32  *  Class is used as base debugger in tests for ThreadReference.ownedMonitorsAndFrames().
 33  *
 34  *  In all this test similar scenario is used:
 35  *      - debugger VM force debugge VM to create test thread which acquires different monitors
 36  *      - when test thread acquire all monitors debuggee save information about all acquired monitors in special array 'monitorsInfo'
 37  *      - debugger read data from 'monitorsInfo' and compare it with data returned by ThreadReference.ownedMonitorsAndFrames()
 38  */
 39 public class OwnedMonitorsDebugger extends TestDebuggerType2 {
 40 
 41     /*
 42      * debug data about monitor acquired by debuggee test thread,
 43      * intended to compare with data returned by ThreadReference.ownedMonitorsAndFrames
 44      */
 45     public static class DebugMonitorInfo {
 46         // create DebugMonitorInfo using mirror of instance of nsk.share.locks.LockingThread.DebugMonitorInfo
 47         DebugMonitorInfo(ObjectReference debuggeeMirror) {
 48             monitor = (ObjectReference) debuggeeMirror.getValue(debuggeeMirror.referenceType().fieldByName("monitor"));
 49             stackDepth = ((IntegerValue) debuggeeMirror.getValue(debuggeeMirror.referenceType().fieldByName("stackDepth"))).intValue();
 50             thread = (ThreadReference) debuggeeMirror.getValue(debuggeeMirror.referenceType().fieldByName("thread"));
 51         }
 52 
 53         public DebugMonitorInfo(ObjectReference monitor, int stackDepth, ThreadReference thread) {
 54             this.monitor = monitor;
 55             this.stackDepth = stackDepth;
 56             this.thread = thread;
 57         }
 58 
 59         public ObjectReference monitor;
 60 
 61         public int stackDepth;
 62 
 63         public ThreadReference thread;
 64     }
 65 
 66     public static void main(String argv[]) {
 67         System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE);
 68     }
 69 
 70     public static int run(String argv[], PrintStream out) {
 71         return new OwnedMonitorsDebugger().runIt(argv, out);
 72     }
 73 
 74     protected boolean canRunTest() {
 75         if (!vm.canGetMonitorFrameInfo()) {
 76             log.display("TEST CANCELED due to: vm.canGetMonitorFrameInfo() = false");
 77             return false;
 78         } else
 79             return super.canRunTest();
 80     }
 81 
 82     protected String debuggeeClassName() {
 83         return OwnedMonitorsDebuggee.class.getName();
 84     }
 85 
 86     // read debuggee's array 'monitorsInfo' containing information about acquired monitors
 87     protected List<DebugMonitorInfo> getDebugMonitorsInfo() {
 88         List<DebugMonitorInfo> result = new ArrayList<DebugMonitorInfo>();
 89 
 90         ReferenceType referenceType = debuggee.classByName(debuggeeClassNameWithoutArgs());
 91         ArrayReference monitorsInfo = (ArrayReference) referenceType.getValue(referenceType.fieldByName("monitorsInfo"));
 92 
 93         for (int i = 0; i < monitorsInfo.length(); i++)
 94             result.add(new DebugMonitorInfo((ObjectReference) monitorsInfo.getValue(i)));
 95 
 96         return result;
 97     }
 98 
 99     private boolean compare(MonitorInfo actual, DebugMonitorInfo expected) {
100         boolean success = true;
101 
102         if (actual.stackDepth() != expected.stackDepth) {
103             setSuccess(false);
104             success = false;
105             log.complain("Expected and actual monitor(" + actual.monitor() + ") stack depth differs, expected: " + expected.stackDepth + " actual: "
106                     + actual.stackDepth());
107         }
108 
109         if (!actual.thread().equals(expected.thread)) {
110             setSuccess(false);
111             success = false;
112             log.complain("Expected and actual monitor(" + actual.monitor() + " thread differs, expected: " + expected.thread + " actual: "
113                     + actual.thread());
114         }
115 
116         return success;
117     }
118 
119     protected void compare(List<MonitorInfo> actualData, List<DebugMonitorInfo> expectedData) {
120         boolean success = true;
121 
122         // compare total amount of monitors
123         if (actualData.size() != expectedData.size()) {
124             setSuccess(false);
125             success = false;
126             log.complain("Number of expected monitors and actual ones differs");
127             log.complain("Expected: " + expectedData.size() + ", actual: " + actualData.size());
128         }
129 
130         // check that all expected monitors are contained in 'actualData'
131         for (DebugMonitorInfo expectedMonitorInfo : expectedData) {
132             boolean isMonitorFound = false;
133 
134             for (MonitorInfo actualMonitorInfo : actualData) {
135                 if (expectedMonitorInfo.monitor.equals(actualMonitorInfo.monitor())) {
136                     isMonitorFound = true;
137 
138                     if (!compare(actualMonitorInfo, expectedMonitorInfo))
139                         success = false;
140 
141                     break;
142                 }
143             }
144 
145             if (!isMonitorFound) {
146                 setSuccess(false);
147                 success = false;
148                 log.complain("Expected monitor not found in result of ownedMonitorsAndFrames(): " + expectedMonitorInfo.monitor);
149             }
150         }
151 
152         // check that all monitors from 'actualData' are contained in
153         // 'expectedData'
154         for (MonitorInfo actualMonitorInfo : actualData) {
155             boolean isMonitorFound = false;
156 
157             for (DebugMonitorInfo expectedMonitorInfo : expectedData) {
158                 if (actualMonitorInfo.monitor().equals(expectedMonitorInfo.monitor)) {
159                     isMonitorFound = true;
160                     break;
161                 }
162             }
163 
164             if (!isMonitorFound) {
165                 setSuccess(false);
166                 success = false;
167                 log.complain("Unexpected monitor in result of ownedMonitorsAndFrames(): " + actualMonitorInfo.monitor() + " Depth: "
168                         + actualMonitorInfo.stackDepth() + " Thread: " + actualMonitorInfo.thread());
169             }
170         }
171 
172         if (!success)
173             logDebugInfo(actualData, expectedData);
174     }
175 
176     private void logDebugInfo(List<MonitorInfo> actualData, List<DebugMonitorInfo> expectedData) {
177         log.display("ACTUAL MONITORS (total " + actualData.size() + "):");
178 
179         ThreadReference thread = null;
180 
181         for (MonitorInfo monitorInfo : actualData) {
182             log.display("Monitor: " + monitorInfo.monitor());
183             log.display("Depth: " + monitorInfo.stackDepth());
184             log.display("Thread: " + monitorInfo.thread());
185             if (thread == null)
186                 thread =  monitorInfo.thread();
187         }
188 
189         log.display("EXPECTED MONITORS (total " + expectedData.size() + "):");
190 
191         for (DebugMonitorInfo monitorInfo : expectedData) {
192             log.display("Monitor: " + monitorInfo.monitor);
193             log.display("Depth: " + monitorInfo.stackDepth);
194             log.display("Thread: " + monitorInfo.thread);
195             if (thread == null)
196                 thread =  monitorInfo.thread;
197         }
198 
199         if (thread != null) {
200             try {
201                 log.display("Thread frames:");
202                 for (StackFrame frame : thread.frames()) {
203                     Location location = frame.location();
204                     log.display(location.declaringType().name() + "." + location.method().name() + ", line: " + location.lineNumber());
205                 }
206             } catch (Exception e) {
207                 unexpectedException(e);
208             }
209         }
210     }
211 
212     /*
213      * Check that ThreadReference.ownedMonitorsAndFrames() returns correct
214      * data before calling this method debuggee should save information about
215      * acquired monitors in special array 'monitorsInfo',
216      * debugger forces debuggee to do it using command 'COMMAND_UPDATE_MONITOR_INFO'
217      */
218     protected void checkMonitorInfo(ThreadReference threadReference) {
219         List<MonitorInfo> actualData = null;
220 
221         try {
222             actualData = threadReference.ownedMonitorsAndFrames();
223         } catch (Exception e) {
224             setSuccess(false);
225             log.complain("Unexpected exception: " + e);
226             e.printStackTrace(log.getOutStream());
227         }
228 
229         List<DebugMonitorInfo> expectedData = getDebugMonitorsInfo();
230 
231         compare(actualData, expectedData);
232     }
233 
234 }