1 /* 2 * Copyright (c) 2001, 2011, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This source code is provided to illustrate the usage of a given feature 28 * or technique and has been deliberately simplified. Additional steps 29 * required for a production-quality application, such as security checks, 30 * input validation and proper error handling, might not be present in 31 * this sample code. 32 */ 33 34 35 package com.sun.tools.example.trace; 36 37 import com.sun.jdi.VirtualMachine; 38 import com.sun.jdi.Bootstrap; 39 import com.sun.jdi.connect.*; 40 41 import java.util.Map; 42 import java.util.List; 43 44 import java.io.PrintWriter; 45 import java.io.FileWriter; 46 import java.io.IOException; 47 48 /** 49 * This program traces the execution of another program. 50 * See "java Trace -help". 51 * It is a simple example of the use of the Java Debug Interface. 52 * 53 * @author Robert Field 54 */ 55 public class Trace { 56 57 // Running remote VM 58 private final VirtualMachine vm; 59 60 // Thread transferring remote error stream to our error stream 61 private Thread errThread = null; 62 63 // Thread transferring remote output stream to our output stream 64 private Thread outThread = null; 65 66 // Mode for tracing the Trace program (default= 0 off) 67 private int debugTraceMode = 0; 68 69 // Do we want to watch assignments to fields 70 private boolean watchFields = false; 71 72 // Class patterns for which we don't want events 73 private String[] excludes = {"java.*", "javax.*", "sun.*", 74 "com.sun.*"}; 75 76 /** 77 * main 78 */ 79 public static void main(String[] args) { 80 new Trace(args); 81 } 82 83 /** 84 * Parse the command line arguments. 85 * Launch target VM. 86 * Generate the trace. 87 */ 88 Trace(String[] args) { 89 PrintWriter writer = new PrintWriter(System.out); 90 int inx; 91 for (inx = 0; inx < args.length; ++inx) { 92 String arg = args[inx]; 93 if (arg.charAt(0) != '-') { 94 break; 95 } 96 if (arg.equals("-output")) { 97 try { 98 writer = new PrintWriter(new FileWriter(args[++inx])); 99 } catch (IOException exc) { 100 System.err.println("Cannot open output file: " + args[inx] 101 + " - " + exc); 102 System.exit(1); 103 } 104 } else if (arg.equals("-all")) { 105 excludes = new String[0]; 106 } else if (arg.equals("-fields")) { 107 watchFields = true; 108 } else if (arg.equals("-dbgtrace")) { 109 debugTraceMode = Integer.parseInt(args[++inx]); 110 } else if (arg.equals("-help")) { 111 usage(); 112 System.exit(0); 113 } else { 114 System.err.println("No option: " + arg); 115 usage(); 116 System.exit(1); 117 } 118 } 119 if (inx >= args.length) { 120 System.err.println("<class> missing"); 121 usage(); 122 System.exit(1); 123 } 124 StringBuilder sb = new StringBuilder(); 125 sb.append(args[inx]); 126 for (++inx; inx < args.length; ++inx) { 127 sb.append(' '); 128 sb.append(args[inx]); 129 } 130 vm = launchTarget(sb.toString()); 131 generateTrace(writer); 132 } 133 134 135 /** 136 * Generate the trace. 137 * Enable events, start thread to display events, 138 * start threads to forward remote error and output streams, 139 * resume the remote VM, wait for the final event, and shutdown. 140 */ 141 void generateTrace(PrintWriter writer) { 142 vm.setDebugTraceMode(debugTraceMode); 143 EventThread eventThread = new EventThread(vm, excludes, writer); 144 eventThread.setEventRequests(watchFields); 145 eventThread.start(); 146 redirectOutput(); 147 vm.resume(); 148 149 // Shutdown begins when event thread terminates 150 try { 151 eventThread.join(); 152 errThread.join(); // Make sure output is forwarded 153 outThread.join(); // before we exit 154 } catch (InterruptedException exc) { 155 // we don't interrupt 156 } 157 writer.close(); 158 } 159 160 /** 161 * Launch target VM. 162 * Forward target's output and error. 163 */ 164 VirtualMachine launchTarget(String mainArgs) { 165 LaunchingConnector connector = findLaunchingConnector(); 166 Map<String, Connector.Argument> arguments = 167 connectorArguments(connector, mainArgs); 168 try { 169 return connector.launch(arguments); 170 } catch (IOException exc) { 171 throw new Error("Unable to launch target VM: " + exc); 172 } catch (IllegalConnectorArgumentsException exc) { 173 throw new Error("Internal error: " + exc); 174 } catch (VMStartException exc) { 175 throw new Error("Target VM failed to initialize: " + 176 exc.getMessage()); 177 } 178 } 179 180 void redirectOutput() { 181 Process process = vm.process(); 182 183 // Copy target's output and error to our output and error. 184 errThread = new StreamRedirectThread("error reader", 185 process.getErrorStream(), 186 System.err); 187 outThread = new StreamRedirectThread("output reader", 188 process.getInputStream(), 189 System.out); 190 errThread.start(); 191 outThread.start(); 192 } 193 194 /** 195 * Find a com.sun.jdi.CommandLineLaunch connector 196 */ 197 LaunchingConnector findLaunchingConnector() { 198 List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors(); 199 for (Connector connector : connectors) { 200 if (connector.name().equals("com.sun.jdi.CommandLineLaunch")) { 201 return (LaunchingConnector)connector; 202 } 203 } 204 throw new Error("No launching connector"); 205 } 206 207 /** 208 * Return the launching connector's arguments. 209 */ 210 Map<String, Connector.Argument> connectorArguments(LaunchingConnector connector, String mainArgs) { 211 Map<String, Connector.Argument> arguments = connector.defaultArguments(); 212 Connector.Argument mainArg = 213 (Connector.Argument)arguments.get("main"); 214 if (mainArg == null) { 215 throw new Error("Bad launching connector"); 216 } 217 mainArg.setValue(mainArgs); 218 219 if (watchFields) { 220 // We need a VM that supports watchpoints 221 Connector.Argument optionArg = 222 (Connector.Argument)arguments.get("options"); 223 if (optionArg == null) { 224 throw new Error("Bad launching connector"); 225 } 226 optionArg.setValue("-classic"); 227 } 228 return arguments; 229 } 230 231 /** 232 * Print command line usage help 233 */ 234 void usage() { 235 System.err.println("Usage: java Trace <options> <class> <args>"); 236 System.err.println("<options> are:"); 237 System.err.println( 238 " -output <filename> Output trace to <filename>"); 239 System.err.println( 240 " -all Include system classes in output"); 241 System.err.println( 242 " -help Print this help message"); 243 System.err.println("<class> is the program to trace"); 244 System.err.println("<args> are the arguments to <class>"); 245 } 246 }