Skip to content

Commit d383730

Browse files
authored
Timing breakdown (#588)
1 parent 8e44cb5 commit d383730

File tree

48 files changed

+2063
-226
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2063
-226
lines changed

NOTICE

+45
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,48 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
359359

360360
###############################################################################
361361

362+
This product includes code from HdrHistogram, dual-licensed under CC0 and BSD 2-Clause License
363+
364+
The code in this repository code was Written by Gil Tene, Michael Barker,
365+
and Matt Warren, and released to the public domain, as explained at
366+
http://creativecommons.org/publicdomain/zero/1.0/
367+
368+
For users of this code who wish to consume it under the "BSD" license
369+
rather than under the public domain or CC0 contribution text mentioned
370+
above, the code found under this directory is *also* provided under the
371+
following license (commonly referred to as the BSD 2-Clause License). This
372+
license does not detract from the above stated release of the code into
373+
the public domain, and simply represents an additional license granted by
374+
the Author.
375+
376+
-----------------------------------------------------------------------------
377+
** Beginning of "BSD 2-Clause License" text. **
378+
379+
Copyright (c) 2012, 2013, 2014, 2015, 2016 Gil Tene
380+
Copyright (c) 2014 Michael Barker
381+
Copyright (c) 2014 Matt Warren
382+
All rights reserved.
383+
384+
Redistribution and use in source and binary forms, with or without
385+
modification, are permitted provided that the following conditions are met:
386+
387+
1. Redistributions of source code must retain the above copyright notice,
388+
this list of conditions and the following disclaimer.
389+
390+
2. Redistributions in binary form must reproduce the above copyright notice,
391+
this list of conditions and the following disclaimer in the documentation
392+
and/or other materials provided with the distribution.
393+
394+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
395+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
396+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
397+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
398+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
399+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
400+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
401+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
402+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
403+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
404+
THE POSSIBILITY OF SUCH DAMAGE.
405+
406+
###############################################################################

apm-agent-benchmarks/src/main/java/co/elastic/apm/agent/benchmark/ElasticApmContinuousBenchmark.java

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public void setUp(Blackhole blackhole) {
150150

151151
@TearDown
152152
public void tearDown() throws ExecutionException, InterruptedException {
153+
Thread.sleep(1000);
153154
tracer.getReporter().flush().get();
154155
server.stop();
155156
System.out.println("Reported: " + tracer.getReporter().getReported());

apm-agent-core/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@
9292
<artifactId>byte-buddy</artifactId>
9393
<version>${version.byte-buddy}</version>
9494
</dependency>
95+
<dependency>
96+
<groupId>org.hdrhistogram</groupId>
97+
<artifactId>HdrHistogram</artifactId>
98+
<version>2.1.11</version>
99+
</dependency>
95100

96101
<dependency>
97102
<groupId>com.squareup.okhttp3</groupId>

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
* the Apache License, Version 2.0 (the "License"); you may
1212
* not use this file except in compliance with the License.
1313
* You may obtain a copy of the License at
14-
*
14+
*
1515
* http://www.apache.org/licenses/LICENSE-2.0
16-
*
16+
*
1717
* Unless required by applicable law or agreed to in writing,
1818
* software distributed under the License is distributed on an
1919
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -399,7 +399,7 @@ public void endSpan(Span span) {
399399
if (span.isSampled() && !span.isDiscard()) {
400400
long spanFramesMinDurationMs = stacktraceConfiguration.getSpanFramesMinDurationMs();
401401
if (spanFramesMinDurationMs != 0 && span.isSampled()) {
402-
if (span.getDuration() >= spanFramesMinDurationMs) {
402+
if (span.getDurationMs() >= spanFramesMinDurationMs) {
403403
span.withStacktrace(new Throwable());
404404
}
405405
}

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/async/SpanInScopeCallableWrapper.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
* the Apache License, Version 2.0 (the "License"); you may
1212
* not use this file except in compliance with the License.
1313
* You may obtain a copy of the License at
14-
*
14+
*
1515
* http://www.apache.org/licenses/LICENSE-2.0
16-
*
16+
*
1717
* Unless required by applicable law or agreed to in writing,
1818
* software distributed under the License is distributed on an
1919
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/async/SpanInScopeRunnableWrapper.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
* the Apache License, Version 2.0 (the "License"); you may
1212
* not use this file except in compliance with the License.
1313
* You may obtain a copy of the License at
14-
*
14+
*
1515
* http://www.apache.org/licenses/LICENSE-2.0
16-
*
16+
*
1717
* Unless required by applicable law or agreed to in writing,
1818
* software distributed under the License is distributed on an
1919
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/AbstractSpan.java

+95-14
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
* the Apache License, Version 2.0 (the "License"); you may
1212
* not use this file except in compliance with the License.
1313
* You may obtain a copy of the License at
14-
*
14+
*
1515
* http://www.apache.org/licenses/LICENSE-2.0
16-
*
16+
*
1717
* Unless required by applicable law or agreed to in writing,
1818
* software distributed under the License is distributed on an
1919
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -26,13 +26,17 @@
2626

2727
import co.elastic.apm.agent.impl.ElasticApmTracer;
2828
import co.elastic.apm.agent.impl.context.AbstractContext;
29+
import co.elastic.apm.agent.matcher.WildcardMatcher;
30+
import co.elastic.apm.agent.objectpool.Recyclable;
31+
import co.elastic.apm.agent.report.ReporterConfiguration;
2932
import org.slf4j.Logger;
3033
import org.slf4j.LoggerFactory;
3134

3235
import javax.annotation.Nullable;
3336
import java.util.concurrent.Callable;
3437
import java.util.concurrent.TimeUnit;
3538
import java.util.concurrent.atomic.AtomicInteger;
39+
import java.util.concurrent.atomic.AtomicLong;
3640

3741
public abstract class AbstractSpan<T extends AbstractSpan> extends TraceContextHolder<T> {
3842
private static final Logger logger = LoggerFactory.getLogger(AbstractSpan.class);
@@ -43,37 +47,98 @@ public abstract class AbstractSpan<T extends AbstractSpan> extends TraceContextH
4347
* Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')
4448
*/
4549
protected final StringBuilder name = new StringBuilder();
50+
protected final boolean collectBreakdownMetrics;
4651
private long timestamp;
4752

48-
/**
49-
* How long the transaction took to complete, in ms with 3 decimal points
50-
* (Required)
51-
*/
52-
protected double duration;
53+
// in microseconds
54+
protected long duration;
55+
private ChildDurationTimer childDurations = new ChildDurationTimer();
5356
protected AtomicInteger references = new AtomicInteger();
5457
protected volatile boolean finished = true;
5558

5659
public int getReferenceCount() {
5760
return references.get();
5861
}
5962

63+
private static class ChildDurationTimer implements Recyclable {
64+
65+
private AtomicInteger activeChildren = new AtomicInteger();
66+
private AtomicLong start = new AtomicLong();
67+
private AtomicLong duration = new AtomicLong();
68+
69+
/**
70+
* Starts the timer if it has not been started already.
71+
*
72+
* @param startTimestamp
73+
*/
74+
void onChildStart(long startTimestamp) {
75+
if (activeChildren.incrementAndGet() == 1) {
76+
start.set(startTimestamp);
77+
}
78+
}
79+
80+
/**
81+
* Stops the timer and increments the duration if no other direct children are still running
82+
* @param endTimestamp
83+
*/
84+
void onChildEnd(long endTimestamp) {
85+
if (activeChildren.decrementAndGet() == 0) {
86+
incrementDuration(endTimestamp);
87+
}
88+
}
89+
90+
/**
91+
* Stops the timer and increments the duration even if there are direct children which are still running
92+
*
93+
* @param endTimestamp
94+
*/
95+
void onSpanEnd(long endTimestamp) {
96+
if (activeChildren.getAndSet(0) != 0) {
97+
incrementDuration(endTimestamp);
98+
}
99+
}
100+
101+
private void incrementDuration(long epochMicros) {
102+
duration.addAndGet(epochMicros - start.get());
103+
}
104+
105+
@Override
106+
public void resetState() {
107+
activeChildren.set(0);
108+
start.set(0);
109+
duration.set(0);
110+
}
111+
112+
public long getDuration() {
113+
return duration.get();
114+
}
115+
}
116+
60117
public AbstractSpan(ElasticApmTracer tracer) {
61118
super(tracer);
62119
traceContext = TraceContext.with64BitId(this.tracer);
120+
collectBreakdownMetrics = !WildcardMatcher.isAnyMatch(tracer.getConfig(ReporterConfiguration.class).getDisableMetrics(), "span.self_time");
63121
}
64122

65123
public boolean isReferenced() {
66124
return references.get() > 0;
67125
}
68126

69127
/**
70-
* How long the transaction took to complete, in ms with 3 decimal points
71-
* (Required)
128+
* How long the transaction took to complete, in µs
72129
*/
73-
public double getDuration() {
130+
public long getDuration() {
74131
return duration;
75132
}
76133

134+
public long getSelfDuration() {
135+
return duration - childDurations.getDuration();
136+
}
137+
138+
public double getDurationMs() {
139+
return duration / AbstractSpan.MS_IN_MICROS;
140+
}
141+
77142
/**
78143
* Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')
79144
*/
@@ -124,6 +189,7 @@ public void resetState() {
124189
timestamp = 0;
125190
duration = 0;
126191
traceContext.resetState();
192+
childDurations.resetState();
127193
references.set(0);
128194
}
129195

@@ -177,20 +243,23 @@ public void end() {
177243

178244
public final void end(long epochMicros) {
179245
if (!finished) {
180-
this.duration = (epochMicros - timestamp) / AbstractSpan.MS_IN_MICROS;
246+
this.duration = (epochMicros - timestamp);
181247
if (name.length() == 0) {
182248
name.append("unnamed");
183249
}
184-
doEnd(epochMicros);
185-
// has to be set last so doEnd callbacks don't think it has already been finished
250+
childDurations.onSpanEnd(epochMicros);
251+
beforeEnd(epochMicros);
186252
this.finished = true;
253+
afterEnd();
187254
} else {
188255
logger.warn("End has already been called: {}", this);
189256
assert false;
190257
}
191258
}
192259

193-
protected abstract void doEnd(long epochMicros);
260+
protected abstract void beforeEnd(long epochMicros);
261+
262+
protected abstract void afterEnd();
194263

195264
@Override
196265
public boolean isChildOf(TraceContextHolder other) {
@@ -242,6 +311,18 @@ public void setStartTimestamp(long epochMicros) {
242311
timestamp = epochMicros;
243312
}
244313

314+
void onChildStart(long epochMicros) {
315+
if (collectBreakdownMetrics) {
316+
childDurations.onChildStart(epochMicros);
317+
}
318+
}
319+
320+
void onChildEnd(long epochMicros) {
321+
if (collectBreakdownMetrics) {
322+
childDurations.onChildEnd(epochMicros);
323+
}
324+
}
325+
245326
public void incrementReferences() {
246327
references.incrementAndGet();
247328
if (logger.isDebugEnabled()) {

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Span.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
* the Apache License, Version 2.0 (the "License"); you may
1212
* not use this file except in compliance with the License.
1313
* You may obtain a copy of the License at
14-
*
14+
*
1515
* http://www.apache.org/licenses/LICENSE-2.0
16-
*
16+
*
1717
* Unless required by applicable law or agreed to in writing,
1818
* software distributed under the License is distributed on an
1919
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -77,12 +77,10 @@ public <T> Span start(TraceContext.ChildContextCreator<T> childContextCreator, T
7777
if (parentContext instanceof Transaction) {
7878
this.transaction = (Transaction) parentContext;
7979
this.parent = this.transaction;
80-
this.parent.incrementReferences();
8180
} else if (parentContext instanceof Span) {
8281
final Span parentSpan = (Span) parentContext;
8382
this.parent = parentSpan;
8483
this.transaction = parentSpan.transaction;
85-
this.parent.incrementReferences();
8684
}
8785
if (dropped) {
8886
traceContext.setRecorded(false);
@@ -103,6 +101,15 @@ public <T> Span start(TraceContext.ChildContextCreator<T> childContextCreator, T
103101
return this;
104102
}
105103

104+
@Override
105+
protected void onAfterStart() {
106+
super.onAfterStart();
107+
if (parent != null) {
108+
this.parent.incrementReferences();
109+
this.parent.onChildStart(getTimestamp());
110+
}
111+
}
112+
106113
/**
107114
* Any other arbitrary data captured by the agent, optionally provided by the user
108115
*/
@@ -188,7 +195,7 @@ public String getAction() {
188195
}
189196

190197
@Override
191-
public void doEnd(long epochMicros) {
198+
public void beforeEnd(long epochMicros) {
192199
if (logger.isDebugEnabled()) {
193200
logger.debug("} endSpan {}", this);
194201
if (logger.isTraceEnabled()) {
@@ -198,9 +205,17 @@ public void doEnd(long epochMicros) {
198205
if (type == null) {
199206
type = "custom";
200207
}
208+
if (transaction != null) {
209+
transaction.incrementTimer(type, subtype, getSelfDuration());
210+
}
201211
if (parent != null) {
212+
parent.onChildEnd(epochMicros);
202213
parent.decrementReferences();
203214
}
215+
}
216+
217+
@Override
218+
protected void afterEnd() {
204219
this.tracer.endSpan(this);
205220
}
206221

0 commit comments

Comments
 (0)