Commit 4a8e0f1f authored by Lubomir Bulej's avatar Lubomir Bulej

- Separated the build tools from DiSL sources.

- Added an Ant task to create a formatted dump of binary file.
- The Ant build system now generates the files needed by the native agents.
- The DiSL agent build system now expects bytecode.c to be present.
parent 9add01a7
......@@ -22,6 +22,7 @@ src.shvm.agent=src-shvm-agent
src.shvm.dispatch=src-shvm-dispatch
src.test=src-test
src.util=src-util
src.tools=src-tools
src.doc=doc
src.doc.jdoc=${src.doc}/javadoc
src.doc.intro=${src.doc}/intro
......@@ -32,9 +33,10 @@ src.examples=examples
out.dir=output
# Directories for building artifacts
# Directories for build artifacts
build.dir=${out.dir}/build
build.util=${build.dir}/util
build.tools=${build.dir}/tools
build.disl=${build.dir}/disl
build.disl.thread=${build.disl}-thread
build.disl.bypass=${build.disl}-bypass
......
......@@ -27,7 +27,6 @@
<pathelement location="${junit.hamcrest.path}" />
</path>
<path id="util.classpath">
<pathelement location="${build.util}" />
</path>
......@@ -104,6 +103,19 @@
</macrodef>
<!-- Build tools -->
<target name="compile-tools">
<mkdir dir="${build.tools}" />
<javac destdir="${build.tools}" srcdir="${src.tools}">
<classpath>
<path refid="asm.classpath" />
<path location="${ant.core.lib}" />
</classpath>
</javac>
</target>
<!-- DiSL bypass -->
<!--
......@@ -114,33 +126,25 @@
one also for compilation.
Here we first check if the extended Thread class exists, and
if not, we compile a simple Thread class extender and use it
to generate the extended Thread. The check is inside a target
so that it is performed only before actually attempting to
create the extended Thread.
if not, we use a thread extended from build tools to generate
the extended Thread class.
-->
<target name="check-disl-thread">
<pathconvert property="disl.thread.available" setonempty="false">
<fileset dir="." includes="${build.disl.thread}/**/Thread.class" />
</pathconvert>
</target>
<pathconvert property="disl.thread.available" setonempty="false">
<fileset dir="." includes="${build.disl.thread}/**/Thread.class" />
</pathconvert>
<target name="create-disl-thread" depends="check-disl-thread" unless="disl.thread.available">
<!-- Compile Thread extender -->
<mkdir dir="${build.disl}" />
<javac destdir="${build.disl}" srcdir="${src.disl}" debug="yes">
<include name="**/ExtendThread.java" />
<classpath refid="asm.classpath" />
</javac>
<target name="prepare-disl-thread" unless="disl.thread.available">
<antcall target="create-disl-thread" />
</target>
<!-- Generated extended Thread -->
<target name="create-disl-thread" depends="compile-tools">
<local name="extender.class" />
<find-class property="extender.class" dir="${build.disl}" name="ExtendThread" />
<find-class property="extender.class" dir="${build.tools}" name="ExtendThread" />
<mkdir dir="${build.disl.thread}" />
<java classname="${extender.class}" fork="true">
<classpath>
<path refid="disl.classpath" />
<path location="${build.tools}" />
<path refid="asm.classpath" />
</classpath>
<arg value="${basedir}/${build.disl.thread}" />
......@@ -162,7 +166,7 @@
The DiSL server requires both classes to create class literals
in the weaver code.
-->
<target name="compile-disl-bypass" depends="create-disl-thread">
<target name="compile-disl-bypass" depends="prepare-disl-thread">
<for list="always,never,dynamic" param="variant">
<sequential>
<mkdir dir="${build.disl.bypass}/@{variant}" />
......@@ -264,44 +268,72 @@
<!--
The DiSL agent requires the bytecode of several bypass
classes. The agent build system will convert the bytecode to
a C source file, but it needs to know where to look for the
class files. This target generates "Makefile.bypass" which
provides the C build system with the necessary information.
classes. This target generates the "bytecode.c" containing
the necessary bytecode.
-->
<property name="disl.agent.makefile.path" value="${src.disl.agent}/Makefile.bypass" />
<uptodate property="disl.agent.makefile.uptodate" targetfile="${disl.agent.makefile.path}">
<property name="disl.agent.bytecode.path" value="${src.disl.agent}/bytecode.c" />
<uptodate property="disl.agent.bytecode.uptodate" targetfile="${disl.agent.bytecode.path}">
<srcfiles dir="${src.disl.bypass}" />
</uptodate>
<target name="prepare-disl-agent-makefile" unless="disl.agent.makefile.uptodate">
<antcall target="generate-disl-agent-makefile" />
<target name="prepare-disl-agent-bytecode" unless="disl.agent.bytecode.uptodate">
<antcall target="generate-disl-agent-bytecode" />
</target>
<target name="generate-disl-agent-makefile" depends="compile-disl">
<echo file="${disl.agent.makefile.path}"># Generated using the "prepare-disl-agent-makefile" Ant target.${line.separator}</echo>
<echo file="${disl.agent.makefile.path}" append="true">BYPASS_DIR = ../${build.disl.bypass}${line.separator}</echo>
<echo file="${disl.agent.makefile.path}" append="true">BYPASS_PKG_PATH = ch/usi/dag/disl/dynamicbypass${line.separator}</echo>
<target name="generate-disl-agent-bytecode" depends="compile-tools,compile-disl">
<taskdef name="bytedump" classname="ch.usi.dag.disl.tools.ByteDumpTask">
<classpath>
<pathelement location="${build.tools}" />
</classpath>
</taskdef>
<echo file="${disl.agent.bytecode.path}">/**${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true"> * DO NOT EDIT!${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true"> *${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true"> * This file was generated using the 'generate-disl-agent-bytecode' target.${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true"> */${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true">#include &lt;jvmti.h&gt;${line.separator}</echo>
<for param="path">
<for param="file.path">
<path>
<fileset dir="${build.disl.bypass}" includes="**/BypassCheck.class" />
<!-- fileset dir="${build.disl.bypass}" includes="**/DynamicBypass.class" /-->
</path>
<sequential>
<local name="path.relative" />
<pathconvert property="path.relative">
<path path="@{path}" />
<mapper type="glob" from="${basedir}/*" to="../*" />
<!-- Variant of the class, i.e., the top-level directory name. -->
<local name="file.variant" />
<pathconvert property="file.variant">
<path path="@{file.path}" />
<mapper type="regexp" from="${basedir}/${build.disl.bypass}/([^/]+)/.*" to="\1" />
</pathconvert>
<echo file="${disl.agent.makefile.path}" append="true">BYPASS_FILES += ${path.relative}${line.separator}</echo>
<!-- Name of the class. -->
<local name="file.class" />
<pathconvert property="file.class">
<path path="@{file.path}" />
<mapper type="regexp" from=".*/([^/]+).class" to="\1" />
</pathconvert>
<local name="file.length" />
<length property="file.length" file="@{file.path}" />
<!-- Define a structure for each class file. -->
<echo file="${disl.agent.bytecode.path}" append="true">${line.separator}jvmtiClassDefinition ${file.variant}_${file.class}_classdef = {${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true">&#9;.class_byte_count = ${file.length},${line.separator}</echo>
<echo file="${disl.agent.bytecode.path}" append="true">&#9;.class_bytes = (unsigned char *)${line.separator}</echo>
<bytedump
srcFile="@{file.path}" destFile="${disl.agent.bytecode.path}" append="true"
blockLength="16" byteFormat="\x%02x"
blockPrefix="&#9;&#9;&quot;" blockSuffix="&quot;${line.separator}" lastSuffix="&quot;,${line.separator}"
/>
<echo file="${disl.agent.bytecode.path}" append="true">};${line.separator}</echo>
</sequential>
</for>
</target>
<target name="prepare-disl-agent" depends="prepare-disl-agent-codeflags,prepare-disl-agent-makefile" />
<target name="prepare-disl-agent" depends="prepare-disl-agent-codeflags,prepare-disl-agent-bytecode" />
<target name="compile-disl-agent" depends="prepare-disl-agent,determine-lib-names">
......@@ -556,7 +588,7 @@
<local name="pdflatex.output" />
<local name="pdflatex.result" />
<exec executable="pdflatex" dir="@{dir}" outputproperty="pdflatex.output" resultproperty="pdflatex.result">
<exec executable="pdflatex" dir="@{dir}" outputProperty="pdflatex.output" resultProperty="pdflatex.result">
<exec-elements />
</exec>
<fail message="${pdflatex.output}">
......@@ -571,7 +603,7 @@
<property name="doc.intro.pdf" value="${build.doc.intro}/dislintro.pdf" />
<fileset id="doc.intro.files" dir="${src.doc.intro}" />
<uptodate property="doc.intro.uptodate" targetfile="${doc.intro.pdf}">
<uptodate property="doc.intro.uptodate" targetFile="${doc.intro.pdf}">
<srcfiles refid="doc.intro.files" />
</uptodate>
</target>
......@@ -599,8 +631,8 @@
<mkdir dir="${out.doc.jdoc}" />
<javadoc destdir="${out.doc.jdoc}"
access="public" author="true" version="true" use="true"
nodeprecated="false" nodeprecatedlist="false"
noindex="false" splitindex="true" nonavbar="false" notree="false"
noDeprecated="false" nodeprecatedlist="false"
noIndex="false" splitIndex="true" noNavbar="false" noTree="false"
overview="${src.doc.jdoc}/overview.html"
source="1.7"
>
......@@ -660,7 +692,7 @@
<target name="release-bin" depends="build,package-src">
<!-- Collect the artifacts from output and source directories. -->
<zip destfile="${release.bin.zip}">
<zipfileset prefix="${disl.prefix}/${dist.bin}" dir="${src.bin}" filemode="755" />
<zipfileset prefix="${disl.prefix}/${dist.bin}" dir="${src.bin}" fileMode="755" />
<zipfileset prefix="${disl.prefix}/${dist.lib}" dir="${out.lib}">
<include name="${asm.lib}" />
<include name="${disl.lib}" />
......
......@@ -62,7 +62,7 @@ SOURCES = bytecode.c common.c jvmtiutil.c connection.c \
connpool.c msgchannel.c network.c dislagent.c
HEADERS = $(wildcard *.h) codeflags.h
GENSRCS = Makefile.bypass bytecode.c codeflags.h
GENSRCS = bytecode.c codeflags.h
OBJECTS = $(SOURCES:%.c=%.o)
SRCDEPS = $(SOURCES:%.c=%.d)
......@@ -117,17 +117,8 @@ debug:
# Generated files
Makefile.bypass:
ant -f ../build.xml prepare-disl-agent-makefile
include Makefile.bypass
bytecode.c: Makefile.bypass bin2cdef $(BYPASS_FILES)
./bin2cdef \
$(foreach BPC,$(BYPASS_FILES),$(BPC) $(subst /$(BYPASS_PKG_PATH)/,_,$(subst $(BYPASS_DIR)/,,$(BPC:.class=)))) \
> $@
bytecode.c:
ant -f ../build.xml prepare-disl-agent-bytecode
codeflags.h:
ant -f ../build.xml prepare-disl-agent-codeflags
......
# Generated using the "prepare-disl-agent-makefile" Ant target.
BYPASS_DIR = ../output/build/disl-bypass
BYPASS_PKG_PATH = ch/usi/dag/disl/dynamicbypass
BYPASS_FILES += ../output/build/disl-bypass/always/ch/usi/dag/disl/dynamicbypass/BypassCheck.class
BYPASS_FILES += ../output/build/disl-bypass/dynamic/ch/usi/dag/disl/dynamicbypass/BypassCheck.class
BYPASS_FILES += ../output/build/disl-bypass/never/ch/usi/dag/disl/dynamicbypass/BypassCheck.class
#!/bin/bash
cat <<- EOT
/**
* DO NOT EDIT!
*
* This file was generated using the bin2cdef script.
*/
#include <jvmti.h>
EOT
while [ $# -gt 1 ]; do
FILE=$1; shift
NAME=$1; shift
cat << EOT
jvmtiClassDefinition ${NAME}_classdef = {
.class_byte_count = $(ls -l ${FILE} | awk '{print $5}'),
.class_bytes = (unsigned char *)
$(hexdump -e '"\t\t" "Q" 16/1 "xxx%02x" "Q" "\n"' ${FILE} |sed 's/xxx //g; y/Q/"/; s/xxx/\\x/g'),
};
EOT
done
/**
* DO NOT EDIT!
*
* This file was generated using the bin2cdef script.
* This file was generated using the 'generate-disl-agent-bytecode' target.
*/
#include <jvmti.h>
jvmtiClassDefinition always_BypassCheck_classdef = {
.class_byte_count = 388,
.class_bytes = (unsigned char *)
......@@ -36,7 +35,6 @@ jvmtiClassDefinition always_BypassCheck_classdef = {
"\x00\x02\x00\x0e",
};
jvmtiClassDefinition dynamic_BypassCheck_classdef = {
.class_byte_count = 460,
.class_bytes = (unsigned char *)
......@@ -71,7 +69,6 @@ jvmtiClassDefinition dynamic_BypassCheck_classdef = {
"\x00\x0b\x00\x01\x00\x0e\x00\x00\x00\x02\x00\x0f",
};
jvmtiClassDefinition never_BypassCheck_classdef = {
.class_byte_count = 388,
.class_bytes = (unsigned char *)
......
package ch.usi.dag.disl.tools;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.IllegalFormatException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
public final class ByteDumpTask extends Task {
private File __srcFile;
private File __destFile;
private int __blockLength = 16;
private String __blockPrefix = "";
private String __blockSuffix = "\n";
private String __lastPrefix;
private String __lastSuffix;
private String __byteFormat = "%02x ";
private String __text;
private boolean __append = false;
//
public void setSrcFile (final File file) {
if (!file.canRead ()) {
throw new BuildException (
"file does not exist or is not readable: "+ file,
getLocation ()
);
}
__srcFile = file;
}
public void setDestFile (final File file) {
__destFile = file;
}
public void setBlockLength (final int blockLength) {
if (blockLength < 1) {
throw new BuildException (
"Property 'blockLength' must be greater than zero",
getLocation ()
);
}
__blockLength = blockLength;
}
public void setBlockPrefix (final String blockPrefix) {
__blockPrefix = blockPrefix;
}
public void setBlockSuffix (final String blockSuffix) {
__blockSuffix = blockSuffix;
}
public void setLastPrefix (final String lastPrefix) {
__lastPrefix = lastPrefix;
}
public void setLastSuffix (final String lastSuffix) {
__lastSuffix = lastSuffix;
}
public void setByteFormat (final String byteFormat) {
try {
String.format (byteFormat, 0);
} catch (final IllegalFormatException e) {
throw new BuildException (
"illegal byte format: "+ byteFormat, getLocation ()
);
}
__byteFormat = byteFormat;
}
public void setAppend (final boolean append) {
__append = append;
}
public void addText (final String rawText) {
__text = getProject ().replaceProperties (rawText);
}
//
@Override
public void execute () {
//
// Set defaults for last block prefix and suffix.
//
if (__lastPrefix == null) {
__lastPrefix = __blockPrefix;
}
if (__lastSuffix == null) {
__lastSuffix = __blockSuffix;
}
//
// Load the input and dump it to the destination file.
//
final ByteBuffer input = ByteBuffer.wrap (__loadInput ());
final PrintStream output = __createOutput (__destFile, __append);
log (String.format (
"Dumping %d bytes, %d per block",
input.capacity (), __blockLength
), Project.MSG_VERBOSE);
__printBytes (input, output);
}
private byte [] __loadInput () {
if (__text != null) {
log ("Using embedded text as input", Project.MSG_VERBOSE);
return __text.getBytes (Charset.forName ("UTF-8"));
} else if (__srcFile != null) {
log ("Loading "+ __srcFile, Project.MSG_VERBOSE);
return __loadFile (__srcFile);
} else {
throw new BuildException ("Missing input: neither 'srcFile' nor embedded text specified", getLocation ());
}
}
private static byte [] __loadFile (final File file) {
try {
final FileInputStream fis = new FileInputStream (file);
try {
return __copyStream (
fis, new ByteArrayOutputStream ()
).toByteArray ();
} finally {
fis.close ();
}
} catch (final IOException ioe) {
throw new BuildException (String.format (
"error reading %s: %s", file.toString (), ioe.getMessage ()
));
}
}
private PrintStream __createOutput (final File file, final boolean append) {
if (file == null) {
log ("Using standard output as destination", Project.MSG_VERBOSE);
return System.out;
} else {
try {
log ("Output file: "+ file.toString (), Project.MSG_VERBOSE);
return new PrintStream (new FileOutputStream (file, append));
} catch (final FileNotFoundException e) {
throw new BuildException (
"cannot create output file: "+ file.toString (),
getLocation ()
);
}
}
}
private void __printBytes (final ByteBuffer bytes, final PrintStream output) {
__formatBytes (bytes, output);
output.flush ();
if (output != System.out) {
output.close ();
}
}
private void __formatBytes (final ByteBuffer buffer, final PrintStream out) {
while (buffer.hasRemaining ()) {
final int lineLength = Math.min (buffer.remaining (), __blockLength);
out.print (buffer.remaining () > lineLength ? __blockPrefix : __lastPrefix);
for (int i = 0; i < lineLength; i++) {
out.printf (__byteFormat, buffer.get ());
}
out.print (buffer.hasRemaining () ? __blockSuffix : __lastSuffix);
}
}
private static <T extends OutputStream> T __copyStream (
final InputStream input, final T output
) throws IOException {
final byte [] buffer = new byte [4096];
COPY: while (true) {
final int bytesRead = input.read (buffer);
if (bytesRead < 0) {
break COPY;
}
output.write (buffer, 0, bytesRead);
}
return output;
}
}
package ch.usi.dag.disl.utilinstr.tlvinserter;
package ch.usi.dag.disl.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import ch.usi.dag.disl.localvar.ThreadLocalVar;
import org.objectweb.asm.commons.AdviceAdapter;
/**
......@@ -22,16 +20,107 @@ import ch.usi.dag.disl.localvar.ThreadLocalVar;
*/
public final class ExtendThread {
public static void main (final String ... args) throws Exception {
private static abstract class FieldDefinition {
private final int __access;
private final String __name;
private final Class <?> __type;
FieldDefinition (
final int access, final String name, final Class <?> type
) {
__access = access;
__name = name;
__type = type;
}
int access () {
return __access;
}
String name () {
return __name;
}
String desc () {
return Type.getDescriptor (__type);
}
Type type () {
return Type.getType (__type);
}
protected abstract void initialize (Type owner, AdviceAdapter mv);
}
private static final class FieldExtender extends ClassVisitor {
private final FieldDefinition __fd;
private Type __owner;
public FieldExtender (final FieldDefinition fd, final ClassWriter cw) {
super (Opcodes.ASM5, cw);
__fd = fd;
}
@Override
public void visit (
final int version, final int access, final String name,
final String signature, final String superName,
final String [] interfaces
) {
super.visit (version, access, name, signature, superName, interfaces);
__owner = Type.getObjectType (name);
};
@Override
public MethodVisitor visitMethod (
final int access, final String name, final String desc,
final String signature, final String [] exceptions
) {
final MethodVisitor mv = super.visitMethod (
access, name, desc, signature, exceptions
);
if ("<init>".equals (name)) {
// Initialize the field.
return new AdviceAdapter (Opcodes.ASM5, mv, access, name, desc) {
@Override
protected void onMethodEnter () {
__fd.initialize (__owner, this);
}
};
} else {
return mv;
}
};
@Override
public void visitEnd () {
// Create the field.
super.visitField (
__fd.access (), __fd.name (), __fd.desc (),
null, null
);
super.visitEnd ();
}
}
public static void main (final String... args) throws Exception {
if (args.length < 1) {
System.err.println ("usage: ExtendThread <output-directory>");
System.exit (1);
}
final File outputDir = new File (args [0]);
final File outputDir = new File (args [0]);
if (!outputDir.isDirectory ()) {
System.err.printf ("error: %s does not exist or is not a directory!\n", outputDir);
System.err.printf (
"error: %s does not exist or is not a directory!\n", outputDir);
System.exit (1);
}
......@@ -39,28 +128,29 @@ public final class ExtendThread {
// Define a thread-local non-inheritable boolean variable named
// "bypass", with a default value of false,
//
final ThreadLocalVar tlBypass = new ThreadLocalVar (
null, "bypass", Type.getType (boolean.class), false
);
tlBypass.setDefaultValue (0);
final FieldDefinition bypassField = new FieldDefinition (
Opcodes.ACC_PUBLIC, "bypass", boolean.class) {
@Override
protected void initialize (final Type owner, final AdviceAdapter mv) {
mv.loadThis ();
mv.push (false);
mv.putField (owner, name (), type ());
}
};
//
// Extend Thread with a "bypass" variable and dump the new Thread
// bytecode into given directory.
// Extend Thread and dump the new bytecode into the given directory.
//
__writeThread (outputDir, __extendThread (tlBypass));
final byte [] extendedThread = __extendThread (bypassField);
__writeThread (outputDir, extendedThread);
}
private static byte [] __extendThread (
final ThreadLocalVar ... tlvs
) throws IOException {
final InputStream is = Thread.class.getResourceAsStream ("Thread.class");
final ClassReader cr = new ClassReader (is);
private static byte [] __extendThread (final FieldDefinition fd)
throws IOException {
final ClassReader cr = new ClassReader (Thread.class.getName ());
final ClassWriter cw = new ClassWriter (cr, 0);
final Set <ThreadLocalVar> vars = new HashSet <ThreadLocalVar> (Arrays.asList (tlvs));
cr.accept (new TLVInserter (cw, vars), 0);
cr.accept (new FieldExtender (fd, cw), 0);
return cw.toByteArray ();
}
......
Markdown is supported
0% or