Commit 2916ed9f authored by Lubomir Bulej's avatar Lubomir Bulej

Merge branch 'shvm-protobuf' into 'devel'

Shvm protobuf

See merge request !6
parents c68f274e 8c4a2319
Pipeline #4604 passed with stages
in 3 minutes and 57 seconds
...@@ -26,15 +26,7 @@ ...@@ -26,15 +26,7 @@
<classpathentry kind="src" path="disl/src-test"/> <classpathentry kind="src" path="disl/src-test"/>
<classpathentry kind="src" path="shvm/src/src-dislreserver"/> <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-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-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-msg-analyze-mtdispatch"/>
<classpathentry kind="src" path="shvm/src/src-remoteanalysis"/> <classpathentry kind="src" path="shvm/src/src-remoteanalysis"/>
<classpathentry kind="src" path="shvm/src/src-reqdispatch"/> <classpathentry kind="src" path="shvm/src/src-reqdispatch"/>
...@@ -42,6 +34,7 @@ ...@@ -42,6 +34,7 @@
<classpathentry kind="src" path="shvm/src/src-util"/> <classpathentry kind="src" path="shvm/src/src-util"/>
<classpathentry kind="src" path="shvm/src/src-server-api"/> <classpathentry kind="src" path="shvm/src/src-server-api"/>
<classpathentry kind="src" path="shvm/src-test"/> <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="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="output/lib/disl-bypass.jar"/> <classpathentry kind="lib" path="output/lib/disl-bypass.jar"/>
<classpathentry kind="lib" path="lib/disl/log4j.jar" sourcepath="lib/devel/log4j-source.jar"/> <classpathentry kind="lib" path="lib/disl/log4j.jar" sourcepath="lib/devel/log4j-source.jar"/>
......
...@@ -55,15 +55,7 @@ ...@@ -55,15 +55,7 @@
<src path="src/src-dislreserver" /> <src path="src/src-dislreserver" />
<src path="src/src-msg-analyze" /> <src path="src/src-msg-analyze" />
<src path="src/src-msg-analyze-mtdispatch" /> <src path="src/src-msg-analyze-mtdispatch" />
<src path="src/src-msg-classinfo" /> <src path="src/src-proto" />
<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-remoteanalysis" /> <src path="src/src-remoteanalysis" />
<src path="src/src-reqdispatch" /> <src path="src/src-reqdispatch" />
<src path="src/src-server-api" /> <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; package ch.usi.dag.dislreserver;
import java.io.Closeable; import java.io.*;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.StandardSocketOptions; import java.net.StandardSocketOptions;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ServerSocketChannel; import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import ch.usi.dag.util.logging.Logger; import ch.usi.dag.util.logging.Logger;
...@@ -45,9 +46,30 @@ public abstract class DiSLREServer { ...@@ -45,9 +46,30 @@ public abstract class DiSLREServer {
__log.debug ("server started"); __log.debug ("server started");
__serverStarted (); __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"); __log.debug ("server shutting down");
__closeSocket (socket); __closeSocket (socket);
...@@ -57,6 +79,43 @@ public abstract class DiSLREServer { ...@@ -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; ...@@ -13,16 +13,16 @@ import java.util.concurrent.LinkedBlockingQueue;
class ATEManager { class ATEManager {
// we need concurrent for waitForAllToProcessEpoch method // we need concurrent for waitForAllToProcessEpoch method
protected final ConcurrentMap<Long, AnalysisTaskExecutor> liveExecutors = private final ConcurrentMap<Long, AnalysisTaskExecutor> liveExecutors =
new ConcurrentHashMap<Long, AnalysisTaskExecutor>(); new ConcurrentHashMap<Long, AnalysisTaskExecutor>();
protected final BlockingQueue<AnalysisTaskExecutor> endingExecutors = private final BlockingQueue<AnalysisTaskExecutor> endingExecutors =
new LinkedBlockingQueue<AnalysisTaskExecutor>();