- Improve comments in the agent package

- Cleanup code in agent package
This commit is contained in:
Martin Hare Robertson 2010-04-12 17:39:41 +01:00
parent 2b4c9862c3
commit e3cf53ac0f
9 changed files with 356 additions and 243 deletions

View File

@ -4,5 +4,6 @@
<classpathentry kind="lib" path="lib/traceagent.jar" sourcepath="/org.intrace"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="C:/Program Files/Java/jdk1.6.0/lib/tools.jar"/>
<classpathentry kind="lib" path="genbin"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -1,7 +1,5 @@
package org.intrace.agent;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import org.intrace.agent.server.AgentServer;
@ -10,57 +8,66 @@ import org.intrace.output.callers.CallersOutput;
import org.intrace.output.trace.TraceOutput;
/**
* Trace Agent: Installs a Class Transformer to add trace lines.
* InTrace Agent: Installs a Class Transformer to instrument class bytecode.
*/
public class Agent
{
/**
* Agent called on JVM init.
* Entry point when loaded using -agent command line arg.
*
* @param agentArgs
* @param inst
* @throws IOException
*/
public static void premain(String agentArgs, Instrumentation inst) throws IOException
public static void premain(String agentArgs, Instrumentation inst)
{
initialize(agentArgs, inst);
}
/**
* Agent called after JVM init.
* Entry point when loaded into running JVM.
*
* @param agentArgs
* @param inst
* @throws IOException
*/
public static void agentmain(String agentArgs, Instrumentation inst) throws IOException
public static void agentmain(String agentArgs, Instrumentation inst)
{
initialize(agentArgs, inst);
}
/**
* Common init function.
*
* @param agentArgs
* @param inst
* @throws IOException
*/
private static void initialize(String agentArgs, Instrumentation inst) throws IOException
private static void initialize(String agentArgs, Instrumentation inst)
{
System.out.println("Loaded Tracing Agent.");
System.out.println("## Loaded InTrace Agent.");
if (agentArgs == null) agentArgs = "";
if (agentArgs == null)
{
agentArgs = "";
}
// Setup the output handlers
AgentHelper.outputHandlers.put(new TraceOutput(), new Object());
AgentHelper.outputHandlers.put(new CallersOutput(), new Object());
// Parse startup args
AgentSettings args = new AgentSettings(agentArgs);
AgentHelper.getResponses(agentArgs);
// Construct Transformer
ClassTransformer t = new ClassTransformer(inst, args);
inst.addTransformer(t, true);
t.traceLoadedClasses();
// Ensure loaded classes are traced
t.instrumentLoadedClasses();
// Start Server thread
Thread traceServer = new Thread(new AgentServer(t));
traceServer.setName("TraceServer");
traceServer.setDaemon(true);
traceServer.start();
}
}
}

View File

@ -16,7 +16,7 @@ import org.intrace.shared.AgentConfigConstants;
public class AgentSettings
{
private Pattern classRegex = Pattern.compile(".*");
private boolean tracingEnabled = false;
private boolean instruEnabled = false;
private boolean saveTracedClassfiles = false;
private boolean verboseMode = false;
private boolean allowJarsToBeTraced = false;
@ -29,7 +29,7 @@ public class AgentSettings
public AgentSettings(AgentSettings oldInstance)
{
classRegex = oldInstance.getClassRegex();
tracingEnabled = oldInstance.isTracingEnabled();
instruEnabled = oldInstance.isInstrumentationEnabled();
saveTracedClassfiles = oldInstance.saveTracedClassfiles();
verboseMode = oldInstance.isVerboseMode();
allowJarsToBeTraced = oldInstance.allowJarsToBeTraced();
@ -54,13 +54,13 @@ public class AgentSettings
{
verboseMode = false;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.TRACING_ENABLED + "true"))
else if (arg.toLowerCase().equals(AgentConfigConstants.INSTRU_ENABLED + "true"))
{
tracingEnabled = true;
instruEnabled = true;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.TRACING_ENABLED + "false"))
else if (arg.toLowerCase().equals(AgentConfigConstants.INSTRU_ENABLED + "false"))
{
tracingEnabled = false;
instruEnabled = false;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.SAVE_TRACED_CLASSFILES + "true"))
{
@ -90,9 +90,9 @@ public class AgentSettings
return classRegex;
}
public boolean isTracingEnabled()
public boolean isInstrumentationEnabled()
{
return tracingEnabled;
return instruEnabled;
}
public boolean saveTracedClassfiles()
@ -115,7 +115,7 @@ public class AgentSettings
{
String currentSettings = "";
currentSettings += "Class Regex : " + classRegex + "\n";
currentSettings += "Tracing Enabled : " + tracingEnabled + "\n";
currentSettings += "Tracing Enabled : " + instruEnabled + "\n";
currentSettings += "Save Traced Class Files : " + saveTracedClassfiles + "\n";
currentSettings += "Trace Classes in JAR Files : " + allowJarsToBeTraced + "\n";
return currentSettings;
@ -124,7 +124,7 @@ public class AgentSettings
public Map<String,String> getSettingsMap()
{
Map<String,String> settingsMap = new HashMap<String, String>();
settingsMap.put(AgentConfigConstants.TRACING_ENABLED, Boolean.toString(tracingEnabled));
settingsMap.put(AgentConfigConstants.INSTRU_ENABLED, Boolean.toString(instruEnabled));
settingsMap.put(AgentConfigConstants.CLASS_REGEX, classRegex.pattern());
settingsMap.put(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED, Boolean.toString(allowJarsToBeTraced));
settingsMap.put(AgentConfigConstants.VERBOSE_MODE, Boolean.toString(verboseMode));

View File

@ -12,56 +12,104 @@ import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.EmptyVisitor;
/**
* First pass analysis phase
* 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 ClassBranchLineAnalysis#visitLineNumber(int, Label)} is called and
* we record a mapping from label1 to line 5.
* </ul>
*
* <h3>Line 8:</h3>
* <ul>
* <li>{@link ClassBranchLineAnalysis#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 ClassBranchLineAnalysis extends EmptyVisitor
{
public final Map<String, Set<Integer>> methodBranchTraceLines = new HashMap<String, Set<Integer>>();
public final Map<String, Integer> methodEntryLine = new HashMap<String, Integer>();
private Set<Integer> branchTraceLines = new HashSet<Integer>();
private final Map<Label,Integer> methodLabelLineNos = new HashMap<Label,Integer>();
private String methodSig;
private boolean traceThisLine = false;
private boolean recordedMethodEntryLine = false;
// 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)
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions)
{
methodLabelLineNos.clear();
methodSig = name + desc;
recordedMethodEntryLine = false;
currentMethod_labelLineNos.clear();
currentMethod_sig = name + desc;
currentMethod_recordedEntryLine = false;
return this;
}
@Override
public void visitLineNumber(int xiLineNo, Label xiLabel)
{
if (!recordedMethodEntryLine)
if (!currentMethod_recordedEntryLine)
{
methodEntryLine.put(methodSig, xiLineNo);
recordedMethodEntryLine = true;
}
methodLabelLineNos.put(xiLabel, xiLineNo);
if (traceThisLine)
{
branchTraceLines.add(xiLineNo);
traceThisLine = false;
methodEntryLines.put(currentMethod_sig, xiLineNo);
currentMethod_recordedEntryLine = true;
}
currentMethod_labelLineNos.put(xiLabel, xiLineNo);
}
@Override
public void visitJumpInsn(int xiOpCode, Label xiBranchLabel)
{
if (xiOpCode != GOTO)
if (xiOpCode == GOTO)
{
traceThisLine= true;
}
else
{
Integer lineNo = methodLabelLineNos.get(xiBranchLabel);
Integer lineNo = currentMethod_labelLineNos.get(xiBranchLabel);
if (lineNo != null)
{
branchTraceLines.add(lineNo);
currentMethod_reverseGOTOLines.add(lineNo);
}
}
}
@ -69,11 +117,12 @@ public class ClassBranchLineAnalysis extends EmptyVisitor
@Override
public void visitEnd()
{
if (methodSig != null)
if (currentMethod_sig != null)
{
methodBranchTraceLines.put(methodSig, branchTraceLines);
methodSig = null;
branchTraceLines = new HashSet<Integer>();
methodReverseGOTOLines.put(currentMethod_sig,
currentMethod_reverseGOTOLines);
currentMethod_sig = null;
currentMethod_reverseGOTOLines = new HashSet<Integer>();
}
}
}

View File

@ -18,20 +18,28 @@ import org.intrace.output.AgentHelper;
import org.objectweb.asm.ClassReader;
/**
* Uses ASM2 to transform class files to add Trace output.
* Uses ASM2 to transform class files to add Trace instrumentation.
*/
public class ClassTransformer implements ClassFileTransformer
{
/**
* Map of modified class names to their original bytes
*/
private final Set<String> modifiedClasses =
new ConcurrentSkipListSet<String>();
private final Set<String> modifiedClasses = new ConcurrentSkipListSet<String>();
/**
* Instrumentation interface.
*/
private final Instrumentation inst;
private final AgentSettings args;
/**
* Settings for this Transformer
*/
private final AgentSettings settings;
/**
* cTor
*
* @param xiInst
* @param xiEnableTracing
* @param xiClassRegex
@ -42,81 +50,41 @@ public class ClassTransformer implements ClassFileTransformer
public ClassTransformer(Instrumentation xiInst, AgentSettings xiArgs)
{
inst = xiInst;
args = xiArgs;
if (args.isVerboseMode())
settings = xiArgs;
if (settings.isVerboseMode())
{
System.out.println(args.toString());
System.out.println(settings.toString());
}
}
/**
* Toggle whether tracing is enabled
* @param xiTracingEnabled
* Generate and return instrumented class bytes.
*
* @param xiClassName
* @param classfileBuffer
* @return Instrumented class bytes
*/
public void setTracingEnabled(boolean xiOldTracingEnabled)
private byte[] getInstrumentedClassBytes(String xiClassName,
byte[] classfileBuffer)
{
if (args.isTracingEnabled() && !xiOldTracingEnabled)
{
traceLoadedClasses();
}
else if (!args.isTracingEnabled() && xiOldTracingEnabled)
{
recheckModifiedClasses();
}
ClassReader cr = new ClassReader(classfileBuffer);
ClassBranchLineAnalysis analysis = new ClassBranchLineAnalysis();
cr.accept(analysis, false);
InstrumentedClassWriter writer = new InstrumentedClassWriter(xiClassName,
cr, analysis);
cr.accept(writer, false);
return writer.toByteArray();
}
/**
* Apply Trace transformation to loaded classes.
* Retransform all modified classes.
* <p>
* Iterates over all loaded classes and retransforms those which we know we
* have modified.
*
* @param xiInst
*/
public void traceLoadedClasses()
{
Class<?>[] loadedClasses = inst.getAllLoadedClasses();
for (Class<?> loadedClass : loadedClasses)
{
if (loadedClass.isAnnotation())
{
if (args.isVerboseMode())
{
System.out.println("Ignoring annotation class: " + loadedClass.getCanonicalName());
}
}
else if (loadedClass.isSynthetic())
{
if (args.isVerboseMode())
{
System.out.println("Ignoring synthetic class: " + loadedClass.getCanonicalName());
}
}
else if (!inst.isModifiableClass(loadedClass))
{
if (args.isVerboseMode())
{
System.out.println("Ignoring unmodifiable class: " + loadedClass.getCanonicalName());
}
}
else if (args.isTracingEnabled() &&
isToBeConsideredForCoverage(loadedClass.getName(), loadedClass.getProtectionDomain()))
{
try
{
inst.retransformClasses(loadedClass);
}
catch (Throwable e)
{
// Write exception to stdout
System.out.println(loadedClass.getName());
e.printStackTrace();
}
}
}
}
/**
* Restore original class bytes
* @param xiInst
*/
private void recheckModifiedClasses()
private void retransformModifiedClasses()
{
Class<?>[] loadedClasses = inst.getAllLoadedClasses();
for (Class<?> loadedClass : loadedClasses)
@ -136,16 +104,40 @@ public class ClassTransformer implements ClassFileTransformer
}
}
private boolean isToBeConsideredForCoverage(String className,
ProtectionDomain protectionDomain)
/**
* Determine whether a given className is eligible for modification. Any of
* the following conditions will make a class ineligible for instrumentation.
* <ul>
* <li>Class name which begins with "org.intrace"
* <li>Class name which begins with "org.objectweb.asm"
* <li>The class has already been modified
* <li>Class name ends with "Test"
* <li>Class name doesn't match the regex
* <li>Class is in a JAR and JAR instrumention is disabled
* </ul>
*
* @param className
* @param protectionDomain
* @return True if the Class with name className should be instrumented.
*/
private boolean isToBeConsideredForInstrumentation(
String className,
ProtectionDomain protectionDomain)
{
// Don't modify self
if (className.startsWith("org.intrace") ||
className.startsWith("org.objectweb.asm"))
// Don't modify anything if tracing is disabled
if (!settings.isInstrumentationEnabled())
{
if (args.isVerboseMode())
return false;
}
// Don't modify self
if (className.startsWith("org.intrace")
|| className.startsWith("org.objectweb.asm"))
{
if (settings.isVerboseMode())
{
System.out.println("Ignoring class in gb.instrument package: " + className);
System.out.println("Ignoring class in gb.instrument package: "
+ className);
}
return false;
}
@ -153,7 +145,7 @@ public class ClassTransformer implements ClassFileTransformer
// Don't modify a class which is already modified
if (modifiedClasses.contains(className))
{
if (args.isVerboseMode())
if (settings.isVerboseMode())
{
System.out.println("Ignoring class already modified: " + className);
}
@ -165,7 +157,7 @@ public class ClassTransformer implements ClassFileTransformer
if (className.endsWith("Test") || p > 0
&& className.substring(0, p).endsWith("Test"))
{
if (args.isVerboseMode())
if (settings.isVerboseMode())
{
System.out.println("Ignoring class name ending in Test: " + className);
}
@ -173,54 +165,63 @@ public class ClassTransformer implements ClassFileTransformer
}
// Don't modify classes which fail to match the regex
if ((args.getClassRegex() == null) ||
!args.getClassRegex().matcher(className).matches())
if ((settings.getClassRegex() == null)
|| !settings.getClassRegex().matcher(className).matches())
{
if (args.isVerboseMode())
if (settings.isVerboseMode())
{
System.out.println("Ignoring class not matching the active regex: " + className);
System.out.println("Ignoring class not matching the active regex: "
+ className);
}
return false;
}
// Don't modify a class from a JAR file unless this is allowed
CodeSource codeSource = protectionDomain.getCodeSource();
if (!args.allowJarsToBeTraced() &&
(codeSource != null) &&
codeSource.getLocation().getPath().endsWith(".jar"))
if (!settings.allowJarsToBeTraced() && (codeSource != null)
&& codeSource.getLocation().getPath().endsWith(".jar"))
{
if (args.isVerboseMode())
if (settings.isVerboseMode())
{
System.out.println("Ignoring class in a JAR: " + className);
}
return false;
}
// All checks passed - class can be instrumented
return true;
}
/**
* java.lang.instrument Entry Point
* <p>
* Optionally transform a class file to add instrumentation.
* {@link ClassTransformer#isToBeConsideredForInstrumentation(String, ProtectionDomain)}
* determines whether a class is eligible for instrumentation.
*/
@Override
public byte[] transform(ClassLoader loader,
String internalClassName,
public byte[] transform(ClassLoader loader, String internalClassName,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] originalClassfile)
throws IllegalClassFormatException
throws IllegalClassFormatException
{
String className = internalClassName.replace('/', '.');
if (args.isTracingEnabled() &&
isToBeConsideredForCoverage(className, protectionDomain))
if (isToBeConsideredForInstrumentation(className, protectionDomain))
{
if (args.isVerboseMode())
if (settings.isVerboseMode())
{
System.out.println("!! Instrumenting class: " + className);
}
byte[] newBytes = readAndModifyClassForTracing(className, originalClassfile);
if (args.saveTracedClassfiles())
byte[] newBytes = getInstrumentedClassBytes(className, originalClassfile);
if (settings.saveTracedClassfiles())
{
try
{
File classOut = new File("./genbin/" + internalClassName + "_gen.class");
File classOut = new File("./genbin/" + internalClassName
+ "_gen.class");
File parentDir = classOut.getParentFile();
boolean dirExists = parentDir.exists();
if (!dirExists)
@ -236,8 +237,8 @@ public class ClassTransformer implements ClassFileTransformer
}
else
{
System.out.println("Can't create directory " + parentDir +
" for saving traced classfiles.");
System.out.println("Can't create directory " + parentDir
+ " for saving traced classfiles.");
}
}
catch (Exception e)
@ -256,58 +257,134 @@ public class ClassTransformer implements ClassFileTransformer
}
}
private byte[] readAndModifyClassForTracing(String xiClassName,
byte[] classfileBuffer)
/**
* Consider loaded classes for transformation. Any of the following reasons
* would prevent a loaded class from being eligible for instrumentation.
* <ul>
* <li>Class is an annotation
* <li>Class is synthetic
* <li>Class is not modifiable
* <li>Class is rejected by
* {@link ClassTransformer#isToBeConsideredForInstrumentation(String, ProtectionDomain)}
* </ul>
*/
public void instrumentLoadedClasses()
{
ClassReader cr = new ClassReader(classfileBuffer);
ClassBranchLineAnalysis analysis = new ClassBranchLineAnalysis();
cr.accept(analysis, false);
InstrumentedClassWriter writer = new InstrumentedClassWriter(xiClassName,
cr,
analysis);
cr.accept(writer, false);
return writer.toByteArray();
Class<?>[] loadedClasses = inst.getAllLoadedClasses();
for (Class<?> loadedClass : loadedClasses)
{
if (loadedClass.isAnnotation())
{
if (settings.isVerboseMode())
{
System.out.println("Ignoring annotation class: "
+ loadedClass.getCanonicalName());
}
}
else if (loadedClass.isSynthetic())
{
if (settings.isVerboseMode())
{
System.out.println("Ignoring synthetic class: "
+ loadedClass.getCanonicalName());
}
}
else if (!inst.isModifiableClass(loadedClass))
{
if (settings.isVerboseMode())
{
System.out.println("Ignoring unmodifiable class: "
+ loadedClass.getCanonicalName());
}
}
else if (isToBeConsideredForInstrumentation(
loadedClass.getName(),
loadedClass
.getProtectionDomain()))
{
try
{
inst.retransformClasses(loadedClass);
}
catch (Throwable e)
{
// Write exception to stdout
System.out.println(loadedClass.getName());
e.printStackTrace();
}
}
}
}
/**
* Toggle whether instrumentation is enabled
*
* @param xiTracingEnabled
*/
public void setInstrumentationEnabled(boolean xiInstrumentationEnabled)
{
if (xiInstrumentationEnabled)
{
instrumentLoadedClasses();
}
else if (!xiInstrumentationEnabled)
{
retransformModifiedClasses();
}
}
/**
* @return The currently active settings.
*/
public Map<String, String> getSettings()
{
return settings.getSettingsMap();
}
/**
* Handle a message and return a response.
*
* @param message
* @return Response or null if there is no response.
*/
public List<String> getResponse(String message)
{
AgentSettings oldSettings = new AgentSettings(args);
args.parseArgs(message);
AgentSettings oldSettings = new AgentSettings(settings);
settings.parseArgs(message);
if (args.isVerboseMode() &&
(oldSettings.isVerboseMode() != args.isVerboseMode()))
if (settings.isVerboseMode()
&& (oldSettings.isVerboseMode() != settings.isVerboseMode()))
{
System.out.println(args.toString());
System.out.println(settings.toString());
}
else if (oldSettings.isTracingEnabled() != args.isTracingEnabled())
else if (oldSettings.isInstrumentationEnabled() != settings
.isInstrumentationEnabled())
{
System.out.println("## Settings Changed");
setTracingEnabled(oldSettings.isTracingEnabled());
setInstrumentationEnabled(settings.isInstrumentationEnabled());
}
else if (!oldSettings.getClassRegex().pattern().equals(args.getClassRegex().pattern()))
else if (!oldSettings.getClassRegex().pattern()
.equals(settings.getClassRegex().pattern()))
{
System.out.println("## Settings Changed");
recheckModifiedClasses();
traceLoadedClasses();
retransformModifiedClasses();
instrumentLoadedClasses();
}
else if (oldSettings.allowJarsToBeTraced() != args.allowJarsToBeTraced())
else if (oldSettings.allowJarsToBeTraced() != settings
.allowJarsToBeTraced())
{
System.out.println("## Settings Changed");
recheckModifiedClasses();
traceLoadedClasses();
retransformModifiedClasses();
instrumentLoadedClasses();
}
else if (oldSettings.saveTracedClassfiles() != args.saveTracedClassfiles())
else if (oldSettings.saveTracedClassfiles() != settings
.saveTracedClassfiles())
{
System.out.println("## Settings Changed");
recheckModifiedClasses();
traceLoadedClasses();
retransformModifiedClasses();
instrumentLoadedClasses();
}
return AgentHelper.getResponses(message);
}
public Map<String, String> getSettings()
{
return args.getSettingsMap();
}
}

View File

@ -36,8 +36,8 @@ public class InstrumentedClassWriter extends ClassWriter
{
MethodVisitor mv = super.visitMethod(access, name, desc, signature,
exceptions);
Set<Integer> branchTraceLines = analysis.methodBranchTraceLines.get(name + desc);
Integer entryLine = analysis.methodEntryLine.get(name + desc);
Set<Integer> branchTraceLines = analysis.methodReverseGOTOLines.get(name + desc);
Integer entryLine = analysis.methodEntryLines.get(name + desc);
if (branchTraceLines == null)
{
branchTraceLines = new HashSet<Integer>();

View File

@ -12,7 +12,7 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* ASM2 MethodVisitor used to add trace to methods.
* ASM2 MethodVisitor used to instrument methods.
*/
public class InstrumentedMethodWriter extends MethodAdapter
{
@ -29,6 +29,7 @@ public class InstrumentedMethodWriter extends MethodAdapter
/**
* cTor
*
* @param xiMethodVisitor
* @param xiClassName
* @param xiMethodName
@ -37,11 +38,8 @@ public class InstrumentedMethodWriter extends MethodAdapter
* @param entryLine
*/
public InstrumentedMethodWriter(MethodVisitor xiMethodVisitor,
String xiClassName,
String xiMethodName,
String xiDesc,
Set<Integer> xiBranchTraceLines,
Integer xiEntryLine)
String xiClassName, String xiMethodName, String xiDesc,
Set<Integer> xiBranchTraceLines, Integer xiEntryLine)
{
super(xiMethodVisitor);
className = xiClassName;
@ -54,12 +52,13 @@ public class InstrumentedMethodWriter extends MethodAdapter
@Override
public void visitCode()
{
generateCallToWriteBranchTrace(TraceType.BEGIN,((entryLine != null ? entryLine : -1)));
traceArgs();
generateCallToWriteTrace(TraceType.BEGIN, ((entryLine != null ? entryLine
: -1)));
traceMethodArgs();
super.visitCode();
}
private void traceArgs()
private void traceMethodArgs()
{
Type[] argTypes = Type.getArgumentTypes(methodDescriptor);
for (int ii = 0; ii < argTypes.length; ii++)
@ -70,8 +69,8 @@ public class InstrumentedMethodWriter extends MethodAdapter
{
typeDescriptor = "Ljava/lang/Object;";
}
else if ((argTypes[ii].getSort() == Type.ARRAY) &&
(argTypes[ii].getDescriptor().startsWith("[L")))
else if ((argTypes[ii].getSort() == Type.ARRAY)
&& (argTypes[ii].getDescriptor().startsWith("[L")))
{
typeDescriptor = "[Ljava/lang/Object;";
}
@ -79,10 +78,9 @@ public class InstrumentedMethodWriter extends MethodAdapter
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitVarInsn(Opcodes.ALOAD, ii);
mv.visitMethodInsn(INVOKESTATIC,
HELPER_CLASS,
"arg",
"(Ljava/lang/String;Ljava/lang/String;" + typeDescriptor + ")V");
mv.visitMethodInsn(INVOKESTATIC, HELPER_CLASS, "arg",
"(Ljava/lang/String;Ljava/lang/String;"
+ typeDescriptor + ")V");
}
}
@ -90,14 +88,10 @@ public class InstrumentedMethodWriter extends MethodAdapter
public void visitLineNumber(int xiLineNumber, Label label)
{
lineNumber = xiLineNumber;
if (writeTraceLine ||
branchTraceLines.contains(xiLineNumber))
if (writeTraceLine || branchTraceLines.contains(xiLineNumber))
{
generateCallToWriteBranchTrace(TraceType.BRANCH, lineNumber);
if (writeTraceLine)
{
writeTraceLine = false;
}
generateCallToWriteTrace(TraceType.BRANCH, lineNumber);
writeTraceLine = false;
}
super.visitLineNumber(xiLineNumber, label);
}
@ -107,7 +101,7 @@ public class InstrumentedMethodWriter extends MethodAdapter
{
if (xiOpCode == RETURN)
{
generateCallToWriteBranchTrace(TraceType.END, lineNumber);
generateCallToWriteTrace(TraceType.END, lineNumber);
}
super.visitInsn(xiOpCode);
}
@ -119,53 +113,38 @@ public class InstrumentedMethodWriter extends MethodAdapter
super.visitJumpInsn(xiOpCode, xiBranchLabel);
}
private void generateCallToWriteBranchTrace(TraceType traceType,
int lineNumber)
private void generateCallToWriteTrace(TraceType traceType, int lineNumber)
{
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitIntInsn(Opcodes.BIPUSH, lineNumber);
switch (traceType)
{
case BEGIN:
{
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitIntInsn(Opcodes.BIPUSH, lineNumber);
mv.visitMethodInsn(INVOKESTATIC,
HELPER_CLASS,
"enter",
"(Ljava/lang/String;Ljava/lang/String;I)V");
mv.visitMethodInsn(INVOKESTATIC, HELPER_CLASS, "enter",
"(Ljava/lang/String;Ljava/lang/String;I)V");
}
break;
break;
case BRANCH:
{
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitIntInsn(Opcodes.BIPUSH, lineNumber);
mv.visitMethodInsn(INVOKESTATIC,
HELPER_CLASS,
"branch",
"(Ljava/lang/String;Ljava/lang/String;I)V");
mv.visitMethodInsn(INVOKESTATIC, HELPER_CLASS, "branch",
"(Ljava/lang/String;Ljava/lang/String;I)V");
}
break;
break;
case END:
{
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitIntInsn(Opcodes.BIPUSH, lineNumber);
mv.visitMethodInsn(INVOKESTATIC,
HELPER_CLASS,
"exit",
"(Ljava/lang/String;Ljava/lang/String;I)V");
mv.visitMethodInsn(INVOKESTATIC, HELPER_CLASS, "exit",
"(Ljava/lang/String;Ljava/lang/String;I)V");
}
break;
break;
}
}
private enum TraceType
{
BEGIN,
BRANCH,
END
BEGIN, BRANCH, END
}
}

View File

@ -34,7 +34,7 @@ public class ParsedSettingsData
classRegex = settingsMap.get(AgentConfigConstants.CLASS_REGEX);
if ("true".equals(settingsMap.get(AgentConfigConstants.TRACING_ENABLED)))
if ("true".equals(settingsMap.get(AgentConfigConstants.INSTRU_ENABLED)))
{
instrEnabled = true;
}

View File

@ -5,16 +5,16 @@ import java.util.Set;
public class AgentConfigConstants
{
public static final String CLASS_REGEX = "[regex-";
public static final String TRACING_ENABLED = "[instru-";
public static final String SAVE_TRACED_CLASSFILES = "[saveinstru-";
public static final String VERBOSE_MODE = "[verbose-";
public static final String CLASS_REGEX = "[regex-";
public static final String INSTRU_ENABLED = "[instru-";
public static final String SAVE_TRACED_CLASSFILES = "[saveinstru-";
public static final String VERBOSE_MODE = "[verbose-";
public static final String ALLOW_JARS_TO_BE_TRACED = "[instrujars-";
public static final Set<String> COMMANDS = new HashSet<String>();
static
{
COMMANDS.add(CLASS_REGEX + "<regex>");
COMMANDS.add(TRACING_ENABLED + "<true/false>");
COMMANDS.add(INSTRU_ENABLED + "<true/false>");
COMMANDS.add(SAVE_TRACED_CLASSFILES + "<true/false>");
COMMANDS.add(VERBOSE_MODE + "<true/false>");
COMMANDS.add(ALLOW_JARS_TO_BE_TRACED + "<true/false>");