- Add unit tests

- Fix array argument passing
- Upgrade to asm 3.2
- Checkin modified asm library with fix for synthetic bit handling
This commit is contained in:
Martin Hare Robertson 2010-04-18 09:12:25 +01:00
parent 37e14ce43a
commit e9760a29c2
26 changed files with 1790 additions and 118 deletions

5
.gitignore vendored
View File

@ -1,3 +1,6 @@
.metadata/
*/bin/*
*/built/*
*/built/*
*/genbin/*
asm3.2_*
libsrc*

Binary file not shown.

View File

@ -0,0 +1,161 @@
package org.test.intrace;
import java.util.List;
import java.util.Map;
import com.sun.jdi.Bootstrap;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.AttachingConnector;
import com.sun.jdi.connect.Connector.Argument;
public class DebugTest
{
private static final String field = "Foo";
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
int ii = 10;
threadID(Thread.currentThread().getId());
System.out.println(ii);
System.out.println(field);
}
private static void threadID(long id)
{
Thread dbg = new Thread(new Debugger(Thread.currentThread().getId(),
Thread.currentThread().getName()));
dbg.start();
try
{
dbg.join();
}
catch (InterruptedException e)
{
// Throw away
e.printStackTrace();
}
}
public static class Debugger implements Runnable
{
private final String name;
private final long id;
public Debugger(long id, String name)
{
this.id = id;
this.name = name;
}
@Override
public void run()
{
VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
List<AttachingConnector> connectors = vmm.attachingConnectors();
for (AttachingConnector connector : connectors)
{
if (connector.name().contains("SocketAttach"))
{
try
{
Map<String, Argument> args = connector.defaultArguments();
args.get("hostname").setValue("localhost");
args.get("port").setValue("8000");
VirtualMachine vm = connector.attach(args);
useVM(vm);
vm.dispose();
}
catch (Exception e)
{
e.printStackTrace();
}
break;
}
}
}
private void useVM(VirtualMachine vm)
{
List<ThreadReference> threads = vm.allThreads();
for (ThreadReference thread : threads)
{
if (name.equals(thread.name()))
{
thread.suspend();
try
{
int ii = 0;
for (StackFrame frame : thread.frames())
{
if (matchingFrame(frame))
{
StackFrame callingFrame = thread.frame(ii + 1);
useFrame(callingFrame);
}
ii++;
}
thread.resume();
}
catch (IncompatibleThreadStateException e)
{
e.printStackTrace();
}
}
}
}
private boolean matchingFrame(StackFrame frame)
{
if ("threadID".equals(frame.location().method().name())
&& (frame.getArgumentValues().size() == 1)
&& (frame.getArgumentValues().get(0).toString()
.equals(Long
.toString(id))))
{
return true;
}
else
{
return false;
}
}
private void useFrame(StackFrame frame)
{
try
{
System.out.println("Fields:");
ReferenceType type = frame.location().declaringType();
for (Field field : type.fields())
{
System.out.println(field.name() + " => " + type.getValue(field));
}
System.out.println("\nLocal Variables:");
for (LocalVariable variable : frame.visibleVariables())
{
if (!variable.isArgument())
{
System.out.println(variable.name() + " => "
+ frame.getValue(variable));
}
}
System.out.println("\nDone\n");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,55 @@
package org.test.intrace;
import java.util.Arrays;
public class TraceExample
{
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception
{
otherMain("123");
}
public static void otherMain(String arg) throws Exception
{
while(true)
{
Thread.sleep(1000);
InnerTest.foo();
workMethod("foobar");
}
}
private static void workMethod(String foo)
{
long currentTime = System.currentTimeMillis();
System.setProperty("a",foo);
intArrayMethod(new int[] {1,2,3});
if ((currentTime % 2) == 0)
{
System.setProperty("a","Even time");
}
else
{
System.setProperty("a","Odd time");
}
}
private static void intArrayMethod(int[] intArg)
{
System.setProperty("a",Arrays.toString(intArg));
}
private static class InnerTest
{
private static void foo()
{
System.setProperty("a","bar");
}
}
}

View File

@ -1,9 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="testsrc"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="lib" path="lib/asm-2.2.3.jar"/>
<classpathentry kind="lib" path="lib/asm-commons-2.2.3.jar"/>
<classpathentry kind="con" path="SWT_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="lib" path="lib/org.eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar"/>
<classpathentry kind="lib" path="lib/easymockclassextension-2.5.2.jar"/>
<classpathentry kind="lib" path="lib/easymock-2.5.2.jar"/>
<classpathentry kind="lib" path="lib/cglib-nodep-2.2.jar"/>
<classpathentry kind="lib" path="genbin"/>
<classpathentry kind="lib" path="lib/asm-3.2.jar"/>
<classpathentry kind="lib" path="lib/asm-commons-3.2.jar"/>
<classpathentry kind="lib" path="lib/asm-util-3.2.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -22,15 +22,21 @@
<delete dir="./lib/libclasses" includes="**/*" />
<mkdir dir="./lib/libclasses" />
<unzip src="./lib/asm-2.2.3.jar" dest="./lib/libclasses">
<unzip src="./lib/asm-3.2.jar" dest="./lib/libclasses">
<patternset>
<include name="**/*.class" />
<exclude name="META-INF" />
</patternset>
</unzip>
<unzip src="./lib/asm-commons-2.2.3.jar" dest="./lib/libclasses">
<unzip src="./lib/asm-commons-3.2.jar" dest="./lib/libclasses">
<patternset>
<include name="**/EmptyVisitor.class" />
<include name="**/*.class" />
<exclude name="META-INF" />
</patternset>
</unzip>
<unzip src="./lib/asm-util-3.2.jar" dest="./lib/libclasses">
<patternset>
<include name="**/*.class" />
<exclude name="META-INF" />
</patternset>
</unzip>
@ -45,6 +51,10 @@
<fileset dir="./lib/libclasses" includes="**/*.class" />
</jar>
<!-- Test Trace Agent -->
<jar destfile="./built/traceagent_test.jar" compress="false" manifest="conf/META-INF/MANIFEST.MF">
</jar>
<!-- CUI Trace Client -->
<jar destfile="./built/cuitraceclient.jar" compress="false">
<manifest>
@ -70,6 +80,6 @@
</target>
<target name="deploy" description="Copy agent jar to another project">
<copy file="./built/traceagent.jar" tofile="../TestProject/lib/traceagent.jar" overwrite="true" />
<copy file="./built/traceagent.jar" tofile="../org.intrace.test/lib/traceagent.jar" overwrite="true" />
</target>
</project>

Binary file not shown.

BIN
org.intrace/lib/asm-3.2.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,17 +7,15 @@ import java.util.regex.Pattern;
import org.intrace.shared.AgentConfigConstants;
/**
* Args Format:
* "[arg1[arg2[arg3"
* Args Format: "[arg1[arg2[arg3"
*
* where argx is of the form
* value-parameter
* where argx is of the form value-parameter
*/
public class AgentSettings
{
private Pattern classRegex = Pattern.compile(".*");
private boolean instruEnabled = false;
private boolean saveTracedClassfiles = false;
private boolean saveTracedClassfiles = true;
private boolean verboseMode = false;
private boolean allowJarsToBeTraced = false;
@ -50,31 +48,41 @@ public class AgentSettings
{
verboseMode = true;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.VERBOSE_MODE + "false"))
else if (arg.toLowerCase().equals(
AgentConfigConstants.VERBOSE_MODE
+ "false"))
{
verboseMode = false;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.INSTRU_ENABLED + "true"))
else if (arg.toLowerCase().equals(
AgentConfigConstants.INSTRU_ENABLED
+ "true"))
{
instruEnabled = true;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.INSTRU_ENABLED + "false"))
else if (arg.toLowerCase().equals(
AgentConfigConstants.INSTRU_ENABLED
+ "false"))
{
instruEnabled = false;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.SAVE_TRACED_CLASSFILES + "true"))
else if (arg.toLowerCase()
.equals(AgentConfigConstants.SAVE_TRACED_CLASSFILES + "true"))
{
saveTracedClassfiles = true;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.SAVE_TRACED_CLASSFILES + "false"))
else if (arg.toLowerCase()
.equals(AgentConfigConstants.SAVE_TRACED_CLASSFILES + "false"))
{
saveTracedClassfiles = false;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED + "true"))
else if (arg.toLowerCase()
.equals(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED + "true"))
{
allowJarsToBeTraced = true;
}
else if (arg.toLowerCase().equals(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED + "false"))
else if (arg.toLowerCase()
.equals(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED + "false"))
{
allowJarsToBeTraced = false;
}
@ -116,19 +124,25 @@ public class AgentSettings
String currentSettings = "";
currentSettings += "Class Regex : " + classRegex + "\n";
currentSettings += "Tracing Enabled : " + instruEnabled + "\n";
currentSettings += "Save Traced Class Files : " + saveTracedClassfiles + "\n";
currentSettings += "Trace Classes in JAR Files : " + allowJarsToBeTraced + "\n";
currentSettings += "Save Traced Class Files : " + saveTracedClassfiles
+ "\n";
currentSettings += "Trace Classes in JAR Files : " + allowJarsToBeTraced
+ "\n";
return currentSettings;
}
public Map<String,String> getSettingsMap()
public Map<String, String> getSettingsMap()
{
Map<String,String> settingsMap = new HashMap<String, String>();
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));
settingsMap.put(AgentConfigConstants.SAVE_TRACED_CLASSFILES, Boolean.toString(saveTracedClassfiles));
Map<String, String> settingsMap = new HashMap<String, String>();
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));
settingsMap.put(AgentConfigConstants.SAVE_TRACED_CLASSFILES,
Boolean.toString(saveTracedClassfiles));
return settingsMap;
}
}

View File

@ -1,7 +1,5 @@
package org.intrace.agent;
import static org.objectweb.asm.Opcodes.GOTO;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -51,15 +49,15 @@ import org.objectweb.asm.commons.EmptyVisitor;
*
* <h3>Line 5:</h3>
* <ul>
* <li>{@link ClassAnalysis#visitLineNumber(int, Label)} is called and
* we record a mapping from label1 to line 5.
* <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.
* <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
@ -104,25 +102,19 @@ public class ClassAnalysis extends EmptyVisitor
@Override
public void visitJumpInsn(int xiOpCode, Label xiBranchLabel)
{
if (xiOpCode == GOTO)
Integer lineNo = currentMethod_labelLineNos.get(xiBranchLabel);
if (lineNo != null)
{
Integer lineNo = currentMethod_labelLineNos.get(xiBranchLabel);
if (lineNo != null)
{
currentMethod_reverseGOTOLines.add(lineNo);
}
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>();
}
methodReverseGOTOLines.put(currentMethod_sig,
currentMethod_reverseGOTOLines);
currentMethod_sig = null;
currentMethod_reverseGOTOLines = new HashSet<Integer>();
}
}

View File

@ -69,10 +69,12 @@ public class ClassTransformer implements ClassFileTransformer
{
ClassReader cr = new ClassReader(classfileBuffer);
ClassAnalysis analysis = new ClassAnalysis();
cr.accept(analysis, false);
cr.accept(analysis, 0);
InstrumentedClassWriter writer = new InstrumentedClassWriter(xiClassName,
cr, analysis);
cr.accept(writer, false);
cr.accept(writer, 0);
return writer.toByteArray();
}
@ -93,6 +95,10 @@ public class ClassTransformer implements ClassFileTransformer
{
if (modifiedClasses.contains(loadedClass.getName()))
{
if (settings.isVerboseMode())
{
System.out.println("Retransform class: " + loadedClass.getName());
}
inst.retransformClasses(loadedClass);
}
}
@ -136,7 +142,8 @@ public class ClassTransformer implements ClassFileTransformer
{
if (settings.isVerboseMode())
{
System.out.println("Ignoring class in gb.instrument package: "
System.out
.println("Ignoring class in org.intrace or org.objectweb.asm package: "
+ className);
}
return false;
@ -214,37 +221,25 @@ public class ClassTransformer implements ClassFileTransformer
System.out.println("!! Instrumenting class: " + className);
}
byte[] newBytes = getInstrumentedClassBytes(className, originalClassfile);
byte[] newBytes;
try
{
newBytes = getInstrumentedClassBytes(className, originalClassfile);
}
catch (RuntimeException th)
{
th.printStackTrace();
throw th;
}
catch (Error th)
{
th.printStackTrace();
throw th;
}
if (settings.saveTracedClassfiles())
{
try
{
File classOut = new File("./genbin/" + internalClassName
+ "_gen.class");
File parentDir = classOut.getParentFile();
boolean dirExists = parentDir.exists();
if (!dirExists)
{
dirExists = parentDir.mkdirs();
}
if (dirExists)
{
OutputStream out = new FileOutputStream(classOut);
out.write(newBytes);
out.flush();
out.close();
}
else
{
System.out.println("Can't create directory " + parentDir
+ " for saving traced classfiles.");
}
}
catch (Exception e)
{
e.printStackTrace();
}
writeClassBytes(newBytes, internalClassName + "_gen.class");
}
modifiedClasses.add(className);
@ -257,6 +252,36 @@ public class ClassTransformer implements ClassFileTransformer
}
}
private void writeClassBytes(byte[] newBytes, String className)
{
try
{
File classOut = new File("./genbin/" + className);
File parentDir = classOut.getParentFile();
boolean dirExists = parentDir.exists();
if (!dirExists)
{
dirExists = parentDir.mkdirs();
}
if (dirExists)
{
OutputStream out = new FileOutputStream(classOut);
out.write(newBytes);
out.flush();
out.close();
}
else
{
System.out.println("Can't create directory " + parentDir
+ " for saving traced classfiles.");
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Consider loaded classes for transformation. Any of the following reasons
* would prevent a loaded class from being eligible for instrumentation.
@ -304,6 +329,7 @@ public class ClassTransformer implements ClassFileTransformer
{
try
{
// System.out.println("ReTransform: " + loadedClass.getName());
inst.retransformClasses(loadedClass);
}
catch (Throwable e)

View File

@ -1,12 +1,14 @@
package org.intrace.agent;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.intrace.output.AgentHelper;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
@ -21,8 +23,9 @@ import org.objectweb.asm.Type;
*/
public class InstrumentedClassWriter extends ClassWriter
{
private final String mClassName;
private final String className;
private final ClassAnalysis analysis;
private int methodNum = 1;
/**
* cTor
@ -34,8 +37,8 @@ public class InstrumentedClassWriter extends ClassWriter
public InstrumentedClassWriter(String xiClassName, ClassReader xiReader,
ClassAnalysis xiAnalysis)
{
super(xiReader, true);
mClassName = xiClassName;
super(xiReader, COMPUTE_MAXS);
className = xiClassName;
analysis = xiAnalysis;
}
@ -53,14 +56,10 @@ public class InstrumentedClassWriter extends ClassWriter
Set<Integer> branchTraceLines = analysis.methodReverseGOTOLines.get(name
+ desc);
Integer entryLine = analysis.methodEntryLines.get(name + desc);
if (branchTraceLines == null)
{
branchTraceLines = new HashSet<Integer>();
}
// Transform the method
return new InstrumentedMethodWriter(mv, name, desc, branchTraceLines,
entryLine);
return new InstrumentedMethodWriter(mv, access, name, desc,
branchTraceLines, entryLine);
}
/**
@ -73,6 +72,9 @@ public class InstrumentedClassWriter extends ClassWriter
// Final method fields
private final String methodName;
private final String methodDescriptor;
private final int methodAccess;
private final Map<Label, Integer> labelLineNos = new HashMap<Label, Integer>();
private final Set<Label> traceLabels = new HashSet<Label>();
// Analysis data
private final Set<Integer> reverseGOTOLines;
@ -80,27 +82,43 @@ public class InstrumentedClassWriter extends ClassWriter
// State
private boolean writeTraceLine = false;
private CTorEntryState ctorEntryState = CTorEntryState.NORMALMETHOD;
private int lineNumber = -1;
private int numBranchesOnLine = 0;
private TernaryState ternState = TernaryState.BASE;
/**
* cTor
*
* @param xiMethodVisitor
* @param access
* @param xiClassName
* @param xiMethodName
* @param xiDesc
* @param xiBranchTraceLines
* @param entryLine
*/
public InstrumentedMethodWriter(MethodVisitor xiMethodVisitor,
public InstrumentedMethodWriter(MethodVisitor xiMethodVisitor, int access,
String xiMethodName, String xiDesc, Set<Integer> xiBranchTraceLines,
Integer xiEntryLine)
{
super(xiMethodVisitor);
methodAccess = access;
methodName = xiMethodName;
methodDescriptor = xiDesc;
reverseGOTOLines = xiBranchTraceLines;
entryLine = xiEntryLine;
if (methodName.equals("<init>"))
{
ctorEntryState = CTorEntryState.ISCTOR;
}
}
// MCHR: Need to preserve Synthetic attribute
@Override
public void visitAttribute(Attribute attr)
{
super.visitAttribute(attr);
}
/**
@ -108,11 +126,22 @@ public class InstrumentedClassWriter extends ClassWriter
*/
@Override
public void visitCode()
{
methodNum++;
if (ctorEntryState != CTorEntryState.ISCTOR)
{
addEntryCalls();
}
// For Constructors we add the entry calls after the first invokeSpecial
// which calls into the superclass constructor.
super.visitCode();
}
private void addEntryCalls()
{
generateCallToAgentHelper(InstrumentationType.ENTER,
((entryLine != null ? entryLine : -1)));
traceMethodArgs();
super.visitCode();
}
/**
@ -121,29 +150,81 @@ public class InstrumentedClassWriter extends ClassWriter
private void traceMethodArgs()
{
Type[] argTypes = Type.getArgumentTypes(methodDescriptor);
boolean isStaticAccess = ((methodAccess & Opcodes.ACC_STATIC) > 0);
int offset = (isStaticAccess ? 0 : 1);
for (int ii = 0; ii < argTypes.length; ii++)
{
String typeDescriptor = argTypes[ii].getDescriptor();
int varslot = ii + offset;
int opcode = Opcodes.ILOAD;
if (argTypes[ii].getSort() == Type.OBJECT)
{
typeDescriptor = "Ljava/lang/Object;";
opcode = Opcodes.ALOAD;
}
else if ((argTypes[ii].getSort() == Type.ARRAY)
&& (argTypes[ii].getDescriptor().startsWith("[[")))
{
// All multidimensional arrays are handled by the object array
// function
typeDescriptor = "[Ljava/lang/Object;";
}
else if ((argTypes[ii].getSort() == Type.ARRAY)
&& (argTypes[ii].getDescriptor().startsWith("[L")))
{
// All object arrays are cast to the object supertype
typeDescriptor = "[Ljava/lang/Object;";
}
else if (argTypes[ii].getSort() == Type.LONG)
{
opcode = Opcodes.LLOAD;
offset++;
}
else if (argTypes[ii].getSort() == Type.FLOAT)
{
opcode = Opcodes.FLOAD;
}
else if (argTypes[ii].getSort() == Type.DOUBLE)
{
opcode = Opcodes.DLOAD;
offset++;
}
mv.visitLdcInsn(mClassName);
if (argTypes[ii].getSort() == Type.ARRAY)
{
opcode = Opcodes.ALOAD;
}
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitVarInsn(Opcodes.ALOAD, ii);
mv.visitVarInsn(opcode, varslot);
mv.visitMethodInsn(INVOKESTATIC, HELPER_CLASS, "arg",
"(Ljava/lang/String;Ljava/lang/String;"
+ typeDescriptor + ")V");
}
}
/**
* Guard against a label which hasn't been resolved. This happens sometimes,
* I assume due to a bug in ASM.
*/
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index)
{
try
{
end.getOffset();
}
catch (IllegalStateException ex)
{
mv.visitLabel(end);
}
super.visitLocalVariable(name, desc, signature, start, end, index);
}
/**
* Save the current line number and generate a call to {@link AgentHelper}
* if either writeTraceLine is set to true or the current line was marked by
@ -153,11 +234,32 @@ public class InstrumentedClassWriter extends ClassWriter
public void visitLineNumber(int xiLineNumber, Label label)
{
lineNumber = xiLineNumber;
if (writeTraceLine || reverseGOTOLines.contains(xiLineNumber))
labelLineNos.put(label, xiLineNumber);
if (writeTraceLine || reverseGOTOLines.contains(xiLineNumber)
|| traceLabels.contains(label))
{
generateCallToAgentHelper(InstrumentationType.BRANCH, lineNumber);
writeTraceLine = false;
// This check excludes ternary statements where we can't add trace
if (numBranchesOnLine < 2)
{
if (ctorEntryState == CTorEntryState.SEEN_SPECIAL)
{
addEntryCalls();
ctorEntryState = CTorEntryState.ENTRY_WRITTEN;
}
else
{
generateCallToAgentHelper(InstrumentationType.BRANCH, lineNumber);
}
writeTraceLine = false;
}
else
{
writeTraceLine = false;
}
}
numBranchesOnLine = 0;
super.visitLineNumber(xiLineNumber, label);
}
@ -168,13 +270,45 @@ public class InstrumentedClassWriter extends ClassWriter
@Override
public void visitInsn(int xiOpCode)
{
if (xiOpCode == RETURN)
if ((ternState == TernaryState.BASE) && (xiOpCode == Opcodes.DUP))
{
ternState = TernaryState.SEEN_DUP;
}
else if ((ternState == TernaryState.SEEN_BRANCH)
&& (xiOpCode == Opcodes.POP))
{
ternState = TernaryState.BASE;
writeTraceLine = false;
}
else
{
ternState = TernaryState.BASE;
}
if (xiOpCode == Opcodes.RETURN)
{
generateCallToAgentHelper(InstrumentationType.EXIT, lineNumber);
}
super.visitInsn(xiOpCode);
}
/**
* Handle cTor case where we write entry trace later.
*/
@Override
public void visitMethodInsn(int xiOpCode, String owner, String name,
String desc)
{
if ((ctorEntryState == CTorEntryState.ISCTOR)
&& (xiOpCode == Opcodes.INVOKESPECIAL))
{
ctorEntryState = CTorEntryState.SEEN_SPECIAL;
writeTraceLine = true;
}
super.visitMethodInsn(xiOpCode, owner, name, desc);
}
/**
* A jump instruction indicates that we are about to enter an optional block
* of code. We set "writeTraceLine = true;" to ensure that we generate a
@ -183,10 +317,47 @@ public class InstrumentedClassWriter extends ClassWriter
@Override
public void visitJumpInsn(int xiOpCode, Label xiBranchLabel)
{
writeTraceLine = true;
if (ternState == TernaryState.SEEN_DUP)
{
ternState = TernaryState.SEEN_BRANCH;
}
numBranchesOnLine++;
Integer lineNo = labelLineNos.get(xiBranchLabel);
if (lineNo == null)
{
// This is a forward jump
writeTraceLine = true;
}
super.visitJumpInsn(xiOpCode, xiBranchLabel);
}
/**
* Try catch handler
*/
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type)
{
traceLabels.add(handler);
super.visitTryCatchBlock(start, end, handler, type);
}
/**
* Switch block
*/
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label[] labels)
{
traceLabels.add(dflt);
for (Label label : labels)
{
traceLabels.add(label);
}
super.visitTableSwitchInsn(min, max, dflt, labels);
}
/**
* Generate an ENTER/BRANCH/EXIT instrumentation call.
*
@ -196,9 +367,9 @@ public class InstrumentedClassWriter extends ClassWriter
private void generateCallToAgentHelper(InstrumentationType traceType,
int lineNumber)
{
mv.visitLdcInsn(mClassName);
mv.visitLdcInsn(className);
mv.visitLdcInsn(methodName);
mv.visitIntInsn(Opcodes.BIPUSH, lineNumber);
mv.visitLdcInsn(lineNumber);
switch (traceType)
{
case ENTER:
@ -232,4 +403,15 @@ public class InstrumentedClassWriter extends ClassWriter
{
ENTER, BRANCH, EXIT
}
private enum TernaryState
{
BASE, SEEN_DUP, SEEN_BRANCH
/* SEEN_POP */
}
private enum CTorEntryState
{
NORMALMETHOD, ISCTOR, SEEN_SPECIAL, ENTRY_WRITTEN
}
}

View File

@ -131,9 +131,8 @@ public class AgentClientConnection implements Runnable
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
throw new IOException(e);
}
return null;
}
/**

View File

@ -1,6 +1,5 @@
package org.intrace.output.trace;
import java.util.Arrays;
import java.util.Map;
@ -23,9 +22,12 @@ public class TraceHandler implements IInstrumentationHandler
TraceSettings oldSettings = new TraceSettings(traceSettings);
traceSettings.parseArgs(args);
if ((oldSettings.isEntryExitTraceEnabled() != traceSettings.isEntryExitTraceEnabled()) ||
(oldSettings.isBranchTraceEnabled() != traceSettings.isBranchTraceEnabled()) ||
(oldSettings.isArgTraceEnabled() != traceSettings.isArgTraceEnabled()))
if ((oldSettings.isEntryExitTraceEnabled() != traceSettings
.isEntryExitTraceEnabled())
|| (oldSettings.isBranchTraceEnabled() != traceSettings
.isBranchTraceEnabled())
|| (oldSettings.isArgTraceEnabled() != traceSettings
.isArgTraceEnabled()))
{
System.out.println("## Trace Settings Changed");
}
@ -37,7 +39,7 @@ public class TraceHandler implements IInstrumentationHandler
return null;
}
public Map<String,String> getSettingsMap()
public Map<String, String> getSettingsMap()
{
return traceSettings.getSettingsMap();
}
@ -47,7 +49,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + byteArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ byteArg);
}
}
@ -65,7 +68,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + shortArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ shortArg);
}
}
@ -83,7 +87,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + intArg);
AgentHelper
.writeOutput(className + ":" + methodName + ": Arg: " + intArg);
}
}
@ -102,7 +107,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + longArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ longArg);
}
}
@ -121,7 +127,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + floatArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ floatArg);
}
}
@ -140,7 +147,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + doubleArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ doubleArg);
}
}
@ -159,7 +167,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + boolArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ boolArg);
}
}
@ -178,7 +187,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + charArg);
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ charArg);
}
}
@ -197,7 +207,8 @@ public class TraceHandler implements IInstrumentationHandler
{
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: " + objArg);
AgentHelper
.writeOutput(className + ":" + methodName + ": Arg: " + objArg);
}
}
@ -206,7 +217,7 @@ public class TraceHandler implements IInstrumentationHandler
if (argTrace)
{
AgentHelper.writeOutput(className + ":" + methodName + ": Arg: "
+ Arrays.toString(objArrayArg));
+ Arrays.deepToString(objArrayArg));
}
}

View File

@ -0,0 +1,51 @@
package org.test.intrace.agent;
import java.util.Map;
import junit.framework.TestCase;
import org.intrace.agent.AgentSettings;
import org.intrace.shared.AgentConfigConstants;
public class AgentSettingsTest extends TestCase
{
public void testAgentSettings()
{
AgentSettings as = new AgentSettings(
AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED
+ "true"
+ AgentConfigConstants.CLASS_REGEX
+ ".*"
+ AgentConfigConstants.INSTRU_ENABLED
+ "true"
+ AgentConfigConstants.SAVE_TRACED_CLASSFILES
+ "true"
+ AgentConfigConstants.VERBOSE_MODE
+ "true");
assertEquals(as.allowJarsToBeTraced(), true);
assertEquals(as.getClassRegex().pattern(), ".*");
assertEquals(as.isInstrumentationEnabled(), true);
assertEquals(as.saveTracedClassfiles(), true);
assertEquals(as.isVerboseMode(), true);
as = new AgentSettings(as);
assertEquals(as.allowJarsToBeTraced(), true);
assertEquals(as.getClassRegex().pattern(), ".*");
assertEquals(as.isInstrumentationEnabled(), true);
assertEquals(as.saveTracedClassfiles(), true);
assertEquals(as.isVerboseMode(), true);
String toString = as.toString();
assertNotNull(toString);
Map<String, String> settingsMap = as.getSettingsMap();
assertEquals(settingsMap.get(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED),
"true");
assertEquals(settingsMap.get(AgentConfigConstants.CLASS_REGEX), ".*");
assertEquals(settingsMap.get(AgentConfigConstants.INSTRU_ENABLED), "true");
assertEquals(settingsMap.get(AgentConfigConstants.SAVE_TRACED_CLASSFILES),
"true");
assertEquals(settingsMap.get(AgentConfigConstants.VERBOSE_MODE), "true");
}
}

View File

@ -0,0 +1,663 @@
package org.test.intrace.agent;
import static org.easymock.EasyMock.isA;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import junit.framework.TestCase;
import org.easymock.IAnswer;
import org.easymock.classextension.EasyMock;
import org.intrace.output.AgentHelper;
import org.intrace.output.IInstrumentationHandler;
import org.intrace.shared.AgentConfigConstants;
import org.intrace.shared.OutputConfigConstants;
import org.intrace.shared.TraceConfigConstants;
/**
* This test tests the Agent. To run this test you must run the test with this
* JVM argument: "-javaagent:built/traceagent_test.jar="
*/
public class AgentTest extends TestCase
{
private Receiver receiver;
private Sender sender;
private Socket socket;
@Override
protected void setUp() throws Exception
{
super.setUp();
deleteOldClassFiles();
AgentHelper.instrumentationHandlers.clear();
connectToAgent();
testSetting(AgentConfigConstants.INSTRU_ENABLED, "false");
}
private void deleteOldClassFiles()
{
File genbin = new File("./genbin/");
File[] files = genbin.listFiles();
if (files != null)
{
for (int i = 0; i < files.length; i++)
{
if (files[i].isDirectory())
{
deleteDirectory(files[i]);
}
else
{
files[i].delete();
}
}
}
}
public static boolean deleteDirectory(File path)
{
if (path.exists())
{
File[] files = path.listFiles();
for (int i = 0; i < files.length; i++)
{
if (files[i].isDirectory())
{
deleteDirectory(files[i]);
}
else
{
files[i].delete();
}
}
}
return (path.delete());
}
public void testGetSettings() throws Exception
{
sender.sendMessage("getsettings");
Object settingsResponse = receiver.incomingMessages.take();
assertNotNull(settingsResponse);
assertTrue(settingsResponse instanceof Map<?, ?>);
}
public void testHelp() throws Exception
{
sender.sendMessage("help");
Object helpResponse = receiver.incomingMessages.take();
assertNotNull(helpResponse);
assertTrue(helpResponse instanceof Set<?>);
Set<String> expectedHelpResponse = new HashSet<String>();
expectedHelpResponse.addAll(AgentConfigConstants.COMMANDS);
expectedHelpResponse.addAll(OutputConfigConstants.COMMANDS);
expectedHelpResponse.addAll(TraceConfigConstants.COMMANDS);
assertEquals(helpResponse, expectedHelpResponse);
}
public void testSettings() throws Exception
{
// Boolean settings
testSetting(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED, "true");
testSetting(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED, "false");
testSetting(AgentConfigConstants.INSTRU_ENABLED, "true");
testSetting(AgentConfigConstants.INSTRU_ENABLED, "false");
testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true");
testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "false");
testSetting(AgentConfigConstants.VERBOSE_MODE, "true");
testSetting(AgentConfigConstants.VERBOSE_MODE, "false");
// Regex (Test in verbose mode to exercise the verbose code
testSetting(AgentConfigConstants.CLASS_REGEX, "foo.*");
testSetting(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED, "true");
testSetting(AgentConfigConstants.INSTRU_ENABLED, "true");
testSetting(AgentConfigConstants.VERBOSE_MODE, "true");
testSetting(AgentConfigConstants.CLASS_REGEX, ".*");
}
public void testBranchPatterns() throws Throwable
{
// Create and init the mock
IInstrumentationHandler testHandler = EasyMock
.createMock(IInstrumentationHandler.class);
EasyMock.expect(testHandler.getResponse(isA(String.class))).andReturn(null)
.anyTimes();
EasyMock.expect(testHandler.getSettingsMap())
.andReturn(new HashMap<String, String>()).anyTimes();
// Capture objects
final BlockingQueue<String> capturedTrace = new LinkedBlockingQueue<String>();
IAnswer<Object> entryTraceWriter = new IAnswer<Object>()
{
@Override
public Object answer() throws Throwable
{
Object[] args = EasyMock.getCurrentArguments();
capturedTrace.put("Enter:##:" + Arrays.toString(args));
return null;
}
};
IAnswer<Object> branchTraceWriter = new IAnswer<Object>()
{
@Override
public Object answer() throws Throwable
{
Object[] args = EasyMock.getCurrentArguments();
capturedTrace.put("Branch:##:" + Arrays.toString(args));
return null;
}
};
IAnswer<Object> exitTraceWriter = new IAnswer<Object>()
{
@Override
public Object answer() throws Throwable
{
Object[] args = EasyMock.getCurrentArguments();
capturedTrace.put("Exit:##:" + Arrays.toString(args));
return null;
}
};
// Expect trace calls
testHandler.enter(isA(String.class), isA(String.class), EasyMock.anyInt());
EasyMock.expectLastCall().andAnswer(entryTraceWriter).anyTimes();
testHandler.branch(isA(String.class), isA(String.class), EasyMock.anyInt());
EasyMock.expectLastCall().andAnswer(branchTraceWriter).anyTimes();
testHandler.exit(isA(String.class), isA(String.class), EasyMock.anyInt());
EasyMock.expectLastCall().andAnswer(exitTraceWriter).anyTimes();
testHandler
.arg(isA(String.class), isA(String.class), EasyMock.anyBoolean());
EasyMock.expectLastCall().anyTimes();
testHandler.arg(isA(String.class), isA(String.class), EasyMock.anyObject());
EasyMock.expectLastCall().anyTimes();
testHandler.arg(isA(String.class), isA(String.class), EasyMock.anyInt());
EasyMock.expectLastCall().anyTimes();
EasyMock.replay(testHandler);
AgentHelper.instrumentationHandlers.put(testHandler, new Object());
// Setup agent
testSetting(AgentConfigConstants.INSTRU_ENABLED, "false");
testSetting(AgentConfigConstants.CLASS_REGEX, ".*BranchPatterns.*");
testSetting(AgentConfigConstants.VERBOSE_MODE, "false");
testSetting(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED, "false");
testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true");
testSetting(AgentConfigConstants.INSTRU_ENABLED, "true");
// Run Patterns thread
BranchPatterns branchPatterns = new BranchPatterns();
Thread patternThread = new Thread(branchPatterns);
patternThread.start();
patternThread.join();
if (branchPatterns.th != null)
{
throw branchPatterns.th;
}
// Verify that we got all of the expected calls
EasyMock.verify(testHandler);
// Parse the trace
Map<String, TraceData> parsedTraceData = new HashMap<String, TraceData>();
String traceLine = capturedTrace.poll();
while (traceLine != null)
{
parseLine(parsedTraceData, traceLine);
traceLine = capturedTrace.poll();
}
// Verify the trace
assertNotNull(parsedTraceData.get("switchblock"));
{
TraceData trData = parsedTraceData.get("switchblock");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(4, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("ternary"));
{
TraceData trData = parsedTraceData.get("ternary");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(0, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("trycatchfinally"));
{
TraceData trData = parsedTraceData.get("trycatchfinally");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(1, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("whileloop"));
{
TraceData trData = parsedTraceData.get("whileloop");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(2, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("forloop"));
{
TraceData trData = parsedTraceData.get("forloop");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(2, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("singlelineif"));
{
TraceData trData = parsedTraceData.get("singlelineif");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(1, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("ifthenelse"));
{
TraceData trData = parsedTraceData.get("ifthenelse");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(2, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("ifthenelseifelse"));
{
TraceData trData = parsedTraceData.get("ifthenelseifelse");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(5, trData.branchLines.size());
}
assertNotNull(parsedTraceData.get("dowhile"));
{
TraceData trData = parsedTraceData.get("dowhile");
assertTrue(trData.seenEnter);
assertTrue(trData.seenExit);
assertEquals(2, trData.branchLines.size());
}
}
public void testArgumentTypes() throws Throwable
{
// Create and init the mock
final BlockingQueue<String> capturedTrace = new LinkedBlockingQueue<String>();
IInstrumentationHandler testHandler = new ArgCapture(capturedTrace);
AgentHelper.instrumentationHandlers.put(testHandler, new Object());
// Setup agent
testSetting(AgentConfigConstants.INSTRU_ENABLED, "false");
testSetting(AgentConfigConstants.CLASS_REGEX, ".*ArgumentTypes.*");
testSetting(AgentConfigConstants.VERBOSE_MODE, "false");
testSetting(AgentConfigConstants.ALLOW_JARS_TO_BE_TRACED, "false");
testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true");
testSetting(AgentConfigConstants.INSTRU_ENABLED, "true");
// Run Patterns thread
ArgumentTypes argTypes = new ArgumentTypes();
Thread patternThread = new Thread(argTypes);
patternThread.start();
patternThread.join();
if (argTypes.th != null)
{
throw argTypes.th;
}
// Parse the trace
Map<String, TraceData> parsedTraceData = new HashMap<String, TraceData>();
String traceLine = capturedTrace.poll();
while (traceLine != null)
{
parseLine(parsedTraceData, traceLine);
traceLine = capturedTrace.poll();
}
// Verify the trace
assertNotNull(parsedTraceData.get("byteArg"));
{
TraceData trData = parsedTraceData.get("byteArg");
assertEquals(1, trData.args.size());
assertEquals("0", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("byteArrayArg"));
{
TraceData trData = parsedTraceData.get("byteArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[1]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("shortArg"));
{
TraceData trData = parsedTraceData.get("shortArg");
assertEquals(1, trData.args.size());
assertEquals("2", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("shortArrayArg"));
{
TraceData trData = parsedTraceData.get("shortArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[3]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("intArg"));
{
TraceData trData = parsedTraceData.get("intArg");
assertEquals(1, trData.args.size());
assertEquals("4", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("intArrayArg"));
{
TraceData trData = parsedTraceData.get("intArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[5]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("longArg"));
{
TraceData trData = parsedTraceData.get("longArg");
assertEquals(1, trData.args.size());
assertEquals("6", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("longArrayArg"));
{
TraceData trData = parsedTraceData.get("longArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[7]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("floatArg"));
{
TraceData trData = parsedTraceData.get("floatArg");
assertEquals(1, trData.args.size());
assertEquals("8.0", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("floatArrayArg"));
{
TraceData trData = parsedTraceData.get("floatArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[9.0]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("doubleArg"));
{
TraceData trData = parsedTraceData.get("doubleArg");
assertEquals(1, trData.args.size());
assertEquals("10.0", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("doubleArrayArg"));
{
TraceData trData = parsedTraceData.get("doubleArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[11.0]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("boolArg"));
{
TraceData trData = parsedTraceData.get("boolArg");
assertEquals(2, trData.args.size());
assertEquals("true", trData.args.get(0));
assertEquals("false", trData.args.get(1));
}
assertNotNull(parsedTraceData.get("boolArrayArg"));
{
TraceData trData = parsedTraceData.get("boolArrayArg");
assertEquals(2, trData.args.size());
assertEquals("[true]", trData.args.get(0));
assertEquals("[false]", trData.args.get(1));
}
assertNotNull(parsedTraceData.get("boolArrayArrayArg"));
{
TraceData trData = parsedTraceData.get("boolArrayArrayArg");
assertEquals(2, trData.args.size());
assertEquals("[[true]]", trData.args.get(0));
assertEquals("[[false]]", trData.args.get(1));
}
assertNotNull(parsedTraceData.get("charArg"));
{
TraceData trData = parsedTraceData.get("charArg");
assertEquals(1, trData.args.size());
assertEquals("2", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("charArrayArg"));
{
TraceData trData = parsedTraceData.get("charArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[3]", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("objArg"));
{
TraceData trData = parsedTraceData.get("objArg");
assertEquals(1, trData.args.size());
assertEquals("obj", trData.args.get(0));
}
assertNotNull(parsedTraceData.get("objArrayArg"));
{
TraceData trData = parsedTraceData.get("objArrayArg");
assertEquals(1, trData.args.size());
assertEquals("[obj]", trData.args.get(0));
}
}
private void parseLine(Map<String, TraceData> parsedTraceData,
String traceLine)
{
String[] traceParts = traceLine.split(":##:");
String traceType = traceParts[0];
String traceLineData = traceParts[1];
traceLineData = traceLineData.substring(1, traceLineData.length() - 1);
String[] dataParts = traceLineData.split(",");
String methodSig = dataParts[1].trim();
TraceData traceData = parsedTraceData.get(methodSig);
if (traceData == null)
{
traceData = new TraceData();
parsedTraceData.put(methodSig, traceData);
}
if (traceType.equals("Enter"))
{
traceData.seenEnter = true;
}
else if (traceType.equals("Exit"))
{
traceData.seenExit = true;
}
else if (traceType.equals("Branch"))
{
traceData.branchLines.add(Integer.parseInt(dataParts[2].trim()));
}
else if (traceType.equals("Arg"))
{
traceData.args.add(dataParts[2].trim());
}
}
@SuppressWarnings("unchecked")
private void testSetting(String configConstant, String configValue)
throws Exception
{
// Set setting
sender.sendMessage(configConstant + configValue);
Object okResponse = receiver.incomingMessages.take();
assertNotNull(okResponse);
assertTrue(okResponse instanceof String);
assertEquals(okResponse, "OK");
// Get settings
sender.sendMessage("getsettings");
Object settingsResponse = receiver.incomingMessages.take();
assertNotNull(settingsResponse);
assertTrue(settingsResponse instanceof Map<?, ?>);
Map<String, String> settingsResponseMap = (Map<String, String>) settingsResponse;
assertEquals(configValue, settingsResponseMap.get(configConstant));
}
@Override
protected void tearDown() throws Exception
{
receiver.stop();
sender.stop();
socket.close();
super.tearDown();
}
private void connectToAgent() throws Exception
{
String host = "localhost";
int port = 9123;
socket = new Socket();
socket.connect(new InetSocketAddress(host, port));
// Start threads
receiver = new Receiver(socket.getInputStream());
receiver.start();
sender = new Sender(socket.getOutputStream());
sender.start();
}
private static class Sender implements Runnable
{
private final OutputStream outputStream;
private final BlockingQueue<String> outgoingMessages = new LinkedBlockingQueue<String>();
private Thread th;
public Sender(OutputStream outputStream)
{
this.outputStream = outputStream;
}
public void stop()
{
try
{
outputStream.close();
}
catch (IOException e)
{
// Throw away
}
th.interrupt();
}
public void start()
{
th = new Thread(this);
th.setDaemon(true);
th.setName("Sender");
th.start();
}
@Override
public void run()
{
try
{
while (true)
{
String message = outgoingMessages.take();
ObjectOutputStream objOut = new ObjectOutputStream(outputStream);
objOut.writeObject(message);
objOut.flush();
}
}
catch (Exception e)
{
// Do something
}
}
public void sendMessage(String message)
{
try
{
outgoingMessages.put(message);
}
catch (InterruptedException e)
{
// Do nothing
}
}
}
private static class Receiver implements Runnable
{
private final InputStream inputStream;
private final BlockingQueue<Object> incomingMessages = new LinkedBlockingQueue<Object>();
private Thread th;
public Receiver(InputStream inputStream)
{
this.inputStream = inputStream;
}
public void stop()
{
try
{
inputStream.close();
}
catch (IOException e)
{
// Throw away
}
th.interrupt();
}
public void start()
{
th = new Thread(this);
th.setDaemon(true);
th.setName("Receiver");
th.start();
}
@Override
public void run()
{
try
{
while (true)
{
ObjectInputStream objIn = new ObjectInputStream(inputStream);
Object receivedMessage = objIn.readObject();
incomingMessages.put(receivedMessage);
}
}
catch (Exception e)
{
// Do something
}
}
}
}

View File

@ -0,0 +1,168 @@
package org.test.intrace.agent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import org.intrace.output.IInstrumentationHandler;
public class ArgCapture implements IInstrumentationHandler
{
private final BlockingQueue<String> capturedTrace;
public ArgCapture(BlockingQueue<String> capturedTrace)
{
this.capturedTrace = capturedTrace;
}
private void addArg(String arg)
{
capturedTrace.add(arg);
}
@Override
public void arg(String className, String methodName, byte byteArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + byteArg + "]");
}
public void arg(String className, String methodName, byte[] byteArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(byteArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, short shortArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + shortArg + "]");
}
public void arg(String className, String methodName, short[] shortArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(shortArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, int intArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + intArg + "]");
}
@Override
public void arg(String className, String methodName, int[] intArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(intArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, long longArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + longArg + "]");
}
@Override
public void arg(String className, String methodName, long[] longArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(longArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, float floatArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + floatArg + "]");
}
@Override
public void arg(String className, String methodName, float[] floatArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(floatArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, double doubleArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + doubleArg + "]");
}
@Override
public void arg(String className, String methodName, double[] doubleArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(doubleArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, boolean boolArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + boolArg + "]");
}
@Override
public void arg(String className, String methodName, boolean[] boolArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(boolArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, char charArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + charArg + "]");
}
@Override
public void arg(String className, String methodName, char[] charArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.toString(charArrayArg) + "]");
}
@Override
public void arg(String className, String methodName, Object objArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", " + objArg + "]");
}
public void arg(String className, String methodName, Object[] objArrayArg)
{
addArg("Arg:##:[" + className + ", " + methodName + ", "
+ Arrays.deepToString(objArrayArg) + "]");
}
@Override
public void branch(String className, String methodName, int lineNo)
{
// Do nothing
}
@Override
public void enter(String className, String methodName, int lineNo)
{
// Do nothing
}
@Override
public void exit(String className, String methodName, int lineNo)
{
// Do nothing
}
@Override
public String getResponse(String args)
{
return null;
}
@Override
public Map<String, String> getSettingsMap()
{
return new HashMap<String, String>();
}
}

View File

@ -0,0 +1,172 @@
package org.test.intrace.agent;
import java.util.Arrays;
public class ArgumentTypes implements Runnable
{
public Throwable th = null;
@Override
public void run()
{
try
{
byteArg((byte) 0);
byteArrayArg(new byte[]
{ 1 });
shortArg((short) 2);
shortArrayArg(new short[]
{ 3 });
intArg(4);
intArrayArg(new int[]
{ 5 });
longArg(6l);
longArrayArg(new long[]
{ 7l });
floatArg(8f);
floatArrayArg(new float[]
{ 9f });
doubleArg(10d);
doubleArrayArg(new double[]
{ 11d });
boolArg(true);
boolArg(false);
boolArrayArg(new boolean[]
{ true });
boolArrayArg(new boolean[]
{ false });
boolArrayArrayArg(new boolean[][]
{ new boolean[]
{ true } });
boolArrayArrayArg(new boolean[][]
{ new boolean[]
{ false } });
charArg('2');
charArrayArg(new char[]
{ '3' });
Object objToStr = new Object()
{
@Override
public String toString()
{
return "obj";
}
};
objArg(objToStr);
objArrayArg(new Object[]
{ objToStr });
}
catch (Throwable thr)
{
th = thr;
}
}
private void byteArg(byte arg)
{
setProperty(Byte.toString(arg));
}
private void byteArrayArg(byte[] arg)
{
setProperty(Arrays.toString(arg));
}
private void shortArg(short arg)
{
setProperty(Short.toString(arg));
}
private void shortArrayArg(short[] arg)
{
setProperty(Arrays.toString(arg));
}
private void intArg(int arg)
{
setProperty(Integer.toString(arg));
}
private void intArrayArg(int[] arg)
{
setProperty(Arrays.toString(arg));
}
private void longArg(long arg)
{
setProperty(Long.toString(arg));
}
private void longArrayArg(long[] arg)
{
setProperty(Arrays.toString(arg));
}
private void floatArg(float arg)
{
setProperty(Float.toString(arg));
}
private void floatArrayArg(float[] arg)
{
setProperty(Arrays.toString(arg));
}
private void doubleArg(double arg)
{
setProperty(Double.toString(arg));
}
private void doubleArrayArg(double[] arg)
{
setProperty(Arrays.toString(arg));
}
private void boolArg(boolean arg)
{
setProperty(Boolean.toString(arg));
}
private void boolArrayArg(boolean[] arg)
{
setProperty(Arrays.toString(arg));
}
private void boolArrayArrayArg(boolean[][] arg)
{
setProperty(Arrays.toString(arg));
}
private void charArg(char arg)
{
setProperty(Character.toString(arg));
}
private void charArrayArg(char[] arg)
{
setProperty(Arrays.toString(arg));
}
private void objArg(Object arg)
{
setProperty(arg.toString());
}
private void objArrayArg(Object[] arg)
{
setProperty(Arrays.toString(arg));
}
private void setProperty(String value)
{
System.setProperty("test-key", value);
}
}

View File

@ -0,0 +1,146 @@
package org.test.intrace.agent;
public class BranchPatterns implements Runnable
{
public Throwable th = null;
@Override
public void run()
{
try
{
dowhile();
forloop();
whileloop();
switchblock(1);
switchblock(2);
ternary(true);
ternary(false);
trycatchfinally();
singlelineif(true);
ifthenelse(true);
ifthenelse(false);
ifthenelseifelse(true, false);
ifthenelseifelse(false, true);
ifthenelseifelse(false, false);
}
catch (Throwable thr)
{
th = thr;
}
}
private void dowhile()
{
int loopCount = 0;
do
{
setProperty();
loopCount++;
}
while (loopCount < 2);
}
private void whileloop()
{
int loopCount = 0;
while (loopCount < 2)
{
setProperty();
loopCount++;
}
}
private void forloop()
{
for (int ii = 0; ii < 2; ii++)
{
setProperty();
}
}
private void switchblock(int arg)
{
switch (arg)
{
case 1:
{
setProperty("1");
break;
}
case 2:
{
setProperty("2");
break;
}
}
}
private void ternary(boolean arg)
{
String value = (arg ? "a" : "b");
setProperty(value);
}
private void trycatchfinally()
{
try
{
setProperty();
throw new Exception("test");
}
catch (Exception ex)
{
setProperty();
}
finally
{
setProperty();
}
}
private void singlelineif(boolean arg)
{
if (arg)
setProperty();
}
private void ifthenelse(boolean arg)
{
if (arg)
{
setProperty();
}
else
{
setProperty();
}
}
private void ifthenelseifelse(boolean arg1, boolean arg2)
{
if (arg1)
{
setProperty();
}
else if (arg2)
{
setProperty();
}
else
{
setProperty();
}
}
private void setProperty()
{
setProperty(Long.toString(System.currentTimeMillis()));
}
private void setProperty(String value)
{
System.setProperty("test-key", value);
}
}

View File

@ -0,0 +1,12 @@
package org.test.intrace.agent;
import java.util.ArrayList;
import java.util.List;
public class TraceData
{
public boolean seenEnter;
public boolean seenExit;
public final List<Integer> branchLines = new ArrayList<Integer>();
public final List<String> args = new ArrayList<String>();
}