diff --git a/microbat/META-INF/MANIFEST.MF b/microbat/META-INF/MANIFEST.MF
index ddd0547fd..a6ffc57f2 100644
--- a/microbat/META-INF/MANIFEST.MF
+++ b/microbat/META-INF/MANIFEST.MF
@@ -132,6 +132,8 @@ Export-Package: microbat;uses:="org.osgi.framework,org.eclipse.jface.resource,or
org.eclipse.core.commands,
microbat.model.trace",
microbat.handler.xml,
+ microbat.instrumentation.instr.aggreplay.shared,
+ microbat.instrumentation.model.id,
microbat.instrumentation.output,
microbat.model;uses:="microbat.model.variable,microbat.model.value,microbat.model.trace",
microbat.model.trace;
diff --git a/microbat/lib/instrumentator.jar b/microbat/lib/instrumentator.jar
index c3063d0f1..84fb16e3a 100644
Binary files a/microbat/lib/instrumentator.jar and b/microbat/lib/instrumentator.jar differ
diff --git a/microbat/plugin.xml b/microbat/plugin.xml
index 4d0b462af..806cb6e8a 100644
--- a/microbat/plugin.xml
+++ b/microbat/plugin.xml
@@ -132,6 +132,22 @@
id="microbat.command.RestoreTrace"
name="Restore Trace">
+
+
+
+
+
+
@@ -194,6 +210,19 @@
label="Restore Trace"
style="push">
+
builder,
+ VMConfiguration config) {
+ builder.appendIf("-Xmx30g", enableSettingHeapSize);
+ // builder.appendIf("-Xmn10g", enableSettingHeapSize);
+ builder.appendIf("-XX:+UseG1GC", enableSettingHeapSize);
+ super.buildVmOption(builder, config);
+ }
+
+ private void runSharedVarDetector() throws SavException {
+ super.addAgentParam(AgentParams.OPT_SHARED_DETECTION, true);
+ super.addAgentParam(AgentParams.OPT_DUMP_FILE, VARIABLE_DUMP_FILEPATH);
+ super.startAndWaitUntilStop(getProgramArgs());
+ }
+
+ /**
+ * Method used for running the individual concurrent modes.
+ * @param mode
+ * @param concDumpFile The path to the conc dump
+ * @param dumpFile The dump file
+ * @throws SavException
+ */
+ public void concReplay(String mode, String concDumpFile, String dumpFile) throws SavException {
+ setUp();
+ addAgentParam(mode, true);
+ addAgentParam(AgentParams.OPT_CONC_RECORD_DUMP, concDumpFile);
+ addAgentParam(AgentParams.OPT_DUMP_FILE, dumpFile);
+ super.startAndWaitUntilStop(this.configuration);
+ removeAgentParam(mode);
+ }
+
+ /**
+ * Run's the three stages of the record and replay
+ */
+ @Override
+ public void run() {
+ SingleTimer.start("Aggr");
+ try {
+ runSharedVarDetector();
+ } catch (SavException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+
+ }
+
+}
diff --git a/microbat/src/main/microbat/agent/ExecTraceFileReader.java b/microbat/src/main/microbat/agent/ExecTraceFileReader.java
index 366380248..f8843f212 100644
--- a/microbat/src/main/microbat/agent/ExecTraceFileReader.java
+++ b/microbat/src/main/microbat/agent/ExecTraceFileReader.java
@@ -1,6 +1,7 @@
package microbat.agent;
import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import microbat.codeanalysis.runtime.PreCheckInformation;
@@ -11,14 +12,20 @@
public class ExecTraceFileReader {
private String msg;
private Trace trace;
+ private List allTraces;
public Trace read(String execTraceFile) {
RunningInfo info = RunningInfo.readFromFile(execTraceFile);
this.trace = info.getMainTrace();
this.msg = info.getProgramMsg();
+ this.allTraces = info.getTraceList();
return trace;
}
+ public List getAllTraces() {
+ return this.allTraces;
+ }
+
public PreCheckInformation readPrecheck(String precheckFile) {
PrecheckInfo info = PrecheckInfo.readFromFile(precheckFile);
PreCheckInformation result = new PreCheckInformation(info.getThreadNum(), info.getStepTotal(), info.isOverLong(),
diff --git a/microbat/src/main/microbat/agent/TraceAgentRunner.java b/microbat/src/main/microbat/agent/TraceAgentRunner.java
index ffaec254d..a1b426264 100644
--- a/microbat/src/main/microbat/agent/TraceAgentRunner.java
+++ b/microbat/src/main/microbat/agent/TraceAgentRunner.java
@@ -17,6 +17,7 @@
import microbat.model.trace.Trace;
import microbat.preference.DatabasePreference;
import microbat.trace.Reader;
+import microbat.util.MicroBatUtil;
import sav.common.core.SavException;
import sav.common.core.SavRtException;
import sav.common.core.utils.CollectionBuilder;
@@ -42,6 +43,10 @@ public class TraceAgentRunner extends AgentVmRunner {
private List traces;
+ public void stopRunning() {
+ super.stop();
+ }
+
public TraceAgentRunner(String agentJar, VMConfiguration vmConfig) {
super(agentJar, AgentConstants.AGENT_OPTION_SEPARATOR, AgentConstants.AGENT_PARAMS_SEPARATOR);
this.setConfig(vmConfig);
@@ -54,6 +59,31 @@ protected void buildVmOption(CollectionBuilder builder, VMConfigurati
builder.appendIf("-XX:+UseG1GC", enableSettingHeapSize);
super.buildVmOption(builder, config);
}
+
+ public boolean sharedDetection() throws SavException {
+ addAgentParam(AgentParams.OPT_SHARED_DETECTION, true);
+ super.startAndWaitUntilStop(getConfig());
+ removeAgentParam(AgentParams.OPT_SHARED_DETECTION);
+ return true;
+ }
+
+ /**
+ * Method used for running the individual concurrent modes.
+ * @param mode
+ * @param concDumpFile The path to the conc dump
+ * @param dumpFile The dump file
+ * @throws SavException
+ */
+ public void concReplay(String mode, String concDumpFile, String dumpFile) throws SavException {
+ addAgentParam(mode, true);
+ addAgentParam(AgentParams.OPT_CONC_RECORD_DUMP, concDumpFile);
+ addAgentParam(AgentParams.OPT_DUMP_FILE, dumpFile);
+ super.startAndWaitUntilStop(getConfig());
+ removeAgentParam(mode);
+ removeAgentParam(AgentParams.OPT_DUMP_FILE);
+ removeAgentParam(AgentParams.OPT_CONC_RECORD_DUMP);
+ }
+
public boolean precheck(String filePath) throws SavException {
isPrecheckMode = true;
@@ -279,8 +309,12 @@ private void updateTestResult(String msg) {
unknownTestResult = true;
return;
}
+ isTestSuccessful = MicroBatUtil.checkTestResult(msg);
int sIdx = msg.indexOf(";");
- isTestSuccessful = Boolean.valueOf(msg.substring(0, sIdx));
+ if (sIdx < 0 || msg.length() < sIdx) {
+ unknownTestResult = true;
+ return;
+ }
testFailureMessage = msg.substring(sIdx + 1, msg.length());
}
diff --git a/microbat/src/main/microbat/codeanalysis/runtime/InstrumentationExecutor.java b/microbat/src/main/microbat/codeanalysis/runtime/InstrumentationExecutor.java
index a1e19e322..0f821b772 100644
--- a/microbat/src/main/microbat/codeanalysis/runtime/InstrumentationExecutor.java
+++ b/microbat/src/main/microbat/codeanalysis/runtime/InstrumentationExecutor.java
@@ -1,6 +1,10 @@
package microbat.codeanalysis.runtime;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -9,6 +13,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Scanner;
import org.apache.commons.io.FileUtils;
import org.eclipse.jdt.core.dom.ASTNode;
@@ -33,6 +38,7 @@
import microbat.preference.MicrobatPreference;
import microbat.sql.DBSettings;
import microbat.sql.DbService;
+import microbat.trace.FileTraceReader;
import microbat.util.JavaUtil;
import microbat.util.MinimumASTNodeFinder;
import microbat.util.Settings;
@@ -49,6 +55,7 @@ public class InstrumentationExecutor {
private String traceExecFilePath;
private TraceAgentRunner agentRunner;
private long timeout = VMRunner.NO_TIME_OUT;
+ private boolean isForceJunit3Or4 = false;
private List includeLibs = Collections.emptyList();
private List excludeLibs = Collections.emptyList();
@@ -69,6 +76,11 @@ public InstrumentationExecutor(AppJavaClassPath appPath, String traceExecFilePat
agentRunner = createTraceAgentRunner();
}
+ public void setIsForceJunit3Or4(boolean isForceJunit3Or4) {
+ this.isForceJunit3Or4 = isForceJunit3Or4;
+ agentRunner = createTraceAgentRunner();
+ }
+
private TraceAgentRunner createTraceAgentRunner() {
if(DBSettings.USE_DB.equals("true")) {
@@ -92,6 +104,11 @@ private TraceAgentRunner createTraceAgentRunner() {
if (appPath.getOptionalTestClass() != null) {
config.addProgramArgs(appPath.getOptionalTestClass());
config.addProgramArgs(appPath.getOptionalTestMethod());
+ // force the test runner to run only 3 or 4 -> skip the check
+ if (this.isForceJunit3Or4) {
+ config.addProgramArgs("forceJunit3Or4");
+ }
+
agentRunner.addAgentParam(AgentParams.OPT_LAUNCH_CLASS, appPath.getOptionalTestClass());
} else {
agentRunner.addAgentParam(AgentParams.OPT_ENTRY_POINT,
@@ -117,7 +134,8 @@ private TraceAgentRunner createTraceAgentRunner() {
agentRunner.setTimeout(timeout);
// FIXME Xuezhi [2]
List entries = ExecutionRangePreference.getCodeRangeEntrys();
- agentRunner.addAgentParams(AgentParams.OPT_CODE_RANGE, entries);
+ agentRunner.addAgentParams(AgentParams.OPT_CODE_RANGE, entries);
+ agentRunner.addAgentParam(AgentParams.TIMEOUT, Settings.timeLimit + "");
return agentRunner;
}
@@ -164,6 +182,123 @@ public RunningInfo run() throws StepLimitException {
return null;
}
+ public RunningInfo runCounter() throws StepLimitException {
+ try {
+ agentRunner.getConfig().setDebug(Settings.isRunWtihDebugMode);
+ agentRunner.getConfig().setPort(9000);
+
+ System.out.println("precheck..");
+ agentRunner.precheck(null);
+ PrecheckInfo info = agentRunner.getPrecheckInfo();
+ System.out.println(info);
+ PreCheckInformation precheckInfomation = new PreCheckInformation(info.getThreadNum(), info.getStepTotal(),
+ info.isOverLong(), new ArrayList<>(info.getVisitedLocs()), info.getExceedingLimitMethods(), info.getLoadedClasses());
+ precheckInfomation.setPassTest(agentRunner.isTestSuccessful());
+ this.setPrecheckInfo(precheckInfomation);
+ } catch (SavException e1) {
+ e1.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public String runMemoryMeasureMent(String dumpFile) {
+
+ agentRunner.getConfig().setDebug(Settings.isRunWtihDebugMode);
+ agentRunner.addAgentParam(AgentParams.OPT_DUMP_FILE, dumpFile);
+ agentRunner.getConfig().setPort(9000);
+ agentRunner.addAgentParam(AgentParams.MEASURE_MEM, true);
+ try {
+ agentRunner.startAndWaitUntilStop(agentRunner.getConfig());
+ } catch (SavException e) {
+ e.printStackTrace();
+ }
+ agentRunner.removeAgentParam(AgentParams.OPT_DUMP_FILE);
+ agentRunner.removeAgentParam(AgentParams.MEASURE_MEM);
+
+ return agentRunner.getProccessError();
+ }
+
+
+ public String runSharedVariable(String dumpFile, int stepLimit) {
+ agentRunner.getConfig().setDebug(Settings.isRunWtihDebugMode);
+ agentRunner.getConfig().setPort(9000);
+ agentRunner.addAgentParam(AgentParams.OPT_DUMP_FILE, dumpFile);
+ agentRunner.addAgentParam(AgentParams.OPT_STEP_LIMIT, stepLimit);
+ agentRunner.addAgentParam(AgentParams.OPT_SHARED_DETECTION, true);
+ String result = "";
+ try {
+ agentRunner.sharedDetection();
+ result = agentRunner.getProccessError();
+ } catch (SavException e) {
+ // TODO Auto-generated catch block
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ result = pw.toString();
+ } finally {
+
+ }
+ agentRunner.removeAgentParam(AgentParams.OPT_SHARED_DETECTION);
+ return result;
+ }
+
+ /**
+ *
+ * @param dumpFile
+ * @param concDumpFile
+ * @param stepLimit
+ */
+ public String runRecordConc(String dumpFile, String concDumpFile, int stepLimit) {
+ agentRunner.getConfig().setDebug(Settings.isRunWtihDebugMode);
+ agentRunner.getConfig().setPort(9000);
+ agentRunner.addAgentParam(AgentParams.OPT_STEP_LIMIT, stepLimit);
+ String result;
+ try {
+ agentRunner.concReplay(AgentParams.OPT_CONC_RECORD, concDumpFile, dumpFile);
+ String processError = agentRunner.getProccessError();
+ result = processError;
+ } catch (SavException e) {
+ StringWriter s = new StringWriter();
+ PrintWriter p = new PrintWriter(s);
+ e.printStackTrace(p);
+ result = s.toString();
+ }
+ return result;
+ }
+
+ public String getProcessError() {
+ return this.agentRunner.getProccessError();
+ }
+
+
+ public RunningInfo runReplayTracer(String concFile, String outputFile, int stepLimit) {
+ agentRunner.getConfig().setDebug(Settings.isRunWtihDebugMode);
+ System.out.println("Debug " + agentRunner.getConfig().isDebug());
+ agentRunner.getConfig().setPort(9000);
+ agentRunner.addAgentParam(AgentParams.OPT_STEP_LIMIT, stepLimit);
+ agentRunner.addAgentParam(AgentParams.OPT_TRACE_RECORDER, "FILE");
+ agentRunner.addAgentParam(AgentParams.REPLAY_MODE, Settings.replayMode.toString());
+ try {
+ agentRunner.concReplay(AgentParams.OPT_CONC_REPLAY, concFile, outputFile);
+ } catch (SavException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ agentRunner.removeAgentParam(AgentParams.REPLAY_MODE);
+ FileTraceReader fileTraceReader = new FileTraceReader();
+ RunningInfo result = fileTraceReader.read(null, outputFile);
+ for (Trace trace : result.getTraceList()) {
+ trace.setAppJavaClassPath(appPath);
+ appendMissingInfo(trace, appPath);
+ }
+ return result;
+ }
+
+ public void interrupt() {
+ agentRunner.stopRunning();
+ }
+
public PreCheckInformation runPrecheck(String dumpFile, int stepLimit) {
try {
/* test stepLimit */
@@ -198,7 +333,8 @@ public RunningInfo execute(PreCheckInformation info) {
RunningInfo result = agentRunner.getRunningInfo();
// System.out.println(result);
System.out.println("isExpectedStepsMet? " + result.isExpectedStepsMet());
- System.out.println("trace length: " + result.getMainTrace() == null ? "0" : result.getMainTrace().size());
+ int size = result.getMainTrace() == null ? 0 : result.getMainTrace().size();
+ System.out.println("trace length: " + size);
System.out.println("isTestSuccessful? " + agentRunner.isTestSuccessful());
System.out.println("testFailureMessage: " + agentRunner.getTestFailureMessage());
System.out.println("finish!");
@@ -208,7 +344,10 @@ public RunningInfo execute(PreCheckInformation info) {
trace.setAppJavaClassPath(appPath);
// trace.setMultiThread(info.getThreadNum()!=1);
- appendMissingInfo(trace, appPath);
+// appendMissingInfo(trace, appPath);
+ for (Trace trace1 : result.getTraceList()) {
+ appendMissingInfo(trace1, appPath);
+ }
trace.setConstructTime((int) (System.currentTimeMillis() - start));
return result;
diff --git a/microbat/src/main/microbat/handler/CancelThread.java b/microbat/src/main/microbat/handler/CancelThread.java
new file mode 100644
index 000000000..7539b0b5a
--- /dev/null
+++ b/microbat/src/main/microbat/handler/CancelThread.java
@@ -0,0 +1,45 @@
+package microbat.handler;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+
+/**
+ * Used to check if the job is canceled
+ * @author Gabau
+ *
+ */
+public class CancelThread extends Thread {
+ public boolean stopped = false;
+ IProgressMonitor monitor;
+ InstrumentationExecutor executor;
+ public CancelThread(IProgressMonitor monitor,
+ InstrumentationExecutor executor) {
+ this.setName("Cancel thread");
+ this.monitor = monitor;
+ this.executor = executor;
+ this.setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ while (!stopped) {
+ if (monitor.isCanceled()) {
+ if (executor != null) executor.interrupt();
+ stopped = true;
+ break;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void stopMonitoring() {
+ this.stopped = true;
+ }
+
+}
\ No newline at end of file
diff --git a/microbat/src/main/microbat/handler/ConcurrentRecordHandler.java b/microbat/src/main/microbat/handler/ConcurrentRecordHandler.java
new file mode 100644
index 000000000..d5b2e8925
--- /dev/null
+++ b/microbat/src/main/microbat/handler/ConcurrentRecordHandler.java
@@ -0,0 +1,120 @@
+package microbat.handler;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.common.AbstractHandleObjectEvent;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+import microbat.evaluation.junit.TestCaseAnalyzer;
+import microbat.instrumentation.output.RunningInfo;
+import microbat.model.trace.Trace;
+import microbat.preference.AnalysisScopePreference;
+import microbat.util.MicroBatUtil;
+import microbat.util.Settings;
+import microbat.views.DebugFeedbackView;
+import microbat.views.MicroBatViews;
+import microbat.views.TraceView;
+import sav.common.core.utils.FileUtils;
+import sav.strategies.dto.AppJavaClassPath;
+
+/**
+ *
+ * @author Gabau
+ *
+ */
+public class ConcurrentRecordHandler extends AbstractHandler {
+
+
+ protected String generateTraceDir(AppJavaClassPath appPath) {
+ String traceFolder;
+ if (appPath.getOptionalTestClass() != null) {
+ traceFolder = FileUtils.getFilePath(MicroBatUtil.getTraceFolder(),
+ Settings.projectName,
+ appPath.getOptionalTestClass(),
+ appPath.getOptionalTestMethod());
+ } else {
+ traceFolder = FileUtils.getFilePath(MicroBatUtil.getTraceFolder(),
+ Settings.projectName,
+ appPath.getLaunchClass());
+ }
+ FileUtils.createFolder(traceFolder);
+ return traceFolder;
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+
+ final AppJavaClassPath appClassPath = MicroBatUtil.constructClassPaths();
+ if (Settings.isRunTest) {
+ appClassPath.setOptionalTestClass(Settings.launchClass);
+ appClassPath.setOptionalTestMethod(Settings.testMethod);
+ appClassPath.setLaunchClass(TestCaseAnalyzer.TEST_RUNNER);
+ appClassPath.setTestCodePath(MicroBatUtil.getSourceFolder(Settings.launchClass, Settings.projectName));
+ }
+
+ List srcFolders = MicroBatUtil.getSourceFolders(Settings.projectName);
+ appClassPath.setSourceCodePath(appClassPath.getTestCodePath());
+ for (String srcFolder : srcFolders) {
+ if (!srcFolder.equals(appClassPath.getTestCodePath())) {
+ appClassPath.getAdditionalSourceFolders().add(srcFolder);
+ }
+ }
+ return executeAggrRecord(event, appClassPath);
+
+ }
+
+
+
+ private Object executeAggrRecord(ExecutionEvent event, final AppJavaClassPath appJavaClassPath) {
+ List includedClassNames = AnalysisScopePreference.getIncludedLibList();
+ List excludedClassNames = AnalysisScopePreference.getExcludedLibList();
+ InstrumentationExecutor executor = new InstrumentationExecutor(appJavaClassPath,
+ generateTraceDir(appJavaClassPath), "trace", includedClassNames, excludedClassNames);
+ Job runningJob = new Job("Run aggr") {
+ @Override
+ public IStatus run(IProgressMonitor monitor) {
+ String fileName = null;
+ File dumpFile = null;
+ File concDumpFile = null;
+ // the absolute path to the dump file.
+ String concFileNameString = null;
+ if (Settings.concurrentDumpFile.isPresent()) {
+ concFileNameString = Settings.concurrentDumpFile.get();
+ concDumpFile = new File(concFileNameString);
+ }
+ try {
+ dumpFile = File.createTempFile("temp", ".txt");
+ if (concDumpFile == null) {
+ concDumpFile = File.createTempFile("concTemp", ".txt");
+ Settings.concurrentDumpFile = Optional.of(concDumpFile.getPath());
+ }
+ fileName = dumpFile.getPath();
+ concFileNameString = concDumpFile.getPath();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ CancelThread ctThread = new CancelThread(monitor, executor);
+ ctThread.start();
+ executor.runSharedVariable(fileName, Settings.stepLimit);
+ executor.runRecordConc(fileName, concFileNameString, Settings.stepLimit);
+ ctThread.stopMonitoring();
+ return Status.OK_STATUS;
+ }
+ };
+ runningJob.schedule();
+ return null;
+ }
+
+}
diff --git a/microbat/src/main/microbat/handler/ConcurrentReplayHandler.java b/microbat/src/main/microbat/handler/ConcurrentReplayHandler.java
new file mode 100644
index 000000000..aa04fb066
--- /dev/null
+++ b/microbat/src/main/microbat/handler/ConcurrentReplayHandler.java
@@ -0,0 +1,57 @@
+package microbat.handler;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputFilter.Status;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+import microbat.handler.replayexp.ConcurrentReplayJob;
+import microbat.instrumentation.output.RunningInfo;
+import microbat.model.trace.Trace;
+import microbat.util.MicroBatUtil;
+import microbat.util.Settings;
+import microbat.views.MicroBatViews;
+import microbat.views.TraceView;
+import sav.common.core.utils.FileUtils;
+import sav.strategies.dto.AppJavaClassPath;
+
+public class ConcurrentReplayHandler extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Job job = createReplayJob();
+ job.schedule();
+ return null;
+ }
+
+ public ConcurrentReplayJob createReplayJob() {
+ return new ConcurrentReplayJob();
+ }
+
+
+ protected String generateTraceDir(AppJavaClassPath appPath) {
+ String traceFolder;
+ if (appPath.getOptionalTestClass() != null) {
+ traceFolder = FileUtils.getFilePath(MicroBatUtil.getTraceFolder(),
+ Settings.projectName,
+ appPath.getOptionalTestClass(),
+ appPath.getOptionalTestMethod());
+ } else {
+ traceFolder = FileUtils.getFilePath(MicroBatUtil.getTraceFolder(),
+ Settings.projectName,
+ appPath.getLaunchClass());
+ }
+ FileUtils.createFolder(traceFolder);
+ return traceFolder;
+ }
+
+}
diff --git a/microbat/src/main/microbat/handler/InstrumentExecutorSupplierImpl.java b/microbat/src/main/microbat/handler/InstrumentExecutorSupplierImpl.java
new file mode 100644
index 000000000..a155ebe04
--- /dev/null
+++ b/microbat/src/main/microbat/handler/InstrumentExecutorSupplierImpl.java
@@ -0,0 +1,66 @@
+package microbat.handler;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.eclipse.core.commands.AbstractHandler;
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+import microbat.evaluation.junit.TestCaseAnalyzer;
+import microbat.preference.AnalysisScopePreference;
+import microbat.util.MicroBatUtil;
+import microbat.util.Settings;
+import sav.common.core.utils.FileUtils;
+import sav.strategies.dto.AppJavaClassPath;
+
+/**
+ * Represents a handler which initialises the app java class
+ * @author Gabau
+ *
+ */
+public class InstrumentExecutorSupplierImpl implements Supplier {
+
+ @Override
+ public InstrumentationExecutor get() {
+ final AppJavaClassPath appClassPath = MicroBatUtil.constructClassPaths();
+ if (Settings.isRunTest) {
+ appClassPath.setOptionalTestClass(Settings.launchClass);
+ appClassPath.setOptionalTestMethod(Settings.testMethod);
+ appClassPath.setLaunchClass(TestCaseAnalyzer.TEST_RUNNER);
+ appClassPath.setTestCodePath(MicroBatUtil.getSourceFolder(Settings.launchClass, Settings.projectName));
+ }
+
+ List srcFolders = MicroBatUtil.getSourceFolders(Settings.projectName);
+ appClassPath.setSourceCodePath(appClassPath.getTestCodePath());
+ for (String srcFolder : srcFolders) {
+ if (!srcFolder.equals(appClassPath.getTestCodePath())) {
+ appClassPath.getAdditionalSourceFolders().add(srcFolder);
+ }
+ }
+
+ List includedClassNames = AnalysisScopePreference.getIncludedLibList();
+ List excludedClassNames = AnalysisScopePreference.getExcludedLibList();
+ return new InstrumentationExecutor(appClassPath,
+ generateTraceDir(appClassPath), "trace", includedClassNames, excludedClassNames);
+
+ }
+
+
+ protected String generateTraceDir(AppJavaClassPath appPath) {
+ String traceFolder;
+ if (appPath.getOptionalTestClass() != null) {
+ traceFolder = FileUtils.getFilePath(MicroBatUtil.getTraceFolder(),
+ Settings.projectName,
+ appPath.getOptionalTestClass(),
+ appPath.getOptionalTestMethod());
+ } else {
+ traceFolder = FileUtils.getFilePath(MicroBatUtil.getTraceFolder(),
+ Settings.projectName,
+ appPath.getLaunchClass());
+ }
+ FileUtils.createFolder(traceFolder);
+ return traceFolder;
+ }
+
+
+}
diff --git a/microbat/src/main/microbat/handler/ReplayExperimentHandler.java b/microbat/src/main/microbat/handler/ReplayExperimentHandler.java
new file mode 100644
index 000000000..4db9b975b
--- /dev/null
+++ b/microbat/src/main/microbat/handler/ReplayExperimentHandler.java
@@ -0,0 +1,201 @@
+package microbat.handler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Scanner;
+import java.util.function.Function;
+
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+import microbat.handler.replayexp.ConcurrentReplayJob;
+import microbat.handler.replayexp.NormalTraceJob;
+import microbat.handler.replayexp.ReplayJob;
+import microbat.instrumentation.instr.aggreplay.ReplayMode;
+import microbat.instrumentation.utils.MicrobatUtils;
+import microbat.util.Settings;
+import sav.common.core.utils.SingleTimer;
+
+
+public class ReplayExperimentHandler extends AbstractHandler {
+ private int numberOfRuns = 200;
+ private static String resultLocation = "D:\\replayExperiment.xlsx";
+ private static boolean skipStrict = false;
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ ExperimentJob experimentJob = new ExperimentJob(numberOfRuns);
+ experimentJob.schedule();
+ return null;
+ }
+
+ private static class MemoryJob extends ReplayJob {
+ private String dumpFile;
+ public MemoryJob(String dumpFile) {
+ super("normal memory run");
+ // TODO Auto-generated constructor stub
+ this.dumpFile = dumpFile;
+
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ SingleTimer timer = SingleTimer.start("no trace");
+ InstrumentationExecutor executor = new InstrumentExecutorSupplierImpl().get();
+ String processError = executor.runMemoryMeasureMent(dumpFile);
+ long runTime = timer.getExecutionTime();
+ stats.setRunTime(runTime);
+ stats.setStdError(processError);
+ stats.memoryUsed = getMemory();
+ return Status.OK_STATUS;
+ }
+
+
+ protected long getMemory() {
+ Scanner dumpFileScanner;
+ long origMemSize = -1;
+ try {
+ dumpFileScanner = new Scanner(new File(dumpFile));
+ if (dumpFileScanner.hasNext()) origMemSize = dumpFileScanner.nextLong();
+ dumpFileScanner.close();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return origMemSize;
+ }
+
+
+ }
+
+ private static class ExperimentJob extends Job {
+
+ Class> statClass = ReplayStats.class;
+ private XSSFWorkbook workbook = new XSSFWorkbook();
+ private int numberOfRuns = 100;
+ public ExperimentJob(int numberOfRuns) {
+ super("Run replay experiment");
+ this.numberOfRuns = numberOfRuns;
+ }
+
+ private void writeWorkBook() {
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(new File(resultLocation));
+ this.workbook.write(fileOutputStream);
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ private void runJobProducer(Function jobProducer, String workSheetName) {
+ LinkedList stats = new LinkedList<>();
+ for (int i = 0; i < numberOfRuns; ++i) {
+ ReplayJob job = jobProducer.apply("");
+ job.schedule();
+ try {
+ job.join();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ stats.add(job.getReplayStats());
+ }
+ XSSFSheet sheet = workbook.createSheet(workSheetName);
+ writeToWorkSheet(sheet, stats);
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ ConcurrentReplayHandler replayHandler = new ConcurrentReplayHandler();
+ ReplayMode[] replayMode = ReplayMode.values();
+
+ ReplayMode temp = Settings.replayMode;
+ // run the experiment over the replay modes
+ for (int j = 0; j < replayMode.length; ++j) {
+ if (skipStrict && replayMode[j] == ReplayMode.STRICT_RW) continue;
+ Settings.replayMode = replayMode[j];
+ runJobProducer(v -> replayHandler.createReplayJob(), "experiment" + Settings.replayMode.toString());
+ }
+ runJobProducer(v -> new NormalTraceJob(), "normal runs");
+
+ File tempFile = null;
+ try {
+ tempFile = File.createTempFile("memory", "txt");
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ final File tFile = tempFile;
+ runJobProducer(v -> new MemoryJob(tFile.getAbsolutePath()), "memory run");
+ writeWorkBook();
+
+ return Status.OK_STATUS;
+ }
+
+ protected void writeToWorkSheet(XSSFSheet workSheet, Collection stats) {
+ int rowNum = 0;
+ initHeaders(workSheet.createRow(rowNum));
+ rowNum++;
+ for (ReplayStats stat : stats) {
+ writeToRow(workSheet.createRow(rowNum), stat);
+ rowNum++;
+ }
+ writeWorkBook();
+
+ }
+
+ protected void initHeaders(XSSFRow row) {
+ Field[] fields = statClass.getFields();
+ int v = 0;
+ for (Field field : fields) {
+ XSSFCell cell = row.createCell(v++);
+ cell.setCellValue(field.getName());
+ }
+ }
+
+ private void writeToRow(XSSFRow row, ReplayStats replayStats) {
+ Field[] fields = statClass.getFields();
+ int cellIdx = 0;
+ for (Field field : fields) {
+ try {
+ Object resultObject = field.get(replayStats);
+ XSSFCell cell = row.createCell(cellIdx++);
+ if (resultObject == null) {
+ cell.setCellValue("null");
+ } else {
+ cell.setCellValue(resultObject.toString());
+ }
+
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/microbat/src/main/microbat/handler/ReplayStats.java b/microbat/src/main/microbat/handler/ReplayStats.java
new file mode 100644
index 000000000..5ffafabff
--- /dev/null
+++ b/microbat/src/main/microbat/handler/ReplayStats.java
@@ -0,0 +1,99 @@
+package microbat.handler;
+
+import org.eclipse.core.runtime.jobs.Job;
+
+import microbat.instrumentation.output.RunningInfo;
+
+public class ReplayStats {
+
+ /**
+ * The program msg sent to Agent._exitProgram
+ */
+ public String programMsgString;
+ /**
+ * False if not junit test.
+ */
+ public boolean hasPassedTest = false;
+ /**
+ * The stdout data
+ */
+ public String stderr;
+ /**
+ * The size of the recording.
+ */
+ public long dumpFileSize = -1;
+ /**
+ * The size of the logs.
+ */
+ public long traceFileSize = -1;
+ /**
+ * The runtime in miliseconds
+ */
+ public long runTime = -1;
+
+ /**
+ * The memory used during execution
+ */
+ public long memoryUsed = -1;
+
+
+ public ReplayStats() {
+
+ }
+
+ public String getProgramMsgString() {
+ return programMsgString;
+ }
+
+ public void setProgramMsgString(String programMsgString) {
+ this.programMsgString = programMsgString;
+ }
+
+ public boolean isHasPassedTest() {
+ return hasPassedTest;
+ }
+
+ public void setHasPassedTest(boolean hasPassedTest) {
+ this.hasPassedTest = hasPassedTest;
+ }
+
+ public String getStdout() {
+ if (stderr == null) return "null";
+ return stderr;
+ }
+
+ public void setStdError(String stderrString) {
+ if (stderrString == null) this.stderr = "null";
+ this.stderr = stderrString;
+ }
+
+ public long getDumpFileSize() {
+ return dumpFileSize;
+ }
+
+ public void setDumpFileSize(long dumpFileSize) {
+ this.dumpFileSize = dumpFileSize;
+ }
+
+ public long getTraceFileSize() {
+ return traceFileSize;
+ }
+
+ public void setTraceFileSize(long traceFileSize) {
+ this.traceFileSize = traceFileSize;
+ }
+
+ public double getRunTime() {
+ return runTime;
+ }
+
+ public void setRunTime(long runTime) {
+ this.runTime = runTime;
+ }
+
+ public void updateFromRunningInfo(RunningInfo info) {
+ this.setHasPassedTest(info.hasPassedTest());
+ this.setProgramMsgString(info.getProgramMsg());
+ }
+
+}
\ No newline at end of file
diff --git a/microbat/src/main/microbat/handler/StartDebugHandler.java b/microbat/src/main/microbat/handler/StartDebugHandler.java
index b3f6625dc..b276bb44f 100644
--- a/microbat/src/main/microbat/handler/StartDebugHandler.java
+++ b/microbat/src/main/microbat/handler/StartDebugHandler.java
@@ -1,5 +1,6 @@
package microbat.handler;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -17,6 +18,7 @@
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.swt.widgets.Display;
+import microbat.agent.AggrePlaySharedVarRunner;
import microbat.behavior.Behavior;
import microbat.behavior.BehaviorData;
import microbat.behavior.BehaviorReader;
@@ -60,7 +62,9 @@ public void run() {
});
}
+
public Object execute(ExecutionEvent event) throws ExecutionException {
+
final AppJavaClassPath appClassPath = MicroBatUtil.constructClassPaths();
if (Settings.isRunTest) {
appClassPath.setOptionalTestClass(Settings.launchClass);
@@ -68,6 +72,7 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
appClassPath.setLaunchClass(TestCaseAnalyzer.TEST_RUNNER);
appClassPath.setTestCodePath(MicroBatUtil.getSourceFolder(Settings.launchClass, Settings.projectName));
}
+
List srcFolders = MicroBatUtil.getSourceFolders(Settings.projectName);
appClassPath.setSourceCodePath(appClassPath.getTestCodePath());
for (String srcFolder : srcFolders) {
diff --git a/microbat/src/main/microbat/handler/replayexp/ConcurrentReplayJob.java b/microbat/src/main/microbat/handler/replayexp/ConcurrentReplayJob.java
new file mode 100644
index 000000000..68efcfb71
--- /dev/null
+++ b/microbat/src/main/microbat/handler/replayexp/ConcurrentReplayJob.java
@@ -0,0 +1,102 @@
+package microbat.handler.replayexp;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+import microbat.handler.CancelThread;
+import microbat.handler.InstrumentExecutorSupplierImpl;
+import microbat.handler.ReplayStats;
+import microbat.instrumentation.output.RunningInfo;
+import microbat.model.trace.Trace;
+import microbat.util.Settings;
+import microbat.views.MicroBatViews;
+import microbat.views.TraceView;
+import sav.common.core.utils.SingleTimer;
+
+public class ConcurrentReplayJob extends ReplayJob {
+
+ boolean finishedExecution = false;
+ boolean requireVisualisation = true;
+ public ConcurrentReplayJob() {
+ super("Run replay");
+ }
+
+ public static ConcurrentReplayJob withoutVis() {
+ ConcurrentReplayJob job = new ConcurrentReplayJob();
+ job.requireVisualisation = false;
+ return job;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ SingleTimer timer = SingleTimer.start("Run replay");
+ InstrumentationExecutor executor = new InstrumentExecutorSupplierImpl().get();
+ CancelThread ct = new CancelThread(monitor, executor);
+ File concDumpFile = null;
+ File outputFile = null;
+ // the absolute path to the dump file.
+ String concFileNameString = null;
+ if (Settings.concurrentDumpFile.isPresent()) {
+ concFileNameString = Settings.concurrentDumpFile.get();
+ concDumpFile = new File(concFileNameString);
+ System.out.println("Used recording in " + concFileNameString);
+ }
+ try {
+ if (concDumpFile == null) {
+ concDumpFile = File.createTempFile("concTemp", ".txt");
+ }
+ outputFile = File.createTempFile("outputFile", ".txt");
+ concFileNameString = concDumpFile.getPath();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ ct.start();
+ RunningInfo result = executor.runReplayTracer(concFileNameString, outputFile.getPath(), Settings.stepLimit);
+ Trace mainTrace = result.getMainTrace();
+ if (mainTrace != null) {
+ this.stats.memoryUsed = mainTrace.getMemoryUsed();
+ }
+ ct.stopMonitoring();
+ long executionTime = timer.getExecutionTime();
+ this.stats.setRunTime(executionTime);
+ this.stats.setDumpFileSize(concDumpFile.length());
+ this.stats.setTraceFileSize(outputFile.length());
+ this.stats.setStdError(executor.getProcessError());
+ if (requireVisualisation) {
+ Display.getDefault().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ TraceView traceView = MicroBatViews.getTraceView();
+ if (result == null) {
+ traceView.setMainTrace(null);
+ traceView.setTraceList(null);
+ return;
+ }
+ Trace trace = result.getMainTrace();
+ trace.setAppJavaClassPath(executor.getAppPath());
+ List traces = result.getTraceList();
+
+ traceView.setMainTrace(trace);
+ traceView.setTraceList(traces);
+ traceView.updateData();
+ }
+ });
+ }
+
+ // TODO Auto-generated method stub
+ return org.eclipse.core.runtime.Status.OK_STATUS;
+ }
+
+ public ReplayStats getReplayStats() {
+ return this.stats;
+ }
+}
diff --git a/microbat/src/main/microbat/handler/replayexp/NormalTraceJob.java b/microbat/src/main/microbat/handler/replayexp/NormalTraceJob.java
new file mode 100644
index 000000000..d540cdc9e
--- /dev/null
+++ b/microbat/src/main/microbat/handler/replayexp/NormalTraceJob.java
@@ -0,0 +1,82 @@
+package microbat.handler.replayexp;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+
+import microbat.codeanalysis.runtime.InstrumentationExecutor;
+import microbat.codeanalysis.runtime.StepLimitException;
+import microbat.handler.CancelThread;
+import microbat.handler.InstrumentExecutorSupplierImpl;
+import microbat.instrumentation.output.RunningInfo;
+import microbat.model.trace.Trace;
+import microbat.util.Settings;
+import microbat.views.MicroBatViews;
+import microbat.views.TraceView;
+import sav.common.core.utils.SingleTimer;
+
+public class NormalTraceJob extends ReplayJob {
+
+
+ boolean finishedExecution = false;
+
+ public NormalTraceJob() {
+ super("Run normal trace");
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ SingleTimer timer = SingleTimer.start("Run replay");
+ InstrumentationExecutor executor = new InstrumentExecutorSupplierImpl().get();
+ CancelThread ct = new CancelThread(monitor, executor);
+ File concDumpFile = null;
+ File outputFile = null;
+ // the absolute path to the dump file.
+ String concFileNameString = null;
+ if (Settings.concurrentDumpFile.isPresent()) {
+ concFileNameString = Settings.concurrentDumpFile.get();
+ concDumpFile = new File(concFileNameString);
+ System.out.println("Used recording in " + concFileNameString);
+ }
+ try {
+ if (concDumpFile == null) {
+ concDumpFile = File.createTempFile("concTemp", ".txt");
+ }
+ outputFile = File.createTempFile("outputFile", ".txt");
+ concFileNameString = concDumpFile.getPath();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ ct.start();
+ RunningInfo result = null;
+ try {
+ result = executor.run();
+ this.stats.updateFromRunningInfo(result);
+ } catch (StepLimitException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ ct.stopMonitoring();
+ long executionTime = timer.getExecutionTime();
+ this.stats.setRunTime(executionTime);
+ // when there is a trace avail
+ if (result != null && result.getMainTrace() != null) {
+ Trace mainTrace = result.getMainTrace();
+ this.stats.memoryUsed = mainTrace.getMemoryUsed();
+ this.stats.setRunTime(mainTrace.getConstructTime());
+ }
+ this.stats.setDumpFileSize(-1);
+ this.stats.setTraceFileSize(outputFile.length());
+ this.stats.setStdError(executor.getProcessError());
+ // TODO Auto-generated method stub
+ return org.eclipse.core.runtime.Status.OK_STATUS;
+ }
+
+
+}
diff --git a/microbat/src/main/microbat/handler/replayexp/ReplayJob.java b/microbat/src/main/microbat/handler/replayexp/ReplayJob.java
new file mode 100644
index 000000000..fe229f833
--- /dev/null
+++ b/microbat/src/main/microbat/handler/replayexp/ReplayJob.java
@@ -0,0 +1,16 @@
+package microbat.handler.replayexp;
+
+import org.eclipse.core.runtime.jobs.Job;
+
+import microbat.handler.ReplayStats;
+
+public abstract class ReplayJob extends Job {
+ public ReplayJob(String name) {
+ super(name);
+ // TODO Auto-generated constructor stub
+ }
+ protected ReplayStats stats = new ReplayStats();
+ public ReplayStats getReplayStats() {
+ return this.stats;
+ }
+}
diff --git a/microbat/src/main/microbat/model/ConcNode.java b/microbat/src/main/microbat/model/ConcNode.java
new file mode 100644
index 000000000..044cef2a2
--- /dev/null
+++ b/microbat/src/main/microbat/model/ConcNode.java
@@ -0,0 +1,61 @@
+package microbat.model;
+
+import microbat.model.trace.ConcurrentTrace;
+import microbat.model.trace.TraceNode;
+import microbat.model.value.VarValue;
+import sav.common.core.Pair;
+
+public class ConcNode {
+ private int node1;
+ // after
+ private int node2;
+ // node1 is before
+ private boolean isBefore1;
+ // node2 isbefore
+ private boolean isBefore2;
+
+ private int changeType;
+
+ private VarValue linkedVarValue = null;
+
+ public VarValue getLinkedValue() {
+ return linkedVarValue;
+ }
+
+ public int getChangeType() {
+ return this.changeType;
+ }
+ public ConcNode(int node1, int node2, boolean isBefore1, boolean isBefore2, int changeType) {
+ this.node1 = node1;
+ this.node2 = node2;
+ this.isBefore1 = isBefore1;
+ this.isBefore2 = isBefore2;
+ this.changeType = changeType;
+ }
+ public int getNode1() {
+ return node1;
+ }
+ public int getNode2() {
+ return node2;
+ }
+ public boolean isBefore1() {
+ return isBefore1;
+ }
+ public boolean isBefore2() {
+ return isBefore2;
+ }
+
+ public Pair getFirst() {
+ return Pair.of(node1, isBefore1);
+ }
+
+ public Pair getSecond() {
+ return Pair.of(node2, isBefore2);
+ }
+
+
+ public static ConcNode fromTraceNodes(TraceNode node1, TraceNode node2, boolean isBefore1, boolean isBefore2,
+ int changeType) {
+ return new ConcNode(node1.getBound().getOrder(), node2.getBound().getOrder(), isBefore1, isBefore2, changeType);
+ }
+}
diff --git a/microbat/src/main/microbat/model/trace/ConcurrentTrace.java b/microbat/src/main/microbat/model/trace/ConcurrentTrace.java
new file mode 100644
index 000000000..6c1c793df
--- /dev/null
+++ b/microbat/src/main/microbat/model/trace/ConcurrentTrace.java
@@ -0,0 +1,459 @@
+package microbat.model.trace;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.model.value.VarValue;
+import microbat.model.variable.Variable;
+import sav.strategies.dto.AppJavaClassPath;
+
+public class ConcurrentTrace extends Trace {
+
+ private List originalTraces;
+ // temporary storage for processing
+ private List generatedTraceNodes;
+ // the non sequential concurrent trace nodes
+ private ArrayList> traces
+ = new ArrayList<>();
+
+ private ConcurrentTraceNode linkedTraceNode;
+
+ public List getTraceList() {
+ return originalTraces;
+ }
+
+ public Trace getMainTrace() {
+ return originalTraces.get(0);
+ }
+
+ public List getSequentialTrace() {
+ return generatedTraceNodes;
+ }
+
+ public ConcurrentTrace(AppJavaClassPath appJavaClassPath) {
+ super(appJavaClassPath);
+ // TODO Auto-generated constructor stub
+ }
+
+ public Trace getCorrespondingTrace(ThreadId threadId) {
+ for (Trace trace : originalTraces) {
+ if (threadId.equals(trace.getInnerThreadId())) {
+ return trace;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Technique to update trace nodes values
+ */
+ private void postProcess() {
+ for (int i = 0; i < generatedTraceNodes.size(); ++i) {
+ generatedTraceNodes.get(i).setOrder(i + 1);
+ generatedTraceNodes.get(i).setConcurrentTrace(this);
+ this.addTraceNode(generatedTraceNodes.get(i));
+ }
+ }
+
+ /**
+ * Function used to generate concurrent read write nodes.
+ * Need to split the trace nodes into read and write.
+ */
+ private void generateTraces() {
+ if (traces.size() != 0) throw new RuntimeException("Should not generate more than once");
+ if (originalTraces == null) throw new RuntimeException("Original traces not set");
+ traces.ensureCapacity(originalTraces.size());
+ int ctr = 0;
+ for (Trace trace: originalTraces) {
+ ArrayList result = new ArrayList<>();
+ // reserve double the size than expected
+ result.ensureCapacity(trace.getExecutionList().size() * 2);
+ for (int i = 0; i < trace.getExecutionList().size(); ++i) {
+ TraceNode traceNode = trace.getExecutionList().get(i);
+ result.addAll(ConcurrentTraceNode.splitTraceNode(traceNode, ctr, trace.getThreadId()));
+ }
+ ctr += 1;
+ traces.add(result);
+ }
+ // update the step in previous and step in next
+ for (ArrayList trace: traces) {
+ ConcurrentTraceNode previous = null;
+ for (int i = 0; i < trace.size(); ++i) {
+ if (previous != null) {
+ trace.get(i).setStepInPrevious(trace.get(i-1));
+ }
+ if (i != trace.size() - 1) {
+ trace.get(i).setStepInNext(trace.get(i+1));
+ }
+ previous = trace.get(i);
+ }
+ }
+ }
+
+ @Override
+ public TraceNode findProducer(VarValue varValue, TraceNode startNode) {
+
+ // check if it is on heap
+ String s = varValue.getAliasVarID();
+ boolean isLocalVar = varValue.isLocalVariable();
+ boolean isOnHeap = !isLocalVar;
+ if (!(startNode instanceof ConcurrentTraceNode)) {
+ startNode = startNode.getBound();
+ }
+ ConcurrentTraceNode concNode = (ConcurrentTraceNode) startNode;
+ int j = concNode.getOrder();
+// if (!concNode.isAtomic() && concNode.getWrittenVariables().size() > 0) {
+// return concNode.getLinkedTraceNode().initialTraceNode;
+// }
+//
+ // find the individual trace to use the find data dependenccy
+ String varID = Variable.truncateSimpleID(varValue.getVarID());
+ String headID = Variable.truncateSimpleID(varValue.getAliasVarID());
+
+
+ for(int i=j-2; i>=1; i--) {
+ ConcurrentTraceNode node = this.getSequentialTrace().get(i);
+ for(VarValue writtenValue: node.getWrittenVariables()) {
+ String k = writtenValue.getAliasVarID();
+ if (isOnHeap && writtenValue.getAliasVarID() != null
+ && writtenValue.getAliasVarID().equals(varValue.getAliasVarID())) {
+ return node.initialTraceNode;
+ }
+ if (!isOnHeap && concNode.getCurrentThread() != node.getCurrentThread()) {
+ // when the value is on a different thread
+ continue;
+ }
+
+ String wVarID = Variable.truncateSimpleID(writtenValue.getVarID());
+ String wHeadID = Variable.truncateSimpleID(writtenValue.getAliasVarID());
+// if (wVarID != null && wVarID.equals(varID) && concNode.getCurrentThread() == node.getCurrentThread()) {
+ if(wVarID != null && wVarID.equals(varID)) {
+ return node.initialTraceNode;
+ }
+
+ if(wHeadID != null && wHeadID.equals(headID)) {
+ return node.initialTraceNode;
+ }
+
+ VarValue childValue = writtenValue.findVarValue(varID, headID);
+ if(childValue != null) {
+ return node.initialTraceNode;
+ }
+
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generates a concurrent trace from the given trace by timestamp order.
+ * End goal is to remove timestamp synchronisation - aka deprecate this.
+ * @param traces The trace to generate the trace from
+ * @return
+ */
+ public static ConcurrentTrace fromTimeStampOrder(List inputTraces) {
+ ConcurrentTrace resultTrace = new ConcurrentTrace("");
+ resultTrace.originalTraces = inputTraces;
+
+ AppJavaClassPath cPath = null;
+ for (Trace trace : inputTraces) {
+ cPath = trace.getAppJavaClassPath();
+ if (cPath != null) break;
+ }
+ resultTrace.setAppJavaClassPath(cPath);
+ resultTrace.generateTraces();
+ ArrayList> traces = resultTrace.traces;
+
+ ArrayList result = new ArrayList<>();
+ resultTrace.generatedTraceNodes = result;
+ List sharedVarIds = getSharedVarIDs(inputTraces);
+ HashSet sharedVarAliasIds = new HashSet<>();
+ for (VarValue val: sharedVarIds) {
+ sharedVarAliasIds.add(val.getAliasVarID());
+ }
+ HashMap sharedVarValues = new HashMap<>();
+ ArrayList tracesPtr = new ArrayList<>();
+ tracesPtr.ensureCapacity(traces.size());
+ for (int i = 0; i < traces.size(); ++i) {
+ tracesPtr.add(0); // point to the first trace node
+ }
+ while (true) {
+ // select the first trace based off of time stamp order
+ // if there is a tie, break tie based off of current shared variables status
+ ArrayList possibleNodes = new ArrayList<>();
+ for (int i = 0; i < traces.size(); ++i) {
+ ArrayList trace = traces.get(i);
+ int currentPoint = tracesPtr.get(i);
+ if (currentPoint >= trace.size()) {
+ // deal with case when all the pointers are at the end of the trace
+ continue;
+ }
+ ConcurrentTraceNode m = trace.get(currentPoint);
+ if (possibleNodes.size() == 0) {
+ possibleNodes.add(m);
+ } else {
+ if (possibleNodes.get(0).getTimestamp() < m.getTimestamp()) {
+ continue;
+ }
+ if (possibleNodes.get(0).getTimestamp() == m.getTimestamp()) {
+ possibleNodes.add(m);
+ continue;
+ }
+ // when all possible nodes are larger
+ possibleNodes.clear();
+ possibleNodes.add(m);
+ }
+ }
+ if (possibleNodes.size() == 0) {
+ break;
+ }
+
+ // greedy resolution -> pick the first
+ result.add(possibleNodes.get(0));
+ int id = possibleNodes.get(0).getCurrentTraceId();
+ tracesPtr.set(id, tracesPtr.get(id) + 1);
+ // todo: proper resolution
+ // deal with possible nodes.
+ // allocate the first possible node
+ // will be the bulk of the logic without timestamps
+ //ConcurrentTraceNode selected = extractPossibleTraceNode(result, sharedVarAliasIds, sharedVarValues, possibleNodes);
+ //tracesPtr.set(selected.getCurrentTraceId(), tracesPtr.get(selected.getCurrentTraceId()) + 1);
+ }
+
+ resultTrace.postProcess();
+
+ return resultTrace;
+ }
+
+ /**
+ * Extracts a tracenode
+ * @param result
+ * @param sharedVarAliasIds
+ * @param sharedVarValues
+ * @param possibleNodes
+ * @return
+ */
+ private static ConcurrentTraceNode extractPossibleTraceNode(ArrayList result,
+ HashSet sharedVarAliasIds, HashMap sharedVarValues,
+ ArrayList possibleNodes) {
+ ConcurrentTraceNode selected = null;
+ for (int i = 0; i < possibleNodes.size(); ++i) {
+ ConcurrentTraceNode current = possibleNodes.get(i);
+ if (current.getWrittenVariables().size() > 0) {
+ continue;
+ }
+ List readValues = current.getReadVariables();
+ // check the string value to if satis
+ boolean satis = true;
+ for (VarValue val: readValues) {
+ assert(val.getAliasVarID() == null || (!sharedVarAliasIds.contains(val.getAliasVarID())
+ || sharedVarValues.containsKey(val.getAliasVarID())));
+ // when there isn't a match
+ if (val.getAliasVarID() != null && sharedVarAliasIds.contains(val.getAliasVarID())
+ && !val.getStringValue().equals(sharedVarValues.get(val.getAliasVarID()))) {
+ satis = false;
+ break;
+ }
+ }
+ if (satis) {
+ selected = current;
+ }
+ }
+ if (selected != null ) {
+ result.add(selected);
+ return selected;
+ }
+ // todo: this resolution is too greedy
+ // it doesn't work as we do not preserve W -> R
+ // may need to use some kind of constraint solving
+ // may not be possible until we indicate in the instrumentation
+ // to record RC vector (To determine what reads must occur before this write)
+ for (int i = 0; i < possibleNodes.size(); ++i) {
+ ConcurrentTraceNode current = possibleNodes.get(i);
+ if (current.getWrittenVariables().size() > 0) {
+ // take the first write
+ selected = current;
+ break;
+ }
+ }
+ if (selected == null) {
+ throw new RuntimeException("Failed to allocate trace node to concurrent trace");
+ }
+ return selected;
+ }
+
+
+
+
+
+ @Override
+ public TraceNode getTraceNode(int order) {
+ // TODO Auto-generated method stub
+ return this.getSequentialTrace().get(order - 1);
+ }
+
+
+
+ /**
+ * Finds data dependency relative to the concurrent trace
+ */
+ @Override
+ public TraceNode findDataDependency(TraceNode checkingNode, VarValue readVar) {
+ return this.findProducer(readVar, checkingNode);
+ }
+
+
+
+ @Override
+ public List findDataDependentee(TraceNode traceNode, VarValue writtenVar) {
+ List consumers = new ArrayList();
+ if (!(traceNode instanceof ConcurrentTraceNode)) {
+ traceNode = traceNode.getBound();
+ }
+
+ ConcurrentTraceNode cnode = (ConcurrentTraceNode) traceNode;
+ String varID = Variable.truncateSimpleID(writtenVar.getVarID());
+ String headID = Variable.truncateSimpleID(writtenVar.getAliasVarID());
+
+ for(int i=cnode.getOrder() + 1; i <= this.getSequentialTrace().size(); i++) {
+ ConcurrentTraceNode node = this.getSequentialTrace().get(i);
+ for(VarValue readVar: node.getReadVariables()) {
+
+ String rVarID = Variable.truncateSimpleID(readVar.getVarID());
+ String rHeadID = Variable.truncateSimpleID(readVar.getAliasVarID());
+
+ if(rVarID != null && rVarID.equals(varID)) {
+ consumers.add(node.getInitialTraceNode());
+ }
+
+ if(rHeadID != null && rHeadID.equals(headID)) {
+ consumers.add(node.getInitialTraceNode());
+ }
+
+ VarValue childValue = readVar.findVarValue(varID, headID);
+ if(childValue != null) {
+ consumers.add(node.getInitialTraceNode());
+ }
+
+ }
+ }
+ return consumers;
+
+ }
+
+
+
+ public ConcurrentTrace(String id) {
+ super(id);
+ }
+
+ public static ConcurrentTrace fromTraces(List traces) {
+ return null;
+ }
+
+ private List generateSequential(List traces) {
+ return null;
+ }
+
+ // convert the trace nodes to have read only and write only
+ private static List convertTraceNodes(Trace trace) {
+ ArrayList result = new ArrayList<>();
+ for (int i = 0; i < trace.size(); ++i) {
+ TraceNode current = trace.getTraceNode(i);
+ }
+ return null;
+ }
+
+ private static List getSharedVarIDs(List traces) {
+ HashSet sharedVariables = new HashSet<>();
+ List result = new ArrayList<>();
+ for (Trace trace: traces) {
+ for (TraceNode tnode: trace.getExecutionList()) {
+ List writtenValues = tnode.getWrittenVariables();
+ List readValues = tnode.getReadVariables();
+ List combined = new ArrayList<>();
+ for (VarValue wVal: writtenValues) {
+ combined.add(wVal);
+ }
+ for (VarValue rVal: readValues) {
+ combined.add(rVal);
+ }
+ for (VarValue value: combined) {
+ if (value.getAliasVarID() == null) continue;
+ if (sharedVariables.contains(value.getAliasVarID())) {
+ result.add(value);
+ continue;
+ }
+ sharedVariables.add(value.getAliasVarID());
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates a virtual trace given a concurrent trace
+ * @return
+ */
+ private static List constructSequentialTrace(List traces) {
+ Set sharedVariables = new HashSet<>();
+ HashMap valueMap = new HashMap<>();
+
+ sharedVariables.addAll(getSharedVarIDs(traces));
+ for (VarValue sharedVar: sharedVariables) {
+ valueMap.put(sharedVar.getAliasVarID(), "");
+ }
+
+ ArrayList sequentialTrace = new ArrayList<>();
+ // split all the traces into read + write trace nodes
+
+ int[] startingPtr = new int[traces.size()];
+ for (int i = 0; i < traces.size(); ++i) {
+ startingPtr[i] = 0;
+ }
+
+ while (true) {
+ // keep track of all traces -> if none of them have a matching node -> we can make no progress
+ boolean allNotStatis = true;
+ for (int i = 0; i < traces.size(); ++i) {
+ TraceNode current = traces.get(i).getTraceNode(startingPtr[i]);
+ List readValues = current.getReadVariables();
+ List writeValues = current.getWrittenVariables();
+ boolean satis = true;
+ // check if the read value matches the current variables value
+ for (VarValue val: readValues) {
+ if (sharedVariables.contains(val)
+ && valueMap.get(val) != val.getStringValue()) {
+ satis = false;
+ break;
+ }
+ }
+ if (!satis) continue;
+ for (int k = 0; k < traces.size(); ++k) {
+ if (k == i) continue;
+
+ }
+ // todo: check if this is blocking the other points
+ startingPtr[i] += 1;
+ sequentialTrace.add(current);
+ break;
+ }
+ boolean completed = true;
+ for (int i = 0; i < traces.size(); ++i) {
+ if (startingPtr[i] < traces.get(i).size()) {
+ completed = false;
+ break;
+ }
+ }
+ if (completed) break;
+ }
+ return sequentialTrace;
+
+ }
+}
\ No newline at end of file
diff --git a/microbat/src/main/microbat/model/trace/ConcurrentTraceNode.java b/microbat/src/main/microbat/model/trace/ConcurrentTraceNode.java
new file mode 100644
index 000000000..d8be87321
--- /dev/null
+++ b/microbat/src/main/microbat/model/trace/ConcurrentTraceNode.java
@@ -0,0 +1,376 @@
+package microbat.model.trace;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.editors.text.TextFileDocumentProvider;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.model.BreakPoint;
+import microbat.model.BreakPointValue;
+import microbat.model.UserInterestedVariables;
+import microbat.model.value.VarValue;
+import microbat.model.variable.Variable;
+import microbat.util.JavaUtil;
+import microbat.views.ReferenceAnnotation;
+
+/**
+ * Represents a concurrent trace node
+ *
+ * Adds additional logic for determining dependencies
+ * @author Gabau
+ *
+ */
+public class ConcurrentTraceNode extends TraceNode {
+
+ public static TraceNode getIniTraceNode(TraceNode traceNode) {
+ if (traceNode instanceof ConcurrentTraceNode) {
+ return ((ConcurrentTraceNode) traceNode).getInitialTraceNode();
+ }
+ return traceNode;
+ }
+
+ /**
+ * Obtains the original trace nodes given a list
+ * of a mix of concurrent trace nodes and trace nodes
+ * @param traceNodes
+ * @return
+ */
+ public static List convert(List traceNodes) {
+ List result = new LinkedList<>();
+ for (TraceNode traceNode: traceNodes) {
+ if (result instanceof ConcurrentTraceNode) {
+ result.add(((ConcurrentTraceNode) traceNode).getInitialTraceNode());
+ } else {
+ result.add(traceNode);
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * The index of the trace that this trace node is in
+ */
+ protected int currentTraceId = 0;
+ protected boolean isAtomic = false;
+ protected TraceNode initialTraceNode;
+ // the sequential order it is in
+ protected int concurrentOrder;
+ /**
+ * The thread id
+ */
+ protected long threadId = 0;
+ private ConcurrentTrace concurrentTrace;
+
+ public void setConcurrentTrace(ConcurrentTrace concurrentTrace) {
+ this.concurrentTrace = concurrentTrace;
+ }
+
+ public ConcurrentTrace getConcurrentTrace() {
+ return this.concurrentTrace;
+ }
+
+ public long getCurrentThread() {
+ return initialTraceNode.getTrace().getThreadId();
+ }
+
+
+
+
+ @Override
+ public ConcurrentTraceNode getBound() {
+ return this;
+ }
+
+
+
+
+ @Override
+ public TraceNode getStepInNext() {
+ return initialTraceNode.getStepInNext();
+ }
+
+
+
+
+ @Override
+ public TraceNode getStepInPrevious() {
+ return initialTraceNode.getStepInPrevious();
+ }
+
+
+
+
+ @Override
+ public TraceNode getStepOverNext() {
+ return initialTraceNode.getStepOverNext();
+ }
+
+
+
+
+ @Override
+ public TraceNode getStepOverPrevious() {
+ return initialTraceNode.getStepOverPrevious();
+ }
+
+
+
+ /**
+ * Returns the order this node is in in the global order.
+ */
+ @Override
+ public int getOrder() {
+ return concurrentOrder;
+ }
+
+ /**
+ * Returns the order of this trace node within the trace
+ * @return
+ */
+ public int getInitialOrder() {
+ return initialTraceNode.getOrder();
+ }
+
+ @Override
+ public void setOrder(int concurrentOrder) {
+ this.concurrentOrder = concurrentOrder;
+ }
+
+
+ /**
+ * Pointer to the trace node which writes to this trace node / reads to this trace node
+ * nullable
+ */
+ private ConcurrentTraceNode linkedTraceNode;
+ public int getCurrentTraceId() {
+ return currentTraceId;
+ }
+
+ /**
+ * Link the read and write trace nodes
+ * @return
+ */
+ public ConcurrentTraceNode getLinkedTraceNode() {
+ return linkedTraceNode;
+ }
+
+
+ public ConcurrentTraceNode(BreakPoint breakPoint, BreakPointValue programState, int order, Trace trace,
+ String bytecode, long threadId) {
+ super(breakPoint, programState, order, trace, bytecode);
+ this.threadId = threadId;
+ // TODO Auto-generated constructor stub
+ }
+
+ public ConcurrentTraceNode(TraceNode node, int currentTraceId, long threadId) {
+ super(node.getBreakPoint(), node.getProgramState(),
+ node.getOrder(), node.getTrace(), node.getBytecode());
+ this.setTimestamp(node.getTimestamp());
+ this.currentTraceId = currentTraceId;
+ this.threadId = threadId;
+ }
+
+ /**
+ * Due to the way concurrency works, we separate the read and the
+ * writes, unless there is a synchronisation step.
+ * @return
+ */
+ public boolean isWrite() {
+ return this.writtenVariables.size() > 0;
+ }
+
+ public boolean isAtomic() {
+ return isAtomic;
+ }
+
+
+ /**
+ * This implementation currently means that if this
+ * node only has write, we get the written VarValues that match the selection.
+ *
+ */
+ @Override
+ public List getWrongReadVars(UserInterestedVariables interestedVariables) {
+ if (this.isAtomic()) {
+ return super.getWrongReadVars(interestedVariables);
+ }
+ if (this.isWrite()) {
+
+ List vars = new ArrayList<>();
+ for(VarValue var: getWrittenVariables()){
+ if(interestedVariables.contains(var)){
+ vars.add(var);
+ }
+ List children = var.getAllDescedentChildren();
+ for(VarValue child: children){
+ if(interestedVariables.contains(child)){
+ if(!vars.contains(child)){
+ vars.add(child);
+ }
+ }
+ }
+ }
+
+ return vars;
+ }
+ return super.getWrongReadVars(interestedVariables);
+ }
+
+ public TraceNode getInitialTraceNode() {
+ return this.initialTraceNode;
+ }
+
+//
+// @Override
+// public TraceNode findProducer(VarValue varValue, TraceNode startNode) {
+//
+// // check if it is on heap
+// String s = varValue.getAliasVarID();
+// boolean isLocalVar = varValue.isLocalVariable();
+// boolean isOnHeap = !isLocalVar;
+// if (!(startNode instanceof ConcurrentTraceNode)) {
+// throw new RuntimeException("Wrong checking node type");
+// }
+// ConcurrentTraceNode concNode = (ConcurrentTraceNode) startNode;
+// int j = concNode.getOrder();
+// if (!concNode.isAtomic() && concNode.getWrittenVariables().size() > 0) {
+// return concNode.getLinkedTraceNode();
+// }
+//
+// // find the individual trace to use the find data dependenccy
+// String varID = Variable.truncateSimpleID(varValue.getVarID());
+// String headID = Variable.truncateSimpleID(varValue.getAliasVarID());
+//
+//
+// for(int i=j-2; i>=1; i--) {
+// ConcurrentTraceNode node = this.getSequentialTrace().get(i);
+// for(VarValue writtenValue: node.getWrittenVariables()) {
+//
+// if (isOnHeap && writtenValue.getAliasVarID() != null
+// && writtenValue.getAliasVarID().equals(varValue.getAliasVarID())) {
+// return node;
+// }
+// if (!isOnHeap && concNode.getCurrentThread() != node.getCurrentThread()) {
+// // when the value is on a different thread
+// continue;
+// }
+//
+// String wVarID = Variable.truncateSimpleID(writtenValue.getVarID());
+// String wHeadID = Variable.truncateSimpleID(writtenValue.getAliasVarID());
+//
+// if(wVarID != null && wVarID.equals(varID) && node.getCurrentTraceId()
+// == concNode.getCurrentTraceId()) {
+// return node;
+// }
+//
+// if(wHeadID != null && wHeadID.equals(headID)) {
+// return node;
+// }
+//
+// VarValue childValue = writtenValue.findVarValue(varID, headID);
+// if(childValue != null) {
+// return node;
+// }
+//
+// }
+// }
+// return null;
+// }
+//
+//
+
+
+
+ /**
+ * Split a given trace node into read and write concurrent
+ * @param node
+ * @return
+ */
+ public static List splitTraceNode(TraceNode node, int traceId, long threadId) {
+ ArrayList result = new ArrayList<>();
+ ConcurrentTraceNode r = new ConcurrentTraceNode(node, traceId, threadId);
+ node.setBoundTraceNode(r);
+ r.setReadVariables(node.getReadVariables());
+ r.setWrittenVariables(node.getWrittenVariables());
+ r.initialTraceNode = node;
+ r.isAtomic = true;
+ return List.of(r);
+
+// if (node.getReadVariables().size() == 0 && node.getWrittenVariables().size() == 0) {
+// ConcurrentTraceNode r = new ConcurrentTraceNode(node, threadId);
+// node.setConcurrentTraceNode(r);
+// return List.of(r);
+// }
+// if (node.getReadVariables().size() > 0) {
+// ConcurrentTraceNode readVariables = new ConcurrentTraceNode(node, threadId);
+// readVariables.setReadVariables(node.getReadVariables());
+// node.setConcurrentTraceNode(readVariables);
+// result.add(readVariables);
+// }
+// if (node.getWrittenVariables().size() > 0) {
+// ConcurrentTraceNode writeVariables = new ConcurrentTraceNode(node, threadId);
+// writeVariables.setWrittenVariables(node.getWrittenVariables());
+// result.add(writeVariables);
+// if (result.size() == 2) {
+// result.get(1).linkedTraceNode = result.get(0);
+// result.get(0).linkedTraceNode = result.get(1);
+// }
+// }
+// return result;
+ }
+
+ @Override
+ public TraceNode getInvocationMethodOrDominator() {
+ TraceNode invcationParent = super.getInvocationMethodOrDominator();
+ if (invcationParent != null) return invcationParent;
+ ConcurrentTrace concTrace = this.getConcurrentTrace();
+ Trace currTrace = this.initialTraceNode.getTrace();
+ ThreadId threadId = currTrace.getInnerThreadId();
+ if (threadId == null) return null;
+ if (threadId.getSpawnOrder() != -1) {
+ Trace parentTrace = concTrace.getCorrespondingTrace(threadId.getParent());
+ if (parentTrace != null) return parentTrace.getTraceNode(threadId.getSpawnOrder());
+ }
+
+ // find the node which invoked this thread
+ return null;
+
+ }
+
+ @Override
+ public TraceNode getDataDominator(VarValue readVar) {
+ return this.getConcurrentTrace().findDataDependency(this, readVar);
+ }
+
+ @Override
+ public String toString() {
+ // TODO Auto-generated method stub
+ return "Current threadId: " + this.currentTraceId + " " + super.toString();
+ }
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/microbat/src/main/microbat/model/trace/Trace.java b/microbat/src/main/microbat/model/trace/Trace.java
index 81e699686..5ee9a292c 100644
--- a/microbat/src/main/microbat/model/trace/Trace.java
+++ b/microbat/src/main/microbat/model/trace/Trace.java
@@ -1,8 +1,10 @@
package microbat.model.trace;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
@@ -12,6 +14,7 @@
import org.eclipse.jdt.core.dom.CompilationUnit;
import microbat.codeanalysis.ast.LocalVariableScopes;
+import microbat.instrumentation.model.id.ThreadId;
import microbat.model.AttributionVar;
import microbat.model.BreakPoint;
import microbat.model.Scope;
@@ -36,6 +39,51 @@ public class Trace {
private boolean isMain;
private String threadName;
private String id;
+ private ThreadId innerThreadId;
+ private long memoryUsed = -1;
+
+ public void setMemoryUsed(long memoryUsed) {
+ this.memoryUsed = memoryUsed;
+ }
+
+ public long getMemoryUsed() {
+ return this.memoryUsed;
+ }
+
+ /**
+ * Used to detect deadlocks
+ */
+ private List acquiredLocks = null;
+ private Long acquiringLock = null;
+
+ public Long getAcquiringLock() {
+ return this.acquiringLock;
+ }
+
+ public List getAcquiredLocks() {
+ return this.acquiredLocks;
+ }
+
+ public void setAcquiredLocks(Collection values) {
+ this.acquiredLocks = new LinkedList(values);
+ }
+
+ public void setAcquiringLock(Long value) {
+ this.acquiringLock = value;
+ }
+
+ /**
+ * Getter for inner thread id. Representation of a
+ * thread id using parent counter.
+ * @return
+ */
+ public ThreadId getInnerThreadId() {
+ return innerThreadId;
+ }
+
+ public void setInnerThreadId(ThreadId threadId) {
+ this.innerThreadId = threadId;
+ }
/**
* This variable is to trace whether the variables in different lines are the same
@@ -254,6 +302,9 @@ public void conductStateDiff() {
* @return
*/
public TraceNode findDataDependency(TraceNode checkingNode, VarValue readVar) {
+ if (checkingNode.getBound() != null) {
+ return checkingNode.getBound().getDataDominator(readVar);
+ }
return findProducer(readVar, checkingNode);
}
diff --git a/microbat/src/main/microbat/model/trace/TraceNode.java b/microbat/src/main/microbat/model/trace/TraceNode.java
index 39bdd60ab..1cc4356d1 100644
--- a/microbat/src/main/microbat/model/trace/TraceNode.java
+++ b/microbat/src/main/microbat/model/trace/TraceNode.java
@@ -57,7 +57,6 @@ public class TraceNode{
// private Map> dataDominators = new HashMap<>();
// private Map> dataDominatees = new HashMap<>();
-
private TraceNode controlDominator;
private List controlDominatees = new ArrayList<>();
@@ -96,6 +95,17 @@ public class TraceNode{
private transient double sliceBreakerProbability = 0;
+ // Utility binding
+ private ConcurrentTraceNode boundTraceNode;
+
+ public void setBoundTraceNode(ConcurrentTraceNode boundNode) {
+ this.boundTraceNode = boundNode;
+ }
+
+ public ConcurrentTraceNode getBound() {
+ return this.boundTraceNode;
+ }
+
/**
* the first element of the pair is the read variable list, the second element is the
* written variable list.
@@ -169,6 +179,9 @@ public TraceNode getDataDominator(VarValue readVar) {
// }
//
// return dataDominator;
+ if (this.boundTraceNode != null) {
+ return this.boundTraceNode.getDataDominator(readVar);
+ }
return this.trace.findDataDependency(this, readVar);
}
@@ -270,6 +283,8 @@ public boolean equals(Object obj) {
TraceNode other = (TraceNode) obj;
if (order != other.order)
return false;
+ if (trace.getThreadId() != other.getTrace().getThreadId())
+ return false;
return true;
}
@@ -492,7 +507,7 @@ public String getShortMethodSignature() {
public Map getDataDominators() {
Map dataDominators = new HashMap<>();
for(VarValue readVar: this.getReadVariables()){
- TraceNode dominator = this.trace.findDataDependency(this, readVar);
+ TraceNode dominator = this.getDataDominator(readVar);
if(dominator != null){
dataDominators.put(dominator, readVar);
}
@@ -503,8 +518,12 @@ public Map getDataDominators() {
public Map getDataDominatee() {
Map dataDominatees = new HashMap<>();
+ Trace trace = this.trace;
+ if (this.getBound() != null) {
+ trace = this.getBound().getConcurrentTrace();
+ }
for(VarValue writtenVar: this.getWrittenVariables()){
- List dominatees = this.trace.findDataDependentee(this, writtenVar);
+ List dominatees = trace.findDataDependentee(this, writtenVar);
for(TraceNode dominatee: dominatees){
dataDominatees.put(dominatee, writtenVar);
}
diff --git a/microbat/src/main/microbat/preference/MicrobatPreference.java b/microbat/src/main/microbat/preference/MicrobatPreference.java
index a60347065..9da70a8b2 100644
--- a/microbat/src/main/microbat/preference/MicrobatPreference.java
+++ b/microbat/src/main/microbat/preference/MicrobatPreference.java
@@ -21,7 +21,9 @@
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
+import java_cup.internal_error;
import microbat.Activator;
+import microbat.instrumentation.instr.aggreplay.ReplayMode;
import microbat.util.SWTFactory;
import microbat.util.Settings;
@@ -50,6 +52,7 @@ public void init(IWorkbench workbench) {
this.defaultRecordSnapshot = Activator.getDefault().getPreferenceStore().getString(RECORD_SNAPSHORT);
this.defaultRunWithDebugMode = Activator.getDefault().getPreferenceStore().getString(RUN_WITH_DEBUG_MODE);
this.defaultAdvancedDetailInspector = Activator.getDefault().getPreferenceStore().getString(APPLY_ADVANCE_INSPECTOR);
+ this.defaultTimeoutLong = Long.parseLong(Activator.getDefault().getPreferenceStore().getString(TIMEOUT));
this.defaultStepLimit = getStepLimit();
this.defaultRunTest = Activator.getDefault().getPreferenceStore().getString(RUN_TEST);
this.defaultVariableLayer = getVariableValue();
@@ -57,6 +60,7 @@ public void init(IWorkbench workbench) {
this.defaultApplyRecodingOptimization = Activator.getDefault().getPreferenceStore().getString(RECORDING_OPTIMIZATION);
this.defaultEnableMethodSplitting = Activator.getDefault().getPreferenceStore().getBoolean(REQUIRE_METHOD_SPLITTING);
this.defaultRunWithDebugMode = Activator.getDefault().getPreferenceStore().getString(RUN_WITH_DEBUG_MODE);
+ this.defaultReplayMode = ReplayMode.parse(Activator.getDefault().getPreferenceStore().getString(REPLAY_MODE));
}
public static String getStepLimit() {
@@ -85,7 +89,10 @@ public static String getValue(String key) {
public static final String RECORDING_OPTIMIZATION = "recording_optimization";
public static final String REQUIRE_METHOD_SPLITTING = "enableMethodSplitting";
public static final String SUPPORT_CONCURRENT_TRACE = "supportConcurrentTrace";
+ public static final String AGGREPLAY_CONCURRENT_RECORDING = "aggrePlayConcurrentRecording";
public static final String RUN_WITH_DEBUG_MODE = "runWithDebugMode";
+ public static final String TIMEOUT = "timeOut";
+ public static final String REPLAY_MODE = "replay_mode";
private Combo projectCombo;
private Text lanuchClassText;
@@ -94,6 +101,7 @@ public static String getValue(String key) {
private Text lineNumberText;
private Text stepLimitText;
private Text variableLayerText;
+ private Text timeOutText;
private Button recordSnapshotButton;
private Button supportConcurrentTraceButton;
private Button recordingOptimizationButton;
@@ -102,6 +110,7 @@ public static String getValue(String key) {
private Button runWithDebugModeButton;
private Button enableMethodSplittingButton;
private Text java7HomePathText;
+ private Combo replayModeSelectorCombo;
private String defaultTargetProject = "";
private String defaultLanuchClass = "";
@@ -117,6 +126,8 @@ public static String getValue(String key) {
private String defaultJava7HomePath;
private String defaultApplyRecodingOptimization;
private String defaultRunWithDebugMode = "false";
+ private Long defaultTimeoutLong = 10000L;
+ private ReplayMode defaultReplayMode = ReplayMode.AGGR;
private boolean defaultEnableMethodSplitting;
@Override
@@ -181,6 +192,19 @@ private void createSettingGroup(Composite parent){
variableLayerText.setLayoutData(variableLayerTextData);
variableLayerText.setToolTipText("how many layers of variable children does the debugger need to retrieve, -1 means infinite.");
+ Label timeOutLabel = new Label(settingGroup, SWT.NONE);
+ timeOutLabel.setText("Timeout: ");
+
+
+ GridData timeOutLayerTextData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ timeOutLayerTextData.horizontalSpan = 2;
+ timeOutText = new Text(settingGroup, SWT.BORDER);
+ timeOutText.setText(this.defaultTimeoutLong + "");
+ timeOutText.setLayoutData(variableLayerTextData);
+ timeOutText.setToolTipText("How long do you want the timeout, -1 means infinite.");
+
+
+
supportConcurrentTraceButton = new Button(settingGroup, SWT.CHECK);
supportConcurrentTraceButton.setText("Support concurrent trace");
GridData supportConcurrentTraceButtonData = new GridData(SWT.FILL, SWT.FILL, true, false);
@@ -223,6 +247,26 @@ private void createSettingGroup(Composite parent){
enableMethodSplittingButton = SWTFactory.createCheckbox(settingGroup, "Enable method splitting function", 2);
enableMethodSplittingButton.setSelection(this.defaultEnableMethodSplitting);
+
+ createReplaySelector(parent);
+ }
+
+
+ private void createReplaySelector(Composite parent) {
+ Label replaySelectorLabel = new Label(parent, SWT.BORDER);
+ replaySelectorLabel.setText("Select replay mode");
+ replaySelectorLabel.setToolTipText("Strict - Chance of deadlock when sync methods used");
+ this.replayModeSelectorCombo = new Combo(parent, SWT.BORDER);
+ ReplayMode[] replayModes = ReplayMode.values();
+ String[] dataStrings = new String[replayModes.length];
+ for (int i = 0; i < dataStrings.length; ++i) {
+ dataStrings[i] = replayModes[i].toString();
+ }
+ replayModeSelectorCombo.setItems(dataStrings);
+ replayModeSelectorCombo.setText(defaultReplayMode.toString());
+ GridData comboData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ comboData.horizontalSpan = 2;
+ this.replayModeSelectorCombo.setLayoutData(comboData);
}
private void createSeedStatementGroup(Composite parent){
@@ -284,6 +328,7 @@ public boolean performOk(){
preferences.put(TARGET_PORJECT, this.projectCombo.getText());
preferences.put(LANUCH_CLASS, this.lanuchClassText.getText());
preferences.put(TEST_METHOD, this.testMethodText.getText());
+ preferences.put(TIMEOUT, this.timeOutText.getText());
// preferences.put(CLASS_NAME, this.classNameText.getText());
// preferences.put(LINE_NUMBER, this.lineNumberText.getText());
preferences.put(RECORD_SNAPSHORT, String.valueOf(this.recordSnapshotButton.getSelection()));
@@ -296,10 +341,12 @@ public boolean performOk(){
preferences.putBoolean(REQUIRE_METHOD_SPLITTING, this.enableMethodSplittingButton.getSelection());
preferences.put(SUPPORT_CONCURRENT_TRACE, String.valueOf(this.supportConcurrentTraceButton.getSelection()));
preferences.put(RUN_WITH_DEBUG_MODE, String.valueOf(this.runWithDebugModeButton.getSelection()));
+ preferences.put(REPLAY_MODE, this.replayModeSelectorCombo.getText());
Activator.getDefault().getPreferenceStore().putValue(TARGET_PORJECT, this.projectCombo.getText());
Activator.getDefault().getPreferenceStore().putValue(LANUCH_CLASS, this.lanuchClassText.getText());
Activator.getDefault().getPreferenceStore().putValue(TEST_METHOD, this.testMethodText.getText());
+ Activator.getDefault().getPreferenceStore().putValue(TIMEOUT, timeOutText.getText());
// Activator.getDefault().getPreferenceStore().putValue(CLASS_NAME, this.classNameText.getText());
// Activator.getDefault().getPreferenceStore().putValue(LINE_NUMBER, this.lineNumberText.getText());
Activator.getDefault().getPreferenceStore().putValue(RECORD_SNAPSHORT, String.valueOf(this.recordSnapshotButton.getSelection()));
@@ -312,7 +359,7 @@ public boolean performOk(){
Activator.getDefault().getPreferenceStore().putValue(REQUIRE_METHOD_SPLITTING, String.valueOf(this.enableMethodSplittingButton.getSelection()));
Activator.getDefault().getPreferenceStore().putValue(SUPPORT_CONCURRENT_TRACE, String.valueOf(this.supportConcurrentTraceButton.getSelection()));
Activator.getDefault().getPreferenceStore().putValue(RUN_WITH_DEBUG_MODE, String.valueOf(this.runWithDebugModeButton.getSelection()));
-
+ Activator.getDefault().getPreferenceStore().putValue(REPLAY_MODE, this.replayModeSelectorCombo.getText());
confirmChanges();
return true;
@@ -323,6 +370,7 @@ private void confirmChanges(){
Settings.projectName = this.projectCombo.getText();
Settings.launchClass = this.lanuchClassText.getText();
Settings.testMethod = this.testMethodText.getText();
+ Settings.timeLimit = Long.parseLong(this.timeOutText.getText());
// Settings.buggyClassName = this.classNameText.getText();
// Settings.buggyLineNumber = this.lineNumberText.getText();
Settings.isRecordSnapshot = this.recordSnapshotButton.getSelection();
@@ -333,6 +381,7 @@ private void confirmChanges(){
Settings.applyLibraryOptimization = this.recordingOptimizationButton.getSelection();
Settings.supportConcurrentTrace = this.supportConcurrentTraceButton.getSelection();
Settings.isRunWtihDebugMode = this.runWithDebugModeButton.getSelection();
+ Settings.replayMode = ReplayMode.parse(this.replayModeSelectorCombo.getText());
}
private String[] getProjectsInWorkspace(){
diff --git a/microbat/src/main/microbat/util/MicroBatUtil.java b/microbat/src/main/microbat/util/MicroBatUtil.java
index 5b9616601..30d4032e8 100644
--- a/microbat/src/main/microbat/util/MicroBatUtil.java
+++ b/microbat/src/main/microbat/util/MicroBatUtil.java
@@ -37,6 +37,14 @@ public class MicroBatUtil {
private static Logger log = LoggerFactory.getLogger(MicroBatUtil.class);
private MicroBatUtil(){}
+ public static boolean checkTestResult(String msg) {
+ int sIdx = msg.indexOf(";");
+ if (sIdx < 0 || msg.length() < sIdx) {
+ return false;
+ }
+ return Boolean.valueOf(msg.substring(0, sIdx));
+ }
+
public static String getProjectPath(String projectName){
IWorkspaceRoot myWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject iProject = myWorkspaceRoot.getProject(projectName);
diff --git a/microbat/src/main/microbat/util/Settings.java b/microbat/src/main/microbat/util/Settings.java
index 8d6edee12..b0473f0ac 100644
--- a/microbat/src/main/microbat/util/Settings.java
+++ b/microbat/src/main/microbat/util/Settings.java
@@ -2,13 +2,16 @@
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Stack;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.CompilationUnit;
+import fj.P;
import microbat.Activator;
import microbat.handler.CheckingState;
+import microbat.instrumentation.instr.aggreplay.ReplayMode;
import microbat.model.UserInterestedVariables;
import microbat.model.trace.PotentialCorrectPatternList;
import microbat.preference.MicrobatPreference;
@@ -19,11 +22,20 @@ public class Settings {
public static String launchClass;
public static String testMethod;
+ /**
+ * Used to indicate the dump file used for record and replay.
+ * If the dump file is not provided, a temp file is used.
+ */
+ public static Optional concurrentDumpFile = Optional.empty();
+
public static boolean isRecordSnapshot;
public static boolean isApplyAdvancedInspector;
public static boolean isRunTest;
public static boolean isRunWtihDebugMode;
public static int stepLimit;
+ public static long timeLimit;
+ public static ReplayMode replayMode;
+ public static boolean isForce3Or4;
private static Integer variableLayer;
@@ -54,6 +66,7 @@ public class Settings {
static{
if(Activator.getDefault() != null){
try{
+ replayMode = ReplayMode.parse(Activator.getDefault().getPreferenceStore().getString(MicrobatPreference.REPLAY_MODE));
projectName = Activator.getDefault().getPreferenceStore().getString(MicrobatPreference.TARGET_PORJECT);
launchClass = Activator.getDefault().getPreferenceStore().getString(MicrobatPreference.LANUCH_CLASS);
testMethod = Activator.getDefault().getPreferenceStore().getString(MicrobatPreference.TEST_METHOD);
@@ -68,6 +81,16 @@ public class Settings {
if(stepLimit == 0){
stepLimit = 5000;
}
+ String timeLimitString = Activator.getDefault().getPreferenceStore().getString(MicrobatPreference.TIMEOUT);
+ try {
+ timeLimit = Long.valueOf(timeLimitString);
+ } catch (NumberFormatException except) {
+
+ }
+
+ if (timeLimit == 0) {
+ timeLimit = 10000L;
+ }
String varLayerString = Activator.getDefault().getPreferenceStore().getString(MicrobatPreference.VARIABLE_LAYER);
variableLayer = Integer.valueOf(varLayerString);
@@ -120,5 +143,6 @@ public static void setVariableLayer(Integer level){
public static HashMap iCompilationUnitMap = new HashMap<>();
public static boolean enableLoopInference = true;
public static boolean supportConcurrentTrace;
+ public static boolean supportAggrePlayTrace;
}
diff --git a/microbat/src/main/microbat/views/ConcurrentTraceView.java b/microbat/src/main/microbat/views/ConcurrentTraceView.java
index a437307db..8a434d1bf 100644
--- a/microbat/src/main/microbat/views/ConcurrentTraceView.java
+++ b/microbat/src/main/microbat/views/ConcurrentTraceView.java
@@ -72,6 +72,7 @@
public class ConcurrentTraceView extends TraceView {
protected Map traceMap;
+ protected Map treeViewMap = new HashMap<>();
protected Trace curTrace;
public Trace getCurrentTrace() {
@@ -198,6 +199,8 @@ public void jumpToNode(Trace trace, int order, boolean refreshProgramState) {
}
/** keep the original expanded list */
+ curTreeViewer = this.treeViewMap.get(trace.getThreadId());
+
Object[] expandedElements = curTreeViewer.getExpandedElements();
for (Object obj : expandedElements) {
TraceNode tn = (TraceNode) obj;
@@ -290,6 +293,7 @@ public void creatPart(Composite parent, Trace trace) {
String threadName = trace.getThreadName();
group.setText(threadName != null ? threadName : "NA");
TreeViewer viewer = new TreeViewer(group, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
+ treeViewMap.put(trace.getThreadId(), viewer);
viewer.setContentProvider(new TraceContentProvider());
viewer.setLabelProvider(new TraceLabelProvider());
viewerList.add(viewer);
@@ -487,7 +491,7 @@ public void menuAboutToShow(IMenuManager manager) {
viewer.getTree().setMenu(menuMgr.createContextMenu(viewer.getTree()));
}
- protected void otherViewsBehavior(TraceNode node) {
+ public void otherViewsBehavior(TraceNode node) {
DebugFeedbackView feedbackView = MicroBatViews.getDebugFeedbackView();
if (this.refreshProgramState) {
@@ -502,7 +506,7 @@ protected void otherViewsBehavior(TraceNode node) {
}
public void updateData() {
-
+ treeViewMap.clear();
Control[] childs = tracePanel.getChildren();
for (int k = 0; k < childs.length; k++) {
childs[k].dispose();
diff --git a/microbat_examples/resources/instrumentator.jar b/microbat_examples/resources/instrumentator.jar
index 4e7b7bf2f..a1046e781 100644
Binary files a/microbat_examples/resources/instrumentator.jar and b/microbat_examples/resources/instrumentator.jar differ
diff --git a/microbat_instrumentator/.classpath b/microbat_instrumentator/.classpath
index 75ac85ffd..e461609b5 100644
--- a/microbat_instrumentator/.classpath
+++ b/microbat_instrumentator/.classpath
@@ -11,7 +11,7 @@
-
+
diff --git a/microbat_instrumentator/.settings/org.eclipse.jdt.core.prefs b/microbat_instrumentator/.settings/org.eclipse.jdt.core.prefs
index 980b98c1d..ace45ceef 100644
--- a/microbat_instrumentator/.settings/org.eclipse.jdt.core.prefs
+++ b/microbat_instrumentator/.settings/org.eclipse.jdt.core.prefs
@@ -1,12 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/microbat_instrumentator/lib/instrumentator_agent_v02.jar b/microbat_instrumentator/lib/instrumentator_agent_v02.jar
index 2e2b0e5af..8b55a5568 100644
Binary files a/microbat_instrumentator/lib/instrumentator_agent_v02.jar and b/microbat_instrumentator/lib/instrumentator_agent_v02.jar differ
diff --git a/microbat_instrumentator/src/core/microbat/model/trace/Trace.java b/microbat_instrumentator/src/core/microbat/model/trace/Trace.java
index 6447d70d5..4d45c9478 100644
--- a/microbat_instrumentator/src/core/microbat/model/trace/Trace.java
+++ b/microbat_instrumentator/src/core/microbat/model/trace/Trace.java
@@ -1,7 +1,9 @@
package microbat.model.trace;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -15,6 +17,7 @@
import microbat.codeanalysis.bytecode.CFGConstructor;
import microbat.codeanalysis.bytecode.CFGNode;
import microbat.codeanalysis.bytecode.MethodFinderByLine;
+import microbat.instrumentation.model.id.ThreadId;
import microbat.model.BreakPoint;
import microbat.model.ClassLocation;
import microbat.model.ControlScope;
@@ -37,7 +40,50 @@ public class Trace {
private long threadId;
private boolean isMain;
private String threadName;
-
+ private ThreadId innerThreadId;
+ private long memoryUsed = -1;
+ /**
+ * Used to detect deadlocks
+ */
+ private List acquiredLocks = null;
+ private Long acquiringLock = null;
+
+ public void setMemoryUsed(long memoryUsed) {
+ this.memoryUsed = memoryUsed;
+ }
+
+ public long getMemoryUsed() {
+ return this.memoryUsed;
+ }
+
+ public Long getAcquiringLock() {
+ if (this.acquiringLock == null) {
+ return -1L;
+ }
+ return this.acquiringLock;
+ }
+
+ public List getAcquiredLocks() {
+ return this.acquiredLocks;
+ }
+
+ public void setAcquiredLocks(Collection values) {
+ this.acquiredLocks = new LinkedList(values);
+ }
+
+ public void setAcquiringLock(Long value) {
+ this.acquiringLock = value;
+ }
+
+
+ public ThreadId getInnerThreadId() {
+ return innerThreadId;
+ }
+
+ public void setInnerThreadId(ThreadId threadId) {
+ this.innerThreadId = threadId;
+ }
+
/**
* This variable is to trace whether the variables in different lines are the same
* local variable.
@@ -414,8 +460,9 @@ public void setIncludedLibraryClasses(List includedLibraryClasses) {
this.includedLibraryClasses = includedLibraryClasses;
}
+ // TODO(Gab): Remove the botch Linked list (used cause of an unknown concurrent access bug)
public List getExcludedLibraryClasses() {
- return CollectionUtils.nullToEmpty(excludedLibraryClasses);
+ return new LinkedList<>(CollectionUtils.nullToEmpty(excludedLibraryClasses));
}
public void setExcludedLibraryClasses(List excludedLibraryClasses) {
diff --git a/microbat_instrumentator/src/core/microbat/model/trace/TraceNode.java b/microbat_instrumentator/src/core/microbat/model/trace/TraceNode.java
index e32a6bbfa..3c67eb563 100644
--- a/microbat_instrumentator/src/core/microbat/model/trace/TraceNode.java
+++ b/microbat_instrumentator/src/core/microbat/model/trace/TraceNode.java
@@ -5,6 +5,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -93,6 +94,7 @@ public class TraceNode{
private long timestamp;
private String bytecode;
+
public TraceNode(BreakPoint breakPoint, BreakPointValue programState, int order, Trace trace, String bytecode) {
this(breakPoint, programState, order, trace, -1, -1, System.currentTimeMillis(), bytecode);
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/Agent.java b/microbat_instrumentator/src/main/microbat/instrumentation/Agent.java
index bd4ecf173..67d025fb5 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/Agent.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/Agent.java
@@ -6,6 +6,7 @@
import java.util.List;
import microbat.instrumentation.filter.GlobalFilterChecker;
+import microbat.instrumentation.instr.aggreplay.TimeoutThread;
import microbat.instrumentation.runtime.ExecutionTracer;
import microbat.instrumentation.runtime.IExecutionTracer;
@@ -76,12 +77,37 @@ public void run() {
});
}
+ public static void _forceProgramStop(String programMsg) {
+ String currentThreadName = Thread.currentThread().getName();
+ assert(currentThreadName.equals(TimeoutThread.ID));
+ Agent.programMsg = programMsg;
+ for (IExecutionTracer tracer: ExecutionTracer.getAllThreadStore()) {
+ if (tracer instanceof ExecutionTracer) {
+ ExecutionTracer executionTracer = (ExecutionTracer) tracer;
+ executionTracer.setLock();
+ }
+ }
+ ExecutionTracer.getMainThreadStore().lock();
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ stop();
+ ExecutionTracer.getMainThreadStore().unLock();
+ Runtime.getRuntime().exit(1); // force program to exit to avoid getting stuck by background running threads.
+ }
+
/**
* This method will be instrumented at the end of main() method.
* @param programMsg
*/
public static void _exitProgram(String programMsg) {
- if(Thread.currentThread().getName().equals("main")) {
+ // this is necessary to handle exitProgram called in test case
+ if (!ExecutionTracer.isRecordingOrStarted() && !ExecutionTracer.isShutdown()) return;
+ String currentThreadName = Thread.currentThread().getName();
+ if(currentThreadName.equals("main") || currentThreadName.equals(TimeoutThread.ID)) {
ExecutionTracer.getMainThreadStore().lock();
Agent.programMsg = programMsg;
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/AgentFactory.java b/microbat_instrumentator/src/main/microbat/instrumentation/AgentFactory.java
index fe1a74007..b447c4849 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/AgentFactory.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/AgentFactory.java
@@ -5,6 +5,10 @@
import microbat.instrumentation.AgentParams.LogType;
import microbat.instrumentation.cfgcoverage.CoverageAgent;
import microbat.instrumentation.cfgcoverage.CoverageAgentParams;
+import microbat.instrumentation.instr.aggreplay.agents.AggrePlayReplayAgent;
+import microbat.instrumentation.instr.aggreplay.agents.SharedVariableAgent;
+import microbat.instrumentation.instr.aggreplay.agents.RNRRecordingAgent;
+import microbat.instrumentation.precheck.MemoryMeasurementAgent;
import microbat.instrumentation.precheck.PrecheckAgent;
/**
@@ -27,6 +31,18 @@ public static Agent createAgent(CommandLine cmd, Instrumentation inst) {
} else if (cmd.getBoolean(AgentParams.OPT_PRECHECK, false)) {
agent = new PrecheckAgent(cmd, inst);
agent.setInstrumentation(inst);
+ } else if (cmd.getBoolean(AgentParams.OPT_SHARED_DETECTION, false)) {
+ agent = SharedVariableAgent.getAgent(cmd);
+ agent.setInstrumentation(inst);
+ } else if (cmd.getBoolean(AgentParams.OPT_CONC_RECORD, false)) {
+ agent = RNRRecordingAgent.getAttached(cmd);
+ agent.setInstrumentation(inst);
+ } else if (cmd.getBoolean(AgentParams.OPT_CONC_REPLAY, false)) {
+ agent = AggrePlayReplayAgent.getAttached(cmd);
+ agent.setInstrumentation(inst);
+ } else if (cmd.getBoolean(AgentParams.MEASURE_MEM, false)) {
+ agent = MemoryMeasurementAgent.getMeasurementAgent(cmd);
+ agent.setInstrumentation(inst);
} else {
agent = new TraceAgent(cmd);
agent.setInstrumentation(inst);
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/AgentParams.java b/microbat_instrumentator/src/main/microbat/instrumentation/AgentParams.java
index dd720793b..af8fad0c7 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/AgentParams.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/AgentParams.java
@@ -7,6 +7,7 @@
import java.util.Set;
import microbat.instrumentation.filter.CodeRangeEntry;
+import microbat.instrumentation.instr.aggreplay.ReplayMode;
import microbat.instrumentation.instr.instruction.info.EntryPoint;
import microbat.instrumentation.utils.FileUtils;
import microbat.sql.TraceRecorder;
@@ -24,11 +25,15 @@ public class AgentParams extends CommonParams {
public static final String OPT_WORKING_DIR = CommonParams.OPT_WORKING_DIR;
public static final String OPT_LOG = CommonParams.OPT_LOG;
+ public static final String OPT_SHARED_DETECTION = "detect_share";
+ public static final String OPT_CONC_RECORD = "record_conc";
+ public static final String OPT_CONC_REPLAY = "replay_conc";
public static final String OPT_PRECHECK = "precheck";
public static final String OPT_ENTRY_POINT = "entry_point";
public static final String OPT_LAUNCH_CLASS = "launch_class";
public static final String OPT_JAVA_HOME = "java_home";
public static final String OPT_DUMP_FILE = "dump_file_path";
+ public static final String OPT_CONC_RECORD_DUMP = "conc_dump_file_path";
public static final String OPT_TCP_PORT = "tcp_port";
public static final String OPT_INCLUDES = "includes";
public static final String OPT_EXCLUDES = "excludes";
@@ -43,10 +48,15 @@ public class AgentParams extends CommonParams {
public static final String OPT_CODE_RANGE = "code_range";
public static final String OPT_TRACE_RECORDER = "trace_recorder";
public static final String OPT_RUN_ID = "run_id";
+ public static final String MEASURE_MEM = "measure_mem";
+ public static final String TIMEOUT = "time_out"; // the timeout of instrumentation.
+ public static final String REPLAY_MODE = "replay_mode";
+ private long timeOut = 100000L;
+ private ReplayMode replayMode = ReplayMode.AGGR;
private boolean precheck;
private EntryPoint entryPoint;
-
+ private String concDumpFile;
private String javaHome;
private String launchClass;
private int tcpPort = -1;
@@ -68,6 +78,7 @@ public AgentParams(CommandLine cmd) {
super(cmd);
precheck = cmd.getBoolean(OPT_PRECHECK, false);
String entryPointStr = cmd.getString(OPT_ENTRY_POINT);
+ replayMode = cmd.getReplayMode(REPLAY_MODE);
if (entryPointStr != null) {
int idx = entryPointStr.lastIndexOf(".");
String mainClass = entryPointStr.substring(0, idx);
@@ -75,7 +86,9 @@ public AgentParams(CommandLine cmd) {
EntryPoint entryPoint = new EntryPoint(mainClass, mainMethod);
this.entryPoint = entryPoint;
}
-
+ if (cmd.getString(TIMEOUT) != null) {
+ this.timeOut = Long.parseLong(cmd.getString(TIMEOUT));
+ }
setJavaHome(cmd.getString(OPT_JAVA_HOME));
setWorkingDirectory(cmd.getString(OPT_WORKING_DIR));
@@ -87,6 +100,7 @@ public AgentParams(CommandLine cmd) {
tcpPort = cmd.getInt(OPT_TCP_PORT, -1);
dumpFile = cmd.getString(OPT_DUMP_FILE);
+ concDumpFile = cmd.getString(OPT_CONC_RECORD_DUMP);
includesExpression = getFilterExpression(cmd, OPT_INCLUDES_FILE, OPT_INCLUDES);
excludesExpression = getFilterExpression(cmd, OPT_EXCLUDES_FILE, OPT_EXCLUDES);
variableLayer = cmd.getInt(OPT_VARIABLE_LAYER, 2);
@@ -127,6 +141,10 @@ private static String getFilterExpression(CommandLine cmd, String fileOpt, Strin
return expression;
}
+ public long getTimeOut() {
+ return this.timeOut;
+ }
+
public EntryPoint getEntryPoint() {
return entryPoint;
}
@@ -151,6 +169,10 @@ public void setLaunchClass(String launchClass) {
this.launchClass = launchClass;
}
+ public ReplayMode getReplayMode() {
+ return replayMode;
+ }
+
public int getTcpPort() {
return tcpPort;
}
@@ -158,6 +180,10 @@ public int getTcpPort() {
public String getDumpFile() {
return dumpFile;
}
+
+ public String getConcDumpFile() {
+ return concDumpFile;
+ }
public String getExcludesExpression() {
return excludesExpression;
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/CommandLine.java b/microbat_instrumentator/src/main/microbat/instrumentation/CommandLine.java
index 50665dbe3..1d81cc65d 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/CommandLine.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/CommandLine.java
@@ -7,6 +7,7 @@
import java.util.Map;
import java.util.Set;
+import microbat.instrumentation.instr.aggreplay.ReplayMode;
import sav.common.core.utils.CollectionUtils;
/**
@@ -64,4 +65,16 @@ public Set getStringSet(String option) {
public String getString(String option) {
return argMap.get(option);
}
+
+ public ReplayMode getReplayMode(String option) {
+ String valueString = argMap.get(option);
+ if (valueString == null) {
+ return ReplayMode.AGGR;
+ }
+ ReplayMode resultMode = ReplayMode.parse(valueString);
+ if (resultMode == null) {
+ return ReplayMode.AGGR;
+ }
+ return resultMode;
+ }
}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/TraceAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/TraceAgent.java
index 6e051e66d..196ed9913 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/TraceAgent.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/TraceAgent.java
@@ -7,7 +7,10 @@
import microbat.instrumentation.filter.CodeRangeUserFilter;
import microbat.instrumentation.filter.GlobalFilterChecker;
import microbat.instrumentation.filter.OverLongMethodFilter;
+import microbat.instrumentation.instr.SystemClassTransformer;
import microbat.instrumentation.instr.TraceTransformer;
+import microbat.instrumentation.instr.aggreplay.TimeoutThread;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
import microbat.instrumentation.runtime.ExecutionTracer;
import microbat.instrumentation.runtime.IExecutionTracer;
import microbat.model.trace.Trace;
@@ -18,19 +21,32 @@
public class TraceAgent extends Agent {
private AgentParams agentParams;
+ protected static ThreadIdGenerator threadIdGenerator = new ThreadIdGenerator();
+ private TimeoutThread timeoutThread = new TimeoutThread();
// private StopTimer timer;
public TraceAgent(CommandLine cmd) {
this.agentParams = AgentParams.initFrom(cmd);
}
+
+ public static void _onThreadStart(Thread thread) {
+ if (ExecutionTracer.isRecordingOrStarted()) {
+ int order = ExecutionTracer.getCurrentThreadStore().getLatestOrder();
+ threadIdGenerator.createId(thread, order);
+ }
+ }
public void startup0(long vmStartupTime, long agentPreStartup) {
+ timeoutThread.setTimeout(agentParams.getTimeOut());
+ if (!timeoutThread.isAlive()) timeoutThread.start();
+ SystemClassTransformer.attachThreadId(getInstrumentation(), TraceAgent.class);
// timer = new StopTimer("Trace Construction");
// timer.newPoint("Execution");
/* init filter */
AppJavaClassPath appPath = agentParams.initAppClassPath();
GlobalFilterChecker.setup(appPath, agentParams.getIncludesExpression(), agentParams.getExcludesExpression());
ExecutionTracer.appJavaClassPath = appPath;
+ System.out.println("Exection class paths" + ExecutionTracer.appJavaClassPath.getClasspaths());
ExecutionTracer.variableLayer = agentParams.getVariableLayer();
ExecutionTracer.setStepLimit(agentParams.getStepLimit());
if (!agentParams.isRequireMethodSplit()) {
@@ -62,13 +78,16 @@ public void shutdown() throws Exception {
Trace trace = tracer.getTrace();
trace.setThreadId(tracer.getThreadId());
+ trace.setInnerThreadId(threadIdGenerator.getId(tracer.getThreadId()));
trace.setThreadName(tracer.getThreadName());
trace.setMain(ExecutionTracer.getMainThreadStore().equals(tracer));
-
+ trace.setAppJavaClassPath(ExecutionTracer.appJavaClassPath);
+ trace.setAcquiredLocks(tracer.getLockAcquired());
+ trace.setAcquiringLock(tracer.getAcquiringLock());
constructTrace(trace);
traceList.add(trace);
}
-
+
// timer.newPoint("Saving trace");
Recorder.create(agentParams).store(traceList);
// AgentLogger.debug(timer.getResultString());
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/cfgcoverage/instr/CoverageInstrumenter.java b/microbat_instrumentator/src/main/microbat/instrumentation/cfgcoverage/instr/CoverageInstrumenter.java
index 652136422..1df33e1bf 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/cfgcoverage/instr/CoverageInstrumenter.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/cfgcoverage/instr/CoverageInstrumenter.java
@@ -337,7 +337,6 @@ public void setEntryPoint(String entryPoint) {
@Override
protected boolean instrumentMethod(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
boolean isAppClass, boolean isMainMethod, boolean isEntry) {
- // TODO Auto-generated method stub
return false;
}
}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/AbstractInstrumenter.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/AbstractInstrumenter.java
index 7f2709ff7..51bdf1d62 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/instr/AbstractInstrumenter.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/AbstractInstrumenter.java
@@ -36,6 +36,9 @@ public abstract class AbstractInstrumenter {
public byte[] instrument(String classFName, byte[] classfileBuffer) throws Exception {
String className = classFName.replace("/", ".");
+ if (!shouldInstrument(className)) {
+ return null;
+ }
ClassParser cp = new ClassParser(new java.io.ByteArrayInputStream(classfileBuffer), classFName);
JavaClass jc = cp.parse();
// First, make sure we have to instrument this class:
@@ -47,6 +50,10 @@ public byte[] instrument(String classFName, byte[] classfileBuffer) throws Excep
return instrument(classFName, className, jc);
}
+ protected boolean shouldInstrument(String className) {
+ return true;
+ }
+
protected abstract boolean instrumentMethod(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
boolean isAppClass, boolean isMainMethod, boolean isEntry);
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/SystemClassTransformer.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/SystemClassTransformer.java
index 37a659626..a9466ff25 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/instr/SystemClassTransformer.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/SystemClassTransformer.java
@@ -7,6 +7,7 @@
import java.security.ProtectionDomain;
import microbat.instrumentation.AgentLogger;
+import microbat.instrumentation.instr.aggreplay.ThreadIdInstrumenter;
public class SystemClassTransformer {
@@ -18,6 +19,19 @@ public static void transformThread(Instrumentation inst) {
transform(inst, Thread.class, new ThreadInstrumenter());
}
+ /**
+ * Used to generate persistent thread id during runtime.
+ * Needed so that thread id is consistent across runs.
+ * @param inst
+ */
+ public static void attachThreadId(Instrumentation inst) {
+ transform(inst, Thread.class, new ThreadIdInstrumenter());
+ }
+
+ public static void attachThreadId(Instrumentation inst, Class> clazz) {
+ transform(inst, Thread.class, new ThreadIdInstrumenter(clazz));
+ }
+
public static void transform(Instrumentation inst, final Class> clazz, final AbstractInstrumenter instrumenter) {
final String clazzFName = clazz.getName().replace(".", "/");
ClassFileTransformer transformer = new AbstractTransformer() {
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceInstrumenter.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceInstrumenter.java
index eb79b0b40..1c37fb4b3 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceInstrumenter.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceInstrumenter.java
@@ -61,6 +61,8 @@
import microbat.instrumentation.instr.instruction.info.LineInstructionInfo;
import microbat.instrumentation.instr.instruction.info.LocalVarInstructionInfo;
import microbat.instrumentation.instr.instruction.info.RWInstructionInfo;
+import microbat.instrumentation.model.id.AggrePlayMethods;
+import microbat.instrumentation.runtime.ExecutionTracer;
import microbat.instrumentation.runtime.IExecutionTracer;
import microbat.instrumentation.runtime.TraceUtils;
import microbat.instrumentation.utils.MicrobatUtils;
@@ -70,7 +72,7 @@ public class TraceInstrumenter extends AbstractInstrumenter {
private static final String TEMP_VAR_NAME = "$tempVar"; // local var
private int tempVarIdx = 0;
- private EntryPoint entryPoint;
+ protected EntryPoint entryPoint;
private Set requireSplittingMethods = Collections.emptySet();
private UserFilters userFilters;
@@ -152,7 +154,7 @@ protected byte[] instrument(String classFName, String className, JavaClass jc) {
return null;
}
- private boolean isThread(JavaClass jc) {
+ protected boolean isThread(JavaClass jc) {
try {
for(JavaClass interf: jc.getAllInterfaces()) {
if(interf.getClassName().equals("java.lang.Runnable")) {
@@ -175,7 +177,7 @@ private boolean isThread(JavaClass jc) {
return false;
}
- private boolean doesBytecodeExceedLimit(GeneratedMethods generatedMethods) {
+ protected boolean doesBytecodeExceedLimit(GeneratedMethods generatedMethods) {
boolean excessive = doesBytecodeExceedLimit(generatedMethods.getRootMethod());
for (MethodGen addedMethod : generatedMethods.getExtractedMethods()) {
excessive |= doesBytecodeExceedLimit(addedMethod);
@@ -183,7 +185,7 @@ private boolean doesBytecodeExceedLimit(GeneratedMethods generatedMethods) {
return excessive;
}
- private GeneratedMethods runMethodInstrumentation(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
+ protected GeneratedMethods runMethodInstrumentation(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
boolean isAppClass, boolean isMainMethod, boolean isEntry) {
String methodFullName = MicrobatUtils.getMicrobatMethodFullName(classGen.getClassName(), method);
boolean changed = instrumentMethod(classGen, constPool, methodGen, method, isAppClass, isMainMethod, isEntry);
@@ -300,14 +302,39 @@ protected boolean instrumentMethod(ClassGen classGen, ConstantPoolGen constPool,
injectCodeTracerExit(exitInsHandle, insnList, constPool, tracerVar, line, classNameVar, methodSigVar, isMainMethod, isEntry);
}
+ for (InstructionHandle monitorEnterHandle : lineInfo.getMonitorEnterInstructionHandles()) {
+ injectMonitorEnterInstruction(insnList, monitorEnterHandle, constPool);
+ }
+
+ for (InstructionHandle monitorExitHandle : lineInfo.getMonitorExitInstructionHandles()) {
+ injectMonitorExitInstruction(insnList, monitorExitHandle, constPool);
+ }
+
lineInfo.dispose();
}
injectCodeInitTracer(methodGen, constPool, startLine, endLine, isAppClass, classNameVar,
methodSigVar, isMainMethod, tracerVar);
return true;
}
+
+ protected void injectMonitorEnterInstruction(InstructionList il, InstructionHandle ih, ConstantPoolGen cpg) {
+ InstructionList beforeMonitorEnter = new InstructionList();
+ beforeMonitorEnter.append(new DUP());
+ beforeMonitorEnter.append(AggrePlayMethods.ON_LOCK_ACQUIRE.toInvokeStatic(cpg, ExecutionTracer.class));
+
+ InstructionList afterMonitorEnter = new InstructionList();
+ afterMonitorEnter.append(AggrePlayMethods.ON_LOCK_ACQUIRE2.toInvokeStatic(cpg, ExecutionTracer.class));
+ insertInsnHandler(il, beforeMonitorEnter, ih);
+ appendInstruction(il, afterMonitorEnter, ih);
+ }
+
+ protected void injectMonitorExitInstruction(InstructionList il, InstructionHandle ih, ConstantPoolGen cp) {
+ InstructionList afterMonitorExitInstructionList = new InstructionList();
+ afterMonitorExitInstructionList.append(AggrePlayMethods.AFTER_LOCK_ACQUIRE.toInvokeStatic(cp, ExecutionTracer.class));
+ appendInstruction(il, afterMonitorExitInstructionList, ih);
+ }
- private void injectCodeTracerExit(InstructionHandle exitInsHandle, InstructionList insnList,
+ protected void injectCodeTracerExit(InstructionHandle exitInsHandle, InstructionList insnList,
ConstantPoolGen constPool, LocalVariableGen tracerVar, int line, LocalVariableGen classNameVar,
LocalVariableGen methodSigVar, boolean isMainMethod, boolean isEntry) {
InstructionList newInsns = new InstructionList();
@@ -330,7 +357,7 @@ private void injectCodeTracerExit(InstructionHandle exitInsHandle, InstructionLi
newInsns.dispose();
}
- private void injectCodeTracerReturn(InstructionList insnList, ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected void injectCodeTracerReturn(InstructionList insnList, ConstantPoolGen constPool, LocalVariableGen tracerVar,
InstructionHandle insnHandler, int line, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
InstructionList newInsns = new InstructionList();
ReturnInstruction insn = (ReturnInstruction) insnHandler.getInstruction();
@@ -378,7 +405,7 @@ private void injectCodeTracerReturn(InstructionList insnList, ConstantPoolGen co
newInsns.dispose();
}
- private void injectCodeTracerInvokeMethod(MethodGen methodGen, InstructionList insnList, ConstantPoolGen constPool,
+ protected void injectCodeTracerInvokeMethod(MethodGen methodGen, InstructionList insnList, ConstantPoolGen constPool,
InstructionFactory instructionFactory, LocalVariableGen tracerVar, InstructionHandle insnHandler,
int line, LocalVariableGen classNameVar, LocalVariableGen methodSigVar, boolean isAppClass) {
InvokeInstruction insn = (InvokeInstruction) insnHandler.getInstruction();
@@ -566,7 +593,7 @@ private void injectCodeAfterInvoke(InstructionList insnList, ConstantPoolGen con
newInsns.dispose();
}
- private InstructionList getInjectCodeTracerRWriteField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodeTracerRWriteField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
FieldInstruction insn = (FieldInstruction) info.getInstruction();
if (insn instanceof PUTFIELD) {
@@ -581,7 +608,7 @@ private InstructionList getInjectCodeTracerRWriteField(ConstantPoolGen constPool
return null;
}
- private InstructionList getInjectCodePutField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodePutField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
InstructionList newInsns = new InstructionList();
/*
@@ -662,7 +689,7 @@ private InstructionList getInjectCodePutField(ConstantPoolGen constPool, LocalVa
* @param methodSigVar
* @param classNameVar
*/
- private InstructionList getInjectCodePutStatic(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodePutStatic(ConstantPoolGen constPool, LocalVariableGen tracerVar,
FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
InstructionList newInsns = new InstructionList();
if (info.isNextToAconstNull()) {
@@ -708,7 +735,7 @@ protected void appendTracerMethodInvoke(InstructionList newInsns, TracerMethods
}
}
- private InstructionList getInjectCodeGetField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodeGetField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
InstructionList newInsns = new InstructionList();
@@ -757,7 +784,7 @@ private InstructionList getInjectCodeGetField(ConstantPoolGen constPool, LocalVa
return newInsns;
}
- private InstructionList getInjectCodeGetStatic(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodeGetStatic(ConstantPoolGen constPool, LocalVariableGen tracerVar,
FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
InstructionList newInsns = new InstructionList();
GETSTATIC insn = (GETSTATIC) info.getInstruction();
@@ -793,7 +820,7 @@ private InstructionList getInjectCodeGetStatic(ConstantPoolGen constPool, LocalV
return newInsns;
}
- private InstructionList getInjectCodeTracerRWLocalVar(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodeTracerRWLocalVar(ConstantPoolGen constPool, LocalVariableGen tracerVar,
LocalVarInstructionInfo insnInfo, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
LocalVariableInstruction insn = insnInfo.getInstruction();
// ignore reference to self
@@ -856,7 +883,7 @@ private InstructionList getInjectCodeTracerRWLocalVar(ConstantPoolGen constPool,
return newInsns;
}
- private InstructionList getInjectCodeTracerIINC(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ protected InstructionList getInjectCodeTracerIINC(ConstantPoolGen constPool, LocalVariableGen tracerVar,
LocalVarInstructionInfo insnInfo, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
IINC insn = (IINC) insnInfo.getInstruction();
// ignore reference to self
@@ -904,7 +931,7 @@ private InstructionList getInjectCodeTracerIINC(ConstantPoolGen constPool, Local
return newInsns;
}
- private InstructionList getInjectCodeTracerRWriteArray(MethodGen methodGen, ConstantPoolGen constPool,
+ protected InstructionList getInjectCodeTracerRWriteArray(MethodGen methodGen, ConstantPoolGen constPool,
LocalVariableGen tracerVar, ArrayInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
InstructionHandle insnHandler = info.getInstructionHandler();
ArrayInstruction insn = info.getInstruction();
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceTransformer.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceTransformer.java
index e8a4474a7..685e3a4b7 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceTransformer.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/TraceTransformer.java
@@ -19,6 +19,11 @@ public TraceTransformer(AgentParams params) {
instrumenter = new TraceInstrumenter(params);
}
+
+ public TraceTransformer(TraceInstrumenter instr) {
+ this.instrumenter = instr;
+ }
+
@Override
protected byte[] doTransform(ClassLoader loader, String classFName, Class> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/TracerMethods.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/TracerMethods.java
index a323fece2..7f85cc1a0 100644
--- a/microbat_instrumentator/src/main/microbat/instrumentation/instr/TracerMethods.java
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/TracerMethods.java
@@ -25,7 +25,8 @@ public enum TracerMethods {
WRITE_FIELD(true, "microbat/instrumentation/runtime/IExecutionTracer", "_writeField", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V", 8),
WRITE_LOCAL_VAR(true, "microbat/instrumentation/runtime/IExecutionTracer", "_writeLocalVar", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;)V", 10),
WRITE_STATIC_FIELD(true, "microbat/instrumentation/runtime/IExecutionTracer", "_writeStaticField", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V", 8),
-
+ MONITER_ENTER(false, "microbat/instrumentation/runtime/ExecutionTracer", "_onLockAcquire", "(Ljava/lang/Object;)V", 1),
+ MONITOR_EXIT(false, "microbat/instrumentation/runtime/ExecutionTracer", "_afterLockAcquire", "()V", 0)
;
private boolean interfaceMethod;
private String declareClass;
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/AggrePlayTraceInstrumenter.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/AggrePlayTraceInstrumenter.java
new file mode 100644
index 000000000..9ee1a727f
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/AggrePlayTraceInstrumenter.java
@@ -0,0 +1,525 @@
+package microbat.instrumentation.instr.aggreplay;
+
+import java.util.List;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.DUP;
+import org.apache.bcel.generic.DUP2;
+import org.apache.bcel.generic.DUP2_X1;
+import org.apache.bcel.generic.DUP2_X2;
+import org.apache.bcel.generic.DUP_X2;
+import org.apache.bcel.generic.FieldInstruction;
+import org.apache.bcel.generic.GETSTATIC;
+import org.apache.bcel.generic.IINC;
+import org.apache.bcel.generic.INVOKESTATIC;
+import org.apache.bcel.generic.Instruction;
+import org.apache.bcel.generic.InstructionFactory;
+import org.apache.bcel.generic.InstructionHandle;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.InvokeInstruction;
+import org.apache.bcel.generic.LDC;
+import org.apache.bcel.generic.LocalVariableGen;
+import org.apache.bcel.generic.MethodGen;
+import org.apache.bcel.generic.POP;
+import org.apache.bcel.generic.POP2;
+import org.apache.bcel.generic.PUTFIELD;
+import org.apache.bcel.generic.PUTSTATIC;
+import org.apache.bcel.generic.SWAP;
+import org.apache.bcel.generic.TargetLostException;
+import org.apache.bcel.generic.Type;
+
+import microbat.instrumentation.AgentConstants;
+import microbat.instrumentation.AgentLogger;
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.filter.GlobalFilterChecker;
+import microbat.instrumentation.filter.UserFilters;
+import microbat.instrumentation.instr.GeneratedMethods;
+import microbat.instrumentation.instr.LocalVariableSupporter;
+import microbat.instrumentation.instr.TraceInstrumenter;
+import microbat.instrumentation.instr.aggreplay.agents.AggrePlayReplayAgent;
+import microbat.instrumentation.instr.instruction.info.ArrayInstructionInfo;
+import microbat.instrumentation.instr.instruction.info.FieldInstructionInfo;
+import microbat.instrumentation.instr.instruction.info.LineInstructionInfo;
+import microbat.instrumentation.instr.instruction.info.LocalVarInstructionInfo;
+import microbat.instrumentation.instr.instruction.info.RWInstructionInfo;
+import microbat.instrumentation.model.id.AggrePlayMethods;
+import microbat.instrumentation.runtime.IExecutionTracer;
+import microbat.instrumentation.utils.MicrobatUtils;
+
+public class AggrePlayTraceInstrumenter extends TraceInstrumenter {
+
+ private UserFilters userFilters;
+ private int tempVarIdx;
+ private Class> instrumentationClass = AggrePlayReplayAgent.class;
+
+ public AggrePlayTraceInstrumenter(AgentParams params) {
+ super(params);
+ }
+
+// @Override
+// protected byte[] instrument(String classFName, String className, JavaClass jc) {
+// ClassGen cg = new ClassGen(jc);
+// ConstantPoolGen cpg = cg.getConstantPool();
+// for (Method method : cg.getMethods()) {
+// if (method.isNative() || method.isAbstract() || method.getCode() == null) {
+// continue;
+// }
+// MethodGen mg = new MethodGen(method, classFName, cpg);
+// mg.getInstructionList();
+// /* fill up missing variables in localVariableTable */
+// LocalVariableSupporter.fillUpVariableTable(mg, method, cpg);
+//
+// List lineInsnInfos = LineInstructionInfo.buildLineInstructionInfos(cg, cpg,
+// mg, method, true, mg.getInstructionList());
+//
+// }
+// return cg.getJavaClass().getBytes();
+// }
+
+
+
+
+
+ @Override
+ protected byte[] instrument(String classFName, String className, JavaClass jc) {
+ ClassGen classGen = new ClassGen(jc);
+ ConstantPoolGen constPool = classGen.getConstantPool();
+ JavaClass newJC = null;
+ boolean entry = entryPoint == null ? false : className.equals(entryPoint.getClassName());
+// boolean isAppClass = GlobalFilterChecker.isAppClass(classFName) || entry;
+ boolean isAppClass = true;
+ if (userFilters != null && !userFilters.isInstrumentable(className)) {
+ return null;
+ }
+ for (Method method : jc.getMethods()) {
+ if (method.isNative() || method.isAbstract() || method.getCode() == null) {
+ continue; // Only instrument methods with code in them!
+ }
+ try {
+ MethodGen methodGen = new MethodGen(method, classFName, constPool);
+ boolean isMainMethod = false;
+ if (entry && entryPoint.matchMethod(method.getName(), method.getSignature())) {
+ isMainMethod = true;
+ }
+
+ boolean isEntry = false;
+ if(method.getName().equals("run") && isThread(jc)) {
+ isEntry = true;
+ }
+
+
+ GeneratedMethods generatedMethods = runMethodInstrumentation(classGen, constPool, methodGen,
+ method, isAppClass, isMainMethod, isEntry);
+ if (generatedMethods != null) {
+ if (doesBytecodeExceedLimit(generatedMethods)) {
+ AgentLogger.info(String.format("Warning: %s exceeds bytecode limit!",
+ MicrobatUtils.getMicrobatMethodFullName(classGen.getClassName(), method)));
+ } else {
+ for (MethodGen newMethod : generatedMethods.getExtractedMethods()) {
+ newMethod.setMaxStack();
+ newMethod.setMaxLocals();
+ classGen.addMethod(newMethod.getMethod());
+ }
+ methodGen = generatedMethods.getRootMethod();
+ // All changes made, so finish off the method:
+ InstructionList instructionList = methodGen.getInstructionList();
+ instructionList.setPositions();
+ methodGen.setMaxStack();
+ methodGen.setMaxLocals();
+ classGen.replaceMethod(method, methodGen.getMethod());
+ }
+ }
+ newJC = classGen.getJavaClass();
+ newJC.setConstantPool(constPool.getFinalConstantPool());
+ } catch (Exception e) {
+ String message = e.getMessage();
+ if (e.getMessage() != null && e.getMessage().contains("offset too large")) {
+ message = "offset too large";
+ }
+ AgentLogger.info(String.format("Warning: %s [%s]",
+ MicrobatUtils.getMicrobatMethodFullName(classGen.getClassName(), method), message));
+ AgentLogger.error(e);
+ e.printStackTrace();
+ }
+ }
+ if (newJC != null) {
+ byte[] data = newJC.getBytes();
+ return data;
+ }
+
+ return null;
+ }
+
+
+
+
+ @Override
+ protected InstructionList getInjectCodePutField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
+ InstructionList result = new InstructionList();
+ if (info.isComputationalType2()) {
+ // o, d -> d, o, o
+ result.append(new DUP2_X1());
+ result.append(new POP2());
+ result.append(new DUP());
+ } else {
+ result.append(new SWAP()); // val, obj
+ result.append(new DUP()); // val, obj, obj
+ }
+
+
+ result.append(new LDC(constPool.addString(info.getFieldName()))); // v, o, o, s
+ result.append(AggrePlayMethods.BEFORE_OBJECT_WRITE.toInvokeStatic(constPool, instrumentationClass)); // v, o
+
+ if (info.isComputationalType2()) {
+ result.append(new DUP_X2());
+ result.append(new POP());
+ } else {
+ result.append(new SWAP());
+ }
+ result.append(super.getInjectCodePutField(constPool, tracerVar, info, classNameVar, methodSigVar));
+ return result;
+ }
+
+ @Override
+ protected InstructionList getInjectCodeGetField(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
+ InstructionList beforeGetFieldInstructionList = new InstructionList();
+ InstructionList afterGetFieldInstructionList = new InstructionList();
+ // obj
+ beforeGetFieldInstructionList.append(new DUP());
+ // obj, obj
+ beforeGetFieldInstructionList.append(new LDC(constPool.addString(info.getFieldName())));
+ beforeGetFieldInstructionList.append(AggrePlayMethods.BEFORE_OBJECT_READ.toInvokeStatic(constPool, instrumentationClass));
+ afterGetFieldInstructionList.append(AggrePlayMethods.AFTER_OBJECT_READ.toInvokeStatic(constPool, instrumentationClass));
+ InstructionList getFieldInstructions =
+ super.getInjectCodeGetField(constPool, tracerVar, info, classNameVar, methodSigVar);
+ beforeGetFieldInstructionList.append(getFieldInstructions);
+ beforeGetFieldInstructionList.append(afterGetFieldInstructionList);
+ return beforeGetFieldInstructionList;
+ // return super.getInjectCodeGetField(constPool, tracerVar, info, classNameVar, methodSigVar);
+ }
+
+
+
+
+
+
+ private InstructionList afterWrite(ConstantPoolGen cpg) {
+ InstructionList result = new InstructionList();
+ INVOKESTATIC afterPutFieldInvokestatic =
+ AggrePlayMethods.AFTER_OBJECT_WRITE.toInvokeStatic(cpg, instrumentationClass);
+ result.append(afterPutFieldInvokestatic);
+ return result;
+ }
+
+
+
+ protected InstructionList appendFieldInstructions(FieldInstructionInfo info, ConstantPoolGen cpg) {
+ // only need to deal with putfield and putstatic
+ Instruction instruction = info.getInstruction();
+ if (instruction.getOpcode() == Const.PUTFIELD) {
+ return afterWrite(cpg);
+ }
+ if (instruction.getOpcode() == Const.PUTSTATIC) {
+ return afterWrite(cpg);
+ }
+ return null;
+ }
+
+ // TODO:
+ /**
+ * Currently store instruction, i.e. write instructions, we only instrument the before stage,
+ * We do not instrument the after stage -> should modify to instrument the after stage
+ * with the minimum amount of change.
+ * TODO: reduce the amount of change in this code
+ */
+ @Override
+ protected boolean instrumentMethod(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
+ boolean isAppClass, boolean isMainMethod, boolean isEntry) {
+ // TODO Auto-generated method stub
+ if (userFilters != null && !userFilters.isInstrumentable(classGen.getClassName(), method, methodGen.getLineNumbers())) {
+ return false;
+ }
+ tempVarIdx = 0;
+ InstructionList insnList = methodGen.getInstructionList();
+ InstructionHandle startInsn = insnList.getStart();
+ if (startInsn == null) {
+ // empty method
+ return false;
+ }
+
+ /* fill up missing variables in localVariableTable */
+ LocalVariableSupporter.fillUpVariableTable(methodGen, method, constPool);
+
+ List lineInsnInfos = LineInstructionInfo.buildLineInstructionInfos(classGen, constPool,
+ methodGen, method, isAppClass, insnList);
+ int startLine = Integer.MAX_VALUE;
+ int endLine = AgentConstants.UNKNOWN_LINE;
+ for (LineInstructionInfo lineInfo : lineInsnInfos) {
+ int line = lineInfo.getLine();
+ if (line < startLine) {
+ startLine = line;
+ }
+ if (line > endLine) {
+ endLine = line;
+ }
+ }
+ if (startLine == Integer.MAX_VALUE) {
+ startLine = AgentConstants.UNKNOWN_LINE;
+ }
+ LocalVariableGen classNameVar = createLocalVariable(CLASS_NAME, methodGen, constPool);
+ LocalVariableGen methodSigVar = createLocalVariable(METHOD_SIGNATURE, methodGen, constPool);
+ LocalVariableGen tracerVar = methodGen.addLocalVariable(TRACER_VAR_NAME, Type.getType(IExecutionTracer.class),
+ insnList.getStart(), insnList.getEnd());
+
+ if (userFilters != null) userFilters.filter(lineInsnInfos, classGen.getClassName(), method);
+ for (LineInstructionInfo lineInfo : lineInsnInfos) {
+ /* instrument RW instructions */
+ List rwInsns = lineInfo.getRWInstructions();
+// if (lineInfo.hasNoInstrumentation()) {
+ injectCodeTracerHitLine(insnList, constPool, tracerVar, lineInfo.getLine(), lineInfo.getLineNumberInsn(),
+ classNameVar, methodSigVar, lineInfo.hasExceptionTarget(), lineInfo.getReadWriteInsnTotal(false),
+ lineInfo.getReadWriteInsnTotal(true), lineInfo);
+// }
+ for (RWInstructionInfo rwInsnInfo : rwInsns) {
+ InstructionList newInsns = null;
+ // instructions to append --> only on write instructions
+ InstructionList appendInsnsInstructionList = null;
+ if (rwInsnInfo instanceof FieldInstructionInfo) {
+ newInsns = getInjectCodeTracerRWriteField(constPool, tracerVar, (FieldInstructionInfo) rwInsnInfo, classNameVar, methodSigVar);
+ appendInsnsInstructionList = appendFieldInstructions((FieldInstructionInfo) rwInsnInfo, constPool);
+ } else if (rwInsnInfo instanceof ArrayInstructionInfo) {
+ newInsns = getInjectCodeTracerRWriteArray(methodGen, constPool, tracerVar,
+ (ArrayInstructionInfo) rwInsnInfo, classNameVar, methodSigVar);
+ appendInsnsInstructionList = appendInstructionListRWArray((ArrayInstructionInfo) rwInsnInfo, constPool);
+ } else if (rwInsnInfo instanceof LocalVarInstructionInfo) {
+ if (rwInsnInfo.getInstruction() instanceof IINC) {
+ newInsns = getInjectCodeTracerIINC(constPool, tracerVar,
+ (LocalVarInstructionInfo) rwInsnInfo, classNameVar, methodSigVar);
+ } else {
+ newInsns = getInjectCodeTracerRWLocalVar(constPool, tracerVar,
+ (LocalVarInstructionInfo) rwInsnInfo, classNameVar, methodSigVar);
+ }
+ }
+ if ((newInsns != null) && (newInsns.getLength() > 0)) {
+ InstructionHandle insnHandler = rwInsnInfo.getInstructionHandler();
+ if (rwInsnInfo.isStoreInstruction()) {
+ insertInsnHandler(insnList, newInsns, insnHandler);
+ if (appendInsnsInstructionList != null) appendInstruction(insnList, appendInsnsInstructionList, insnHandler);
+ newInsns.dispose();
+ } else {
+ insertInsnHandler(insnList, newInsns, insnHandler);
+ try {
+ updateTarget(insnHandler, insnHandler.getPrev(), insnHandler.getNext());
+ insnList.delete(insnHandler);
+ } catch (TargetLostException e) {
+ e.printStackTrace();
+ }
+ newInsns.dispose();
+ }
+ }
+ }
+ int line = lineInfo.getLine();
+ /* instrument Invocation instructions */
+ InstructionFactory instructionFactory = new InstructionFactory(classGen, constPool);
+ for (InstructionHandle insn : lineInfo.getInvokeInstructions()) {
+ injectCodeTracerInvokeMethod(methodGen, insnList, constPool, instructionFactory, tracerVar, insn, line,
+ classNameVar, methodSigVar, isAppClass);
+ }
+ /* instrument Return instructions */
+ for (InstructionHandle insn : lineInfo.getReturnInsns()) {
+ injectCodeTracerReturn(insnList, constPool, tracerVar, insn, line, classNameVar, methodSigVar);
+ }
+
+ /**
+ * instrument exit instructions
+ */
+ for (InstructionHandle exitInsHandle : lineInfo.getExitInsns()) {
+ injectCodeTracerExit(exitInsHandle, insnList, constPool, tracerVar, line, classNameVar, methodSigVar, isMainMethod, isEntry);
+ }
+
+ /**
+ * Instrument new instructions
+ */
+ for (InstructionHandle newInsnHandle: lineInfo.getNewInstructions()) {
+ injectOnNewInstructions(insnList, newInsnHandle, constPool);
+ }
+
+ /**
+ * Instrument new array instructions
+ */
+ for (InstructionHandle newArrayInsnHandle: lineInfo.getNewArrayInstructions()) {
+ injectOnNewArrayInsns(insnList, newArrayInsnHandle, constPool);
+ }
+
+ for (InstructionHandle ih : lineInfo.getMonitorEnterInstructionHandles()) {
+ injectMonitorEnterInstruction(insnList, ih, constPool);
+ }
+
+ for (InstructionHandle ih : lineInfo.getMonitorExitInstructionHandles()) {
+ injectMonitorExitInstruction(insnList, ih, constPool);
+ }
+
+
+ lineInfo.dispose();
+ }
+ injectCodeInitTracer(methodGen, constPool, startLine, endLine, isAppClass, classNameVar,
+ methodSigVar, isMainMethod, tracerVar);
+ return true;
+ }
+
+ @Override
+ protected void injectMonitorEnterInstruction(InstructionList il, InstructionHandle ih, ConstantPoolGen cpg) {
+ super.injectMonitorEnterInstruction(il, ih, cpg);
+ InstructionList beforeMonitorEnter = new InstructionList();
+ beforeMonitorEnter.append(new DUP());
+ beforeMonitorEnter.append(AggrePlayMethods.ON_LOCK_ACQUIRE.toInvokeStatic(cpg, instrumentationClass));
+ insertInsnHandler(il, beforeMonitorEnter, ih);
+ }
+ @Override
+ protected void injectMonitorExitInstruction(InstructionList il, InstructionHandle ih, ConstantPoolGen cp) {
+ super.injectMonitorExitInstruction(il, ih, cp);
+ InstructionList afterMonitorExitInstructionList = new InstructionList();
+ afterMonitorExitInstructionList.append(AggrePlayMethods.AFTER_LOCK_ACQUIRE.toInvokeStatic(cp, instrumentationClass));
+ appendInstruction(il, afterMonitorExitInstructionList, ih);
+ }
+
+ private void injectOnNewArrayInsns(InstructionList instructionList, InstructionHandle ih, ConstantPoolGen cpg) {
+ InstructionList toAppend = new InstructionList();
+ toAppend.append(new DUP());
+ toAppend.append(AggrePlayMethods.ON_NEW_ARRAY.toInvokeStatic(cpg, instrumentationClass));
+ appendInstruction(instructionList, toAppend, ih);
+ }
+
+
+
+ @Override
+ protected void injectCodeTracerInvokeMethod(MethodGen methodGen, InstructionList insnList,
+ ConstantPoolGen constPool, InstructionFactory instructionFactory, LocalVariableGen tracerVar,
+ InstructionHandle insnHandler, int line, LocalVariableGen classNameVar, LocalVariableGen methodSigVar,
+ boolean isAppClass) {
+ // TODO Auto-generated method stub
+ super.injectCodeTracerInvokeMethod(methodGen, insnList, constPool, instructionFactory, tracerVar, insnHandler, line,
+ classNameVar, methodSigVar, isAppClass);
+ InstructionList toAppend = new InstructionList();
+ InvokeInstruction invokeInstruction = (InvokeInstruction) insnHandler.getInstruction();
+ String signature = invokeInstruction.getReturnType(constPool).getSignature();
+ if (signature.startsWith("L")) {
+ toAppend.append(new DUP());
+ toAppend.append(AggrePlayMethods.ASSERT_OBJECT_EXISTS.toInvokeStatic(constPool, instrumentationClass));
+ appendInstruction(insnList, toAppend, insnHandler);
+ } else if (signature.startsWith("[")) {
+ toAppend.append(new DUP());
+ toAppend.append(AggrePlayMethods.ASSERT_ARRAY_EXISTS.toInvokeStatic(constPool, instrumentationClass));
+ appendInstruction(insnList, toAppend, insnHandler);
+ }
+ }
+
+ /**
+ *
+ * @param info
+ * @return
+ */
+ private InstructionList appendInstructionListRWArray(ArrayInstructionInfo info, ConstantPoolGen cpg) {
+
+ InstructionList result = new InstructionList();
+ if (info.isStoreInstruction()) {
+ result.append(AggrePlayMethods.AFTER_OBJECT_WRITE.toInvokeStatic(cpg, instrumentationClass));
+ }
+ return result;
+ }
+
+ @Override
+ protected InstructionList getInjectCodeTracerRWriteArray(MethodGen methodGen, ConstantPoolGen constPool,
+ LocalVariableGen tracerVar, ArrayInstructionInfo info, LocalVariableGen classNameVar,
+ LocalVariableGen methodSigVar) {
+ InstructionList before = new InstructionList();
+ if (info.isStoreInstruction()) {
+ // a, i, d -> a, i, d, a, i
+ if (info.isComputationalType2()) {
+ before.append(new DUP2_X2()); // d a i d
+ before.append(new POP2()); // d a i
+ before.append(new DUP2_X2());// a i d a i
+ } else {
+ // a i d - d a i d
+ before.append(new DUP_X2());
+ before.append(new POP());
+ before.append(new DUP2_X1());
+ }
+ before.append(AggrePlayMethods.BEFORE_ARRAY_WRITE.toInvokeStatic(constPool, instrumentationClass));
+ } else {
+ before.append(new DUP2());
+ before.append(AggrePlayMethods.BEFORE_ARRAY_READ.toInvokeStatic(constPool, instrumentationClass));
+ }
+ InstructionList intermediate = super.getInjectCodeTracerRWriteArray(methodGen, constPool, tracerVar, info, classNameVar, methodSigVar);
+
+ before.append(intermediate);
+ if (!info.isStoreInstruction()) {
+ before.append(AggrePlayMethods.AFTER_OBJECT_READ.toInvokeStatic(constPool, instrumentationClass));
+ }
+ return before;
+ }
+
+ @Override
+ protected InstructionList getInjectCodePutStatic(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
+ InstructionList before = new InstructionList();
+ PUTSTATIC insPutstatic = (PUTSTATIC) info.getInstruction();
+ String classNameString = insPutstatic.getReferenceType(constPool).getSignature();
+
+ before.append(new GETSTATIC(insPutstatic.getIndex()));
+ if (info.isComputationalType2()) {
+ before.append(new POP2());
+ } else {
+ before.append(new POP());
+ }
+ before.append(new LDC(constPool.addString(classNameString)));
+ before.append(new LDC(constPool.addString(info.getFieldName())));
+ before.append(AggrePlayMethods.BEFORE_STATIC_WRITE.toInvokeStatic(constPool, instrumentationClass));
+ InstructionList inBetween = super.getInjectCodePutStatic(constPool, tracerVar, info, classNameVar, methodSigVar);
+ before.append(inBetween);
+
+ return before;
+ }
+
+ @Override
+ protected InstructionList getInjectCodeGetStatic(ConstantPoolGen constPool, LocalVariableGen tracerVar,
+ FieldInstructionInfo info, LocalVariableGen classNameVar, LocalVariableGen methodSigVar) {
+ InstructionList before = new InstructionList();
+ GETSTATIC insPutstatic = (GETSTATIC) info.getInstruction();
+ String classNameString = insPutstatic.getReferenceType(constPool).getSignature();
+
+ before.append(insPutstatic);
+ if (info.isComputationalType2()) {
+ before.append(new POP2());
+ } else {
+ before.append(new POP());
+ }
+
+ before.append(new LDC(constPool.addString(classNameString)));
+ before.append(new LDC(constPool.addString(info.getFieldName())));
+ before.append(AggrePlayMethods.BEFORE_STATIC_READ.toInvokeStatic(constPool, instrumentationClass));
+
+
+ InstructionList tracerCode = super.getInjectCodeGetStatic(constPool, tracerVar, info, classNameVar, methodSigVar);
+ before.append(tracerCode);
+ before.append(AggrePlayMethods.AFTER_OBJECT_READ.toInvokeStatic(constPool, instrumentationClass));
+ return before;
+ }
+
+ protected void injectOnNewInstructions(InstructionList instructionList,
+ InstructionHandle instructionHandle,
+ ConstantPoolGen cpg) {
+ InstructionList toAppend = new InstructionList();
+ toAppend.append(new DUP());
+ toAppend.append(AggrePlayMethods.ON_NEW_OBJECT.toInvokeStatic(cpg, instrumentationClass));
+ appendInstruction(instructionList, toAppend, instructionHandle);
+ }
+
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ObjectAccessInstrumentator.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ObjectAccessInstrumentator.java
new file mode 100644
index 000000000..c476178d6
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ObjectAccessInstrumentator.java
@@ -0,0 +1,371 @@
+package microbat.instrumentation.instr.aggreplay;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.ArrayInstruction;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.DUP;
+import org.apache.bcel.generic.DUP2;
+import org.apache.bcel.generic.DUP2_X1;
+import org.apache.bcel.generic.DUP2_X2;
+import org.apache.bcel.generic.DUP_X2;
+import org.apache.bcel.generic.FieldInstruction;
+import org.apache.bcel.generic.GETFIELD;
+import org.apache.bcel.generic.GETSTATIC;
+import org.apache.bcel.generic.INVOKESTATIC;
+import org.apache.bcel.generic.InstructionHandle;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.InvokeInstruction;
+import org.apache.bcel.generic.LASTORE;
+import org.apache.bcel.generic.LDC;
+import org.apache.bcel.generic.MONITORENTER;
+import org.apache.bcel.generic.MethodGen;
+import org.apache.bcel.generic.NEWARRAY;
+import org.apache.bcel.generic.POP;
+import org.apache.bcel.generic.POP2;
+import org.apache.bcel.generic.PUTFIELD;
+import org.apache.bcel.generic.PUTSTATIC;
+import org.apache.bcel.generic.SWAP;
+import org.apache.bcel.generic.Type;
+
+import javassist.bytecode.Opcode;
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.filter.UserFilters;
+import microbat.instrumentation.instr.AbstractInstrumenter;
+import microbat.instrumentation.instr.GeneratedMethods;
+import microbat.instrumentation.instr.MethodSplitter;
+import microbat.instrumentation.instr.TracerMethods;
+import microbat.instrumentation.instr.instruction.info.EntryPoint;
+import microbat.instrumentation.model.id.AggrePlayMethods;
+import microbat.instrumentation.runtime.ExecutionTracer;
+import microbat.instrumentation.utils.MicrobatUtils;
+
+public abstract class ObjectAccessInstrumentator extends AbstractInstrumenter {
+
+ protected Class> agentClass;
+
+ private EntryPoint entryPoint;
+ private UserFilters userFilters;
+ private Set requireSplittingMethods = Collections.emptySet();
+
+ /**
+ * Fired after a new object is created
+ */
+ public static final String ON_NEW_OBJECT = "_onNewObject";
+ public static final String ON_NEW_OBJECT_SIG = "(Ljava/lang/Object;)V";
+
+ // for getfield and putfield instructions
+ /**
+ * Before object write
+ */
+ public static final String ON_OBJECT_WRITE = "_onObjectWrite";
+ public static final String ON_OBJECT_WRITE_SIG = "(Ljava/lang/Object;Ljava/lang/String;)V";
+ /**
+ * Before object read
+ */
+ public static final String ON_OBJECT_READ = "_onObjectRead";
+ public static final String ON_OBJECT_READ_SIG = "(Ljava/lang/Object;Ljava/lang/String;)V";
+
+ public ObjectAccessInstrumentator(Class> agentClass, AgentParams agentParams) {
+ this.agentClass = agentClass;
+ this.entryPoint = agentParams.getEntryPoint();
+ if (agentParams.isRequireMethodSplit()) {
+ this.requireSplittingMethods = agentParams.getOverlongMethods();
+ }
+ }
+
+ @Override
+ protected boolean instrumentMethod(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
+ boolean isAppClass, boolean isMainMethod, boolean isEntry) {
+
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ private int addFieldName(ConstantPoolGen constPool, FieldInstruction getfield) {
+ String fieldName = getfield.getName(constPool);
+ return constPool.addString(fieldName);
+ }
+
+
+ @Override
+ protected byte[] instrument(String classFName, String className, JavaClass jc) {
+ ClassGen classGen = new ClassGen(jc);
+ ConstantPoolGen constPool = classGen.getConstantPool();
+ String agentClassString = agentClass.getName().replace(".", "/");
+ final int onNewObjectMethod =
+ constPool.addMethodref(agentClassString, ON_NEW_OBJECT, ON_NEW_OBJECT_SIG);
+ final int onObjectWrite =
+ constPool.addMethodref(agentClassString, ON_OBJECT_WRITE, ON_OBJECT_WRITE_SIG);
+ final int onObjectRead =
+ constPool.addMethodref(agentClassString, ON_OBJECT_READ, ON_OBJECT_READ_SIG);
+
+ final INVOKESTATIC onNewObjectInvoke = new INVOKESTATIC(onNewObjectMethod);
+ final INVOKESTATIC onObjectWriteInvoke = new INVOKESTATIC(onObjectWrite);
+ final INVOKESTATIC onObjectReadInvoke = new INVOKESTATIC(onObjectRead);
+ final DUP dup = new DUP();
+ boolean entry = entryPoint == null ? false : className.equals(entryPoint.getClassName());
+ for (Method method : classGen.getMethods()) {
+ if (method.isNative() || method.isAbstract() || method.getCode() == null) {
+ continue; // Only instrument methods with code in them!
+ }
+ MethodGen mGen = new MethodGen(method, className, constPool);
+ InstructionList iList = mGen.getInstructionList();
+ InstructionHandle[] ihs = iList.getInstructionHandles();
+ boolean isMainMethod = entry && entryPoint.matchMethod(method.getName(), method.getSignature());
+ if (isMainMethod) {
+ iList.insert(AggrePlayMethods.START.toInvokeStatic(constPool, ExecutionTracer.class));
+ }
+ instrumentMethod(constPool, onNewObjectInvoke, onObjectWriteInvoke, onObjectReadInvoke, dup, mGen, iList,
+ ihs);
+ checkSplitMethods(mGen, classGen, method);
+ }
+
+ return classGen.getJavaClass().getBytes();
+ }
+
+ private void checkSplitMethods(MethodGen mg, ClassGen classGen, Method oldMethod) {
+ String methodFullName = MicrobatUtils.getMicrobatMethodFullName(classGen.getClassName(), mg.getMethod());
+ if (requireSplittingMethods.contains(methodFullName)) {
+ MethodSplitter methodSplitter = new MethodSplitter(classGen, classGen.getConstantPool());
+ GeneratedMethods generatedMethods = methodSplitter.splitMethod(mg);
+ for (MethodGen newMethodGen : generatedMethods.getExtractedMethods()) {
+ newMethodGen.setMaxStack();
+ newMethodGen.setMaxLocals();
+ classGen.addMethod(newMethodGen.getMethod());
+ }
+ MethodGen rootMethodGen = generatedMethods.getRootMethod();
+ InstructionList iList = rootMethodGen.getInstructionList();
+ iList.setPositions();
+ rootMethodGen.setMaxStack();
+ rootMethodGen.setMaxLocals();
+ classGen.replaceMethod(oldMethod, rootMethodGen.getMethod());;
+ } else {
+ InstructionList instructionList = mg.getInstructionList();
+ instructionList.setPositions();
+ mg.setMaxStack();
+ mg.setMaxLocals();
+ classGen.replaceMethod(oldMethod, mg.getMethod());
+ }
+ }
+
+ private void instrumentMethod(ConstantPoolGen constPool, final INVOKESTATIC onNewObjectInvoke,
+ final INVOKESTATIC onObjectWriteInvoke, final INVOKESTATIC onObjectReadInvoke, final DUP dup,
+ MethodGen mGen, InstructionList iList, InstructionHandle[] ihs) {
+ for (InstructionHandle handle: ihs) {
+ InstructionList newList = new InstructionList();
+ if (handle.getInstruction().getOpcode() == Opcode.NEW) {
+ newList.append(dup);
+ newList.append(onNewObjectInvoke);
+ if(handle.getNext() == null) {
+ iList.append(newList);
+ } else {
+ insertInsnHandler(iList, newList, handle.getNext());
+ }
+ newList.dispose();
+ continue;
+ }
+ if (handle.getInstruction().getOpcode() == Opcode.GETFIELD) {
+ GETFIELD getfield = (GETFIELD) handle.getInstruction();
+ instrumentGetField(constPool, onObjectReadInvoke, iList, handle, getfield);
+ continue;
+ }
+ if (handle.getInstruction().getOpcode() == Opcode.PUTFIELD) {
+ PUTFIELD putField = (PUTFIELD) handle.getInstruction();
+ instrumentPutField(constPool, onObjectWriteInvoke, iList, handle, putField);
+ continue;
+ }
+ if (handle.getInstruction().getOpcode() == Opcode.MONITORENTER) {
+ instrumentMonitorEnter(constPool, iList, handle);
+ continue;
+ }
+ if (handle.getInstruction() instanceof ArrayInstruction) {
+ instrumentArrayAccess(constPool, iList, handle);
+ continue;
+ }
+ if (handle.getInstruction().getOpcode() == Opcode.GETSTATIC) {
+ instrumentGetStaticInstruction(constPool, iList, handle);
+ continue;
+ }
+ if (handle.getInstruction().getOpcode() == Opcode.PUTSTATIC) {
+ instrumentPutStatic(constPool, iList, handle);
+ continue;
+ }
+ if (handle.getInstruction().getOpcode() == Opcode.MULTIANEWARRAY
+ || handle.getInstruction().getOpcode() == Opcode.NEWARRAY
+ || handle.getInstruction().getOpcode() == Opcode.ANEWARRAY) {
+ instrumentNewArray(constPool, iList, handle);
+ continue;
+ }
+
+ if (handle.getInstruction() instanceof InvokeInstruction) {
+ instrumentMethodReturn(constPool, iList, handle);
+ continue;
+ }
+ }
+ }
+
+ protected void instrumentMethodReturn(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ InstructionList toAppend = new InstructionList();
+ String signature = ((InvokeInstruction) ih.getInstruction()).getReturnType(cpg).getSignature();
+ if (signature.startsWith("L")) {
+ toAppend.append(new DUP());
+ toAppend.append(AggrePlayMethods.ASSERT_OBJECT_EXISTS.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, toAppend, ih);
+ } else if (signature.startsWith("[")) {
+ toAppend.append(new DUP());
+ toAppend.append(AggrePlayMethods.ASSERT_ARRAY_EXISTS.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, toAppend, ih);
+ }
+ }
+
+ /**
+ * Instruments NEWARRAY or MULTIANEWARRAY
+ * @param cpg
+ * @param il
+ * @param il
+ */
+ protected void instrumentNewArray(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ InstructionList toAppend = new InstructionList();
+ toAppend.append(new DUP()); // arrayRef, arrayRef
+ toAppend.append(AggrePlayMethods.ON_NEW_ARRAY.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, toAppend, ih);
+ }
+
+
+ protected void instrumentPutStatic(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ InstructionList beforeInstructionList = new InstructionList();
+ InstructionList afterInstructionList = new InstructionList();
+
+ PUTSTATIC ps = (PUTSTATIC) ih.getInstruction();
+
+ // ldc class + field
+ beforeInstructionList.append(new LDC(cpg.addString(ps.getReferenceType(cpg).getSignature())));
+ beforeInstructionList.append(new LDC(cpg.addString(ps.getFieldName(cpg))));
+ beforeInstructionList.append(AggrePlayMethods.BEFORE_STATIC_WRITE.toInvokeStatic(cpg, agentClass));
+ insertInsnHandler(il, beforeInstructionList, ih);
+ }
+
+ protected void instrumentGetStaticInstruction(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ InstructionList beforeInstructionList = new InstructionList();
+ GETSTATIC ps = (GETSTATIC) ih.getInstruction();
+ // ldc class + field
+ beforeInstructionList.append(new LDC(cpg.addString(ps.getReferenceType(cpg).getSignature())));
+ beforeInstructionList.append(new LDC(cpg.addString(ps.getFieldName(cpg))));
+ beforeInstructionList.append(AggrePlayMethods.BEFORE_STATIC_READ.toInvokeStatic(cpg, agentClass));
+ insertInsnHandler(il, beforeInstructionList, ih);
+ }
+
+ protected void instrumentArrayAccess(ConstantPoolGen cpg,
+ InstructionList instructionList, InstructionHandle ih) {
+ switch (ih.getInstruction().getOpcode()) {
+ case Const.AALOAD:
+ case Const.BALOAD:
+ case Const.CALOAD:
+ case Const.DALOAD:
+ case Const.FALOAD:
+ case Const.IALOAD:
+ case Const.LALOAD:
+ case Const.SALOAD:
+ instrumentArrayRead(cpg, instructionList, ih);
+ break;
+ // deal with store
+ default:
+ instrumentArrayWrite(cpg, instructionList, ih);
+ break;
+ }
+ }
+
+ protected void instrumentArrayRead(ConstantPoolGen cpg,
+ InstructionList il, InstructionHandle ih) {
+ InstructionList befInstructionList = new InstructionList();
+ befInstructionList.append(new DUP2());
+ befInstructionList.append(AggrePlayMethods.BEFORE_ARRAY_READ.toInvokeStatic(cpg, agentClass));
+ insertInsnHandler(il, befInstructionList, ih);
+ }
+
+ protected void instrumentArrayWrite(ConstantPoolGen cpg,
+ InstructionList il, InstructionHandle ih) {
+
+ InstructionList befInstructionList = new InstructionList();
+ boolean c2 = (ih.getInstruction().getOpcode() == Const.LASTORE
+ || ih.getInstruction().getOpcode() == Const.DASTORE);
+ if (c2) {
+ // arra, index, value1, value2
+ befInstructionList.append(new DUP2_X2());
+ befInstructionList.append(new POP2());
+ befInstructionList.append(new DUP2_X2());
+ } else {
+ befInstructionList.append(new DUP_X2());
+ befInstructionList.append(new POP());
+ befInstructionList.append(new DUP2_X1());
+ }
+ befInstructionList.append(AggrePlayMethods.BEFORE_ARRAY_WRITE.toInvokeStatic(cpg, agentClass));
+ insertInsnHandler(il, befInstructionList, ih);
+ }
+
+
+ protected void instrumentMonitorEnter(ConstantPoolGen constPool, InstructionList instructionList,
+ InstructionHandle handle) {
+ InstructionList beforeInstructionList = new InstructionList();
+ InstructionList afterInstructionList = new InstructionList();
+ beforeInstructionList.append(new DUP());
+ afterInstructionList.append(createInvokeStatic(constPool, agentClass, AggrePlayMethods.ON_LOCK_ACQUIRE));
+ insertInsnHandler(instructionList, beforeInstructionList, handle);
+ appendInstruction(instructionList, afterInstructionList, handle);
+ }
+
+ protected void instrumentPutField(ConstantPoolGen constPool, final INVOKESTATIC onObjectWriteInvoke, InstructionList iList,
+ InstructionHandle handle, PUTFIELD putField) {
+ InstructionList newList = new InstructionList();
+ int fieldRef = addFieldName(constPool, putField);
+ Type fieldType = putField.getType(constPool);
+ if (fieldType.equals(Type.LONG)
+ || fieldType.equals(Type.DOUBLE)) {
+ newList.append(new DUP2_X1());
+ newList.append(new POP2());
+ newList.append(new DUP());
+ } else {
+ newList.append(new SWAP());
+ newList.append(new DUP()); // value, object, object
+ }
+
+ newList.append(new LDC(fieldRef)); // object, object, fieldName
+ newList.append(onObjectWriteInvoke); // object
+ if (fieldType.equals(Type.LONG) || fieldType.equals(Type.DOUBLE)) {
+ newList.append(new DUP_X2());
+ newList.append(new POP());
+ } else {
+ newList.append(new SWAP());
+ }
+ insertInsnHandler(iList, newList, handle);
+ newList.dispose();
+ }
+
+ protected void instrumentGetField(
+ ConstantPoolGen constPool, final INVOKESTATIC onObjectReadInvoke,
+ InstructionList iList, InstructionHandle handle, GETFIELD getfield) {
+ InstructionList newList = new InstructionList();
+ int fieldRef = addFieldName(constPool, getfield);
+ newList.append(new DUP()); // object, object
+ newList.append(new LDC(fieldRef)); // object, object, fieldName
+ newList.append(onObjectReadInvoke); // object
+ insertInsnHandler(iList, newList, handle);
+ newList.dispose();
+ }
+
+ protected INVOKESTATIC createInvokeStatic(ConstantPoolGen cpg, Class> clazz, AggrePlayMethods method) {
+ return createInvokeStatic(cpg, clazz, method.methodName, method.methodSig);
+ }
+
+ protected INVOKESTATIC createInvokeStatic(ConstantPoolGen cpg, Class> clazz, String methodName, String signature) {
+ return new INVOKESTATIC(cpg.addMethodref(clazz.getName().replace(".", "/"), methodName, signature));
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ReplayMode.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ReplayMode.java
new file mode 100644
index 000000000..156694362
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ReplayMode.java
@@ -0,0 +1,25 @@
+package microbat.instrumentation.instr.aggreplay;
+
+public enum ReplayMode {
+ STRICT_RW("strict_rw"),
+ RELAX_RW("relax_rw"),
+ AGGR("aggr_rw");
+
+ private String idString;
+ private ReplayMode(String name) {
+ idString = name;
+ }
+
+ public String getIdString() {
+ return this.idString;
+ }
+
+ public static ReplayMode parse(String value) {
+ try {
+ return ReplayMode.valueOf(value);
+ } catch (IllegalArgumentException arg) {
+ return AGGR;
+ }
+
+ }
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ThreadIdInstrumenter.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ThreadIdInstrumenter.java
new file mode 100644
index 000000000..c7d7405ed
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/ThreadIdInstrumenter.java
@@ -0,0 +1,86 @@
+package microbat.instrumentation.instr.aggreplay;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.ALOAD;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.INVOKESTATIC;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.MethodGen;
+
+import microbat.instrumentation.instr.AbstractInstrumenter;
+import microbat.instrumentation.instr.aggreplay.agents.SharedVariableAgent;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
+import microbat.instrumentation.runtime.ExecutionTracer;
+
+/**
+ * Instrumenter solely for generating thread id
+ * @author Gabau
+ *
+ */
+public class ThreadIdInstrumenter extends AbstractInstrumenter {
+
+ // the class containing _onThreadStart (Ljava/lang/Thread;)V, IMPT!! should not be interface
+ private Class> instrumentedClass = getClass();
+
+ public ThreadIdInstrumenter() {}
+
+ public ThreadIdInstrumenter(Class> instrumentedClass) {
+ this.instrumentedClass = instrumentedClass;
+ }
+
+ public static void _onThreadStart(Thread thread) {
+ if (ExecutionTracer.isRecordingOrStarted()) {
+ ThreadIdGenerator.threadGenerator.createId(thread);
+ }
+ }
+
+
+ @Override
+ protected boolean shouldInstrument(String className) {
+ return className.equals(Thread.class.getName());
+ }
+
+ @Override
+ protected byte[] instrument(String classFName, String className, JavaClass jc) {
+ System.out.println("Instrumeted thread " + classFName);
+ ClassGen classGen = new ClassGen(jc);
+ Method startMethod = null;
+ for (Method method: classGen.getMethods()) {
+ if ("start".equals(method.getName())) {
+ startMethod = method;
+ }
+ }
+ instrumentStartMethod(classGen, startMethod);
+ return classGen.getJavaClass().getBytes();
+ }
+
+ protected void instrumentStartMethod(ClassGen cg, Method startMethod) {
+ MethodGen mGen = new MethodGen(startMethod, cg.getClassName(), cg.getConstantPool());
+ InstructionList iList = mGen.getInstructionList();
+ ConstantPoolGen constantPoolGen = cg.getConstantPool();
+ String threadStartClass = instrumentedClass.getName().replace(".", "/");
+ ALOAD aload = new ALOAD(0);
+ INVOKESTATIC invokestatic = new INVOKESTATIC(constantPoolGen.addMethodref(
+ threadStartClass, "_onThreadStart", "(Ljava/lang/Thread;)V"));
+ iList.insert(invokestatic);
+ iList.insert(aload);
+ iList.setPositions();
+ mGen.setMaxLocals();
+ mGen.setMaxStack();
+ cg.replaceMethod(startMethod, mGen.getMethod());
+ }
+
+
+ @Override
+ protected boolean instrumentMethod(ClassGen classGen, ConstantPoolGen constPool, MethodGen methodGen, Method method,
+ boolean isAppClass, boolean isMainMethod, boolean isEntry) {
+ return false;
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/TimeoutThread.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/TimeoutThread.java
new file mode 100644
index 000000000..22758c894
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/TimeoutThread.java
@@ -0,0 +1,51 @@
+package microbat.instrumentation.instr.aggreplay;
+
+import microbat.instrumentation.Agent;
+import microbat.instrumentation.AgentLogger;
+import microbat.instrumentation.runtime.ExecutionTracer;
+
+public class TimeoutThread extends Thread {
+
+ private Agent attachedAgent = null;
+ public static final String ID = "Timeout-thread$$";
+ public static final String TIMEOUT_MSG = "false;Timeout";
+
+ private long timeOut = 10000L;
+
+ public TimeoutThread(Agent attachedAgent) {
+ this.attachedAgent = attachedAgent;
+ super.setName(ID);
+ super.setDaemon(true);
+ }
+
+ public void setTimeout(long timeOut) {
+ this.timeOut = timeOut;
+ }
+
+ public TimeoutThread() {
+ super.setName(ID);
+ super.setDaemon(true);
+ }
+ @Override
+ public void run() {
+ // the timeout is forever
+ if (timeOut < 0) return;
+ try {
+ Thread.sleep(timeOut);
+ } catch (InterruptedException e) {
+ }
+ AgentLogger.debug("Interrupted program due to timeout");
+ ExecutionTracer.shutdown();
+ Agent._forceProgramStop(TIMEOUT_MSG);
+// if (attachedAgent == null) {
+// Agent._exitProgram("false;Timeout");
+// } else {
+//// try {
+//// attachedAgent.shutdown();
+//// } catch (Exception e) {
+//// e.printStackTrace();
+//// }
+// }
+// System.exit(1);
+ }
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/AggrePlayRecordingRWAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/AggrePlayRecordingRWAgent.java
new file mode 100644
index 000000000..ab01eb106
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/AggrePlayRecordingRWAgent.java
@@ -0,0 +1,139 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.stream.Collectors;
+
+import javassist.compiler.ast.Pair;
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.instr.aggreplay.ObjectAccessInstrumentator;
+import microbat.instrumentation.instr.aggreplay.output.SharedVariableOutput;
+import microbat.instrumentation.instr.aggreplay.record.RecordingInstrumentor;
+import microbat.instrumentation.instr.aggreplay.shared.BasicTransformer;
+import microbat.instrumentation.instr.aggreplay.shared.RecordingOutput;
+import microbat.instrumentation.model.generator.ObjectIdGenerator;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.MemoryLocation;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.ReadWriteAccessList;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+import microbat.instrumentation.runtime.ExecutionTracer;
+
+/**
+ * Agent used for recording data.
+ * Only records read and write.
+ * @author Gabau
+ *
+ */
+public class AggrePlayRecordingRWAgent extends RNRRecordingAgent {
+
+ private Semaphore smp = new Semaphore(1);
+ private Map> lockAcquisitionListMap = new HashMap<>();
+
+
+
+ protected AggrePlayRecordingRWAgent(CommandLine cml) {
+ super(cml);
+ }
+
+ @Override
+ protected void onLockAcquire(Object object) {
+ ObjectId oId = objectIdGenerator.getId(object);
+ if (!lockAcquisitionListMap.containsKey(oId)) {
+ lockAcquisitionListMap.put(oId, new LinkedList());
+ }
+ lockAcquisitionListMap.get(oId).add(new Event(null));
+ }
+ @Override
+ protected RecordingOutput getRecordingOutput() {
+ RecordingOutput result = new RecordingOutput(new ReadWriteAccessList(), ThreadIdGenerator.threadGenerator.getThreadIds(),
+ sharedGenerator.getAllLocations(),
+ lockAcquisitionListMap);
+ return result;
+ }
+
+ @Override
+ protected void parseSharedOutput(SharedVariableOutput svo) {
+
+ }
+
+ @Override
+ public void startTest(String junitClass, String junitMethod) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void finishTest(String junitClass, String junitMethod) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public ClassFileTransformer getTransformer0() {
+ return new BasicTransformer(new RecordingInstrumentor(RNRRecordingAgent.class, agentParams));
+ }
+
+ @Override
+ public void retransformBootstrapClasses(Instrumentation instrumentation, Class>[] retransformableClasses)
+ throws Exception {
+ instrumentation.retransformClasses(retransformableClasses);
+ }
+
+ @Override
+ public void exitTest(String testResultMsg, String junitClass, String junitMethod, long threadId) {
+
+ }
+
+ @Override
+ public boolean isInstrumentationActive0() {
+ return !ExecutionTracer.isShutdown();
+ }
+
+ @Override
+ public void acquireLock() {
+ try {
+ smp.acquire();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void releaseLock() {
+ smp.release();
+ }
+
+ @Override
+ protected void onRead(SharedMemoryLocation sml) {
+ Event readEvent = new Event(sml);
+ lastEvent.set(readEvent);
+ acquireLock();
+ sml.appendExList(sml.getLastWrite(), readEvent);
+ }
+ @Override
+ protected void onWrite(SharedMemoryLocation sml) {
+ Event writeEvent = new Event(sml);
+ lastEvent.set(writeEvent);
+ acquireLock();
+ sml.write(writeEvent);
+ }
+
+ @Override
+ protected void afterWrite() {
+ releaseLock();
+ }
+
+ @Override
+ protected void afterRead() {
+ releaseLock();
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/AggrePlayReplayAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/AggrePlayReplayAgent.java
new file mode 100644
index 000000000..f6159280f
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/AggrePlayReplayAgent.java
@@ -0,0 +1,465 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.security.KeyStore.Entry;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.function.Supplier;
+
+import microbat.instrumentation.Agent;
+import microbat.instrumentation.AgentLogger;
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.TraceAgent;
+import microbat.instrumentation.instr.SystemClassTransformer;
+import microbat.instrumentation.instr.ThreadInstrumenter;
+import microbat.instrumentation.instr.TraceInstrumenter;
+import microbat.instrumentation.instr.TraceTransformer;
+import microbat.instrumentation.instr.aggreplay.AggrePlayTraceInstrumenter;
+import microbat.instrumentation.instr.aggreplay.ThreadIdInstrumenter;
+import microbat.instrumentation.instr.aggreplay.shared.BasicTransformer;
+import microbat.instrumentation.instr.aggreplay.shared.ParseData;
+import microbat.instrumentation.instr.aggreplay.shared.RecordingOutput;
+import microbat.instrumentation.instr.aggreplay.shared.SharedDataParser;
+import microbat.instrumentation.model.ReadWriteAccessListReplay;
+import microbat.instrumentation.model.SharedMemGeneratorInitialiser;
+import microbat.instrumentation.model.generator.ObjectIdGenerator;
+import microbat.instrumentation.model.generator.SharedMemoryGenerator;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.ReadCountVector;
+import microbat.instrumentation.model.id.ReadWriteAccessList;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.instrumentation.output.StorableReader;
+import microbat.instrumentation.runtime.ExecutionTracer;
+import microbat.instrumentation.runtime.IExecutionTracer;
+import microbat.model.trace.Trace;
+import microbat.sql.Recorder;
+
+public class AggrePlayReplayAgent extends TraceAgent {
+
+ private AgentParams agentParams;
+
+ /**
+ * Current replay values
+ */
+ protected ObjectIdGenerator objectIdGenerator = new ObjectIdGenerator();
+ protected SharedMemoryGenerator sharedMemGenerator = new SharedMemoryGenerator(objectIdGenerator);
+ private ClassFileTransformer transformer;
+ private static AggrePlayReplayAgent attachedAgent;
+ protected ReadCountVector rcVector = new ReadCountVector();
+ // null if the last event not was shared
+ ThreadLocal lastEventLocal = ThreadLocal.withInitial(new Supplier() {
+ @Override
+ public Event get() {
+ return null;
+ }
+ });
+
+ ThreadLocal> lastObjStackLocal = ThreadLocal.withInitial(() -> new Stack<>());
+
+ // maps from recording thread to replay
+ private HashMap threadIdMap = new HashMap<>();
+
+ /**
+ * Recorded output
+ */
+ private ReadWriteAccessList rwal;
+ protected RecordingOutput recordingOutput;
+ private HashMap recordedThreadIdMap = new HashMap<>();
+ protected Map> lockAcquisitionMap;
+ private ReadWriteAccessListReplay rwalGeneratedAccessListReplay;
+
+
+ protected static boolean cannotRecord() {
+ return !ExecutionTracer.isRecordingOrStarted();
+ }
+
+
+ public static void _assertObjectExists(Object obj) {
+ if (cannotRecord()) return;
+ if (attachedAgent.objectIdGenerator.getId(obj) != null) {
+ return;
+ }
+ _onNewObject(obj);
+ }
+
+ public static void _assertArrayExists(Object object) {
+ if (cannotRecord()) return;
+ attachedAgent.assertArrayExists(object);
+ }
+
+ protected void assertArrayExists(Object object) {
+ sharedMemGenerator.assertArray(object);
+ }
+
+ public static void _onNewObject(Object object) {
+ if (cannotRecord()) return;
+ attachedAgent.onObjectCreate(object);
+ }
+
+ private void onObjectCreate(Object object) {
+ objectIdGenerator.createId(object);
+ }
+
+ public static void _onThreadStart(Thread thread) {
+ if (cannotRecord()) return;
+ if (ExecutionTracer.isRecordingOrStarted()) {
+ attachedAgent.onThreadStart(thread);
+ }
+
+ }
+
+ /**
+ * Instruemented before the lock acquisition
+ * @param object
+ */
+ public static void _onLockAcquire(Object object) {
+ if (cannotRecord()) return;
+ attachedAgent.onLockAcquire(object);
+ }
+
+ protected void onLockAcquire(Object obj) {
+ ObjectId oid = this.sharedMemGenerator.ofObjectOrArray(obj);
+
+ Stack eventStack = this.lockAcquisitionMap.get(oid);
+ Event currEvent = new Event(null, getPreviousThreadId());
+ if (eventStack == null || eventStack.empty() || !currEvent.equals(eventStack.peek())) {
+ Thread.yield();
+ }
+ lastObjStackLocal.set(eventStack);
+ }
+
+ public static void _afterLockAcquire() {
+ if (cannotRecord()) return;
+ attachedAgent.afterLockAcquire();
+ }
+
+ protected void afterLockAcquire() {
+ if (lastObjStackLocal.get() == null) return;
+ if (lastObjStackLocal.get().empty()) return;
+ lastObjStackLocal.get().pop();
+ }
+
+ private void onThreadStart(Thread thread) {
+ threadIdGenerator.createId(thread);
+ ThreadId tId = threadIdGenerator.getId(thread);
+ threadIdMap.put(thread.getId(), recordedThreadIdMap.get(tId));
+ }
+
+ public AggrePlayReplayAgent(CommandLine cmd) {
+ super(cmd);
+ agentParams = AgentParams.initFrom(cmd);
+ this.transformer = new BasicTransformer(new AggrePlayTraceInstrumenter(agentParams));
+ }
+
+
+
+ public static AggrePlayReplayAgent getAttached(CommandLine cmd) {
+ AgentParams parameAgentParams = AgentParams.initFrom(cmd);
+ if (attachedAgent == null) {
+ switch (parameAgentParams.getReplayMode()) {
+ case RELAX_RW:
+ attachedAgent = new LaxRWReplayAgent(cmd);
+ break;
+ case STRICT_RW:
+ attachedAgent = new StrictRWReplay(cmd);
+ break;
+ case AGGR:
+ default:
+ attachedAgent = new AggrePlayReplayAgent(cmd);
+ }
+ }
+ attachedAgent.agentParams = parameAgentParams;
+ return attachedAgent;
+ }
+
+
+ /**
+ * Used to get the thread id from the previous thread which the current thread maps to.
+ * @return
+ */
+ protected long getPreviousThreadId() {
+ Long previousThreadID = threadIdMap.get(Thread.currentThread().getId());
+ if (previousThreadID == null) {
+ ThreadId currentThreadId = threadIdGenerator.getId(Thread.currentThread());
+ if (currentThreadId == null) {
+ return -1;
+ }
+ return recordedThreadIdMap.get(currentThreadId);
+ }
+ return previousThreadID;
+ }
+
+ private void onObjectRead(Object object, String field) {
+ if (!attachedAgent.sharedMemGenerator.isSharedObject(object, field)) {
+ lastEventLocal.set(null);
+ return;
+ }
+
+ long previousThreadId = getPreviousThreadId();
+ SharedMemoryLocation sharedMemLocation = attachedAgent.sharedMemGenerator.ofField(object, field);
+ // consider a better alternative
+ onRead(previousThreadId, sharedMemLocation);
+ }
+
+ protected void onRead(long previousThreadId, SharedMemoryLocation sharedMemLocation) {
+ Event readEvent = new Event(sharedMemLocation, previousThreadId);
+ lastEventLocal.set(readEvent);
+ if (!sharedMemLocation.isSameAsLastWrite(readEvent)) {
+ Thread.yield();
+ lastEventLocal.set(null);
+ }
+ }
+ public static void _onStaticRead(String className, String fieldName) {
+ if (cannotRecord()) return;
+ attachedAgent.onStaticRead(className, fieldName);
+ }
+
+ public static void _onStaticWrite(String className, String fieldName) {
+ if (cannotRecord()) return;
+ attachedAgent.onStaticWrite(className, fieldName);
+ }
+
+ public static void _onNewArray(Object object) {
+ if (cannotRecord()) return;
+ attachedAgent.onNewArray(object);
+ }
+
+ protected void onNewArray(Object object) {
+ this.sharedMemGenerator.newArray(object);
+ }
+
+ public static void _onArrayRead(Object arrayRef, int index) {
+ if (cannotRecord()) return;
+ _assertArrayExists(arrayRef);
+ attachedAgent.onArrayRead(arrayRef, index);
+ }
+
+ public static void _onArrayWrite(Object arrayRef, int index) {
+ if (cannotRecord()) return;
+ _assertArrayExists(arrayRef);
+ attachedAgent.onArrayWrite(arrayRef, index);
+ }
+
+ public static void _onObjectRead(Object object, String field) {
+ if (cannotRecord()) return;
+ _assertObjectExists(object);
+ attachedAgent.onObjectRead(object, field);
+ }
+
+ public static void _onObjectWrite(Object object, String field) {
+ if (cannotRecord()) return;
+ _assertObjectExists(object);
+ attachedAgent.onObjectWrite(object, field);
+ }
+
+ protected void onStaticWrite(String className, String fieldName) {
+ if (!sharedMemGenerator.isSharedStaticField(className, fieldName)) {
+ lastEventLocal.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedMemGenerator.ofStaticField(className, fieldName);
+ this.onWrite(getPreviousThreadId(), sml);
+ }
+
+ protected void onStaticRead(String className, String fieldName) {
+ if (!sharedMemGenerator.isSharedStaticField(className, fieldName)) {
+ lastEventLocal.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedMemGenerator.ofStaticField(className, fieldName);
+ this.onRead(getPreviousThreadId(), sml);
+ }
+
+ protected void onArrayWrite(Object arrayRef, int index) {
+ if (!sharedMemGenerator.isSharedArray(arrayRef, index)) {
+ lastEventLocal.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedMemGenerator.ofArray(arrayRef, index);
+ this.onWrite(getPreviousThreadId(), sml);
+ }
+
+ protected void onArrayRead(Object arrayRef, int index) {
+ if (!sharedMemGenerator.isSharedArray(arrayRef, index)) {
+ lastEventLocal.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedMemGenerator.ofArray(arrayRef, index);
+ this.onRead(getPreviousThreadId(), sml);
+ }
+
+ private void onObjectWrite(Object object, String field) {
+ if (!sharedMemGenerator.isSharedObject(object, field)) {
+ lastEventLocal.set(null);
+ return;
+ }
+ long p_tid = getPreviousThreadId();
+ SharedMemoryLocation shm = sharedMemGenerator.ofField(object, field);
+ onWrite(p_tid, shm);
+
+ }
+
+ protected void onWrite(long p_tid, SharedMemoryLocation shm) {
+ Event writeEvent = new Event(shm, p_tid);
+ synchronized (rcVector) {
+ rcVector.updateReadVectors(shm.getLocation(), p_tid);
+ }
+
+ if (!shm.isSameAsPrevRunWrite(writeEvent) || !checkReads(p_tid)) {
+ Thread.yield();
+ lastEventLocal.set(null);
+ return;
+ }
+ lastEventLocal.set(writeEvent);
+ }
+
+ public static void _afterObjectWrite() {
+ if (cannotRecord()) return;
+ attachedAgent.afterObjectWrite();
+ }
+
+ public boolean checkReads(long threadId) {
+ return rwalGeneratedAccessListReplay.checkRead(rcVector, threadId);
+ }
+
+ protected void afterObjectWrite() {
+ if (lastEventLocal.get() == null) {
+ return;
+ }
+ SharedMemoryLocation mlcation = lastEventLocal.get().getLocation();
+ mlcation.addRepWriteEvent(lastEventLocal.get());
+ mlcation.popEvent();
+
+ }
+ public static void _afterObjectRead() {
+ if (cannotRecord()) return;
+ attachedAgent.afterObjectRead();
+ }
+
+ protected boolean wasShared() {
+ return lastEventLocal.get() != null;
+ }
+
+ protected void afterObjectRead() {
+ if (!wasShared()) return;
+ long t = getPreviousThreadId();
+ SharedMemoryLocation mLocation = lastEventLocal.get().getLocation();
+ // TODO(Gab): can this run in parallel?
+ synchronized (mLocation) {
+ rcVector.increment(t, lastEventLocal.get().getLocation().getLocation());
+ mLocation.popRecordedLastWR();
+ }
+ }
+
+ private void initialiseLockAcquisitionMap(Map> lockAcquisitionMap) {
+ HashMap> result = new HashMap<>();
+ for (Map.Entry> entry : lockAcquisitionMap.entrySet()) {
+ Stack toAddEvents = new Stack<>();
+ LinkedList eLLinkedList = new LinkedList<>(entry.getValue());
+ eLLinkedList.descendingIterator().forEachRemaining(v -> toAddEvents.push(v));
+ result.put(entry.getKey(), toAddEvents);
+ }
+ this.lockAcquisitionMap = result;
+ }
+
+ @Override
+ public void startup0(long vmStartupTime, long agentPreStartup) {
+ File concDumpFile = new File(agentParams.getConcDumpFile());
+ try {
+ StorableReader reader = new StorableReader(concDumpFile);
+ RecordingOutput input = new RecordingOutput();
+ List result = reader.read();
+ RecordingOutput output = input.parse(result.get(0));
+ this.rwal = output.rwAccessList;
+ this.recordingOutput = output;
+ this.rwalGeneratedAccessListReplay = new ReadWriteAccessListReplay(rwal);
+ initialiseLockAcquisitionMap(output.lockAcquisitionMap);
+ for (ThreadId threadId: recordingOutput.threadIds) {
+ this.recordedThreadIdMap.put(threadId, threadId.getId());
+ }
+ this.sharedMemGenerator.init(this.recordingOutput);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ System.exit(-1);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ super.startup0(vmStartupTime, agentPreStartup);
+ }
+
+
+ @Override
+ public void shutdown() throws Exception {
+ // Agent._exitProgram(getProgramMsg());
+ System.out.println("Shutting down");
+ try {
+
+ shutdownInner();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ System.out.println("Shutdown");
+ }
+
+ private void shutdownInner() {
+ ExecutionTracer.shutdown();
+ /* collect trace & store */
+ AgentLogger.debug("Building trace dependencies ...");
+// timer.newPoint("Building trace dependencies");
+ // FIXME -mutithread LINYUN [3]
+ // LLT: only trace of main thread is recorded.
+ List tracers = ExecutionTracer.getAllThreadStore();
+
+ int size = tracers.size();
+ List traceList = new ArrayList<>(size);
+ for (IExecutionTracer tracerInner: tracers) {
+
+ ExecutionTracer tracer = (ExecutionTracer) tracerInner;
+
+ Trace trace = tracer.getTrace();
+ trace.setThreadId(tracer.getThreadId());
+ trace.setInnerThreadId(threadIdGenerator.getId(tracer.getThreadId()));
+ trace.setThreadName(tracer.getThreadName());
+ trace.setMain(ExecutionTracer.getMainThreadStore().equals(tracer));
+ trace.setInnerThreadId(threadIdGenerator.getId(trace.getThreadId()));
+ // TODO(Gab): Botch needed because tracer can be initialised
+ // in thread id instrumenter
+ if (trace.getAppJavaClassPath() == null) {
+ trace.setAppJavaClassPath(ExecutionTracer.appJavaClassPath);
+ }
+ constructTrace(trace);
+ traceList.add(trace);
+ }
+
+// timer.newPoint("Saving trace");
+ Recorder.create(agentParams).store(traceList);
+// AgentLogger.debug(timer.getResultString());
+ }
+
+
+ @Override
+ public ClassFileTransformer getTransformer() {
+ // TODO Auto-generated method stub
+ return this.transformer;
+ }
+
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/LaxRWReplayAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/LaxRWReplayAgent.java
new file mode 100644
index 000000000..104cbfc49
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/LaxRWReplayAgent.java
@@ -0,0 +1,93 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.util.Stack;
+import java.util.concurrent.Semaphore;
+
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+
+public class LaxRWReplayAgent extends AggrePlayReplayAgent {
+
+ /**
+ * RW replay with non blocking yield
+ * @param cmd
+ */
+ public LaxRWReplayAgent(CommandLine cmd) {
+ super(cmd);
+ }
+
+ private Semaphore smp = new Semaphore(1);
+
+
+ @Override
+ protected void onLockAcquire(Object obj) {
+
+ ObjectId oid = sharedMemGenerator.ofObjectOrArray(obj);
+ Stack eventStack = this.lockAcquisitionMap.get(oid);
+ Event currEvent = new Event(null, getPreviousThreadId());
+ if (eventStack == null || eventStack.empty() || !currEvent.equals(eventStack.peek())) {
+ Thread.yield();
+ }
+ lastObjStackLocal.set(eventStack);
+ }
+
+ @Override
+ protected void onRead(long previousThreadId, SharedMemoryLocation sharedMemLocation) {
+ // TODO Auto-generated method stub
+ Event readEvent = new Event(sharedMemLocation, previousThreadId);
+ lastEventLocal.set(readEvent);
+ if (!sharedMemLocation.canRead(readEvent)) {
+ Thread.yield();
+ }
+ }
+
+ @Override
+ protected void onWrite(long p_tid, SharedMemoryLocation shm) {
+ Event writeEvent = new Event(shm, p_tid);
+ if (!shm.canWrite(writeEvent)) {
+ Thread.yield();
+ }
+ lastEventLocal.set(writeEvent);
+ }
+
+ private void acquireLock() {
+ try {
+ this.smp.acquire();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void releaseLock() {
+ this.smp.release();
+ }
+
+
+ @Override
+ protected void afterObjectWrite() {
+ if (!wasShared()) {
+ return;
+ }
+ SharedMemoryLocation lastLocation = lastEventLocal.get().getLocation();
+ lastLocation.setLastWrite(lastEventLocal.get());
+ lastLocation.popEvent();
+ }
+
+ @Override
+ protected void afterObjectRead() {
+ if (!wasShared()) {
+ return;
+ }
+ SharedMemoryLocation lastLocation = lastEventLocal.get().getLocation();
+ lastLocation.read(lastEventLocal.get());
+ }
+
+ @Override
+ public void startup0(long vmStartupTime, long agentPreStartup) {
+ super.startup0(vmStartupTime, agentPreStartup);
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/LaxRecordingAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/LaxRecordingAgent.java
new file mode 100644
index 000000000..a2e64de7f
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/LaxRecordingAgent.java
@@ -0,0 +1,172 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.function.Supplier;
+
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.instr.aggreplay.output.SharedVariableOutput;
+import microbat.instrumentation.instr.aggreplay.record.RecordingInstrumentor;
+import microbat.instrumentation.instr.aggreplay.shared.BasicTransformer;
+import microbat.instrumentation.instr.aggreplay.shared.RecordingOutput;
+import microbat.instrumentation.model.RecorderObjectId;
+import microbat.instrumentation.model.generator.ObjectIdGenerator;
+import microbat.instrumentation.model.generator.SharedMemoryGenerator;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.MemoryLocation;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.ReadCountVector;
+import microbat.instrumentation.model.id.ReadWriteAccessList;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.instrumentation.runtime.ExecutionTracer;
+
+/**
+ * Implementration of AggrePlay for non-blocking wait.
+ * @author Gabau
+ *
+ */
+public class LaxRecordingAgent extends RNRRecordingAgent {
+ private ReadCountVector rcVector = new ReadCountVector();
+ private ReadWriteAccessList rwal = new ReadWriteAccessList();
+ private Map> lockAcquisitionListMap = new HashMap<>();
+
+
+ public static final Semaphore LOCK_OBJECT = new Semaphore(1);
+
+ // last write, these two variables are used for computation,
+ // not for the actual last write, last read
+ private ThreadLocal lw = ThreadLocal.withInitial(new Supplier() {
+ @Override
+ public Event get() {
+ return null;
+ }
+ });
+ // last read
+ private ThreadLocal lr = ThreadLocal.withInitial(new Supplier() {
+ @Override
+ public Event get() {
+ return null;
+ }
+ });
+ private boolean wasShared = false;
+
+ protected LaxRecordingAgent(CommandLine cml) {
+ super(cml);
+ }
+
+ @Override
+ protected void onRead(SharedMemoryLocation smLocation) {
+ Event readEvent = new Event(smLocation);
+ lr.set(readEvent);
+ lastEvent.set(readEvent);
+ rcVector.increment(Thread.currentThread().getId(), smLocation.getLocation());
+ _acquireLock();
+ Event lastWrite = smLocation.getLastWrite();
+ lw.set(lastWrite);
+ wasShared = true;
+ }
+
+
+ private void updateReadVectors(Event event) {
+ rcVector.updateReadVectors(
+ event.getLocation().getLocation(), Thread.currentThread().getId());
+ rwal.add(event.getLocation().getLocation(), event, rcVector);
+ }
+
+ @Override
+ protected void onWrite(SharedMemoryLocation sml) {
+ wasShared = true;
+ Event writeEvent = new Event(sml);
+ this.updateReadVectors(writeEvent);
+ sml.write(writeEvent);
+ }
+
+ @Override
+ protected void acquireLock() {
+ try {
+ LOCK_OBJECT.acquire();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected void releaseLock() {
+
+ LOCK_OBJECT.release();
+ }
+
+ @Override
+ protected void afterWrite() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected void afterRead() {
+ if (!this.wasShared) return;
+ _releaseLock();
+ Event lw = this.lw.get();
+ Event lr = this.lr.get();
+ lr.getLocation().appendExList(lw, lr);
+ }
+
+ @Override
+ protected void onLockAcquire(Object lockObject) {
+ ObjectId oId = sharedGenerator.ofObjectOrArray(lockObject);
+ if (!lockAcquisitionListMap.containsKey(oId)) {
+ lockAcquisitionListMap.put(oId, new LinkedList());
+ }
+ lockAcquisitionListMap.get(oId).add(new Event(null));
+ }
+
+ @Override
+ protected RecordingOutput getRecordingOutput() {
+ List threadIds = ThreadIdGenerator.threadGenerator.getThreadIds();
+ RecordingOutput output = new RecordingOutput(rwal, threadIds,
+ sharedGenerator.getAllLocations(),
+ lockAcquisitionListMap);
+ return output;
+ }
+
+ @Override
+ protected void parseSharedOutput(SharedVariableOutput svo) {
+
+ }
+
+
+
+ @Override
+ public ClassFileTransformer getTransformer0() {
+ // TODO Auto-generated method stub
+ return new BasicTransformer(new RecordingInstrumentor(RNRRecordingAgent.class, agentParams));
+ }
+
+ @Override
+ public void retransformBootstrapClasses(Instrumentation instrumentation, Class>[] retransformableClasses)
+ throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void exitTest(String testResultMsg, String junitClass, String junitMethod, long threadId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isInstrumentationActive0() {
+ return !ExecutionTracer.isShutdown();
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/RNRRecordingAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/RNRRecordingAgent.java
new file mode 100644
index 000000000..5003ca57f
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/RNRRecordingAgent.java
@@ -0,0 +1,341 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Supplier;
+
+import microbat.instrumentation.Agent;
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.filter.GlobalFilterChecker;
+import microbat.instrumentation.instr.SystemClassTransformer;
+import microbat.instrumentation.instr.aggreplay.TimeoutThread;
+import microbat.instrumentation.instr.aggreplay.output.SharedVariableOutput;
+import microbat.instrumentation.instr.aggreplay.shared.ParseData;
+import microbat.instrumentation.instr.aggreplay.shared.RecordingOutput;
+import microbat.instrumentation.instr.aggreplay.shared.SharedDataParser;
+import microbat.instrumentation.model.generator.ObjectIdGenerator;
+import microbat.instrumentation.model.generator.SharedMemoryGenerator;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.instrumentation.model.storage.FileStorage;
+import microbat.instrumentation.model.storage.Storable;
+import microbat.instrumentation.output.StorableReader;
+import microbat.instrumentation.output.StorableWriter;
+import microbat.instrumentation.runtime.ExecutionTracer;
+import sav.strategies.dto.AppJavaClassPath;
+
+/**
+ * Abstract class representing a recording class
+ * @author Gabau
+ *
+ */
+public abstract class RNRRecordingAgent extends Agent {
+ private static RNRRecordingAgent recordingAgent;
+ protected ObjectIdGenerator objectIdGenerator = new ObjectIdGenerator();
+ protected SharedMemoryGenerator sharedGenerator = new SharedMemoryGenerator(objectIdGenerator);
+ protected ThreadLocal lastEvent = ThreadLocal.withInitial(new Supplier() {
+ @Override
+ public Event get() {
+ return null;
+ }
+ });
+ protected AgentParams agentParams;
+ protected TimeoutThread timeoutThread;
+
+
+ protected static boolean cannotRecord() {
+ return !ExecutionTracer.isRecordingOrStarted();
+ }
+
+ protected void assertObjectExists(Object object) {
+ if (objectIdGenerator.getId(object) != null) {
+ return;
+ }
+ objectIdGenerator.createId(object);
+ }
+
+ public static void _assertObjectExists(Object object) {
+ if (cannotRecord()) return;
+ recordingAgent.assertObjectExists(object);
+ }
+
+ public static void _assertArrayExists(Object object) {
+ if (cannotRecord()) return;
+ recordingAgent.assertArrayExists(object);
+ }
+
+ protected void assertArrayExists(Object object) {
+ sharedGenerator.assertArray(object);
+ }
+
+
+ public static void _acquireLock() {
+ if (cannotRecord()) return;
+ recordingAgent.acquireLock();
+ }
+
+ public static void _releaseLock() {
+ if (cannotRecord()) return;
+ recordingAgent.releaseLock();
+ }
+
+ public static RNRRecordingAgent getAttached(CommandLine cml) {
+ if (recordingAgent != null) {
+ recordingAgent.agentParams = AgentParams.initFrom(cml);
+ return recordingAgent;
+ }
+ recordingAgent = new LaxRecordingAgent(cml);
+ return recordingAgent;
+ }
+
+
+ protected RNRRecordingAgent(CommandLine cml) {
+ this.agentParams = AgentParams.initFrom(cml);
+ }
+
+ public static void _onNewObject(Object object) {
+ if (cannotRecord()) return;
+ recordingAgent.onObjectCreate(object);
+ }
+
+ public static void _onNewArray(Object obj) {
+ if (cannotRecord()) return;
+ recordingAgent.onNewArray(obj);
+ }
+
+ private void onNewArray(Object obj) {
+ sharedGenerator.newArray(obj);
+ }
+
+ public static void _onObjectRead(Object object, String field) {
+ if (cannotRecord()) return;
+ _assertObjectExists(object);
+ recordingAgent.onObjectRead(object, field);
+ }
+
+ public static void _onObjectWrite(Object object, String field) {
+ if (cannotRecord()) return;
+ _assertObjectExists(object);
+ recordingAgent.onObjectWrite(object, field);
+ }
+
+
+ public static void _afterObjectWrite() {
+ if (cannotRecord()) return;
+ recordingAgent._afterObjectWriteInner();
+ }
+
+
+
+
+ private void _afterObjectWriteInner() {
+ if (this.lastEvent.get() == null) {
+ return;
+ }
+ this.afterWrite();
+ }
+
+ private void _afterObjectReadInner() {
+ if (this.lastEvent.get() == null) {
+ return;
+ }
+ this.afterRead();
+ }
+
+ public static void _afterObjectRead() {
+ if (cannotRecord()) return;
+ recordingAgent._afterObjectReadInner();
+ }
+
+ public static void _onStaticRead(String className, String fieldName) {
+ if (cannotRecord()) return;
+ recordingAgent.onStaticRead(className, fieldName);
+ }
+
+ public static void _onStaticWrite(String className, String fieldName) {
+ if (cannotRecord()) return;
+ recordingAgent.onStaticWrite(className, fieldName);
+ }
+
+ public static void _onArrayRead(Object arrayRef, int index) {
+ if (cannotRecord()) return;
+ _assertArrayExists(arrayRef);
+ recordingAgent.onArrayRead(arrayRef, index);
+ }
+
+ public static void _onArrayWrite(Object arrayRef, int index) {
+ if (cannotRecord()) return;
+ _assertArrayExists(arrayRef);
+ recordingAgent.onArrayWrite(arrayRef, index);
+ }
+
+ /**
+ * Instrumented after the lock has been acquired
+ * @param object
+ */
+ public static void _onLockAcquire(Object object) {
+ if (cannotRecord()) return;
+ recordingAgent.onLockAcquire(object);
+ }
+
+
+ protected abstract void onRead(SharedMemoryLocation sml);
+
+ protected abstract void onWrite(SharedMemoryLocation sml);
+
+ protected abstract void acquireLock();
+ protected abstract void releaseLock();
+
+ /**
+ * After object creation
+ * @param object
+ */
+ protected final void onObjectCreate(Object object) {
+ objectIdGenerator.createId(object);
+ }
+
+ /**
+ * Before object read.
+ * @param object
+ * @param field
+ */
+ protected final void onObjectRead(Object object, String field) {
+ if (!sharedGenerator.isSharedObject(object, field)) {
+ lastEvent.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedGenerator.ofField(object, field);
+ this.onRead(sml);
+ }
+
+ /**
+ * Called before object write
+ * @param object
+ * @param field
+ */
+ protected final void onObjectWrite(Object object, String field) {
+ if (!sharedGenerator.isSharedObject(object, field)) {
+ lastEvent.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedGenerator.ofField(object, field);
+ this.onWrite(sml);
+ }
+
+ /**
+ * Called after object write
+ */
+ protected abstract void afterWrite();
+
+ /**
+ * Called after object read
+ */
+ protected abstract void afterRead();
+
+ protected abstract void onLockAcquire(Object object);
+
+ protected final void onStaticRead(String className, String fieldName) {
+ if (!sharedGenerator.isSharedStaticField(className, fieldName)) {
+ lastEvent.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedGenerator.ofStaticField(className, fieldName);
+ this.onRead(sml);
+ }
+
+ protected final void onStaticWrite(String className, String fieldName) {
+ if (!sharedGenerator.isSharedStaticField(className, fieldName)) {
+ lastEvent.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedGenerator.ofStaticField(className, fieldName);
+ this.onWrite(sml);
+ }
+
+ protected final void onArrayRead(Object arrayRef, int arrayIndex) {
+ if (!sharedGenerator.isSharedArray(arrayRef, arrayIndex)) {
+ lastEvent.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedGenerator.ofArray(arrayRef, arrayIndex);
+ this.onRead(sml);
+ }
+
+ protected final void onArrayWrite(Object arrayRef, int arrayIndex) {
+ if (!sharedGenerator.isSharedArray(arrayRef, arrayIndex)) {
+ lastEvent.set(null);
+ return;
+ }
+ SharedMemoryLocation sml = sharedGenerator.ofArray(arrayRef, arrayIndex);
+ this.onWrite(sml);
+ }
+
+ @Override
+ public void startup0(long vmStartupTime, long agentStartupTime) {
+ timeoutThread = new TimeoutThread(this);
+ timeoutThread.setTimeout(agentParams.getTimeOut());
+ timeoutThread.start();
+ AppJavaClassPath appPath = agentParams.initAppClassPath();
+ GlobalFilterChecker.setup(appPath, agentParams.getIncludesExpression(), agentParams.getExcludesExpression());
+ recordingAgent = this;
+ SystemClassTransformer.attachThreadId(getInstrumentation());
+ String dumpFileStr = agentParams.getDumpFile();
+ if (dumpFileStr == null) dumpFileStr = "temp.txt";
+ File dumpFile = new File(dumpFileStr);
+ StorableReader fileReader = null;
+ try {
+ fileReader = new StorableReader(dumpFile);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Failed to find dump file");
+ }
+
+ try {
+ List data = fileReader.read();
+ SharedVariableOutput svOutput = new SharedVariableOutput(data.get(0));
+ sharedGenerator.init(svOutput);
+ parseSharedOutput(svOutput);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ File file = new File(this.agentParams.getConcDumpFile());
+ try {
+ StorableWriter writer = new StorableWriter(file);
+ RecordingOutput output = getRecordingOutput();
+ writer.writeStorable(output);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ protected abstract RecordingOutput getRecordingOutput();
+
+ protected abstract void parseSharedOutput(SharedVariableOutput svo);
+
+ @Override
+ public void startTest(String junitClass, String junitMethod) {
+ ExecutionTracer._start();
+ ExecutionTracer.appJavaClassPath.setOptionalTestClass(junitClass);
+ ExecutionTracer.appJavaClassPath.setOptionalTestMethod(junitMethod);
+ }
+
+ @Override
+ public void finishTest(String junitClass, String junitMethod) {
+ ExecutionTracer.shutdown();
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/SharedVariableAgent.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/SharedVariableAgent.java
new file mode 100644
index 000000000..113c00643
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/SharedVariableAgent.java
@@ -0,0 +1,179 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.io.File;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import javax.print.CancelablePrintJob;
+
+import microbat.instrumentation.Agent;
+import microbat.instrumentation.AgentLogger;
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.filter.GlobalFilterChecker;
+import microbat.instrumentation.instr.SystemClassTransformer;
+import microbat.instrumentation.instr.aggreplay.TimeoutThread;
+import microbat.instrumentation.instr.aggreplay.output.SharedVariableOutput;
+import microbat.instrumentation.instr.aggreplay.shared.BasicTransformer;
+import microbat.instrumentation.instr.aggreplay.shared.SharedObjectAccessInstrumentator;
+import microbat.instrumentation.model.SharedVariableObjectId;
+import microbat.instrumentation.model.generator.IdGenerator;
+import microbat.instrumentation.model.generator.ObjectIdGenerator;
+import microbat.instrumentation.model.generator.SharedVariableArrayRef;
+import microbat.instrumentation.model.generator.SharedVariableObjectGenerator;
+import microbat.instrumentation.model.generator.ThreadIdGenerator;
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.StaticFieldLocation;
+import microbat.instrumentation.model.storage.FileStorage;
+import microbat.instrumentation.model.storage.Storable;
+import microbat.instrumentation.output.StorableWriter;
+import microbat.instrumentation.runtime.ExecutionTracer;
+import sav.common.core.Pair;
+import sav.strategies.dto.AppJavaClassPath;
+
+/**
+ * Dynamically determines whether a memory location is shared or not
+ * @author Gabau
+ *
+ */
+public class SharedVariableAgent extends Agent {
+
+ private ClassFileTransformer transformer = null;
+ private SharedVariableObjectGenerator shObjectIdGenerator = new SharedVariableObjectGenerator();
+ private static SharedVariableAgent agent = new SharedVariableAgent();
+ private AgentParams agentParams = null;
+ private TimeoutThread timeoutThread;
+
+
+ public static SharedVariableAgent getAgent(CommandLine cmd) {
+ agent.agentParams = AgentParams.initFrom(cmd);
+ return agent;
+ }
+
+ protected static boolean cannotRecord() {
+ return !ExecutionTracer.isRecordingOrStarted();
+ }
+
+ // TODO(Gab)
+ public static void _onNewArray(Object arrayRef) {
+ if (cannotRecord()) return;
+ agent.shObjectIdGenerator.createArrayId(arrayRef);
+ }
+
+ public static void _onLockAcquire(Object object) {
+
+ }
+
+ public static void _onArrayAccess(Object arrayRef, int index) {
+ if (cannotRecord()) return;
+ _assertArrayExists(arrayRef);
+ agent.onArrayAccess(arrayRef, index);
+ }
+
+ private void onArrayAccess(Object arrayRef, int index) {
+ SharedVariableArrayRef arrayVal = shObjectIdGenerator.getArrayId(arrayRef);
+ arrayVal.addAccess(index, Thread.currentThread().getId());
+ }
+
+ public static void _assertObjectExists(Object object) {
+ if (cannotRecord()) return;
+ agent.shObjectIdGenerator.assertId(object);
+ }
+
+ public static void _assertArrayExists(Object object) {
+ if (cannotRecord()) return;
+ agent.shObjectIdGenerator.assertArrayId(object);
+ }
+
+ /**
+ * Generate object id on object creation
+ * @param object
+ */
+ public static void _onObjectCreation(Object object) {
+ if (cannotRecord()) return;
+ agent.shObjectIdGenerator.createId(object);
+ }
+
+
+ public static void _onObjectAccess(Object object, String field) {
+ if (cannotRecord()) return;
+ _assertObjectExists(object);
+ agent.shObjectIdGenerator.getId(object).addAccess(Thread.currentThread().getId(), field);
+ }
+
+ public static void _onStaticAccess(String className, String fieldName) {
+ if (cannotRecord()) return;
+ agent.onStaticAccess(className, fieldName);
+ }
+
+ private void onStaticAccess(String className, String fieldName) {
+ this.shObjectIdGenerator.addAccessStaticField(new StaticFieldLocation(className, fieldName),
+ Thread.currentThread().getId());
+ }
+
+
+
+ @Override
+ public void startup0(long vmStartupTime, long agentPreStartup) {
+ this.timeoutThread = new TimeoutThread(this);
+ this.timeoutThread.setTimeout(agentParams.getTimeOut());
+ timeoutThread.start();
+ AppJavaClassPath appPath = agentParams.initAppClassPath();
+ GlobalFilterChecker.setup(appPath, agentParams.getIncludesExpression(), agentParams.getExcludesExpression());
+ SystemClassTransformer.attachThreadId(getInstrumentation());
+ }
+
+ @Override
+ public void shutdown() throws Exception {
+ write();
+ AgentLogger.debug("Ended program");
+ }
+
+ private void write() throws Exception {
+ StorableWriter writer = new StorableWriter(new File(agentParams.getDumpFile()));
+ SharedVariableOutput output = new SharedVariableOutput(shObjectIdGenerator);
+ writer.writeStorable(output);
+ }
+
+ @Override
+ public void startTest(String junitClass, String junitMethod) {
+ ExecutionTracer._start();
+ ExecutionTracer.appJavaClassPath.setOptionalTestClass(junitClass);
+ ExecutionTracer.appJavaClassPath.setOptionalTestMethod(junitMethod);
+ }
+
+ @Override
+ public void finishTest(String junitClass, String junitMethod) {
+ ExecutionTracer.shutdown();
+ }
+
+ @Override
+ public ClassFileTransformer getTransformer0() {
+ SharedObjectAccessInstrumentator instrumentator = new SharedObjectAccessInstrumentator(agentParams);
+ return new BasicTransformer(instrumentator);
+ }
+
+ @Override
+ public void retransformBootstrapClasses(Instrumentation instrumentation, Class>[] retransformableClasses)
+ throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void exitTest(String testResultMsg, String junitClass, String junitMethod, long threadId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isInstrumentationActive0() {
+ return !ExecutionTracer.isShutdown();
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/StrictRWReplay.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/StrictRWReplay.java
new file mode 100644
index 000000000..d974ea1d7
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/agents/StrictRWReplay.java
@@ -0,0 +1,104 @@
+package microbat.instrumentation.instr.aggreplay.agents;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.util.Map;
+import java.util.Stack;
+import java.util.concurrent.Semaphore;
+
+import microbat.instrumentation.CommandLine;
+import microbat.instrumentation.model.RecorderObjectId;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+
+/**
+ * Different mode of replay, used to test stricter replay.
+ * Use blocking yield instead.
+ * For every shm, we have a list of writes, and the reads associated to each write.
+ * So each write, we can simply wait for the read to occur
+ * before we allow another write.
+ *
+ * @author Gabau
+ *
+ */
+public class StrictRWReplay extends AggrePlayReplayAgent {
+
+ private Semaphore smp = new Semaphore(1);
+
+ public StrictRWReplay(CommandLine cmd) {
+ super(cmd);
+ }
+
+
+ @Override
+ protected void onLockAcquire(Object obj) {
+
+ ObjectId oid = this.sharedMemGenerator.ofObjectOrArray(obj);
+
+ Stack eventStack = this.lockAcquisitionMap.get(oid);
+ Event currEvent = new Event(null, getPreviousThreadId());
+ while (eventStack == null || !currEvent.equals(eventStack.peek())) {
+ Thread.yield();
+ }
+ lastObjStackLocal.set(eventStack);
+ }
+
+
+ @Override
+ protected void onRead(long previousThreadId, SharedMemoryLocation sharedMemLocation) {
+ // TODO Auto-generated method stub
+ Event readEvent = new Event(sharedMemLocation, previousThreadId);
+ lastEventLocal.set(readEvent);
+ while (!sharedMemLocation.canRead(readEvent)) {
+ Thread.yield();
+ }
+ }
+
+ @Override
+ protected void onWrite(long p_tid, SharedMemoryLocation shm) {
+ Event writeEvent = new Event(shm, p_tid);
+ while (!shm.canWrite(writeEvent)) {
+ Thread.yield();
+ }
+ lastEventLocal.set(writeEvent);
+ }
+
+ private void acquireLock() {
+ try {
+ this.smp.acquire();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void releaseLock() {
+ this.smp.release();
+ }
+
+
+ @Override
+ protected void afterObjectWrite() {
+ if (!wasShared()) {
+ return;
+ }
+ SharedMemoryLocation lastLocation = lastEventLocal.get().getLocation();
+ lastLocation.setLastWrite(lastEventLocal.get());
+ lastLocation.popEvent();
+ }
+
+ @Override
+ protected void afterObjectRead() {
+ if (!wasShared()) {
+ return;
+ }
+ SharedMemoryLocation lastLocation = lastEventLocal.get().getLocation();
+ lastLocation.read(lastEventLocal.get());
+ }
+
+ @Override
+ public void startup0(long vmStartupTime, long agentPreStartup) {
+ super.startup0(vmStartupTime, agentPreStartup);
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/output/SharedVariableOutput.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/output/SharedVariableOutput.java
new file mode 100644
index 000000000..d6d3c2e7d
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/output/SharedVariableOutput.java
@@ -0,0 +1,115 @@
+package microbat.instrumentation.instr.aggreplay.output;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import microbat.instrumentation.instr.aggreplay.shared.ParseData;
+import microbat.instrumentation.instr.aggreplay.shared.Parser;
+import microbat.instrumentation.model.RecorderObjectId;
+import microbat.instrumentation.model.SharedMemGeneratorInitialiser;
+import microbat.instrumentation.model.SharedVariableObjectId;
+import microbat.instrumentation.model.generator.ArrayIndexMemLocation;
+import microbat.instrumentation.model.generator.SharedVariableArrayRef;
+import microbat.instrumentation.model.generator.SharedVariableObjectGenerator;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+import microbat.instrumentation.model.id.StaticFieldLocation;
+import microbat.instrumentation.model.storage.Storable;
+
+/**
+ * Class representing the Shared variable output.
+ * Data needed for shared variable analysis
+ *
+ * Memory locations which are shared
+ *
+ * @author Gabau
+ *
+ */
+public class SharedVariableOutput extends Storable implements Parser, SharedMemGeneratorInitialiser {
+ public Set sharedObjects;
+ public Set sharedArrays;
+ public Set sharedStaticFields;
+ public SharedVariableOutput(SharedVariableObjectGenerator objectGen) {
+ Collection sharedObjectsIds = objectGen.getSharedVariables();
+ sharedObjects = objectGen.getSharedVariables()
+ .stream().collect(Collectors.toSet());
+ sharedArrays = objectGen.getSharedArrays();
+ sharedStaticFields = objectGen.getSharedStaticFields();
+ }
+ public SharedVariableOutput(ParseData data) {
+ parse(data);
+ }
+
+
+ public Map getObjects() {
+ Map result = new HashMap<>();
+ for (SharedVariableObjectId svoId: sharedObjects) {
+ RecorderObjectId fromSharedVar = new RecorderObjectId(svoId.getObjectId());
+ fromSharedVar.updateSharedFieldSet(svoId.getFieldAccessList());
+ result.put(fromSharedVar.getObjectId(), fromSharedVar);
+ }
+ return result;
+
+ }
+
+ @Override
+ public SharedVariableOutput parse(ParseData data) {
+ List objectIds = data.getField("sharedObjects").toList(new Function() {
+ @Override
+ public SharedVariableObjectId apply(ParseData data) {
+ SharedVariableObjectId object = new SharedVariableObjectId();
+ object.parse(data);
+ return object;
+ }
+ });
+ this.sharedObjects = objectIds.stream().collect(Collectors.toSet());
+ this.sharedArrays = data.getField("sharedArrays").toList()
+ .stream()
+ .map(v -> new SharedVariableArrayRef(null).parse(v))
+ .collect(Collectors.toSet());
+ this.sharedStaticFields = data.getField("sharedStaticFields")
+ .toList()
+ .stream()
+ .map(v -> new StaticFieldLocation(v))
+ .collect(Collectors.toSet());
+ return this;
+ }
+ @Override
+ public int hashCode() {
+ return Objects.hash(sharedObjects);
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SharedVariableOutput other = (SharedVariableOutput) obj;
+ return Objects.equals(sharedObjects, other.sharedObjects);
+ }
+ @Override
+ public Set getArrayRefs() {
+ return this.sharedArrays.stream()
+ .flatMap(v -> v.getSharedMemLocations().stream())
+ .map(v -> new SharedMemoryLocation(v))
+ .collect(Collectors.toSet());
+ }
+ @Override
+ public Set getStaticFields() {
+ return this.sharedStaticFields.stream()
+ .map(v -> new SharedMemoryLocation(v)).collect(Collectors.toSet());
+ }
+
+
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/record/RecordingInstrumentor.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/record/RecordingInstrumentor.java
new file mode 100644
index 000000000..4968bbe13
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/record/RecordingInstrumentor.java
@@ -0,0 +1,173 @@
+package microbat.instrumentation.instr.aggreplay.record;
+
+import java.util.concurrent.Semaphore;
+
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.DUP;
+import org.apache.bcel.generic.DUP2;
+import org.apache.bcel.generic.DUP2_X1;
+import org.apache.bcel.generic.DUP_X2;
+import org.apache.bcel.generic.FieldInstruction;
+import org.apache.bcel.generic.GETFIELD;
+import org.apache.bcel.generic.GETSTATIC;
+import org.apache.bcel.generic.INVOKESTATIC;
+import org.apache.bcel.generic.Instruction;
+import org.apache.bcel.generic.InstructionHandle;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.LDC;
+import org.apache.bcel.generic.NEW;
+import org.apache.bcel.generic.POP;
+import org.apache.bcel.generic.POP2;
+import org.apache.bcel.generic.PUTFIELD;
+import org.apache.bcel.generic.PUTSTATIC;
+import org.apache.bcel.generic.SWAP;
+import org.apache.bcel.generic.Type;
+
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.instr.aggreplay.ObjectAccessInstrumentator;
+import microbat.instrumentation.instr.aggreplay.agents.SharedVariableAgent;
+import microbat.instrumentation.model.id.AggrePlayMethods;
+
+public class RecordingInstrumentor extends ObjectAccessInstrumentator {
+
+ public static final String ACQUIRE_LOCK_STRING = "_acquireLock";
+ public static final String RELEASE_LOCK_STRING = "_releaseLock";
+ public static final String LOCK_SIG_STRING = "()V";
+
+ public RecordingInstrumentor(Class> clazz, AgentParams agentParams) {
+ super(clazz, agentParams);
+ }
+
+
+
+
+
+ @Override
+ protected void instrumentPutStatic(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ /**
+ * Need to call GETSTATIC to force the class loader to run first
+ * so that there is no interleaving between static calls.
+ */
+ InstructionList beforePutStatic = new InstructionList();
+ PUTSTATIC ps = (PUTSTATIC) ih.getInstruction();
+ GETSTATIC gs = new GETSTATIC(ps.getIndex());
+ beforePutStatic.append(gs);
+ if (ps.getType(cpg).equals(Type.LONG) || ps.getType(cpg).equals(Type.DOUBLE)) {
+ beforePutStatic.append(new POP2());
+ } else {
+ beforePutStatic.append(new POP());
+ }
+ insertInsnHandler(il, beforePutStatic, ih);
+
+ super.instrumentPutStatic(cpg, il, ih);
+ InstructionList afterInstructionList = new InstructionList();
+ afterInstructionList.append(AggrePlayMethods.AFTER_OBJECT_WRITE.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, afterInstructionList, ih);
+ }
+
+ protected boolean isComputationType2(FieldInstruction ih, ConstantPoolGen cpg) {
+ return ih.getType(cpg).equals(Type.LONG)
+ || ih.getType(cpg).equals(Type.DOUBLE);
+ }
+
+ @Override
+ protected void instrumentGetStaticInstruction(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ InstructionList beforeGetStaticInstructionList = new InstructionList();
+ beforeGetStaticInstructionList.append(ih.getInstruction());
+ if (isComputationType2((FieldInstruction) ih.getInstruction(), cpg)) {
+ beforeGetStaticInstructionList.append(new POP2());
+ } else {
+ beforeGetStaticInstructionList.append(new POP());
+ }
+ insertInsnHandler(il, beforeGetStaticInstructionList, ih);
+ super.instrumentGetStaticInstruction(cpg, il, ih);
+ InstructionList afterInstructionList = new InstructionList();
+ afterInstructionList.append(AggrePlayMethods.AFTER_OBJECT_READ.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, afterInstructionList, ih);
+ }
+
+
+
+
+
+ @Override
+ protected void instrumentArrayRead(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ // TODO Auto-generated method stub
+ super.instrumentArrayRead(cpg, il, ih);
+ InstructionList toAppend = new InstructionList();
+ toAppend.append(AggrePlayMethods.AFTER_OBJECT_READ.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, toAppend, ih);
+ }
+
+ @Override
+ protected void instrumentArrayWrite(ConstantPoolGen cpg, InstructionList il, InstructionHandle ih) {
+ // TODO Auto-generated method stub
+ super.instrumentArrayWrite(cpg, il, ih);
+ InstructionList toAppend = new InstructionList();
+ toAppend.append(AggrePlayMethods.AFTER_OBJECT_WRITE.toInvokeStatic(cpg, agentClass));
+ appendInstruction(il, toAppend, ih);
+ }
+
+ @Override
+ protected void instrumentPutField(ConstantPoolGen constPool, INVOKESTATIC onObjectWriteInvoke,
+ InstructionList iList, InstructionHandle handle, PUTFIELD putField) {
+ InstructionList beforeInstructionList = new InstructionList();
+ InstructionList afterInstructionList = new InstructionList();
+
+ if (isComputationType2(putField, constPool)) {
+ beforeInstructionList.append(new DUP2_X1());
+ beforeInstructionList.append(new POP2());
+ beforeInstructionList.append(new DUP());
+ } else {
+ // objectRef, value
+ beforeInstructionList.append(new SWAP());
+ // v, o, o
+ beforeInstructionList.append(new DUP());
+ }
+ beforeInstructionList.append(new LDC(constPool.addString(putField.getFieldName(constPool))));
+ // v, o, o, c
+ beforeInstructionList
+ .append(createInvokeStatic(constPool, agentClass,
+ AggrePlayMethods.BEFORE_OBJECT_WRITE));
+ // v, o
+ if (isComputationType2(putField, constPool)) {
+ beforeInstructionList.append(new DUP_X2());
+ beforeInstructionList.append(new POP());
+ } else {
+ beforeInstructionList.append(new SWAP());
+ }
+ // o, v
+ afterInstructionList.append(AggrePlayMethods.AFTER_OBJECT_WRITE.toInvokeStatic(constPool, agentClass));
+
+ insertInsnHandler(iList, beforeInstructionList, handle);
+ appendInstruction(iList, afterInstructionList, handle);
+ }
+
+ @Override
+ protected void instrumentGetField(ConstantPoolGen constPool, INVOKESTATIC onObjectReadInvoke, InstructionList iList,
+ InstructionHandle handle, GETFIELD getfield) {
+
+ // TODO Auto-generated method stub
+ InstructionList beforeInstructionList = new InstructionList();
+ InstructionList afterInstructionList = new InstructionList();
+
+ beforeInstructionList.append(new DUP());
+ // objectRef, objectRef
+ beforeInstructionList
+ .append(new LDC(constPool.addString(getfield.getFieldName(constPool))));
+ // obj, obj, field
+ beforeInstructionList.append(createInvokeStatic(constPool,
+ agentClass, AggrePlayMethods.BEFORE_OBJECT_READ));
+
+ afterInstructionList.append(createInvokeStatic(constPool, agentClass,
+ AggrePlayMethods.AFTER_OBJECT_READ));
+ insertInsnHandler(iList, beforeInstructionList, handle);
+ appendInstruction(iList, afterInstructionList, handle);
+ }
+
+
+
+
+
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/replay/ReplayingInstrumentator.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/replay/ReplayingInstrumentator.java
new file mode 100644
index 000000000..48c4358d0
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/replay/ReplayingInstrumentator.java
@@ -0,0 +1,5 @@
+package microbat.instrumentation.instr.aggreplay.replay;
+
+public class ReplayingInstrumentator {
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/BasicTransformer.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/BasicTransformer.java
new file mode 100644
index 000000000..23ba4a3fe
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/BasicTransformer.java
@@ -0,0 +1,73 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.time.DayOfWeek;
+
+import microbat.instrumentation.filter.GlobalFilterChecker;
+import microbat.instrumentation.instr.AbstractInstrumenter;
+import microbat.instrumentation.instr.AbstractTransformer;
+
+public class BasicTransformer extends AbstractTransformer implements ClassFileTransformer {
+ private AbstractInstrumenter instrumenter;
+
+ public BasicTransformer(AbstractInstrumenter inst) {
+ this.instrumenter = inst;
+ }
+
+ @Override
+ public byte[] doTransform(ClassLoader loader, String classFName, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ // TODO Auto-generated method stub
+ /* bootstrap classes */
+
+ if ((loader == null) || (protectionDomain == null)) {
+ if (!GlobalFilterChecker.isTransformable(classFName, null, true)) {
+ return null;
+ }
+ }
+
+ if (protectionDomain != null) {
+ String path = protectionDomain.getCodeSource().getLocation().getFile();
+ if (!GlobalFilterChecker.isTransformable(classFName, path, false)) {
+ return null;
+ }
+ }
+
+ /* do instrumentation */
+ try {
+ byte[] result = instrumenter.instrument(classFName, classfileBuffer);
+
+ if (classFName.equals("simplebug1/simplebug/TestObject")) {
+ File otherFile = new File("K:\\OldOutput.class");
+ File tocreate = new File("K:\\Output.class");
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(otherFile);
+ fileOutputStream.write(classfileBuffer);
+
+ FileOutputStream fw = new FileOutputStream(tocreate);
+ fw.write(result);
+ fw.flush();
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ fw.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ return result;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+
+ }
+
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/ObjectIdParser.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/ObjectIdParser.java
new file mode 100644
index 000000000..6eded9ac2
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/ObjectIdParser.java
@@ -0,0 +1,20 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+import java.util.HashSet;
+
+import microbat.instrumentation.model.RecorderObjectId;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.ThreadId;
+
+public class ObjectIdParser implements Parser {
+
+ @Override
+ public ObjectId parse(ParseData data) {
+ ParseData threadId = data.innerDataMap.get("threadId");
+ ThreadId actualThreadId = ThreadId.createThreadId(threadId);
+ int objectCounter = Integer.parseInt(data.innerDataMap.get("objectCounter").getValue());
+ ObjectId objectId = new ObjectId(actualThreadId, objectCounter);
+ return objectId;
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/ParseData.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/ParseData.java
new file mode 100644
index 000000000..b8f3002de
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/ParseData.java
@@ -0,0 +1,102 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import sav.common.core.Pair;
+
+public class ParseData {
+ Map innerDataMap = new HashMap<>();
+ List listData;
+ String actualData = null;
+
+ public String getObjectType() {
+ return innerDataMap.get("ObjectType").getValue();
+ }
+
+ public ParseData getField(String field) {
+ return innerDataMap.get(field);
+ }
+ public String getValue() {
+ return actualData;
+ }
+
+ public Long getLongValue() {
+ return Long.parseLong(actualData);
+ }
+
+ public List toList(Function function) {
+ if (this.listData == null) {
+ return Collections.emptyList();
+ }
+ return this.listData.stream().map(function).collect(Collectors.toList());
+ }
+
+ public Pair toPair(Function keyGen, Function valueGen) {
+ T firstValue = null;
+ V secondValue = null;
+ int counter = 0;
+ for (ParseData data : this.listData) {
+ if (counter == 2) {
+ break;
+ }
+ if (counter == 0) {
+ firstValue = keyGen.apply(data);
+ } else {
+ secondValue = valueGen.apply(data);
+ }
+ counter++;
+ }
+ return Pair.of(firstValue, secondValue);
+
+ }
+
+ public Map toMap(Function keyGen, Function valueGen) {
+ Map result = new HashMap<>();
+ for (Pair dataPair : toPairList()) {
+ T key = keyGen.apply(dataPair.first());
+ V value = valueGen.apply(dataPair.second());
+ result.put(key, value);
+ }
+ return result;
+ }
+
+ public List> toPairList() {
+ if (this.listData == null) {
+ return Collections.emptyList();
+ }
+ LinkedList> result = new LinkedList<>();
+ ParseData data1 = null;
+ ParseData data2 = null;
+ int ctr = 0;
+ for (ParseData data : listData) {
+ if (ctr%2 == 0) {
+ data1 = data;
+ } else {
+ data2 = data;
+ result.add(Pair.of(data1, data2));
+ }
+ ctr++;
+ }
+
+ return result;
+ }
+
+ public List toList() {
+ return listData;
+ }
+
+ public int getIntValue() {
+ return Integer.parseInt(actualData);
+ }
+
+
+ public boolean isClass(Class> clazz) {
+ return getObjectType().equals(clazz.getName());
+ }
+}
\ No newline at end of file
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/Parser.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/Parser.java
new file mode 100644
index 000000000..09fba8809
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/Parser.java
@@ -0,0 +1,5 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+public interface Parser {
+ public T parse(ParseData data);
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/RecordingOutput.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/RecordingOutput.java
new file mode 100644
index 000000000..0e9cf2f38
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/RecordingOutput.java
@@ -0,0 +1,176 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import microbat.instrumentation.instr.aggreplay.output.SharedVariableOutput;
+import microbat.instrumentation.model.RecorderObjectId;
+import microbat.instrumentation.model.SharedMemGeneratorInitialiser;
+import microbat.instrumentation.model.generator.ArrayIndexMemLocation;
+import microbat.instrumentation.model.generator.ObjectIdGenerator;
+import microbat.instrumentation.model.generator.SharedVariableArrayRef;
+import microbat.instrumentation.model.id.Event;
+import microbat.instrumentation.model.id.MemoryLocation;
+import microbat.instrumentation.model.id.ObjectFieldMemoryLocation;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.ReadCountVector;
+import microbat.instrumentation.model.id.ReadWriteAccessList;
+import microbat.instrumentation.model.id.SharedMemoryLocation;
+import microbat.instrumentation.model.id.StaticFieldLocation;
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.instrumentation.model.storage.Storable;
+
+/**
+ * Class that represents the recording output for aggreplay recording.
+ * Stages involved -> Shared var detection -> recording -> replay
+ *
+ * @author Gabau
+ *
+ */
+public class RecordingOutput extends Storable implements Parser, SharedMemGeneratorInitialiser {
+ public ReadWriteAccessList rwAccessList; // WR_var(e)
+ // used to get the object acquisition
+ public List threadIds;
+ public List sharedMemoryLocations; // W_var(e)
+ public Map> lockAcquisitionMap;
+ public long memoryUsed;
+ public RecordingOutput(ReadWriteAccessList rwAccessList,
+ List threadIds,
+ List sharedMemoryLocations,
+ Map> lockAcquisitionMap) {
+ super();
+ this.rwAccessList = rwAccessList;
+ // TODO(Gab): filter out the objects that aren't used.
+ this.sharedMemoryLocations = sharedMemoryLocations;
+ this.threadIds = threadIds;
+ this.lockAcquisitionMap = lockAcquisitionMap;
+ this.memoryUsed = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+ }
+
+ public RecordingOutput() {
+
+ }
+ public Map> getLockAcquisitionMap() {
+ return lockAcquisitionMap;
+ }
+
+ // used for shared mem locations
+ @Override
+ public Map getObjects() {
+ Map result = new HashMap<>();
+ for (SharedMemoryLocation shMemoryLocation : this.sharedMemoryLocations) {
+ if (shMemoryLocation.isSharedObjectMem()) {
+ ObjectId objectId = shMemoryLocation.getLocation().getObjectId();
+ if (!result.containsKey(objectId)) {
+ result.put(objectId, new RecorderObjectId(objectId));
+ }
+ RecorderObjectId toObtainRObjectId = result.get(objectId);
+ ObjectFieldMemoryLocation ofml = (ObjectFieldMemoryLocation) shMemoryLocation.getLocation();
+ toObtainRObjectId.setField(ofml.getField(), shMemoryLocation);
+ }
+ }
+
+ return result;
+ }
+
+
+ public static List parseThreadIds(ParseData parseData) {
+ List values = parseData.toList();
+ return values.stream().map(new Function() {
+ @Override
+ public ThreadId apply(ParseData pData) {
+ return ThreadId.createThreadId(pData);
+ }
+ }).collect(Collectors.toList());
+ }
+
+ // TODO:
+ public RecordingOutput parse(ParseData parseData) {
+ rwAccessList = new ReadWriteAccessList().parse(parseData.getField("rwAccessList"));
+ threadIds = parseThreadIds(parseData.getField("threadIds"));
+ this.memoryUsed = parseData.getField("memoryUsed").getLongValue();
+ this.lockAcquisitionMap = parseData.getField("lockAcquisitionMap")
+ .toMap(new Function() {
+ @Override
+ public ObjectId apply(ParseData data) {
+ ObjectId objectId = new ObjectId(false);
+ objectId.parse(data);
+ return objectId;
+ }
+ }, new Function>() {
+ @Override
+ public List apply(ParseData data) {
+ return data.toList(new Function() {
+ @Override
+ public Event apply(ParseData parseData) {
+ Event result = Event.parseEvent(parseData);
+ return result;
+ }
+ });
+ }
+ });
+
+ this.sharedMemoryLocations =
+ parseData.getField("sharedMemoryLocations").toList(new Function() {
+
+ @Override
+ public SharedMemoryLocation apply(ParseData t) {
+ return new SharedMemoryLocation().parse(t);
+ }
+
+ });
+
+ for (SharedMemoryLocation sml : this.sharedMemoryLocations) {
+ sml.generateWRMap();
+ }
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.lockAcquisitionMap, rwAccessList, sharedMemoryLocations, threadIds);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RecordingOutput other = (RecordingOutput) obj;
+ return Objects.equals(this.lockAcquisitionMap, other.lockAcquisitionMap)
+ && Objects.equals(rwAccessList, other.rwAccessList)
+ && Objects.equals(sharedMemoryLocations, other.sharedMemoryLocations)
+ && Objects.equals(threadIds, other.threadIds);
+ }
+
+ @Override
+ public Set getArrayRefs() {
+ return this.sharedMemoryLocations
+ .stream()
+ .filter((v) -> v.getLocation() instanceof ArrayIndexMemLocation).collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set getStaticFields() {
+ Set locations = new HashSet<>();
+ for (SharedMemoryLocation sml: this.sharedMemoryLocations) {
+ if (sml.getLocation() instanceof StaticFieldLocation) {
+ locations.add(sml);
+ }
+ }
+ return locations;
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/SharedDataParser.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/SharedDataParser.java
new file mode 100644
index 000000000..e9ddc3b48
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/SharedDataParser.java
@@ -0,0 +1,148 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import microbat.instrumentation.model.RecorderObjectId;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.ThreadId;
+import microbat.instrumentation.model.storage.Storable;
+import microbat.instrumentation.model.storage.Storage;
+import microbat.instrumentation.output.OutputReader;
+
+/**
+ * Performs parsing of data generated during instrumentation.
+ * Generic object parser
+ * @author Gabau
+ *
+ */
+public class SharedDataParser {
+
+
+
+ private HashSet parseSet(String setString) {
+ HashSet result = new HashSet<>();
+ // TODO: add the values in the string to the set.
+ String[] fieldNames = setString.split(Storage.STORE_DELIM_STRING);
+ for (String fieldName : fieldNames) {
+ result.add(fieldName);
+ }
+ return result;
+ }
+
+ public RecorderObjectId parseRecordObject(ParseData data) {
+ ParseData threadId = data.innerDataMap.get("threadId");
+ ThreadId actualThreadId = ThreadId.createThreadId(threadId);
+ int objectCounter = Integer.parseInt(data.innerDataMap.get("objectCounter").getValue());
+ ObjectId objectId = new ObjectId(actualThreadId, objectCounter);
+ HashSet fieldNames = parseSet(data.innerDataMap.get("fieldAccessMap").getValue());
+ RecorderObjectId result = new RecorderObjectId(objectId);
+ result.updateSharedFieldSet(fieldNames);
+ return result;
+ }
+
+ public Map generateObjectIds(List data) {
+ HashMap resultObjectIdsHashMap = new HashMap<>();
+ for (ParseData value : data) {
+ if (value.isClass(ObjectId.class)) {
+ RecorderObjectId roiObjectId = parseRecordObject(value);
+ resultObjectIdsHashMap.put(roiObjectId.getObjectId(), roiObjectId);
+ }
+ }
+ return resultObjectIdsHashMap;
+ }
+
+ public List parse(Reader data) throws IOException {
+ LinkedList result = new LinkedList<>();
+ int k = data.read();
+ while (k != -1) {
+ while (k != Storage.START_OBJECT_STRING.charAt(0)) {
+ if (k == -1) break;
+ k = data.read();
+ }
+ if (k == -1) break;
+ result.add(parseInner(data));
+ k = data.read();
+ }
+ return result;
+ }
+
+
+ private ParseData parseList(Reader reader) throws IOException {
+ // grab the contents of the list
+ int c = reader.read();
+ List data = new LinkedList<>();
+ ParseData result = new ParseData();
+ while (c != Storage.LIST_END) {
+ if (c == Storage.START_OBJECT_STRING.charAt(0)) {
+ data.add(parseInner(reader));
+ } else if (c == Storage.LIST_SEP) {
+ c = reader.read();
+ continue;
+ } else if (c == Storage.LIST_START) {
+ ParseData listData = parseList(reader);
+ data.add(listData);
+ } else {
+ StringBuilder sb = new StringBuilder();
+ while (c != Storage.LIST_SEP && c != Storage.LIST_END) {
+ sb.append((char) c);
+ c = reader.read();
+ }
+ ParseData toAdd = new ParseData();
+ toAdd.actualData = sb.toString();
+ data.add(toAdd);
+ continue;
+ }
+ c = reader.read();
+ }
+ result.listData = data;
+ return result;
+ }
+
+ private ParseData parseInner(Reader reader) throws IOException {
+ ParseData parseData = new ParseData();
+ StringBuilder sb = new StringBuilder();
+ while (true) {
+ char c = (char) reader.read();
+ if (c == Storage.CLOSE_OBJECT_STRING.charAt(0)) {
+ break;
+ }
+ if (c == Storage.OBJECT_SEPARATOR.charAt(0)
+ || c == '\n' || c == '\r') {
+ continue;
+ }
+ if (c == Storage.STORE_DELIM_STRING.charAt(0)) {
+ String field = sb.toString().trim();
+ char k = (char) reader.read();
+ ParseData value = new ParseData();
+ if (k == Storage.LIST_START) {
+ value = parseList(reader);
+ } else if (k == Storage.START_OBJECT_STRING.charAt(0)) {
+ value = parseInner(reader);
+ } else {
+ StringBuilder innerValue = new StringBuilder();
+ while (k != Storage.OBJECT_SEPARATOR.charAt(0)) {
+ innerValue.append(k);
+ k = (char) reader.read();
+ }
+ value.actualData = innerValue.toString();
+ }
+ parseData.innerDataMap.put(field, value);
+ sb.setLength(0);
+ continue;
+ }
+ sb.append(c);
+ }
+ return parseData;
+ }
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/SharedObjectAccessInstrumentator.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/SharedObjectAccessInstrumentator.java
new file mode 100644
index 000000000..70b404276
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/SharedObjectAccessInstrumentator.java
@@ -0,0 +1,56 @@
+package microbat.instrumentation.instr.aggreplay.shared;
+
+import microbat.instrumentation.AgentParams;
+import microbat.instrumentation.instr.aggreplay.ObjectAccessInstrumentator;
+import microbat.instrumentation.instr.aggreplay.agents.SharedVariableAgent;
+
+public class SharedObjectAccessInstrumentator extends ObjectAccessInstrumentator {
+
+ public SharedObjectAccessInstrumentator(AgentParams agentParams) {
+ super(SharedObjectAccessInstrumentator.class, agentParams);
+ }
+
+ public static void _onNewObject(Object object) {
+ SharedVariableAgent._onObjectCreation(object);
+ }
+
+ public static void _onObjectWrite(Object object, String field) {
+ SharedVariableAgent._onObjectAccess(object, field);
+ }
+
+ public static void _onObjectRead(Object object, String field) {
+ SharedVariableAgent._onObjectAccess(object, field);
+ }
+
+ public static void _onStaticRead(String className, String fieldName) {
+ SharedVariableAgent._onStaticAccess(className, fieldName);
+ }
+
+ public static void _onStaticWrite(String className, String fieldName) {
+ SharedVariableAgent._onStaticAccess(className, fieldName);
+ }
+
+ public static void _onArrayRead(Object arrayRef, int index) {
+ SharedVariableAgent._onArrayAccess(arrayRef, index);
+ }
+
+ public static void _assertObjectExists(Object object) {
+ SharedVariableAgent._assertObjectExists(object);
+ }
+
+ public static void _assertArrayExists(Object object) {
+ SharedVariableAgent._assertArrayExists(object);
+ }
+
+ public static void _onArrayWrite(Object arrayRef, int index) {
+ SharedVariableAgent._onArrayAccess(arrayRef, index);
+ }
+
+ public static void _onNewArray(Object arrayRef) {
+ SharedVariableAgent._onNewArray(arrayRef);
+ }
+
+ public static void _onLockAcquire(Object object) {
+ SharedVariableAgent._onLockAcquire(object);
+ }
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/parser/MemoryLocationParser.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/parser/MemoryLocationParser.java
new file mode 100644
index 000000000..d334d0442
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/parser/MemoryLocationParser.java
@@ -0,0 +1,39 @@
+package microbat.instrumentation.instr.aggreplay.shared.parser;
+
+import microbat.instrumentation.instr.aggreplay.shared.ObjectIdParser;
+import microbat.instrumentation.instr.aggreplay.shared.ParseData;
+import microbat.instrumentation.instr.aggreplay.shared.Parser;
+import microbat.instrumentation.instr.aggreplay.shared.SharedDataParser;
+import microbat.instrumentation.model.generator.ArrayIndexMemLocation;
+import microbat.instrumentation.model.id.MemoryLocation;
+import microbat.instrumentation.model.id.ObjectFieldMemoryLocation;
+import microbat.instrumentation.model.id.ObjectId;
+import microbat.instrumentation.model.id.StaticFieldLocation;
+
+public class MemoryLocationParser implements Parser {
+ private static class ObjectFieldMemLocationParser implements Parser {
+ @Override
+ public ObjectFieldMemoryLocation parse(ParseData data) {
+ return new ObjectFieldMemoryLocation(data.getField("fieldName").getValue(),
+ new ObjectIdParser().parse(data.getField("objectId")));
+ }
+
+ }
+
+ @Override
+ public MemoryLocation parse(ParseData data) {
+ if (data.isClass(ObjectFieldMemoryLocation.class)) {
+ ObjectFieldMemLocationParser ofmParser = new ObjectFieldMemLocationParser();
+ return ofmParser.parse(data);
+ }
+ if (data.isClass(ArrayIndexMemLocation.class)) {
+ return new ArrayIndexMemLocation(data);
+ }
+
+ if (data.isClass(StaticFieldLocation.class)) {
+ return new StaticFieldLocation(data);
+ }
+ return null;
+ }
+
+}
diff --git a/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/parser/RCVectorParser.java b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/parser/RCVectorParser.java
new file mode 100644
index 000000000..127ba5ea2
--- /dev/null
+++ b/microbat_instrumentator/src/main/microbat/instrumentation/instr/aggreplay/shared/parser/RCVectorParser.java
@@ -0,0 +1,28 @@
+package microbat.instrumentation.instr.aggreplay.shared.parser;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import microbat.instrumentation.instr.aggreplay.shared.ParseData;
+import microbat.instrumentation.instr.aggreplay.shared.Parser;
+import sav.common.core.Pair;
+
+/**
+ * Parser for parsing a map from long to int
+ * @author Gabau
+ *
+ */
+public class RCVectorParser implements Parser