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  * @key regression
64  * @summary verify jvm returns correct lock recursion count
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      */
findLaunchingConnector()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 
launchTarget(String mainArgs)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      */
connectorArguments(LaunchingConnector connector, String mainArgs)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 
addClassWatch(VirtualMachine vm)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 
addBreakpoint(VirtualMachine vm, ReferenceType refType)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      */
main(String[] args)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 
getLockRecursions(VirtualMachine vm)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      */
StreamRedirectThread(String name, InputStream in)264     StreamRedirectThread(String name, InputStream in) {
265         super(name);
266         this.in = new BufferedReader(new InputStreamReader(in));
267     }
268 
269     /**
270      * Copy.
271      */
run()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 }
285