Commit dd0bcbcc authored by Lukáš Marek's avatar Lukáš Marek

Jborat inclusion + additional changes - incomplete

parent d163562b
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="src-jborat"/>
<classpathentry kind="src" path="test/src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/jborat-interface.jar"/>
<classpathentry kind="lib" path="lib/jborat-runtime.jar"/>
<classpathentry kind="lib" path="lib/asm-debug-all-4.0.jar"/>
<classpathentry kind="lib" path="/home/lukas/repos/disl/trunk/test/lib/stp.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
package ch.usi.dag.dynamicbypass;
public class Bootstrap {
public static void completed() {
// TODO jb ! redefinition of DynamicBypassCheck
}
}
package ch.usi.dag.dynamicbypass;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
public abstract class CodeMerger {
private static final String METHOD_FINALIZE = "finalize";
private static final String DBCHECK_CLASS = Type.getInternalName(
DynamicBypassCheck.class);
private static final String DBCHECK_METHOD = "executeUninstrumented";
private static final String DBCHECK_DESC = "()Z";
// NOTE: the instrumented class node will serve as an output
public static void mergeClasses(ClassNode originalCN,
ClassNode instrumentedCN) {
// do not merge interfaces
if ((originalCN.access & Opcodes.ACC_INTERFACE) != 0) {
return;
}
for (MethodNode instrMN : instrumentedCN.methods) {
// evaluation is done always but is more visible then in single if
boolean methodAbstract = (instrMN.access & Opcodes.ACC_ABSTRACT) != 0;
boolean methodNative = (instrMN.access & Opcodes.ACC_NATIVE) != 0;
// TODO jb ! test
boolean methodFinalizeInObject = instrumentedCN.name.equals(Type
.getInternalName(Object.class))
&& instrMN.name.equals(METHOD_FINALIZE);
// skip methods that should not be merged
if (methodAbstract | methodNative | methodFinalizeInObject) {
continue;
}
MethodNode origMN = getMethodNode(originalCN, instrMN.name,
instrMN.desc);
InsnList ilist = instrMN.instructions;
// TODO jb - check for similarity of the instructions
// add reference to the original code
LabelNode origCodeL = new LabelNode();
ilist.add(origCodeL);
// add original code
ilist.add(origMN.instructions);
// add exception handlers of the original code
instrMN.tryCatchBlocks.addAll(origMN.tryCatchBlocks);
// if the dynamic bypass is activated (non-zero value returned)
// then jump to original code
ilist.insert(new JumpInsnNode(Opcodes.IFNE, origCodeL));
ilist.insert(new MethodInsnNode(Opcodes.INVOKESTATIC,
DBCHECK_CLASS, DBCHECK_METHOD, DBCHECK_DESC));
}
}
private static MethodNode getMethodNode(ClassNode cnToSearch,
String methodName, String methodDesc) {
for (MethodNode mn : cnToSearch.methods) {
if (methodName.equals(mn.name) && methodDesc.equals(mn.desc)) {
return mn;
}
}
throw new RuntimeException(
"Code merger fatal error: method for merge not found");
}
}
package ch.usi.dag.dynamicbypass;
public class DynamicBypass {
private static final String PROP_DEBUG = "debug";
private static final boolean debug = Boolean.getBoolean(PROP_DEBUG);
public static boolean isActivated() {
return Thread.currentThread().bypass;
}
public static void activate() {
// bypass should be 0 in this state
if(debug && Thread.currentThread().bypass) {
throw new RuntimeException(
"Dynamic bypass fatal error: activated twice");
}
Thread.currentThread().bypass = true;
}
public static void deactivate() {
// bypass should be 1 in this state
if(debug && ! Thread.currentThread().bypass) {
throw new RuntimeException(
"Dynamic bypass fatal error: deactivated twice");
}
Thread.currentThread().bypass = false;
}
}
package ch.usi.dag.dynamicbypass;
public class DynamicBypassCheck {
// this version is executed before bootstrap phase
// and will be replaced by ...AfterBootstrap() after bootstrap
public static boolean executeUninstrumented() {
return true;
}
public static boolean executeUninstrumentedAfterBootstrap() {
return DynamicBypass.isActivated();
}
}
package ch.usi.dag.jborat.agent;
import java.lang.instrument.Instrumentation;
import ch.usi.dag.dynamicbypass.Bootstrap;
public class JavaAgent {
public static void premain(String agentArguments,
Instrumentation instrumentation) {
if (!Boolean.getBoolean("jborat.noBootstrap")) {
Bootstrap.completed();
}
}
}
package ch.usi.dag.jborat.server;
public class ClassAsBytes {
private byte[] name;
private byte[] code;
public ClassAsBytes(byte[] name, byte[] code) {
this.name = name;
this.code = code;
}
public byte[] getName() {
return name;
}
public byte[] getCode() {
return code;
}
}
package ch.usi.dag.jborat.server;
import java.net.ServerSocket;
import java.util.concurrent.atomic.AtomicInteger;
import ch.usi.dag.disl.DiSL;
import ch.usi.dag.disl.exception.DiSLException;
public abstract class InstrumentationServer {
public static final String PROP_DEBUG = "debug";
private static final boolean debug = Boolean.getBoolean(PROP_DEBUG);
private static final String PROP_PORT = "jborat.port";
private static final int DEFAULT_PORT = 11217;
private static final int port = Integer.getInteger(PROP_PORT, DEFAULT_PORT);
private static final AtomicInteger aliveWorkers = new AtomicInteger();
private static DiSL disl;
public static void main(String args[]) {
try {
// use dynamic bypass
disl = new DiSL(true);
if (debug) {
System.out.println("Instrumentation server is starting on port "
+ port);
}
ServerSocket listenSocket = new ServerSocket(port);
while (true) {
NetClassReader sc =
new NetClassReader(listenSocket.accept());
aliveWorkers.incrementAndGet();
new Worker(sc, disl).start();
if (debug) {
System.out.println("New connection accepted...");
}
}
} catch (Exception e) {
reportError(e);
}
}
public static void reportError(Throwable e) {
if (e instanceof DiSLException) {
// Error reported in DiSL - just exit
return;
}
if (e instanceof JboratException) {
System.err.println("Jborat error: " + e.getMessage());
if (debug) {
e.printStackTrace();
}
}
// fatal exception (unexpected)
System.err.println("Fatal error: " + e.getMessage());
e.printStackTrace();
}
public static void workerDone() {
if (aliveWorkers.decrementAndGet() == 0) {
disl.terminate();
if (debug) {
System.out.println("Instrumentation server is shutting down");
}
// no workers - shutdown
System.exit(0);
}
}
}
package ch.usi.dag.jborat.server;
public class JboratException extends Exception {
private static final long serialVersionUID = 5272000884539359236L;
public JboratException() {
super();
}
public JboratException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public JboratException(String message, Throwable cause) {
super(message, cause);
}
public JboratException(String message) {
super(message);
}
public JboratException(Throwable cause) {
super(cause);
}
}
package ch.usi.dag.jborat.server;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class NetClassReader {
private final Socket socket;
private final DataInputStream is;
private final OutputStream os;
public NetClassReader(Socket socket) throws IOException {
this.socket = socket;
is = new DataInputStream(socket.getInputStream());
os = new BufferedOutputStream(socket.getOutputStream());
}
public ClassAsBytes readClassAsBytes() throws IOException {
// protocol:
// java int - class name length (cnl)
// java int - code length (cl)
// bytes[cnl] - class name
// bytes[cl] - class code
int nameLength = is.readInt();
int codeLength = is.readInt();
// end of communication
if(nameLength == 0 && codeLength == 0) {
return null;
}
// allocate buffer for class reading
byte[] name = new byte[nameLength];
byte[] code = new byte[codeLength];
// read class
is.readFully(name);
is.readFully(code);
return new ClassAsBytes(name, code);
}
public void close() throws IOException {
is.close();
socket.close();
}
public void sendClassAsBytes(ClassAsBytes cab) throws IOException {
// protocol:
// java int - class name length (cnl)
// java int - code length (cl)
// bytes[cnl] - class name
// bytes[cl] - class code
os.write(cab.getName().length);
os.write(cab.getCode().length);
os.write(cab.getName());
os.write(cab.getCode());
os.flush();
}
}
package ch.usi.dag.jborat.server;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import ch.usi.dag.disl.DiSL;
import ch.usi.dag.disl.exception.DiSLException;
import ch.usi.dag.disl.scope.WildCard;
import com.sun.xml.internal.ws.org.objectweb.asm.Type;
public class Worker extends Thread {
private static final boolean debug = Boolean
.getBoolean(InstrumentationServer.PROP_DEBUG);
private static final String PROP_EXCLIST = "jborat.exclusionList";
private static final String excListPath =
System.getProperty(PROP_EXCLIST, null);
private static final String PROP_UNINSTR = "jborat.uninstrumented";
private static final String uninstrPath =
System.getProperty(PROP_UNINSTR, null);
private static final String PROP_INSTR = "jborat.instrumented";
private static final String instrPath =
System.getProperty(PROP_INSTR, null);
private static Set<String> exclusionList;
private final NetClassReader sc;
private final DiSL disl;
Worker(NetClassReader sc, DiSL disl) {
this.sc = sc;
this.disl = disl;
}
public void run() {
try {
instrumentationLoop();
sc.close();
}
catch (Throwable e) {
InstrumentationServer.reportError(e);
}
finally {
InstrumentationServer.workerDone();
}
}
private boolean filterClass(String className) throws JboratException {
// race condition can be here (invocation from multiple threads)
// but this is not serious problem
if(exclusionList == null) {
exclusionList = readExlusionList();
}
// check the exclusion list for possible matches
for(String pattern : exclusionList) {
if(WildCard.match(className, pattern)) {
return false;
}
}
if (debug) {
System.out.println("Excluding class: " + className);
}
if (className.equals(Type.getInternalName(Thread.class))) {
throw new JboratException(Thread.class.getName()
+ " cannot be excluded in exclusion list");
}
return true;
}
private Set<String> readExlusionList() throws JboratException {
final String COMMENT_START = "#";
try {
Set<String> exclSet = new HashSet<String>();
// read exclusion list line by line
Scanner scanner = new Scanner(new FileInputStream(excListPath));
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if(! line.startsWith(COMMENT_START)) {
exclSet.add(internClassName(line));
}
}
scanner.close();
// TODO jb ! add classes from agent and instrumentation jar
return exclSet;
} catch(FileNotFoundException e) {
throw new JboratException(e);
}
}
private String internClassName(String normalClassName) {
final char NORMAL_DELIM = '.';
final char INTERN_DELIM = '/';
return normalClassName.replace(NORMAL_DELIM, INTERN_DELIM);
}
private void instrumentationLoop() throws JboratException, DiSLException {
try {
while (true) {
ClassAsBytes cab = sc.readClassAsBytes();
// communication closed by the client
if (cab == null) {
return;
}
byte[] instrClass;
try {
// TODO jb - weave time stats
instrClass = instrument(new String(cab.getName()),
cab.getCode());
}
catch (DiSLException e) {
// instrumentation error
// send the client a description of the server-side error
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
// error protocol:
// class name contains the description of the server-side error
// class code is an array of size zero
String errMsg = "Error in class " + cab.getName() + ": "
+ sw.toString();
sc.sendClassAsBytes(new ClassAsBytes(errMsg.getBytes(),
new byte[0]));
throw e;
}
sc.sendClassAsBytes(new ClassAsBytes(cab.getName(), instrClass));
}
}
catch (IOException e) {
throw new JboratException(e);
}
}
private byte[] instrument(String className, byte[] origCode)
throws JboratException, DiSLException {
// dump uninstrumented
if (uninstrPath != null) {
dump(className, origCode, uninstrPath);
}
// filter
if (filterClass(className)) {
if (debug) {
System.out.println("Excluding " + className);
}
return origCode;
}
// instrument
byte[] instrCode = disl.instrument(origCode);
// dump instrumented
if (instrPath != null && instrCode != null) {
dump(className, instrCode, instrPath);
}
return instrCode;
}
private void dump(String className, byte[] codeAsBytes, String path)
throws JboratException {
final String CLASS_DELIM = "/";
final String CLASS_EXT = ".class";
try {
// extract the class name and package name
int i = className.lastIndexOf(CLASS_DELIM);
String onlyClassName = className.substring(i + 1);
String packageName = className.substring(0, i + 1);
// construct path to the class
String pathWithPkg = path + File.pathSeparator + packageName;
// create directories
new File(pathWithPkg).mkdirs();
// dump the class code
FileOutputStream fo = new FileOutputStream(pathWithPkg
+ onlyClassName + CLASS_EXT);
fo.write(codeAsBytes);
fo.close();
}
catch (FileNotFoundException e) {
throw new JboratException(e);
}
catch (IOException e) {
throw new JboratException(e);
}
}
}
package ch.usi.dag.disl.weaver;
package ch.usi.dag.tlvinserter;
import java.util.Set;
......
# if there is Makefile.local file then include it
ifneq (, $(wildcard Makefile.local))
include Makefile.local
endif
# Source lists
LIBNAME=jboratagent
SOURCES= jboratagent.c
# Object files needed to create library
OBJECTS=$(SOURCES:%.c=%.o)
# Library name and options needed to build it
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
LIBRARY=lib$(LIBNAME).so
endif
ifeq ($(UNAME), Darwin)
LIBRARY=lib$(LIBNAME).jnilib
endif
# Building a shared library
LINK_SHARED=$(LINK.c) -shared -o $@
# GNU Compiler options needed to build it
COMMON_FLAGS=-fno-strict-aliasing -fPIC -fno-omit-frame-pointer
# Options that help find errors
COMMON_FLAGS+= -W -Wall -Wextra -O3 -Wno-unused-parameter
# To allow access to dladdr()
COMMON_FLAGS+= -D_GNU_SOURCE
# To prevent include of procfs.h
COMMON_FLAGS+= -DLINUX
# To make sure code is reentrant
COMMON_FLAGS+= -D_REENTRANT
CFLAGS += $(COMMON_FLAGS)
CFLAGS += -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
all: $(LIBRARY)
# Build native library
$(LIBRARY): $(OBJECTS)
$(LINK_SHARED) $(OBJECTS) $(LIBRARIES)
# Cleanup the built bits
clean:
rm -f $(LIBRARY) $(OBJECTS)
# rename this file to Makefile.local to take effect
# if variable is not defined autodetection or default is used
#JAVA_HOME = /etc/alternatives/java_sdk/
This diff is collapsed.
#include <jvmti.h>
#ifndef _JBORATCLIENTAGENT_H
#define _JBORATCLIENTAGENT_H
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved);