diff --git a/org.intrace/src/org/intrace/agent/Agent.java b/org.intrace/src/org/intrace/agent/Agent.java
index a806c91..37627b4 100644
--- a/org.intrace/src/org/intrace/agent/Agent.java
+++ b/org.intrace/src/org/intrace/agent/Agent.java
@@ -4,11 +4,14 @@ import java.lang.instrument.Instrumentation;
import org.intrace.agent.server.AgentServer;
import org.intrace.output.AgentHelper;
-import org.intrace.output.callers.CallersOutput;
-import org.intrace.output.trace.TraceOutput;
+import org.intrace.output.IInstrumentationHandler;
+import org.intrace.output.callers.CallersHandler;
+import org.intrace.output.trace.TraceHandler;
/**
- * InTrace Agent: Installs a Class Transformer to instrument class bytecode.
+ * InTrace Agent: Installs a Class Transformer to instrument class bytecode. The
+ * Instrumentation adds calls to {@link AgentHelper} which allows for
+ * {@link IInstrumentationHandler}s to generate output.
*/
public class Agent
{
@@ -50,8 +53,8 @@ public class Agent
}
// Setup the output handlers
- AgentHelper.outputHandlers.put(new TraceOutput(), new Object());
- AgentHelper.outputHandlers.put(new CallersOutput(), new Object());
+ AgentHelper.instrumentationHandlers.put(new TraceHandler(), new Object());
+ AgentHelper.instrumentationHandlers.put(new CallersHandler(), new Object());
// Parse startup args
AgentSettings args = new AgentSettings(agentArgs);
diff --git a/org.intrace/src/org/intrace/agent/ClassAnalysis.java b/org.intrace/src/org/intrace/agent/ClassAnalysis.java
new file mode 100644
index 0000000..328d662
--- /dev/null
+++ b/org.intrace/src/org/intrace/agent/ClassAnalysis.java
@@ -0,0 +1,128 @@
+package org.intrace.agent;
+
+import static org.objectweb.asm.Opcodes.GOTO;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * InTrace uses ASM to instrument class files. However, ASM traverses bytecode
+ * linearly which means some information is not available when we need it. This
+ * class implements a first pass analysis phase to collect information which we
+ * can't collect during the transformation phase.
+ *
+ * This analysis collects two sets of data.
+ *
+ * Reverse GOTO Lines
+ * Method Entry Line
+ *
+ * Reverse GOTO Lines This analysis records the target line number of
+ * GOTOs that jump backwards in the code.
+ *
+ * Example:
+ *
+ *
+ * 1. public void method()
+ * 2. {
+ * 3. do
+ * 4. {
+ * 5. // Branch C
+ * 6. // Branch C
+ * 7. }
+ * 8. while (condition)
+ * 9. }
+ *
+ *
+ * We want to capture line 5 and line 9. Example 1 gets transformed into the
+ * following bytecode:
+ *
+ *
+ * 5. label1:
+ * 5. // Branch C
+ * 6. // Branch C
+ * 8. if (condition) goto label1
+ *
+ *
+ * Line 5:
+ *
+ * {@link ClassAnalysis#visitLineNumber(int, Label)} is called and
+ * we record a mapping from label1 to line 5.
+ *
+ *
+ * Line 8:
+ *
+ * {@link ClassAnalysis#visitJumpInsn(int, Label)} is called with
+ * a GOTO instruction so we check whether we have already seen the target label.
+ * In this case, we have so we mark the target line number as a reverse GOTO.
+ *
+ *
+ * Method Entry Line This analysis records the first source line of a
+ * method. This is necessary so that the transformation phase can add an entry
+ * trace line in the call to {@link InstrumentedMethodWriter#visitCode()} and
+ * know the source line.
+ */
+public class ClassAnalysis extends EmptyVisitor
+{
+ // Output of this analysis
+ public final Map> methodReverseGOTOLines = new HashMap>();
+ public final Map methodEntryLines = new HashMap();
+
+ // Intermediate fields
+ private Set currentMethod_reverseGOTOLines = new HashSet();
+ private final Map currentMethod_labelLineNos = new HashMap();
+ private String currentMethod_sig;
+ private boolean currentMethod_recordedEntryLine = false;
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions)
+ {
+ currentMethod_labelLineNos.clear();
+ currentMethod_sig = name + desc;
+ currentMethod_recordedEntryLine = false;
+ return this;
+ }
+
+ @Override
+ public void visitLineNumber(int xiLineNo, Label xiLabel)
+ {
+ if (!currentMethod_recordedEntryLine)
+ {
+ methodEntryLines.put(currentMethod_sig, xiLineNo);
+ currentMethod_recordedEntryLine = true;
+ }
+
+ currentMethod_labelLineNos.put(xiLabel, xiLineNo);
+ }
+
+ @Override
+ public void visitJumpInsn(int xiOpCode, Label xiBranchLabel)
+ {
+ if (xiOpCode == GOTO)
+ {
+ Integer lineNo = currentMethod_labelLineNos.get(xiBranchLabel);
+ if (lineNo != null)
+ {
+ currentMethod_reverseGOTOLines.add(lineNo);
+ }
+ }
+ }
+
+ @Override
+ public void visitEnd()
+ {
+ if (currentMethod_sig != null)
+ {
+ methodReverseGOTOLines.put(currentMethod_sig,
+ currentMethod_reverseGOTOLines);
+ currentMethod_sig = null;
+ currentMethod_reverseGOTOLines = new HashSet();
+ }
+ }
+}
diff --git a/org.intrace/src/org/intrace/output/AgentHelper.java b/org.intrace/src/org/intrace/output/AgentHelper.java
index 1cd3a39..519b5ac 100644
--- a/org.intrace/src/org/intrace/output/AgentHelper.java
+++ b/org.intrace/src/org/intrace/output/AgentHelper.java
@@ -12,31 +12,46 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-
-
/**
- * Static implementation of the TraceWriter interface
+ * Static implementation of the {@link IInstrumentationHandler} interface
*/
public class AgentHelper
{
- public static final Map outputHandlers = new ConcurrentHashMap();
+ // Set of output handlers
+ public static final Map instrumentationHandlers = new ConcurrentHashMap();
+ // Output Settings
private static OutputSettings outputSettings = new OutputSettings("");
+
+ // Flag to indicate whether file output is currently going to file1 or file2
private static boolean file1Active = true;
+
+ // Variable for tracking the number of bytes written to the output files
private static int writtenChars = 0;
private static final int MAX_CHARS_PER_FILE = 100 * 1000; // 100kb
- private static final Map networkOutputThreads = new ConcurrentHashMap();
+ // Set of active network output threads
+ private static final Map networkOutputThreads = new ConcurrentHashMap();
+ /**
+ * @param agentArgs
+ * @return A List of responses from all of the {@link IInstrumentationHandler}
+ * s and the {@link AgentHelper} itself.
+ */
public static List getResponses(String agentArgs)
{
List responses = new ArrayList();
+
+ // Get the response from the AgentHelper
String response = getResponse(agentArgs);
if (response != null)
{
responses.add(response);
}
- for (IOutput outputHandler : outputHandlers.keySet())
+
+ // Get responses from all of the IInstrumentationHandlers
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
response = outputHandler.getResponse(agentArgs);
if (response != null)
@@ -47,14 +62,20 @@ public class AgentHelper
return responses;
}
+ /**
+ * @param args
+ * @return The response to the given args or null if no response is required.
+ * The only response currently implemented is sending back the local
+ * port for a new network data connection.
+ */
private static String getResponse(String args)
{
boolean oldStdOutEnabled = outputSettings.isStdoutTraceOutputEnabled();
boolean oldFileOutEnabled = outputSettings.isFileTraceOutputEnabled();
outputSettings.parseArgs(args);
- if ((oldStdOutEnabled != outputSettings.isStdoutTraceOutputEnabled()) ||
- (oldFileOutEnabled != outputSettings.isFileTraceOutputEnabled()))
+ if ((oldStdOutEnabled != outputSettings.isStdoutTraceOutputEnabled())
+ || (oldFileOutEnabled != outputSettings.isFileTraceOutputEnabled()))
{
System.out.println("## Output Settings Changed");
}
@@ -66,7 +87,8 @@ public class AgentHelper
try
{
networkSocket = new ServerSocket(0);
- NetworkDataSenderThread networkOutputThread = new NetworkDataSenderThread(networkSocket);
+ NetworkDataSenderThread networkOutputThread = new NetworkDataSenderThread(
+ networkSocket);
networkOutputThreads.put(networkOutputThread, new Object());
@@ -86,11 +108,16 @@ public class AgentHelper
}
}
+ /**
+ * @return All of the currently active settings for the {@link AgentHelper}
+ * along with all of the active {@link IInstrumentationHandler}s
+ */
public static Map getSettings()
{
Map settings = new HashMap();
settings.putAll(outputSettings.getSettingsMap());
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
settings.putAll(outputHandler.getSettingsMap());
}
@@ -98,16 +125,21 @@ public class AgentHelper
}
/**
- * Write output
+ * Write output to zero or more of the following.
+ *
+ * StdOut
+ * FileOut
+ * NetworkOut
+ *
*
- * @param xiTrace
+ * @param xiOutput
*/
public static void writeOutput(String xiOutput)
{
SimpleDateFormat dateFormat = new SimpleDateFormat();
long threadID = Thread.currentThread().getId();
- String traceString = "[" + dateFormat.format(new Date()) + "]:[" +
- threadID + "]:" + xiOutput;
+ String traceString = "[" + dateFormat.format(new Date()) + "]:[" + threadID
+ + "]:" + xiOutput;
if (outputSettings.isStdoutTraceOutputEnabled())
{
System.out.println(traceString);
@@ -129,7 +161,7 @@ public class AgentHelper
}
/**
- * Write data output
+ * Write data output to all network data connections.
*
* @param xiTrace
*/
@@ -175,7 +207,8 @@ public class AgentHelper
public static void enter(String className, String methodName, int lineNo)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.enter(className, methodName, lineNo);
}
@@ -183,15 +216,18 @@ public class AgentHelper
public static void arg(String className, String methodName, byte byteArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, byteArg);
}
}
- public static void arg(String className, String methodName, byte[] byteArrayArg)
+ public static void arg(String className, String methodName,
+ byte[] byteArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, byteArrayArg);
}
@@ -199,15 +235,18 @@ public class AgentHelper
public static void arg(String className, String methodName, short shortArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, shortArg);
}
}
- public static void arg(String className, String methodName, short[] shortArrayArg)
+ public static void arg(String className, String methodName,
+ short[] shortArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, shortArrayArg);
}
@@ -215,7 +254,8 @@ public class AgentHelper
public static void arg(String className, String methodName, int intArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, intArg);
}
@@ -223,7 +263,8 @@ public class AgentHelper
public static void arg(String className, String methodName, int[] intArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, intArrayArg);
}
@@ -231,15 +272,18 @@ public class AgentHelper
public static void arg(String className, String methodName, long longArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, longArg);
}
}
- public static void arg(String className, String methodName, long[] longArrayArg)
+ public static void arg(String className, String methodName,
+ long[] longArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, longArrayArg);
}
@@ -247,15 +291,18 @@ public class AgentHelper
public static void arg(String className, String methodName, float floatArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, floatArg);
}
}
- public static void arg(String className, String methodName, float[] floatArrayArg)
+ public static void arg(String className, String methodName,
+ float[] floatArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, floatArrayArg);
}
@@ -263,15 +310,18 @@ public class AgentHelper
public static void arg(String className, String methodName, double doubleArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, doubleArg);
}
}
- public static void arg(String className, String methodName, double[] doubleArrayArg)
+ public static void arg(String className, String methodName,
+ double[] doubleArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, doubleArrayArg);
}
@@ -279,15 +329,18 @@ public class AgentHelper
public static void arg(String className, String methodName, boolean boolArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, boolArg);
}
}
- public static void arg(String className, String methodName, boolean[] boolArrayArg)
+ public static void arg(String className, String methodName,
+ boolean[] boolArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, boolArrayArg);
}
@@ -295,15 +348,18 @@ public class AgentHelper
public static void arg(String className, String methodName, char charArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, charArg);
}
}
- public static void arg(String className, String methodName, char[] charArrayArg)
+ public static void arg(String className, String methodName,
+ char[] charArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, charArrayArg);
}
@@ -311,15 +367,18 @@ public class AgentHelper
public static void arg(String className, String methodName, Object objArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, objArg);
}
}
- public static void arg(String className, String methodName, Object[] objArrayArg)
+ public static void arg(String className, String methodName,
+ Object[] objArrayArg)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.arg(className, methodName, objArrayArg);
}
@@ -327,7 +386,8 @@ public class AgentHelper
public static void branch(String className, String methodName, int lineNo)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.branch(className, methodName, lineNo);
}
@@ -335,7 +395,8 @@ public class AgentHelper
public static void exit(String className, String methodName, int lineNo)
{
- for (IOutput outputHandler : outputHandlers.keySet())
+ for (IInstrumentationHandler outputHandler : instrumentationHandlers
+ .keySet())
{
outputHandler.exit(className, methodName, lineNo);
}
diff --git a/org.intrace/src/org/intrace/output/IOutput.java b/org.intrace/src/org/intrace/output/IInstrumentationHandler.java
similarity index 70%
rename from org.intrace/src/org/intrace/output/IOutput.java
rename to org.intrace/src/org/intrace/output/IInstrumentationHandler.java
index be706a9..654c9f6 100644
--- a/org.intrace/src/org/intrace/output/IOutput.java
+++ b/org.intrace/src/org/intrace/output/IInstrumentationHandler.java
@@ -2,39 +2,73 @@ package org.intrace.output;
import java.util.Map;
+import org.intrace.output.callers.CallersHandler;
+import org.intrace.output.trace.TraceHandler;
+
/**
- * Output generated by the instrumentation
+ * Interface for classes which handle the output generated by instrumentation.
+ *
+ * See the following classes for example implementations.
+ *
+ * {@link TraceHandler}
+ * {@link CallersHandler}
+ *
*/
-public interface IOutput
+public interface IInstrumentationHandler
{
+ // Output handling methods
public void enter(String className, String methodName, int lineNo);
public void arg(String className, String methodName, byte byteArg);
+
public void arg(String className, String methodName, byte[] byteArrayArg);
+
public void arg(String className, String methodName, short shortArg);
+
public void arg(String className, String methodName, short[] shortArrayArg);
public void arg(String className, String methodName, int intArg);
+
public void arg(String className, String methodName, int[] intArrayArg);
+
public void arg(String className, String methodName, long longArg);
+
public void arg(String className, String methodName, long[] longArrayArg);
public void arg(String className, String methodName, float floatArg);
+
public void arg(String className, String methodName, float[] floatArrayArg);
+
public void arg(String className, String methodName, double doubleArg);
+
public void arg(String className, String methodName, double[] doubleArrayArg);
public void arg(String className, String methodName, boolean boolArg);
+
public void arg(String className, String methodName, boolean[] boolArrayArg);
+
public void arg(String className, String methodName, char charArg);
+
public void arg(String className, String methodName, char[] charArrayArg);
public void arg(String className, String methodName, Object objArg);
+
public void arg(String className, String methodName, Object[] objArrayArg);
public void branch(String className, String methodName, int lineNo);
+
public void exit(String className, String methodName, int lineNo);
+ /**
+ * Handle String message
+ *
+ * @param args
+ * @return Response or null if no response is required.
+ */
public String getResponse(String args);
- public Map getSettingsMap();
+
+ /**
+ * @return A map containing all active settings
+ */
+ public Map getSettingsMap();
}
diff --git a/org.intrace/src/org/intrace/output/IOutputAdapter.java b/org.intrace/src/org/intrace/output/IInstrumentationHandlerAdapter.java
similarity index 92%
rename from org.intrace/src/org/intrace/output/IOutputAdapter.java
rename to org.intrace/src/org/intrace/output/IInstrumentationHandlerAdapter.java
index 14962f2..872292e 100644
--- a/org.intrace/src/org/intrace/output/IOutputAdapter.java
+++ b/org.intrace/src/org/intrace/output/IInstrumentationHandlerAdapter.java
@@ -3,7 +3,7 @@ package org.intrace.output;
import java.util.HashMap;
import java.util.Map;
-public class IOutputAdapter implements IOutput
+public class IInstrumentationHandlerAdapter implements IInstrumentationHandler
{
@Override
diff --git a/org.intrace/src/org/intrace/output/callers/CallersOutput.java b/org.intrace/src/org/intrace/output/callers/CallersHandler.java
similarity index 90%
rename from org.intrace/src/org/intrace/output/callers/CallersOutput.java
rename to org.intrace/src/org/intrace/output/callers/CallersHandler.java
index 424d2bf..4cef00e 100644
--- a/org.intrace/src/org/intrace/output/callers/CallersOutput.java
+++ b/org.intrace/src/org/intrace/output/callers/CallersHandler.java
@@ -5,10 +5,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.intrace.output.AgentHelper;
-import org.intrace.output.IOutputAdapter;
+import org.intrace.output.IInstrumentationHandlerAdapter;
import org.intrace.shared.CallersConfigConstants;
-public class CallersOutput extends IOutputAdapter
+public class CallersHandler extends IInstrumentationHandlerAdapter
{
private final CallersSettings callersSettings = new CallersSettings("");
private final Map recordedData = new ConcurrentHashMap();
@@ -96,8 +96,8 @@ public class CallersOutput extends IOutputAdapter
{
private boolean running = true;
private Thread thread;
- private final CallersOutput callersRef;
- public CaptureInProgress(CallersOutput callersOutput)
+ private final CallersHandler callersRef;
+ public CaptureInProgress(CallersHandler callersOutput)
{
callersRef = callersOutput;
}
diff --git a/org.intrace/src/org/intrace/output/trace/TraceOutput.java b/org.intrace/src/org/intrace/output/trace/TraceHandler.java
similarity index 94%
rename from org.intrace/src/org/intrace/output/trace/TraceOutput.java
rename to org.intrace/src/org/intrace/output/trace/TraceHandler.java
index 61ff26b..8391219 100644
--- a/org.intrace/src/org/intrace/output/trace/TraceOutput.java
+++ b/org.intrace/src/org/intrace/output/trace/TraceHandler.java
@@ -5,12 +5,12 @@ import java.util.Arrays;
import java.util.Map;
import org.intrace.output.AgentHelper;
-import org.intrace.output.IOutput;
+import org.intrace.output.IInstrumentationHandler;
/**
* Implements Standard Output Tracing
*/
-public class TraceOutput implements IOutput
+public class TraceHandler implements IInstrumentationHandler
{
private boolean entryExitTrace = true;
private boolean branchTrace = false;