Development work to allow any classes to be traced. This will break the tests.

This commit is contained in:
mchr3k 2011-05-24 23:53:21 +01:00
parent 580b21e77a
commit 4474c7d8ec
9 changed files with 346 additions and 241 deletions

View File

@ -10,7 +10,6 @@
<classpathentry kind="lib" path="lib/asm-debug-all-3.3.jar"/>
<classpathentry kind="lib" path="lib/jar-in-jar-loader.jar"/>
<classpathentry kind="lib" path="lib/swt-win32-3.6.2.jar"/>
<classpathentry kind="con" path="SWT_CONTAINER"/>
<classpathentry kind="lib" path="lib/miglayout-3.7.3.1-swt.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -76,22 +76,6 @@
<mkdir dir="./build/tmp/" />
<mkdir dir="./build/jars/" />
<!-- Test Trace Agent -->
<if>
<available file="./lib/intrace-agent-test.jar"/>
<else>
<jar destfile="./lib/intrace-agent-test.jar" compress="false">
<manifest>
<attribute name="Premain-Class" value="org.intrace.agent.Agent" />
<attribute name="Agent-Class" value="org.intrace.agent.Agent" />
<attribute name="Can-Redefine-Classes" value="true" />
<attribute name="Git-Revision" value="${git.revision}" />
<attribute name="Implementation-Version" value="${git.tag}" />
</manifest>
</jar>
</else>
</if>
<!-- Agent -->
<jarjar jarfile="./build/jars/intrace-agent.jar">
<manifest>
@ -108,16 +92,17 @@
<zipfileset excludes="META-INF/*.MF" src="./lib/asm-debug-all-3.3.jar"/>
<rule pattern="org.objectweb.asm.**" result="org.intrace.internal.objectweb.asm.@1"/>
</jarjar>
<copy file="./build/jars/intrace-agent.jar" todir="./lib/" />
<!-- UI (Stage 1) -->
<jarjar jarfile="./build/tmp/intrace-ui-wrapper.jar">
<jarjar jarfile="./build/tmp/intrace-ui-wrapper.jar">
<fileset dir="./build/classes" includes="**/shared/*.class" />
<fileset dir="./build/classes" includes="**/client/gui/**/*.class" />
<zipfileset excludes="META-INF/*.MF" src="lib/miglayout-3.7.3.1-swt.jar"/>
</jarjar>
<!-- UI (Stage 2) -->
<jarjar jarfile="./build/jars/intrace-ui.jar">
<jarjar jarfile="./build/jars/intrace-ui.jar">
<manifest>
<attribute name="Main-Class" value="org.intrace.client.loader.InTraceStandaloneUILoader" />
<attribute name="Class-Path" value="." />
@ -127,14 +112,14 @@
<fileset dir="./build/classes" includes="**/client/loader/*.class" />
<fileset dir="./build/tmp" includes="intrace-ui-wrapper.jar" />
<fileset dir="./lib" includes="swt-*.jar" />
<zipfileset excludes="META-INF/*.MF" src="lib/jar-in-jar-loader.jar"/>
<zipfileset excludes="META-INF/*.MF" src="lib/jar-in-jar-loader.jar"/>
</jarjar>
<!-- UI Library -->
<jar jarfile="./build/jars/intrace-ui-helper.jar">
<jar jarfile="./build/jars/intrace-ui-helper.jar">
<fileset dir="./build/classes" includes="**/shared/*.class" />
<fileset dir="./build/classes" includes="**/client/gui/helper/*.class" />
</jar>
</jar>
</target>
<target name="test" description="Run Tests" depends="build" >
@ -184,9 +169,9 @@
<formatter type="plain"/>
<batchtest fork="yes" todir="./reports/junit/raw">
<fileset dir="./testsrc">
<include name="**/*Test.java"/>
</fileset>
<fileset dir="./testsrc">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
</junit>
@ -235,9 +220,9 @@
</target>
<target name="validate" description="Build jars and run all automated tests" depends="jar, test, findbugs" >
<target name="validate" description="Build jars and run all automated tests" depends="jar, test, findbugs" >
</target>
<target name="release1" description="Validate InTrace and copy files ready for Eclipse build" depends="validate,update_eclipse_jars">
<copy todir="../binaries/jars/latest_development" overwrite="true">
<fileset file="./build/jars/intrace-agent.jar" />
@ -249,12 +234,12 @@
<echo>****</echo>
<echo>****</echo>
</target>
<target name="release2" description="Copy around built Eclipse files">
<copy todir="../intrace.appengine/war" overwrite="true">
<fileset dir="../intrace.ecl.site/" excludes="artifacts.jar,content.jar,.project"/>
</copy>
<delete file="../binaries/jars/latest_development/intrace.ecl.site.zip" />
<zip destfile="../binaries/jars/latest_development/intrace.ecl.site.zip"
basedir="../intrace.ecl.site/"

Binary file not shown.

View File

@ -1,14 +1,12 @@
package org.intrace.agent;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.lang.reflect.Method;
import java.util.jar.JarFile;
import org.intrace.agent.server.AgentClientConnection;
import org.intrace.agent.server.AgentServer;
import org.intrace.output.AgentHelper;
import org.intrace.output.IInstrumentationHandler;
import org.intrace.output.trace.TraceHandler;
/**
* InTrace Agent: Installs a Class Transformer to instrument class bytecode. The
@ -39,100 +37,26 @@ public class Agent
initialize(agentArgs, inst);
}
/**
* Common init function.
*
* @param agentArgs
* @param inst
*/
private 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();
}
}
}
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);
}
}
}

View File

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

View File

@ -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<ComparableClass> 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<ComparableClass> 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<ComparableClass> klasses)
{
this.klasses = klasses;
}
private final Set<ComparableClass> 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
}
}
}

View File

@ -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<String, String> settingsMap = new HashMap<String, String>();
settingsMap.putAll(transformer.getSettings());
settingsMap.putAll(AgentHelper.getSettings());
AgentServer.broadcastMessage(this, settingsMap);
}
else if (message.equals("help"))
{
Set<String> commandSet = new HashSet<String>();
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<String> responses = transformer.getResponse(this, message);
if (responses.size() > 0)
String message = receiveMessage();
if (message.equals("getsettings"))
{
for (String response : responses)
{
sendMessage(response);
}
Map<String, String> settingsMap = new HashMap<String, String>();
settingsMap.putAll(transformer.getSettings());
settingsMap.putAll(AgentHelper.getSettings());
AgentServer.broadcastMessage(this, settingsMap);
}
else if (message.equals("help"))
{
Set<String> commandSet = new HashSet<String>();
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<String> 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);

View File

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