Commit 607a2b1e authored by Lukáš Marek's avatar Lukáš Marek

Merged revisions 485:574 from trunk

parent 614d2aa3
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>disl</name>
<name>DiSL</name>
<comment></comment>
<projects>
</projects>
......
This diff is collapsed.
The DiSL language is hosted in Java using annotations, therefore, no special compiler is required.
A convenient way to specify an instrumentation and to pass it to the DiSL framework is to pack the instrumentation classes with a manifest file into one jar.
The DiSL framework will load the jar, parse the instrumentation specification, and instrument all classes used by an application at load-time.
The examples of simple instrumentation are in src-test directory.
The test can be invoked using runTest.sh script in the root directory.
For user defined instrumentation, it is recommended to mimic the start and build script as they are provided for test.
We are working on better setup scripts to ease the setup effort for developer.
......@@ -12,8 +12,6 @@ else
RE_AGENT="${BASE_DIR}/src-re-agent/libdislreagent.so"
fi
# ipc.socket is the default communication
# to enable shared memory, remove ",ipc.socket" from the options
java -agentpath:${C_AGENT} \
-agentpath:${RE_AGENT} \
-javaagent:${BASE_DIR}/build/dislagent-unspec.jar \
......
......@@ -7,7 +7,7 @@
#include "jvmtihelper.h"
// sends data over network
static void send_data(int sockfd, const void * data, int data_len) {
void send_data(int sockfd, const void * data, int data_len) {
int sent = 0;
......@@ -21,7 +21,7 @@ static void send_data(int sockfd, const void * data, int data_len) {
}
// receives data from network
static void rcv_data(int sockfd, void * data, int data_len) {
void rcv_data(int sockfd, void * data, int data_len) {
int received = 0;
......
......@@ -19,9 +19,6 @@
static const int ERR_SERVER = 10003;
static const int TRUE = 1;
static const int FALSE = 0;
// defaults - be sure that space in host_name is long enough
static const char * DEFAULT_HOST = "localhost";
static const char * DEFAULT_PORT = "11217";
......@@ -147,14 +144,14 @@ static void parse_agent_options(char *options) {
// convert number
int fitsP = strlen(port_start) < sizeof(port_number);
check_std_error(fitsP, FALSE, "Port number is too long");
check_error(! fitsP, "Port number is too long");
strcpy(port_number, port_start);
}
// check if host_name is big enough
int fitsH = strlen(options) < sizeof(host_name);
check_std_error(fitsH, FALSE, "Host name is too long");
check_error(! fitsH, "Host name is too long");
strcpy(host_name, options);
}
......@@ -190,7 +187,7 @@ static void send_msg(connection_item * conn, message * msg) {
}
// receives class from network
message rcv_msg(connection_item * conn) {
static message rcv_msg(connection_item * conn) {
#ifdef DEBUG
printf("Receiving ");
......@@ -241,7 +238,7 @@ static connection_item * open_connection() {
// get host address
struct addrinfo * addr;
int gai_res = getaddrinfo(host_name, port_number, NULL, &addr);
check_std_error(gai_res == 0, FALSE, gai_strerror(gai_res));
check_error(gai_res != 0, gai_strerror(gai_res));
// create stream socket
int sockfd = socket(addr->ai_family, SOCK_STREAM, 0);
......@@ -435,7 +432,7 @@ static void load_super_class(JNIEnv* jni_env, jobject loader, const char* name,
// ******************* CLASS LOAD callback *******************
static void JNICALL jvmti_callback_class_file_load_hook( jvmtiEnv *jvmti_env,
void JNICALL jvmti_callback_class_file_load_hook( jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jclass class_being_redefined, jobject loader,
const char* name, jobject protection_domain, jint class_data_len,
const unsigned char* class_data, jint* new_class_data_len,
......@@ -497,7 +494,7 @@ static void JNICALL jvmti_callback_class_file_load_hook( jvmtiEnv *jvmti_env,
// ******************* SHUTDOWN callback *******************
static void JNICALL jvmti_callback_class_vm_death_hook(jvmtiEnv *jvmti_env, JNIEnv* jni_env) {
void JNICALL jvmti_callback_class_vm_death_hook(jvmtiEnv *jvmti_env, JNIEnv* jni_env) {
enter_critical_section(jvmti_env, global_lock);
{
......@@ -563,8 +560,8 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
callbacks.ClassFileLoadHook = &jvmti_callback_class_file_load_hook;
callbacks.VMDeath = &jvmti_callback_class_vm_death_hook;
(*jvmti_env)->SetEventCallbacks(jvmti_env, &callbacks,
(jint) sizeof(callbacks));
error = (*jvmti_env)->SetEventCallbacks(jvmti_env, &callbacks, (jint) sizeof(callbacks));
check_jvmti_error(jvmti_env, error, "Cannot set callbacks");
error = (*jvmti_env)->SetEventNotificationMode(jvmti_env, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
check_jvmti_error(jvmti_env, error, "Cannot set class load hook");
......
......@@ -9,14 +9,32 @@
#include <stdlib.h>
static const int ERR_JVMTI = 10001;
static const int ERR_COMM = 10002;
// true/false consts
#define TRUE 1
#define FALSE 0
// error nums
#define ERR 10000
#define ERR_STD 10002
#define ERR_JVMTI 10003
/*
* Reports error if condition is true
*/
void check_error(int cond, const char *str) {
if (cond) {
fprintf(stderr, "%s%s\n", ERR_PREFIX, str);
exit(ERR);
}
}
/*
* Check error routine - reporting on one place
*/
static void check_std_error(int retval, int errorval,
const char *str) {
void check_std_error(int retval, int errorval, const char *str) {
if (retval == errorval) {
......@@ -28,7 +46,7 @@ static void check_std_error(int retval, int errorval,
perror(msgbuf);
exit(ERR_COMM);
exit(ERR_STD);
}
}
......@@ -38,8 +56,7 @@ static void check_std_error(int retval, int errorval,
* The interface GetErrorName() returns the actual enumeration constant
* name, making the error messages much easier to understand.
*/
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum,
const char *str) {
void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum, const char *str) {
if (errnum != JVMTI_ERROR_NONE) {
char *errnum_str;
......@@ -58,7 +75,7 @@ static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum,
/*
* Enter a critical section by doing a JVMTI Raw Monitor Enter
*/
static void enter_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
void enter_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
jvmtiError error;
......@@ -69,7 +86,7 @@ static void enter_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
/*
* Exit a critical section by doing a JVMTI Raw Monitor Exit
*/
static void exit_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
void exit_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
jvmtiError error;
......
......@@ -6,11 +6,18 @@ import ch.usi.dag.disl.dynamicbypass.Bootstrap;
public class DiSLAgent {
private static volatile Instrumentation myInstrumentation;
public static void premain(String agentArguments,
Instrumentation instrumentation) {
myInstrumentation = instrumentation;
if (!Boolean.getBoolean("dislserver.noBootstrap")) {
Bootstrap.completed(instrumentation);
}
}
public static Instrumentation getInstrumentation() {
return myInstrumentation;
}
}
package ch.usi.dag.disl.test.after4;
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.AfterThrowing;
import ch.usi.dag.disl.dynamiccontext.DynamicContext;
import ch.usi.dag.disl.marker.BodyMarker;
public class DiSLClass {
@After(marker = BodyMarker.class, scope = "TargetClass.print(boolean)", order = 0)
public static void after(DynamicContext dc) {
System.out.println("after " + dc.getException());
}
@AfterThrowing(marker = BodyMarker.class, scope = "TargetClass.print(boolean)", order = 1)
public static void afterThrowning(DynamicContext dc) {
System.out.println("afterThrowning " + dc.getException());
}
}
Manifest-Version: 1.0
DiSL-Classes: ch.usi.dag.disl.test.after4.DiSLClass
\ No newline at end of file
package ch.usi.dag.disl.test.after4;
public class TargetClass {
public void print(boolean flag) {
try {
System.out.println("try-clause");
if (flag) {
String float_one = "1.0";
int int_one = Integer.valueOf(float_one);
System.out.println(int_one + "This should not be printed!");
}
System.out.println("normal return");
} finally {
System.out.println("finally-clause");
}
}
public static void main(String[] args) {
TargetClass t = new TargetClass();
System.out.println("=========call print(false)=========");
t.print(false);
System.out.println("=========call print(true) =========");
t.print(true);
}
}
......@@ -2,13 +2,13 @@ package ch.usi.dag.disl.test.loop;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.marker.BasicBlockMarker;
import ch.usi.dag.disl.staticcontext.BasicBlockStaticContext;
import ch.usi.dag.disl.staticcontext.LoopStaticContext;
public class DiSLClass {
@Before(marker = BasicBlockMarker.class, scope = "TargetClass.print()", order = 2)
public static void precondition(BasicBlockStaticContext bba) {
System.out.println("Enter basic block ! index: " + bba.getBBindex()
+ " loopstart? " + (bba.isFirstOfLoop() ? "true" : "false"));
public static void precondition(LoopStaticContext lsc) {
System.out.println("Enter basic block ! index: " + lsc.getBBindex()
+ " loopstart? " + (lsc.isFirstOfLoop() ? "true" : "false"));
}
}
......@@ -24,9 +24,11 @@ import ch.usi.dag.disl.cbloader.ManifestHelper.ManifestInfo;
import ch.usi.dag.disl.classparser.ClassParser;
import ch.usi.dag.disl.exception.DiSLException;
import ch.usi.dag.disl.exception.DiSLIOException;
import ch.usi.dag.disl.exception.DynamicInfoException;
import ch.usi.dag.disl.exception.DiSLInMethodException;
import ch.usi.dag.disl.exception.DynamicContextException;
import ch.usi.dag.disl.exception.InitException;
import ch.usi.dag.disl.exception.ManifestInfoException;
import ch.usi.dag.disl.exception.MarkerException;
import ch.usi.dag.disl.exception.ProcessorException;
import ch.usi.dag.disl.exception.ReflectionException;
import ch.usi.dag.disl.exception.StaticContextGenException;
......@@ -51,6 +53,10 @@ import ch.usi.dag.disl.utilinstr.tlvinserter.TLVInserter;
import ch.usi.dag.disl.weaver.Weaver;
// TODO javadoc comment all
/**
* Main DiSL class providing interface for an instrumentation framework
* (normally DiSL Server)
*/
public class DiSL {
public final String PROP_DEBUG = "debug";
......@@ -76,6 +82,10 @@ public class DiSL {
private final List<Snippet> snippets;
/**
* DiSL initialization
* @param useDynamicBypass enable or disable dynamic bypass instrumentation
*/
// this method should be called only once
public DiSL(boolean useDynamicBypass) throws DiSLException {
......@@ -143,6 +153,10 @@ public class DiSL {
// specify this for InstructionMarker
}
/**
* Finds transformer class in configuration and allocates it.
* @return newly allocated transformer.
*/
private Transformer resolveTransformer() throws ManifestInfoException,
ReflectionException {
......@@ -190,12 +204,10 @@ public class DiSL {
* class that will be instrumented
* @param methodNode
* method in the classNode argument, that will be instrumented
* @param staticContextInstances
*
*/
private boolean instrumentMethod(ClassNode classNode, MethodNode methodNode)
throws ReflectionException, StaticContextGenException,
ProcessorException, DynamicInfoException {
ProcessorException, DynamicContextException, MarkerException {
// skip abstract methods
if ((methodNode.access & Opcodes.ACC_ABSTRACT) != 0) {
......@@ -210,6 +222,11 @@ public class DiSL {
String className = classNode.name;
String methodName = methodNode.name;
String methodDesc = methodNode.desc;
if(debug) {
System.out.println("Instrumenting method: " + className
+ Constants.CLASS_DELIM + methodName + "(" + methodDesc
+ ")");
// evaluate exclusions
for (Scope exclScope : exclusionSet) {
......@@ -300,16 +317,18 @@ public class DiSL {
Weaver.instrument(classNode, methodNode, snippetMarkings,
new LinkedList<SyntheticLocalVar>(usedSLVs), staticInfo,
piResolver);
if(debug) {
System.out.println("Instumenting method: " + className
+ Constants.CLASS_DELIM + methodName + "(" + methodDesc
+ ")");
}
return true;
}
/**
* Selects only shadows matching the passed guard.
*
* @param guard guard, on witch conditions are the shadows selected
* @param marking the list of shadows from where the gurads selects
* @return selected shadows
*/
private List<Shadow> selectShadowsWithGuard(Method guard,
List<Shadow> marking) {
......@@ -331,6 +350,9 @@ public class DiSL {
return selectedMarking;
}
/**
* Data holder for an instrumented class
*/
private static class InstrumentedClass {
private ClassNode classNode;
......@@ -352,7 +374,15 @@ public class DiSL {
}
}
// this method is thread safe after initialization
/**
* Instruments class node.
*
* Note: This method is thread safe. Parameter classNode is changed during
* the invocation.
*
* @param classNode class node to instrument
* @return instrumented class
*/
private InstrumentedClass instrumentClass(ClassNode classNode)
throws DiSLException {
......@@ -366,7 +396,17 @@ public class DiSL {
// instrument all methods in a class
for (MethodNode methodNode : classNode.methods) {
boolean methodChanged = instrumentMethod(classNode, methodNode);
boolean methodChanged = false;
// intercept all exceptions and add a method name
try {
methodChanged = instrumentMethod(classNode, methodNode);
}
catch(DiSLException e) {
throw new DiSLInMethodException(
classNode.name + "." + methodNode.name, e);
}
// add method to the set of changed methods
if (methodChanged) {
......@@ -415,6 +455,12 @@ public class DiSL {
return null;
}
/**
* Instruments array of bytes representing a class
*
* @param classAsBytes class as array of bytes
* @return instrumeted class as array of bytes
*/
public byte[] instrument(byte[] classAsBytes) throws DiSLException {
// output bytes into the file
......@@ -448,9 +494,7 @@ public class DiSL {
// - classNode with API param is required by ASM 4.0 guidelines
ClassNode classNode = new ClassNode(Opcodes.ASM4);
// TODO jb - try without SKIP_DEBUG
classReader.accept(classNode, ClassReader.SKIP_DEBUG
| ClassReader.EXPAND_FRAMES);
classReader.accept(classNode, ClassReader.EXPAND_FRAMES);
InstrumentedClass instrClass = instrumentClass(classNode);
......@@ -473,9 +517,7 @@ public class DiSL {
ClassReader origCR = new ClassReader(classAsBytes);
ClassNode origCN = new ClassNode();
// TODO jb - try without SKIP_DEBUG
origCR.accept(origCN, ClassReader.SKIP_DEBUG
| ClassReader.EXPAND_FRAMES);
origCR.accept(origCN, ClassReader.EXPAND_FRAMES);
// origCN and instrCN are destroyed during the merging
instrCN = CodeMerger.mergeClasses(origCN, instrCN,
......@@ -510,6 +552,9 @@ public class DiSL {
return cw.toByteArray();
}
/**
* Termination handler - should be invoked by the instrumentation framework
*/
public void terminate() {
}
......
......@@ -2,6 +2,22 @@ package ch.usi.dag.disl.annotation;
import ch.usi.dag.disl.marker.Marker;
/**
* The After annotation instructs DiSL to insert the snippet body after the
* marked region. The snippet will be invoked after a normal exit of the region
* or after an exit caused by an exception.
*
* NOTE: This is only general contract. It depends on particular marker how the
* contract will be implemented.
*
* This annotation should be used with methods.
*
* The method should be static, not return any values and not throw any
* exceptions.
*
* Method argument can be StaticContext, DynamicContext, ClassContext and
* ArgumentProcessorContext.
*/
public @interface After {
// NOTE if you want to change names, you need to change
......@@ -9,13 +25,52 @@ public @interface After {
// NOTE because of implementation of annotations in java the defaults
// are not retrieved from here but from class mentioned above
/**
* Marker class defines a region where the snippet is applied.
*/
Class<? extends Marker> marker();
/**
* Argument for the marker (as string).
*
* Default value: ""
*/
String args() default ""; // cannot be null :(
/**
* Scope of the methods, where the snippet is applied.
*
* @see ch.usi.dag.disl.scope package for more info about scoping language.
*
* Default value: "*"
*/
String scope() default "*";
/**
* The guard class defining if the snippet will be inlined in particular
* region or not.
*
* Default value: Object.class - means none
*/
Class<? extends Object> guard() default Object.class; // cannot be null :(
/**
* Defines ordering of the snippets. Smaller number indicates that snippet
* will be inlined closer to the instrumented code.
*
* Default value: 100
*/
int order() default 100;
// NOTE that activation of dynamic bypass is decided by the instrumentation
// framework in first place
/**
* Advanced option. You can in general disable dynamic bypass on snippets,
* that are not using any other class.
*
* NOTE Usage of dynamic bypass is determined by the underlying
* instrumentation framework.
*
* Default value: true
*/
boolean dynamicBypass() default true;
}
......@@ -2,6 +2,22 @@ package ch.usi.dag.disl.annotation;
import ch.usi.dag.disl.marker.Marker;
/**
* The AfterReturning annotation instructs DiSL to insert the snippet body after
* the marked region. The snippet will be invoked after a normal exit of the
* region.
*
* NOTE: This is only general contract. It depends on particular marker how the
* contract will be implemented.
*
* This annotation should be used with methods.
*
* The method should be static, not return any values and not throw any
* exceptions.
*
* Method argument can be StaticContext, DynamicContext, ClassContext and
* ArgumentProcessorContext.
*/
public @interface AfterReturning {
// NOTE if you want to change names, you need to change
......@@ -10,12 +26,51 @@ public @interface AfterReturning {
// NOTE because of implementation of annotations in java the defaults
// are not retrieved from here but from class mentioned above
/**
* Marker class defines a region where the snippet is applied.
*/
Class<? extends Marker> marker();
/**
* Argument for the marker (as string).
*
* Default value: ""
*/
String args() default ""; // cannot be null :(
/**
* Scope of the methods, where the snippet is applied.
*
* @see ch.usi.dag.disl.scope package for more info about scoping language.
*
* Default value: "*"
*/
String scope() default "*";
/**
* The guard class defining if the snippet will be inlined in particular
* region or not.
*
* Default value: Object.class - means none
*/
Class<? extends Object> guard() default Object.class; // cannot be null :(
/**
* Defines ordering of the snippets. Smaller number indicates that snippet
* will be inlined closer to the instrumented code.
*
* Default value: 100
*/
int order() default 100;
// NOTE that activation of dynamic bypass is decided by the instrumentation
// framework in first place
/**
* Advanced option. You can in general disable dynamic bypass on snippets,
* that are not using any other class.
*
* NOTE Usage of dynamic bypass is determined by the underlying
* instrumentation framework.
*
* Default value: true
*/
boolean dynamicBypass() default true;
}
......@@ -2,6 +2,22 @@ package ch.usi.dag.disl.annotation;
import ch.usi.dag.disl.marker.Marker;
/**
* The AfterThrowing annotation instructs DiSL to insert the snippet body after
* the marked region. The snippet will be invoked after an exit caused by an
* exception.
*
* NOTE: This is only general contract. It depends on particular marker how the
* contract will be implemented.
*
* This annotation should be used with methods.
*
* The method should be static, not return any values and not throw any
* exceptions.
*
* Method argument can be StaticContext, DynamicContext, ClassContext and
* ArgumentProcessorContext.
*/
public @interface AfterThrowing {
// NOTE if you want to change names, you need to change
......@@ -10,12 +26,51 @@ public @interface AfterThrowing {
// NOTE because of implementation of annotations in java the defaults
// are not retrieved from here but from class mentioned above
/**
* Marker class defines a region where the snippet is applied.
*/
Class<? extends Marker> marker();
/**
* Argument for the marker (as string).
*
* Default value: ""
*/
String args() default ""; // cannot be null :(
/**
* Scope of the methods, where the snippet is applied.
*
* @see ch.usi.dag.disl.scope package for more info about scoping language.
*
* Default value: "*"
*/
String scope() default "*";
/**
* The guard class defining if the snippet will be inlined in particular
* region or not.
*
* Default value: Object.class - means none
*/
Class<? extends Object> guard() default Object.class; // cannot be null :(
/**
* Defines ordering of the snippets. Smaller number indicates that snippet
* will be inlined closer to the instrumented code.
*
* Default value: 100
*/
int order() default 100;
// NOTE that activation of dynamic bypass is decided by the instrumentation
// framework in first place
/**
* Advanced option. You can in general disable dynamic bypass on snippets,
* that are not using any other class.
*
* NOTE Usage of dynamic bypass is determined by the underlying
* instrumentation framework.
*
* Default value: true
*/
boolean dynamicBypass() default true;
}
package ch.usi.dag.disl.annotation;
/**
* Annotated class defines method for processing method arguments. The specified
* methods will be inlined into snippets to process one method argument value.
*
* First argument of the method is a type, that will be processed by the method.
* The allowed types are all basic types, String and Object class. The processed
* type can be extend in some special cases by ProcessAlso annotation.
* During run-time, the argument will contain a processed method argument value.
*
* ArgumentContext can be used to fetch additional data about the argument.
*
* This annotation should be used with classes.