- Add trace to the core output classes

This commit is contained in:
Martin Hare Robertson 2010-04-13 20:24:17 +01:00
parent df88c16700
commit 3eeee84c14
7 changed files with 285 additions and 59 deletions

View File

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

View File

@ -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.
* <p>
* This analysis collects two sets of data.
* <ul>
* <li>Reverse GOTO Lines
* <li>Method Entry Line
* </ul>
* <h1>Reverse GOTO Lines</h1> This analysis records the target line number of
* GOTOs that jump backwards in the code.
*
* <h2>Example:</h2>
*
* <pre>
* 1. public void method()
* 2. {
* 3. do
* 4. {
* 5. // Branch C
* 6. // Branch C
* 7. }
* 8. while (condition)
* 9. }
* </pre>
*
* We want to capture line 5 and line 9. Example 1 gets transformed into the
* following bytecode:
*
* <pre>
* 5. label1:
* 5. // Branch C
* 6. // Branch C
* 8. if (condition) goto label1
* </pre>
*
* <h3>Line 5:</h3>
* <ul>
* <li>{@link ClassAnalysis#visitLineNumber(int, Label)} is called and
* we record a mapping from label1 to line 5.
* </ul>
*
* <h3>Line 8:</h3>
* <ul>
* <li>{@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.
* </ul>
*
* <h1>Method Entry Line</h1> 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<String, Set<Integer>> methodReverseGOTOLines = new HashMap<String, Set<Integer>>();
public final Map<String, Integer> methodEntryLines = new HashMap<String, Integer>();
// Intermediate fields
private Set<Integer> currentMethod_reverseGOTOLines = new HashSet<Integer>();
private final Map<Label, Integer> currentMethod_labelLineNos = new HashMap<Label, Integer>();
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<Integer>();
}
}
}

View File

@ -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<IOutput,Object> outputHandlers = new ConcurrentHashMap<IOutput,Object>();
// Set of output handlers
public static final Map<IInstrumentationHandler, Object> instrumentationHandlers = new ConcurrentHashMap<IInstrumentationHandler, Object>();
// 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<NetworkDataSenderThread,Object> networkOutputThreads = new ConcurrentHashMap<NetworkDataSenderThread,Object>();
// Set of active network output threads
private static final Map<NetworkDataSenderThread, Object> networkOutputThreads = new ConcurrentHashMap<NetworkDataSenderThread, Object>();
/**
* @param agentArgs
* @return A List of responses from all of the {@link IInstrumentationHandler}
* s and the {@link AgentHelper} itself.
*/
public static List<String> getResponses(String agentArgs)
{
List<String> responses = new ArrayList<String>();
// 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<String, String> getSettings()
{
Map<String, String> settings = new HashMap<String, String>();
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.
* <ul>
* <li>StdOut
* <li>FileOut
* <li>NetworkOut
* </ul>
*
* @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);
}

View File

@ -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.
* <p>
* See the following classes for example implementations.
* <ul>
* <li>{@link TraceHandler}
* <li>{@link CallersHandler}
* </ul>
*/
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<String,String> getSettingsMap();
/**
* @return A map containing all active settings
*/
public Map<String, String> getSettingsMap();
}

View File

@ -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

View File

@ -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<String, Object> recordedData = new ConcurrentHashMap<String, Object>();
@ -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;
}

View File

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