Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import me.lucko.spark.proto.SparkSamplerProtos;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.List;

Expand Down Expand Up @@ -81,11 +82,18 @@ protected Collection<StackTraceNode> exportChildren(Collection<StackTraceNode> c
}

List<StackTraceNode> list = new ArrayList<>(children.size());
BitSet detached = new BitSet();

outer:
for (StackTraceNode child : children) {
for (StackTraceNode other : list) {
for (int i = 0; i < list.size(); i++) {
StackTraceNode other = list.get(i);
if (this.mergeStrategy.shouldMerge(this.methodDisambiguator, other, child)) {
if (!detached.get(i)) {
other = other.copy();
list.set(i, other);
detached.set(i);
}
other.merge(child);
continue outer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ public StackTraceNode(Description description) {
this.description = description;
}

public StackTraceNode copy() {
StackTraceNode copy = new StackTraceNode(this.description);
copy.merge(this);
return copy;
}

public String getClassName() {
return this.description.className();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
import me.lucko.spark.common.sampler.SamplerMode;
import me.lucko.spark.common.sampler.async.AsyncNodeExporter;
import me.lucko.spark.common.sampler.async.AsyncStackTraceElement;
import me.lucko.spark.common.sampler.java.JavaNodeExporter;
import me.lucko.spark.common.sampler.java.MergeStrategy;
import me.lucko.spark.common.sampler.window.ProtoTimeEncoder;
import me.lucko.spark.common.util.MethodDisambiguator;
import me.lucko.spark.proto.SparkSamplerProtos;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -155,6 +158,51 @@ public void testExport() {
assertEquals(expected, proto);
}

@Test
public void testJavaExportDoesNotMutateSourceTree() {
StackTraceNode.Describer<StackTraceElement> describer = (element, parent) -> new StackTraceNode.JavaDescription(
element.getClassName(),
element.getMethodName(),
element.getLineNumber(),
parent == null ? StackTraceNode.NULL_LINE_NUMBER : parent.getLineNumber()
);

StackTraceElement root = new StackTraceElement("java.lang.Thread", "run", "Thread.java", 1);
StackTraceElement caller = new StackTraceElement("test.Caller", "run", "Caller.java", 50);
StackTraceElement target1 = new StackTraceElement("test.Target", "work", "Target.java", 10);
StackTraceElement target2 = new StackTraceElement("test.Target", "work", "Target.java", 20);

ThreadNode threadNode = new ThreadNode("Test Thread");
threadNode.log(describer, new StackTraceElement[]{target1, caller, root}, TimeUnit.SECONDS.toMicros(1), WINDOW);
threadNode.log(describer, new StackTraceElement[]{target2, caller, root}, TimeUnit.SECONDS.toMicros(1), WINDOW);

ProtoTimeEncoder timeEncoder = new ProtoTimeEncoder(SamplerMode.EXECUTION.valueTransformer(), ImmutableList.of(threadNode));
JavaNodeExporter exporter = new JavaNodeExporter(
timeEncoder,
MergeStrategy.SAME_METHOD,
new MethodDisambiguator(className -> null)
);

SparkSamplerProtos.ThreadNode first = exporter.export(threadNode);
SparkSamplerProtos.ThreadNode second = exporter.export(threadNode);

assertEquals(first, second);
assertEquals(3, first.getChildrenCount());

SparkSamplerProtos.StackTraceNode exportedTarget = first.getChildrenList().stream()
.filter(node -> node.getClassName().equals("test.Target"))
.findFirst()
.orElseThrow(AssertionError::new);
assertEquals(2000, exportedTarget.getTimes(0));

StackTraceNode threadRun = threadNode.getChildren().iterator().next();
StackTraceNode callerNode = threadRun.getChildren().iterator().next();
assertEquals(2, callerNode.getChildren().size());
for (StackTraceNode target : callerNode.getChildren()) {
assertEquals(TimeUnit.SECONDS.toMicros(1), target.getTimeAccumulator(WINDOW).longValue());
}
}

@Test
public void testRemoveTimeWindows() {
ThreadNode threadNode = new ThreadNode("Test Thread");
Expand Down