1 /*
  2  * Copyright (c) 2014 SAP SE. All rights reserved.
  3  * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  */
 24 
 25 import java.io.BufferedReader;
 26 import java.io.IOException;
 27 import java.io.InputStream;
 28 import java.io.InputStreamReader;
 29 import java.util.Iterator;
 30 import java.util.List;
 31 import java.util.Map;
 32 
 33 import com.sun.jdi.AbsentInformationException;
 34 import com.sun.jdi.Bootstrap;
 35 import com.sun.jdi.LocalVariable;
 36 import com.sun.jdi.Location;
 37 import com.sun.jdi.ObjectReference;
 38 import com.sun.jdi.ReferenceType;
 39 import com.sun.jdi.StackFrame;
 40 import com.sun.jdi.ThreadReference;
 41 import com.sun.jdi.Value;
 42 import com.sun.jdi.VirtualMachine;
 43 import com.sun.jdi.connect.Connector;
 44 import com.sun.jdi.connect.Connector.Argument;
 45 import com.sun.jdi.connect.IllegalConnectorArgumentsException;
 46 import com.sun.jdi.connect.LaunchingConnector;
 47 import com.sun.jdi.connect.VMStartException;
 48 import com.sun.jdi.event.BreakpointEvent;
 49 import com.sun.jdi.event.ClassPrepareEvent;
 50 import com.sun.jdi.event.Event;
 51 import com.sun.jdi.event.EventQueue;
 52 import com.sun.jdi.event.EventSet;
 53 import com.sun.jdi.event.VMDeathEvent;
 54 import com.sun.jdi.event.VMDisconnectEvent;
 55 import com.sun.jdi.event.VMStartEvent;
 56 import com.sun.jdi.request.BreakpointRequest;
 57 import com.sun.jdi.request.ClassPrepareRequest;
 58 import com.sun.jdi.request.EventRequestManager;
 59 
 60 import jdk.test.lib.Utils;
 61 
 62 /*
 63  * @test GetObjectLockCount.java
 64  * @bug 8036666
 65  * @summary verify jvm returns correct lock recursion count
 66  * @requires vm.jvmti
 67  * @library /test/lib
 68  * @run compile -g RecursiveObjectLock.java
 69  * @run main/othervm GetObjectLockCount
 70  * @author axel.siebenborn@sap.com
 71  */
 72 
 73 public class GetObjectLockCount {
 74 
 75     public static final String CLASS_NAME  = "RecursiveObjectLock";
 76     public static final String METHOD_NAME = "breakpoint1";
 77 
 78     /**
 79      * Find a com.sun.jdi.CommandLineLaunch connector
 80      */
 81     static LaunchingConnector findLaunchingConnector() {
 82         List <Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
 83         Iterator <Connector> iter = connectors.iterator();
 84         while (iter.hasNext()) {
 85             Connector connector = iter.next();
 86             if (connector.name().equals("com.sun.jdi.CommandLineLaunch")) {
 87                 return (LaunchingConnector)connector;
 88             }
 89         }
 90         throw new Error("No launching connector");
 91     }
 92 
 93     static VirtualMachine launchTarget(String mainArgs) {
 94         LaunchingConnector connector = findLaunchingConnector();
 95         Map<String, Argument>  arguments = connectorArguments(connector, mainArgs);
 96         try {
 97             return (VirtualMachine) connector.launch(arguments);
 98         } catch (IOException exc) {
 99             throw new Error("Unable to launch target VM: " + exc);
100         } catch (IllegalConnectorArgumentsException exc) {
101             throw new Error("Internal error: " + exc);
102         } catch (VMStartException exc) {
103             throw new Error("Target VM failed to initialize: " +
104                     exc.getMessage());
105         }
106     }
107     /**
108      * Return the launching connector's arguments.
109      */
110     static Map <String,Connector.Argument> connectorArguments(LaunchingConnector connector, String mainArgs) {
111         Map<String,Connector.Argument> arguments = connector.defaultArguments();
112 
113         Connector.Argument mainArg = (Connector.Argument)arguments.get("main");
114         if (mainArg == null) {
115             throw new Error("Bad launching connector");
116         }
117         mainArg.setValue(mainArgs);
118 
119         Connector.Argument optionsArg = (Connector.Argument)arguments.get("options");
120         if (optionsArg == null) {
121             throw new Error("Bad launching connector");
122         }
123         optionsArg.setValue(String.join(" ", Utils.getTestJavaOpts()));
124         return arguments;
125     }
126 
127     private static void addClassWatch(VirtualMachine vm) {
128         EventRequestManager erm = vm.eventRequestManager();
129         ClassPrepareRequest classPrepareRequest = erm
130                 .createClassPrepareRequest();
131         classPrepareRequest.addClassFilter(CLASS_NAME);
132         classPrepareRequest.setEnabled(true);
133     }
134 
135     private static void addBreakpoint(VirtualMachine vm, ReferenceType refType) {
136         Location breakpointLocation = null;
137         List<Location> locs;
138         try {
139             locs = refType.allLineLocations();
140             for (Location loc: locs) {
141                 if (loc.method().name().equals(METHOD_NAME)) {
142                     breakpointLocation = loc;
143                     break;
144                 }
145             }
146         } catch (AbsentInformationException e) {
147             // TODO Auto-generated catch block
148             e.printStackTrace();
149         }
150         if (breakpointLocation != null) {
151             EventRequestManager evtReqMgr = vm.eventRequestManager();
152             BreakpointRequest bReq = evtReqMgr.createBreakpointRequest(breakpointLocation);
153             bReq.setSuspendPolicy(BreakpointRequest.SUSPEND_ALL);
154             bReq.enable();
155         }
156     }
157 
158     /**
159      * @param args
160      * @throws InterruptedException
161      */
162     public static void main(String[] args) throws InterruptedException  {
163 
164         VirtualMachine vm = launchTarget(CLASS_NAME);
165 
166         // process events
167         EventQueue eventQueue = vm.eventQueue();
168         // resume the vm
169         boolean launched = false;
170 
171         while (!launched) {
172             EventSet eventSet = eventQueue.remove();
173             for (Event event : eventSet) {
174                 if (event instanceof VMStartEvent) {
175                     System.out.println("Vm launched");
176                     // set watch field on already loaded classes
177                     List<ReferenceType> referenceTypes = vm.classesByName(CLASS_NAME);
178                     for (ReferenceType refType : referenceTypes) {
179                         System.out.println("Found Class");
180                         addBreakpoint(vm, refType);
181                     }
182 
183                     // watch for loaded classes
184                     addClassWatch(vm);
185                     vm.resume();
186                     launched = true;
187                 }
188             }
189         }
190 
191         Process process = vm.process();
192 
193         // Copy target's output and error to our output and error.
194         Thread outThread = new StreamRedirectThread("out reader", process.getInputStream());
195         Thread errThread = new StreamRedirectThread("error reader", process.getErrorStream());
196 
197         int recursionCount = -1;
198 
199         errThread.start();
200         outThread.start();
201         boolean connected = true;
202         while (connected) {
203             EventSet eventSet = eventQueue.remove();
204             for (Event event : eventSet) {
205                 if (event instanceof VMDeathEvent || event instanceof VMDisconnectEvent) {
206                     // exit
207                     connected = false;
208                 }
209                 else if (event instanceof ClassPrepareEvent) {
210                     // watch field on loaded class
211                     System.out.println("ClassPrepareEvent");
212                     ClassPrepareEvent classPrepEvent = (ClassPrepareEvent) event;
213                     ReferenceType refType = classPrepEvent.referenceType();
214                     addBreakpoint(vm, refType);
215                 } else if (event instanceof BreakpointEvent) {
216                     recursionCount = getLockRecursions(vm);
217                     System.out.println("resume...");
218                 }
219             }
220             eventSet.resume();
221         }
222         // Shutdown begins when event thread terminates
223         try {
224             errThread.join(); // Make sure output is forwarded
225             outThread.join();
226         } catch (InterruptedException e) {
227             // we don't interrupt
228             e.printStackTrace();
229         }
230         if (recursionCount != 3) {
231             throw new AssertionError("recursions: expected 3, but was " + recursionCount);
232         }
233     }
234 
235     public static int getLockRecursions(VirtualMachine vm) {
236         List <ThreadReference> threads = vm.allThreads();
237         for (ThreadReference thread : threads) {
238             if (thread.name().equals("main")) {
239 
240                 System.out.println("Found main thread.");
241                 try{
242                     StackFrame frame = thread.frame(3);
243                     return frame.thisObject().entryCount();
244                 } catch (Exception e) {
245                     e.printStackTrace();
246                 }
247             }
248             System.out.println("Main thread not found!");
249         }
250         return -1;
251     }
252 }
253 
254 class StreamRedirectThread extends Thread {
255 
256     private final BufferedReader in;
257 
258     private static final int BUFFER_SIZE = 2048;
259 
260     /**
261      * Set up for copy.
262      * @param name  Name of the thread
263      * @param in    Stream to copy from
264      */
265     StreamRedirectThread(String name, InputStream in) {
266         super(name);
267         this.in = new BufferedReader(new InputStreamReader(in));
268     }
269 
270     /**
271      * Copy.
272      */
273     public void run() {
274         try {
275             String line;
276             while ((line = in.readLine ()) != null) {
277                 System.out.println("testvm: " + line);
278             }
279             System.out.flush();
280         } catch(IOException exc) {
281             System.err.println("Child I/O Transfer - " + exc);
282             exc.printStackTrace();
283         }
284     }
285 }