package org.opensolaris.os.dtrace;
import java.io.*;
import java.util.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.swing.event.EventListenerList;
import java.util.logging.*;
public class LocalConsumer implements Consumer {
static Logger logger = Logger.getLogger(LocalConsumer.class.getName());
private static final int DTRACE_JNI_VERSION = 3;
private static final Option[] DEFAULT_OPTIONS = new Option[] {
new Option(Option.bufsize, Option.kb(256)),
new Option(Option.aggsize, Option.kb(256)),
};
private static native void _loadJniTable();
private static boolean debug;
private static int maxConsumers;
static {
LocalConsumer.configureLogging();
LocalConsumer.getConfigurationOptions();
Utility.loadLibrary("libdtrace_jni.so.1", debug);
_checkVersion(DTRACE_JNI_VERSION);
_setDebug(debug);
if (maxConsumers > 0) {
_setMaximumConsumers(maxConsumers);
}
_loadJniTable();
}
private static native void _checkVersion(int version);
private native void _open(OpenFlag[] flags) throws DTraceException;
private native Program _compileString(String program, String[] args)
throws DTraceException;
private native Program.File _compileFile(String path, String[] args)
throws DTraceException;
private native void _exec(Program program) throws DTraceException;
private native void _getProgramInfo(Program program)
throws DTraceException;
private native void _setOption(String option, String value)
throws DTraceException;
private native long _getOption(String option) throws DTraceException;
private native boolean _isEnabled();
private native void _checkProgramEnabling();
private native void _go() throws DTraceException;
private native void _stop() throws DTraceException;
private native void _consume() throws DTraceException;
private native void _interrupt();
private native void _close();
private native Aggregate _getAggregate(AggregateSpec spec)
throws DTraceException;
private native int _createProcess(String cmd) throws DTraceException;
private native void _grabProcess(int pid) throws DTraceException;
private native void _listProbes(List <ProbeDescription> probeList,
ProbeDescription filter);
private native void _listProbeDetail(List <Probe> probeList,
ProbeDescription filter);
private native void _listCompiledProbes(
List <ProbeDescription> probeList, Program program);
private native void _listCompiledProbeDetail(
List <Probe> probeList, Program program);
private static native String _getVersion();
private static native int _openCount();
private native void _destroy();
static native long _quantizeBucket(int i);
private native String _lookupKernelFunction(Number address);
private native String _lookupUserFunction(int pid, Number address);
private static native String _getExecutableName();
private static native void _setMaximumConsumers(int max);
private static native void _setDebug(boolean debug);
protected EventListenerList listenerList;
protected ExceptionHandler exceptionHandler;
private int _handle = -1;
private final Identifier id;
private enum State {
INIT,
OPEN,
COMPILED,
GO,
STARTED,
STOPPED,
CLOSED
}
private State state = State.INIT;
private boolean stopCalled;
private boolean abortCalled;
private Object consumerLock;
private Object stopLock;
private boolean workEnded;
private static int sequence = 0;
private static void
configureLogging()
{
logger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
logger.addHandler(handler);
logger.setLevel(Level.OFF);
}
private static Integer
getIntegerProperty(String name)
{
Integer value = null;
String property = System.getProperty(name);
if (property != null && property.length() != 0) {
try {
value = Integer.parseInt(property);
System.out.println(name + "=" + value);
} catch (NumberFormatException e) {
System.err.println("Warning: property ignored: " +
name + "=" + property);
}
}
return value;
}
private static void
getConfigurationOptions()
{
Integer property;
property = getIntegerProperty("JAVA_DTRACE_API_DEBUG");
if (property != null) {
debug = (property != 0);
}
property = getIntegerProperty("JAVA_DTRACE_MAX_CONSUMERS");
if (property != null) {
maxConsumers = property;
}
}
public
LocalConsumer()
{
id = new LocalConsumer.Identifier(this);
consumerLock = new Object();
stopLock = new Object();
listenerList = new EventListenerList();
}
private int
getHandle()
{
return _handle;
}
private void
setHandle(int n)
{
_handle = n;
}
public synchronized void
open(OpenFlag ... flags) throws DTraceException
{
if (state == State.CLOSED) {
throw new IllegalStateException("cannot reopen a closed consumer");
}
if (state != State.INIT) {
throw new IllegalStateException("consumer already open");
}
for (OpenFlag flag : flags) {
if (flag == null) {
throw new NullPointerException("open flag is null");
}
}
synchronized (LocalConsumer.class) {
_open(flags);
}
state = State.OPEN;
setOptions(DEFAULT_OPTIONS);
if (abortCalled) {
_interrupt();
}
if (logger.isLoggable(Level.INFO)) {
logger.info("consumer table count: " + _openCount());
}
}
private synchronized void
checkCompile()
{
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
case COMPILED:
break;
case GO:
case STARTED:
throw new IllegalStateException("go() already called");
case STOPPED:
throw new IllegalStateException("consumer stopped");
case CLOSED:
throw new IllegalStateException("consumer closed");
}
}
public synchronized Program
compile(String program, String ... macroArgs) throws DTraceException
{
if (program == null) {
throw new NullPointerException("program string is null");
}
checkCompile();
Program p = null;
String[] argv = null;
if (macroArgs != null) {
for (String macroArg : macroArgs) {
if (macroArg == null) {
throw new NullPointerException("macro argument is null");
}
}
argv = new String[macroArgs.length + 1];
synchronized (LocalConsumer.class) {
argv[0] = _getExecutableName();
}
System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length);
} else {
synchronized (LocalConsumer.class) {
argv = new String[] { _getExecutableName() };
}
}
synchronized (LocalConsumer.class) {
p = _compileString(program, argv);
}
p.consumerID = id;
p.contents = program;
p.validate();
state = State.COMPILED;
return p;
}
public synchronized Program
compile(File program, String ... macroArgs) throws DTraceException,
IOException, SecurityException
{
if (program == null) {
throw new NullPointerException("program file is null");
}
if (!program.canRead()) {
throw new FileNotFoundException("failed to open " +
program.getName());
}
checkCompile();
Program.File p = null;
String[] argv = null;
if (macroArgs != null) {
for (String macroArg : macroArgs) {
if (macroArg == null) {
throw new NullPointerException("macro argument is null");
}
}
argv = new String[macroArgs.length + 1];
argv[0] = program.getPath();
System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length);
} else {
macroArgs = new String[] { program.getPath() };
}
synchronized (LocalConsumer.class) {
p = _compileFile(program.getPath(), argv);
}
p.consumerID = id;
p.contents = Program.getProgramString(program);
p.file = program;
p.validate();
p.validateFile();
state = State.COMPILED;
return p;
}
private synchronized void
checkProgram(Program program)
{
if (program == null) {
throw new NullPointerException("program is null");
}
if (!id.equals(program.consumerID)) {
throw new IllegalArgumentException("program not compiled " +
"by this consumer");
}
}
public void
enable() throws DTraceException
{
enable(null);
}
public synchronized void
enable(Program program) throws DTraceException
{
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
throw new IllegalStateException("no compiled program");
case COMPILED:
break;
case GO:
case STARTED:
throw new IllegalStateException("go() already called");
case STOPPED:
throw new IllegalStateException("consumer stopped");
case CLOSED:
throw new IllegalStateException("consumer closed");
}
if (program != null) {
checkProgram(program);
}
synchronized (LocalConsumer.class) {
_exec(program);
}
}
public synchronized void
getProgramInfo(Program program) throws DTraceException
{
checkProgram(program);
if (state == State.CLOSED) {
throw new IllegalStateException("consumer closed");
}
assert ((state != State.INIT) && (state != State.OPEN));
synchronized (LocalConsumer.class) {
_getProgramInfo(program);
}
}
private void
setOptions(Option[] options) throws DTraceException
{
for (Option o : options) {
setOption(o.getName(), o.getValue());
}
}
public void
setOption(String option) throws DTraceException
{
setOption(option, Option.VALUE_SET);
}
public void
unsetOption(String option) throws DTraceException
{
setOption(option, Option.VALUE_UNSET);
}
public synchronized void
setOption(String option, String value) throws DTraceException
{
if (option == null) {
throw new NullPointerException("option is null");
}
if (value == null) {
throw new NullPointerException("option value is null");
}
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
case COMPILED:
case GO:
case STARTED:
case STOPPED:
break;
case CLOSED:
throw new IllegalStateException("consumer closed");
}
synchronized (LocalConsumer.class) {
_setOption(option, value);
}
}
public synchronized long
getOption(String option) throws DTraceException
{
if (option == null) {
throw new NullPointerException("option is null");
}
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
case COMPILED:
case GO:
case STARTED:
case STOPPED:
break;
case CLOSED:
throw new IllegalStateException("consumer closed");
}
long value;
synchronized (LocalConsumer.class) {
value = _getOption(option);
}
return value;
}
public final synchronized boolean
isOpen()
{
return ((state != State.INIT) && (state != State.CLOSED));
}
public final synchronized boolean
isEnabled()
{
if (state != State.COMPILED) {
return false;
}
return _isEnabled();
}
public final synchronized boolean
isRunning()
{
return (state == State.STARTED);
}
public final synchronized boolean
isClosed()
{
return (state == State.CLOSED);
}
protected final void
work()
{
try {
synchronized (this) {
if (state != State.GO) {
return;
}
state = State.STARTED;
fireConsumerStarted(new ConsumerEvent(this,
System.nanoTime()));
}
_consume();
} catch (Throwable e) {
if (exceptionHandler != null) {
exceptionHandler.handleException(e);
} else {
e.printStackTrace();
}
} finally {
synchronized (stopLock) {
synchronized (this) {
if (state == State.STOPPED || state == State.CLOSED) {
} else {
state = State.STOPPED;
fireConsumerStopped(new ConsumerEvent(this,
System.nanoTime()));
}
}
workEnded = true;
stopLock.notifyAll();
}
}
}
protected Thread
createThread()
{
Thread t = new Thread(new Runnable() {
public void run() {
work();
}
}, "DTrace consumer " + id);
return t;
}
public void
go() throws DTraceException
{
go(null);
}
public synchronized void
go(ExceptionHandler h) throws DTraceException
{
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
throw new IllegalStateException("no compiled program");
case COMPILED:
_checkProgramEnabling();
break;
case GO:
case STARTED:
throw new IllegalStateException("go() already called");
case STOPPED:
throw new IllegalStateException("consumer stopped");
case CLOSED:
throw new IllegalStateException("consumer closed");
default:
throw new IllegalArgumentException("unknown state: " + state);
}
synchronized (LocalConsumer.class) {
_go();
}
state = State.GO;
exceptionHandler = h;
Thread t = createThread();
t.start();
}
public void
stop()
{
boolean running = false;
synchronized (this) {
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
case COMPILED:
throw new IllegalStateException("go() not called");
case GO:
try {
synchronized (LocalConsumer.class) {
_stop();
}
state = State.STOPPED;
fireConsumerStopped(new ConsumerEvent(this,
System.nanoTime()));
} catch (DTraceException e) {
if (exceptionHandler != null) {
exceptionHandler.handleException(e);
} else {
e.printStackTrace();
}
}
break;
case STARTED:
running = true;
break;
case STOPPED:
if (stopCalled) {
throw new IllegalStateException(
"consumer already stopped");
}
logger.fine("consumer already stopped");
break;
case CLOSED:
throw new IllegalStateException("consumer closed");
default:
throw new IllegalArgumentException("unknown state: " +
state);
}
stopCalled = true;
}
if (running) {
if (Thread.holdsLock(this)) {
throw new IllegalThreadStateException("The current " +
"thread cannot stop this LocalConsumer while " +
"holding the lock on this LocalConsumer");
}
_interrupt();
synchronized (stopLock) {
while (!workEnded) {
try {
stopLock.wait();
} catch (InterruptedException e) {
logger.warning(e.toString());
}
}
}
}
}
public synchronized void
abort()
{
if ((state != State.INIT) && (state != State.CLOSED)) {
_interrupt();
}
abortCalled = true;
}
public void
close()
{
synchronized (this) {
if ((state == State.INIT) || (state == State.CLOSED)) {
state = State.CLOSED;
return;
}
}
try {
stop();
} catch (IllegalStateException e) {
}
synchronized (this) {
if (state != State.CLOSED) {
synchronized (LocalConsumer.class) {
_close();
}
_destroy();
state = State.CLOSED;
if (logger.isLoggable(Level.INFO)) {
logger.info("consumer table count: " + _openCount());
}
}
}
}
public void
addConsumerListener(ConsumerListener l)
{
listenerList.add(ConsumerListener.class, l);
}
public void
removeConsumerListener(ConsumerListener l)
{
listenerList.remove(ConsumerListener.class, l);
}
public Aggregate
getAggregate() throws DTraceException
{
return getAggregate(null, Collections. <String> emptySet());
}
public Aggregate
getAggregate(Set <String> includedAggregationNames)
throws DTraceException
{
return getAggregate(includedAggregationNames,
Collections. <String> emptySet());
}
public Aggregate
getAggregate(Set <String> includedAggregationNames,
Set <String> clearedAggregationNames)
throws DTraceException
{
AggregateSpec spec = new AggregateSpec();
if (includedAggregationNames == null) {
spec.setIncludeByDefault(true);
} else {
spec.setIncludeByDefault(false);
for (String included : includedAggregationNames) {
spec.addIncludedAggregationName(included);
}
}
if (clearedAggregationNames == null) {
spec.setClearByDefault(true);
} else {
spec.setClearByDefault(false);
for (String cleared : clearedAggregationNames) {
spec.addClearedAggregationName(cleared);
}
}
return getAggregate(spec);
}
private synchronized Aggregate
getAggregate(AggregateSpec spec) throws DTraceException
{
checkGoCalled();
Aggregate aggregate = _getAggregate(spec);
return aggregate;
}
private synchronized void
checkGoCalled()
{
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
case COMPILED:
throw new IllegalStateException("go() not called");
case GO:
case STARTED:
case STOPPED:
break;
case CLOSED:
throw new IllegalStateException("consumer closed");
}
}
private synchronized void
checkGoNotCalled()
{
switch (state) {
case INIT:
throw new IllegalStateException("consumer not open");
case OPEN:
case COMPILED:
break;
case GO:
case STARTED:
throw new IllegalStateException("go() already called");
case STOPPED:
throw new IllegalStateException("consumer stopped");
case CLOSED:
throw new IllegalStateException("consumer closed");
}
}
public synchronized int
createProcess(String command) throws DTraceException
{
if (command == null) {
throw new NullPointerException("command is null");
}
checkGoNotCalled();
int pid;
synchronized (LocalConsumer.class) {
pid = _createProcess(command);
}
return pid;
}
public synchronized void
grabProcess(int pid) throws DTraceException
{
checkGoNotCalled();
synchronized (LocalConsumer.class) {
_grabProcess(pid);
}
}
public synchronized List <ProbeDescription>
listProbes(ProbeDescription filter) throws DTraceException
{
checkGoNotCalled();
List <ProbeDescription> probeList =
new LinkedList <ProbeDescription> ();
if (filter == ProbeDescription.EMPTY) {
filter = null;
}
synchronized (LocalConsumer.class) {
_listProbes(probeList, filter);
}
return probeList;
}
public synchronized List <Probe>
listProbeDetail(ProbeDescription filter) throws DTraceException
{
checkGoNotCalled();
List <Probe> probeList = new LinkedList <Probe> ();
if (filter == ProbeDescription.EMPTY) {
filter = null;
}
synchronized (LocalConsumer.class) {
_listProbeDetail(probeList, filter);
}
return probeList;
}
public synchronized List <ProbeDescription>
listProgramProbes(Program program) throws DTraceException
{
checkProgram(program);
checkGoNotCalled();
List <ProbeDescription> probeList =
new LinkedList <ProbeDescription> ();
synchronized (LocalConsumer.class) {
_listCompiledProbes(probeList, program);
}
return probeList;
}
public synchronized List <Probe>
listProgramProbeDetail(Program program) throws DTraceException
{
checkProgram(program);
checkGoNotCalled();
List <Probe> probeList = new LinkedList <Probe> ();
synchronized (LocalConsumer.class) {
_listCompiledProbeDetail(probeList, program);
}
return probeList;
}
public synchronized String
lookupKernelFunction(int address)
{
checkGoCalled();
synchronized (LocalConsumer.class) {
return _lookupKernelFunction(Integer.valueOf(address));
}
}
public synchronized String
lookupKernelFunction(long address)
{
checkGoCalled();
synchronized (LocalConsumer.class) {
return _lookupKernelFunction(Long.valueOf(address));
}
}
public synchronized String
lookupUserFunction(int pid, int address)
{
checkGoCalled();
synchronized (LocalConsumer.class) {
return _lookupUserFunction(pid, Integer.valueOf(address));
}
}
public synchronized String
lookupUserFunction(int pid, long address)
{
checkGoCalled();
synchronized (LocalConsumer.class) {
return _lookupUserFunction(pid, Long.valueOf(address));
}
}
public String
getVersion()
{
synchronized (LocalConsumer.class) {
return LocalConsumer._getVersion();
}
}
private void
nextProbeData(ProbeData probeData) throws ConsumerException
{
fireDataReceived(new DataEvent(this, probeData));
}
private void
dataDropped(Drop drop) throws ConsumerException
{
fireDataDropped(new DropEvent(this, drop));
}
private void
errorEncountered(Error error) throws ConsumerException
{
fireErrorEncountered(new ErrorEvent(this, error));
}
private void
processStateChanged(ProcessState processState) throws ConsumerException
{
fireProcessStateChanged(new ProcessEvent(this, processState));
}
protected void
fireDataReceived(DataEvent e) throws ConsumerException
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).dataReceived(e);
}
}
}
protected void
fireDataDropped(DropEvent e) throws ConsumerException
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).dataDropped(e);
}
}
}
protected void
fireErrorEncountered(ErrorEvent e) throws ConsumerException
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).errorEncountered(e);
}
}
}
protected void
fireProcessStateChanged(ProcessEvent e) throws ConsumerException
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).processStateChanged(e);
}
}
}
protected void
fireConsumerStarted(ConsumerEvent e)
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).consumerStarted(e);
}
}
}
protected void
fireConsumerStopped(ConsumerEvent e)
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).consumerStopped(e);
}
}
}
private void
intervalBegan()
{
fireIntervalBegan(new ConsumerEvent(this, System.nanoTime()));
}
protected void
fireIntervalBegan(ConsumerEvent e)
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).intervalBegan(e);
}
}
}
private void
intervalEnded()
{
fireIntervalEnded(new ConsumerEvent(this, System.nanoTime()));
}
protected void
fireIntervalEnded(ConsumerEvent e)
{
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ConsumerListener.class) {
((ConsumerListener)listeners[i + 1]).intervalEnded(e);
}
}
}
public String
toString()
{
StringBuilder buf = new StringBuilder(LocalConsumer.class.getName());
synchronized (this) {
buf.append("[open = ");
buf.append(isOpen());
buf.append(", enabled = ");
buf.append(isEnabled());
buf.append(", running = ");
buf.append(isRunning());
buf.append(", closed = ");
buf.append(isClosed());
}
buf.append(']');
return buf.toString();
}
protected void
finalize()
{
close();
}
private String
getTag()
{
return super.toString();
}
static class Identifier implements Serializable {
static final long serialVersionUID = 2183165132305302834L;
private int id;
private long timestamp;
private InetAddress localHost;
private String tag;
private
Identifier(LocalConsumer consumer)
{
id = LocalConsumer.sequence++;
timestamp = System.currentTimeMillis();
try {
localHost = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
localHost = null;
}
tag = consumer.getTag();
}
@Override
public boolean
equals(Object o)
{
if (o == this) {
return true;
}
if (o instanceof Identifier) {
Identifier i = (Identifier)o;
return ((id == i.id) &&
(timestamp == i.timestamp) &&
((localHost == null) ? (i.localHost == null) :
localHost.equals(i.localHost)) &&
tag.equals(i.tag));
}
return false;
}
@Override
public int
hashCode()
{
int hash = 17;
hash = (37 * hash) + id;
hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32)));
hash = (37 * hash) + (localHost == null ? 0 :
localHost.hashCode());
hash = (37 * hash) + tag.hashCode();
return hash;
}
@Override
public String
toString()
{
StringBuilder buf = new StringBuilder();
buf.append(Identifier.class.getName());
buf.append("[id = ");
buf.append(id);
buf.append(", timestamp = ");
buf.append(timestamp);
buf.append(", localHost = ");
buf.append(localHost);
buf.append(", tag = ");
buf.append(tag);
buf.append(']');
return buf.toString();
}
}
}