/*
* Copyright 2006-2011 Brian S O'Neill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cojen.dirmi.trace;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.security.SecureRandom;
/**
* Instrumentation agent for tracing. To install, pass a java argument as follows:
*
* <pre>
* java -javaagent:<path to agent jar file>=<class name of trace handler>[;<argument>]
* </pre>
*
* For example:
*
* <pre>
* java -javaagent:dirmi-1.0.jar=org.cojen.dirmi.trace.SimpleHandler ...
* </pre>
*
* Handlers may accept a single string argument, provided after the handler name,
* separated by a semi-colon.
*
* @author Brian S O'Neill
* @see org.cojen.dirmi.Trace
* @see TraceHandler
*/
public class TraceAgent {
private static final Random cRandom = new SecureRandom();
private static final Map<Long, TraceAgent> cAgents = new HashMap<Long, TraceAgent>();
/**
* Premain method, as required by instrumentation agents.
*
* @param agentArgs specify trace handler class name
* @param inst instrumentation instance passed in by JVM
* @see TraceHandler
*/
public static void premain(String agentArgs, Instrumentation inst) throws Throwable {
TraceAgent agent = new TraceAgent(agentArgs);
inst.addTransformer(new Transformer(agent));
}
/**
* Method called by instrumented class to get reference to agent.
*/
public static TraceAgent getTraceAgent(long id) {
return cAgents.get(id);
}
private static synchronized long registerAgent(TraceAgent agent) {
Long id;
do {
id = cRandom.nextLong();
} while (cAgents.containsKey(id));
cAgents.put(id, agent);
return id;
}
private final long mAgentId;
private final TracedMethodRegistry mRegistry = new TracedMethodRegistry();
private final TraceHandler mHandler;
private TraceAgent(String agentArgs) throws Throwable {
if (agentArgs == null) {
throw new IllegalArgumentException
("Must pass handler class name. For example, \"" +
"java -javaagent:<path to agent jar file>=<class name of trace handler> ...\"");
}
String name, arg;
int index = agentArgs.indexOf(';');
if (index < 0) {
name = agentArgs;
arg = null;
} else {
name = agentArgs.substring(0, index);
arg = agentArgs.substring(index + 1);
}
Class handlerClass = Class.forName(name);
if (!TraceHandler.class.isAssignableFrom(handlerClass)) {
throw new IllegalArgumentException
("Class doesn't implement TraceHandler: " + handlerClass.getName());
}
Constructor ctor;
boolean passArg;
if (arg == null) {
try {
ctor = handlerClass.getConstructor(TraceToolbox.class);
passArg = false;
} catch (NoSuchMethodException e) {
try {
ctor = handlerClass.getConstructor(TraceToolbox.class, String.class);
} catch (NoSuchMethodException e2) {
throw e;
}
passArg = true;
}
} else {
try {
ctor = handlerClass.getConstructor(TraceToolbox.class, String.class);
passArg = true;
} catch (NoSuchMethodException e) {
try {
ctor = handlerClass.getConstructor(TraceToolbox.class);
} catch (NoSuchMethodException e2) {
throw e;
}
passArg = false;
}
}
TraceToolbox toolbox = new TraceToolbox(mRegistry);
Object[] ctorArgs = new Object[passArg ? 2 : 1];
ctorArgs[0] = toolbox;
if (passArg) {
ctorArgs[1] = arg;
}
TraceHandler handler;
try {
handler = (TraceHandler) ctor.newInstance(ctorArgs);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
throw cause == null ? e : cause;
}
mAgentId = registerAgent(this);
mHandler = handler;
}
/**
* Method called by instrumented class to get the trace handler.
*/
public TraceHandler getTraceHandler() {
return mHandler;
}
/**
* Method called by instrumented class to register traced methods.
*/
public void registerTraceMethod(int mid, String operation, Class clazz,
String methodName, Class returnType, Class... paramTypes)
{
mRegistry.registerMethod
(mid, new TracedMethod(mid, operation, clazz, methodName, returnType, paramTypes));
}
public long getAgentId() {
return mAgentId;
}
TraceModes getTraceModes(String className) {
return mHandler.getTraceModes(className);
}
int reserveMethod(boolean root, boolean graft) {
return mRegistry.reserveMethod(root, graft);
}
}