Commit 22fb3da3 authored by Aibek Sarimbekov's avatar Aibek Sarimbekov

recasted racer

parent 750adf58
<project name="disl-test" default="compile" basedir=".">
<property file="../../disl.version"/>
<property file="../../build.properties"/>
<property file="build.properties"/>
<property name="disl.jar.path" value="../../build/dislserver-${disl.version}.jar"/>
<path id="buildpath">
<pathelement location="${disl.jar.path}"/>
<pathelement location="../../${asm.path}"/>
<pathelement location="../../build/dislagent-unspec.jar"/>
</path>
<target name="check-disl-jar">
<available file="${disl.jar.path}" property="disl.jar.present"/>
</target>
<target name="report-missing-jar" depends="check-disl-jar" unless="disl.jar.present">
<fail message="DiSL jar not found at ${disl.jar.path}. Build DiSL first!" />
</target>
<target name="compile" depends="report-missing-jar">
<mkdir dir="bin"/>
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
<classpath refid="buildpath"/>
</javac>
</target>
<target name="check-test-property">
<condition property="test.set">
<isset property="test.name"/>
</condition>
</target>
<target name="report-missing-property" depends="check-test-property" unless="test.set">
<fail message="Property test.name is not set. Set it using -Dtest.name=value" />
</target>
<property name="test.path" value="ch/usi/dag/disl/example/${test.name}"/>
<target name="package" depends="report-missing-property,compile" description="create instrumentation package for specified test">
<mkdir dir="build"/>
<jar jarfile="../../build/${instr.jar.name}"
basedir="./bin"
includes="${test.path}/**"
excludes="${test.path}/TargetClass*.class ${test.path}/MANIFEST.MF"
manifest="./src/${test.path}/MANIFEST.MF">
</jar>
</target>
<target name="clean">
<delete dir="bin"/>
<delete dir="build"/>
</target>
</project>
# THESE ARE THE BASIC SETTINGS
#sun.instrument.*
# THESE ARE THE RUNTIME CLASSES THAT MUST NOT BE WOVEN
# BEGIN JDK WEAVING
# COMMENT THE FOLLOWING FOR FULL COVERAGE
#org.jcp.*
#org.omg.*
#org.xml.*
#org.ietf.*
java.*.*
java.lang.Object.*.*
javax.*.*
com.sun.*.*
com.apple.*.*
sun.*.*
sunx.*.*
# END JDK
# /bin/sh
EXPECTED_ARGS=1
if [ $# -gt $EXPECTED_ARGS ]
then
echo "Usage: `basename $0` [pkg]"
exit
fi
SERVER_FILE=.server.pid
export SERVER_FILE
if [ -e ${SERVER_FILE} ]
then
kill -KILL `cat ${SERVER_FILE}`
rm .server.pid
fi
DISL_CLASS="./bin/ch/usi/dag/disl/example/racer/DiSLClass.class"
TARGET_CLASS="ch.usi.dag.disl.example.racer.TargetClass"
if [ "$1" = "pkg" ]
then
# start server and take pid
ant package -Dtest.name=racer
./runServer.sh
else
# start server and take pid
./runServer.sh -Ddisl.classes=${DISL_CLASS}
fi
# wait for server startup
sleep 5
# run client
./runClient.sh ${TARGET_CLASS}
# kill server
kill -KILL `cat ${SERVER_FILE}`
rm .server.pid
#!/bin/bash
OS=`uname`
if [ "${OS}" = "Darwin" ]; then
C_AGENT="../../src-agent-c/libdislagent.jnilib"
else
C_AGENT="../../src-agent-c/libdislagent.so"
fi
# ipc.socket is the default communication
# to enable shared memory, remove ",ipc.socket" from the options
java -Xmx4g -XX:MaxPermSize=128m -noverify -agentpath:${C_AGENT} \
-javaagent:../../build/dislagent-unspec.jar \
-Xbootclasspath/a:../../build/dislagent-unspec.jar:../../build/dislinstr.jar \
$*
#!/bin/sh
# available options
# -Ddebug=true \
# -Ddisl.classes="list of disl classes (: - separator)"
# -Ddisl.noexcepthandler=true \
# -Ddisl.exclusionList="path" \
# -Ddislserver.instrumented="path" \
# -Ddislserver.uninstrumented="path" \
# -Ddislserver.port="portNum" \
# -Ddislserver.timestat=true \
# -Ddislserver.continuous=true \
java $* \
-Ddislserver.instrumented="instrumented" \
-Ddislserver.uninstrumented="uninstrumented" \
-Ddisl.exclusionList="conf/exclusion.lst" \
-Ddislserver.continuous=true \
-jar ../../build/dislserver-unspec.jar \
&
echo $! > ${SERVER_FILE}
package ch.usi.dag.disl.example.racer;
import java.util.Stack;
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.annotation.GuardMethod;
import ch.usi.dag.disl.annotation.ThreadLocal;
import ch.usi.dag.disl.classcontext.ClassContext;
import ch.usi.dag.disl.dynamiccontext.DynamicContext;
import ch.usi.dag.disl.example.racer.runtime.AdviceExecutor;
import ch.usi.dag.disl.marker.BytecodeMarker;
import ch.usi.dag.disl.staticcontext.BytecodeStaticContext;
import ch.usi.dag.disl.staticcontext.MethodStaticContext;
public class DiSLClass {
@ThreadLocal
private static Stack<Object> bag;
@Before(marker = BytecodeMarker.class, args = "monitorenter")
public static void onMonitorEnter(MethodStaticContext sc, DynamicContext dc) {
Object objectOnLock = dc.getStackValue(0, Object.class);
if (bag == null)
bag = new Stack<Object>(); bag.add(objectOnLock);
}
@Before(marker = BytecodeMarker.class, args = "monitorexit")
public static void onMonitorExit(MethodStaticContext sc, DynamicContext dc) {
if(bag == null)
System.err.println("MONITOREXIT CAN'T HAPPEN BEFORE MONITORENTER!!!");
bag.pop();
}
@Before(marker = BytecodeMarker.class, args = "getfield")
public static void beforeFieldRead(FieldAccessStaticContext sc, DynamicContext dc, MethodStaticContext msc) {
if (bag == null)
bag = new Stack<Object>(); Object fieldOwner = dc.getStackValue(0, Object.class);
String fieldID = sc.getFieldId();
String accessSite = sc.thisMethodFullName();
// System.out.println("BFR: " + fieldID + "\t" + msc.thisMethodFullName());
AdviceExecutor.getInstance().onFieldAccess(fieldOwner, fieldID, accessSite, bag, true);
}
@Before(marker = BytecodeMarker.class, args = "putfield")
public static void beforeFieldWrite(FieldAccessStaticContext sc, DynamicContext dc, MethodStaticContext msc) {
if (bag == null)
bag = new Stack<Object>();
Object fieldOwner = dc.getStackValue(1, Object.class);
String fieldID = sc.getFieldId();
String accessSite = sc.thisMethodFullName();
// System.out.println("BFW: " + fieldID + "\t" + msc.thisMethodFullName());
AdviceExecutor.getInstance().onFieldAccess(fieldOwner, fieldID, accessSite, bag, false);
}
@After(marker = BytecodeMarker.class, args = "getstatic")
public static void beforeStaticFieldRead(FieldAccessStaticContext sc, DynamicContext dc, ClassContext cc) {
if (bag == null)
bag = new Stack<Object>();
Object clazz = cc.asClass(sc.getOwner());
String accessSite = sc.thisMethodFullName();
String fieldID = sc.getFieldId();
AdviceExecutor.getInstance().onFieldAccess(clazz, fieldID, accessSite, bag, true);
}
@Before(marker = BytecodeMarker.class, args = "putstatic")
public static void beforeStaticFieldWrite(FieldAccessStaticContext sc, DynamicContext dc, ClassContext cc) {
if (bag == null)
bag = new Stack<Object>();
Object clazz = cc.asClass(sc.getOwner());
String fieldID = sc.getFieldId();
String accessSite = sc.thisMethodFullName();
AdviceExecutor.getInstance().onFieldAccess(clazz, fieldID, accessSite, bag, false);
}
public static class ConstructorGuard {
@GuardMethod
public static boolean isApplicable(MethodStaticContext sc) {
return "<init>".equals(sc.thisMethodName());
}
}
}
package ch.usi.dag.disl.example.racer;
import org.objectweb.asm.tree.FieldInsnNode;
import ch.usi.dag.disl.staticcontext.MethodStaticContext;
public class FieldAccessStaticContext extends MethodStaticContext {
public String getOwner() {
return ((FieldInsnNode) staticContextData.getRegionStart()).owner;
}
public String getFieldId() {
FieldInsnNode fin = ((FieldInsnNode) staticContextData.getRegionStart());
return fin.owner + "." + fin.name;
}
}
Manifest-Version: 1.0
DiSL-Classes: ch.usi.dag.disl.example.racer.DiSLClass
DiSL-Transformer: ch.usi.dag.disl.example.racer.SynchronizedMethodsTransformer
\ No newline at end of file
package ch.usi.dag.disl.example.racer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.VarInsnNode;
import ch.usi.dag.disl.transformer.Transformer;
import ch.usi.dag.disl.util.Constants;
import ch.usi.dag.dislserver.DiSLServerException;
public class SynchronizedMethodsTransformer implements Transformer {
final private static String instrPath = "transformed";
final private static String CLASS_EXT = ".class";
final private static char PACKAGE_STD_DELIM = '.';
final private static boolean DEBUG = false;
@Override
public byte[] transform(byte[] classfileBuffer) throws Exception {
byte[] instrumentedBytes = null;
ClassReader cr = new ClassReader(classfileBuffer);
ClassNode clazz = new ClassNode(Opcodes.ASM4);
cr.accept(clazz, 0);
// if(clazz.name.startsWith("java")
// || clazz.name.startsWith("sun")
// //|| clazz.name.contains("disl")
// || clazz.name.contains("com")
// ) {
// return classfileBuffer;
// }
String className = clazz.name.replace('/', '.');
for (MethodNode method : (List<MethodNode>) clazz.methods) {
boolean isStatic = false;
if ((method.access & Opcodes.ACC_ABSTRACT) != 0
|| (method.access & Opcodes.ACC_NATIVE) != 0) {
continue;
}
if ((method.access & Opcodes.ACC_SYNCHRONIZED) != 0) {
if(DEBUG)
System.err.println("++++ Transfroming the method " + clazz.name +"." + method.name + " ++++");
if ((method.access & Opcodes.ACC_STATIC) != 0) {
isStatic = true;
}
method.access -= Opcodes.ACC_SYNCHRONIZED;
InsnList instructions = method.instructions;
final LabelNode startTryCatchBlockLabel = new LabelNode();
final LabelNode endTryCatchBlockLabel = new LabelNode();
InsnList myList = new InsnList();
myList.add(startTryCatchBlockLabel);
if(isStatic) {
myList.add(new LdcInsnNode(className));
myList.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"java/lang/Class",
"forName",
"(Ljava/lang/String;)Ljava/lang/Class;"));
}
else {
myList.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
myList.add(new InsnNode(Opcodes.MONITORENTER));
instructions.insert(myList);
Iterator<AbstractInsnNode> it = instructions.iterator();
while(it.hasNext()) {
AbstractInsnNode instruction = (AbstractInsnNode) it.next();
int opcode = instruction.getOpcode();
if(opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
{
myList.clear();
if(isStatic) {
myList.add(new LdcInsnNode(className));
myList.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"java/lang/Class",
"forName",
"(Ljava/lang/String;)Ljava/lang/Class;"));
}
else {
myList.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
myList.add(new InsnNode(Opcodes.MONITOREXIT));
instructions.insert(instruction.getPrevious(), myList);
}
}
myList.clear();
LabelNode normalExecutionFinallyBlockLabel = new LabelNode();
myList.add(endTryCatchBlockLabel);
method.tryCatchBlocks.add(new
TryCatchBlockNode(startTryCatchBlockLabel, endTryCatchBlockLabel,
endTryCatchBlockLabel, null));
if(isStatic) {
myList.add(new LdcInsnNode(className));
myList.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"java/lang/Class",
"forName",
"(Ljava/lang/String;)Ljava/lang/Class;"));
}
else {
myList.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
myList.add(new InsnNode(Opcodes.MONITOREXIT));
myList.add(new InsnNode(Opcodes.ATHROW));
myList.add(normalExecutionFinallyBlockLabel);
instructions.add(myList);
}
}
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
clazz.accept(cw);
instrumentedBytes = cw.toByteArray();
dump(clazz.name, instrumentedBytes, instrPath);
return instrumentedBytes;
}
@Override
public boolean propagateUninstrumentedClasses() {
return false;
}
private void dump(String className, byte[] codeAsBytes, String path)
throws DiSLServerException {
try {
// extract the class name and package name
int i = className.lastIndexOf(Constants.PACKAGE_INTERN_DELIM);
String onlyClassName = className.substring(i + 1);
String packageName = className.substring(0, i + 1);
// construct path to the class
String pathWithPkg = path + File.separator + packageName;
// create directories
new File(pathWithPkg).mkdirs();
// dump the class code
FileOutputStream fo = new FileOutputStream(pathWithPkg
+ onlyClassName + Constants.CLASS_EXT);
fo.write(codeAsBytes);
fo.close();
}
catch (FileNotFoundException e) {
throw new DiSLServerException(e);
}
catch (IOException e) {
throw new DiSLServerException(e);
}
}
}
package ch.usi.dag.disl.example.racer;
public class TargetClass extends Thread {
static int shared;
static int shared_protected;
int not_shared;
public void run() {
System.err.println(shared++);
synchronized (TargetClass.class) {
System.err.println(shared_protected++);
}
System.err.println(not_shared++);
}
public static void main(String[]args){
TargetClass thread1 = new TargetClass();
TargetClass thread2 = new TargetClass();
thread1.start();
thread2.start();
// new TargetClass().test();
}
// public void test() {
// B b = new B();
// A a = b;
//
// System.out.println("TEST1");
// b.set(3);
// a.set(3);
//
// System.out.println("TEST2");
// a = new A();
// b = new B();
// b.set(3);
// a.set(3);
//
// System.out.println("TEST3");
// a = new A();
// C c = new C();
// a.set(3);
// c.set(5);
// System.out.println("a: " + a.get());
// System.out.println("c: " + c.get());
// }
//
// private class A {
// int x;
//
// void set(int y) { x = y; }
// int get() { return x; }
// }
//
// private class B extends A {
// void set(int y) { x = y; }
// int get() { return x; }
// }
// private class C extends A {
// int x;
//
// void set(int y) { x = y; }
// int get() { return x; }
// }
}
package ch.usi.dag.disl.example.racer.runtime;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
public class AdviceExecutor {
private static final int NUMBER_OF_MAPS = 100000;
private static MyWeakKeyIdentityHashMap[] ownerToFieldToState = new MyWeakKeyIdentityHashMap[NUMBER_OF_MAPS];
private static final AdviceExecutor racer;
static {
racer = new AdviceExecutor();
init();
}
public static AdviceExecutor getInstance() {
return racer;
}
static void init() {
for(int i = 0; i < ownerToFieldToState.length; i++) {
ownerToFieldToState[i] = new MyWeakKeyIdentityHashMap(8);
}
}
public void onFieldAccess(Object owner, String fieldID, String accessSite, Stack<Object> bag, boolean onRead) {
try {
Thread t = Thread.currentThread();
ConcurrentHashMap<String, FieldState> fieldToState;
MyWeakKeyIdentityHashMap localOwnerToFieldToState = ownerToFieldToState[System.identityHashCode(owner) % NUMBER_OF_MAPS];
synchronized(localOwnerToFieldToState) {
if((fieldToState = (ConcurrentHashMap<String, FieldState>)localOwnerToFieldToState.get(owner)) == null) {
localOwnerToFieldToState.put(owner, fieldToState = new ConcurrentHashMap<String, FieldState>());
}
}
String signature;
FieldState currentState;
if((currentState = fieldToState.get(signature = fieldID)) == null) {
synchronized (fieldToState) {
if((currentState = fieldToState.get(signature = fieldID)) == null) {
fieldToState.put(signature, currentState = new FieldState(signature));
}
}
}
if(onRead) {
currentState.onRead(t, accessSite, bag);
}
else {
currentState.onWrite(t, accessSite, bag);
}
} catch(Exception e) {
System.out.println("[AdviceExecutor.onFieldAccess] ERROR WHILE PROCESSING THE BUFFER; ABORTING");
e.printStackTrace();
System.exit(-30);
}
}
}
\ No newline at end of file
package ch.usi.dag.disl.example.racer.runtime;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.Stack;
public final class FieldState {
public static final int VIRGIN = 0;
public static final int EXCLUSIVE = 1;
public static final int MODIFIED = 2;
public static final int SHARED = 3;
public static final int SHARED_MODIFIED = 4;
public static final int REPORTED_RACE = 5;
private static Set<String> reportedRaces;
private final String fieldSignature;
private LinkedList<String> readList = new LinkedList<String>();
private LinkedList<String> writeList = new LinkedList<String>();
private MyList<Object> locksList;
private int currentState;
private Thread t;
private boolean virgin;
static {
init();
}
public FieldState(String fieldSignature) {
this.fieldSignature = fieldSignature;
currentState = VIRGIN;
virgin = true;
}
public static void init() {
reportedRaces = new HashSet<String>();
}
public synchronized void onRead(Thread t, String fieldID, Stack<Object> bag) {
boolean reportRace = false;
switch(currentState) {
case VIRGIN:
currentState = EXCLUSIVE;
this.t = t;
readList.add(fieldID);
updateLocks(bag);
return;
case EXCLUSIVE:
if(this.t != t) {
currentState = SHARED;
readList.add(fieldID);
}
updateLocks(bag);
return;
case MODIFIED:
if(this.t != t) {
readList.add(fieldID);
updateLocks(bag);
if(reportRace = needToReportRace()) {
currentState = REPORTED_RACE;
break;
}
else {
currentState = SHARED_MODIFIED;
return;
}
}
else {
updateLocks(bag);
}
return;
case SHARED:
updateLocks(bag);
return;
case SHARED_MODIFIED:
updateLocks(bag);
if(reportRace = needToReportRace()) {
currentState = REPORTED_RACE;
break;
}
return;
case REPORTED_RACE:
return;
}
if(reportRace) {
reportRace();
}
}
public synchronized void onWrite(Thread t, String fieldID, Stack<Object> bag) {
boolean reportRace = false;
switch(currentState) {
case VIRGIN:
currentState = MODIFIED;
this.t = t;
writeList.add(fieldID);
updateLocks(bag);