/*
* Copyright (c) 2008-2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.btrace.client;
import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import sun.misc.Signal;
import sun.misc.SignalHandler;
import com.sun.btrace.CommandListener;
import com.sun.btrace.comm.Command;
import com.sun.btrace.comm.DataCommand;
import com.sun.btrace.comm.ErrorCommand;
import com.sun.btrace.comm.ExitCommand;
import com.sun.btrace.comm.PrintableCommand;
import com.sun.btrace.util.Messages;
/**
* This is the main class for a simple command line
* BTrace client. It is possible to create a GUI
* client using the Client class.
*
* @author A. Sundararajan
*/
public final class Main {
public static volatile boolean exiting;
private static boolean DEBUG;
private static boolean TRUSTED;
private static boolean DUMP_CLASSES;
private static String OUTPUT_FILE;
private static String DUMP_DIR;
private static String PROBE_DESC_PATH;
public static final boolean TRACK_RETRANSFORM;
public static final int BTRACE_DEFAULT_PORT = 2020;
private static final Console con;
private static final PrintWriter out;
static {
DEBUG = Boolean.getBoolean("com.sun.btrace.debug");
if (isDebug()) debugPrint("btrace debug mode is set");
TRACK_RETRANSFORM = Boolean.getBoolean("com.sun.btrace.trackRetransforms");
if (isDebug() && TRACK_RETRANSFORM) debugPrint("trackRetransforms flag is set");
TRUSTED = Boolean.getBoolean("com.sun.btrace.unsafe");
TRUSTED |= Boolean.getBoolean("com.sun.btrace.trusted");
if (isDebug() && TRUSTED) debugPrint("btrace trusted mode is set");
DUMP_CLASSES = Boolean.getBoolean("com.sun.btrace.dumpClasses");
if (isDebug() && DUMP_CLASSES) debugPrint("dumpClasses flag is set");
DUMP_DIR = System.getProperty("com.sun.btrace.dumpDir", ".");
if (DUMP_CLASSES) {
if (isDebug()) debugPrint("dumpDir is " + DUMP_DIR);
}
PROBE_DESC_PATH = System.getProperty("com.sun.btrace.probeDescPath", ".");
con = System.console();
out = getOutWriter(con);
}
@SuppressWarnings("DefaultCharset")
private static PrintWriter getOutWriter(Console con) {
return (con != null)? con.writer() : new PrintWriter(System.out);
}
public static void main(String[] args) {
int port = BTRACE_DEFAULT_PORT;
String classPath = ".";
String includePath = null;
int count = 0;
boolean portDefined = false;
boolean classpathDefined = false;
boolean includePathDefined = false;
String statsdDef = "";
OUTER:
for (String arg : args) {
switch (arg) {
case "-v":
DEBUG = true;
break OUTER;
case "--version":
System.out.println(Messages.get("btrace.version"));
return;
}
}
if (args.length < 2) {
usage();
}
for (;;) {
if (args[count].charAt(0) == '-') {
if (args.length <= count+1) {
usage();
}
if (args[count].equals("-p") && !portDefined) {
try {
port = Integer.parseInt(args[++count]);
if (isDebug()) debugPrint("accepting port " + port);
} catch (NumberFormatException nfe) {
usage();
}
portDefined = true;
} else if (args[count].equals("-u")) {
TRUSTED = true;
if (isDebug()) debugPrint("btrace trusted mode is set");
} else if (args[count].equals("-o")) {
OUTPUT_FILE = args[++count];
if (isDebug()) debugPrint("outputFile is " + OUTPUT_FILE);
} else if (args[count].equals("-d")) {
DUMP_CLASSES = true;
DUMP_DIR = args[++count];
if (isDebug()) debugPrint("dumpDir is " + DUMP_DIR);
} else if (args[count].equals("-pd")) {
PROBE_DESC_PATH = args[++count];
if (isDebug()) debugPrint("probeDescDir is " + PROBE_DESC_PATH);
} else if ((args[count].equals("-cp") ||
args[count].equals("-classpath"))
&& !classpathDefined) {
classPath = args[++count];
if (isDebug()) debugPrint("accepting classpath " + classPath);
classpathDefined = true;
} else if (args[count].equals("-I") && !includePathDefined) {
includePath = args[++count];
if (isDebug()) debugPrint("accepting include path " + includePath);
includePathDefined = true;
} else if (args[count].equals("-statsd")) {
statsdDef = args[++count];
} else if (args[count].equals("-v")) {
// already processed
} else {
usage();
}
count++;
if (count >= args.length) {
break;
}
} else {
break;
}
}
if (! portDefined) {
if (isDebug()) debugPrint("assuming default port " + port);
}
if (! classpathDefined) {
if (isDebug()) debugPrint("assuming default classpath '" + classPath + "'");
}
if (args.length < (count + 1)) {
usage();
}
String pid = args[count];
String fileName = args[count + 1];
String[] btraceArgs = new String[args.length - count];
if (btraceArgs.length > 0) {
System.arraycopy(args, count, btraceArgs, 0, btraceArgs.length);
}
try {
Client client = new Client(port, OUTPUT_FILE, PROBE_DESC_PATH,
DEBUG, TRACK_RETRANSFORM, TRUSTED, DUMP_CLASSES, DUMP_DIR, statsdDef);
if (! new File(fileName).exists()) {
errorExit("File not found: " + fileName, 1);
}
byte[] code = client.compile(fileName, classPath, includePath);
if (code == null) {
errorExit("BTrace compilation failed", 1);
}
client.attach(pid, null, classPath);
registerExitHook(client);
if (con != null) {
registerSignalHandler(client);
}
if (isDebug()) debugPrint("submitting the BTrace program");
client.submit(fileName, code, btraceArgs,
createCommandListener(client));
} catch (IOException exp) {
errorExit(exp.getMessage(), 1);
}
}
private static CommandListener createCommandListener(Client client) {
return new CommandListener() {
@Override
public void onCommand(Command cmd) throws IOException {
int type = cmd.getType();
if (cmd instanceof PrintableCommand) {
((PrintableCommand)cmd).print(out);
out.flush();
} else if (type == Command.EXIT) {
exiting = true;
out.flush();
ExitCommand ecmd = (ExitCommand)cmd;
System.exit(ecmd.getExitCode());
}
}
};
}
private static void registerExitHook(final Client client) {
if (isDebug()) debugPrint("registering shutdown hook");
Runtime.getRuntime().addShutdownHook(new Thread(
new Runnable() {
@Override
public void run() {
if (! exiting) {
try {
if (isDebug()) debugPrint("sending exit command");
client.sendExit(0);
} catch (IOException ioexp) {
if (isDebug()) debugPrint(ioexp.toString());
}
}
}
}));
}
private static void registerSignalHandler(final Client client) {
if (isDebug()) debugPrint("registering signal handler for SIGINT");
Signal.handle(new Signal("INT"),
new SignalHandler() {
@Override
public void handle(Signal sig) {
try {
con.printf("Please enter your option:\n");
con.printf("\t1. exit\n\t2. send an event\n\t3. send a named event\n\t4. flush console output\n");
con.flush();
String option = con.readLine();
option = option.trim();
if (option == null) {
return;
}
switch (option) {
case "1":
System.exit(0);
case "2":
if (isDebug()) debugPrint("sending event command");
client.sendEvent();
break;
case "3":
con.printf("Please enter the event name: ");
String name = con.readLine();
if (name != null) {
if (isDebug()) debugPrint("sending event command");
client.sendEvent(name);
}
break;
case "4":
out.flush();
break;
default:
con.printf("invalid option!\n");
break;
}
} catch (IOException ioexp) {
if (isDebug()) debugPrint(ioexp.toString());
}
}
});
}
private static void usage() {
System.err.println(Messages.get("btrace.usage"));
System.exit(1);
}
private static boolean isDebug() {
return DEBUG;
}
private static boolean isUnsafe() {
return TRUSTED;
}
private static void debugPrint(String msg) {
System.out.println("DEBUG: " + msg);
}
private static void errorExit(String msg, int code) {
exiting = true;
System.err.println(msg);
System.exit(code);
}
}