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      */
  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 }