...
 
Commits (9)
......@@ -26,15 +26,7 @@
<classpathentry kind="src" path="disl/src-test"/>
<classpathentry kind="src" path="shvm/src/src-dislreserver"/>
<classpathentry kind="src" path="shvm/src/src-msg-analyze"/>
<classpathentry kind="src" path="shvm/src/src-msg-close"/>
<classpathentry kind="src" path="shvm/src/src-msg-classinfo"/>
<classpathentry kind="src" path="shvm/src/src-msg-newclass"/>
<classpathentry kind="src" path="shvm/src/src-msg-objfree"/>
<classpathentry kind="src" path="shvm/src/src-msg-reganalysis"/>
<classpathentry kind="src" path="shvm/src/src-msg-instr"/>
<classpathentry kind="src" path="shvm/src/src-msg-stringinfo"/>
<classpathentry kind="src" path="shvm/src/src-msg-threadend"/>
<classpathentry kind="src" path="shvm/src/src-msg-threadinfo"/>
<classpathentry kind="src" path="shvm/src/src-msg-analyze-mtdispatch"/>
<classpathentry kind="src" path="shvm/src/src-remoteanalysis"/>
<classpathentry kind="src" path="shvm/src/src-reqdispatch"/>
......@@ -42,6 +34,7 @@
<classpathentry kind="src" path="shvm/src/src-util"/>
<classpathentry kind="src" path="shvm/src/src-server-api"/>
<classpathentry kind="src" path="shvm/src-test"/>
<classpathentry kind="src" path="shvm/src/src-proto"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="output/lib/disl-bypass.jar"/>
<classpathentry kind="lib" path="lib/disl/log4j.jar" sourcepath="lib/devel/log4j-source.jar"/>
......
......@@ -55,15 +55,7 @@
<src path="src/src-dislreserver" />
<src path="src/src-msg-analyze" />
<src path="src/src-msg-analyze-mtdispatch" />
<src path="src/src-msg-classinfo" />
<src path="src/src-msg-close" />
<src path="src/src-msg-instr" />
<src path="src/src-msg-newclass" />
<src path="src/src-msg-objfree" />
<src path="src/src-msg-reganalysis" />
<src path="src/src-msg-stringinfo" />
<src path="src/src-msg-threadend" />
<src path="src/src-msg-threadinfo" />
<src path="src/src-proto" />
<src path="src/src-remoteanalysis" />
<src path="src/src-reqdispatch" />
<src path="src/src-server-api" />
......
/**
* This file is part of disl project
* Author: Vit Kabele <vit@kabele.me>
* Created on the 04/03/2019
*/
package ch.usi.dag.dislreserver;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import static org.junit.Assert.*;
public class ByteArrayUtilsTest {
/**
* Very basic test with the simplest data
*/
@Test
public void ReadLongOne__Test()
{
byte[] byteArray = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x01};
long l = ByteArrayUtils.ReadLong (byteArray, 0);
assertEquals (1, l);
}
/**
* Test against some random number
*/
@Test
public void ReadLongRandom__Test()
{
byte[] byteArray = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x59};
long l = ByteArrayUtils.ReadLong (byteArray, 0);
assertEquals (16473, l);
}
/**
* Test the result against the result of the function which behaviour we try to simulate.
*
* @throws IOException
*/
@Test
public void ReadLongAgainstStream__Test() throws IOException
{
byte[] byteArray = { 0x0a, 0x75, 0x0, 0x11, 0x0, 0x75, 0x40, 0x59};
long l = ByteArrayUtils.ReadLong (byteArray, 0);
DataInputStream dataInputStream = new DataInputStream (
new ByteArrayInputStream (
byteArray
)
);
assertEquals (dataInputStream.readLong (), l);
}
}
package ch.usi.dag.dislreserver;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.StandardSocketOptions;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import ch.usi.dag.util.logging.Logger;
......@@ -45,9 +46,30 @@ public abstract class DiSLREServer {
__log.debug ("server started");
__serverStarted ();
ISHVM server = new SHVM (socket, __log, debug);
SHVM server = new SHVMImpl (__log);
try {
final SocketChannel clientSocket = socket.accept ();
__log.debug (
"connection from %s", clientSocket.getRemoteAddress ()
);
processRequests (clientSocket.socket (), server);
clientSocket.close ();
} catch (final ClosedByInterruptException cbie) {
//
// The server was interrupted, we are shutting down.
//
} catch (final IOException ioe) {
//
// Communication error -- just log a message here.
//
__log.error ("error accepting a connection: %s", ioe.getMessage ());
}
server.run ();
__log.debug ("server shutting down");
__closeSocket (socket);
......@@ -57,6 +79,43 @@ public abstract class DiSLREServer {
}
/**
* Parse the input stream to protocol buffers messages and pass them to SHVM model, via request
* dispatcher.
*
* @param sock
* @param server
*/
private static void processRequests (final Socket sock, final SHVM server)
{
try {
final DataInputStream is = new DataInputStream (
new BufferedInputStream (sock.getInputStream ()));
Protocol.AgentMessage agentMessage;
int messageSize;
byte[] byteArray;
while (true) {
try {
messageSize = is.readInt ();
} catch (EOFException e){
break;
}
byteArray = new byte[messageSize];
is.readFully (byteArray,0, messageSize);
agentMessage = Protocol.AgentMessage.parseFrom (byteArray);
RequestDispatcher.ProcessMessage (server, agentMessage, __log);
}
} catch (final Exception e) {
Log.__logError (e, __log);
}
}
//
......
package ch.usi.dag.dislreserver;
import ch.usi.dag.dislreserver.Protocol.*;
import ch.usi.dag.util.logging.Logger;
/**
* Request dispatcher will take the protocol buffer message and pass the data to appropriate method
* call.
*
*/
final class RequestDispatcher {
/**
* Process the agent message, and pass the unpacked data to {@link SHVM} instance.
*
* @param shadowVM ShadowVM instance to process the requests.
* @param agentMessage The agent message.
* @param logger Logger
* @throws DiSLREServerException
*/
static void ProcessMessage(
final SHVM shadowVM, final AgentMessage agentMessage, final Logger logger
) throws DiSLREServerException
{
switch (agentMessage.getRequestCase ())
{
case ANALYZE:
Analyze analyze = agentMessage.getAnalyze ();
shadowVM.HandleAnalyze (
analyze.getOrderingID (),
analyze.getInvocationCount (),
analyze.getRawData ().toByteArray ()
);
break;
case CLOSE:
shadowVM.HandleClose ();
break;
case CLASS_INFO:
ClassInfo classInfo = agentMessage.getClassInfo ();
shadowVM.HandleClassInfo (
classInfo.getClassTag (),
classInfo.getClassSignature (),
classInfo.getClassGeneric (),
classInfo.getLoaderTag (),
classInfo.getSuperClassTag ()
);
break;
case NEW_CLASS:
NewClass newClass = agentMessage.getNewClass ();
shadowVM.HandleNewClass (
newClass.getClassName (),
newClass.getClassLoaderTag (),
newClass.getClassData ().toByteArray ()
);
break;
case THREAD_END:
shadowVM.HandleThreadEnd (agentMessage.getThreadEnd ().getThreadId ());
break;
case REGISTER_ANALYSIS:
RegisterAnalysis registerAnalysis = agentMessage.getRegisterAnalysis ();
shadowVM.HandleRegisterAnalysis (
registerAnalysis.getAnalysisId (),
registerAnalysis.getDescriptor ()
);
break;
case STRING_INFO:
StringInfo stringInfo = agentMessage.getStringInfo ();
shadowVM.HandleStringInfo (
stringInfo.getStringTag (),
stringInfo.getStringData ()
);
break;
case THREAD_INFO:
ThreadInfo threadInfo = agentMessage.getThreadInfo ();
shadowVM.HandleThreadInfo (
threadInfo.getThreadTag (),
threadInfo.getName (),
threadInfo.getIsDaemon ()
);
break;
case OBJECT_FREE:
int count = agentMessage.getObjectFree().getCount ();
long[] ids = new long[count];
byte[] byteArray = agentMessage.getObjectFree ().getRawData ().toByteArray ();
logger.error (
"Serious error in data transfer: Indicated length (%d) does not match real length (%d)",
count, byteArray.length);
// ByteArrayUtils method used here, since it's much more lightweight solution than
// creating an input stream again.
for (int i = 0; i < count; ++i) {
ids[i] = ByteArrayUtils.ReadLong (byteArray, i * Long.BYTES);
}
shadowVM.HandleObjectFree (ids);
break;
case REQUEST_NOT_SET:
default:
logger.error (
"Serious problem in data transfer: Unknown type of message from agent"
);
}
}
}
/**
* ISHVM implementation
*
* Author: Vit Kabele <vit@kabele.me>
* Created on the 21/02/2019
*/
package ch.usi.dag.dislreserver;
import java.io.*;
import java.net.Socket;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import ch.usi.dag.util.logging.Logger;
/**
* Implementation of ISHVM.
*
* This class should represent the one re-usable class that will represent
* i.e. one analysis session. All the needed data including reflective information
* should be available in its instance.
*/
public class SHVM implements ISHVM {
/**
* Socket for communication with client
*/
private final ServerSocketChannel __socket;
/**
* Logger instance
*/
private final Logger __log;
/**
* Debug flag
*/
@Deprecated
private final boolean __debug;
/**
* Request dispatcher
*/
private final RequestDispatcher __requestDispatcher;
/**
* Shadow VM context. It's used in the request dispatcher,
* but since the name is {@link SHVMContext} it does make
* a sense to initialize the instance here.
*/
private final SHVMContext __shvmContext;
/**
* Public constructor
*
* @param socket
* @param logger
* @param debug
*/
SHVM (
final ServerSocketChannel socket,
final Logger logger,
final boolean debug
) {
__socket = socket;
__log = logger;
__debug = debug;
__shvmContext = new SHVMContext (__log);
__requestDispatcher = new RequestDispatcher (__shvmContext);
}
@Override
public void run () {
try {
final SocketChannel clientSocket = __socket.accept ();
__log.debug (
"connection from %s", clientSocket.getRemoteAddress ()
);
processRequests (clientSocket.socket ());
clientSocket.close ();
} catch (final ClosedByInterruptException cbie) {
//
// The server was interrupted, we are shutting down.
//
} catch (final IOException ioe) {
//
// Communication error -- just log a message here.
//
__log.error ("error accepting a connection: %s", ioe.getMessage ());
}
}
private void processRequests (final Socket sock) {
try {
final DataInputStream is = new DataInputStream (
new BufferedInputStream (sock.getInputStream ()));
final DataOutputStream os = new DataOutputStream (
new BufferedOutputStream (sock.getOutputStream ()));
while (true) {
final byte requestNo = is.readByte ();
if (__requestDispatcher.dispatch (requestNo, is, os, __debug)) {
break;
}
}
} catch (final Exception e) {
Log.__logError (e, __log);
}
}
}
/**
* SHVM implementation
*
* Author: Vit Kabele <vit@kabele.me>
* Created on the 21/02/2019
*/
package ch.usi.dag.dislreserver;
import java.io.*;
import ch.usi.dag.dislreserver.remoteanalysis.RemoteAnalysis;
import ch.usi.dag.util.logging.Logger;
/**
* Implementation of SHVM.
*
* This class should represent the one re-usable class that will represent
* i.e. one analysis session. All the needed data including reflective information
* should be available in its instance.
*/
public class SHVMImpl implements SHVM {
/**
* Logger instance
*/
private final Logger __log;
/**
* Shadow VM context. It's used in the request dispatcher,
* but since the name is {@link SHVMContext} it does make
* a sense to initialize the instance here.
*/
private final SHVMContext __shvmContext;
/**
* Handler for analysis requests.
*/
private final AnalysisHandler __analysisHandler;
/**
* Public constructor
*
* @param logger
*/
SHVMImpl (
final Logger logger
) {
__log = logger;
__shvmContext = new SHVMContext (__log);
__analysisHandler = new AnalysisHandler (__shvmContext, __log);
}
/**
* Process the batch of analysis invocations on the ShadowVM. Only shadowVM knows, how to
* unmarshall the data.
*
* @param orderingId The total ordering ID
* @param invocationCount Number of method invocations
* @param rawData Packed raw data
*/
@Override
public void HandleAnalyze (long orderingId, int invocationCount, byte[] rawData) throws DiSLREServerException
{
__analysisHandler
.handle (
orderingId,
invocationCount,
new DataInputStream (new ByteArrayInputStream (rawData))
);
}
/**
* Handle the message with data about some previously loaded class.
*
* @param tag Unique identifier of class in context of single VM
* @param signature Class signature (i.e. Java/lang/String)
* @param generic Class generics. Empty string if class is not generic, otherwise class signature.
* @param loaderTag Unique identifier of the class loader in context of single VM.
* @param superClassTag Unique identifier of super class in context of single VM.
*/
@Override
public void HandleClassInfo (
long tag, String signature, String generic, long loaderTag, long superClassTag
)
{
__shvmContext
.getShadowClassTable ()
.registerClass (tag, signature, generic, loaderTag, superClassTag);
}
/**
* Handle the close message about closing connection.
* Wait for all analysis to finish, triggers atExit method on each of them.
*/
@Override
public void HandleClose ()
{
// Wait for all executors to complete run of the analysis invocation
__analysisHandler.exit ();
// invoke atExit on all analyses
for (final RemoteAnalysis analysis : __shvmContext.getAnalysisResolver ().getAllAnalyses ()) {
try {
analysis.atExit ();
} catch (final Exception e) {
// report error during analysis invocation
System.err.format (
"DiSL-RE: exception in analysis %s.atExit(): %s\n",
analysis.getClass ().getName (), e.getMessage ()
);
final Throwable cause = e.getCause ();
if (cause != null) {
cause.printStackTrace (System.err);
}
}
}
}
/**
* Register the new class loaded to the observed JVM.
*
* @param name Class name.
* @param loaderTag Unique identifier of the class in the context of observed VM.
* @param data Bytes with class code.
*/
@Override
public void HandleNewClass (String name, long loaderTag, byte[] data)
{
__shvmContext
.getShadowClassTable ()
.loadClass (name, loaderTag, data);
}
/**
* Handle the batch of object freeing messages.
*
* @param tags Tags of freed objects.
*/
@Override
public void HandleObjectFree (long[] tags)
{
__analysisHandler.objectsFreed (tags);
}
/**
* Register new analysis method in context of current ShadowVM.
*
* @param analysisID Id of the analysis method.
* @param descriptor Descriptor of method. I.e. its signature.
*/
@Override
public void HandleRegisterAnalysis (int analysisID, String descriptor) throws DiSLREServerException
{
__shvmContext
.getAnalysisResolver ()
.registerMethodId ((short)analysisID, descriptor);
__log.debug ("DiSL-RE: registered %s as analysis method %d\n", descriptor, analysisID);
}
/**
* Change the value of the string specified by the tag.
*
* @param tag Unique identifier of the string.
* @param value Actual value of string.
*/
@Override
public void HandleStringInfo (long tag, String value)
{
__shvmContext
.getShadowObjectTable ()
.registerShadowString (tag, value);
}
/**
* Notify the ShadowVm about death of a thread
*
* @param threadID Id of a thread.
*/
@Override
public void HandleThreadEnd (long threadID)
{
__analysisHandler
.threadEnded (threadID);
}
/**
* Change the value of parameters assigned with given thread.
*
* @param tag Unique identifier of the thread.
* @param name Name of the thread.
* @param isDaemon Whether the thread runs as a daemon.
*/
@Override
public void HandleThreadInfo (long tag, String name, boolean isDaemon)
{
__shvmContext
.getShadowObjectTable ()
.registerShadowThread (tag, name, isDaemon);
}
}
......@@ -13,16 +13,16 @@ import java.util.concurrent.LinkedBlockingQueue;
class ATEManager {
// we need concurrent for waitForAllToProcessEpoch method
protected final ConcurrentMap<Long, AnalysisTaskExecutor> liveExecutors =
private final ConcurrentMap<Long, AnalysisTaskExecutor> liveExecutors =
new ConcurrentHashMap<Long, AnalysisTaskExecutor>();
protected final BlockingQueue<AnalysisTaskExecutor> endingExecutors =
private final BlockingQueue<AnalysisTaskExecutor> endingExecutors =
new LinkedBlockingQueue<AnalysisTaskExecutor>();
/**
* Retrieves executor. Creates new one if it does not exists.
*/
public AnalysisTaskExecutor getExecutor(long id) {
AnalysisTaskExecutor getExecutor(long id) {
AnalysisTaskExecutor ate = liveExecutors.get(id);
......@@ -39,7 +39,7 @@ class ATEManager {
/**
* Retrieves all live executors
*/
public Iterable<AnalysisTaskExecutor> getAllLiveExecutors() {
Iterable<AnalysisTaskExecutor> getAllLiveExecutors() {
return Collections.unmodifiableCollection(liveExecutors.values());
}
......@@ -47,7 +47,7 @@ class ATEManager {
/**
* Moves executor from live queue to the ending queue
*/
public void executorIsEnding(long id) {
void executorIsEnding(long id) {
AnalysisTaskExecutor removedATE = liveExecutors.remove(id);
......@@ -62,7 +62,7 @@ class ATEManager {
/**
* Changes global epoch in all executors
*/
public void globalEpochChange(long newEpoch) {
void globalEpochChange(long newEpoch) {
for(AnalysisTaskExecutor ate : liveExecutors.values()) {
ate.globalEpochChanged(newEpoch);
......@@ -76,7 +76,7 @@ class ATEManager {
/**
* Waits for all executors to process an epoch
*/
public void waitForAllToProcessEpoch(long epochToProcess) {
void waitForAllToProcessEpoch(long epochToProcess) {
try {
......@@ -98,7 +98,7 @@ class ATEManager {
/**
* Waits for all executors to end
*/
public void waitForAllToEnd() {
void waitForAllToEnd() {
try {
......@@ -120,7 +120,7 @@ class ATEManager {
/**
* Announces executor end. Can be called concurrently.
*/
public void executorEndConcurrentCallback(AnalysisTaskExecutor ate) {
void executorEndConcurrentCallback(AnalysisTaskExecutor ate) {
endingExecutors.remove(ate);
}
}
......@@ -11,11 +11,11 @@ public class AnalysisDispatcher {
// epoch and adds task for object free thread. The thread has to wait until
// all threads processed all tasks from this epoch and then can start
// with
protected long globalEpoch = 0;
private long globalEpoch = 0;
protected final ATEManager ateManager = new ATEManager();
private final ATEManager ateManager = new ATEManager();
protected final ObjectFreeTaskExecutor oftExec;
private final ObjectFreeTaskExecutor oftExec;
AnalysisDispatcher(final SHVMContext shvmContext) {
super();
......@@ -58,7 +58,7 @@ public class AnalysisDispatcher {
}
// called by analysis handler when thread ended on the application vm
public void threadEndedEvent(long threadId) {
void threadEndedEvent(long threadId) {
// create end of processing analysis task
AnalysisTask at = new AnalysisTask();
......
......@@ -9,24 +9,42 @@ import ch.usi.dag.dislreserver.AnalysisInvocation;
*/
class AnalysisTask {
protected boolean signalsEnd = false;
/**
* Used as mark at the end of queue.
*/
private boolean signalsEnd = false;
/**
* List of invocations in this task
*/
protected List<AnalysisInvocation> invocations;
protected long epoch;
/**
* The epoch determined by objectFree events
*/
long epoch;
/**
* Constructed task signals end of the processing
*/
public AnalysisTask() {
AnalysisTask() {
signalsEnd = true;
}
public AnalysisTask(List<AnalysisInvocation> invocations, long epoch) {
/**
* Construct non empty task.
*
* @param invocations List of analysis invocations
* @param epoch
*/
AnalysisTask(List<AnalysisInvocation> invocations, long epoch) {
super();
this.invocations = invocations;
this.epoch = epoch;
}
public boolean isSignalingEnd() {
boolean isSignalingEnd() {
return signalsEnd;
}
......
......@@ -21,11 +21,11 @@ class AnalysisTaskExecutor {
// work from lower epochs) or sets executorEpoch to processed event epoch
// NOTE: no epoch should have this value
private static final long THREAD_SHUTDOWN = -1;
private static final long THREAD_SHUTDOWN = -1;
final protected ATEManager ateManager;
private final ATEManager ateManager;
protected final AnalysisThread executingThread;
private final AnalysisThread executingThread;
// !! RULES !!
// Lock on "this" is protecting globalEpoch, executorEpoch, and taskQueue.
......@@ -38,19 +38,19 @@ class AnalysisTaskExecutor {
// globalEpoch and taskQueue changes, but it would require more
// sophisticated locking also.
protected long globalEpoch = 0;
private long globalEpoch = 0;
protected long executorEpoch = 0;
protected final Queue<AnalysisTask> taskQueue;
private long executorEpoch = 0;
private final Queue<AnalysisTask> taskQueue;
public AnalysisTaskExecutor(ATEManager ateManager) {
AnalysisTaskExecutor(ATEManager ateManager) {
super();
this.ateManager = ateManager;
this.taskQueue = new LinkedList<AnalysisTask>();
this.executingThread = new AnalysisThread(this);
}
public synchronized void addTask(AnalysisTask at) {
synchronized void addTask(AnalysisTask at) {
taskQueue.add(at);
// changed taskQueue -> according to the rules notifyAll
......
......@@ -10,6 +10,7 @@ import java.util.List;
import ch.usi.dag.dislreserver.AnalysisResolver.AnalysisMethodHolder;
import ch.usi.dag.dislreserver.shadow.ShadowObject;
import ch.usi.dag.dislreserver.shadow.ShadowObjectTable;
import ch.usi.dag.util.logging.Logger;
/**
......@@ -17,7 +18,7 @@ import ch.usi.dag.dislreserver.shadow.ShadowObjectTable;
*
* Takes care of handling the requests to analysis invocations.
*/
public final class AnalysisHandler implements RequestHandler {
public final class AnalysisHandler {
private AnalysisDispatcher dispatcher;
......@@ -27,51 +28,52 @@ public final class AnalysisHandler implements RequestHandler {
private final SHVMContext __shvmContext;
private final Logger __log;
AnalysisHandler(
final SHVMContext shvmContext
final SHVMContext shvmContext,
final Logger logger
) {
__shvmContext = shvmContext;
__log = logger;
dispatcher = new AnalysisDispatcher (__shvmContext);
}
@Override
public void handle (
final DataInputStream is, final DataOutputStream os, final boolean debug
) throws DiSLREServerException {
try {
// get net reference for the thread
long orderingID = is.readLong ();
/**
* Handle the RPC request to analysis call.
*
* @param orderingID Total ordering ID
* @param invocationCount Number of method invocations
* @param is Input stream with the rest of the data.
* @throws DiSLREServerException
*/
public void handle (final long orderingID, final int invocationCount, final DataInputStream is)
throws DiSLREServerException
{
// read and create method invocations
final int invocationCount = is.readInt ();
if (invocationCount < 0) {
if (invocationCount < 0) {
throw new DiSLREServerException (String.format (
"invalid number of analysis invocation requests: %d",
invocationCount
));
}
}
List <AnalysisInvocation> invocations = __unmarshalInvocations (
invocationCount, is, debug
);
List <AnalysisInvocation> invocations = __unmarshalInvocations (invocationCount, is);
dispatcher.addTask (orderingID, invocations);
dispatcher.addTask (orderingID, invocations);
} catch (final IOException ioe) {
throw new DiSLREServerException(ioe);
}
}
private List <AnalysisInvocation> __unmarshalInvocations (
final int invocationCount, final DataInputStream is, final boolean debug
final int invocationCount, final DataInputStream is
) throws DiSLREServerException {
final List <AnalysisInvocation> result =
new LinkedList <AnalysisInvocation> ();
for (int i = 0; i < invocationCount; ++i) {
result.add (__unmarshalInvocation (is, debug));
result.add (__unmarshalInvocation (is));
}
return result;
......@@ -79,7 +81,7 @@ public final class AnalysisHandler implements RequestHandler {
private AnalysisInvocation __unmarshalInvocation (
final DataInputStream is, final boolean debug
final DataInputStream is
) throws DiSLREServerException {
try {
// *** retrieve method ***
......@@ -131,13 +133,11 @@ public final class AnalysisHandler implements RequestHandler {
// *** create analysis invocation ***
if(debug) {
System.out.printf (
"DiSL-RE: dispatching analysis method (%d) to %s.%s()\n",
methodId, amh.getAnalysisInstance().getClass().getSimpleName (),
method.getName()
);
}
__log.debug (
"DiSL-RE: dispatching analysis method (%d) to %s.%s()\n",
methodId, amh.getAnalysisInstance().getClass().getSimpleName (),
method.getName()
);
return new AnalysisInvocation (
method, amh.getAnalysisInstance (), args
......@@ -218,7 +218,6 @@ public final class AnalysisHandler implements RequestHandler {
dispatcher.objectsFreedEvent(objFreeIDs);
}
@Override
public void exit() {
dispatcher.exit();
}
......
package ch.usi.dag.dislreserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public final class ClassInfoHandler implements RequestHandler {
/**
* Reference to the shadow VM context.
* This is used to retrieve/modify the global state
* of SHVM instead of relying on global variables.
*/
private final SHVMContext __shvmContext;
ClassInfoHandler(
final SHVMContext shvmContext
) {
__shvmContext = shvmContext;
}
@Override
public void handle (
final DataInputStream is, final DataOutputStream os,
final boolean debug
) throws DiSLREServerException {
try {
final long netReference = is.readLong ();
final String typeDescriptor = is.readUTF ();
final String classGenericStr = is.readUTF ();
final long classLoaderNetReference = is.readLong ();
final long superclassNetReference = is.readLong ();
__shvmContext
.getShadowClassTable ()
.registerClass (
netReference, typeDescriptor, classGenericStr,
classLoaderNetReference, superclassNetReference
);
} catch (final IOException e) {
throw new DiSLREServerException (e);
}
}
@Override
public void exit () {
// do nothing
}
}
package ch.usi.dag.dislreserver;
import ch.usi.dag.dislreserver.remoteanalysis.RemoteAnalysis;
import java.io.DataInputStream;
import java.io.DataOutputStream;
/**
* CloseHandler class.
*
* Handle the request signalizing the end of analysis.
* This includes calling "destructor" on all registered event handlers
* and on all analyses.
*/
public final class CloseHandler implements RequestHandler {
/**
* Reference to the shadow VM context.
* This is used to retrieve/modify the global state
* of SHVM instead of relying on global variables.
*/
private final SHVMContext __shvmContext;
/**
* Reference to the request dispatcher.
*/
private final RequestDispatcher __requestDispatcher;
/**
* The default constructor.
*
* @param shvmContext Shadow VM Context
* @param requestDispatcher Request dispatcher instance
*/
CloseHandler(
final SHVMContext shvmContext,
final RequestDispatcher requestDispatcher
) {
__shvmContext = shvmContext;
__requestDispatcher = requestDispatcher;
}
/**
* Handle the Close request. This includes calling exit on each request handler
* and proper ending of all running analyses.
*
* @param is Input stream
* @param os Output stream
* @param debug Debug flag
*/
@Override
public void handle (
final DataInputStream is, final DataOutputStream os, final boolean debug
) {
// call exit on all request handlers - waits for all uncompleted actions
for (final RequestHandler handler : __requestDispatcher.getAllHandlers ()) {
handler.exit ();
}
// invoke atExit on all analyses
for (final RemoteAnalysis analysis : __shvmContext.getAnalysisResolver ().getAllAnalyses ()) {
try {
analysis.atExit ();
} catch (final Exception e) {
// report error during analysis invocation
System.err.format (
"DiSL-RE: exception in analysis %s.atExit(): %s\n",
analysis.getClass ().getName (), e.getMessage ()
);
final Throwable cause = e.getCause ();
if (cause != null) {
cause.printStackTrace (System.err);
}
}
}
}
@Override
public void exit () {
// empty
}
}
package ch.usi.dag.dislreserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import ch.usi.dag.dislreserver.InstrMsgReader.InstrClassMessage;
public abstract class AbstractInstrumentation implements RequestHandler {
// used for replays
private static final byte[] emptyByteArray = new byte[0];
public void handle(DataInputStream is, DataOutputStream os, boolean debug)
throws DiSLREServerException {
try {
InstrClassMessage nm = InstrMsgReader.readMessage(is);
byte[] instrClass;
try {
instrClass = instrument(new String(nm.getControl()),
nm.getClassCode());
}
catch (DiSLREServerException e) {
// instrumentation error
// send the client a description of the server-side error
String errToReport = e.getMessage();
// during debug send the whole message
if(debug) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
errToReport = sw.toString();
}
// error protocol:
// control contains the description of the server-side error
// class code is an array of size zero
String errMsg = "Instrumentation error for class "
+ new String(nm.getControl()) + ": " + errToReport;
InstrMsgReader.sendMessage(os, new InstrClassMessage(errMsg
.getBytes(), emptyByteArray));
throw e;
}
InstrClassMessage replyData = null;
if(instrClass != null) {
// class was modified - send modified data
replyData = new InstrClassMessage(emptyByteArray, instrClass);
}
else {
// zero length means no modification
replyData = new InstrClassMessage(emptyByteArray, emptyByteArray);
}
InstrMsgReader.sendMessage(os, replyData);
}
catch (IOException e) {
throw new DiSLREServerException(e);
}
}
protected abstract byte[] instrument(String string, byte[] classCode)
throws DiSLREServerException;
}
package ch.usi.dag.dislreserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class InstrMsgReader {
public static class InstrClassMessage {
private byte[] control;
private byte[] classCode;
public InstrClassMessage(byte[] control, byte[] classCode) {
this.control = control;
this.classCode = classCode;
}
public byte[] getControl() {
return control;
}
public byte[] getClassCode() {
return classCode;
}
}
public static InstrClassMessage readMessage(DataInputStream is) throws IOException {
// protocol:
// java int - control string length (ctl)
// java int - class code length (ccl)
// bytes[ctl] - control string (contains class name)
// bytes[ccl] - class code
int controlLength = is.readInt();
int classCodeLength = is.readInt();
// allocate buffer for class reading
byte[] control = new byte[controlLength];
byte[] classCode = new byte[classCodeLength];
// read class
is.readFully(control);
is.readFully(classCode);
return new InstrClassMessage(control, classCode);
}
public static void sendMessage(DataOutputStream os, InstrClassMessage icm)
throws IOException {
// protocol:
// java int - control string (ctl)
// java int - class code length (ccl)
// bytes[ctl] - control string
// bytes[ccl] - class code
os.writeInt(icm.getControl().length);
os.writeInt(icm.getClassCode().length);
os.write(icm.getControl());
os.write(icm.getClassCode());
os.flush();
}
}
package ch.usi.dag.dislreserver;
import ch.usi.dag.dislreserver.shadow.ShadowClassTable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* NewClassHandler class
*
* Handle the request signalizing that a new class has been loaded
* to the observed virtual machine.
*/
public class NewClassHandler implements RequestHandler {
/**
* Reference to the shadow VM context.
* This is used to retrieve/modify the global state
* of SHVM instead of relying on global variables.
*/
private final SHVMContext __shvmContext;
NewClassHandler(
final SHVMContext shvmContext
) {
__shvmContext = shvmContext;
}
@Override
public void handle (
final DataInputStream is, final DataOutputStream os, final boolean debug
) throws DiSLREServerException {
try {
final String classInternalName = is.readUTF ();
final long classLoaderNetReference = is.readLong ();
final int classCodeLength = is.readInt ();
final byte [] classCode = new byte [classCodeLength];
is.readFully (classCode);
__shvmContext.getShadowClassTable ().loadClass (
classInternalName, classLoaderNetReference, classCode
);
} catch (final IOException e) {
throw new DiSLREServerException (e);
}
}
@Override
public void exit () {
// do nothing
}
}
package ch.usi.dag.dislreserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* ObjectFreeHandler class
*
* Handle the request notifying about the object free event in observed VM
*/
public class ObjectFreeHandler implements RequestHandler {
private final AnalysisHandler analysisHandler;
ObjectFreeHandler (AnalysisHandler anlHndl) {
analysisHandler = anlHndl;
}
@Override
public void handle (
DataInputStream is,
DataOutputStream os,
boolean debug
) throws DiSLREServerException
{
try {
int freeCount = is.readInt ();
long[] objFreeIDs = new long[freeCount];
for (int i = 0; i < freeCount; ++i) {
long netref = is.readLong ();
objFreeIDs[i] = netref;
}
analysisHandler.objectsFreed (objFreeIDs);
} catch (IOException e) {
throw new DiSLREServerException (e);
}
}
@Override
public void exit () {}
}
package ch.usi.dag.dislreserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Register analysis method handler
*/
public final class RegAnalysisHandler implements RequestHandler {
/**
* Current {@link SHVM} context.
*/
private final SHVMContext __shvmContext;
/**
* Default constructor.
*
* @param shvmContext
*/
RegAnalysisHandler(final SHVMContext shvmContext) {
__shvmContext = shvmContext;
}
@Override
public void handle(
final DataInputStream is,
final DataOutputStream os,
final boolean debug
) throws DiSLREServerException
{
try {
final short methodId = is.readShort();
String methodString = is.readUTF();
// register method
__shvmContext.getAnalysisResolver ().registerMethodId(methodId, methodString);
if (debug) {
System.out.printf(
"DiSL-RE: registered %s as analysis method %d\n",
methodString.toString(), methodId);
}
} catch (final IOException ioe) {
throw new DiSLREServerException(ioe);
}
}
@Override
public void exit() {}
}
package ch.usi.dag.dislreserver;
import ch.usi.dag.dislreserver.shadow.ShadowObjectTable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public final class StringInfoHandler implements RequestHandler {
/**
* Reference to the shadow VM context.
* This is used to retrieve/modify the global state
* of SHVM instead of relying on global variables.
*/
private final SHVMContext __shvmContext;
StringInfoHandler(
final SHVMContext shvmContext
) {
__shvmContext = shvmContext;
}
@Override
public void handle (
final DataInputStream is, final DataOutputStream os,
final boolean debug
) throws DiSLREServerException {
try {
final long netReference = is.readLong ();
final String value = is.readUTF ();
__shvmContext.getShadowObjectTable ().registerShadowString (netReference, value);
} catch (final IOException e) {
throw new DiSLREServerException (e);
}
}
@Override
public void exit () {
// do nothing
}
}
package ch.usi.dag.dislreserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class ThreadEndHandler implements RequestHandler {
final AnalysisHandler analysisHandler;
ThreadEndHandler(AnalysisHandler anlHndl) {
analysisHandler = anlHndl;
}
public void handle(DataInputStream is, DataOutputStream os, boolean debug)
throws DiSLREServerException {
try {
long threadId = is.readLong();
// announce thread end to the analysis handler
analysisHandler.threadEnded(threadId);
} catch (IOException e) {
throw new DiSLREServerException(e);
}
}
public void awaitProcessing() {
}
public void exit() {
}
}
package ch.usi.dag.dislreserver;
import ch.usi.dag.dislreserver.shadow.ShadowObjectTable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Handle the thread info message.
*/
public class ThreadInfoHandler implements RequestHandler {
/**
* Reference to the shadow VM context.
* This is used to retrieve/modify the global state
* of SHVM instead of relying on global variables.
*/
private final SHVMContext __shvmContext;
ThreadInfoHandler(
final SHVMContext shvmContext
) {
__shvmContext = shvmContext;
}
@Override
public void handle (
final DataInputStream is, final DataOutputStream os,
final boolean debug
) throws DiSLREServerException {
try {
final long netReference = is.readLong ();
final String name = is.readUTF ();
final boolean isDaemon = is.readBoolean ();
__shvmContext.getShadowObjectTable ().registerShadowThread (
netReference, name, isDaemon
);
} catch (final IOException e) {
throw new DiSLREServerException (e);
}
}
@Override
public void exit () {
// do nothing
}
}