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