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();