From bfff86f70615fc3c2b0d65bd55e056a7803a6690 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 12:24:09 +0200 Subject: [PATCH 1/7] Add test cases which fail due to cycles in trace graph --- .../elastic/otel/profiler/TraceContext.java | 7 +- .../otel/profiler/CallTreeSpanifyTest.java | 100 +++++++++++++++++- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java index aeb0e0a30..49d190518 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java @@ -45,14 +45,15 @@ public class TraceContext implements Recyclable { public TraceContext() {} // For testing only - static TraceContext fromSpanContextWithZeroClockAnchor(SpanContext ctx) { + static TraceContext fromSpanContextWithZeroClockAnchor(SpanContext ctx, + @Nullable String parentSpanId) { TraceContext result = new TraceContext(); - result.filLFromSpanContext(ctx); + result.fillFromSpanContext(ctx, parentSpanId); result.clockAnchor = 0L; return result; } - private void filLFromSpanContext(SpanContext ctx) { + private void fillFromSpanContext(SpanContext ctx, @Nullable String parentSpanId) { id = HexUtils.hexToLong(ctx.getSpanId(), 0); traceIdHigh = HexUtils.hexToLong(ctx.getTraceId(), 0); traceIdLow = HexUtils.hexToLong(ctx.getTraceId(), 16); diff --git a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java index 4259c4788..c00e11d44 100644 --- a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java +++ b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java @@ -107,7 +107,7 @@ void testCallTreeWithActiveSpan() { TraceContext rootContext = TraceContext.fromSpanContextWithZeroClockAnchor( SpanContext.create( - traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault())); + traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), null); ObjectPool rootPool = ObjectPool.createRecyclable(2, CallTree.Root::new); ObjectPool childPool = ObjectPool.createRecyclable(2, CallTree::new); @@ -119,7 +119,8 @@ void testCallTreeWithActiveSpan() { TraceContext spanContext = TraceContext.fromSpanContextWithZeroClockAnchor( SpanContext.create( - traceId, childSpanId, TraceFlags.getSampled(), TraceState.getDefault())); + traceId, childSpanId, TraceFlags.getSampled(), TraceState.getDefault()), + rootSpanId); root.onActivation(spanContext.serialize(), TimeUnit.MILLISECONDS.toNanos(5)); root.addStackTrace( @@ -142,8 +143,6 @@ void testCallTreeWithActiveSpan() { 0); root.end(childPool, 0); - System.out.println(root); - assertThat(root.getCount()).isEqualTo(4); assertThat(root.getDurationUs()).isEqualTo(30_000); assertThat(root.getChildren()).hasSize(1); @@ -179,4 +178,97 @@ void testCallTreeWithActiveSpan() { assertThat(spans.get(1)).hasTraceId(traceId).hasParentSpanId(childSpanId); } } + + @Test + void testSpanWithInvertedActivation() { + FixedClock nanoClock = new FixedClock(); + + String traceId = "0af7651916cd43dd8448eb211c80319c"; + String rootSpanId = "77ad6b7169203331"; + TraceContext rootContext = + TraceContext.fromSpanContextWithZeroClockAnchor( + SpanContext.create( + traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), null); + + String childSpanId = "11b2c3d4e5f64242"; + TraceContext childSpanContext = + TraceContext.fromSpanContextWithZeroClockAnchor( + SpanContext.create( + traceId, childSpanId, TraceFlags.getSampled(), TraceState.getDefault()), + rootSpanId); + + ObjectPool rootPool = ObjectPool.createRecyclable(2, CallTree.Root::new); + ObjectPool childPool = ObjectPool.createRecyclable(2, CallTree::new); + + CallTree.Root root = CallTree.createRoot(rootPool, childSpanContext.serialize(), 0); + root.addStackTrace(Collections.singletonList(StackFrame.of("A", "a")), 10_000, childPool, 0); + + root.onActivation(rootContext.serialize(), 20_000); + root.onDeactivation(rootContext.serialize(), childSpanContext.serialize(), 30_000); + + root.addStackTrace(Collections.singletonList(StackFrame.of("A", "a")), 40_000, childPool, 0); + root.end(childPool, 0); + + InMemorySpanExporter exporter = InMemorySpanExporter.create(); + OpenTelemetrySdkBuilder sdkBuilder = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .build()); + try (OpenTelemetrySdk outputSdk = sdkBuilder.build()) { + root.spanify(nanoClock, outputSdk.getTracer("dummy-tracer")); + + List spans = exporter.getFinishedSpanItems(); + assertThat(spans).hasSize(1); + assertThat(spans.get(0)).hasTraceId(traceId).hasParentSpanId(childSpanId); + // the inferred span should not have any span links because this + // span link would cause a cycle in the trace + assertThat(spans.get(0).getLinks()).isEmpty(); + } + } + + @Test + void testSpanWithNestedActivation() { + FixedClock nanoClock = new FixedClock(); + + String traceId = "0af7651916cd43dd8448eb211c80319c"; + String rootSpanId = "77ad6b7169203331"; + TraceContext rootContext = + TraceContext.fromSpanContextWithZeroClockAnchor( + SpanContext.create( + traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), null); + + ObjectPool rootPool = ObjectPool.createRecyclable(2, CallTree.Root::new); + ObjectPool childPool = ObjectPool.createRecyclable(2, CallTree::new); + + CallTree.Root root = CallTree.createRoot(rootPool, rootContext.serialize(), 0); + root.addStackTrace(Collections.singletonList(StackFrame.of("A", "a")), 10_000, childPool, 0); + + root.onActivation(rootContext.serialize(), 20_000); + root.onDeactivation(rootContext.serialize(), rootContext.serialize(), 30_000); + + root.addStackTrace(Collections.singletonList(StackFrame.of("A", "a")), 40_000, childPool, 0); + root.end(childPool, 0); + + InMemorySpanExporter exporter = InMemorySpanExporter.create(); + OpenTelemetrySdkBuilder sdkBuilder = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .build()); + try (OpenTelemetrySdk outputSdk = sdkBuilder.build()) { + root.spanify(nanoClock, outputSdk.getTracer("dummy-tracer")); + + List spans = exporter.getFinishedSpanItems(); + assertThat(spans).hasSize(1); + assertThat(spans.get(0)).hasTraceId(traceId).hasParentSpanId(rootSpanId); + // the inferred span should not have any span links because this + // span link would cause a cycle in the trace + assertThat(spans.get(0).getLinks()).isEmpty(); + } + } + + } From 082aeca5eeb97588b3507d327bb127b245660a87 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 13:15:44 +0200 Subject: [PATCH 2/7] Only add inferred spans in between existing parent-child relationships when using child_ids. --- .../co/elastic/otel/profiler/CallTree.java | 25 +++++++-- .../otel/profiler/SamplingProfiler.java | 5 +- .../elastic/otel/profiler/TraceContext.java | 52 +++++++++++++++++-- 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java index 7d2d89a1a..fae21eb66 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java @@ -623,11 +623,22 @@ public void resetState() { * * @param id the child span id to add to this call tree element */ - public void addMaybeChildId(long id) { - if (maybeChildIds == null) { - maybeChildIds = new LongList(); + public void addMaybeChildId(byte[] serializedTraceContext) { + TraceContext ctx = findNonInferredParentContext(); + if (TraceContext.parentIdIs(serializedTraceContext, ctx.getSpanId())) { + if (maybeChildIds == null) { + maybeChildIds = new LongList(); + } + maybeChildIds.add(TraceContext.getSpanId(serializedTraceContext)); + } + } + + protected TraceContext findNonInferredParentContext() { + if (activeContextOfDirectParent != null) { //setActiveSpan has been called + return activeContextOfDirectParent; + } else { + return parent.findNonInferredParentContext(); } - maybeChildIds.add(id); } public void addChildId(long id) { @@ -743,7 +754,7 @@ public void onActivation(byte[] active, long timestamp) { long spanId = TraceContext.getSpanId(active); activeSet.add(spanId); if (!isNestedActivation(topOfStack)) { - topOfStack.addMaybeChildId(spanId); + topOfStack.addMaybeChildId(active); } } } @@ -872,6 +883,10 @@ public int spanify(SpanAnchoredClock clock, Tracer tracer) { return createdSpans; } + protected TraceContext findNonInferredParentContext() { + return getRootContext(); + } + public TraceContext getRootContext() { return rootContext; } diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java index cdd75cb40..c02d9622f 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java @@ -862,13 +862,12 @@ private void set( @Nullable Span previousContext, long nanoTime, SpanAnchoredClock clock) { - TraceContext.serialize( - traceContext.getSpanContext(), clock.getAnchor(traceContext), traceContextBuffer); + TraceContext.serialize(traceContext, clock.getAnchor(traceContext), traceContextBuffer); this.threadId = threadId; this.activation = activation; if (previousContext != null) { TraceContext.serialize( - previousContext.getSpanContext(), + previousContext, clock.getAnchor(previousContext), previousContextBuffer); rootContext = false; diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java index 49d190518..368d43f21 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java @@ -25,6 +25,7 @@ import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.TraceFlags; import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.sdk.trace.ReadableSpan; import javax.annotation.Nullable; /** @@ -34,10 +35,13 @@ */ public class TraceContext implements Recyclable { - public static final int SERIALIZED_LENGTH = 16 + 8 + 1 + 8; + public static final int SERIALIZED_LENGTH = 16 + 8 + 1 + 1 + 8 + 8; private long traceIdLow; private long traceIdHigh; private long id; + + private boolean hasParentId; + private long parentId; private byte flags; private long clockAnchor; @@ -53,10 +57,17 @@ static TraceContext fromSpanContextWithZeroClockAnchor(SpanContext ctx, return result; } + private void fillFromSpanContext(SpanContext ctx, @Nullable String parentSpanId) { id = HexUtils.hexToLong(ctx.getSpanId(), 0); traceIdHigh = HexUtils.hexToLong(ctx.getTraceId(), 0); traceIdLow = HexUtils.hexToLong(ctx.getTraceId(), 16); + if (parentSpanId != null) { + hasParentId = true; + parentId = HexUtils.hexToLong(parentSpanId, 0); + } else { + hasParentId = false; + } flags = ctx.getTraceFlags().asByte(); } @@ -74,6 +85,10 @@ public SpanContext toOtelSpanContext(StringBuilder temporaryBuilder) { traceIdStr, idStr, TraceFlags.fromByte(flags), TraceState.getDefault()); } + public long getSpanId() { + return id; + } + public boolean idEquals(@Nullable TraceContext o) { if (o == null) { return false; @@ -90,7 +105,14 @@ public void deserialize(byte[] serialized) { traceIdHigh = ByteUtils.getLong(serialized, 8); id = ByteUtils.getLong(serialized, 16); flags = serialized[24]; - clockAnchor = ByteUtils.getLong(serialized, 25); + hasParentId = serialized[25] != 0; + parentId = ByteUtils.getLong(serialized, 26); + clockAnchor = ByteUtils.getLong(serialized, 34); + } + + public static boolean parentIdIs(byte[] serializedTraceContext, long expectedParent) { + boolean hasParent = serializedTraceContext[25] != 0; + return hasParent && ByteUtils.getLong(serializedTraceContext, 26) == expectedParent; } public boolean traceIdAndIdEquals(byte[] otherSerialized) { @@ -106,7 +128,13 @@ public boolean traceIdAndIdEquals(byte[] otherSerialized) { return id == otherId; } - public static void serialize(SpanContext ctx, long clockAnchor, byte[] buffer) { + public static void serialize(Span span, long clockAnchor, byte[] buffer) { + SpanContext ctx = span.getSpanContext(); + SpanContext parentSpanCtx = SpanContext.getInvalid(); + if (span instanceof ReadableSpan) { + parentSpanCtx = ((ReadableSpan) span).getParentSpanContext(); + } + long id = HexUtils.hexToLong(ctx.getSpanId(), 0); long traceIdHigh = HexUtils.hexToLong(ctx.getTraceId(), 0); long traceIdLow = HexUtils.hexToLong(ctx.getTraceId(), 16); @@ -115,7 +143,14 @@ public static void serialize(SpanContext ctx, long clockAnchor, byte[] buffer) { ByteUtils.putLong(buffer, 8, traceIdHigh); ByteUtils.putLong(buffer, 16, id); buffer[24] = flags; - ByteUtils.putLong(buffer, 25, clockAnchor); + if (parentSpanCtx.isValid()) { + buffer[25] = 1; + ByteUtils.putLong(buffer, 26, HexUtils.hexToLong(parentSpanCtx.getSpanId(), 0)); + } else { + buffer[25] = 0; + ByteUtils.putLong(buffer, 26, 0); + } + ByteUtils.putLong(buffer, 34, clockAnchor); } public void serialize(byte[] buffer) { @@ -123,7 +158,14 @@ public void serialize(byte[] buffer) { ByteUtils.putLong(buffer, 8, traceIdHigh); ByteUtils.putLong(buffer, 16, id); buffer[24] = flags; - ByteUtils.putLong(buffer, 25, clockAnchor); + if (hasParentId) { + buffer[25] = 1; + ByteUtils.putLong(buffer, 26, parentId); + } else { + buffer[25] = 0; + ByteUtils.putLong(buffer, 26, 0); + } + ByteUtils.putLong(buffer, 34, clockAnchor); } public byte[] serialize() { From e2767ebd891cea5cbbebc1df0e88c204958a7258 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 13:19:51 +0200 Subject: [PATCH 3/7] spotless --- .../main/java/co/elastic/otel/profiler/CallTree.java | 2 +- .../co/elastic/otel/profiler/SamplingProfiler.java | 4 +--- .../java/co/elastic/otel/profiler/TraceContext.java | 5 ++--- .../co/elastic/otel/profiler/CallTreeSpanifyTest.java | 11 ++++++----- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java index fae21eb66..26c087a3d 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java @@ -634,7 +634,7 @@ public void addMaybeChildId(byte[] serializedTraceContext) { } protected TraceContext findNonInferredParentContext() { - if (activeContextOfDirectParent != null) { //setActiveSpan has been called + if (activeContextOfDirectParent != null) { // setActiveSpan has been called return activeContextOfDirectParent; } else { return parent.findNonInferredParentContext(); diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java index c02d9622f..89dea3716 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/SamplingProfiler.java @@ -867,9 +867,7 @@ private void set( this.activation = activation; if (previousContext != null) { TraceContext.serialize( - previousContext, - clock.getAnchor(previousContext), - previousContextBuffer); + previousContext, clock.getAnchor(previousContext), previousContextBuffer); rootContext = false; } else { rootContext = true; diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java index 368d43f21..77c0a0cfb 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java @@ -49,15 +49,14 @@ public class TraceContext implements Recyclable { public TraceContext() {} // For testing only - static TraceContext fromSpanContextWithZeroClockAnchor(SpanContext ctx, - @Nullable String parentSpanId) { + static TraceContext fromSpanContextWithZeroClockAnchor( + SpanContext ctx, @Nullable String parentSpanId) { TraceContext result = new TraceContext(); result.fillFromSpanContext(ctx, parentSpanId); result.clockAnchor = 0L; return result; } - private void fillFromSpanContext(SpanContext ctx, @Nullable String parentSpanId) { id = HexUtils.hexToLong(ctx.getSpanId(), 0); traceIdHigh = HexUtils.hexToLong(ctx.getTraceId(), 0); diff --git a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java index c00e11d44..25fa3e7e1 100644 --- a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java +++ b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeSpanifyTest.java @@ -107,7 +107,8 @@ void testCallTreeWithActiveSpan() { TraceContext rootContext = TraceContext.fromSpanContextWithZeroClockAnchor( SpanContext.create( - traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), null); + traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), + null); ObjectPool rootPool = ObjectPool.createRecyclable(2, CallTree.Root::new); ObjectPool childPool = ObjectPool.createRecyclable(2, CallTree::new); @@ -188,7 +189,8 @@ void testSpanWithInvertedActivation() { TraceContext rootContext = TraceContext.fromSpanContextWithZeroClockAnchor( SpanContext.create( - traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), null); + traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), + null); String childSpanId = "11b2c3d4e5f64242"; TraceContext childSpanContext = @@ -237,7 +239,8 @@ void testSpanWithNestedActivation() { TraceContext rootContext = TraceContext.fromSpanContextWithZeroClockAnchor( SpanContext.create( - traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), null); + traceId, rootSpanId, TraceFlags.getSampled(), TraceState.getDefault()), + null); ObjectPool rootPool = ObjectPool.createRecyclable(2, CallTree.Root::new); ObjectPool childPool = ObjectPool.createRecyclable(2, CallTree::new); @@ -269,6 +272,4 @@ void testSpanWithNestedActivation() { assertThat(spans.get(0).getLinks()).isEmpty(); } } - - } From 4633abb6b6fe3eecd3691cc7659891a30209e234 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 13:26:20 +0200 Subject: [PATCH 4/7] javadoc --- .../src/main/java/co/elastic/otel/profiler/CallTree.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java index 26c087a3d..1340758bb 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java @@ -621,7 +621,8 @@ public void resetState() { * {@link CallTree.Root#addStackTrace}. After seeing another frame of {@code a}, we know that * {@code 1} is really the child of {@code a}, so we {@link #transferMaybeChildIdsToChildIds()}. * - * @param id the child span id to add to this call tree element + * @param serializedTraceContext the trace context of the child span to add to this call tree + * element */ public void addMaybeChildId(byte[] serializedTraceContext) { TraceContext ctx = findNonInferredParentContext(); From 97feda98d080a62abd6ac0f2ca1971c2c22bbc30 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 14:40:00 +0200 Subject: [PATCH 5/7] Revert approach --- .../co/elastic/otel/profiler/CallTree.java | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java index 1340758bb..7d2d89a1a 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java @@ -621,25 +621,13 @@ public void resetState() { * {@link CallTree.Root#addStackTrace}. After seeing another frame of {@code a}, we know that * {@code 1} is really the child of {@code a}, so we {@link #transferMaybeChildIdsToChildIds()}. * - * @param serializedTraceContext the trace context of the child span to add to this call tree - * element + * @param id the child span id to add to this call tree element */ - public void addMaybeChildId(byte[] serializedTraceContext) { - TraceContext ctx = findNonInferredParentContext(); - if (TraceContext.parentIdIs(serializedTraceContext, ctx.getSpanId())) { - if (maybeChildIds == null) { - maybeChildIds = new LongList(); - } - maybeChildIds.add(TraceContext.getSpanId(serializedTraceContext)); - } - } - - protected TraceContext findNonInferredParentContext() { - if (activeContextOfDirectParent != null) { // setActiveSpan has been called - return activeContextOfDirectParent; - } else { - return parent.findNonInferredParentContext(); + public void addMaybeChildId(long id) { + if (maybeChildIds == null) { + maybeChildIds = new LongList(); } + maybeChildIds.add(id); } public void addChildId(long id) { @@ -755,7 +743,7 @@ public void onActivation(byte[] active, long timestamp) { long spanId = TraceContext.getSpanId(active); activeSet.add(spanId); if (!isNestedActivation(topOfStack)) { - topOfStack.addMaybeChildId(active); + topOfStack.addMaybeChildId(spanId); } } } @@ -884,10 +872,6 @@ public int spanify(SpanAnchoredClock clock, Tracer tracer) { return createdSpans; } - protected TraceContext findNonInferredParentContext() { - return getRootContext(); - } - public TraceContext getRootContext() { return rootContext; } From daa00356d1ae39731e2d4c71217f69c8f06ab3ff Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 15:00:18 +0200 Subject: [PATCH 6/7] Changed approach: filter on spanification --- .../co/elastic/otel/profiler/CallTree.java | 59 +-- .../co/elastic/otel/profiler/ChildList.java | 63 +++ .../elastic/otel/profiler/TraceContext.java | 7 +- .../elastic/otel/profiler/CallTreeTest.java | 394 +++++++++--------- 4 files changed, 300 insertions(+), 223 deletions(-) create mode 100644 inferred-spans/src/main/java/co/elastic/otel/profiler/ChildList.java diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java index 7d2d89a1a..6c3ef62f8 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/CallTree.java @@ -24,7 +24,6 @@ import co.elastic.otel.common.ElasticAttributes; import co.elastic.otel.common.util.HexUtils; import co.elastic.otel.profiler.collections.LongHashSet; -import co.elastic.otel.profiler.collections.LongList; import co.elastic.otel.profiler.pooling.ObjectPool; import co.elastic.otel.profiler.pooling.Recyclable; import io.opentelemetry.api.common.Attributes; @@ -84,9 +83,9 @@ public class CallTree implements Recyclable { private boolean isSpan; private int depth; - @Nullable private LongList childIds; + @Nullable private ChildList childIds; - @Nullable private LongList maybeChildIds; + @Nullable private ChildList maybeChildIds; public CallTree() {} @@ -504,7 +503,8 @@ protected Span asSpan( .setStartTimestamp( clock.toEpochNanos(parentContext.getClockAnchor(), this.start), TimeUnit.NANOSECONDS); - insertChildIdLinks(spanBuilder, Span.fromContext(parentOtelCtx).getSpanContext(), tempBuilder); + insertChildIdLinks( + spanBuilder, Span.fromContext(parentOtelCtx).getSpanContext(), parentContext, tempBuilder); // we're not interested in the very bottom of the stack which contains things like accepting and // handling connections @@ -524,20 +524,27 @@ protected Span asSpan( } private void insertChildIdLinks( - SpanBuilder span, SpanContext parentContext, StringBuilder tempBuilder) { + SpanBuilder span, + SpanContext parentContext, + TraceContext nonInferredParent, + StringBuilder tempBuilder) { if (childIds == null || childIds.isEmpty()) { return; } for (int i = 0; i < childIds.getSize(); i++) { - tempBuilder.setLength(0); - HexUtils.appendLongAsHex(childIds.get(i), tempBuilder); - SpanContext spanContext = - SpanContext.create( - parentContext.getTraceId(), - tempBuilder.toString(), - parentContext.getTraceFlags(), - parentContext.getTraceState()); - span.addLink(spanContext, CHILD_LINK_ATTRIBUTES); + // to avoid cycles, we only insert child-ids if the parent of the child is also + // the parent of the stack of inferred spans inserted + if (nonInferredParent.getSpanId() == childIds.getParentId(i)) { + tempBuilder.setLength(0); + HexUtils.appendLongAsHex(childIds.getId(i), tempBuilder); + SpanContext spanContext = + SpanContext.create( + parentContext.getTraceId(), + tempBuilder.toString(), + parentContext.getTraceFlags(), + parentContext.getTraceState()); + span.addLink(spanContext, CHILD_LINK_ATTRIBUTES); + } } } @@ -623,18 +630,18 @@ public void resetState() { * * @param id the child span id to add to this call tree element */ - public void addMaybeChildId(long id) { + public void addMaybeChildId(long id, long parentId) { if (maybeChildIds == null) { - maybeChildIds = new LongList(); + maybeChildIds = new ChildList(); } - maybeChildIds.add(id); + maybeChildIds.add(id, parentId); } - public void addChildId(long id) { + public void addChildId(long id, long parentId) { if (childIds == null) { - childIds = new LongList(); + childIds = new ChildList(); } - childIds.add(id); + childIds.add(id, parentId); } public boolean hasChildIds() { @@ -664,7 +671,11 @@ void giveChildIdsTo(CallTree giveTo) { void giveLastChildIdTo(CallTree giveTo) { if (childIds != null && !childIds.isEmpty()) { - giveTo.addChildId(childIds.remove(childIds.getSize() - 1)); + int size = childIds.getSize(); + long id = childIds.getId(size - 1); + long parentId = childIds.getParentId(size - 1); + giveTo.addChildId(id, parentId); + childIds.removeLast(); } } @@ -743,7 +754,7 @@ public void onActivation(byte[] active, long timestamp) { long spanId = TraceContext.getSpanId(active); activeSet.add(spanId); if (!isNestedActivation(topOfStack)) { - topOfStack.addMaybeChildId(spanId); + topOfStack.addMaybeChildId(spanId, TraceContext.getParentId(active)); } } } @@ -752,12 +763,12 @@ private boolean isNestedActivation(CallTree topOfStack) { return isAnyActive(topOfStack.childIds) || isAnyActive(topOfStack.maybeChildIds); } - private boolean isAnyActive(@Nullable LongList spanIds) { + private boolean isAnyActive(@Nullable ChildList spanIds) { if (spanIds == null) { return false; } for (int i = 0, size = spanIds.getSize(); i < size; i++) { - if (activeSet.contains(spanIds.get(i))) { + if (activeSet.contains(spanIds.getId(i))) { return true; } } diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/ChildList.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/ChildList.java new file mode 100644 index 000000000..7b7a62db9 --- /dev/null +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/ChildList.java @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.otel.profiler; + +import co.elastic.otel.profiler.collections.LongList; + +/** List for maintaining pairs of (spanId,parentIds) both represented as longs. */ +public class ChildList { + + // this list contains the (spanId,parentIds) flattened + private LongList idsWithParentIds = new LongList(); + + public void add(long id, long parentId) { + idsWithParentIds.add(id); + idsWithParentIds.add(parentId); + } + + public long getId(int index) { + return idsWithParentIds.get(index * 2); + } + + public long getParentId(int index) { + return idsWithParentIds.get(index * 2 + 1); + } + + public int getSize() { + return idsWithParentIds.getSize() / 2; + } + + public void addAll(ChildList other) { + idsWithParentIds.addAll(other.idsWithParentIds); + } + + public void clear() { + idsWithParentIds.clear(); + } + + public boolean isEmpty() { + return getSize() == 0; + } + + public void removeLast() { + int size = idsWithParentIds.getSize(); + idsWithParentIds.remove(size - 1); + idsWithParentIds.remove(size - 2); + } +} diff --git a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java index 77c0a0cfb..0c0b0ad31 100644 --- a/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java +++ b/inferred-spans/src/main/java/co/elastic/otel/profiler/TraceContext.java @@ -109,9 +109,12 @@ public void deserialize(byte[] serialized) { clockAnchor = ByteUtils.getLong(serialized, 34); } - public static boolean parentIdIs(byte[] serializedTraceContext, long expectedParent) { + public static long getParentId(byte[] serializedTraceContext) { boolean hasParent = serializedTraceContext[25] != 0; - return hasParent && ByteUtils.getLong(serializedTraceContext, 26) == expectedParent; + if (!hasParent) { + return 0L; + } + return ByteUtils.getLong(serializedTraceContext, 26); } public boolean traceIdAndIdEquals(byte[] otherSerialized) { diff --git a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java index 304828569..efd3e4d74 100644 --- a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java +++ b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java @@ -123,7 +123,7 @@ void testCallTree() { @Test void testGiveEmptyChildIdsTo() { CallTree rich = new CallTree(); - rich.addChildId(42); + rich.addChildId(42, 0L); CallTree robinHood = new CallTree(); CallTree poor = new CallTree(); @@ -142,9 +142,9 @@ void testTwoDistinctInvocationsOfMethodBShouldNotBeFoldedIntoOne() throws Except assertCallTree( new String[] {" bb bb", "aaaaaa"}, new Object[][] { - {"a", 6}, - {" b", 2}, - {" b", 2} + {"a", 6}, + {" b", 2}, + {" b", 2} }); } @@ -153,14 +153,14 @@ void testBasicCallTree() throws Exception { assertCallTree( new String[] {" cc ", " bbb", "aaaa"}, new Object[][] { - {"a", 4}, - {" b", 3}, - {" c", 2} + {"a", 4}, + {" b", 3}, + {" c", 2} }, new Object[][] { - {"a", 3}, - {" b", 2}, - {" c", 1} + {"a", 3}, + {" b", 2}, + {" c", 1} }); } @@ -169,14 +169,14 @@ void testShouldNotCreateInferredSpansForPillarsAndLeafShouldHaveStacktrace() thr assertCallTree( new String[] {" dd ", " cc ", " bb ", "aaaa"}, new Object[][] { - {"a", 4}, - {" b", 2}, - {" c", 2}, - {" d", 2} + {"a", 4}, + {" b", 2}, + {" c", 2}, + {" d", 2} }, new Object[][] { - {"a", 3}, - {" d", 1, Arrays.asList("c", "b")} + {"a", 3}, + {" d", 1, Arrays.asList("c", "b")} }); } @@ -191,10 +191,10 @@ void testSameTopOfStackDifferentBottom() throws Exception { assertCallTree( new String[] {"cccc", "aabb"}, new Object[][] { - {"a", 2}, - {" c", 2}, - {"b", 2}, - {" c", 2}, + {"a", 2}, + {" c", 2}, + {"b", 2}, + {" c", 2}, }); } @@ -203,12 +203,12 @@ void testStackTraceWithRecursion() throws Exception { assertCallTree( new String[] {"bbccbbcc", "bbbbbbbb", "aaaaaaaa"}, new Object[][] { - {"a", 8}, - {" b", 8}, - {" b", 2}, - {" c", 2}, - {" b", 2}, - {" c", 2}, + {"a", 8}, + {" b", 8}, + {" b", 2}, + {" c", 2}, + {" b", 2}, + {" c", 2}, }); } @@ -217,11 +217,11 @@ void testFirstInferredSpanShouldHaveNoStackTrace() throws Exception { assertCallTree( new String[] {"bb", "aa"}, new Object[][] { - {"a", 2}, - {" b", 2}, + {"a", 2}, + {" b", 2}, }, new Object[][] { - {"b", 1}, + {"b", 1}, }); } @@ -230,19 +230,19 @@ void testCallTreeWithSpanActivations() throws Exception { assertCallTree( new String[] {" cc ee ", " bbb dd ", " a aaaaaa a ", "1 2 2 1"}, new Object[][] { - {"a", 8}, - {" b", 3}, - {" c", 2}, - {" d", 2}, - {" e", 2}, + {"a", 8}, + {" b", 3}, + {" c", 2}, + {" d", 2}, + {" e", 2}, }, new Object[][] { - {"1", 11}, - {" a", 9}, - {" 2", 7}, - {" b", 2}, - {" c", 1}, - {" e", 1, Arrays.asList("d")}, + {"1", 11}, + {" a", 9}, + {" 2", 7}, + {" b", 2}, + {" c", 1}, + {" e", 1, Arrays.asList("d")}, }); } @@ -258,25 +258,25 @@ void testCallTreeWithSpanActivations() throws Exception { void testDeactivationBeforeEnd() throws Exception { assertCallTree( new String[] { - " dd ", - " cccc c ", - " bbbb bb ", // <- deactivation for span 2 happens before b and c ends - " a aaaa aa ", // that means b and c must have started before 2 has been activated - "1 2 2 1" // but we saw the first stack trace of b only after the activation of 2 + " dd ", + " cccc c ", + " bbbb bb ", // <- deactivation for span 2 happens before b and c ends + " a aaaa aa ", // that means b and c must have started before 2 has been activated + "1 2 2 1" // but we saw the first stack trace of b only after the activation of 2 }, new Object[][] { - {"a", 7}, - {" b", 6}, - {" c", 5}, - {" d", 2}, + {"a", 7}, + {" b", 6}, + {" c", 5}, + {" d", 2}, }, new Object[][] { - {"1", 10}, - {" a", 8}, - {" b", 7}, - {" c", 6}, - {" 2", 5}, - {" d", 1}, + {"1", 10}, + {" a", 8}, + {" b", 7}, + {" c", 6}, + {" 2", 5}, + {" d", 1}, }); } @@ -291,15 +291,15 @@ void testDectivationBeforeEnd2() throws Exception { assertCallTree( new String[] {" bbbb b ", " a aaaa a a a ", "1 2 2 3 3 1"}, new Object[][] { - {"a", 8}, - {" b", 5}, + {"a", 8}, + {" b", 5}, }, new Object[][] { - {"1", 13}, - {" a", 11}, - {" b", 6}, - {" 2", 5}, - {" 3", 2}, + {"1", 13}, + {" a", 11}, + {" b", 6}, + {" 2", 5}, + {" 3", 2}, }); } @@ -316,15 +316,15 @@ void testDectivationBeforeEnd_DontStealChildIdsOfUnrelatedActivations() throws E assertCallTree( new String[] {" c c ", " b b ", "a a a aa", " 1 1 2 2 "}, new Object[][] { - {"a", 5}, - {" b", 2}, - {" c", 2}, + {"a", 5}, + {" b", 2}, + {" c", 2}, }, new Object[][] { - {"a", 9}, - {" 1", 2}, - {" c", 3, Arrays.asList("b")}, - {" 2", 2}, + {"a", 9}, + {" 1", 2}, + {" c", 3, Arrays.asList("b")}, + {" 2", 2}, }); assertThat(spans.get("a").getLinks()) .hasSize(1) @@ -349,16 +349,16 @@ void testDectivationBeforeEnd_DontStealChildIdsOfUnrelatedActivations_Nested() t assertCallTree( new String[] {" c c ", " b b ", "a a a aa", " 1 1 23 32 "}, new Object[][] { - {"a", 5}, - {" b", 2}, - {" c", 2}, + {"a", 5}, + {" b", 2}, + {" c", 2}, }, new Object[][] { - {"a", 11}, - {" 1", 2}, - {" c", 4, Arrays.asList("b")}, - {" 2", 4}, - {" 3", 2}, + {"a", 11}, + {" 1", 2}, + {" c", 4, Arrays.asList("b")}, + {" 2", 4}, + {" 3", 2}, }); assertThat(spans.get("a").getLinks()) .hasSize(1) @@ -379,13 +379,13 @@ void testActivationAfterMethodEnds() throws Exception { assertCallTree( new String[] {"bb ", "aa a ", " 1 1"}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 3}, - {" b", 1}, - {" 1", 2} + {"a", 3}, + {" b", 1}, + {" 1", 2} }); } @@ -398,13 +398,13 @@ void testActivationBetweenMethods() throws Exception { assertCallTree( new String[] {"bb ", "aa a", " 11 "}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 4}, - {" b", 1}, - {" 1", 1}, + {"a", 4}, + {" b", 1}, + {" 1", 1}, }); } @@ -418,13 +418,13 @@ void testActivationBetweenMethods_AfterFastMethod() throws Exception { assertCallTree( new String[] {" c ", "bb ", "aa a", " 11 "}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 4}, - {" b", 1}, - {" 1", 1}, + {"a", 4}, + {" b", 1}, + {" 1", 1}, }); } @@ -438,14 +438,14 @@ void testActivationBetweenFastMethods() throws Exception { assertCallTree( new String[] {"c d ", "b b ", "a a a", " 11 22 "}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 6}, - {" b", 3}, - {" 1", 1}, - {" 2", 1}, + {"a", 6}, + {" b", 3}, + {" 1", 1}, + {" 2", 1}, }); } @@ -486,12 +486,12 @@ void testNestedActivation() throws Exception { assertCallTree( new String[] {"a a a", " 12 21 "}, new Object[][] { - {"a", 3}, + {"a", 3}, }, new Object[][] { - {"a", 6}, - {" 1", 4}, - {" 2", 2}, + {"a", 6}, + {" 1", 4}, + {" 2", 2}, }); } @@ -507,16 +507,16 @@ void testNestedActivationAfterMethodEnds_RootChangesToC() throws Exception { assertCallTree( new String[] {" bbb ", " aaa ccc ", "1 23 321"}, new Object[][] { - {"a", 3}, - {" b", 3}, - {"c", 3}, + {"a", 3}, + {" b", 3}, + {"c", 3}, }, new Object[][] { - {"1", 11}, - {" b", 2, Arrays.asList("a")}, - {" 2", 6}, - {" 3", 4}, - {" c", 2} + {"1", 11}, + {" b", 2, Arrays.asList("a")}, + {" 2", 6}, + {" 3", 4}, + {" c", 2} }); assertThat(spans.get("b").getLinks()).isEmpty(); @@ -533,17 +533,17 @@ void testRegularActivationFollowedByNestedActivationAfterMethodEnds() throws Exc assertCallTree( new String[] {" d ", " b b b ", " a a a ccc ", "1 2 2 34 431"}, new Object[][] { - {"a", 3}, - {" b", 3}, - {"c", 3}, + {"a", 3}, + {" b", 3}, + {"c", 3}, }, new Object[][] { - {"1", 13}, - {" b", 4, Arrays.asList("a")}, - {" 2", 2}, - {" 3", 6}, - {" 4", 4}, - {" c", 2} + {"1", 13}, + {" b", 4, Arrays.asList("a")}, + {" 2", 2}, + {" 3", 6}, + {" 4", 4}, + {" c", 2} }); } @@ -560,18 +560,18 @@ void testNestedActivationAfterMethodEnds_CommonAncestorA() throws Exception { assertCallTree( new String[] {" b b b ccc ", " aa a a aaa a ", "1 2 2 34 43 1"}, new Object[][] { - {"a", 8}, - {" b", 3}, - {" c", 3}, + {"a", 8}, + {" b", 3}, + {" c", 3}, }, new Object[][] { - {"1", 15}, - {" a", 13}, - {" b", 4}, - {" 2", 2}, - {" 3", 6}, - {" 4", 4}, - {" c", 2} + {"1", 15}, + {" a", 13}, + {" b", 4}, + {" 2", 2}, + {" 3", 6}, + {" 4", 4}, + {" c", 2} }); assertThat(spans.get("b").getLinks()) @@ -609,15 +609,15 @@ void testActivationAfterMethodEnds_RootChangesToB() throws Exception { assertCallTree( new String[] {" ccc ", " aaa bbb ", "1 2 21"}, new Object[][] { - {"a", 3}, - {"b", 3}, - {" c", 3}, + {"a", 3}, + {"b", 3}, + {" c", 3}, }, new Object[][] { - {"1", 9}, - {" a", 2}, - {" 2", 4}, - {" c", 2, Arrays.asList("b")} + {"1", 9}, + {" a", 2}, + {" 2", 4}, + {" c", 2, Arrays.asList("b")} }); } @@ -632,14 +632,14 @@ void testActivationAfterMethodEnds_RootChangesToB2() throws Exception { assertCallTree( new String[] {" aaa bbb ", "1 2 21"}, new Object[][] { - {"a", 3}, - {"b", 3}, + {"a", 3}, + {"b", 3}, }, new Object[][] { - {"1", 9}, - {" a", 2}, - {" 2", 4}, - {" b", 2} + {"1", 9}, + {" a", 2}, + {" 2", 4}, + {" b", 2} }); } @@ -671,14 +671,14 @@ void testActivationAfterMethodEnds_SameRootDeeperStack() throws Exception { assertCallTree( new String[] {" ccc ", " aaa aaa ", "1 2 21"}, new Object[][] { - {"a", 6}, - {" c", 3}, + {"a", 6}, + {" c", 3}, }, new Object[][] { - {"1", 9}, - {" a", 6}, - {" 2", 4}, - {" c", 2} + {"1", 9}, + {" a", 6}, + {" 2", 4}, + {" c", 2} }); } @@ -693,14 +693,14 @@ void testActivationBeforeMethodStarts() throws Exception { assertCallTree( new String[] {" bbb ", " a aaa a ", "1 2 2 1"}, new Object[][] { - {"a", 5}, - {" b", 3}, + {"a", 5}, + {" b", 3}, }, new Object[][] { - {"1", 8}, - {" a", 6}, - {" 2", 4}, - {" b", 2} + {"1", 8}, + {" a", 6}, + {" 2", 4}, + {" b", 2} }); } @@ -716,25 +716,25 @@ void testActivationBeforeMethodStarts() throws Exception { void testDectivationAfterEnd() throws Exception { assertCallTree( new String[] { - " dd ", - " c ccc ", - " bb bbb ", // <- deactivation for span 2 happens after b ends - " aaa aaa aa ", // that means b must have ended after 2 has been deactivated - "1 2 2 1" // but we saw the last stack trace of b before the deactivation of 2 + " dd ", + " c ccc ", + " bb bbb ", // <- deactivation for span 2 happens after b ends + " aaa aaa aa ", // that means b must have ended after 2 has been deactivated + "1 2 2 1" // but we saw the last stack trace of b before the deactivation of 2 }, new Object[][] { - {"a", 8}, - {" b", 5}, - {" c", 4}, - {" d", 2}, + {"a", 8}, + {" b", 5}, + {" c", 4}, + {" d", 2}, }, new Object[][] { - {"1", 11}, - {" a", 9}, - {" b", 6}, - {" c", 5}, - {" 2", 4}, - {" d", 1}, + {"1", 11}, + {" a", 9}, + {" b", 6}, + {" c", 5}, + {" 2", 4}, + {" d", 1}, }); } @@ -744,9 +744,9 @@ void testCallTreeActivationAsParentOfFastSpan() throws Exception { new String[] {" b ", " aa a aa ", "1 2 2 1"}, new Object[][] {{"a", 5}}, new Object[][] { - {"1", 8}, - {" a", 6}, - {" 2", 2}, + {"1", 8}, + {" a", 6}, + {" 2", 2}, }); } @@ -765,9 +765,9 @@ void testCallTreeActivationAsChildOfFastSpan() throws Exception { new String[] {" c c ", " b b ", " aaa aaa ", "1 22 1"}, new Object[][] {{"a", 6}}, new Object[][] { - {"1", 9}, - {" a", 7}, - {" 2", 1}, + {"1", 9}, + {" a", 7}, + {" 2", 1}, }); } @@ -777,9 +777,9 @@ void testCallTreeActivationAsLeaf() throws Exception { new String[] {" aa aa ", "1 22 1"}, new Object[][] {{"a", 4}}, new Object[][] { - {"1", 7}, - {" a", 5}, - {" 2", 1}, + {"1", 7}, + {" a", 5}, + {" 2", 1}, }); } @@ -789,10 +789,10 @@ void testCallTreeMultipleActivationsAsLeaf() throws Exception { new String[] {" aa aaa aa ", "1 22 33 1"}, new Object[][] {{"a", 7}}, new Object[][] { - {"1", 12}, - {" a", 10}, - {" 2", 1}, - {" 3", 1}, + {"1", 12}, + {" a", 10}, + {" 2", 1}, + {" 3", 1}, }); } @@ -812,10 +812,10 @@ void testCallTreeMultipleActivationsAsLeafWithExcludedParent() throws Exception new String[] {" b b c c ", " aa aaa aa ", "1 22 33 1"}, new Object[][] {{"a", 7}}, new Object[][] { - {"1", 12}, - {" a", 10}, - {" 2", 1}, - {" 3", 1}, + {"1", 12}, + {" a", 10}, + {" 2", 1}, + {" 3", 1}, }); } @@ -824,15 +824,15 @@ void testCallTreeMultipleActivationsWithOneChild() throws Exception { assertCallTree( new String[] {" bb ", " aa aaa aa aa ", "1 22 3 3 1"}, new Object[][] { - {"a", 9}, - {" b", 2} + {"a", 9}, + {" b", 2} }, new Object[][] { - {"1", 14}, - {" a", 12}, - {" 2", 1}, - {" 3", 3}, - {" b", 1}, + {"1", 14}, + {" a", 12}, + {" 2", 1}, + {" 3", 3}, + {" b", 1}, }); } @@ -849,12 +849,12 @@ void testNestedActivationBeforeCallTree() throws Exception { assertCallTree( new String[] {" aaa ", "12 2 1"}, new Object[][] { - {"a", 3}, + {"a", 3}, }, new Object[][] { - {"1", 5}, - {" a", 3}, // a is actually a child of the transaction - {" 2", 2}, // 2 is not within the child_ids of a + {"1", 5}, + {" a", 3}, // a is actually a child of the transaction + {" 2", 2}, // 2 is not within the child_ids of a }); } From caff42ef4a8ca5cdda1307607cbd8d9e18f64f0c Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 11 Apr 2024 15:02:53 +0200 Subject: [PATCH 7/7] spotless --- .../elastic/otel/profiler/CallTreeTest.java | 392 +++++++++--------- 1 file changed, 196 insertions(+), 196 deletions(-) diff --git a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java index efd3e4d74..821feb957 100644 --- a/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java +++ b/inferred-spans/src/test/java/co/elastic/otel/profiler/CallTreeTest.java @@ -142,9 +142,9 @@ void testTwoDistinctInvocationsOfMethodBShouldNotBeFoldedIntoOne() throws Except assertCallTree( new String[] {" bb bb", "aaaaaa"}, new Object[][] { - {"a", 6}, - {" b", 2}, - {" b", 2} + {"a", 6}, + {" b", 2}, + {" b", 2} }); } @@ -153,14 +153,14 @@ void testBasicCallTree() throws Exception { assertCallTree( new String[] {" cc ", " bbb", "aaaa"}, new Object[][] { - {"a", 4}, - {" b", 3}, - {" c", 2} + {"a", 4}, + {" b", 3}, + {" c", 2} }, new Object[][] { - {"a", 3}, - {" b", 2}, - {" c", 1} + {"a", 3}, + {" b", 2}, + {" c", 1} }); } @@ -169,14 +169,14 @@ void testShouldNotCreateInferredSpansForPillarsAndLeafShouldHaveStacktrace() thr assertCallTree( new String[] {" dd ", " cc ", " bb ", "aaaa"}, new Object[][] { - {"a", 4}, - {" b", 2}, - {" c", 2}, - {" d", 2} + {"a", 4}, + {" b", 2}, + {" c", 2}, + {" d", 2} }, new Object[][] { - {"a", 3}, - {" d", 1, Arrays.asList("c", "b")} + {"a", 3}, + {" d", 1, Arrays.asList("c", "b")} }); } @@ -191,10 +191,10 @@ void testSameTopOfStackDifferentBottom() throws Exception { assertCallTree( new String[] {"cccc", "aabb"}, new Object[][] { - {"a", 2}, - {" c", 2}, - {"b", 2}, - {" c", 2}, + {"a", 2}, + {" c", 2}, + {"b", 2}, + {" c", 2}, }); } @@ -203,12 +203,12 @@ void testStackTraceWithRecursion() throws Exception { assertCallTree( new String[] {"bbccbbcc", "bbbbbbbb", "aaaaaaaa"}, new Object[][] { - {"a", 8}, - {" b", 8}, - {" b", 2}, - {" c", 2}, - {" b", 2}, - {" c", 2}, + {"a", 8}, + {" b", 8}, + {" b", 2}, + {" c", 2}, + {" b", 2}, + {" c", 2}, }); } @@ -217,11 +217,11 @@ void testFirstInferredSpanShouldHaveNoStackTrace() throws Exception { assertCallTree( new String[] {"bb", "aa"}, new Object[][] { - {"a", 2}, - {" b", 2}, + {"a", 2}, + {" b", 2}, }, new Object[][] { - {"b", 1}, + {"b", 1}, }); } @@ -230,19 +230,19 @@ void testCallTreeWithSpanActivations() throws Exception { assertCallTree( new String[] {" cc ee ", " bbb dd ", " a aaaaaa a ", "1 2 2 1"}, new Object[][] { - {"a", 8}, - {" b", 3}, - {" c", 2}, - {" d", 2}, - {" e", 2}, + {"a", 8}, + {" b", 3}, + {" c", 2}, + {" d", 2}, + {" e", 2}, }, new Object[][] { - {"1", 11}, - {" a", 9}, - {" 2", 7}, - {" b", 2}, - {" c", 1}, - {" e", 1, Arrays.asList("d")}, + {"1", 11}, + {" a", 9}, + {" 2", 7}, + {" b", 2}, + {" c", 1}, + {" e", 1, Arrays.asList("d")}, }); } @@ -258,25 +258,25 @@ void testCallTreeWithSpanActivations() throws Exception { void testDeactivationBeforeEnd() throws Exception { assertCallTree( new String[] { - " dd ", - " cccc c ", - " bbbb bb ", // <- deactivation for span 2 happens before b and c ends - " a aaaa aa ", // that means b and c must have started before 2 has been activated - "1 2 2 1" // but we saw the first stack trace of b only after the activation of 2 + " dd ", + " cccc c ", + " bbbb bb ", // <- deactivation for span 2 happens before b and c ends + " a aaaa aa ", // that means b and c must have started before 2 has been activated + "1 2 2 1" // but we saw the first stack trace of b only after the activation of 2 }, new Object[][] { - {"a", 7}, - {" b", 6}, - {" c", 5}, - {" d", 2}, + {"a", 7}, + {" b", 6}, + {" c", 5}, + {" d", 2}, }, new Object[][] { - {"1", 10}, - {" a", 8}, - {" b", 7}, - {" c", 6}, - {" 2", 5}, - {" d", 1}, + {"1", 10}, + {" a", 8}, + {" b", 7}, + {" c", 6}, + {" 2", 5}, + {" d", 1}, }); } @@ -291,15 +291,15 @@ void testDectivationBeforeEnd2() throws Exception { assertCallTree( new String[] {" bbbb b ", " a aaaa a a a ", "1 2 2 3 3 1"}, new Object[][] { - {"a", 8}, - {" b", 5}, + {"a", 8}, + {" b", 5}, }, new Object[][] { - {"1", 13}, - {" a", 11}, - {" b", 6}, - {" 2", 5}, - {" 3", 2}, + {"1", 13}, + {" a", 11}, + {" b", 6}, + {" 2", 5}, + {" 3", 2}, }); } @@ -316,15 +316,15 @@ void testDectivationBeforeEnd_DontStealChildIdsOfUnrelatedActivations() throws E assertCallTree( new String[] {" c c ", " b b ", "a a a aa", " 1 1 2 2 "}, new Object[][] { - {"a", 5}, - {" b", 2}, - {" c", 2}, + {"a", 5}, + {" b", 2}, + {" c", 2}, }, new Object[][] { - {"a", 9}, - {" 1", 2}, - {" c", 3, Arrays.asList("b")}, - {" 2", 2}, + {"a", 9}, + {" 1", 2}, + {" c", 3, Arrays.asList("b")}, + {" 2", 2}, }); assertThat(spans.get("a").getLinks()) .hasSize(1) @@ -349,16 +349,16 @@ void testDectivationBeforeEnd_DontStealChildIdsOfUnrelatedActivations_Nested() t assertCallTree( new String[] {" c c ", " b b ", "a a a aa", " 1 1 23 32 "}, new Object[][] { - {"a", 5}, - {" b", 2}, - {" c", 2}, + {"a", 5}, + {" b", 2}, + {" c", 2}, }, new Object[][] { - {"a", 11}, - {" 1", 2}, - {" c", 4, Arrays.asList("b")}, - {" 2", 4}, - {" 3", 2}, + {"a", 11}, + {" 1", 2}, + {" c", 4, Arrays.asList("b")}, + {" 2", 4}, + {" 3", 2}, }); assertThat(spans.get("a").getLinks()) .hasSize(1) @@ -379,13 +379,13 @@ void testActivationAfterMethodEnds() throws Exception { assertCallTree( new String[] {"bb ", "aa a ", " 1 1"}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 3}, - {" b", 1}, - {" 1", 2} + {"a", 3}, + {" b", 1}, + {" 1", 2} }); } @@ -398,13 +398,13 @@ void testActivationBetweenMethods() throws Exception { assertCallTree( new String[] {"bb ", "aa a", " 11 "}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 4}, - {" b", 1}, - {" 1", 1}, + {"a", 4}, + {" b", 1}, + {" 1", 1}, }); } @@ -418,13 +418,13 @@ void testActivationBetweenMethods_AfterFastMethod() throws Exception { assertCallTree( new String[] {" c ", "bb ", "aa a", " 11 "}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 4}, - {" b", 1}, - {" 1", 1}, + {"a", 4}, + {" b", 1}, + {" 1", 1}, }); } @@ -438,14 +438,14 @@ void testActivationBetweenFastMethods() throws Exception { assertCallTree( new String[] {"c d ", "b b ", "a a a", " 11 22 "}, new Object[][] { - {"a", 3}, - {" b", 2}, + {"a", 3}, + {" b", 2}, }, new Object[][] { - {"a", 6}, - {" b", 3}, - {" 1", 1}, - {" 2", 1}, + {"a", 6}, + {" b", 3}, + {" 1", 1}, + {" 2", 1}, }); } @@ -486,12 +486,12 @@ void testNestedActivation() throws Exception { assertCallTree( new String[] {"a a a", " 12 21 "}, new Object[][] { - {"a", 3}, + {"a", 3}, }, new Object[][] { - {"a", 6}, - {" 1", 4}, - {" 2", 2}, + {"a", 6}, + {" 1", 4}, + {" 2", 2}, }); } @@ -507,16 +507,16 @@ void testNestedActivationAfterMethodEnds_RootChangesToC() throws Exception { assertCallTree( new String[] {" bbb ", " aaa ccc ", "1 23 321"}, new Object[][] { - {"a", 3}, - {" b", 3}, - {"c", 3}, + {"a", 3}, + {" b", 3}, + {"c", 3}, }, new Object[][] { - {"1", 11}, - {" b", 2, Arrays.asList("a")}, - {" 2", 6}, - {" 3", 4}, - {" c", 2} + {"1", 11}, + {" b", 2, Arrays.asList("a")}, + {" 2", 6}, + {" 3", 4}, + {" c", 2} }); assertThat(spans.get("b").getLinks()).isEmpty(); @@ -533,17 +533,17 @@ void testRegularActivationFollowedByNestedActivationAfterMethodEnds() throws Exc assertCallTree( new String[] {" d ", " b b b ", " a a a ccc ", "1 2 2 34 431"}, new Object[][] { - {"a", 3}, - {" b", 3}, - {"c", 3}, + {"a", 3}, + {" b", 3}, + {"c", 3}, }, new Object[][] { - {"1", 13}, - {" b", 4, Arrays.asList("a")}, - {" 2", 2}, - {" 3", 6}, - {" 4", 4}, - {" c", 2} + {"1", 13}, + {" b", 4, Arrays.asList("a")}, + {" 2", 2}, + {" 3", 6}, + {" 4", 4}, + {" c", 2} }); } @@ -560,18 +560,18 @@ void testNestedActivationAfterMethodEnds_CommonAncestorA() throws Exception { assertCallTree( new String[] {" b b b ccc ", " aa a a aaa a ", "1 2 2 34 43 1"}, new Object[][] { - {"a", 8}, - {" b", 3}, - {" c", 3}, + {"a", 8}, + {" b", 3}, + {" c", 3}, }, new Object[][] { - {"1", 15}, - {" a", 13}, - {" b", 4}, - {" 2", 2}, - {" 3", 6}, - {" 4", 4}, - {" c", 2} + {"1", 15}, + {" a", 13}, + {" b", 4}, + {" 2", 2}, + {" 3", 6}, + {" 4", 4}, + {" c", 2} }); assertThat(spans.get("b").getLinks()) @@ -609,15 +609,15 @@ void testActivationAfterMethodEnds_RootChangesToB() throws Exception { assertCallTree( new String[] {" ccc ", " aaa bbb ", "1 2 21"}, new Object[][] { - {"a", 3}, - {"b", 3}, - {" c", 3}, + {"a", 3}, + {"b", 3}, + {" c", 3}, }, new Object[][] { - {"1", 9}, - {" a", 2}, - {" 2", 4}, - {" c", 2, Arrays.asList("b")} + {"1", 9}, + {" a", 2}, + {" 2", 4}, + {" c", 2, Arrays.asList("b")} }); } @@ -632,14 +632,14 @@ void testActivationAfterMethodEnds_RootChangesToB2() throws Exception { assertCallTree( new String[] {" aaa bbb ", "1 2 21"}, new Object[][] { - {"a", 3}, - {"b", 3}, + {"a", 3}, + {"b", 3}, }, new Object[][] { - {"1", 9}, - {" a", 2}, - {" 2", 4}, - {" b", 2} + {"1", 9}, + {" a", 2}, + {" 2", 4}, + {" b", 2} }); } @@ -671,14 +671,14 @@ void testActivationAfterMethodEnds_SameRootDeeperStack() throws Exception { assertCallTree( new String[] {" ccc ", " aaa aaa ", "1 2 21"}, new Object[][] { - {"a", 6}, - {" c", 3}, + {"a", 6}, + {" c", 3}, }, new Object[][] { - {"1", 9}, - {" a", 6}, - {" 2", 4}, - {" c", 2} + {"1", 9}, + {" a", 6}, + {" 2", 4}, + {" c", 2} }); } @@ -693,14 +693,14 @@ void testActivationBeforeMethodStarts() throws Exception { assertCallTree( new String[] {" bbb ", " a aaa a ", "1 2 2 1"}, new Object[][] { - {"a", 5}, - {" b", 3}, + {"a", 5}, + {" b", 3}, }, new Object[][] { - {"1", 8}, - {" a", 6}, - {" 2", 4}, - {" b", 2} + {"1", 8}, + {" a", 6}, + {" 2", 4}, + {" b", 2} }); } @@ -716,25 +716,25 @@ void testActivationBeforeMethodStarts() throws Exception { void testDectivationAfterEnd() throws Exception { assertCallTree( new String[] { - " dd ", - " c ccc ", - " bb bbb ", // <- deactivation for span 2 happens after b ends - " aaa aaa aa ", // that means b must have ended after 2 has been deactivated - "1 2 2 1" // but we saw the last stack trace of b before the deactivation of 2 + " dd ", + " c ccc ", + " bb bbb ", // <- deactivation for span 2 happens after b ends + " aaa aaa aa ", // that means b must have ended after 2 has been deactivated + "1 2 2 1" // but we saw the last stack trace of b before the deactivation of 2 }, new Object[][] { - {"a", 8}, - {" b", 5}, - {" c", 4}, - {" d", 2}, + {"a", 8}, + {" b", 5}, + {" c", 4}, + {" d", 2}, }, new Object[][] { - {"1", 11}, - {" a", 9}, - {" b", 6}, - {" c", 5}, - {" 2", 4}, - {" d", 1}, + {"1", 11}, + {" a", 9}, + {" b", 6}, + {" c", 5}, + {" 2", 4}, + {" d", 1}, }); } @@ -744,9 +744,9 @@ void testCallTreeActivationAsParentOfFastSpan() throws Exception { new String[] {" b ", " aa a aa ", "1 2 2 1"}, new Object[][] {{"a", 5}}, new Object[][] { - {"1", 8}, - {" a", 6}, - {" 2", 2}, + {"1", 8}, + {" a", 6}, + {" 2", 2}, }); } @@ -765,9 +765,9 @@ void testCallTreeActivationAsChildOfFastSpan() throws Exception { new String[] {" c c ", " b b ", " aaa aaa ", "1 22 1"}, new Object[][] {{"a", 6}}, new Object[][] { - {"1", 9}, - {" a", 7}, - {" 2", 1}, + {"1", 9}, + {" a", 7}, + {" 2", 1}, }); } @@ -777,9 +777,9 @@ void testCallTreeActivationAsLeaf() throws Exception { new String[] {" aa aa ", "1 22 1"}, new Object[][] {{"a", 4}}, new Object[][] { - {"1", 7}, - {" a", 5}, - {" 2", 1}, + {"1", 7}, + {" a", 5}, + {" 2", 1}, }); } @@ -789,10 +789,10 @@ void testCallTreeMultipleActivationsAsLeaf() throws Exception { new String[] {" aa aaa aa ", "1 22 33 1"}, new Object[][] {{"a", 7}}, new Object[][] { - {"1", 12}, - {" a", 10}, - {" 2", 1}, - {" 3", 1}, + {"1", 12}, + {" a", 10}, + {" 2", 1}, + {" 3", 1}, }); } @@ -812,10 +812,10 @@ void testCallTreeMultipleActivationsAsLeafWithExcludedParent() throws Exception new String[] {" b b c c ", " aa aaa aa ", "1 22 33 1"}, new Object[][] {{"a", 7}}, new Object[][] { - {"1", 12}, - {" a", 10}, - {" 2", 1}, - {" 3", 1}, + {"1", 12}, + {" a", 10}, + {" 2", 1}, + {" 3", 1}, }); } @@ -824,15 +824,15 @@ void testCallTreeMultipleActivationsWithOneChild() throws Exception { assertCallTree( new String[] {" bb ", " aa aaa aa aa ", "1 22 3 3 1"}, new Object[][] { - {"a", 9}, - {" b", 2} + {"a", 9}, + {" b", 2} }, new Object[][] { - {"1", 14}, - {" a", 12}, - {" 2", 1}, - {" 3", 3}, - {" b", 1}, + {"1", 14}, + {" a", 12}, + {" 2", 1}, + {" 3", 3}, + {" b", 1}, }); } @@ -849,12 +849,12 @@ void testNestedActivationBeforeCallTree() throws Exception { assertCallTree( new String[] {" aaa ", "12 2 1"}, new Object[][] { - {"a", 3}, + {"a", 3}, }, new Object[][] { - {"1", 5}, - {" a", 3}, // a is actually a child of the transaction - {" 2", 2}, // 2 is not within the child_ids of a + {"1", 5}, + {" a", 3}, // a is actually a child of the transaction + {" 2", 2}, // 2 is not within the child_ids of a }); }