diff --git a/org.intrace/.classpath b/org.intrace/.classpath
index a7cb3c4..a2f5798 100644
--- a/org.intrace/.classpath
+++ b/org.intrace/.classpath
@@ -10,7 +10,6 @@
-
diff --git a/org.intrace/build.xml b/org.intrace/build.xml
index eaf1365..aebf039 100644
--- a/org.intrace/build.xml
+++ b/org.intrace/build.xml
@@ -76,22 +76,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -108,16 +92,17 @@
+
-
+
-
+
-
+
@@ -127,14 +112,14 @@
-
+
-
+
-
+
@@ -184,9 +169,9 @@
-
-
-
+
+
+
@@ -235,9 +220,9 @@
-
+
-
+
@@ -249,12 +234,12 @@
****
****
-
+
-
+
-1)
- {
- System.out.println("## Establishing Callback Connection...");
- doCallbackConnection(args.getCallbackPort(), t);
- }
-
- // Wait for startup
- if (args.isWaitStart())
- {
- try
- {
- System.out.println("## Program Paused");
- AgentServer.waitForStartSignal();
- }
- catch (InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- }
-
- private static int serverPort = -1;
-
- public static synchronized void setServerPort(int xiServerPort)
- {
- serverPort = xiServerPort;
- Agent.class.notifyAll();
- }
-
- private static synchronized void waitForServerPort()
{
try
{
- Agent.class.wait();
- }
- catch (InterruptedException e)
- {
- e.printStackTrace();
- }
- }
+ // Prepare boot classpath
+ String agentPath = Agent.class.getProtectionDomain().getCodeSource()
+ .getLocation().getPath();
+ System.out.println("Agent path: " + agentPath);
+ inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(agentPath)));
- private static void doCallbackConnection(int callbackPort, ClassTransformer t)
- {
- try
- {
- Socket callback = new Socket();
- callback.connect(new InetSocketAddress("localhost", callbackPort));
- AgentClientConnection clientConnection = new AgentClientConnection(
- callback,
- t);
- AgentServer.addClientConnection(clientConnection);
- clientConnection.start(1);
- clientConnection.waitForTraceConn();
+ // Class AgentInit in boot classloader
+ Class> agentInit = Agent.class.getClassLoader().loadClass(
+ "org.intrace.agent.AgentInit");
+ Method initMethod = agentInit.getMethod("initialize", String.class, Instrumentation.class);
+ initMethod.invoke(null, agentArgs, inst);
}
catch (Exception ex)
{
- ex.printStackTrace();
+ throw new RuntimeException(ex);
}
+
}
}
\ No newline at end of file
diff --git a/org.intrace/src/org/intrace/agent/AgentInit.java b/org.intrace/src/org/intrace/agent/AgentInit.java
new file mode 100644
index 0000000..0a5777f
--- /dev/null
+++ b/org.intrace/src/org/intrace/agent/AgentInit.java
@@ -0,0 +1,112 @@
+package org.intrace.agent;
+
+import java.lang.instrument.Instrumentation;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.intrace.agent.server.AgentClientConnection;
+import org.intrace.agent.server.AgentServer;
+import org.intrace.output.AgentHelper;
+import org.intrace.output.trace.TraceHandler;
+
+public class AgentInit
+{
+
+ static int serverPort = -1;
+
+ /**
+ * Common init function.
+ *
+ * @param agentArgs
+ * @param inst
+ */
+ public static void initialize(String agentArgs, Instrumentation inst)
+ {
+ System.out.println("## Loaded InTrace Agent.");
+
+ if (agentArgs == null)
+ {
+ agentArgs = "";
+ }
+
+ // Setup the trace instrumentation handler
+ AgentHelper.instrumentationHandler = TraceHandler.INSTANCE;
+
+ // Parse startup args
+ AgentSettings args = new AgentSettings(agentArgs);
+ AgentHelper.getResponses(null, agentArgs);
+
+ // Construct Transformer
+ ClassTransformer t = new ClassTransformer(inst, args);
+ inst.addTransformer(t, true);
+
+ // Ensure loaded classes are traced
+ t.instrumentKlasses(t.getLoadedClassesForModification());
+
+ // Start Server thread
+ new AgentServer(t, args.getServerPort()).start();
+
+ // Store server port
+ waitForServerPort();
+ args.setActualServerPort(serverPort);
+
+ // Wait for callback connection
+ if (args.getCallbackPort() > -1)
+ {
+ System.out.println("## Establishing Callback Connection...");
+ doCallbackConnection(args.getCallbackPort(), t);
+ }
+
+ // Wait for startup
+ if (args.isWaitStart())
+ {
+ try
+ {
+ System.out.println("## Program Paused");
+ AgentServer.waitForStartSignal();
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static synchronized void setServerPort(int xiServerPort)
+ {
+ serverPort = xiServerPort;
+ AgentInit.class.notifyAll();
+ }
+
+ static synchronized void waitForServerPort()
+ {
+ try
+ {
+ AgentInit.class.wait();
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ static void doCallbackConnection(int callbackPort, ClassTransformer t)
+ {
+ try
+ {
+ Socket callback = new Socket();
+ callback.connect(new InetSocketAddress("localhost", callbackPort));
+ AgentClientConnection clientConnection = new AgentClientConnection(
+ callback,
+ t);
+ AgentServer.addClientConnection(clientConnection);
+ clientConnection.start(1);
+ clientConnection.waitForTraceConn();
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+}
diff --git a/org.intrace/src/org/intrace/agent/ClassTransformer.java b/org.intrace/src/org/intrace/agent/ClassTransformer.java
index 00e0084..4f593fa 100644
--- a/org.intrace/src/org/intrace/agent/ClassTransformer.java
+++ b/org.intrace/src/org/intrace/agent/ClassTransformer.java
@@ -5,6 +5,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
@@ -193,10 +194,10 @@ public class ClassTransformer implements ClassFileTransformer
private boolean isSensitiveClass(String className)
{
- return className.contains(".intrace.") || className.contains("objectweb.asm")
- || className.startsWith("sun")
- || className.startsWith("org.openide")
- || className.startsWith("com.sun") || className.startsWith("java");
+ return className.contains(".intrace.") || className.contains("objectweb.asm");
+// || className.startsWith("sun")
+// || className.startsWith("org.openide")
+// || className.startsWith("com.sun") || className.startsWith("java");
}
/**
@@ -213,63 +214,87 @@ public class ClassTransformer implements ClassFileTransformer
byte[] originalClassfile)
throws IllegalClassFormatException
{
- String className = internalClassName.replace('/', '.');
- ComparableClassName compclass = new ComparableClassName(className, loader);
- int modifiedSize = modifiedClasses.size();
- int allClassesSize = allClasses.size();
-
- if (isToBeConsideredForInstrumentation(classBeingRedefined, loader,
- className, protectionDomain))
+ // Touching the Thread and UncaughtExceptionHandler classes here is safe
+ // as they must already be loaded by the time we get here.
+ Thread currentTh = Thread.currentThread();
+ UncaughtExceptionHandler handler = currentTh.getUncaughtExceptionHandler();
+ if (handler != CRITICAL_BLOCK)
{
- System.out.println("!! Instrumenting class: " + compclass);
-
- if (settings.saveTracedClassfiles())
- {
- writeClassBytes(originalClassfile, internalClassName + "_src.class");
- }
-
- byte[] newBytes;
+ // Allow instrumentation to proceed
+ currentTh.setUncaughtExceptionHandler(CRITICAL_BLOCK);
+
try
{
- newBytes = getInstrumentedClassBytes(className, originalClassfile);
+ String className = internalClassName.replace('/', '.');
+ ComparableClassName compclass = new ComparableClassName(className, loader);
+ System.out.println("!! Consider: " + compclass);
+ int modifiedSize = modifiedClasses.size();
+ int allClassesSize = allClasses.size();
+
+ if (isToBeConsideredForInstrumentation(classBeingRedefined, loader,
+ className, protectionDomain))
+ {
+ System.out.println("!! Instrumenting class: " + compclass);
+
+ if (settings.saveTracedClassfiles())
+ {
+ writeClassBytes(originalClassfile, internalClassName + "_src.class");
+ }
+
+ byte[] newBytes;
+ try
+ {
+ newBytes = getInstrumentedClassBytes(className, originalClassfile);
+ }
+ catch (RuntimeException th)
+ {
+ // Ensure the JVM doesn't silently swallow an unchecked exception
+ th.printStackTrace();
+ throw th;
+ }
+ catch (Error th)
+ {
+ // Ensure the JVM doesn't silently swallow an unchecked exception
+ th.printStackTrace();
+ throw th;
+ }
+
+ if (settings.saveTracedClassfiles())
+ {
+ writeClassBytes(newBytes, internalClassName + "_gen.class");
+ }
+
+ modifiedClasses.add(compclass);
+
+ if (!isSensitiveClass(className))
+ {
+ // Only send updates if we aren't handling a "sensitive class"
+ detectStatusUpdate(modifiedSize, allClassesSize);
+ }
+
+ return newBytes;
+ }
+ else
+ {
+ modifiedClasses.remove(compclass);
+
+ if (!isSensitiveClass(className))
+ {
+ // Only send updates if we aren't handling a "sensitive class"
+ detectStatusUpdate(modifiedSize, allClassesSize);
+ }
+ return null;
+ }
}
- catch (RuntimeException th)
+ finally
{
- // Ensure the JVM doesn't silently swallow an unchecked exception
- th.printStackTrace();
- throw th;
+ currentTh.setUncaughtExceptionHandler(handler);
}
- catch (Error th)
- {
- // Ensure the JVM doesn't silently swallow an unchecked exception
- th.printStackTrace();
- throw th;
- }
-
- if (settings.saveTracedClassfiles())
- {
- writeClassBytes(newBytes, internalClassName + "_gen.class");
- }
-
- modifiedClasses.add(compclass);
-
- if (!isSensitiveClass(className))
- {
- // Only send updates if we aren't handling a "sensitive class"
- detectStatusUpdate(modifiedSize, allClassesSize);
- }
-
- return newBytes;
}
else
{
- modifiedClasses.remove(compclass);
-
- if (!isSensitiveClass(className))
- {
- // Only send updates if we aren't handling a "sensitive class"
- detectStatusUpdate(modifiedSize, allClassesSize);
- }
+ // This thread is currently handling instrumenting a class.
+ // We must return now.
return null;
}
}
@@ -501,39 +526,80 @@ public class ClassTransformer implements ClassFileTransformer
return unmodifiedKlasses;
}
- public void instrumentKlasses(Set klasses)
- {
- try
- {
- bulkUpdateActive.set(true);
- int countNumClasses = 0;
- int totalNumClasses = klasses.size();
- broadcastProgress(countNumClasses, totalNumClasses);
- for (ComparableClass klass : klasses)
- {
- try
- {
- inst.retransformClasses(klass.klass);
+ private InstruKlassesAction instruAction = null;
+ private final Object instruLock = new Object();
- countNumClasses++;
- if ((countNumClasses % 10) == 0)
+ public void instrumentKlasses(final Set klasses)
+ {
+ synchronized (instruLock)
+ {
+ if (instruAction != null)
+ {
+ instruAction.active = false;
+ instruAction = null;
+ }
+ instruAction = new InstruKlassesAction(klasses);
+ instruAction.start();
+ }
+ }
+
+ private class InstruKlassesAction implements Runnable
+ {
+ public InstruKlassesAction(Set klasses)
+ {
+ this.klasses = klasses;
+ }
+
+ private final Set klasses;
+ public volatile boolean active = true;
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ bulkUpdateActive.set(true);
+ int countNumClasses = 0;
+ int totalNumClasses = klasses.size();
+ broadcastProgress(countNumClasses, totalNumClasses);
+ for (ComparableClass klass : klasses)
+ {
+ if (!active)
{
- broadcastProgress(countNumClasses, totalNumClasses);
+ break;
+ }
+ try
+ {
+ inst.retransformClasses(klass.klass);
+
+ countNumClasses++;
+ if ((countNumClasses % 10) == 0)
+ {
+ broadcastProgress(countNumClasses, totalNumClasses);
+ }
+ }
+ catch (Throwable e)
+ {
+ // Write exception to stdout
+ System.out.println(klass.klass.getName());
+ e.printStackTrace();
}
}
- catch (Throwable e)
- {
- // Write exception to stdout
- System.out.println(klass.klass.getName());
- e.printStackTrace();
- }
+ broadcastProgress(totalNumClasses, totalNumClasses, true);
}
- broadcastProgress(totalNumClasses, totalNumClasses, true);
+ finally
+ {
+ bulkUpdateActive.set(false);
+ broadcastStatus(modifiedClasses.size(), allClasses.size());
+ }
}
- finally
+
+ public void start()
{
- bulkUpdateActive.set(false);
- broadcastStatus(modifiedClasses.size(), allClasses.size());
+ Thread th = new Thread(this);
+ th.setName("instrumentKlasses");
+ th.setDaemon(true);
+ th.start();
}
}
@@ -804,4 +870,13 @@ public class ClassTransformer implements ClassFileTransformer
return klassloaderStr + klass.getName();
}
}
+ public static final CriticalBlock CRITICAL_BLOCK = new CriticalBlock();
+ private static class CriticalBlock implements Thread.UncaughtExceptionHandler
+ {
+ @Override
+ public void uncaughtException(Thread t, Throwable e)
+ {
+ // Do nothing
+ }
+ }
}
diff --git a/org.intrace/src/org/intrace/agent/server/AgentClientConnection.java b/org.intrace/src/org/intrace/agent/server/AgentClientConnection.java
index 2fdc5e5..223c67c 100644
--- a/org.intrace/src/org/intrace/agent/server/AgentClientConnection.java
+++ b/org.intrace/src/org/intrace/agent/server/AgentClientConnection.java
@@ -5,6 +5,7 @@ import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
+import java.lang.Thread.UncaughtExceptionHandler;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
@@ -83,63 +84,72 @@ public class AgentClientConnection implements Runnable
@Override
public void run()
{
+ UncaughtExceptionHandler handler = Thread.currentThread().getUncaughtExceptionHandler();
try
{
+ Thread.currentThread().setUncaughtExceptionHandler(ClassTransformer.CRITICAL_BLOCK);
try
{
- while (true)
+ try
{
- String message = receiveMessage();
- if (message.equals("getsettings"))
+ while (true)
{
- Map settingsMap = new HashMap();
- settingsMap.putAll(transformer.getSettings());
- settingsMap.putAll(AgentHelper.getSettings());
- AgentServer.broadcastMessage(this, settingsMap);
- }
- else if (message.equals("help"))
- {
- Set commandSet = new HashSet();
- commandSet.addAll(AgentConfigConstants.COMMANDS);
- commandSet.addAll(TraceConfigConstants.COMMANDS);
- sendMessage(commandSet);
- }
- else if (message.equalsIgnoreCase(AgentConfigConstants.START_ACTIVATE))
- {
- transformer.getResponse(this, message);
- AgentServer.setStartSignalled();
- }
- else
- {
- List responses = transformer.getResponse(this, message);
- if (responses.size() > 0)
+ String message = receiveMessage();
+ if (message.equals("getsettings"))
{
- for (String response : responses)
- {
- sendMessage(response);
- }
+ Map settingsMap = new HashMap();
+ settingsMap.putAll(transformer.getSettings());
+ settingsMap.putAll(AgentHelper.getSettings());
+ AgentServer.broadcastMessage(this, settingsMap);
+ }
+ else if (message.equals("help"))
+ {
+ Set commandSet = new HashSet();
+ commandSet.addAll(AgentConfigConstants.COMMANDS);
+ commandSet.addAll(TraceConfigConstants.COMMANDS);
+ sendMessage(commandSet);
+ }
+ else if (message.equalsIgnoreCase(AgentConfigConstants.START_ACTIVATE))
+ {
+ transformer.getResponse(this, message);
+ AgentServer.setStartSignalled();
}
else
{
- sendMessage("OK");
+ List responses = transformer.getResponse(this, message);
+ if (responses.size() > 0)
+ {
+ for (String response : responses)
+ {
+ sendMessage(response);
+ }
+ }
+ else
+ {
+ sendMessage("OK");
+ }
}
}
}
+ catch (IOException ex)
+ {
+ System.out.println("## Control Connection Disconnected (Port: " +
+ connectedClient.getPort() + ")");
+ }
+ connectedClient.close();
}
- catch (IOException ex)
+ catch (IOException e1)
{
- System.out.println("## Control Connection Disconnected (Port: " +
- connectedClient.getPort() + ")");
+ e1.printStackTrace();
+ }
+ finally
+ {
+ AgentServer.removeClientConnection(this);
}
- connectedClient.close();
- }
- catch (IOException e1)
- {
- e1.printStackTrace();
}
finally
{
- AgentServer.removeClientConnection(this);
+ Thread.currentThread().setUncaughtExceptionHandler(handler);
}
}
@@ -170,9 +180,9 @@ public class AgentClientConnection implements Runnable
* @param xiObject
* @throws IOException
*/
- public void sendMessage(Object xiObject) throws IOException
+ public synchronized void sendMessage(Object xiObject) throws IOException
{
- synchronized (connectedClient)
+// synchronized (connectedClient)
{
OutputStream out = connectedClient.getOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out);
diff --git a/org.intrace/src/org/intrace/agent/server/AgentServer.java b/org.intrace/src/org/intrace/agent/server/AgentServer.java
index f00518a..d3aa471 100644
--- a/org.intrace/src/org/intrace/agent/server/AgentServer.java
+++ b/org.intrace/src/org/intrace/agent/server/AgentServer.java
@@ -7,7 +7,7 @@ import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import org.intrace.agent.Agent;
+import org.intrace.agent.AgentInit;
import org.intrace.agent.ClassTransformer;
/**
@@ -129,7 +129,7 @@ public class AgentServer implements Runnable
System.out.println("## Listening on port " + serversock.getLocalPort());
System.setProperty("org.intrace.port",
Integer.toString(serversock.getLocalPort()));
- Agent.setServerPort(serversock.getLocalPort());
+ AgentInit.setServerPort(serversock.getLocalPort());
while (true)
{
Socket connectedClient = serversock.accept();