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

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:

+ * + * + *

Line 8:

+ * + * + *

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;