org.glowroot.agent.impl.TraceEntryComponent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glowroot-agent-it-harness Show documentation
Show all versions of glowroot-agent-it-harness Show documentation
Glowroot Agent Integration Test Harness
/*
* Copyright 2011-2018 the original author or authors.
*
* Licensed 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 org.glowroot.agent.impl;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.Logger;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.LoggerFactory;
import org.glowroot.agent.model.AsyncTimerImpl;
import org.glowroot.agent.model.ErrorMessage;
import org.glowroot.agent.model.QueryData;
import org.glowroot.agent.plugin.api.MessageSupplier;
// this supports updating by a single thread and reading by multiple threads
class TraceEntryComponent {
private static final Logger logger = LoggerFactory.getLogger(TraceEntryComponent.class);
private final long startTick;
// not volatile, so depends on memory barrier in Transaction for visibility
private boolean completed;
// not volatile, so depends on memory barrier in Transaction for visibility
private long endTick;
private final ThreadContextImpl threadContext;
private final TraceEntryImpl rootEntry;
private TraceEntryImpl activeEntry;
private TraceEntryImpl tailEntry;
TraceEntryComponent(ThreadContextImpl threadContext, MessageSupplier messageSupplier,
TimerImpl timer, long startTick) {
this.threadContext = threadContext;
this.startTick = startTick;
rootEntry = new TraceEntryImpl(threadContext, null, messageSupplier, null, 0, startTick,
timer, null);
activeEntry = rootEntry;
tailEntry = rootEntry;
}
TraceEntryImpl getRootEntry() {
return rootEntry;
}
long getStartTick() {
return startTick;
}
boolean isCompleted() {
return completed;
}
long getEndTick() {
return endTick;
}
TraceEntryImpl pushEntry(long startTick, Object messageSupplier, TimerImpl syncTimer,
@Nullable AsyncTimerImpl asyncTimer, @Nullable QueryData queryData,
long queryExecutionCount) {
TraceEntryImpl entry = new TraceEntryImpl(threadContext, activeEntry, messageSupplier,
queryData, queryExecutionCount, startTick, syncTimer, asyncTimer);
tailEntry.setNextTraceEntry(entry);
tailEntry = entry;
activeEntry = entry;
return entry;
}
// typically pop() methods don't require the objects to pop, but for safety, the entry is
// passed in just to make sure it is the one on top (and if not, then pop until it is found,
// preventing any nasty bugs from a missed pop, e.g. an entry never being marked as complete)
void popEntry(TraceEntryImpl entry, long endTick) {
popEntrySafe(entry);
if (entry == rootEntry) {
this.endTick = endTick;
this.completed = true;
}
}
// typically pop() methods don't require the objects to pop, but for safety, the entry is
// passed in just to make sure it is the one on top (and if not, then pop until it is found,
// preventing any nasty bugs from a missed pop, e.g. an entry never being marked as complete)
void popNonRootEntry(TraceEntryImpl entry) {
popEntrySafe(entry);
}
TraceEntryImpl addErrorEntry(long startTick, long endTick, @Nullable Object messageSupplier,
@Nullable QueryData queryData, ErrorMessage errorMessage) {
TraceEntryImpl entry = TraceEntryImpl.createCompletedErrorEntry(threadContext, activeEntry,
messageSupplier, queryData, errorMessage, startTick, endTick);
tailEntry.setNextTraceEntry(entry);
tailEntry = entry;
return entry;
}
TraceEntryImpl getActiveEntry() {
return activeEntry;
}
TraceEntryImpl getTailEntry() {
return tailEntry;
}
boolean isEmpty() {
return rootEntry == tailEntry;
}
private void popEntrySafe(TraceEntryImpl entry) {
if (activeEntry != entry) {
// somehow(?) a pop was missed (or maybe too many pops), this is just damage control
popEntryBailout(entry);
return;
}
TraceEntryImpl parentTraceEntry = activeEntry.getParentTraceEntry();
if (parentTraceEntry != null) {
// don't pop the root entry in case an async trace that exceeded the trace entry limit
// is still active, and ends with an error, but has not exceeded the extra error entry
// limit, so then adds a trace entry error, and so still needs an "active trace"
activeEntry = parentTraceEntry;
}
}
// split typically unused path into separate method to not affect inlining budget
private void popEntryBailout(TraceEntryImpl entry) {
logger.error("found entry {} at top of stack when expecting entry {}", activeEntry, entry,
new Exception("location stack trace"));
TraceEntryImpl parentTraceEntry = activeEntry.getParentTraceEntry();
while (activeEntry != entry && parentTraceEntry != null) {
activeEntry = parentTraceEntry;
parentTraceEntry = parentTraceEntry.getParentTraceEntry();
}
}
}