Commit 27fd4f3b authored by František Haas's avatar František Haas

updated scope description

fixed scopeimpl bugs
added scopeimpl tests
updated example readme
added structured examples
parent d3e9420a
DiSL SETUP EXAMPLE
==================
================================================================================
= DiSL SETUP EXAMPLE
================================================================================
This simple example illustrates a basic instrumentation setup. It shows, how
These simple examples illustrates a basic instrumentation setup. It shows, how
the instrumentation should be packaged and contains a scripts that should ease
running DiSL together with your program.
The example contains simple user application that prints hello string on
command line and instrumentation printing strings before and after application
main.
HOW TO RUN THE EXAMPLE
======================
They can be also used as a quick start.
These examples demonstrates following features of DiSL.
--------------------------------------------------------------------------------
-- smoke
--------------------------------------------------------------------------------
The simplest of the examples. The application prints a "hello" message from the
main and exits while DiSL instruments the entry and exit of the main method and
prints more text around the "hello".
The code in the function annotated with "@After" and "@Before" is a snippet.
That's the code that is according to information specified in the annotation
instrumented into the client application class' code.
--------------------------------------------------------------------------------
-- scope
--------------------------------------------------------------------------------
This example demonstrates the scoping features. That means the ways how can the
instrumentation application filter application methods based on class-name,
method-name, parameter-types and return-type and instrument just the right
methods. For example those from specified package, class or just those
returning the right types or accepting the right parameters or a combination of
these
--------------------------------------------------------------------------------
-- marker
--------------------------------------------------------------------------------
This example shows how to place instrumentation code on different locations
across methods. That means one can, using scopes, declare methods that should
be processed. But the marker enables to specify where exactly the code should
be placed.
The code might be added at the method body level (BodyMarker), basic block
level (BasicBlockMarker) or even instruction level (e.g.
MethodInvocationMarker). The marker essentially splits the method body into
instrumentation regions. There are some already predefined markers but custom
markers might be created. Creation of custom marker is also shown in the
example.
The marker basically goes trough (scoped) method code and whenever it finds a
region between two instruction interesting it marks it. However, the marker
must be cauties to mark all possible entries and exits of such region (goto,
return, throw, ..).
Then there are region specifiers. They specifiy at which entry or exit of the
region the code should be placed. There is (After), (Before), (AfterReturning),
(AfterThrow).
--------------------------------------------------------------------------------
-- guard
--------------------------------------------------------------------------------
This is another way to select a region for instrumentation. A snippet
annotation might specify a guard class that has a guard method which decides
whether a snippet will or will not be included.
--------------------------------------------------------------------------------
-- static
--------------------------------------------------------------------------------
This example shows how to use a static context information in snippets or guard
methods. A static context is information derived from the original method's
code. There are multiples classes implementing "StaticContext" interface that
provide various information from static analysis of the code. It's also
demonstrated how to implement and use custom static contexts.
A snippet method might accept unspecified number of static contexts in any
order that can be used in the snippet.
For example static context might tell how many instructions there are in the
method or if it contains a loop.
--------------------------------------------------------------------------------
-- dynamic
--------------------------------------------------------------------------------
This example shows how snippets can use also runtime information. For example
values of local variables, object instance in member methods, method arguments
or current exceptions.
================================================================================
= HOW TO RUN THE EXAMPLE
================================================================================
First be sure that you compiled DiSL (see README in the root directory). Then
follow the steps:
Easiest way to run the example is to execute "ant run".
Enter the examples directory, for example "smoke".
Then the easiest way to run the example is to execute "ant run".
This command builds and packs the application and the instrumentation
application. And then starts the instrumentation server and later on also the
......@@ -29,31 +106,24 @@ When compiled the example can be also run using "disl.py" script directly. This
enables more detailed settings. To learn more about this run "./disl.py -h"
NOTE
====
================================================================================
= NOTE
================================================================================
If you want to use "disl.py" to run your application, don't forget to set
"DISL_HOME" variable correctly or it can be specified to "disl.py" at command
line or inside the script as a default value.
NOTE
====
================================================================================
= NOTE
================================================================================
If you want to include use some library in your instrumentation you
shlould insert it directly to the instrumentation jar using jarjar.
If you want to include use some library in your instrumentation you should
insert it directly to the instrumentation jar using jarjar.
http://code.google.com/p/jarjar/
Other option is to provide library's classpath to the "disl.py" utility.
DIRECTORIES (FILES)
===================
app
- contains sources of simple application
instr
- contains sources of simple instrumentation
disl.py
- python script starting the application together with DiSL (+instrumentation)
lib.path=../../build
lib.path=../../../build
dislserver.path=${lib.path}/disl-server.jar
......
<project name="disl-example" default="prepare-all" basedir=".">
<target name="prepare-all">
<ant antfile="app/build.xml" target="package" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="package" useNativeBasedir="true" />
</target>
<project name="disl-example" default="clean" basedir=".">
<target name="clean">
<ant antfile="app/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="clean" useNativeBasedir="true" />
</target>
<target name="run" depends="prepare-all">
<exec executable="./disl.py">
<arg value="--" />
<arg value="instr/build/disl-instr.jar" />
<arg value="-jar" />
<arg value="app/build/example-app.jar" />
</exec>
<ant antfile="dynamic/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="guard/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="marker/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="scope/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="smoke/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="static/build.xml" target="clean" useNativeBasedir="true" />
</target>
</project>
......@@ -11,10 +11,10 @@ from subprocess import *
######################################################################
# CONSTANTS
######################################################################
# defualt disl_home value, relative to the script
default_disl_home = "../"
# default disl_home value, relative to the script
DEFAULT_DISL_HOME = "../../"
# string to be substituted by the actual value of DISL_HOME in paths
variable_disl_home = "${DISL_HOME}"
VARIABLE_DISL_HOME = "${DISL_HOME}"
######################################################################
......@@ -25,7 +25,7 @@ variable_disl_home = "${DISL_HOME}"
######################################################################
def disl_home():
if os.getenv("DISL_HOME") is None:
return default_disl_home
return DEFAULT_DISL_HOME
else:
return os.getenv("DISL_HOME")
......@@ -93,17 +93,17 @@ def client_parser(parser):
if platform.system() == "Darwin":
group.add_argument("-c_cagent",
default=variable_disl_home+"/build/libdislagent.jnilib",
default=VARIABLE_DISL_HOME+"/build/libdislagent.jnilib",
metavar="PATH",
help="path to c-agent library")
else:
group.add_argument("-c_cagent",
default=variable_disl_home+"/build/libdislagent.so",
default=VARIABLE_DISL_HOME+"/build/libdislagent.so",
metavar="PATH",
help="path to c-agent library")
group.add_argument("-c_jagent",
default=variable_disl_home+"/build/disl-agent.jar",
default=VARIABLE_DISL_HOME+"/build/disl-agent.jar",
metavar="PATH",
help="path to java-agent library")
......@@ -150,7 +150,7 @@ def server_parser(parser):
help="java options of the server")
group.add_argument('-s_jar',
default=variable_disl_home+"/build/disl-server.jar",
default=VARIABLE_DISL_HOME+"/build/disl-server.jar",
metavar="PATH",
help="path to disl server jar")
......@@ -294,14 +294,14 @@ def parse_arguments(parser):
args = parser.parse_args()
# substite ${DISL_HOME}
if args.c_cagent.startswith(variable_disl_home):
args.c_cagent = args.c_cagent.replace(variable_disl_home, args.disl_home)
if args.c_cagent.startswith(VARIABLE_DISL_HOME):
args.c_cagent = args.c_cagent.replace(VARIABLE_DISL_HOME, args.disl_home)
if args.c_jagent.startswith(variable_disl_home):
args.c_jagent = args.c_jagent.replace(variable_disl_home, args.disl_home)
if args.c_jagent.startswith(VARIABLE_DISL_HOME):
args.c_jagent = args.c_jagent.replace(VARIABLE_DISL_HOME, args.disl_home)
if args.s_jar.startswith(variable_disl_home):
args.s_jar = args.s_jar.replace(variable_disl_home, args.disl_home)
if args.s_jar.startswith(VARIABLE_DISL_HOME):
args.s_jar = args.s_jar.replace(VARIABLE_DISL_HOME, args.disl_home)
args.c_opts = flatten_all(args.c_opts)
args.c_app = flatten_all(args.c_app)
......
<project name="example-app" default="package" basedir=".">
<path id="buildpath">
<pathelement location="${bin}" />
</path>
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
<classpath refid="buildpath" />
</javac>
</target>
......
public class Main {
public String name = "app-name";
public void testInstance() {
System.out.println("app: Main.testInstance()");
}
@Override
public String toString() {
return name;
}
public static int testMul(int x, int y) {
System.out.println("app: Main.testMul()");
int a = x;
int b = y;
int mul = a * b;
System.out.println("app: a=" + a);
System.out.println("app: b=" + b);
System.out.println("app: mul=" + mul);
return mul;
}
public static void testException() {
System.out.println("app: Main.testException()");
throw new RuntimeException("app-exception");
}
public static void main(String[] args) {
testMul(3, 5);
try {
testException();
} catch (Throwable e) {
}
Main m = new Main();
m.testInstance();
}
}
\ No newline at end of file
<project name="disl-example" default="prepare-all" basedir=".">
<target name="prepare-all">
<ant antfile="app/build.xml" target="package" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="package" useNativeBasedir="true" />
</target>
<target name="clean">
<ant antfile="app/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="clean" useNativeBasedir="true" />
</target>
<target name="run" depends="prepare-all">
<exec executable="../disl.py">
<arg value="--" />
<arg value="instr/build/disl-instr.jar" />
<arg value="-jar" />
<arg value="app/build/example-app.jar" />
</exec>
</target>
</project>
<project name="example-disl" default="package" basedir=".">
<property file="build.properties" />
<property file="../../build.properties" />
<path id="buildpath">
<pathelement location="${asm.path}" />
<pathelement location="${dislserver.path}" />
<pathelement location="${bin}" />
</path>
<target name="compile">
......
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.dynamiccontext.DynamicContext;
import ch.usi.dag.disl.marker.BodyMarker;
public class DiSLClass {
@After(marker = BodyMarker.class, scope = "Main.testMul")
public static void afterMul(DynamicContext di) {
int a = di.getLocalVariableValue(0, int.class);
int b = di.getLocalVariableValue(1, int.class);
System.out.println("disl: a=" + a);
System.out.println("disl: b=" + b);
}
@After(marker = BodyMarker.class, scope = "Main.testInstance")
public static void afterInstance(DynamicContext di) {
Object o = di.getThis();
System.out.println("disl: this=" + o.toString());
}
@After(marker = BodyMarker.class, scope = "Main.testException")
public static void afterException(DynamicContext di) {
Throwable o = di.getException();
System.out.println("disl: exception=" + o.getMessage());
}
}
<project name="example-app" default="package" basedir=".">
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
</javac>
</target>
<target name="package" depends="compile">
<mkdir dir="build" />
<jar basedir="bin" destfile="build/example-app.jar">
<manifest>
<attribute name="Main-Class" value="Main" />
</manifest>
</jar>
</target>
<target name="clean">
<delete dir="bin" />
<delete dir="build" />
</target>
</project>
public class Main {
public static void test1() {
System.out.println("app: Main.test1()");
}
public static void test2() {
System.out.println("app: Main.test2()");
}
public static void test3() {
System.out.println("app: Main.test3()");
}
public static void main(String[] args) {
test1();
test2();
test3();
}
}
<project name="disl-example" default="prepare-all" basedir=".">
<target name="prepare-all">
<ant antfile="app/build.xml" target="package" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="package" useNativeBasedir="true" />
</target>
<target name="clean">
<ant antfile="app/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="clean" useNativeBasedir="true" />
</target>
<target name="run" depends="prepare-all">
<exec executable="../disl.py">
<arg value="--" />
<arg value="instr/build/disl-instr.jar" />
<arg value="-jar" />
<arg value="app/build/example-app.jar" />
</exec>
</target>
</project>
<project name="example-disl" default="package" basedir=".">
<property file="../../build.properties" />
<path id="buildpath">
<pathelement location="${asm.path}" />
<pathelement location="${dislserver.path}" />
</path>
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
<classpath refid="buildpath" />
</javac>
</target>
<target name="package" depends="compile" description="create instrumentation package">
<mkdir dir="build"/>
<jar jarfile="build/${instr.jar.name}"
basedir="bin"
excludes="MANIFEST.MF"
manifest="src/MANIFEST.MF">
</jar>
</target>
<target name="clean">
<delete dir="bin" />
<delete dir="build" />
</target>
</project>
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.AfterReturning;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.marker.MethodInvocationMarker;
public class DiSLClass {
/**
* <p>
* This is added before every method call in Main.main method. And is always
* used thanks to guard.
*/
@Before(marker = MethodInvocationMarker.class, scope = "Main.main", guard = GuardYes.class)
public static void beforeInvocation() {
System.out.println("disl: before invocation");
}
/**
* <p>
* This snippet is never invoked due to guard.
*/
@After(marker = MethodInvocationMarker.class, scope = "Main.main", guard = GuardNo.class)
public static void afterInvocation() {
System.out.println("disl: after invocation");
}
}
import ch.usi.dag.disl.annotation.GuardMethod;
public abstract class GuardNo {
@GuardMethod
public static boolean isApplicable() {
return false;
}
}
import ch.usi.dag.disl.annotation.GuardMethod;
public abstract class GuardYes {
@GuardMethod
public static boolean isApplicable() {
return true;
}
}
\ No newline at end of file
Manifest-Version: 1.0
DiSL-Classes: DiSLClass
<project name="example-app" default="package" basedir=".">
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
</javac>
</target>
<target name="package" depends="compile">
<mkdir dir="build" />
<jar basedir="bin" destfile="build/example-app.jar">
<manifest>
<attribute name="Main-Class" value="Main" />
</manifest>
</jar>
</target>
<target name="clean">
<delete dir="bin" />
<delete dir="build" />
</target>
</project>
public class Main {
public static void test1() {
System.out.println("app: Main.test1()");
}
public static void test2() {
System.out.println("app: Main.test2()");
}
public static void test3() {
System.out.println("app: Main.test3()");
}
public static void testThrow() {
System.out.println("app: Main.testThrow()");
throw new RuntimeException();
}
public static void testNoThrow() {
System.out.println("app: Main.testNoThrow()");
}
public static void main(String[] args) {
test1();
test2();
test3();
try {
testThrow();
} catch (Throwable e) {
}
testNoThrow();
}
}
<project name="disl-example" default="prepare-all" basedir=".">
<target name="prepare-all">
<ant antfile="app/build.xml" target="package" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="package" useNativeBasedir="true" />
</target>
<target name="clean">
<ant antfile="app/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="clean" useNativeBasedir="true" />
</target>
<target name="run" depends="prepare-all">
<exec executable="../disl.py">
<arg value="--" />
<arg value="instr/build/disl-instr.jar" />
<arg value="-jar" />
<arg value="app/build/example-app.jar" />
</exec>
</target>
</project>
<project name="example-disl" default="package" basedir=".">
<property file="../../build.properties" />
<path id="buildpath">
<pathelement location="${asm.path}" />
<pathelement location="${dislserver.path}" />
</path>
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
<classpath refid="buildpath" />
</javac>
</target>
<target name="package" depends="compile" description="create instrumentation package">
<mkdir dir="build"/>
<jar jarfile="build/${instr.jar.name}"
basedir="bin"
excludes="MANIFEST.MF"
manifest="src/MANIFEST.MF">
</jar>
</target>
<target name="clean">
<delete dir="bin" />
<delete dir="build" />
</target>
</project>
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.AfterThrowing;
import ch.usi.dag.disl.annotation.AfterReturning;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.marker.BodyMarker;
/**
* <p>
* This example shows how to insert snippets at various regions and entries or exits of these regions.
*
* <p>
* It also shows how to implements custom code marker.
*/
public class DiSLClass {
/**
* <p>
* This is added before every method call in Main.main method.
*/
@Before(marker = MethodInvocationMarker.class, scope = "Main.main")
public static void beforeInvocation() {
System.out.println("disl: before invocation");
}
/**
* <p>
* This is added after every method call in Main.main method.
*/
@After(marker = MethodInvocationMarker.class, scope = "Main.main")
public static void afterInvocation() {
System.out.println("disl: after invocation");
}
/**
* <p>
* This is added after every method matching Main.test* no matter how it
* ends (throw or no throw).
*/
@After(marker = BodyMarker.class, scope = "Main.test*")
public static void afterMethod() {
System.out.println("disl: after method Main.test*");
}
/**
* <p>
* This is added after every method matching Main.test* that exists via
* throwing exception.
*/
@AfterThrowing(marker = BodyMarker.class, scope = "Main.test*")
public static void afterThrowing() {
System.out.println("disl: after throwing Main.test*");
}
/**
* <p>
* This is added after every method matching Main.test* that returns
* normally.
*/
@AfterReturning(marker = BodyMarker.class, scope = "Main.test*")
public static void afterReturning() {
System.out.println("disl: after returning Main.test*");
}
}
Manifest-Version: 1.0
DiSL-Classes: DiSLClass
\ No newline at end of file
import java.util.LinkedList;
import java.util.List;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;