diff --git a/configCSHistory.json b/configCSHistory.json index bd4fad2..df35e99 100644 --- a/configCSHistory.json +++ b/configCSHistory.json @@ -4,7 +4,7 @@ "port": "5006" }, "entrypoint": { - "className": "com.example.ObjectRefSimulation", + "className": "dummies.ObjectArgsTypes", "methodName": "main", "methodArguments": [ "java.lang.String[]" @@ -12,10 +12,11 @@ "repBefore": 0 }, "endpoint": { - "className": "java.lang.Runtime", - "methodName": "exec", + "className": "dummies.ObjectArgsTypes", + "methodName": "endpoint", "methodArguments": [ - "java.lang.String" + "java.util.List", + "int" ], "repBefore": 0 }, diff --git a/configCSSnapshot.json b/configCSSnapshot.json index bd4fad2..df35e99 100644 --- a/configCSSnapshot.json +++ b/configCSSnapshot.json @@ -4,7 +4,7 @@ "port": "5006" }, "entrypoint": { - "className": "com.example.ObjectRefSimulation", + "className": "dummies.ObjectArgsTypes", "methodName": "main", "methodArguments": [ "java.lang.String[]" @@ -12,10 +12,11 @@ "repBefore": 0 }, "endpoint": { - "className": "java.lang.Runtime", - "methodName": "exec", + "className": "dummies.ObjectArgsTypes", + "methodName": "endpoint", "methodArguments": [ - "java.lang.String" + "java.util.List", + "int" ], "repBefore": 0 }, diff --git a/configTrace.json b/configTrace.json index a90fe37..b43fde3 100644 --- a/configTrace.json +++ b/configTrace.json @@ -4,7 +4,7 @@ "port": "5006" }, "entrypoint": { - "className": "com.example.ObjectRefSimulation", + "className": "dummies.ObjectArgsTypes", "methodName": "main", "methodArguments": [ "java.lang.String[]" @@ -12,18 +12,19 @@ "repBefore": 0 }, "endpoint": { - "className": "java.lang.Runtime", - "methodName": "exec", + "className": "dummies.ObjectArgsTypes", + "methodName": "endpoint", "methodArguments": [ - "java.lang.String" + "java.util.List", + "int" ], "repBefore": 0 }, "activateEndpoint" : false, "collectValues" : true, - "maxObjectDepth" : 14, - "maxMethodDepth" : 21, - "maxSteps" : -1, + "maxObjectDepth" : 1, + "maxMethodDepth" : 23, + "maxSteps" : 10000, "logging": { "outputName" : "JDIOutput", "extension": "tr" diff --git a/src/main/java/org/jdiextractor/config/AbstractExtractorConfig.java b/src/main/java/jdiextractor/config/AbstractExtractorConfig.java similarity index 59% rename from src/main/java/org/jdiextractor/config/AbstractExtractorConfig.java rename to src/main/java/jdiextractor/config/AbstractExtractorConfig.java index b41f9b8..32306f7 100644 --- a/src/main/java/org/jdiextractor/config/AbstractExtractorConfig.java +++ b/src/main/java/jdiextractor/config/AbstractExtractorConfig.java @@ -1,14 +1,14 @@ -package org.jdiextractor.config; +package jdiextractor.config; -import org.jdiextractor.config.components.BreakpointConfig; -import org.jdiextractor.config.components.LoggingConfig; -import org.jdiextractor.config.components.VmConfig; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.config.components.LoggingConfig; +import jdiextractor.config.components.VmConfig; import com.fasterxml.jackson.databind.JsonNode; public class AbstractExtractorConfig { - protected final int DEFAULT_MAX_DEPTH = 20; + public final static int DEFAULT_MAX_DEPTH = 20; protected BreakpointConfig entrypoint; protected BreakpointConfig endpoint; @@ -16,12 +16,25 @@ public class AbstractExtractorConfig { protected LoggingConfig logging; protected int maxObjectDepth; + protected AbstractExtractorConfig() { + } + + protected AbstractExtractorConfig(BreakpointConfig entrypoint, BreakpointConfig endpoint, VmConfig vm, + LoggingConfig logging, int maxObjectDepth) { + this.entrypoint = entrypoint; + this.endpoint = endpoint; + this.vm = vm; + this.logging = logging; + this.maxObjectDepth = maxObjectDepth; + } + protected void fillFromJson(JsonNode rootNode) { this.vm = VmConfig.fromJson(rootNode.get("vm")); this.entrypoint = BreakpointConfig.fromJson(rootNode.get("entrypoint")); this.endpoint = BreakpointConfig.fromJson(rootNode.get("endpoint")); this.logging = LoggingConfig.fromJson(rootNode.get("logging")); - this.maxObjectDepth = rootNode.has("maxObjectDepth") ? rootNode.get("maxObjectDepth").asInt() : DEFAULT_MAX_DEPTH; + this.maxObjectDepth = rootNode.has("maxObjectDepth") ? rootNode.get("maxObjectDepth").asInt() + : DEFAULT_MAX_DEPTH; } public BreakpointConfig getEntrypoint() { diff --git a/src/main/java/jdiextractor/config/CallStackHistoryExtractorConfig.java b/src/main/java/jdiextractor/config/CallStackHistoryExtractorConfig.java new file mode 100644 index 0000000..d590a4e --- /dev/null +++ b/src/main/java/jdiextractor/config/CallStackHistoryExtractorConfig.java @@ -0,0 +1,37 @@ +package jdiextractor.config; + +import com.fasterxml.jackson.databind.JsonNode; + +import jdiextractor.config.builder.CallStackHistoryExtractorConfigBuilder; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.config.components.LoggingConfig; +import jdiextractor.config.components.VmConfig; + +public class CallStackHistoryExtractorConfig extends AbstractExtractorConfig { + + public CallStackHistoryExtractorConfig(BreakpointConfig entrypoint, BreakpointConfig endpoint, VmConfig vm, + LoggingConfig logging, int maxObjectDepth) { + super(entrypoint, endpoint, vm, logging, maxObjectDepth); + } + + public CallStackHistoryExtractorConfig() { + super(); + } + + + public static CallStackHistoryExtractorConfig fromJson(JsonNode rootNode) { + CallStackHistoryExtractorConfig config = new CallStackHistoryExtractorConfig(); + config.fillFromJson(rootNode); + return config; + } + + public static CallStackHistoryExtractorConfigBuilder builder() { + return new CallStackHistoryExtractorConfigBuilder(); + } + + @Override + protected void fillFromJson(JsonNode rootNode) { + super.fillFromJson(rootNode); + } + +} diff --git a/src/main/java/jdiextractor/config/CallStackSnapshotExtractorConfig.java b/src/main/java/jdiextractor/config/CallStackSnapshotExtractorConfig.java new file mode 100644 index 0000000..d44118e --- /dev/null +++ b/src/main/java/jdiextractor/config/CallStackSnapshotExtractorConfig.java @@ -0,0 +1,37 @@ +package jdiextractor.config; + +import com.fasterxml.jackson.databind.JsonNode; + +import jdiextractor.config.builder.CallStackSnapshotExtractorConfigBuilder; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.config.components.LoggingConfig; +import jdiextractor.config.components.VmConfig; + +public class CallStackSnapshotExtractorConfig extends AbstractExtractorConfig { + + public CallStackSnapshotExtractorConfig(BreakpointConfig entrypoint, BreakpointConfig endpoint, VmConfig vm, + LoggingConfig logging, int maxObjectDepth) { + super(entrypoint, endpoint, vm, logging, maxObjectDepth); + } + + public CallStackSnapshotExtractorConfig() { + super(); + } + + public static CallStackSnapshotExtractorConfig fromJson(JsonNode rootNode) { + CallStackSnapshotExtractorConfig config = new CallStackSnapshotExtractorConfig(); + config.fillFromJson(rootNode); + return config; + } + + public static CallStackSnapshotExtractorConfigBuilder builder() { + return new CallStackSnapshotExtractorConfigBuilder(); + } + + @Override + protected void fillFromJson(JsonNode rootNode) { + super.fillFromJson(rootNode); + + } + +} diff --git a/src/main/java/jdiextractor/config/TraceExtractorStepConfig.java b/src/main/java/jdiextractor/config/TraceExtractorStepConfig.java new file mode 100644 index 0000000..c991ab4 --- /dev/null +++ b/src/main/java/jdiextractor/config/TraceExtractorStepConfig.java @@ -0,0 +1,70 @@ +package jdiextractor.config; + +import com.fasterxml.jackson.databind.JsonNode; + +import jdiextractor.config.builder.TraceExtractorStepConfigBuilder; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.config.components.LoggingConfig; +import jdiextractor.config.components.VmConfig; + +public class TraceExtractorStepConfig extends AbstractExtractorConfig { + + public final static boolean DEFAULT_ACTIVATE_ENDPOINT = false; + public final static boolean DEFAULT_COLLECT_VALUES = true; + public final static int DEFAULT_MAX_METHOD_DEPTH = 14; + public final static int DEFAULT_MAX_STEPS = 21; + + protected boolean activateEndpoint; + protected boolean collectValues; + protected int maxMethodDepth; + protected int maxSteps = 21; + + public TraceExtractorStepConfig(BreakpointConfig entrypoint, BreakpointConfig endpoint, VmConfig vm, + LoggingConfig logging, int maxObjectDepth, boolean activateEndpoint, boolean collectValues, + int maxMethodDepth, int maxSteps) { + super(entrypoint, endpoint, vm, logging, maxObjectDepth); + this.activateEndpoint = activateEndpoint; + this.collectValues = collectValues; + this.maxMethodDepth = maxMethodDepth; + this.maxSteps = maxSteps; + } + + public TraceExtractorStepConfig() { + super(); + } + + public static TraceExtractorStepConfig fromJson(JsonNode rootNode) { + TraceExtractorStepConfig config = new TraceExtractorStepConfig(); + config.fillFromJson(rootNode); + return config; + } + + public static TraceExtractorStepConfigBuilder builder() { + return new TraceExtractorStepConfigBuilder(); + } + + @Override + protected void fillFromJson(JsonNode rootNode) { + super.fillFromJson(rootNode); + this.activateEndpoint = rootNode.get("activateEndpoint").asBoolean(); + this.collectValues = rootNode.get("collectValues").asBoolean(); + this.maxMethodDepth = rootNode.get("maxMethodDepth").asInt(); + this.maxSteps = rootNode.get("maxSteps").asInt(); + } + + public boolean activateEndpoint() { + return this.activateEndpoint; + } + + public boolean collectValues() { + return this.collectValues; + } + + public int getMaxMethodDepth() { + return this.maxMethodDepth; + } + + public int getMaxSteps() { + return this.maxSteps; + } +} diff --git a/src/main/java/jdiextractor/config/builder/AbstractExtractorConfigBuilder.java b/src/main/java/jdiextractor/config/builder/AbstractExtractorConfigBuilder.java new file mode 100644 index 0000000..8a87731 --- /dev/null +++ b/src/main/java/jdiextractor/config/builder/AbstractExtractorConfigBuilder.java @@ -0,0 +1,64 @@ +package jdiextractor.config.builder; + +import java.util.List; + +import jdiextractor.config.AbstractExtractorConfig; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.config.components.LoggingConfig; +import jdiextractor.config.components.VmConfig; + +public abstract class AbstractExtractorConfigBuilder, C extends AbstractExtractorConfig> { + + protected BreakpointConfig entrypoint = null; + protected BreakpointConfig endpoint = null; + protected VmConfig vmConfig = null; + protected LoggingConfig logging = null; + protected int maxObjectDepth = AbstractExtractorConfig.DEFAULT_MAX_DEPTH; + + public abstract T self(); + + public abstract C build(); + + protected void ensureConfig() { + if (entrypoint == null) { + entrypoint = new BreakpointConfig("dummies.ObjectArgsSimulation", "main", + List.of("java.lang.String[]", "int"), 0); + } + if (endpoint == null) { + entrypoint = new BreakpointConfig("dummies.ObjectArgsSimulation", "endpoint", List.of("java.lang.String[]"), + 0); + } + if (vmConfig == null) { + vmConfig = new VmConfig("localhost", "5006"); + } + if (logging == null) { + logging = new LoggingConfig("JDIOutput", "tr"); + } + } + + public T entrypoint(BreakpointConfig entrypoint) { + this.entrypoint = entrypoint; + return self(); + } + + public T endpoint(BreakpointConfig endpoint) { + this.endpoint = endpoint; + return self(); + } + + public T vmConfig(VmConfig vmConfig) { + this.vmConfig = vmConfig; + return self(); + } + + public T logging(LoggingConfig logging) { + this.logging = logging; + return self(); + } + + public T maxObjectDepth(int maxObjectDepth) { + this.maxObjectDepth = maxObjectDepth; + return self(); + } + +} diff --git a/src/main/java/jdiextractor/config/builder/CallStackHistoryExtractorConfigBuilder.java b/src/main/java/jdiextractor/config/builder/CallStackHistoryExtractorConfigBuilder.java new file mode 100644 index 0000000..982b90d --- /dev/null +++ b/src/main/java/jdiextractor/config/builder/CallStackHistoryExtractorConfigBuilder.java @@ -0,0 +1,19 @@ +package jdiextractor.config.builder; + +import jdiextractor.config.CallStackHistoryExtractorConfig; + +public class CallStackHistoryExtractorConfigBuilder extends + AbstractExtractorConfigBuilder { + + @Override + public CallStackHistoryExtractorConfigBuilder self() { + return this; + } + + @Override + public CallStackHistoryExtractorConfig build() { + ensureConfig(); + return new CallStackHistoryExtractorConfig(entrypoint, endpoint, vmConfig, logging, maxObjectDepth); + } + +} diff --git a/src/main/java/jdiextractor/config/builder/CallStackSnapshotExtractorConfigBuilder.java b/src/main/java/jdiextractor/config/builder/CallStackSnapshotExtractorConfigBuilder.java new file mode 100644 index 0000000..b381a1a --- /dev/null +++ b/src/main/java/jdiextractor/config/builder/CallStackSnapshotExtractorConfigBuilder.java @@ -0,0 +1,19 @@ +package jdiextractor.config.builder; + +import jdiextractor.config.CallStackSnapshotExtractorConfig; + +public class CallStackSnapshotExtractorConfigBuilder extends + AbstractExtractorConfigBuilder { + + @Override + public CallStackSnapshotExtractorConfigBuilder self() { + return this; + } + + @Override + public CallStackSnapshotExtractorConfig build() { + ensureConfig(); + return new CallStackSnapshotExtractorConfig(entrypoint, endpoint, vmConfig, logging, maxObjectDepth); + } + +} diff --git a/src/main/java/jdiextractor/config/builder/TraceExtractorStepConfigBuilder.java b/src/main/java/jdiextractor/config/builder/TraceExtractorStepConfigBuilder.java new file mode 100644 index 0000000..6386ab9 --- /dev/null +++ b/src/main/java/jdiextractor/config/builder/TraceExtractorStepConfigBuilder.java @@ -0,0 +1,45 @@ +package jdiextractor.config.builder; + +import jdiextractor.config.TraceExtractorStepConfig; + +public class TraceExtractorStepConfigBuilder + extends AbstractExtractorConfigBuilder { + + protected boolean activateEndpoint = TraceExtractorStepConfig.DEFAULT_ACTIVATE_ENDPOINT; + protected boolean collectValues = TraceExtractorStepConfig.DEFAULT_COLLECT_VALUES; + protected int maxMethodDepth = TraceExtractorStepConfig.DEFAULT_MAX_METHOD_DEPTH; + protected int maxSteps = TraceExtractorStepConfig.DEFAULT_MAX_STEPS; + + @Override + public TraceExtractorStepConfigBuilder self() { + return this; + } + + public TraceExtractorStepConfigBuilder activateEndpoint(boolean activateEndpoint) { + this.activateEndpoint = activateEndpoint; + return self(); + } + + public TraceExtractorStepConfigBuilder collectValues(boolean collectValues) { + this.collectValues = collectValues; + return self(); + } + + public TraceExtractorStepConfigBuilder maxMethodDepth(int maxMethodDepth) { + this.maxMethodDepth = maxMethodDepth; + return self(); + } + + public TraceExtractorStepConfigBuilder maxSteps(int maxSteps) { + this.maxSteps = maxSteps; + return self(); + } + + @Override + public TraceExtractorStepConfig build() { + ensureConfig(); + return new TraceExtractorStepConfig(entrypoint, endpoint, vmConfig, logging, maxObjectDepth, activateEndpoint, + collectValues, maxMethodDepth, maxSteps); + } + +} diff --git a/src/main/java/org/jdiextractor/config/components/BreakpointConfig.java b/src/main/java/jdiextractor/config/components/BreakpointConfig.java similarity index 97% rename from src/main/java/org/jdiextractor/config/components/BreakpointConfig.java rename to src/main/java/jdiextractor/config/components/BreakpointConfig.java index 032f873..51c7f53 100644 --- a/src/main/java/org/jdiextractor/config/components/BreakpointConfig.java +++ b/src/main/java/jdiextractor/config/components/BreakpointConfig.java @@ -1,4 +1,4 @@ -package org.jdiextractor.config.components; +package jdiextractor.config.components; import java.util.ArrayList; import java.util.Iterator; diff --git a/src/main/java/org/jdiextractor/config/components/LoggingConfig.java b/src/main/java/jdiextractor/config/components/LoggingConfig.java similarity index 94% rename from src/main/java/org/jdiextractor/config/components/LoggingConfig.java rename to src/main/java/jdiextractor/config/components/LoggingConfig.java index d765f46..380b9de 100644 --- a/src/main/java/org/jdiextractor/config/components/LoggingConfig.java +++ b/src/main/java/jdiextractor/config/components/LoggingConfig.java @@ -1,4 +1,4 @@ -package org.jdiextractor.config.components; +package jdiextractor.config.components; import com.fasterxml.jackson.databind.JsonNode; diff --git a/src/main/java/org/jdiextractor/config/components/VmConfig.java b/src/main/java/jdiextractor/config/components/VmConfig.java similarity index 94% rename from src/main/java/org/jdiextractor/config/components/VmConfig.java rename to src/main/java/jdiextractor/config/components/VmConfig.java index 8230063..6d1d68a 100644 --- a/src/main/java/org/jdiextractor/config/components/VmConfig.java +++ b/src/main/java/jdiextractor/config/components/VmConfig.java @@ -1,4 +1,4 @@ -package org.jdiextractor.config.components; +package jdiextractor.config.components; import com.fasterxml.jackson.databind.JsonNode; diff --git a/src/main/java/org/jdiextractor/core/AbstractExtractor.java b/src/main/java/jdiextractor/core/AbstractExtractor.java similarity index 79% rename from src/main/java/org/jdiextractor/core/AbstractExtractor.java rename to src/main/java/jdiextractor/core/AbstractExtractor.java index 49970cd..93194dd 100644 --- a/src/main/java/org/jdiextractor/core/AbstractExtractor.java +++ b/src/main/java/jdiextractor/core/AbstractExtractor.java @@ -1,13 +1,17 @@ -package org.jdiextractor.core; +package jdiextractor.core; import java.util.List; import java.util.Stack; -import org.jdiextractor.config.AbstractExtractorConfig; -import org.jdiextractor.config.components.BreakpointConfig; -import org.jdiextractor.service.breakpoint.BreakPointInstaller; -import org.jdiextractor.service.breakpoint.BreakpointWrapper; -import org.jdiextractor.service.serializer.JDIToTraceConverter; +import jdiextractor.config.AbstractExtractorConfig; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.service.breakpoint.BreakPointInstaller; +import jdiextractor.service.breakpoint.BreakpointWrapper; +import jdiextractor.service.serializer.BufferedTraceConverter; +import jdiextractor.service.serializer.DefferedTraceConverter; +import jdiextractor.service.serializer.JDIToTraceConverter; +import jdiextractor.service.serializer.TraceSerializerJson; +import jdiextractor.tracemodel.entities.Trace; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.InternalException; @@ -63,6 +67,12 @@ public abstract class AbstractExtractor { */ protected boolean valuesIndependents; + /** + * Does the extractor log or retain all element in a model It is useful for + * testing + */ + protected boolean activateLogging; + protected Stack branchIds; /** @@ -86,9 +96,20 @@ public void launch(VirtualMachine vm, T config) { */ protected AbstractExtractor(boolean valuesIndependents) { this.valuesIndependents = valuesIndependents; + this.activateLogging = true; + this.branchIds = new Stack(); + } + + public AbstractExtractor(boolean valuesIndependents, boolean activateLogging) { + this.valuesIndependents = valuesIndependents; + this.activateLogging = activateLogging; this.branchIds = new Stack(); } + public Trace getTrace() { + return this.jdiToTraceConverter.getTrace(); + } + /** * Defines the main execution logic of the extractor. *

@@ -106,7 +127,22 @@ private void setVM(VirtualMachine vm) { this.vm = vm; } - protected abstract void createTracePopulator(); + protected void createTracePopulator() { + TraceSerializerJson serializer = new TraceSerializerJson(config.getLogging(), this.valuesIndependents); + if (activateLogging) { + this.jdiToTraceConverter = this.defaultTraceConverter(valuesIndependents, config.getObjectMaxDepth(), + serializer); + } else { + this.jdiToTraceConverter = new DefferedTraceConverter(valuesIndependents, config.getObjectMaxDepth(), + serializer); + } + } + + protected JDIToTraceConverter defaultTraceConverter(boolean valuesIndependents, int objectMaxDepth, + TraceSerializerJson serializer) { + return new BufferedTraceConverter(valuesIndependents,objectMaxDepth, serializer); + } + /** * Returns the thread where the execution to extract takes place @@ -231,31 +267,36 @@ protected void processEventsUntilEnd() throws IncompatibleThreadStateException { } /** - * Create the TraceMethod associated to the frame, collecting values of receiver and arguments + * Create the TraceMethod associated to the frame, collecting values of receiver + * and arguments + * * @param frame the StackFrame of the TraceMethod */ protected void createMethodWith(StackFrame frame) { this.createMethodWith(frame, true); } - + /** - * Create the TraceMethod associated to the frame, collecting or not the values of receiver and arguments - * @param frame the StackFrame of the TraceMethod + * Create the TraceMethod associated to the frame, collecting or not the values + * of receiver and arguments + * + * @param frame the StackFrame of the TraceMethod * @param collectValues, true if value should be collected, false otherwise */ protected void createMethodWith(StackFrame frame, boolean collectValues) { Method method = frame.location().method(); - ObjectReference receiver = frame.thisObject(); - List argValues; int newMethodId = jdiToTraceConverter.newTraceElementId(); - try { - argValues = frame.getArgumentValues(); - } catch (InternalException e) { - // Happens for native calls, and can't be obtained - argValues = null; - } if (collectValues) { + ObjectReference receiver = frame.thisObject(); + List argValues; + + try { + argValues = frame.getArgumentValues(); + } catch (InternalException e) { + // Happens for native calls, and can't be obtained + argValues = null; + } jdiToTraceConverter.newMethodFrom(method, argValues, receiver, newMethodId, this.getParentId()); } else { jdiToTraceConverter.newMethodFrom(method, newMethodId, this.getParentId()); @@ -264,7 +305,9 @@ protected void createMethodWith(StackFrame frame, boolean collectValues) { } protected void serializeTrace() { - this.jdiToTraceConverter.serialize(); + if (activateLogging) { + this.jdiToTraceConverter.serialize(); + } } protected int popParentId() { @@ -277,7 +320,8 @@ protected void pushParentId(int id) { protected int getParentId() { if (this.branchIds.empty()) { - // For the first method of the execution, we return a negative id, indicating it has no parent + // For the first method of the execution, we return a negative id, indicating it + // has no parent return -1; } return this.branchIds.peek(); diff --git a/src/main/java/org/jdiextractor/core/CallStackHistoryExtractor.java b/src/main/java/jdiextractor/core/CallStackHistoryExtractor.java similarity index 70% rename from src/main/java/org/jdiextractor/core/CallStackHistoryExtractor.java rename to src/main/java/jdiextractor/core/CallStackHistoryExtractor.java index 237e799..fefeb09 100644 --- a/src/main/java/org/jdiextractor/core/CallStackHistoryExtractor.java +++ b/src/main/java/jdiextractor/core/CallStackHistoryExtractor.java @@ -1,8 +1,10 @@ -package org.jdiextractor.core; +package jdiextractor.core; + +import jdiextractor.config.CallStackHistoryExtractorConfig; +import jdiextractor.service.serializer.DefferedTraceConverter; +import jdiextractor.service.serializer.JDIToTraceConverter; +import jdiextractor.service.serializer.TraceSerializerJson; -import org.jdiextractor.config.CallStackHistoryExtractorConfig; -import org.jdiextractor.service.serializer.DefferedTraceConverter; -import org.jdiextractor.service.serializer.TraceLogger; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.ThreadReference; import com.sun.jdi.event.MethodEntryEvent; @@ -19,12 +21,16 @@ */ public class CallStackHistoryExtractor extends AbstractExtractor { - private int frameCount = 0; + private int frameCountBefore = 0; public CallStackHistoryExtractor() { super(true); } + public CallStackHistoryExtractor(boolean activateLogging) { + super(true,activateLogging); + } + @Override protected void executeExtraction() { try { @@ -35,12 +41,6 @@ protected void executeExtraction() { throw new IllegalStateException("Thread should be at a breakpoint but isn't"); } } - - @Override - protected void createTracePopulator() { - TraceLogger logger = new TraceLogger(config.getLogging(), this.valuesIndependents); - this.jdiToTraceConverter = new DefferedTraceConverter(valuesIndependents, config.getObjectMaxDepth(), logger); - } /** * Blocks execution until the configured breakpoint is hit. Handles @@ -62,14 +62,14 @@ private void collectFrames() throws IncompatibleThreadStateException { protected void reactToStepEvent(StepEvent event) { try { ThreadReference targetThread = event.thread(); - frameCount = targetThread.frameCount(); + int frameCountNow = targetThread.frameCount(); - if (frameCount + 1 == frameCount) { + if (frameCountBefore + 1 == frameCountNow) { this.createMethodWith(targetThread.frame(0)); - frameCount++; - } else if (frameCount - 1 == frameCount) { + frameCountBefore++; + } else if (frameCountBefore - 1 == frameCountNow) { this.jdiToTraceConverter.removeLastElement(); - frameCount--; + frameCountBefore--; } } catch (IncompatibleThreadStateException e) { throw new IllegalStateException("Exception occured during a step event : " + e); @@ -86,4 +86,11 @@ protected void reactToMethodExitEvent(MethodExitEvent event) { // Nothing, should not happen in this scenario } + @Override + protected JDIToTraceConverter defaultTraceConverter(boolean valuesIndependents, int objectMaxDepth, + TraceSerializerJson serializer) { + // Cannot use the Buffered converter because this extractor will remove element from the trace during execution + return new DefferedTraceConverter(valuesIndependents,objectMaxDepth, serializer); + } + } diff --git a/src/main/java/org/jdiextractor/core/CallStackSnapshotExtractor.java b/src/main/java/jdiextractor/core/CallStackSnapshotExtractor.java similarity index 78% rename from src/main/java/org/jdiextractor/core/CallStackSnapshotExtractor.java rename to src/main/java/jdiextractor/core/CallStackSnapshotExtractor.java index 588b4a5..b696546 100644 --- a/src/main/java/org/jdiextractor/core/CallStackSnapshotExtractor.java +++ b/src/main/java/jdiextractor/core/CallStackSnapshotExtractor.java @@ -1,11 +1,8 @@ -package org.jdiextractor.core; +package jdiextractor.core; import java.util.List; -import org.jdiextractor.config.CallStackSnapshotExtractorConfig; -import org.jdiextractor.service.serializer.DefferedTraceConverter; -import org.jdiextractor.service.serializer.TraceLogger; - +import jdiextractor.config.CallStackSnapshotExtractorConfig; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.StackFrame; import com.sun.jdi.event.MethodEntryEvent; @@ -24,6 +21,10 @@ public class CallStackSnapshotExtractor extends AbstractExtractor frameCountBefore) { this.createMethodWith(frame, config.collectValues()); - } else if(frameCountNow < frameCountBefore) { + } else if (frameCountNow < frameCountBefore) { this.popParentId(); } @@ -89,7 +90,7 @@ protected void reactToStepEvent(StepEvent event) { } else { this.ensureStepInto(); } - + if (this.maxStepsAttained()) { this.desactivateSteps(); } @@ -102,7 +103,10 @@ protected void reactToStepEvent(StepEvent event) { } private boolean maxStepsAttained() { - return steps < 0 | steps > config.getMaxSteps(); + // If max steps are negative or 0, no max steps + // Else check if max step has been reached + // TODO could maybe be optimised + return config.getMaxSteps() > 0 & steps > config.getMaxSteps(); } @Override @@ -145,12 +149,6 @@ private boolean isExactMethodMatch(Method method, BreakpointConfig config) { return true; } - @Override - protected void createTracePopulator() { - TraceLogger logger = new TraceLogger(config.getLogging(), this.valuesIndependents); - this.jdiToTraceConverter = new BufferedTraceConverter(valuesIndependents, config.getObjectMaxDepth(), logger); - } - private void ensureStepOver() { if (!stepOver.isEnabled()) { stepInto.disable(); diff --git a/src/main/java/org/jdiextractor/launcher/AbstractLauncher.java b/src/main/java/jdiextractor/launcher/AbstractLauncher.java similarity index 88% rename from src/main/java/org/jdiextractor/launcher/AbstractLauncher.java rename to src/main/java/jdiextractor/launcher/AbstractLauncher.java index 50cdf50..5ff98d2 100644 --- a/src/main/java/org/jdiextractor/launcher/AbstractLauncher.java +++ b/src/main/java/jdiextractor/launcher/AbstractLauncher.java @@ -1,11 +1,11 @@ -package org.jdiextractor.launcher; +package jdiextractor.launcher; import java.io.File; import java.io.IOException; -import org.jdiextractor.config.AbstractExtractorConfig; -import org.jdiextractor.core.AbstractExtractor; -import org.jdiextractor.service.connector.JDIAttach; +import jdiextractor.config.AbstractExtractorConfig; +import jdiextractor.core.AbstractExtractor; +import jdiextractor.service.connector.JDIAttach; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/main/java/org/jdiextractor/launcher/HistoryCSExtractorLauncher.java b/src/main/java/jdiextractor/launcher/HistoryCSExtractorLauncher.java similarity index 86% rename from src/main/java/org/jdiextractor/launcher/HistoryCSExtractorLauncher.java rename to src/main/java/jdiextractor/launcher/HistoryCSExtractorLauncher.java index 4a801e7..3d98559 100644 --- a/src/main/java/org/jdiextractor/launcher/HistoryCSExtractorLauncher.java +++ b/src/main/java/jdiextractor/launcher/HistoryCSExtractorLauncher.java @@ -1,7 +1,7 @@ -package org.jdiextractor.launcher; +package jdiextractor.launcher; -import org.jdiextractor.config.CallStackHistoryExtractorConfig; -import org.jdiextractor.core.CallStackHistoryExtractor; +import jdiextractor.config.CallStackHistoryExtractorConfig; +import jdiextractor.core.CallStackHistoryExtractor; import com.fasterxml.jackson.databind.JsonNode; diff --git a/src/main/java/org/jdiextractor/launcher/SnapshotCSExtractorLauncher.java b/src/main/java/jdiextractor/launcher/SnapshotCSExtractorLauncher.java similarity index 87% rename from src/main/java/org/jdiextractor/launcher/SnapshotCSExtractorLauncher.java rename to src/main/java/jdiextractor/launcher/SnapshotCSExtractorLauncher.java index b29f8c8..fa47962 100644 --- a/src/main/java/org/jdiextractor/launcher/SnapshotCSExtractorLauncher.java +++ b/src/main/java/jdiextractor/launcher/SnapshotCSExtractorLauncher.java @@ -1,7 +1,7 @@ -package org.jdiextractor.launcher; +package jdiextractor.launcher; -import org.jdiextractor.config.CallStackSnapshotExtractorConfig; -import org.jdiextractor.core.CallStackSnapshotExtractor; +import jdiextractor.config.CallStackSnapshotExtractorConfig; +import jdiextractor.core.CallStackSnapshotExtractor; import com.fasterxml.jackson.databind.JsonNode; diff --git a/src/main/java/org/jdiextractor/launcher/TraceExtractorStepLauncher.java b/src/main/java/jdiextractor/launcher/TraceExtractorStepLauncher.java similarity index 81% rename from src/main/java/org/jdiextractor/launcher/TraceExtractorStepLauncher.java rename to src/main/java/jdiextractor/launcher/TraceExtractorStepLauncher.java index b131728..c4333b0 100644 --- a/src/main/java/org/jdiextractor/launcher/TraceExtractorStepLauncher.java +++ b/src/main/java/jdiextractor/launcher/TraceExtractorStepLauncher.java @@ -1,7 +1,7 @@ -package org.jdiextractor.launcher; +package jdiextractor.launcher; -import org.jdiextractor.config.TraceExtractorStepConfig; -import org.jdiextractor.core.TraceExtractorStep; +import jdiextractor.config.TraceExtractorStepConfig; +import jdiextractor.core.TraceExtractorStep; import com.fasterxml.jackson.databind.JsonNode; diff --git a/src/main/java/org/jdiextractor/service/breakpoint/BreakPointInstaller.java b/src/main/java/jdiextractor/service/breakpoint/BreakPointInstaller.java similarity index 97% rename from src/main/java/org/jdiextractor/service/breakpoint/BreakPointInstaller.java rename to src/main/java/jdiextractor/service/breakpoint/BreakPointInstaller.java index 2c65c74..9bffc54 100644 --- a/src/main/java/org/jdiextractor/service/breakpoint/BreakPointInstaller.java +++ b/src/main/java/jdiextractor/service/breakpoint/BreakPointInstaller.java @@ -1,8 +1,8 @@ -package org.jdiextractor.service.breakpoint; +package jdiextractor.service.breakpoint; import java.util.List; -import org.jdiextractor.config.components.BreakpointConfig; +import jdiextractor.config.components.BreakpointConfig; import com.sun.jdi.ClassType; import com.sun.jdi.Location; diff --git a/src/main/java/org/jdiextractor/service/breakpoint/BreakpointWrapper.java b/src/main/java/jdiextractor/service/breakpoint/BreakpointWrapper.java similarity index 94% rename from src/main/java/org/jdiextractor/service/breakpoint/BreakpointWrapper.java rename to src/main/java/jdiextractor/service/breakpoint/BreakpointWrapper.java index 3891bd3..eb6b6de 100644 --- a/src/main/java/org/jdiextractor/service/breakpoint/BreakpointWrapper.java +++ b/src/main/java/jdiextractor/service/breakpoint/BreakpointWrapper.java @@ -1,4 +1,4 @@ -package org.jdiextractor.service.breakpoint; +package jdiextractor.service.breakpoint; import com.sun.jdi.event.Event; import com.sun.jdi.request.BreakpointRequest; diff --git a/src/main/java/org/jdiextractor/service/connector/JDIAttach.java b/src/main/java/jdiextractor/service/connector/JDIAttach.java similarity index 58% rename from src/main/java/org/jdiextractor/service/connector/JDIAttach.java rename to src/main/java/jdiextractor/service/connector/JDIAttach.java index 9354e14..5a3ef95 100644 --- a/src/main/java/org/jdiextractor/service/connector/JDIAttach.java +++ b/src/main/java/jdiextractor/service/connector/JDIAttach.java @@ -1,10 +1,10 @@ -package org.jdiextractor.service.connector; +package jdiextractor.service.connector; import java.io.IOException; import java.net.ConnectException; import java.util.Map; -import org.jdiextractor.config.components.VmConfig; +import jdiextractor.config.components.VmConfig; import com.sun.jdi.Bootstrap; import com.sun.jdi.VirtualMachine; @@ -14,13 +14,15 @@ import com.sun.jdi.connect.IllegalConnectorArgumentsException; public class JDIAttach { - + /** * Attach to a java virtual machine located with the given information + * * @param config all information needed to find the vm * @return the Virtual Machine if one found * @throws IOException when unable to attach. - * @throws IllegalConnectorArgumentsException if no connector socket can be used. + * @throws IllegalConnectorArgumentsException if no connector socket can be + * used. */ public VirtualMachine attachToJDI(VmConfig config) throws IllegalConnectorArgumentsException, IOException { @@ -44,12 +46,30 @@ public VirtualMachine attachToJDI(VmConfig config) throws IllegalConnectorArgume arguments.get("hostname").setValue(config.getHost()); arguments.get("port").setValue(config.getPort()); // need to correspond to the JVM address + // Parameters of the polling strategy + int maxAttempts = 50; // 50 attemps * 100ms = 5 seconds of wating maximum + long retryDelayMs = 100; + // Connect to the JVM - try { - return connector.attach(arguments); - } catch (ConnectException e) { - throw new IllegalStateException("Connection to the JVM refused, maybe check that the adresses are corresponding"); + for (int attempt = 1; attempt <= maxAttempts; attempt++) { + try { + return connector.attach(arguments); + } catch (ConnectException e) { + // If last attempt, then fail + if (attempt == maxAttempts) { + throw new IllegalStateException("Connection to JVM refused on port " + config.getPort() + " after " + + maxAttempts + " attempts. Target process might have crashed.", e); + } + // Else apply delay and retry + try { + Thread.sleep(retryDelayMs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("JDI Attachment polling was interrupted", ie); + } + } } - + + throw new IllegalStateException("Unreachable state in JDI polling loop."); } } diff --git a/src/main/java/jdiextractor/service/serializer/BufferedTraceConverter.java b/src/main/java/jdiextractor/service/serializer/BufferedTraceConverter.java new file mode 100644 index 0000000..a2675d7 --- /dev/null +++ b/src/main/java/jdiextractor/service/serializer/BufferedTraceConverter.java @@ -0,0 +1,38 @@ +package jdiextractor.service.serializer; + +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceElement; + +/** + * A Trace converter that does not retain TraceElement and automatically serialize the trace + * It enable the serialization of large trace data without excessive consumption on the computer memory + */ +public class BufferedTraceConverter extends JDIToTraceConverter { + + + public BufferedTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceSerializer serializer) { + super(valuesIndependents, maxObjectDepth, serializer); + serializer.startSerialize(); + } + + @Override + protected void addElement(TraceElement element) { + serilizer.serialize(element); + } + + @Override + public void serialize() { + serilizer.endSerialize(); + } + + @Override + public void removeLastElement() { + throw new IllegalStateException("Should not remove an element on buffered converter"); + } + + @Override + public Trace getTrace() { + throw new RuntimeException("BufferedTraceConverter cannot give the trace because it does not retain information and automatically log informations"); + } + +} diff --git a/src/main/java/jdiextractor/service/serializer/DefferedTraceConverter.java b/src/main/java/jdiextractor/service/serializer/DefferedTraceConverter.java new file mode 100644 index 0000000..7d92c4b --- /dev/null +++ b/src/main/java/jdiextractor/service/serializer/DefferedTraceConverter.java @@ -0,0 +1,39 @@ +package jdiextractor.service.serializer; + +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceElement; + +/** + * A Trace converter that retain TraceElements and serialize the Trace only at the end + * It enable the analysis and verification of obtained Traces directly in java + */ +public class DefferedTraceConverter extends JDIToTraceConverter { + + private Trace trace; + + public DefferedTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceSerializer serilizer) { + super(valuesIndependents, maxObjectDepth, serilizer); + this.trace = new Trace(); + } + + @Override + protected void addElement(TraceElement element) { + this.trace.addElement(element); + } + + @Override + public void serialize() { + serilizer.serialize(trace); + } + + @Override + public void removeLastElement() { + this.trace.removeLastElement(); + } + + @Override + public Trace getTrace() { + return this.trace; + } + +} diff --git a/src/main/java/org/jdiextractor/service/serializer/JDIToTraceConverter.java b/src/main/java/jdiextractor/service/serializer/JDIToTraceConverter.java similarity index 55% rename from src/main/java/org/jdiextractor/service/serializer/JDIToTraceConverter.java rename to src/main/java/jdiextractor/service/serializer/JDIToTraceConverter.java index 75b0b22..33a51c9 100644 --- a/src/main/java/org/jdiextractor/service/serializer/JDIToTraceConverter.java +++ b/src/main/java/jdiextractor/service/serializer/JDIToTraceConverter.java @@ -1,4 +1,4 @@ -package org.jdiextractor.service.serializer; +package jdiextractor.service.serializer; import java.util.ArrayList; import java.util.HashSet; @@ -6,27 +6,33 @@ import java.util.List; import java.util.Set; -import org.jdiextractor.tracemodel.entities.TraceArgument; -import org.jdiextractor.tracemodel.entities.TraceElement; -import org.jdiextractor.tracemodel.entities.TraceMethod; -import org.jdiextractor.tracemodel.entities.TraceParameter; -import org.jdiextractor.tracemodel.entities.TraceReceiver; -import org.jdiextractor.tracemodel.entities.TraceValue; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaClass; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaPrimitiveType; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaType; -import org.jdiextractor.tracemodel.entities.traceValues.TraceArrayReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceArrayValue; -import org.jdiextractor.tracemodel.entities.traceValues.TraceClassReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceField; -import org.jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; -import org.jdiextractor.tracemodel.entities.traceValues.TraceStringReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceArgument; +import jdiextractor.tracemodel.entities.TraceElement; +import jdiextractor.tracemodel.entities.TraceMethod; +import jdiextractor.tracemodel.entities.TraceParameter; +import jdiextractor.tracemodel.entities.TraceReceiver; +import jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.tracemodel.entities.javaType.TraceJavaInterface; +import jdiextractor.tracemodel.entities.javaType.TraceJavaClass; +import jdiextractor.tracemodel.entities.javaType.TraceJavaPrimitiveType; +import jdiextractor.tracemodel.entities.javaType.TraceJavaReferenceType; +import jdiextractor.tracemodel.entities.javaType.TraceJavaType; +import jdiextractor.tracemodel.entities.traceValues.TraceArrayReference; +import jdiextractor.tracemodel.entities.traceValues.TraceArrayValue; +import jdiextractor.tracemodel.entities.traceValues.TraceClassReference; +import jdiextractor.tracemodel.entities.traceValues.TraceField; +import jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; +import jdiextractor.tracemodel.entities.traceValues.TraceStringReference; +import jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.ArrayReference; +import com.sun.jdi.ArrayType; import com.sun.jdi.ClassNotLoadedException; +import com.sun.jdi.ClassType; import com.sun.jdi.Field; +import com.sun.jdi.InterfaceType; import com.sun.jdi.LocalVariable; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; @@ -42,29 +48,29 @@ public abstract class JDIToTraceConverter { * whether the values are independents with each other, if they are, visited is * reseted between each between each TraceElement creation */ - private boolean valuesIndependents; + protected boolean valuesIndependents; /** * The maximum depth of the object graph */ - private int maxObjectDepth; + protected int maxObjectDepth; /** * The logger used when the trace population is finished */ - protected TraceLogger logger; + protected TraceSerializer serilizer; /** * All already visited object references */ - private Set visitedIds = new HashSet<>(); - + protected Set visitedIds = new HashSet<>(); + protected int lastTraceElementId; - public JDIToTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceLogger logger) { + public JDIToTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceSerializer serializer) { this.valuesIndependents = valuesIndependents; this.maxObjectDepth = maxObjectDepth; - this.logger = logger; + this.serilizer = serializer; this.lastTraceElementId = 0; } @@ -74,11 +80,14 @@ public JDIToTraceConverter(boolean valuesIndependents, int maxObjectDepth, Trace public abstract void removeLastElement(); + public abstract Trace getTrace(); + public int newTraceElementId() { return this.lastTraceElementId++; } - public TraceMethod newMethodFrom(Method method, List argumentValues, ObjectReference receiverObject, int id, int parentId) { + public TraceMethod newMethodFrom(Method method, List argumentValues, ObjectReference receiverObject, int id, + int parentId) { TraceMethod traceMethod = this.coreNewMethodFrom(method, id, parentId); if (argumentValues == null) { @@ -102,13 +111,13 @@ public TraceMethod newMethodFrom(Method method, List argumentValues, Obje } public TraceMethod newMethodFrom(Method method, int id, int parentId) { - TraceMethod traceMethod = this.coreNewMethodFrom(method,id, parentId); + TraceMethod traceMethod = this.coreNewMethodFrom(method, id, parentId); this.addElement(traceMethod); return traceMethod; } - private TraceMethod coreNewMethodFrom(Method method, int id, int parentId) { + protected TraceMethod coreNewMethodFrom(Method method, int id, int parentId) { if (valuesIndependents) { visitedIds = new HashSet<>(); } @@ -133,9 +142,23 @@ private TraceMethod coreNewMethodFrom(Method method, int id, int parentId) { return traceMethod; } - private TraceJavaType newJavaTypeFrom(Type type) { - if (type instanceof ReferenceType) { - return newJavaClassFrom((ReferenceType) type); + protected TraceJavaType newJavaTypeFrom(Type type) { + // First if it is an array, study the component type + if (type instanceof ArrayType) { + try { + type = ((ArrayType) type).componentType(); + } catch (ClassNotLoadedException e) { + e.printStackTrace(); + } + } + + if (type instanceof ReferenceType) { + if(type instanceof InterfaceType) { + return newJavaInterfaceFrom((ReferenceType) type); + } else { + return newJavaClassFrom((ReferenceType) type); + } + } else { return new TraceJavaPrimitiveType(type.name()); } @@ -145,10 +168,13 @@ private TraceJavaType newJavaTypeFrom(Type type) { * Create a JavaType from a String Pay Attention : this method is not able to * determine whether a class is parametric or not * + * It is also not able to determine whether an element is a class or an interface + * * @param typeName the name of the JavaType to create * @return the JavaType created */ - private TraceJavaType newJavaTypeFrom(String typeName) { + protected TraceJavaType newJavaTypeFrom(String typeName) { + // TODO find a better way to make this method if (typeName.contains(".") || Character.isUpperCase(typeName.charAt(0))) { return new TraceJavaClass(typeName); } else { @@ -156,23 +182,106 @@ private TraceJavaType newJavaTypeFrom(String typeName) { } } - private TraceJavaClass newJavaClassFrom(ReferenceType declaringType) { + protected TraceJavaClass newJavaClassFrom(ReferenceType declaringType) { TraceJavaClass traceJavaClass = new TraceJavaClass(declaringType.name()); - - if (declaringType.genericSignature() != null) { - traceJavaClass.setIsParametric(true); + if (isAnonymousClass(declaringType) || isProxyClass(declaringType)) { + traceJavaClass.setAnonymousParent(this.anonymousClassParent(declaringType)); } + + traceJavaClass.setIsParametric(isDeclaredTypeParametric(declaringType)); return traceJavaClass; } - private TraceReceiver newReceiverFrom(ObjectReference receiverObject) { + protected TraceJavaInterface newJavaInterfaceFrom(ReferenceType declaringType) { + TraceJavaInterface traceJavaInterface = new TraceJavaInterface(declaringType.name()); + + traceJavaInterface.setIsParametric(isDeclaredTypeParametric(declaringType)); + return traceJavaInterface; + } + + /** + * A type is parametric if it has a genericSignature and start with the definition of its parametric types + * If it does not start with the definition of its parametric types, it means that it uses parametric types + * @param declaringType + * @return + */ + protected boolean isDeclaredTypeParametric(ReferenceType declaringType) { + String signature = declaringType.genericSignature(); + return signature != null && signature.startsWith("<"); + } + + /** + * Check whether or not the reference type is from an anonymous class + * Its name is always the type it is defined in concatenated with a $number, for example $1 + * + * @See dummies.AnonymousClass, in this class the anonymous declaration of Dog + * will be named "dummies.AnonymousClass$1" + * @param declaringType the potential anonymous type + * @return whether or not the reference type is from an anonymous class + */ + protected boolean isAnonymousClass(ReferenceType declaringType) { + String className = declaringType.name(); + + // Use a regex expression to check if the class can be anonymous + if (!className.matches(".*\\$[0-9]+")) { + return false; + } + + // Verify that the class really is anonymous and not just class declared with + // $number a the end of its name + try { + String sourceName = declaringType.sourceName(); + String simpleName = className.substring(className.lastIndexOf('.') + 1); + String expectedSourceName = simpleName + ".java"; + return !sourceName.equals(expectedSourceName); + + } catch (AbsentInformationException e) { + // If the code is not compiled with debug information (-g:none) + // then accept that it is anonymous by default as it already validate the regex + return true; + } + } + + /** + * Check whether or not the reference type is from a proxy class + * @param declaringType the potential proxy type + * @return whether or not the reference type is from a proxy class + */ + private boolean isProxyClass(ReferenceType declaringType) { + return declaringType.name().startsWith("com.sun.proxy.$Proxy"); + } + + protected TraceJavaReferenceType anonymousClassParent(ReferenceType declaringType) { + if (!(declaringType instanceof ClassType)) { + throw new RuntimeException( + "Trying to add anonymous class informations on a element that is not a class type"); + } + ClassType classType = (ClassType) declaringType; + ClassType superclass = classType.superclass(); + List interfaces = classType.interfaces(); + + // The anonymous' parent can be an interface, and it only has one + if (interfaces.size() == 1) { + return this.newJavaInterfaceFrom(interfaces.get(0)); + } else if (interfaces.size() > 1) { + throw new RuntimeException( + "The supposed anonymous class has multiple interfaces, maybe it is not really an anonymous class"); + } else if (superclass != null) { + // if the anonymous has a superclass, then it is its parent + return this.newJavaClassFrom(superclass); + } + + throw new RuntimeException("Cannot find the parent of the anonymous class"); + } + + protected TraceReceiver newReceiverFrom(ObjectReference receiverObject) { TraceReceiver traceReceiver = new TraceReceiver(); traceReceiver.setValue(this.newValueFromObjectReference(receiverObject, 0)); return traceReceiver; } - private List createArgumentsFor(List argumentValues) { + protected List createArgumentsFor(List argumentValues) { List res = new ArrayList<>(); Iterator argumentsValueIterator = argumentValues.iterator(); @@ -182,13 +291,13 @@ private List createArgumentsFor(List argumentValues) { return res; } - private TraceArgument newArgumentFrom(Value value) { + protected TraceArgument newArgumentFrom(Value value) { TraceArgument traceArgument = new TraceArgument(); traceArgument.setValue(this.newValueFrom(value, 0)); return traceArgument; } - private List createParametersFor(Method method) { + protected List createParametersFor(Method method) { List res = new ArrayList<>(); try { // trying to obtain the arguments information @@ -211,25 +320,29 @@ private List createParametersFor(Method method) { return res; } - private TraceParameter newParameterFrom(LocalVariable param) { + public TraceParameter newParameterFrom(LocalVariable param) { TraceParameter traceParameter = new TraceParameter(); traceParameter.setName(param.name()); try { traceParameter.setType(this.newJavaTypeFrom(param.type())); } catch (ClassNotLoadedException e) { - throw new RuntimeException("Should not happen"); + // When we get to a class that is not loaded, still try to create a class from a signature + // The problem here will be that we cannot determine whether the type is parametric and an interface or a class + // Class types signature are in the form Lorg/project/ClassName; + String signature = param.signature(); + traceParameter.setType(this.newJavaTypeFrom(signature.substring(1, signature.length() - 1).replace("/", "."))); } return traceParameter; } - private TraceParameter newParameterFrom(String typeName) { + protected TraceParameter newParameterFrom(String typeName) { TraceParameter traceParameter = new TraceParameter(); traceParameter.setName(null); traceParameter.setType(this.newJavaTypeFrom(typeName)); return traceParameter; } - private TraceValue newValueFrom(Value value, int depth) { + protected TraceValue newValueFrom(Value value, int depth) { TraceValue traceValue; if (value == null) { traceValue = null; @@ -245,14 +358,14 @@ private TraceValue newValueFrom(Value value, int depth) { return traceValue; } - private TraceValue newValueFromPrimitiveValue(PrimitiveValue value, int depth) { + protected TraceValue newValueFromPrimitiveValue(PrimitiveValue value, int depth) { TracePrimitiveValue tracePrimitiveValue = new TracePrimitiveValue(); tracePrimitiveValue.setType(value.type().name()); tracePrimitiveValue.setValue(value); return tracePrimitiveValue; } - private TraceValue newValueFromObjectReference(ObjectReference objectReference, int depth) { + protected TraceValue newValueFromObjectReference(ObjectReference objectReference, int depth) { long id = objectReference.uniqueID(); if (visitedIds.contains(id)) { @@ -269,7 +382,7 @@ private TraceValue newValueFromObjectReference(ObjectReference objectReference, } } - private TraceValue newClassReferenceFrom(ObjectReference ref, ReferenceType type, int depth) { + protected TraceValue newClassReferenceFrom(ObjectReference ref, ReferenceType type, int depth) { TraceClassReference traceClassReference = new TraceClassReference(); traceClassReference.setUniqueID(ref.uniqueID()); traceClassReference.setType(this.newJavaClassFrom(type)); @@ -287,7 +400,7 @@ private TraceValue newClassReferenceFrom(ObjectReference ref, ReferenceType type return traceClassReference; } - private TraceField newFieldFrom(ObjectReference objectReference, int depth, Field field) { + protected TraceField newFieldFrom(ObjectReference objectReference, int depth, Field field) { TraceField tracefield = new TraceField(); tracefield.setName(field.name()); @@ -308,7 +421,7 @@ private TraceField newFieldFrom(ObjectReference objectReference, int depth, Fiel return tracefield; } - private TraceArrayReference newArrayReferenceFrom(ArrayReference arrayReference, int depth) { + protected TraceArrayReference newArrayReferenceFrom(ArrayReference arrayReference, int depth) { TraceArrayReference traceArrayReference = new TraceArrayReference(); traceArrayReference.setUniqueID(arrayReference.uniqueID()); traceArrayReference.setType(this.newJavaClassFrom(arrayReference.referenceType())); @@ -328,30 +441,33 @@ private TraceArrayReference newArrayReferenceFrom(ArrayReference arrayReference, return traceArrayReference; } - private TraceArrayValue newArrayValueFrom(int depth, List arrayValues, int index) { + protected TraceArrayValue newArrayValueFrom(int depth, List arrayValues, int index) { TraceArrayValue traceArrayValue = new TraceArrayValue(); traceArrayValue.setValue(this.newValueFrom(arrayValues.get(index), depth + 1)); return traceArrayValue; } - private TraceValue newStringReferenceFrom(StringReference stringReference) { + protected TraceValue newStringReferenceFrom(StringReference stringReference) { TraceStringReference traceStringReference = new TraceStringReference(); - /* Cannot collect the value of strings because in some attacks, other type are stored instead of String in some variables - * Hence we just stop to collect string value for now - * see issue https://github.com/moosetechnology/JavaTraceExtractor/issues/25 + /* + * Cannot collect the value of strings because in some attacks, other type are + * stored instead of String in some variables Hence we just stop to collect + * string value for now see issue + * https://github.com/moosetechnology/JavaTraceExtractor/issues/25 */ - //traceStringReference.setValue(stringReference.value()); + // traceStringReference.setValue(stringReference.value()); traceStringReference.setUniqueID(stringReference.uniqueID()); traceStringReference.setType(this.newJavaClassFrom(stringReference.referenceType())); return traceStringReference; } /** - * Returns the signature of a method formatted for the Moose model. - * * @param method The JDI Method object + * Returns the signature of a method formatted for the Moose model. * @param + * method The JDI Method object + * * @return the signature of the method (e.g., "methodName(Type1,Type2)") */ - private String signatureParameter(Method method) { + protected String signatureParameter(Method method) { String genericSignature = method.genericSignature(); // 1. Absolute source of truth: The JNI generic signature @@ -362,11 +478,11 @@ private String signatureParameter(Method method) { if (start != -1 && end != -1) { // Extract the parameter section between the parentheses String paramsSignature = genericSignature.substring(start + 1, end); - + // Fully delegate the lexical parsing to the converter JVMSignatureToMooseSignatureConverter parser = JVMSignatureToMooseSignatureConverter.make(); String parsedParams = parser.parseMethodParameters(paramsSignature); - + return String.format("%s(%s)", method.name(), parsedParams); } } @@ -394,7 +510,7 @@ private String signatureParameter(Method method) { return String.format("%s(%s)", method.name(), paramString); } - private String parseTypeName(LocalVariable var) { + protected String parseTypeName(LocalVariable var) { JVMSignatureToMooseSignatureConverter parser = JVMSignatureToMooseSignatureConverter.make(); if (var.genericSignature() == null) return parser.parseTypeSig(var.signature()); @@ -402,7 +518,7 @@ private String parseTypeName(LocalVariable var) { return parser.parseTypeSig(var.genericSignature()); } - private String parseTypeName(String s) { + protected String parseTypeName(String s) { int lastDotIndex = s.lastIndexOf('.'); return lastDotIndex == -1 ? s : s.substring(lastDotIndex + 1); } diff --git a/src/main/java/org/jdiextractor/service/serializer/JVMSignatureToMooseSignatureConverter.java b/src/main/java/jdiextractor/service/serializer/JVMSignatureToMooseSignatureConverter.java similarity index 99% rename from src/main/java/org/jdiextractor/service/serializer/JVMSignatureToMooseSignatureConverter.java rename to src/main/java/jdiextractor/service/serializer/JVMSignatureToMooseSignatureConverter.java index 0783466..9c036e6 100644 --- a/src/main/java/org/jdiextractor/service/serializer/JVMSignatureToMooseSignatureConverter.java +++ b/src/main/java/jdiextractor/service/serializer/JVMSignatureToMooseSignatureConverter.java @@ -1,4 +1,4 @@ -package org.jdiextractor.service.serializer; +package jdiextractor.service.serializer; import java.lang.reflect.GenericSignatureFormatError; diff --git a/src/main/java/org/jdiextractor/service/serializer/TraceSerializer.java b/src/main/java/jdiextractor/service/serializer/TraceSerializer.java similarity index 50% rename from src/main/java/org/jdiextractor/service/serializer/TraceSerializer.java rename to src/main/java/jdiextractor/service/serializer/TraceSerializer.java index d6777fd..420dae9 100644 --- a/src/main/java/org/jdiextractor/service/serializer/TraceSerializer.java +++ b/src/main/java/jdiextractor/service/serializer/TraceSerializer.java @@ -1,24 +1,25 @@ -package org.jdiextractor.service.serializer; - -import org.jdiextractor.tracemodel.entities.Trace; -import org.jdiextractor.tracemodel.entities.TraceArgument; -import org.jdiextractor.tracemodel.entities.TraceAssignation; -import org.jdiextractor.tracemodel.entities.TraceAssignationLeft; -import org.jdiextractor.tracemodel.entities.TraceAssignationRight; -import org.jdiextractor.tracemodel.entities.TraceElement; -import org.jdiextractor.tracemodel.entities.TraceInvocation; -import org.jdiextractor.tracemodel.entities.TraceMethod; -import org.jdiextractor.tracemodel.entities.TraceParameter; -import org.jdiextractor.tracemodel.entities.TraceReceiver; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaClass; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaPrimitiveType; -import org.jdiextractor.tracemodel.entities.traceValues.TraceArrayReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceArrayValue; -import org.jdiextractor.tracemodel.entities.traceValues.TraceClassReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceField; -import org.jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; -import org.jdiextractor.tracemodel.entities.traceValues.TraceStringReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; +package jdiextractor.service.serializer; + +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceArgument; +import jdiextractor.tracemodel.entities.TraceAssignation; +import jdiextractor.tracemodel.entities.TraceAssignationLeft; +import jdiextractor.tracemodel.entities.TraceAssignationRight; +import jdiextractor.tracemodel.entities.TraceElement; +import jdiextractor.tracemodel.entities.TraceInvocation; +import jdiextractor.tracemodel.entities.TraceMethod; +import jdiextractor.tracemodel.entities.TraceParameter; +import jdiextractor.tracemodel.entities.TraceReceiver; +import jdiextractor.tracemodel.entities.javaType.TraceJavaClass; +import jdiextractor.tracemodel.entities.javaType.TraceJavaInterface; +import jdiextractor.tracemodel.entities.javaType.TraceJavaPrimitiveType; +import jdiextractor.tracemodel.entities.traceValues.TraceArrayReference; +import jdiextractor.tracemodel.entities.traceValues.TraceArrayValue; +import jdiextractor.tracemodel.entities.traceValues.TraceClassReference; +import jdiextractor.tracemodel.entities.traceValues.TraceField; +import jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; +import jdiextractor.tracemodel.entities.traceValues.TraceStringReference; +import jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; public abstract class TraceSerializer { @@ -64,4 +65,6 @@ public abstract class TraceSerializer { public abstract void serialize(TraceJavaClass traceJavaClass); + public abstract void serialize(TraceJavaInterface traceJavaInterface); + } diff --git a/src/main/java/org/jdiextractor/service/serializer/TraceSerializerJson.java b/src/main/java/jdiextractor/service/serializer/TraceSerializerJson.java similarity index 78% rename from src/main/java/org/jdiextractor/service/serializer/TraceSerializerJson.java rename to src/main/java/jdiextractor/service/serializer/TraceSerializerJson.java index 35192ac..5fd025c 100644 --- a/src/main/java/org/jdiextractor/service/serializer/TraceSerializerJson.java +++ b/src/main/java/jdiextractor/service/serializer/TraceSerializerJson.java @@ -1,45 +1,72 @@ -package org.jdiextractor.service.serializer; +package jdiextractor.service.serializer; import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; import java.util.Iterator; -import org.jdiextractor.tracemodel.entities.Trace; -import org.jdiextractor.tracemodel.entities.TraceArgument; -import org.jdiextractor.tracemodel.entities.TraceAssignation; -import org.jdiextractor.tracemodel.entities.TraceAssignationLeft; -import org.jdiextractor.tracemodel.entities.TraceAssignationRight; -import org.jdiextractor.tracemodel.entities.TraceElement; -import org.jdiextractor.tracemodel.entities.TraceInvocation; -import org.jdiextractor.tracemodel.entities.TraceMethod; -import org.jdiextractor.tracemodel.entities.TraceParameter; -import org.jdiextractor.tracemodel.entities.TraceReceiver; -import org.jdiextractor.tracemodel.entities.TraceValue; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaClass; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaPrimitiveType; -import org.jdiextractor.tracemodel.entities.traceValues.TraceArrayReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceArrayValue; -import org.jdiextractor.tracemodel.entities.traceValues.TraceClassReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceField; -import org.jdiextractor.tracemodel.entities.traceValues.TraceObjectReference; -import org.jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; -import org.jdiextractor.tracemodel.entities.traceValues.TraceStringReference; -import org.jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; +import jdiextractor.config.components.LoggingConfig; +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceArgument; +import jdiextractor.tracemodel.entities.TraceAssignation; +import jdiextractor.tracemodel.entities.TraceAssignationLeft; +import jdiextractor.tracemodel.entities.TraceAssignationRight; +import jdiextractor.tracemodel.entities.TraceElement; +import jdiextractor.tracemodel.entities.TraceInvocation; +import jdiextractor.tracemodel.entities.TraceMethod; +import jdiextractor.tracemodel.entities.TraceParameter; +import jdiextractor.tracemodel.entities.TraceReceiver; +import jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.tracemodel.entities.javaType.TraceJavaClass; +import jdiextractor.tracemodel.entities.javaType.TraceJavaInterface; +import jdiextractor.tracemodel.entities.javaType.TraceJavaPrimitiveType; +import jdiextractor.tracemodel.entities.javaType.TraceJavaReferenceType; +import jdiextractor.tracemodel.entities.traceValues.TraceArrayReference; +import jdiextractor.tracemodel.entities.traceValues.TraceArrayValue; +import jdiextractor.tracemodel.entities.traceValues.TraceClassReference; +import jdiextractor.tracemodel.entities.traceValues.TraceField; +import jdiextractor.tracemodel.entities.traceValues.TraceObjectReference; +import jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; +import jdiextractor.tracemodel.entities.traceValues.TraceStringReference; +import jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; public class TraceSerializerJson extends TraceSerializer { - private BufferedWriter writer; + private LoggingConfig loggingConfig; + + private Writer writer; private boolean valueIndependents; private int nbElementLogged; - public TraceSerializerJson(BufferedWriter writer, boolean valueIndependents) { - this.writer = writer; + public TraceSerializerJson(LoggingConfig loggingConfig, boolean valueIndependents) { + this.valueIndependents = valueIndependents; + this.loggingConfig = loggingConfig; + this.nbElementLogged = 0; + } + + /** + * Create a TraceSerializerJson with a pre defined writer + * + * @param valueIndependents whether the values are independents in each frames + */ + public TraceSerializerJson(boolean valueIndependents, Writer writer) { this.valueIndependents = valueIndependents; + this.writer = writer; this.nbElementLogged = 0; } @Override public void startSerialize() { + if (writer == null) { + String fileName = this.loggingConfig.getOutputName() + "." + this.loggingConfig.getExtension(); + try { + this.writer = new BufferedWriter(new FileWriter(fileName)); + } catch (IOException e) { + throw new RuntimeException("Cannot open a file writer on the file: " + fileName); + } + + } try { writer.write(this.objectStart()); @@ -95,7 +122,7 @@ public void endSerialize() { public void serialize(Trace trace) { this.startSerialize(); Iterator ite = trace.getElements().iterator(); - while (ite.hasNext()) { + while (ite.hasNext()) { ite.next().acceptSerializer(this); } this.endSerialize(); @@ -455,25 +482,51 @@ public void serialize(TraceJavaPrimitiveType traceJavaPrimitiveType) { @Override public void serialize(TraceJavaClass traceJavaClass) { - try { - writer.write(this.objectStart()); - writer.write(quotes("name") + ":"); - if (traceJavaClass.getName().contains(".")) { - writer.write(quotes(traceJavaClass.getName())); - } else { - writer.write(quotes(".".concat(traceJavaClass.getName()))); + this.traceReferenceTypeStart(traceJavaClass); + if (traceJavaClass.isAnonymous()) { + try { + writer.write(this.joinElementListing()); + writer.write(quotes("anonymousParentType") + ":"); + } catch (IOException e) { + e.printStackTrace(); } + traceJavaClass.getAnonymousParent().acceptSerializer(this); + } + this.traceReferenceTypeEnd(traceJavaClass); + } - if (traceJavaClass.isParametric()) { + @Override + public void serialize(TraceJavaInterface traceJavaInterface) { + try { + this.traceReferenceTypeStart(traceJavaInterface); + writer.write(this.joinElementListing()); + writer.write(quotes("isInterface") + ":" + "true"); + this.traceReferenceTypeEnd(traceJavaInterface); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void traceReferenceTypeStart(TraceJavaReferenceType refType) { + try { + writer.write(this.objectStart()); + writer.write(quotes("name") + ":" + quotes(refType.getFullyQualifiedName())); + + if (refType.isParametric()) { writer.write(this.joinElementListing()); writer.write(quotes("isParametric") + ":" + "true"); } + } catch (IOException e) { + e.printStackTrace(); + } + } + public void traceReferenceTypeEnd(TraceJavaReferenceType refType) { + try { writer.write(this.objectEnd()); } catch (IOException e) { e.printStackTrace(); } - } private void ObjectReferenceStart(TraceObjectReference traceObjectReference) throws IOException { diff --git a/src/main/java/org/jdiextractor/tracemodel/TraceEntity.java b/src/main/java/jdiextractor/tracemodel/TraceEntity.java similarity index 60% rename from src/main/java/org/jdiextractor/tracemodel/TraceEntity.java rename to src/main/java/jdiextractor/tracemodel/TraceEntity.java index e6eeab8..8ee5f46 100644 --- a/src/main/java/org/jdiextractor/tracemodel/TraceEntity.java +++ b/src/main/java/jdiextractor/tracemodel/TraceEntity.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel; +package jdiextractor.tracemodel; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; /** * I represent an trace entity diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/Trace.java b/src/main/java/jdiextractor/tracemodel/entities/Trace.java similarity index 79% rename from src/main/java/org/jdiextractor/tracemodel/entities/Trace.java rename to src/main/java/jdiextractor/tracemodel/entities/Trace.java index 1bef42d..a382d92 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/Trace.java +++ b/src/main/java/jdiextractor/tracemodel/entities/Trace.java @@ -1,10 +1,10 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; import java.util.List; import java.util.Stack; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; public class Trace extends TraceEntity { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceArgument.java b/src/main/java/jdiextractor/tracemodel/entities/TraceArgument.java similarity index 80% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceArgument.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceArgument.java index dd46bbc..71c8cf6 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceArgument.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceArgument.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; /** * Argument given to a {@link TraceMethod} during execution diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignation.java b/src/main/java/jdiextractor/tracemodel/entities/TraceAssignation.java similarity index 83% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignation.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceAssignation.java index f65f140..3df978d 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignation.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceAssignation.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; public class TraceAssignation extends TraceElement { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignationLeft.java b/src/main/java/jdiextractor/tracemodel/entities/TraceAssignationLeft.java similarity index 71% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignationLeft.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceAssignationLeft.java index 51b4b65..7a39c20 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignationLeft.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceAssignationLeft.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; public class TraceAssignationLeft extends TraceEntity implements TraceValueContainer { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignationRight.java b/src/main/java/jdiextractor/tracemodel/entities/TraceAssignationRight.java similarity index 71% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignationRight.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceAssignationRight.java index 3240662..c5bbb68 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceAssignationRight.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceAssignationRight.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; public class TraceAssignationRight extends TraceEntity implements TraceValueContainer { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceElement.java b/src/main/java/jdiextractor/tracemodel/entities/TraceElement.java similarity index 91% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceElement.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceElement.java index 4297174..6fc4dba 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceElement.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceElement.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.tracemodel.TraceEntity; /** * Abstract base class representing an atomic event in the execution trace. diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceInvocation.java b/src/main/java/jdiextractor/tracemodel/entities/TraceInvocation.java similarity index 80% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceInvocation.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceInvocation.java index 07ce0a4..09a8be8 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceInvocation.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceInvocation.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; import com.sun.jdi.Method; diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceMethod.java b/src/main/java/jdiextractor/tracemodel/entities/TraceMethod.java similarity index 83% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceMethod.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceMethod.java index 01176f0..d61cc31 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceMethod.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceMethod.java @@ -1,10 +1,11 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; import java.util.ArrayList; import java.util.List; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaType; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.entities.javaType.TraceJavaReferenceType; +import jdiextractor.tracemodel.entities.javaType.TraceJavaType; /** * Represents a specific method execution call in the trace. @@ -29,7 +30,7 @@ public class TraceMethod extends TraceElement { private boolean isParametric = false; - private TraceJavaType parentType; + private TraceJavaReferenceType parentType; public TraceMethod() { @@ -101,7 +102,7 @@ public boolean isClassSide() { return this.isClassSide; } - public void setParentType(TraceJavaType parentType) { + public void setParentType(TraceJavaReferenceType parentType) { this.parentType = parentType; } @@ -130,4 +131,8 @@ public void acceptSerializer(TraceSerializer serializer) { serializer.serialize(this); } + public String getFullyQualifiedName() { + return parentType.getFullyQualifiedName() + "." + this.getSignature(); + } + } \ No newline at end of file diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceParameter.java b/src/main/java/jdiextractor/tracemodel/entities/TraceParameter.java similarity index 66% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceParameter.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceParameter.java index 01ccfcf..6fc5835 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceParameter.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceParameter.java @@ -1,8 +1,8 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaType; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; +import jdiextractor.tracemodel.entities.javaType.TraceJavaType; public class TraceParameter extends TraceEntity { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceReceiver.java b/src/main/java/jdiextractor/tracemodel/entities/TraceReceiver.java similarity index 78% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceReceiver.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceReceiver.java index 59ff82a..dc401b6 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceReceiver.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceReceiver.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; public class TraceReceiver extends TraceEntity implements TraceValueContainer { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceValue.java b/src/main/java/jdiextractor/tracemodel/entities/TraceValue.java similarity index 55% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceValue.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceValue.java index 7d70891..565f0b4 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceValue.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceValue.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.tracemodel.TraceEntity; /** diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/TraceValueContainer.java b/src/main/java/jdiextractor/tracemodel/entities/TraceValueContainer.java similarity index 78% rename from src/main/java/org/jdiextractor/tracemodel/entities/TraceValueContainer.java rename to src/main/java/jdiextractor/tracemodel/entities/TraceValueContainer.java index b9efebc..84f2f30 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/TraceValueContainer.java +++ b/src/main/java/jdiextractor/tracemodel/entities/TraceValueContainer.java @@ -1,4 +1,4 @@ -package org.jdiextractor.tracemodel.entities; +package jdiextractor.tracemodel.entities; /** * Container that holds a specific {@link TraceValue}. diff --git a/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaClass.java b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaClass.java new file mode 100644 index 0000000..5947021 --- /dev/null +++ b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaClass.java @@ -0,0 +1,35 @@ +package jdiextractor.tracemodel.entities.javaType; + +import jdiextractor.service.serializer.TraceSerializer; + +public class TraceJavaClass extends TraceJavaReferenceType { + + /** + * If the class is anonymous then we also explicit which type is its parent + * It can be either a Interface or a class + */ + private TraceJavaType anonymousParent = null; + + public TraceJavaClass(String name) { + super(name); + } + + public TraceJavaType getAnonymousParent() { + return anonymousParent; + } + + public void setAnonymousParent(TraceJavaType anonymousParent) { + this.anonymousParent = anonymousParent; + } + + public boolean isAnonymous() { + return anonymousParent != null; + } + + + @Override + public void acceptSerializer(TraceSerializer serializer) { + serializer.serialize(this); + } + +} diff --git a/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaInterface.java b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaInterface.java new file mode 100644 index 0000000..c891e0d --- /dev/null +++ b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaInterface.java @@ -0,0 +1,16 @@ +package jdiextractor.tracemodel.entities.javaType; + +import jdiextractor.service.serializer.TraceSerializer; + +public class TraceJavaInterface extends TraceJavaReferenceType { + + public TraceJavaInterface(String name) { + super(name); + } + + @Override + public void acceptSerializer(TraceSerializer serializer) { + serializer.serialize(this); + } + +} diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaPrimitiveType.java b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaPrimitiveType.java similarity index 67% rename from src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaPrimitiveType.java rename to src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaPrimitiveType.java index 918c8b5..5ace8c8 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaPrimitiveType.java +++ b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaPrimitiveType.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities.javaType; +package jdiextractor.tracemodel.entities.javaType; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; public class TraceJavaPrimitiveType extends TraceJavaType { diff --git a/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaReferenceType.java b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaReferenceType.java new file mode 100644 index 0000000..a526ebe --- /dev/null +++ b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaReferenceType.java @@ -0,0 +1,29 @@ +package jdiextractor.tracemodel.entities.javaType; + +public abstract class TraceJavaReferenceType extends TraceJavaType { + + private final String DEFAULT_PACKAGE = ""; + + private boolean isParametric; + + public TraceJavaReferenceType(String name) { + super(name); + } + + public boolean isParametric() { + return isParametric; + } + + public void setIsParametric(boolean isParametric) { + this.isParametric = isParametric; + } + + public String getFullyQualifiedName() { + if (this.getName().contains(".")) { + return this.getName(); + } else { + return this.DEFAULT_PACKAGE + "." + this.getName(); + } + } + +} diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaType.java b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaType.java similarity index 71% rename from src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaType.java rename to src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaType.java index 95b37e6..a36c6da 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaType.java +++ b/src/main/java/jdiextractor/tracemodel/entities/javaType/TraceJavaType.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities.javaType; +package jdiextractor.tracemodel.entities.javaType; -import org.jdiextractor.tracemodel.TraceEntity; +import jdiextractor.tracemodel.TraceEntity; public abstract class TraceJavaType extends TraceEntity { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceArrayReference.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceArrayReference.java similarity index 86% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceArrayReference.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceArrayReference.java index de2af57..713bcbc 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceArrayReference.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceArrayReference.java @@ -1,9 +1,9 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; import java.util.ArrayList; import java.util.List; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; public class TraceArrayReference extends TraceObjectReference { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceArrayValue.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceArrayValue.java similarity index 54% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceArrayValue.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceArrayValue.java index 2822d1d..304f5c0 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceArrayValue.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceArrayValue.java @@ -1,9 +1,9 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; -import org.jdiextractor.tracemodel.entities.TraceValue; -import org.jdiextractor.tracemodel.entities.TraceValueContainer; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; +import jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.tracemodel.entities.TraceValueContainer; public class TraceArrayValue extends TraceEntity implements TraceValueContainer { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceClassReference.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceClassReference.java similarity index 85% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceClassReference.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceClassReference.java index 8f3f62a..b97b4bb 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceClassReference.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceClassReference.java @@ -1,9 +1,9 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; import java.util.ArrayList; import java.util.List; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; public class TraceClassReference extends TraceObjectReference { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceField.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceField.java similarity index 74% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceField.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceField.java index 4c9d7dc..c5f466c 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceField.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceField.java @@ -1,9 +1,9 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.TraceEntity; -import org.jdiextractor.tracemodel.entities.TraceValue; -import org.jdiextractor.tracemodel.entities.TraceValueContainer; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.TraceEntity; +import jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.tracemodel.entities.TraceValueContainer; public class TraceField extends TraceEntity implements TraceValueContainer { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceObjectReference.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceObjectReference.java similarity index 67% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceObjectReference.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceObjectReference.java index 905a989..1f59b0f 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceObjectReference.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceObjectReference.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; -import org.jdiextractor.tracemodel.entities.TraceValue; -import org.jdiextractor.tracemodel.entities.javaType.TraceJavaType; +import jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.tracemodel.entities.javaType.TraceJavaType; public abstract class TraceObjectReference extends TraceValue { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TracePrimitiveValue.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TracePrimitiveValue.java similarity index 71% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TracePrimitiveValue.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TracePrimitiveValue.java index 8642c30..ba12c93 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TracePrimitiveValue.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TracePrimitiveValue.java @@ -1,7 +1,7 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; -import org.jdiextractor.service.serializer.TraceSerializer; -import org.jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.tracemodel.entities.TraceValue; public class TracePrimitiveValue extends TraceValue { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceStringReference.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceStringReference.java similarity index 73% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceStringReference.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceStringReference.java index c67f03b..b389b96 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceStringReference.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceStringReference.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; public class TraceStringReference extends TraceObjectReference { diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceValueAlreadyFound.java b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceValueAlreadyFound.java similarity index 68% rename from src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceValueAlreadyFound.java rename to src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceValueAlreadyFound.java index 54479cd..57e48bd 100644 --- a/src/main/java/org/jdiextractor/tracemodel/entities/traceValues/TraceValueAlreadyFound.java +++ b/src/main/java/jdiextractor/tracemodel/entities/traceValues/TraceValueAlreadyFound.java @@ -1,6 +1,6 @@ -package org.jdiextractor.tracemodel.entities.traceValues; +package jdiextractor.tracemodel.entities.traceValues; -import org.jdiextractor.service.serializer.TraceSerializer; +import jdiextractor.service.serializer.TraceSerializer; public class TraceValueAlreadyFound extends TraceObjectReference { diff --git a/src/main/java/org/jdiextractor/config/CallStackHistoryExtractorConfig.java b/src/main/java/org/jdiextractor/config/CallStackHistoryExtractorConfig.java deleted file mode 100644 index 0c3d44c..0000000 --- a/src/main/java/org/jdiextractor/config/CallStackHistoryExtractorConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.jdiextractor.config; - -import com.fasterxml.jackson.databind.JsonNode; - -public class CallStackHistoryExtractorConfig extends AbstractExtractorConfig { - - public static CallStackHistoryExtractorConfig fromJson(JsonNode rootNode) { - CallStackHistoryExtractorConfig config = new CallStackHistoryExtractorConfig(); - config.fillFromJson(rootNode); - return config; - } - - @Override - protected void fillFromJson(JsonNode rootNode) { - super.fillFromJson(rootNode); - } - -} diff --git a/src/main/java/org/jdiextractor/config/CallStackSnapshotExtractorConfig.java b/src/main/java/org/jdiextractor/config/CallStackSnapshotExtractorConfig.java deleted file mode 100644 index 917cc96..0000000 --- a/src/main/java/org/jdiextractor/config/CallStackSnapshotExtractorConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.jdiextractor.config; - -import com.fasterxml.jackson.databind.JsonNode; - -public class CallStackSnapshotExtractorConfig extends AbstractExtractorConfig { - - public static CallStackSnapshotExtractorConfig fromJson(JsonNode rootNode) { - CallStackSnapshotExtractorConfig config = new CallStackSnapshotExtractorConfig(); - config.fillFromJson(rootNode); - return config; - } - - @Override - protected void fillFromJson(JsonNode rootNode) { - super.fillFromJson(rootNode); - - } - -} diff --git a/src/main/java/org/jdiextractor/config/TraceExtractorStepConfig.java b/src/main/java/org/jdiextractor/config/TraceExtractorStepConfig.java deleted file mode 100644 index 4fabacd..0000000 --- a/src/main/java/org/jdiextractor/config/TraceExtractorStepConfig.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.jdiextractor.config; - -import com.fasterxml.jackson.databind.JsonNode; - -public class TraceExtractorStepConfig extends AbstractExtractorConfig { - - protected boolean activateEndpoint; - protected boolean collectValues; - protected int maxMethodDepth; - protected int maxSteps; - - public static TraceExtractorStepConfig fromJson(JsonNode rootNode) { - TraceExtractorStepConfig config = new TraceExtractorStepConfig(); - config.fillFromJson(rootNode); - return config; - } - - @Override - protected void fillFromJson(JsonNode rootNode) { - super.fillFromJson(rootNode); - this.activateEndpoint = rootNode.get("activateEndpoint").asBoolean(); - this.collectValues = rootNode.get("collectValues").asBoolean(); - this.maxMethodDepth = rootNode.get("maxMethodDepth").asInt(); - this.maxSteps = rootNode.get("maxSteps").asInt(); - } - - public boolean activateEndpoint() { - return this.activateEndpoint; - } - - public boolean collectValues() { - return this.collectValues; - } - - public int getMaxMethodDepth() { - return this.maxMethodDepth; - } - - public int getMaxSteps() { - return this.maxSteps; - } -} diff --git a/src/main/java/org/jdiextractor/service/serializer/BufferedTraceConverter.java b/src/main/java/org/jdiextractor/service/serializer/BufferedTraceConverter.java deleted file mode 100644 index 92ce3ee..0000000 --- a/src/main/java/org/jdiextractor/service/serializer/BufferedTraceConverter.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.jdiextractor.service.serializer; - -import org.jdiextractor.tracemodel.entities.TraceElement; - -public class BufferedTraceConverter extends JDIToTraceConverter { - - - public BufferedTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceLogger logger) { - super(valuesIndependents, maxObjectDepth, logger); - logger.startSerialize(); - } - - @Override - protected void addElement(TraceElement element) { - logger.serialize(element); - } - - @Override - public void serialize() { - logger.endSerialize(); - } - - @Override - public void removeLastElement() { - throw new IllegalStateException("Should not remove an element on buffered converter"); - } - -} diff --git a/src/main/java/org/jdiextractor/service/serializer/DefferedTraceConverter.java b/src/main/java/org/jdiextractor/service/serializer/DefferedTraceConverter.java deleted file mode 100644 index 2e6f5cb..0000000 --- a/src/main/java/org/jdiextractor/service/serializer/DefferedTraceConverter.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.jdiextractor.service.serializer; - -import org.jdiextractor.tracemodel.entities.Trace; -import org.jdiextractor.tracemodel.entities.TraceElement; - -public class DefferedTraceConverter extends JDIToTraceConverter { - - private Trace trace; - - public DefferedTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceLogger logger) { - super(valuesIndependents, maxObjectDepth, logger); - this.trace = new Trace(); - } - - @Override - protected void addElement(TraceElement element) { - this.trace.addElement(element); - } - - @Override - public void serialize() { - logger.serialize(trace); - } - - @Override - public void removeLastElement() { - this.trace.removeLastElement(); - } - -} diff --git a/src/main/java/org/jdiextractor/service/serializer/TraceLogger.java b/src/main/java/org/jdiextractor/service/serializer/TraceLogger.java deleted file mode 100644 index 84e0840..0000000 --- a/src/main/java/org/jdiextractor/service/serializer/TraceLogger.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.jdiextractor.service.serializer; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; - -import org.jdiextractor.config.components.LoggingConfig; -import org.jdiextractor.tracemodel.entities.Trace; -import org.jdiextractor.tracemodel.entities.TraceElement; - -public class TraceLogger { - - private LoggingConfig loggingConfig; - - private TraceSerializer serializer; - - /** - * Whether the values are independents between all element of the trace or not - */ - private boolean valueIdependents; - - /** - * Constructor of TraceLogger - * - * @param loggingConfig information to instantiate the logger - * @param valueIdependents, Whether the values are independents between all - * element of the trace or not - */ - public TraceLogger(LoggingConfig loggingConfig, boolean valueIdependents) { - this.loggingConfig = loggingConfig; - this.valueIdependents = valueIdependents; - } - - public void serialize(Trace trace) { - this.startSerialize(); - for (TraceElement elem : trace.getElements()) { - this.serialize(elem); - } - this.endSerialize(); - } - - public void startSerialize() { - BufferedWriter output; - try { - output = new BufferedWriter( - new FileWriter(this.loggingConfig.getOutputName() + "." + this.loggingConfig.getExtension())); - this.serializer = new TraceSerializerJson(output, valueIdependents); - this.serializer.startSerialize(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void serialize(TraceElement element) { - this.serializer.serialize(element); - } - - public void endSerialize() { - this.serializer.endSerialize(); - } - -} diff --git a/src/main/java/org/jdiextractor/simulation/ObjectArgsSimulation.java b/src/main/java/org/jdiextractor/simulation/ObjectArgsSimulation.java deleted file mode 100644 index bf2696d..0000000 --- a/src/main/java/org/jdiextractor/simulation/ObjectArgsSimulation.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.jdiextractor.simulation; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Create simple call stack for the JDIAttach when putting a breakpoint on java.lang.Runtime.exec(java.lang.String). - * This simulation demonstrate that not every arguments given to a method are references - */ -public class ObjectArgsSimulation { - - public static void main(String[] args) { - List l = new ArrayList<>(); - addAnElement(l); - } - - public static void addAnElement(List l) { - l.add("AnElement"); - addAnotherElement(l, 7); - } - - public static void addAnotherElement(List l, int i) { - l.add("AnotherElement"); - wowAnElementWasAdded(l); - } - - public static void wowAnElementWasAdded(List l) { - try { - Runtime.getRuntime().exec("open -a calculator"); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/org/jdiextractor/simulation/ObjectRefSimulation.java b/src/main/java/org/jdiextractor/simulation/ObjectRefSimulation.java deleted file mode 100644 index 3786070..0000000 --- a/src/main/java/org/jdiextractor/simulation/ObjectRefSimulation.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.jdiextractor.simulation; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Create simple call stack for the JDIAttach when putting a breakpoint on java.lang.Runtime.exec(java.lang.String). - * This simulation demonstrate the fact that if an object is modified in a later stack frame, previous frames will also have theses changes. - */ -public class ObjectRefSimulation { - - public static void main(String[] args) { - List l = new ArrayList<>(); - addAnElement(l); - } - - public static void addAnElement(List l) { - l.add("AnElement"); - addAnotherElement(l); - } - - public static void addAnotherElement(List l) { - l.add("AnotherElement"); - wowAnElementWasAdded(l); - } - - public static void wowAnElementWasAdded(List l) { - try { - Runtime.getRuntime().exec("open -a calculator"); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaClass.java b/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaClass.java deleted file mode 100644 index 7c719f6..0000000 --- a/src/main/java/org/jdiextractor/tracemodel/entities/javaType/TraceJavaClass.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jdiextractor.tracemodel.entities.javaType; - -import org.jdiextractor.service.serializer.TraceSerializer; - -public class TraceJavaClass extends TraceJavaType { - - private boolean isParametric; - - public TraceJavaClass(String name) { - super(name); - } - - public boolean isParametric() { - return isParametric; - } - - public void setIsParametric(boolean isParametric) { - this.isParametric = isParametric; - } - - @Override - public void acceptSerializer(TraceSerializer serializer) { - serializer.serialize(this); - } - -} diff --git a/src/test/java/dummies/AnonymousClass$10.java b/src/test/java/dummies/AnonymousClass$10.java new file mode 100644 index 0000000..f80127d --- /dev/null +++ b/src/test/java/dummies/AnonymousClass$10.java @@ -0,0 +1,15 @@ +package dummies; + +/** + * This class use the same name as anonymous classes does to check how JavaTraceExtractor handle it + */ +public class AnonymousClass$10 { + + public static void main(String[] args) { + endpoint(); + } + + public static void endpoint() { + } + +} diff --git a/src/test/java/dummies/AnonymousClass.java b/src/test/java/dummies/AnonymousClass.java new file mode 100644 index 0000000..0c5e5b0 --- /dev/null +++ b/src/test/java/dummies/AnonymousClass.java @@ -0,0 +1,26 @@ +package dummies; + +/** + * This class offer a simple Anonymous class declaration to check how JavaTraceExtractor handle it + */ +public class AnonymousClass { + + private static class Dog { + public void foo() { + } + } + + public static void main(String[] args) { + Dog o = new Dog() { + public void foo() { + endpoint(this); + } + }; + + o.foo(); + } + + public static void endpoint(Dog o) { + } + +} diff --git a/src/test/java/dummies/AnonymousInterfaceClass.java b/src/test/java/dummies/AnonymousInterfaceClass.java new file mode 100644 index 0000000..d8ea9f0 --- /dev/null +++ b/src/test/java/dummies/AnonymousInterfaceClass.java @@ -0,0 +1,26 @@ +package dummies; + +/** + * This class offer a simple Anonymous class of an Interface + * It is basically the same as AnonymousClass, but we use an Interface here to check how it is handled + */ +public class AnonymousInterfaceClass { + + private static interface Dog { + public void foo(); + } + + public static void main(String[] args) { + Dog o = new Dog() { + public void foo() { + endpoint(this); + } + }; + + o.foo(); + } + + public static void endpoint(Dog o) { + } + +} diff --git a/src/test/java/dummies/AnonymousObjectClass.java b/src/test/java/dummies/AnonymousObjectClass.java new file mode 100644 index 0000000..508a975 --- /dev/null +++ b/src/test/java/dummies/AnonymousObjectClass.java @@ -0,0 +1,24 @@ +package dummies; + +/** + * This class offer a simple Anonymous class of an Object + * It is basically the same as AnonymousClass, but we use Object here to check how it is handled + */ +public class AnonymousObjectClass { + + + public static void main(String[] args) { + Object o = new Object() { + public String toString() { + endpoint(this); + return ""; + } + }; + + o.toString(); + } + + public static void endpoint(Object o) { + } + +} diff --git a/src/test/java/dummies/ClassNotFoundParameter.java b/src/test/java/dummies/ClassNotFoundParameter.java new file mode 100644 index 0000000..a15cafc --- /dev/null +++ b/src/test/java/dummies/ClassNotFoundParameter.java @@ -0,0 +1,16 @@ +package dummies; + +/** + * Provide a example of the use of ClassNotFoundException for a parameter of a method + * Enable to check if JDI handle it as any other parameter + */ +public class ClassNotFoundParameter { + + public static void main(String[] args) { + endpoint(null); + } + + public static void endpoint(ClassNotFoundException ex) { + } + +} diff --git a/src/test/java/dummies/ClassNotLoadedExample.java b/src/test/java/dummies/ClassNotLoadedExample.java new file mode 100644 index 0000000..9eaf0ac --- /dev/null +++ b/src/test/java/dummies/ClassNotLoadedExample.java @@ -0,0 +1,18 @@ +package dummies; + +public class ClassNotLoadedExample { + //TODO + public static void main(String[] args) { + // Never used + NeverUsed variableFantome = null; + + // Si ton débogueur JDI place un breakpoint sur la ligne ci-dessous + // et demande le type de 'variableFantome', l'exception explosera. + System.out.println("Breakpoint JDI ici."); + } + +} + +class NeverUsed { + int id; +} \ No newline at end of file diff --git a/src/test/java/dummies/EnumValues.java b/src/test/java/dummies/EnumValues.java new file mode 100644 index 0000000..de46ad8 --- /dev/null +++ b/src/test/java/dummies/EnumValues.java @@ -0,0 +1,19 @@ +package dummies; + +/** + * This class offer a simple use of an enum value to check how enum are collected + */ +public class EnumValues { + + public static enum Animal { + DOG; + } + + public static void main(String[] args) { + endpoint(Animal.DOG); + } + + public static void endpoint(Animal d) {} + + +} diff --git a/src/test/java/dummies/ObjectArgsTypes.java b/src/test/java/dummies/ObjectArgsTypes.java new file mode 100644 index 0000000..4013819 --- /dev/null +++ b/src/test/java/dummies/ObjectArgsTypes.java @@ -0,0 +1,20 @@ +package dummies; + +import java.util.ArrayList; +import java.util.List; + +/** + * Create simple call stack for the JDIAttach when putting a breakpoint on the method dummies.ObjectArgsSimulation.endpoint(java.lang.String,int). + * This simulation demonstrate that not every arguments given to a method are references, in this case, the second parameter is an int + */ +public class ObjectArgsTypes { + + public static void main(String[] args) { + List l = new ArrayList<>(); + endpoint(l, 7); + } + + public static void endpoint(List l, int i) {} + + +} diff --git a/src/test/java/dummies/ObjectEvolution.java b/src/test/java/dummies/ObjectEvolution.java new file mode 100644 index 0000000..bf8b9b4 --- /dev/null +++ b/src/test/java/dummies/ObjectEvolution.java @@ -0,0 +1,34 @@ +package dummies; + +/** + * Offer a chain of methods modifying an object in multiple steps Help check if + * the model extracted can follow the evolution of the object or just take the last version of it + */ +public class ObjectEvolution { + + private static class Dog { + private int age; + + public Dog(int age) { + this.age = age; + } + + public void setAge(int age) { + this.age = age; + } + + } + + public static void main(String[] args) { + Dog d = new Dog(1); + changeAge(d); + } + + public static void changeAge(Dog d) { + d.setAge(2); + endpoint(d); + } + + public static void endpoint(Dog d) { + } +} diff --git a/src/test/java/dummies/ParametricClass.java b/src/test/java/dummies/ParametricClass.java new file mode 100644 index 0000000..f77a83e --- /dev/null +++ b/src/test/java/dummies/ParametricClass.java @@ -0,0 +1,15 @@ +package dummies; + +/** + * This class offer a simple Parametric class declaration to check how JavaTraceExtractor handle it + */ +public class ParametricClass { + + public static void main(String[] args) { + endpoint(); + } + + public static void endpoint() { + } + +} diff --git a/src/test/java/dummies/ParametricInterfaceContainer.java b/src/test/java/dummies/ParametricInterfaceContainer.java new file mode 100644 index 0000000..673604b --- /dev/null +++ b/src/test/java/dummies/ParametricInterfaceContainer.java @@ -0,0 +1,17 @@ +package dummies; + +/** + * This class offer a simple Parametric interface declaration to check how JavaTraceExtractor handle it + */ +public class ParametricInterfaceContainer { + + public static interface ParametricInterface{} + + public static void main(String[] args) { + endpoint(null); + } + + public static void endpoint(ParametricInterface p ) { + } + +} diff --git a/src/test/java/dummies/ProxyClass.java b/src/test/java/dummies/ProxyClass.java new file mode 100644 index 0000000..e7e9b4c --- /dev/null +++ b/src/test/java/dummies/ProxyClass.java @@ -0,0 +1,38 @@ +package dummies; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Simple class to test interaction with a proxy + */ +public class ProxyClass { + + public static interface Dog { + public void foo(); + } + + public static class DogHandler implements InvocationHandler { + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + ProxyClass.endpoint((Dog) proxy); + return null; + } + + } + + public static void main(String[] args) { + InvocationHandler handler = new DogHandler(); + + // Create a Dog Proxy + Dog proxy = (Dog) Proxy.newProxyInstance(Dog.class.getClassLoader(), new Class[]{Dog.class}, handler); + + proxy.foo(); + } + + public static void endpoint(Dog dog) { + } + +} diff --git a/src/test/java/dummies/StringValues.java b/src/test/java/dummies/StringValues.java new file mode 100644 index 0000000..b05649b --- /dev/null +++ b/src/test/java/dummies/StringValues.java @@ -0,0 +1,16 @@ +package dummies; + +/** + * This class offer a simple String variable to verify if String values are collected or not + */ +public class StringValues { + + public static void main(String[] args) { + String s = "toto"; + endpoint(s); + } + + public static void endpoint(String s) {} + + +} diff --git a/src/test/java/helper/JsonValidator.java b/src/test/java/helper/JsonValidator.java new file mode 100644 index 0000000..ed90bae --- /dev/null +++ b/src/test/java/helper/JsonValidator.java @@ -0,0 +1,15 @@ +package helper; + +import java.util.regex.Pattern; + +public class JsonValidator { + + private static final Pattern JSON_PATTERN_WITH_TYPES_AND_OTHERS = Pattern.compile( + "\\{\\s*(\"[^\"]+\"\\s*:\\s*(\"[^\"]+\"|true|false|null|\\d+(\\.\\d+)?|\\{[^{}]*}|\\[[^\\[\\]]*]))" + + "(\\s*,\\s*\"[^\"]+\"\\s*:\\s*(\"[^\"]+\"|true|false|null|\\d+(\\.\\d+)?|\\{[^{}]*}|\\[[^\\[\\]]*]))*\\s*}" + ); + + public static boolean validate(String json){ + return JSON_PATTERN_WITH_TYPES_AND_OTHERS.matcher(json).matches(); + } +} diff --git a/src/test/java/jdiextractor/core/JDIExtractorTest_AdHoc.java b/src/test/java/jdiextractor/core/JDIExtractorTest_AdHoc.java new file mode 100644 index 0000000..50701c4 --- /dev/null +++ b/src/test/java/jdiextractor/core/JDIExtractorTest_AdHoc.java @@ -0,0 +1,475 @@ +package jdiextractor.core; + +import static org.junit.Assert.*; + +import java.io.File; +import java.util.List; +import java.util.Optional; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; + +import com.sun.jdi.VirtualMachine; + +import jdiextractor.config.AbstractExtractorConfig; +import jdiextractor.config.CallStackHistoryExtractorConfig; +import jdiextractor.config.CallStackSnapshotExtractorConfig; +import jdiextractor.config.components.BreakpointConfig; +import jdiextractor.service.connector.JDIAttach; +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceMethod; +import jdiextractor.tracemodel.entities.TraceReceiver; +import jdiextractor.tracemodel.entities.TraceValue; +import jdiextractor.tracemodel.entities.javaType.TraceJavaClass; +import jdiextractor.tracemodel.entities.javaType.TraceJavaInterface; +import jdiextractor.tracemodel.entities.javaType.TraceJavaReferenceType; +import jdiextractor.tracemodel.entities.traceValues.TraceClassReference; +import jdiextractor.tracemodel.entities.traceValues.TraceField; +import jdiextractor.tracemodel.entities.traceValues.TracePrimitiveValue; +import jdiextractor.tracemodel.entities.traceValues.TraceStringReference; +import jdiextractor.tracemodel.entities.traceValues.TraceValueAlreadyFound; + +public class JDIExtractorTest_AdHoc { + + /** Target Virtual Machine connected with JDI */ + private VirtualMachine vm; + + /** Underlying OS process running the target JVM */ + private Process process; + + /** + * Launches the target class in a new JVM process and attaches the JDI. + */ + public void startTargetJVM(String className, AbstractExtractorConfig config) { + String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + String currentClasspath = System.getProperty("java.class.path"); + + // The hardcoded port 5006 must strictly match the configuration in + // config.getVm() + ProcessBuilder builder = new ProcessBuilder(javaBin, + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006", "-cp", currentClasspath, + className); + + // Redirects child process I/O to the test console for easier debugging + builder.inheritIO(); + + try { + process = builder.start(); + + vm = (new JDIAttach()).attachToJDI(config.getVm()); + } catch (Exception e) { + fail("Failed to start or attach to target JVM: " + e.getMessage()); + } + } + + @After + /** + * Ensures VM and process resources are strictly terminated after each test. + */ + public void after() { + try { + if (vm != null) { + vm.dispose(); + } + } finally { + // A finally block guarantees the OS process is killed even if JDI fails to + // disconnect. + if (process != null) { + process.destroyForcibly(); + } + } + } + + /** + * Utility method that collect the field of a given name in a class reference + */ + public TraceField getFieldNamed(TraceClassReference classRef, String fieldName) { + Optional optionalField = classRef.getFields().stream().filter((field) -> field.getName().equals(fieldName)).findAny(); + if(optionalField.isEmpty()) { + throw new RuntimeException("Given field name does not exist in the given class"); + } + return optionalField.get(); + } + + @Test + public void testMethodArgumentCanReferencesAndPrimitiveTypes() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ObjectArgsTypes", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ObjectArgsTypes", "endpoint", List.of("java.util.List", "int"), + 0)) + .build(); + this.startTargetJVM("dummies.ObjectArgsTypes", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + + TraceMethod endpoint = (TraceMethod) trace.getElements().get(1); + assertNotNull(endpoint); + + assertEquals(2, endpoint.getParameters().size()); + + assertTrue(endpoint.getArguments().get(0).getValue() instanceof TraceClassReference); + assertTrue(endpoint.getArguments().get(1).getValue() instanceof TracePrimitiveValue); + } + + /** + * This test is ignored because we deactivated the extraction of String values. + * It causes a parsing error when handling specific attack payloads (e.g., + * CommonsCollection1 from ysoserial). * @see + * jdiextractor.core.JDIToTraceConverter#newStringReferenceFrom(com.sun.jdi.StringReference) + */ + @Test + @Ignore("Deactivated: String parsing error on ysoserial payloads. Check JDIToTraceConverter.") + public void testStringValue() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.StringValues", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.StringValues", "endpoint", List.of("java.lang.String"), 0)) + .build(); + this.startTargetJVM("dummies.StringValues", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + + TraceMethod endpoint = (TraceMethod) trace.getElements().get(1); + assertNotNull(endpoint); + + assertEquals("toto", ((TraceStringReference) endpoint.getArguments().get(0).getValue()).getValue()); + } + + @Test + public void testCallStackSnapshotOnlyGetLastVersionOfObject() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ObjectEvolution", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ObjectEvolution", "endpoint", List.of("dummies.ObjectEvolution$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.ObjectEvolution", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + + // At first, the dog has the attribute age set at 1, but the snapshot took the last version and see the age of 2 + TraceMethod changeAge = (TraceMethod) trace.getElements().get(1); + assertNotNull(changeAge); + assertEquals("changeAge", changeAge.getName()); + + TraceClassReference dogReference = (TraceClassReference) changeAge.getArguments().get(0).getValue(); + Object rawAge1 = ((TracePrimitiveValue) getFieldNamed(dogReference, "age").getValue()).getValue(); + int actualAge1 = ((com.sun.jdi.IntegerValue) rawAge1).value(); + + assertEquals(2, actualAge1); + + // At the last method, we have the dog again, but we got a TraceValueAlreadyFound + TraceMethod endpoint = (TraceMethod) trace.getElements().get(2); + assertNotNull(endpoint); + assertEquals("endpoint", endpoint.getName()); + + TraceValueAlreadyFound alreadyFoundDog = (TraceValueAlreadyFound) endpoint.getArguments().get(0).getValue(); + + // Checking that the already found variable is really the dog + assertEquals(dogReference.getUniqueID(),alreadyFoundDog.getUniqueID()); + } + + @Test + public void testCallStackHistoryGetEvolutionOfObject() { + CallStackHistoryExtractorConfig config = CallStackHistoryExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ObjectEvolution", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ObjectEvolution", "endpoint", List.of("dummies.ObjectEvolution$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.ObjectEvolution", config); + + CallStackHistoryExtractor extractor = new CallStackHistoryExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + + // At first, the dog has the attribute age set at 1 + TraceMethod changeAge = (TraceMethod) trace.getElements().get(1); + assertNotNull(changeAge); + assertEquals("changeAge", changeAge.getName()); + + Object rawAge1 = ((TracePrimitiveValue) getFieldNamed((TraceClassReference) changeAge.getArguments().get(0).getValue(), "age").getValue()).getValue(); + int actualAge1 = ((com.sun.jdi.IntegerValue) rawAge1).value(); + + assertEquals(1, actualAge1); + + // At the end, the dog has the attribute age set at 2 + TraceMethod endpoint = (TraceMethod) trace.getElements().get(2); + assertNotNull(endpoint); + assertEquals("endpoint", endpoint.getName()); + + Object rawAge2 = ((TracePrimitiveValue) getFieldNamed((TraceClassReference) endpoint.getArguments().get(0).getValue(), "age").getValue()).getValue(); + int actualAge2 = ((com.sun.jdi.IntegerValue) rawAge2).value(); + + assertEquals(2, actualAge2); + } + + /** + * This test explicitly represent that enum and enum values are represented as any class would + * Meaning that JavaTraceExtractor does not understand what an enum is, but directly represent how an enum really work + * To understand what really is an enum you can read this https://stackoverflow.com/questions/29139633/java-class-equivalent-for-an-enum + * or this https://dev.to/satyam_gupta_0d1ff2152dcc/java-enums-explained-beyond-basic-constants-a84 + */ + @Test + public void testEnumRepresentation() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.EnumValues", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.EnumValues", "endpoint", List.of("dummies.EnumValues$Animal"), 0)) + .build(); + this.startTargetJVM("dummies.EnumValues", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + + TraceMethod endpoint = (TraceMethod) trace.getElements().get(1); + + assertEquals(1,endpoint.getArguments().size()); + assertTrue(endpoint.getArguments().get(0).getValue() instanceof TraceClassReference); + } + + @Test + public void testAnonymousClassRepresentation() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.AnonymousClass", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.AnonymousClass", "endpoint", List.of("dummies.AnonymousClass$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.AnonymousClass", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod foo = (TraceMethod) trace.getElements().get(1); + + assertNotNull(foo); + + TraceReceiver anonymousReceiver = foo.getReceiver(); + + TraceClassReference anonymousClassRef = (TraceClassReference) anonymousReceiver.getValue(); + // IsAnonymous + assertTrue(((TraceJavaClass) anonymousClassRef.getType()).isAnonymous()); + // Anonymous name + assertEquals("dummies.AnonymousClass$1",anonymousClassRef.getType().getName()); + // Parent name + assertEquals("dummies.AnonymousClass$Dog", ((TraceJavaClass) anonymousClassRef.getType()).getAnonymousParent().getName()); + } + + @Test + public void testAnonymousObjectClassRepresentation() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.AnonymousObjectClass", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.AnonymousObjectClass", "endpoint", List.of("java.lang.Object"), 0)) + .build(); + this.startTargetJVM("dummies.AnonymousObjectClass", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod toString = (TraceMethod) trace.getElements().get(1); + assertNotNull(toString); + + TraceReceiver anonymousReceiver = toString.getReceiver(); + + TraceClassReference anonymousClassRef = (TraceClassReference) anonymousReceiver.getValue(); + + // IsAnonymous + assertTrue(((TraceJavaClass) anonymousClassRef.getType()).isAnonymous()); + // Anonymous name + assertEquals("dummies.AnonymousObjectClass$1",anonymousClassRef.getType().getName()); + // Parent name + assertEquals("java.lang.Object", ((TraceJavaClass) anonymousClassRef.getType()).getAnonymousParent().getName()); + } + + @Test + public void testAnonymousInterfaceClassRepresentation() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.AnonymousInterfaceClass", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.AnonymousInterfaceClass", "endpoint", List.of("dummies.AnonymousInterfaceClass$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.AnonymousInterfaceClass", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod foo = (TraceMethod) trace.getElements().get(1); + assertNotNull(foo); + + TraceReceiver anonymousReceiver = foo.getReceiver(); + TraceClassReference anonymousClassRef = (TraceClassReference) anonymousReceiver.getValue(); + + // IsAnonymous + assertTrue(((TraceJavaClass) anonymousClassRef.getType()).isAnonymous()); + // Anonymous name + assertEquals("dummies.AnonymousInterfaceClass$1",anonymousClassRef.getType().getName()); + // Parent name + assertEquals("dummies.AnonymousInterfaceClass$Dog", ((TraceJavaClass) anonymousClassRef.getType()).getAnonymousParent().getName()); + assertTrue(((TraceJavaClass) anonymousClassRef.getType()).getAnonymousParent() instanceof TraceJavaInterface); + } + + @Test + public void testNotReallyAnonymousClass() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.AnonymousClass$10", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.AnonymousClass$10", "endpoint", List.of(), 0)) + .build(); + this.startTargetJVM("dummies.AnonymousClass$10", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod main = (TraceMethod) trace.getElements().get(0); + + assertFalse(((TraceJavaClass) main.getParentType()).isAnonymous()); + } + + @Test + public void testParametricInterface() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ParametricInterfaceContainer", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ParametricInterfaceContainer", "endpoint", List.of("dummies.ParametricInterfaceContainer$ParametricInterface"), 0)) + .build(); + this.startTargetJVM("dummies.ParametricInterfaceContainer", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod endpoint = (TraceMethod) trace.getElements().get(1); + + assertEquals("dummies.ParametricInterfaceContainer$ParametricInterface",endpoint.getParameters().get(0).getType().getName()); + assertTrue(((TraceJavaInterface) endpoint.getParameters().get(0).getType()).isParametric()); + + } + + @Test + public void testParametricClass() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ParametricClass", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ParametricClass", "endpoint", List.of(), 0)) + .build(); + this.startTargetJVM("dummies.ParametricClass", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod main = (TraceMethod) trace.getElements().get(0); + + assertTrue(main.getParentType() instanceof TraceJavaClass); + + TraceJavaClass clazz = (TraceJavaClass) main.getParentType(); + assertTrue(clazz.isParametric()); + } + + @Test + public void testNonParametricClassAreNotParametric() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ObjectEvolution", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ObjectEvolution", "endpoint", List.of("dummies.ObjectEvolution$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.ObjectEvolution", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod main = (TraceMethod) trace.getElements().get(0); + + TraceJavaClass clazz = (TraceJavaClass) main.getParentType(); + assertFalse(clazz.isParametric()); + } + + /** + * The argument of main(String[]) was previously resolved as a parametric class due to the use of only genericSignature on types + * But a genericSignature exist whenever it use a generic parameter OR it extends a class/interface that does + */ + @Test + public void testArrayAreNotParametricClasses() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ObjectEvolution", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ObjectEvolution", "endpoint", List.of("dummies.ObjectEvolution$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.ObjectEvolution", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod main = (TraceMethod) trace.getElements().get(0); + + assertFalse(((TraceJavaReferenceType) main.getParameters().get(0).getType()).isParametric()); + } + + /** + * Check if ClassNotFoundException is handled as any other parameter would + */ + @Test + public void testClassNotFoundExceptionTypeOnMethodParameter() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ClassNotFoundParameter", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ClassNotFoundParameter", "endpoint", List.of("java.lang.ClassNotFoundException"), 0)) + .build(); + this.startTargetJVM("dummies.ClassNotFoundParameter", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod endpoint = (TraceMethod) trace.getElements().get(1); + + assertEquals("java.lang.ClassNotFoundException", endpoint.getParameters().get(0).getType().getName()); + } + + + @Test + public void testProxyStack() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ProxyClass", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ProxyClass", "endpoint", List.of("dummies.ProxyClass$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.ProxyClass", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + assertEquals(4,trace.getElements().size()); + + assertEquals("dummies.ProxyClass.main(String[])",((TraceMethod) trace.getElements().get(0)).getFullyQualifiedName()); + assertEquals("com.sun.proxy.$Proxy0.foo()",((TraceMethod) trace.getElements().get(1)).getFullyQualifiedName()); + assertEquals("dummies.ProxyClass$DogHandler.invoke(Object,Method,Object[])",((TraceMethod) trace.getElements().get(2)).getFullyQualifiedName()); + assertEquals("dummies.ProxyClass.endpoint(ProxyClass$Dog)",((TraceMethod) trace.getElements().get(3)).getFullyQualifiedName()); + } + + @Test + public void testProxyType() { + CallStackSnapshotExtractorConfig config = CallStackSnapshotExtractorConfig.builder() + .entrypoint(new BreakpointConfig("dummies.ProxyClass", "main", List.of("java.lang.String[]"), 0)) + .endpoint(new BreakpointConfig("dummies.ProxyClass", "endpoint", List.of("dummies.ProxyClass$Dog"), 0)) + .build(); + this.startTargetJVM("dummies.ProxyClass", config); + + CallStackSnapshotExtractor extractor = new CallStackSnapshotExtractor(false); + extractor.launch(vm, config); + + Trace trace = extractor.getTrace(); + TraceMethod foo = (TraceMethod) trace.getElements().get(1); + TraceClassReference proxy = (TraceClassReference) foo.getReceiver().getValue(); + + assertTrue(((TraceJavaClass) proxy.getType()).isAnonymous()); + assertEquals("dummies.ProxyClass$Dog", ((TraceJavaClass) proxy.getType()).getAnonymousParent().getName()); + } + + +} \ No newline at end of file diff --git a/src/test/java/jdiextractor/service/serializer/JDIToTraceConverterTest.java b/src/test/java/jdiextractor/service/serializer/JDIToTraceConverterTest.java new file mode 100644 index 0000000..992f2da --- /dev/null +++ b/src/test/java/jdiextractor/service/serializer/JDIToTraceConverterTest.java @@ -0,0 +1,117 @@ +package jdiextractor.service.serializer; + +import com.sun.jdi.ClassNotLoadedException; +import com.sun.jdi.LocalVariable; +import com.sun.jdi.StackFrame; +import com.sun.jdi.Type; +import com.sun.jdi.VirtualMachine; + +import jdiextractor.tracemodel.entities.Trace; +import jdiextractor.tracemodel.entities.TraceElement; +import jdiextractor.tracemodel.entities.TraceParameter; +import jdiextractor.tracemodel.entities.javaType.TraceJavaClass; +import jdiextractor.tracemodel.entities.javaType.TraceJavaType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class JDIToTraceConverterTest { + + + @Test + public void testCreateAClassFromStringWhenTypeIsNotLoaded() throws ClassNotLoadedException { + MockJDIToTraceConverter converter = new MockJDIToTraceConverter(true,0,null); + + LocalVariable mockParam = new MockLocalVariable(); + + TraceParameter result = converter.newParameterFrom(mockParam); + + assertEquals(1, converter.newJavaTypeFromStringCount); + assertEquals("org.project.NotLoadedClass",result.getType().getName()); + // Always a class no matter what it really is + assertTrue(result.getType() instanceof TraceJavaClass); + + } + + + public static class MockJDIToTraceConverter extends JDIToTraceConverter { + + public int newJavaTypeFromStringCount = 0; + + public MockJDIToTraceConverter(boolean valuesIndependents, int maxObjectDepth, TraceSerializer serializer) { + super(valuesIndependents, maxObjectDepth, serializer); + } + + @Override + protected void addElement(TraceElement element) {} + + @Override + public void serialize() {} + + @Override + public void removeLastElement() {} + + @Override + public Trace getTrace() { + return null; + } + + @Override + protected TraceJavaType newJavaTypeFrom(String typeName) { + newJavaTypeFromStringCount++; + return super.newJavaTypeFrom(typeName); + } + + } + public static class MockLocalVariable implements LocalVariable { + + @Override + public VirtualMachine virtualMachine() { + return null; + } + + @Override + public int compareTo(LocalVariable o) { + return 0; + } + + @Override + public String name() { + return "phantom"; + } + + @Override + public String typeName() { + return null; + } + + @Override + public Type type() throws ClassNotLoadedException { + throw new ClassNotLoadedException("Not loaded"); + } + + @Override + public String signature() { + return "Lorg/project/NotLoadedClass;"; + } + + @Override + public String genericSignature() { + return null; + } + + @Override + public boolean isVisible(StackFrame frame) { + return false; + } + + @Override + public boolean isArgument() { + return false; + } + + } + +} diff --git a/src/test/java/jdiextractor/service/serializer/TraceSerializerJsonTest.java b/src/test/java/jdiextractor/service/serializer/TraceSerializerJsonTest.java new file mode 100644 index 0000000..935e44f --- /dev/null +++ b/src/test/java/jdiextractor/service/serializer/TraceSerializerJsonTest.java @@ -0,0 +1,85 @@ +package jdiextractor.service.serializer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.StringWriter; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import helper.JsonValidator; +import jdiextractor.tracemodel.entities.javaType.TraceJavaClass; +import jdiextractor.tracemodel.entities.javaType.TraceJavaInterface; + +public class TraceSerializerJsonTest { + + private TraceSerializerJson serializer; + private StringWriter writer; + + @Before + public void before() { + writer = new StringWriter(); + serializer = new TraceSerializerJson(false, writer); + } + + @After + public void after() { + // Automatically validate the obtained String + assertTrue(JsonValidator.validate(writer.toString())); + } + + @Test + public void testInterfaceSerialization() { + TraceJavaInterface inter = new TraceJavaInterface("my.Interface"); + serializer.serialize(inter); + + assertEquals("{\"name\":\"my.Interface\",\"isInterface\":true}", writer.toString()); + } + + @Test + public void testClassSerialization() { + TraceJavaClass clazz = new TraceJavaClass("my.Class"); + serializer.serialize(clazz); + + assertEquals("{\"name\":\"my.Class\"}", writer.toString()); + assertTrue(JsonValidator.validate(writer.toString())); + } + + @Test + public void testNoPackageClassSerialization() { + TraceJavaClass clazz = new TraceJavaClass("Class"); + serializer.serialize(clazz); + + assertEquals("{\"name\":\".Class\"}", writer.toString()); + } + + @Test + public void testAnonymousClassSerialization() { + TraceJavaClass clazz = new TraceJavaClass("Class"); + clazz.setAnonymousParent(new TraceJavaInterface("my.Interface")); + serializer.serialize(clazz); + + assertEquals( + "{\"name\":\".Class\",\"anonymousParentType\":{\"name\":\"my.Interface\",\"isInterface\":true}}", + writer.toString()); + } + + @Test + public void testParametricClassSerialization() { + TraceJavaClass clazz = new TraceJavaClass("my.Class"); + clazz.setIsParametric(true); + serializer.serialize(clazz); + assertEquals("{\"name\":\"my.Class\",\"isParametric\":true}", writer.toString()); + } + + @Test + public void testParametricInterfaceSerialization() { + TraceJavaInterface inter = new TraceJavaInterface("my.Interface"); + inter.setIsParametric(true); + serializer.serialize(inter); + assertEquals("{\"name\":\"my.Interface\",\"isParametric\":true,\"isInterface\":true}", writer.toString()); + } + +}