/*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.debug;
import static java.util.FormattableFlags.LEFT_JUSTIFY;
import static java.util.FormattableFlags.UPPERCASE;
import static org.graalvm.compiler.debug.DebugOptions.Count;
import static org.graalvm.compiler.debug.DebugOptions.Counters;
import static org.graalvm.compiler.debug.DebugOptions.Dump;
import static org.graalvm.compiler.debug.DebugOptions.DumpOnError;
import static org.graalvm.compiler.debug.DebugOptions.DumpOnPhaseChange;
import static org.graalvm.compiler.debug.DebugOptions.DumpPath;
import static org.graalvm.compiler.debug.DebugOptions.ListMetrics;
import static org.graalvm.compiler.debug.DebugOptions.Log;
import static org.graalvm.compiler.debug.DebugOptions.MemUseTrackers;
import static org.graalvm.compiler.debug.DebugOptions.ShowDumpFiles;
import static org.graalvm.compiler.debug.DebugOptions.Time;
import static org.graalvm.compiler.debug.DebugOptions.Timers;
import static org.graalvm.compiler.debug.DebugOptions.TrackMemUse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import jdk.internal.vm.compiler.collections.EconomicMap;
import jdk.internal.vm.compiler.collections.EconomicSet;
import jdk.internal.vm.compiler.collections.Pair;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.serviceprovider.GraalServices;
import org.graalvm.graphio.GraphOutput;
import jdk.vm.ci.meta.JavaMethod;
A facility for logging and dumping as well as a container for values associated with MetricKey
s. A DebugContext
object must only be used on the thread that created it. This means it needs to be passed around as a parameter. For convenience, it can be encapsulated in a widely used object that is in scope wherever a DebugContext
is needed. However, care must be taken when such objects can be exposed to multiple threads (e.g., they are in a non-thread-local cache). /**
* A facility for logging and dumping as well as a container for values associated with
* {@link MetricKey}s.
*
* A {@code DebugContext} object must only be used on the thread that created it. This means it
* needs to be passed around as a parameter. For convenience, it can be encapsulated in a widely
* used object that is in scope wherever a {@code DebugContext} is needed. However, care must be
* taken when such objects can be exposed to multiple threads (e.g., they are in a non-thread-local
* cache).
*/
public final class DebugContext implements AutoCloseable {
public static final Description NO_DESCRIPTION = new Description(null, "NO_DESCRIPTION");
public static final GlobalMetrics NO_GLOBAL_METRIC_VALUES = null;
public static final Iterable<DebugHandlersFactory> NO_CONFIG_CUSTOMIZERS = Collections.emptyList();
public static final PrintStream DEFAULT_LOG_STREAM = TTY.out;
Contains the immutable parts of a debug context. This separation allows the immutable parts
to be shared and reduces the overhead of initialization since most immutable fields are
configured by parsing options.
/**
* Contains the immutable parts of a debug context. This separation allows the immutable parts
* to be shared and reduces the overhead of initialization since most immutable fields are
* configured by parsing options.
*/
final Immutable immutable;
Determines whether metrics are enabled.
/**
* Determines whether metrics are enabled.
*/
boolean metricsEnabled;
DebugConfigImpl currentConfig;
ScopeImpl currentScope;
CloseableCounter currentTimer;
CloseableCounter currentMemUseTracker;
Scope lastClosedScope;
Throwable lastExceptionThrown;
private IgvDumpChannel sharedChannel;
private GraphOutput<?, ?> parentOutput;
Stores the MetricKey
values. /**
* Stores the {@link MetricKey} values.
*/
private long[] metricValues;
Determines if dynamic scopes are enabled.
/**
* Determines if dynamic scopes are enabled.
*/
public boolean areScopesEnabled() {
return immutable.scopesEnabled;
}
public <G, N, M> GraphOutput<G, M> buildOutput(GraphOutput.Builder<G, N, M> builder) throws IOException {
if (parentOutput != null) {
return builder.build(parentOutput);
} else {
if (sharedChannel == null) {
sharedChannel = new IgvDumpChannel(() -> getDumpPath(".bgv", false), immutable.options);
}
final GraphOutput<G, M> output = builder.build(sharedChannel);
parentOutput = output;
return output;
}
}
Adds version properties to the provided map. The version properties are read at a start of the JVM from a JVM specific location. Each property identifiers a commit of a certain component in the system. The properties added to the properties
map are prefixed with "version."
prefix. Params: - properties – map to add the version properties to or
null
Returns: properties
with version properties added or an unmodifiable map containing the version properties if properties == null
/**
* Adds version properties to the provided map. The version properties are read at a start of
* the JVM from a JVM specific location. Each property identifiers a commit of a certain
* component in the system. The properties added to the {@code properties} map are prefixed with
* {@code "version."} prefix.
*
* @param properties map to add the version properties to or {@code null}
* @return {@code properties} with version properties added or an unmodifiable map containing
* the version properties if {@code properties == null}
*/
public static Map<Object, Object> addVersionProperties(Map<Object, Object> properties) {
return Versions.VERSIONS.withVersions(properties);
}
The immutable configuration that can be shared between DebugContext
objects. /**
* The immutable configuration that can be shared between {@link DebugContext} objects.
*/
static final class Immutable {
private static final Immutable[] CACHE = new Immutable[5];
The options from which this object was configured.
/**
* The options from which this object was configured.
*/
final OptionValues options;
Specifies if dynamic scopes are enabled.
/**
* Specifies if dynamic scopes are enabled.
*/
final boolean scopesEnabled;
final boolean listMetrics;
Names of unscoped counters. A counter is unscoped if this set is empty or contains the
counter's name.
/**
* Names of unscoped counters. A counter is unscoped if this set is empty or contains the
* counter's name.
*/
final EconomicSet<String> unscopedCounters;
Names of unscoped timers. A timer is unscoped if this set is empty or contains the
timer's name.
/**
* Names of unscoped timers. A timer is unscoped if this set is empty or contains the
* timer's name.
*/
final EconomicSet<String> unscopedTimers;
Names of unscoped memory usage trackers. A memory usage tracker is unscoped if this set
is empty or contains the memory usage tracker's name.
/**
* Names of unscoped memory usage trackers. A memory usage tracker is unscoped if this set
* is empty or contains the memory usage tracker's name.
*/
final EconomicSet<String> unscopedMemUseTrackers;
private static EconomicSet<String> parseUnscopedMetricSpec(String spec, boolean unconditional, boolean accumulatedKey) {
EconomicSet<String> res;
if (spec == null) {
if (!unconditional) {
res = null;
} else {
res = EconomicSet.create();
}
} else {
res = EconomicSet.create();
if (!spec.isEmpty()) {
if (!accumulatedKey) {
res.addAll(Arrays.asList(spec.split(",")));
} else {
for (String n : spec.split(",")) {
res.add(n + AccumulatedKey.ACCUMULATED_KEY_SUFFIX);
res.add(n + AccumulatedKey.FLAT_KEY_SUFFIX);
}
}
}
}
return res;
}
static Immutable create(OptionValues options) {
int i = 0;
while (i < CACHE.length) {
Immutable immutable = CACHE[i];
if (immutable == null) {
break;
}
if (immutable.options == options) {
return immutable;
}
i++;
}
Immutable immutable = new Immutable(options);
if (i < CACHE.length) {
CACHE[i] = immutable;
}
return immutable;
}
private static boolean isNotEmpty(OptionKey<String> option, OptionValues options) {
return option.getValue(options) != null && !option.getValue(options).isEmpty();
}
private Immutable(OptionValues options) {
this.options = options;
String timeValue = Time.getValue(options);
String trackMemUseValue = TrackMemUse.getValue(options);
this.unscopedCounters = parseUnscopedMetricSpec(Counters.getValue(options), "".equals(Count.getValue(options)), false);
this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(timeValue), true);
this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(trackMemUseValue), true);
if (unscopedMemUseTrackers != null || trackMemUseValue != null) {
if (!GraalServices.isThreadAllocatedMemorySupported()) {
TTY.println("WARNING: Missing VM support for MemUseTrackers and TrackMemUse options so all reported memory usage will be 0");
}
}
this.scopesEnabled = DumpOnError.getValue(options) ||
Dump.getValue(options) != null ||
Log.getValue(options) != null ||
isNotEmpty(DebugOptions.Count, options) ||
isNotEmpty(DebugOptions.Time, options) ||
isNotEmpty(DebugOptions.TrackMemUse, options) ||
DumpOnPhaseChange.getValue(options) != null;
this.listMetrics = ListMetrics.getValue(options);
}
private Immutable() {
this.options = new OptionValues(EconomicMap.create());
this.unscopedCounters = null;
this.unscopedTimers = null;
this.unscopedMemUseTrackers = null;
this.scopesEnabled = false;
this.listMetrics = false;
}
public boolean hasUnscopedMetrics() {
return unscopedCounters != null || unscopedTimers != null || unscopedMemUseTrackers != null;
}
}
Gets the options this debug context was constructed with.
/**
* Gets the options this debug context was constructed with.
*/
public OptionValues getOptions() {
return immutable.options;
}
static class Activated extends ThreadLocal<DebugContext> {
}
private static final Activated activated = new Activated();
An object used to undo the changes made by DebugContext#activate().
/**
* An object used to undo the changes made by DebugContext#activate().
*/
public static class Activation implements AutoCloseable {
private final DebugContext parent;
Activation(DebugContext parent) {
this.parent = parent;
}
@Override
public void close() {
activated.set(parent);
}
}
Activates this object as the debug context for the
current thread. This method should be used in a try-with-resources statement. Returns: an object that will deactivate the debug context for the current thread when Activation.close()
is called on it
/**
* Activates this object as the debug context {@linkplain DebugContext#forCurrentThread for the
* current thread}. This method should be used in a try-with-resources statement.
*
* @return an object that will deactivate the debug context for the current thread when
* {@link Activation#close()} is called on it
*/
public Activation activate() {
Activation res = new Activation(activated.get());
activated.set(this);
return res;
}
Singleton used to represent a disabled debug context.
/**
* Singleton used to represent a disabled debug context.
*/
private static final DebugContext DISABLED = new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, new Immutable(), NO_CONFIG_CUSTOMIZERS);
Create a DebugContext with debugging disabled.
/**
* Create a DebugContext with debugging disabled.
*/
public static DebugContext disabled(OptionValues options) {
if (options == null || options.getMap().isEmpty()) {
return DISABLED;
}
return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), NO_CONFIG_CUSTOMIZERS);
}
Gets the debug context for the current thread. This should only be used when there is no
other reasonable means to get a hold of a debug context.
/**
* Gets the debug context for the current thread. This should only be used when there is no
* other reasonable means to get a hold of a debug context.
*/
public static DebugContext forCurrentThread() {
DebugContext current = activated.get();
if (current == null) {
return DISABLED;
}
return current;
}
private final GlobalMetrics globalMetrics;
Describes the computation associated with a DebugContext
. /**
* Describes the computation associated with a {@link DebugContext}.
*/
public static class Description {
The primary input to the computation.
/**
* The primary input to the computation.
*/
final Object compilable;
A runtime based identifier that is most likely to be unique.
/**
* A runtime based identifier that is most likely to be unique.
*/
final String identifier;
public Description(Object compilable, String identifier) {
this.compilable = compilable;
this.identifier = identifier;
}
@Override
public String toString() {
String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
return identifier + ":" + compilableName;
}
final String getLabel() {
if (compilable instanceof JavaMethod) {
JavaMethod method = (JavaMethod) compilable;
return method.format("%h.%n(%p)%r");
}
return String.valueOf(compilable);
}
}
private final Description description;
Gets a description of the computation associated with this debug context.
Returns: null
if no description is available
/**
* Gets a description of the computation associated with this debug context.
*
* @return {@code null} if no description is available
*/
public Description getDescription() {
return description;
}
Gets the global metrics associated with this debug context.
Returns: null
if no global metrics are available
/**
* Gets the global metrics associated with this debug context.
*
* @return {@code null} if no global metrics are available
*/
public GlobalMetrics getGlobalMetrics() {
return globalMetrics;
}
Creates a DebugContext
based on a given set of option values and factory
. /**
* Creates a {@link DebugContext} based on a given set of option values and {@code factory}.
*/
public static DebugContext create(OptionValues options, DebugHandlersFactory factory) {
return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), Collections.singletonList(factory));
}
Creates a DebugContext
based on a given set of option values and factories
. The DebugHandlersFactory.LOADER
can be used for the latter. /**
* Creates a {@link DebugContext} based on a given set of option values and {@code factories}.
* The {@link DebugHandlersFactory#LOADER} can be used for the latter.
*/
public static DebugContext create(OptionValues options, Iterable<DebugHandlersFactory> factories) {
return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), factories);
}
public static DebugContext create(OptionValues options, PrintStream logStream, DebugHandlersFactory factory) {
return new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, logStream, Immutable.create(options), Collections.singletonList(factory));
}
Creates a DebugContext
based on a given set of option values and factories
. The DebugHandlersFactory.LOADER
can be used for the latter. /**
* Creates a {@link DebugContext} based on a given set of option values and {@code factories}.
* The {@link DebugHandlersFactory#LOADER} can be used for the latter.
*/
public static DebugContext create(OptionValues options, Description description, Iterable<DebugHandlersFactory> factories) {
return new DebugContext(description, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, Immutable.create(options), factories);
}
Creates a DebugContext
. /**
* Creates a {@link DebugContext}.
*/
public static DebugContext create(OptionValues options, Description description, GlobalMetrics globalMetrics, PrintStream logStream, Iterable<DebugHandlersFactory> factories) {
return new DebugContext(description, globalMetrics, logStream, Immutable.create(options), factories);
}
private DebugContext(Description description, GlobalMetrics globalMetrics, PrintStream logStream, Immutable immutable, Iterable<DebugHandlersFactory> factories) {
this.immutable = immutable;
this.description = description;
this.globalMetrics = globalMetrics;
if (immutable.scopesEnabled) {
OptionValues options = immutable.options;
List<DebugDumpHandler> dumpHandlers = new ArrayList<>();
List<DebugVerifyHandler> verifyHandlers = new ArrayList<>();
for (DebugHandlersFactory factory : factories) {
for (DebugHandler handler : factory.createHandlers(options)) {
if (handler instanceof DebugDumpHandler) {
dumpHandlers.add((DebugDumpHandler) handler);
} else {
assert handler instanceof DebugVerifyHandler;
verifyHandlers.add((DebugVerifyHandler) handler);
}
}
}
currentConfig = new DebugConfigImpl(options, logStream, dumpHandlers, verifyHandlers);
currentScope = new ScopeImpl(this, Thread.currentThread());
currentScope.updateFlags(currentConfig);
metricsEnabled = true;
} else {
metricsEnabled = immutable.hasUnscopedMetrics() || immutable.listMetrics;
}
}
public Path getDumpPath(String extension, boolean createMissingDirectory) {
try {
String id = description == null ? null : description.identifier;
String label = description == null ? null : description.getLabel();
Path result = PathUtilities.createUnique(immutable.options, DumpPath, id, label, extension, createMissingDirectory);
if (ShowDumpFiles.getValue(immutable.options)) {
TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString());
}
return result;
} catch (IOException ex) {
throw rethrowSilently(RuntimeException.class, ex);
}
}
A special dump level that indicates the dumping machinery is enabled but no dumps will be
produced except through other options.
/**
* A special dump level that indicates the dumping machinery is enabled but no dumps will be
* produced except through other options.
*/
public static final int ENABLED_LEVEL = 0;
Basic debug level.
For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier,
after mid tier, after low tier.
LIR dumping: After LIR generation, after each pre-allocation, allocation and post allocation
stage, and after code installation.
/**
* Basic debug level.
*
* For HIR dumping, only ~5 graphs per method: after parsing, after inlining, after high tier,
* after mid tier, after low tier.
*
* LIR dumping: After LIR generation, after each pre-allocation, allocation and post allocation
* stage, and after code installation.
*/
public static final int BASIC_LEVEL = 1;
Informational debug level.
HIR dumping: One graph after each applied top-level phase.
LIR dumping: After each applied phase.
/**
* Informational debug level.
*
* HIR dumping: One graph after each applied top-level phase.
*
* LIR dumping: After each applied phase.
*/
public static final int INFO_LEVEL = 2;
Verbose debug level.
HIR dumping: One graph after each phase (including sub phases).
LIR dumping: After each phase including sub phases.
/**
* Verbose debug level.
*
* HIR dumping: One graph after each phase (including sub phases).
*
* LIR dumping: After each phase including sub phases.
*/
public static final int VERBOSE_LEVEL = 3;
Detailed debug level.
HIR dumping: Graphs within phases where interesting for a phase, max ~5 per phase.
LIR dumping: Dump CFG within phases where interesting.
/**
* Detailed debug level.
*
* HIR dumping: Graphs within phases where interesting for a phase, max ~5 per phase.
*
* LIR dumping: Dump CFG within phases where interesting.
*/
public static final int DETAILED_LEVEL = 4;
Very detailed debug level.
HIR dumping: Graphs per node granularity graph change (before/after change).
LIR dumping: Intermediate CFGs of phases where interesting.
/**
* Very detailed debug level.
*
* HIR dumping: Graphs per node granularity graph change (before/after change).
*
* LIR dumping: Intermediate CFGs of phases where interesting.
*/
public static final int VERY_DETAILED_LEVEL = 5;
public boolean isDumpEnabled(int dumpLevel) {
return currentScope != null && currentScope.isDumpEnabled(dumpLevel);
}
Determines if verification is enabled for any JavaMethod
in the current scope. See Also:
/**
* Determines if verification is enabled for any {@link JavaMethod} in the current scope.
*
* @see DebugContext#verify(Object, String)
*/
public boolean isVerifyEnabledForMethod() {
if (currentScope == null) {
return false;
}
if (currentConfig == null) {
return false;
}
return currentConfig.isVerifyEnabledForMethod(currentScope);
}
Determines if verification is enabled in the current scope.
See Also: - verify.verify(Object, String)
/**
* Determines if verification is enabled in the current scope.
*
* @see DebugContext#verify(Object, String)
*/
public boolean isVerifyEnabled() {
return currentScope != null && currentScope.isVerifyEnabled();
}
public boolean isCountEnabled() {
return currentScope != null && currentScope.isCountEnabled();
}
public boolean isTimeEnabled() {
return currentScope != null && currentScope.isTimeEnabled();
}
public boolean isMemUseTrackingEnabled() {
return currentScope != null && currentScope.isMemUseTrackingEnabled();
}
public boolean isDumpEnabledForMethod() {
if (currentConfig == null) {
return false;
}
return currentConfig.isDumpEnabledForMethod(currentScope);
}
public boolean isLogEnabledForMethod() {
if (currentScope == null) {
return false;
}
if (currentConfig == null) {
return false;
}
return currentConfig.isLogEnabledForMethod(currentScope);
}
public boolean isLogEnabled() {
return currentScope != null && isLogEnabled(BASIC_LEVEL);
}
public boolean isLogEnabled(int logLevel) {
return currentScope != null && currentScope.isLogEnabled(logLevel);
}
Gets a string composed of the names in the current nesting of debug scopes separated by '.'
. /**
* Gets a string composed of the names in the current nesting of debug
* {@linkplain #scope(Object) scopes} separated by {@code '.'}.
*/
public String getCurrentScopeName() {
if (currentScope != null) {
return currentScope.getQualifiedName();
} else {
return "";
}
}
Creates and enters a new debug scope which will be a child of the current debug scope.
It is recommended to use the try-with-resource statement for managing entering and leaving
debug scopes. For example:
try (Scope s = Debug.scope("InliningGraph", inlineeGraph)) {
...
} catch (Throwable e) {
throw Debug.handle(e);
}
The name
argument is subject to the following type based conversion before having Object.toString()
called on it: Type | Conversion
------------------+-----------------
java.lang.Class | arg.getSimpleName()
|
Params: - name – the name of the new scope
- contextObjects – an array of object to be appended to the
current debug context
Throws: - Throwable – used to enforce a catch block.
Returns: the scope entered by this method which will be exited when its Scope.close()
method is called
/**
* Creates and enters a new debug scope which will be a child of the current debug scope.
* <p>
* It is recommended to use the try-with-resource statement for managing entering and leaving
* debug scopes. For example:
*
* <pre>
* try (Scope s = Debug.scope("InliningGraph", inlineeGraph)) {
* ...
* } catch (Throwable e) {
* throw Debug.handle(e);
* }
* </pre>
*
* The {@code name} argument is subject to the following type based conversion before having
* {@link Object#toString()} called on it:
*
* <pre>
* Type | Conversion
* ------------------+-----------------
* java.lang.Class | arg.getSimpleName()
* |
* </pre>
*
* @param name the name of the new scope
* @param contextObjects an array of object to be appended to the {@linkplain #context()
* current} debug context
* @throws Throwable used to enforce a catch block.
* @return the scope entered by this method which will be exited when its {@link Scope#close()}
* method is called
*/
public DebugContext.Scope scope(Object name, Object[] contextObjects) throws Throwable {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null, contextObjects);
} else {
return null;
}
}
Similar to scope(Object, Object[])
but without context objects. Therefore the catch block can be omitted. See Also:
/**
* Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch
* block can be omitted.
*
* @see #scope(Object, Object[])
*/
public DebugContext.Scope scope(Object name) {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null);
} else {
return null;
}
}
private final Invariants invariants = Assertions.assertionsEnabled() ? new Invariants() : null;
static StackTraceElement[] getStackTrace(Thread thread) {
return thread.getStackTrace();
}
Utility for enforcing DebugContext
invariants via assertions. /**
* Utility for enforcing {@link DebugContext} invariants via assertions.
*/
static class Invariants {
private final Thread thread;
private final StackTraceElement[] origin;
Invariants() {
thread = Thread.currentThread();
origin = getStackTrace(thread);
}
boolean checkNoConcurrentAccess() {
Thread currentThread = Thread.currentThread();
if (currentThread != thread) {
Formatter buf = new Formatter();
buf.format("Thread local %s object was created on thread %s but is being accessed by thread %s. The most likely cause is " +
"that the object is being retrieved from a non-thread-local cache.",
DebugContext.class.getName(), thread, currentThread);
int debugContextConstructors = 0;
boolean addedHeader = false;
for (StackTraceElement e : origin) {
if (e.getMethodName().equals("<init>") && e.getClassName().equals(DebugContext.class.getName())) {
debugContextConstructors++;
} else if (debugContextConstructors != 0) {
if (!addedHeader) {
addedHeader = true;
buf.format(" The object was instantiated here:");
}
// Distinguish from assertion stack trace by using double indent and
// "in" instead of "at" prefix.
buf.format("%n\t\tin %s", e);
}
}
if (addedHeader) {
buf.format("%n");
}
throw new AssertionError(buf.toString());
}
return true;
}
}
boolean checkNoConcurrentAccess() {
assert invariants == null || invariants.checkNoConcurrentAccess();
return true;
}
private DebugContext.Scope enterScope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) {
assert checkNoConcurrentAccess();
currentScope = currentScope.scope(name, sandboxConfig, newContextObjects);
return currentScope;
}
Params: - context – an object to be appended to the current debug context
See Also: - scope(Object, Object[])
/**
* @see #scope(Object, Object[])
* @param context an object to be appended to the {@linkplain #context() current} debug context
*/
public DebugContext.Scope scope(Object name, Object context) throws Throwable {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null, context);
} else {
return null;
}
}
Params: See Also: - scope(Object, Object[])
/**
* @see #scope(Object, Object[])
* @param context1 first object to be appended to the {@linkplain #context() current} debug
* context
* @param context2 second object to be appended to the {@linkplain #context() current} debug
* context
*/
public DebugContext.Scope scope(Object name, Object context1, Object context2) throws Throwable {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null, context1, context2);
} else {
return null;
}
}
Params: See Also: - scope(Object, Object[])
/**
* @see #scope(Object, Object[])
* @param context1 first object to be appended to the {@linkplain #context() current} debug
* context
* @param context2 second object to be appended to the {@linkplain #context() current} debug
* context
* @param context3 third object to be appended to the {@linkplain #context() current} debug
* context
*/
public DebugContext.Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null, context1, context2, context3);
} else {
return null;
}
}
Create an unnamed scope that appends some context to the current scope.
Params: - context – an object to be appended to the current debug context
/**
* Create an unnamed scope that appends some context to the current scope.
*
* @param context an object to be appended to the {@linkplain #context() current} debug context
*/
public DebugContext.Scope withContext(Object context) throws Throwable {
if (currentScope != null) {
return enterScope("", null, context);
} else {
return null;
}
}
Creates and enters a new debug scope which will be disjoint from the current debug scope.
It is recommended to use the try-with-resource statement for managing entering and leaving
debug scopes. For example:
try (Scope s = Debug.sandbox("CompilingStub", null, stubGraph)) {
...
} catch (Throwable e) {
throw Debug.handle(e);
}
Params: - name – the name of the new scope
- config – the debug configuration to use for the new scope or
null
to disable the scoping mechanism within the sandbox scope - context – objects to be appended to the current debug context
Returns: the scope entered by this method which will be exited when its Scope.close()
method is called
/**
* Creates and enters a new debug scope which will be disjoint from the current debug scope.
* <p>
* It is recommended to use the try-with-resource statement for managing entering and leaving
* debug scopes. For example:
*
* <pre>
* try (Scope s = Debug.sandbox("CompilingStub", null, stubGraph)) {
* ...
* } catch (Throwable e) {
* throw Debug.handle(e);
* }
* </pre>
*
* @param name the name of the new scope
* @param config the debug configuration to use for the new scope or {@code null} to disable the
* scoping mechanism within the sandbox scope
* @param context objects to be appended to the {@linkplain #context() current} debug context
* @return the scope entered by this method which will be exited when its {@link Scope#close()}
* method is called
*/
public DebugContext.Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable {
if (config == null) {
return disable();
}
if (currentScope != null) {
return enterScope(name, config, context);
} else {
return null;
}
}
Determines if scopes are enabled and this context is in a non-top-level scope.
/**
* Determines if scopes are enabled and this context is in a non-top-level scope.
*/
public boolean inNestedScope() {
if (immutable.scopesEnabled) {
if (currentScope == null) {
// In an active DisabledScope
return true;
}
return !currentScope.isTopLevel();
} else {
return false;
}
}
class DisabledScope implements DebugContext.Scope {
final boolean savedMetricsEnabled;
final ScopeImpl savedScope;
final DebugConfigImpl savedConfig;
DisabledScope() {
this.savedMetricsEnabled = metricsEnabled;
this.savedScope = currentScope;
this.savedConfig = currentConfig;
metricsEnabled = false;
currentScope = null;
currentConfig = null;
}
@Override
public String getQualifiedName() {
return "";
}
@Override
public Iterable<Object> getCurrentContext() {
return Collections.emptyList();
}
@Override
public void close() {
metricsEnabled = savedMetricsEnabled;
currentScope = savedScope;
currentConfig = savedConfig;
lastClosedScope = this;
}
}
Disables all metrics and scope related functionality until close()
is called on the returned object. /**
* Disables all metrics and scope related functionality until {@code close()} is called on the
* returned object.
*/
public DebugContext.Scope disable() {
if (currentScope != null) {
return new DisabledScope();
} else {
return null;
}
}
public DebugContext.Scope forceLog() throws Throwable {
if (currentConfig != null) {
ArrayList<Object> context = new ArrayList<>();
for (Object obj : context()) {
context.add(obj);
}
DebugConfigImpl config = new DebugConfigImpl(new OptionValues(currentConfig.getOptions(), DebugOptions.Log, ":1000"));
return sandbox("forceLog", config, context.toArray());
}
return null;
}
Opens a scope in which exception interception is disabled. The current state of interception is restored when DebugCloseable.close()
is called on the returned object. This is particularly useful to suppress extraneous output in JUnit tests that are expected to throw an exception. /**
* Opens a scope in which exception
* {@linkplain DebugConfig#interceptException(DebugContext, Throwable) interception} is
* disabled. The current state of interception is restored when {@link DebugCloseable#close()}
* is called on the returned object.
*
* This is particularly useful to suppress extraneous output in JUnit tests that are expected to
* throw an exception.
*/
public DebugCloseable disableIntercept() {
if (currentScope != null) {
return currentScope.disableIntercept();
}
return null;
}
Handles an exception in the context of the debug scope just exited. The just exited scope must have the current scope as its parent which will be the case if the try-with-resource pattern recommended by scope(Object)
and sandbox(CharSequence, DebugConfig, Object...)
is used See Also:
/**
* Handles an exception in the context of the debug scope just exited. The just exited scope
* must have the current scope as its parent which will be the case if the try-with-resource
* pattern recommended by {@link #scope(Object)} and
* {@link #sandbox(CharSequence, DebugConfig, Object...)} is used
*
* @see #scope(Object, Object[])
* @see #sandbox(CharSequence, DebugConfig, Object...)
*/
public RuntimeException handle(Throwable exception) {
if (currentScope != null) {
return currentScope.handle(exception);
} else {
if (exception instanceof Error) {
throw (Error) exception;
}
if (exception instanceof RuntimeException) {
throw (RuntimeException) exception;
}
throw new RuntimeException(exception);
}
}
public void log(String msg) {
log(BASIC_LEVEL, msg);
}
Prints a message to the current debug scope's logging stream if logging is enabled.
Params: - msg – the message to log
/**
* Prints a message to the current debug scope's logging stream if logging is enabled.
*
* @param msg the message to log
*/
public void log(int logLevel, String msg) {
if (currentScope != null) {
currentScope.log(logLevel, msg);
}
}
public void log(String format, Object arg) {
log(BASIC_LEVEL, format, arg);
}
Prints a message to the current debug scope's logging stream if logging is enabled.
Params: - format – a format string
- arg – the argument referenced by the format specifiers in
format
/**
* Prints a message to the current debug scope's logging stream if logging is enabled.
*
* @param format a format string
* @param arg the argument referenced by the format specifiers in {@code format}
*/
public void log(int logLevel, String format, Object arg) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg);
}
}
public void log(String format, int arg) {
log(BASIC_LEVEL, format, arg);
}
Prints a message to the current debug scope's logging stream if logging is enabled.
Params: - format – a format string
- arg – the argument referenced by the format specifiers in
format
/**
* Prints a message to the current debug scope's logging stream if logging is enabled.
*
* @param format a format string
* @param arg the argument referenced by the format specifiers in {@code format}
*/
public void log(int logLevel, String format, int arg) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg);
}
}
public void log(String format, Object arg1, Object arg2) {
log(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, Object arg2) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2);
}
}
public void log(String format, int arg1, Object arg2) {
log(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, int arg1, Object arg2) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2);
}
}
public void log(String format, Object arg1, int arg2) {
log(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, int arg2) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2);
}
}
public void log(String format, int arg1, int arg2) {
log(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, int arg1, int arg2) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3) {
log(BASIC_LEVEL, format, arg1, arg2, arg3);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3);
}
}
public void log(String format, int arg1, int arg2, int arg3) {
log(BASIC_LEVEL, format, arg1, arg2, arg3);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, int arg1, int arg2, int arg3) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
See Also: - log(int, String, Object)
/**
* @see #log(int, String, Object)
*/
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
}
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
}
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
}
}
public void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
log(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
}
public void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) {
if (currentScope != null) {
currentScope.log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
}
}
public void logv(String format, Object... args) {
logv(BASIC_LEVEL, format, args);
}
Prints a message to the current debug scope's logging stream. This method must only be called if debugging scopes are enabled as it incurs allocation at the call site. If possible, call one of the other log()
methods in this class that take a fixed number of parameters. Params: - format – a format string
- args – the arguments referenced by the format specifiers in
format
/**
* Prints a message to the current debug scope's logging stream. This method must only be called
* if debugging scopes are {@linkplain DebugContext#areScopesEnabled() enabled} as it incurs
* allocation at the call site. If possible, call one of the other {@code log()} methods in this
* class that take a fixed number of parameters.
*
* @param format a format string
* @param args the arguments referenced by the format specifiers in {@code format}
*/
public void logv(int logLevel, String format, Object... args) {
if (currentScope == null) {
throw new InternalError("Use of Debug.logv() must be guarded by a test of Debug.isEnabled()");
}
currentScope.log(logLevel, format, args);
}
This override exists to catch cases when log(String, Object)
is called with one argument bound to a varargs method parameter. It will bind to this method instead of the single arg variant and produce a deprecation warning instead of silently wrapping the Object[] inside of another Object[]. /**
* This override exists to catch cases when {@link #log(String, Object)} is called with one
* argument bound to a varargs method parameter. It will bind to this method instead of the
* single arg variant and produce a deprecation warning instead of silently wrapping the
* Object[] inside of another Object[].
*/
@Deprecated
public void log(String format, Object[] args) {
assert false : "shouldn't use this";
log(BASIC_LEVEL, format, args);
}
This override exists to catch cases when log(int, String, Object)
is called with one argument bound to a varargs method parameter. It will bind to this method instead of the single arg variant and produce a deprecation warning instead of silently wrapping the Object[] inside of another Object[]. /**
* This override exists to catch cases when {@link #log(int, String, Object)} is called with one
* argument bound to a varargs method parameter. It will bind to this method instead of the
* single arg variant and produce a deprecation warning instead of silently wrapping the
* Object[] inside of another Object[].
*/
@Deprecated
public void log(int logLevel, String format, Object[] args) {
assert false : "shouldn't use this";
logv(logLevel, format, args);
}
Forces an unconditional dump. This method exists mainly for debugging. It can also be used to
force a graph dump from IDEs that support invoking a Java method while at a breakpoint.
/**
* Forces an unconditional dump. This method exists mainly for debugging. It can also be used to
* force a graph dump from IDEs that support invoking a Java method while at a breakpoint.
*/
public void forceDump(Object object, String format, Object... args) {
DebugConfig config = currentConfig;
Collection<DebugDumpHandler> dumpHandlers;
boolean closeAfterDump;
if (config != null) {
dumpHandlers = config.dumpHandlers();
closeAfterDump = false;
} else {
OptionValues options = getOptions();
dumpHandlers = new ArrayList<>();
for (DebugHandlersFactory factory : DebugHandlersFactory.LOADER) {
for (DebugHandler handler : factory.createHandlers(options)) {
if (handler instanceof DebugDumpHandler) {
dumpHandlers.add((DebugDumpHandler) handler);
}
}
}
closeAfterDump = true;
}
for (DebugDumpHandler dumpHandler : dumpHandlers) {
dumpHandler.dump(this, object, format, args);
if (closeAfterDump) {
dumpHandler.close();
}
}
}
public void dump(int dumpLevel, Object object, String msg) {
if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
currentScope.dump(dumpLevel, object, msg);
}
}
public void dump(int dumpLevel, Object object, String format, Object arg) {
if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
currentScope.dump(dumpLevel, object, format, arg);
}
}
public void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2) {
if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
currentScope.dump(dumpLevel, object, format, arg1, arg2);
}
}
public void dump(int dumpLevel, Object object, String format, Object arg1, Object arg2, Object arg3) {
if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
currentScope.dump(dumpLevel, object, format, arg1, arg2, arg3);
}
}
This override exists to catch cases when dump(int, Object, String, Object)
is called with one argument bound to a varargs method parameter. It will bind to this method instead of the single arg variant and produce a deprecation warning instead of silently wrapping the Object[] inside of another Object[]. /**
* This override exists to catch cases when {@link #dump(int, Object, String, Object)} is called
* with one argument bound to a varargs method parameter. It will bind to this method instead of
* the single arg variant and produce a deprecation warning instead of silently wrapping the
* Object[] inside of another Object[].
*/
@Deprecated
public void dump(int dumpLevel, Object object, String format, Object[] args) {
assert false : "shouldn't use this";
if (currentScope != null && currentScope.isDumpEnabled(dumpLevel)) {
currentScope.dump(dumpLevel, object, format, args);
}
}
Calls all DebugVerifyHandler
s in the current config to perform verification on a given object. Params: - object – object to verify
- message – description of verification context
See Also:
/**
* Calls all {@link DebugVerifyHandler}s in the current {@linkplain #getConfig() config} to
* perform verification on a given object.
*
* @param object object to verify
* @param message description of verification context
*
* @see DebugVerifyHandler#verify
*/
public void verify(Object object, String message) {
if (currentScope != null && currentScope.isVerifyEnabled()) {
currentScope.verify(object, message);
}
}
Calls all DebugVerifyHandler
s in the current config to perform verification on a given object. Params: - object – object to verify
- format – a format string for the description of the verification context
- arg – the argument referenced by the format specifiers in
format
See Also:
/**
* Calls all {@link DebugVerifyHandler}s in the current {@linkplain #getConfig() config} to
* perform verification on a given object.
*
* @param object object to verify
* @param format a format string for the description of the verification context
* @param arg the argument referenced by the format specifiers in {@code format}
*
* @see DebugVerifyHandler#verify
*/
public void verify(Object object, String format, Object arg) {
if (currentScope != null && currentScope.isVerifyEnabled()) {
currentScope.verify(object, format, arg);
}
}
This override exists to catch cases when verify(Object, String, Object)
is called with one argument bound to a varargs method parameter. It will bind to this method instead of the single arg variant and produce a deprecation warning instead of silently wrapping the Object[] inside of another Object[]. /**
* This override exists to catch cases when {@link #verify(Object, String, Object)} is called
* with one argument bound to a varargs method parameter. It will bind to this method instead of
* the single arg variant and produce a deprecation warning instead of silently wrapping the
* Object[] inside of another Object[].
*/
@Deprecated
public void verify(Object object, String format, Object[] args) {
assert false : "shouldn't use this";
if (currentScope != null && currentScope.isVerifyEnabled()) {
currentScope.verify(object, format, args);
}
}
Opens a new indentation level (by adding some spaces) based on the current indentation level. This should be used in a try-with-resources pattern. See Also: Returns: an object that reverts to the current indentation level when closed or null if debugging is disabled
/**
* Opens a new indentation level (by adding some spaces) based on the current indentation level.
* This should be used in a {@linkplain Indent try-with-resources} pattern.
*
* @return an object that reverts to the current indentation level when
* {@linkplain Indent#close() closed} or null if debugging is disabled
* @see #logAndIndent(int, String)
* @see #logAndIndent(int, String, Object)
*/
public Indent indent() {
if (currentScope != null) {
return currentScope.pushIndentLogger();
}
return null;
}
public Indent logAndIndent(String msg) {
return logAndIndent(BASIC_LEVEL, msg);
}
A convenience function which combines log(String)
and indent()
. Params: - msg – the message to log
Returns: an object that reverts to the current indentation level when closed or null if debugging is disabled
/**
* A convenience function which combines {@link #log(String)} and {@link #indent()}.
*
* @param msg the message to log
* @return an object that reverts to the current indentation level when
* {@linkplain Indent#close() closed} or null if debugging is disabled
*/
public Indent logAndIndent(int logLevel, String msg) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, msg);
}
return null;
}
public Indent logAndIndent(String format, Object arg) {
return logAndIndent(BASIC_LEVEL, format, arg);
}
A convenience function which combines log(String, Object)
and indent()
. Params: - format – a format string
- arg – the argument referenced by the format specifiers in
format
Returns: an object that reverts to the current indentation level when closed or null if debugging is disabled
/**
* A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
*
* @param format a format string
* @param arg the argument referenced by the format specifiers in {@code format}
* @return an object that reverts to the current indentation level when
* {@linkplain Indent#close() closed} or null if debugging is disabled
*/
public Indent logAndIndent(int logLevel, String format, Object arg) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg);
}
return null;
}
public Indent logAndIndent(String format, int arg) {
return logAndIndent(BASIC_LEVEL, format, arg);
}
A convenience function which combines log(String, Object)
and indent()
. Params: - format – a format string
- arg – the argument referenced by the format specifiers in
format
Returns: an object that reverts to the current indentation level when closed or null if debugging is disabled
/**
* A convenience function which combines {@link #log(String, Object)} and {@link #indent()}.
*
* @param format a format string
* @param arg the argument referenced by the format specifiers in {@code format}
* @return an object that reverts to the current indentation level when
* {@linkplain Indent#close() closed} or null if debugging is disabled
*/
public Indent logAndIndent(int logLevel, String format, int arg) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg);
}
return null;
}
public Indent logAndIndent(String format, int arg1, Object arg2) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, int arg1, Object arg2) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, int arg2) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, int arg2) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2);
}
return null;
}
public Indent logAndIndent(String format, int arg1, int arg2) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, int arg1, int arg2) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, Object arg2) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
}
return null;
}
public Indent logAndIndent(String format, int arg1, int arg2, int arg3) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, int arg1, int arg2, int arg3) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, int arg2, int arg3) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, int arg2, int arg3) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5);
}
return null;
}
public Indent logAndIndent(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
return logAndIndent(BASIC_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6);
}
See Also: - logAndIndent(int, String, Object)
/**
* @see #logAndIndent(int, String, Object)
*/
public Indent logAndIndent(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
if (currentScope != null && isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6);
}
return null;
}
A convenience function which combines logv(int, String, Object...)
and indent()
. Params: - format – a format string
- args – the arguments referenced by the format specifiers in
format
Returns: an object that reverts to the current indentation level when closed or null if debugging is disabled
/**
* A convenience function which combines {@link #logv(int, String, Object...)} and
* {@link #indent()}.
*
* @param format a format string
* @param args the arguments referenced by the format specifiers in {@code format}
* @return an object that reverts to the current indentation level when
* {@linkplain Indent#close() closed} or null if debugging is disabled
*/
public Indent logvAndIndent(int logLevel, String format, Object... args) {
if (currentScope != null) {
if (isLogEnabled(logLevel)) {
return logvAndIndentInternal(logLevel, format, args);
}
return null;
}
throw new InternalError("Use of Debug.logvAndIndent() must be guarded by a test of Debug.isEnabled()");
}
private Indent logvAndIndentInternal(int logLevel, String format, Object... args) {
assert currentScope != null && isLogEnabled(logLevel) : "must have checked Debug.isLogEnabled()";
currentScope.log(logLevel, format, args);
return currentScope.pushIndentLogger();
}
This override exists to catch cases when logAndIndent(String, Object)
is called with one argument bound to a varargs method parameter. It will bind to this method instead of the single arg variant and produce a deprecation warning instead of silently wrapping the Object[] inside of another Object[]. /**
* This override exists to catch cases when {@link #logAndIndent(String, Object)} is called with
* one argument bound to a varargs method parameter. It will bind to this method instead of the
* single arg variant and produce a deprecation warning instead of silently wrapping the
* Object[] inside of another Object[].
*/
@Deprecated
public void logAndIndent(String format, Object[] args) {
assert false : "shouldn't use this";
logAndIndent(BASIC_LEVEL, format, args);
}
This override exists to catch cases when logAndIndent(int, String, Object)
is called with one argument bound to a varargs method parameter. It will bind to this method instead of the single arg variant and produce a deprecation warning instead of silently wrapping the Object[] inside of another Object[]. /**
* This override exists to catch cases when {@link #logAndIndent(int, String, Object)} is called
* with one argument bound to a varargs method parameter. It will bind to this method instead of
* the single arg variant and produce a deprecation warning instead of silently wrapping the
* Object[] inside of another Object[].
*/
@Deprecated
public void logAndIndent(int logLevel, String format, Object[] args) {
assert false : "shouldn't use this";
logvAndIndent(logLevel, format, args);
}
public Iterable<Object> context() {
if (currentScope != null) {
return currentScope.getCurrentContext();
} else {
return Collections.emptyList();
}
}
@SuppressWarnings("unchecked")
public <T> List<T> contextSnapshot(Class<T> clazz) {
if (currentScope != null) {
List<T> result = new ArrayList<>();
for (Object o : context()) {
if (clazz.isInstance(o)) {
result.add((T) o);
}
}
return result;
} else {
return Collections.emptyList();
}
}
Searches the current debug scope, bottom up, for a context object that is an instance of a
given type. The first such object found is returned.
/**
* Searches the current debug scope, bottom up, for a context object that is an instance of a
* given type. The first such object found is returned.
*/
@SuppressWarnings("unchecked")
public <T> T contextLookup(Class<T> clazz) {
if (currentScope != null) {
for (Object o : context()) {
if (clazz.isInstance(o)) {
return ((T) o);
}
}
}
return null;
}
Searches the current debug scope, top down, for a context object that is an instance of a
given type. The first such object found is returned.
/**
* Searches the current debug scope, top down, for a context object that is an instance of a
* given type. The first such object found is returned.
*/
@SuppressWarnings("unchecked")
public <T> T contextLookupTopdown(Class<T> clazz) {
if (currentScope != null) {
T found = null;
for (Object o : context()) {
if (clazz.isInstance(o)) {
found = (T) o;
}
}
return found;
}
return null;
}
Creates a memory use tracker. /**
* Creates a {@linkplain MemUseTrackerKey memory use tracker}.
*/
public static MemUseTrackerKey memUseTracker(CharSequence name) {
return createMemUseTracker("%s", name, null);
}
Creates a debug memory use tracker. Invoking this method is equivalent to:
Debug.memUseTracker(format, arg, null)
except that the string formatting only happens if mem tracking is enabled.
See Also: - counter(String, Object, Object)
/**
* Creates a debug memory use tracker. Invoking this method is equivalent to:
*
* <pre>
* Debug.memUseTracker(format, arg, null)
* </pre>
*
* except that the string formatting only happens if mem tracking is enabled.
*
* @see #counter(String, Object, Object)
*/
public static MemUseTrackerKey memUseTracker(String format, Object arg) {
return createMemUseTracker(format, arg, null);
}
Creates a debug memory use tracker. Invoking this method is equivalent to:
Debug.memUseTracker(String.format(format, arg1, arg2))
except that the string formatting only happens if memory use tracking is enabled. In addition, each argument is subject to the following type based conversion before being passed as an argument to String.format(String, Object...)
: Type | Conversion
------------------+-----------------
java.lang.Class | arg.getSimpleName()
|
See Also:
/**
* Creates a debug memory use tracker. Invoking this method is equivalent to:
*
* <pre>
* Debug.memUseTracker(String.format(format, arg1, arg2))
* </pre>
*
* except that the string formatting only happens if memory use tracking is enabled. In
* addition, each argument is subject to the following type based conversion before being passed
* as an argument to {@link String#format(String, Object...)}:
*
* <pre>
* Type | Conversion
* ------------------+-----------------
* java.lang.Class | arg.getSimpleName()
* |
* </pre>
*
* @see #memUseTracker(CharSequence)
*/
public static MemUseTrackerKey memUseTracker(String format, Object arg1, Object arg2) {
return createMemUseTracker(format, arg1, arg2);
}
private static MemUseTrackerKey createMemUseTracker(String format, Object arg1, Object arg2) {
return new MemUseTrackerKeyImpl(format, arg1, arg2);
}
Creates a counter. /**
* Creates a {@linkplain CounterKey counter}.
*/
public static CounterKey counter(CharSequence name) {
return createCounter("%s", name, null);
}
Gets a tally of the metric values in this context and a given tally.
Params: - tally – the tally to which the metrics should be added
Returns: a tally of the metric values in this context and tally
. This will be tally
if this context has no metric values or tally
is wide enough to hold all the metric values in this context otherwise it will be a new array.
/**
* Gets a tally of the metric values in this context and a given tally.
*
* @param tally the tally to which the metrics should be added
* @return a tally of the metric values in this context and {@code tally}. This will be
* {@code tally} if this context has no metric values or {@code tally} is wide enough to
* hold all the metric values in this context otherwise it will be a new array.
*/
public long[] addValuesTo(long[] tally) {
if (metricValues == null) {
return tally;
}
if (tally == null) {
return metricValues.clone();
} else if (metricValues.length >= tally.length) {
long[] newTally = metricValues.clone();
for (int i = 0; i < tally.length; i++) {
newTally[i] += tally[i];
}
return newTally;
} else {
for (int i = 0; i < metricValues.length; i++) {
tally[i] += metricValues[i];
}
return tally;
}
}
Creates and returns a sorted map from metric names to their values in values
. Params: - values – values for metrics in the
KeyRegistry
.
/**
* Creates and returns a sorted map from metric names to their values in {@code values}.
*
* @param values values for metrics in the {@link KeyRegistry}.
*/
public static EconomicMap<MetricKey, Long> convertValuesToKeyValueMap(long[] values) {
List<MetricKey> keys = KeyRegistry.getKeys();
Collections.sort(keys, MetricKey.NAME_COMPARATOR);
EconomicMap<MetricKey, Long> res = EconomicMap.create(keys.size());
for (MetricKey key : keys) {
int index = ((AbstractKey) key).getIndex();
if (index >= values.length) {
res.put(key, 0L);
} else {
res.put(key, values[index]);
}
}
return res;
}
void setMetricValue(int keyIndex, long l) {
ensureMetricValuesSize(keyIndex);
metricValues[keyIndex] = l;
}
long getMetricValue(int keyIndex) {
if (metricValues == null || metricValues.length <= keyIndex) {
return 0L;
}
return metricValues[keyIndex];
}
private void ensureMetricValuesSize(int index) {
if (metricValues == null) {
metricValues = new long[index + 1];
}
if (metricValues.length <= index) {
metricValues = Arrays.copyOf(metricValues, index + 1);
}
}
public static String applyFormattingFlagsAndWidth(String s, int flags, int width) {
if (flags == 0 && width < 0) {
return s;
}
StringBuilder sb = new StringBuilder(s);
// apply width and justification
int len = sb.length();
if (len < width) {
for (int i = 0; i < width - len; i++) {
if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) {
sb.append(' ');
} else {
sb.insert(0, ' ');
}
}
}
String res = sb.toString();
if ((flags & UPPERCASE) == UPPERCASE) {
res = res.toUpperCase();
}
return res;
}
Creates a debug counter. Invoking this method is equivalent to:
Debug.counter(format, arg, null)
except that the string formatting only happens if count is enabled.
See Also: - counter(String, Object, Object)
/**
* Creates a debug counter. Invoking this method is equivalent to:
*
* <pre>
* Debug.counter(format, arg, null)
* </pre>
*
* except that the string formatting only happens if count is enabled.
*
* @see #counter(String, Object, Object)
*/
public static CounterKey counter(String format, Object arg) {
return createCounter(format, arg, null);
}
Creates a debug counter. Invoking this method is equivalent to:
Debug.counter(String.format(format, arg1, arg2))
except that the string formatting only happens if count is enabled. In addition, each argument is subject to the following type based conversion before being passed as an argument to String.format(String, Object...)
: Type | Conversion
------------------+-----------------
java.lang.Class | arg.getSimpleName()
|
See Also:
/**
* Creates a debug counter. Invoking this method is equivalent to:
*
* <pre>
* Debug.counter(String.format(format, arg1, arg2))
* </pre>
*
* except that the string formatting only happens if count is enabled. In addition, each
* argument is subject to the following type based conversion before being passed as an argument
* to {@link String#format(String, Object...)}:
*
* <pre>
* Type | Conversion
* ------------------+-----------------
* java.lang.Class | arg.getSimpleName()
* |
* </pre>
*
* @see #counter(CharSequence)
*/
public static CounterKey counter(String format, Object arg1, Object arg2) {
return createCounter(format, arg1, arg2);
}
private static CounterKey createCounter(String format, Object arg1, Object arg2) {
return new CounterKeyImpl(format, arg1, arg2);
}
public DebugConfig getConfig() {
return currentConfig;
}
Creates a timer.
A disabled timer has virtually no overhead.
/**
* Creates a {@linkplain TimerKey timer}.
* <p>
* A disabled timer has virtually no overhead.
*/
public static TimerKey timer(CharSequence name) {
return createTimer("%s", name, null);
}
Creates a debug timer. Invoking this method is equivalent to:
Debug.timer(format, arg, null)
except that the string formatting only happens if timing is enabled.
See Also: - timer(String, Object, Object)
/**
* Creates a debug timer. Invoking this method is equivalent to:
*
* <pre>
* Debug.timer(format, arg, null)
* </pre>
*
* except that the string formatting only happens if timing is enabled.
*
* @see #timer(String, Object, Object)
*/
public static TimerKey timer(String format, Object arg) {
return createTimer(format, arg, null);
}
Creates a debug timer. Invoking this method is equivalent to:
Debug.timer(String.format(format, arg1, arg2))
except that the string formatting only happens if timing is enabled. In addition, each argument is subject to the following type based conversion before being passed as an argument to String.format(String, Object...)
: Type | Conversion
------------------+-----------------
java.lang.Class | arg.getSimpleName()
|
See Also:
/**
* Creates a debug timer. Invoking this method is equivalent to:
*
* <pre>
* Debug.timer(String.format(format, arg1, arg2))
* </pre>
*
* except that the string formatting only happens if timing is enabled. In addition, each
* argument is subject to the following type based conversion before being passed as an argument
* to {@link String#format(String, Object...)}:
*
* <pre>
* Type | Conversion
* ------------------+-----------------
* java.lang.Class | arg.getSimpleName()
* |
* </pre>
*
* @see #timer(CharSequence)
*/
public static TimerKey timer(String format, Object arg1, Object arg2) {
return createTimer(format, arg1, arg2);
}
There are paths where construction of formatted class names are common and the code below is
surprisingly expensive, so compute it once and cache it.
/**
* There are paths where construction of formatted class names are common and the code below is
* surprisingly expensive, so compute it once and cache it.
*/
private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
@Override
protected String computeValue(Class<?> c) {
final String simpleName = c.getSimpleName();
Class<?> enclosingClass = c.getEnclosingClass();
if (enclosingClass != null) {
String prefix = "";
while (enclosingClass != null) {
prefix = enclosingClass.getSimpleName() + "_" + prefix;
enclosingClass = enclosingClass.getEnclosingClass();
}
return prefix + simpleName;
} else {
return simpleName;
}
}
};
public static Object convertFormatArg(Object arg) {
if (arg instanceof Class) {
return formattedClassName.get((Class<?>) arg);
}
return arg;
}
static String formatDebugName(String format, Object arg1, Object arg2) {
return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2));
}
private static TimerKey createTimer(String format, Object arg1, Object arg2) {
return new TimerKeyImpl(format, arg1, arg2);
}
Represents a debug scope entered by DebugContext.scope(Object)
or DebugContext.sandbox(CharSequence, DebugConfig, Object...)
. Leaving the scope is achieved via close()
. /**
* Represents a debug scope entered by {@link DebugContext#scope(Object)} or
* {@link DebugContext#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is
* achieved via {@link #close()}.
*/
public interface Scope extends AutoCloseable {
Gets the names of this scope and its ancestors separated by '.'
. /**
* Gets the names of this scope and its ancestors separated by {@code '.'}.
*/
String getQualifiedName();
Iterable<Object> getCurrentContext();
@Override
void close();
}
boolean isTimerEnabled(TimerKeyImpl key) {
if (!metricsEnabled) {
// Pulling this common case out of `isTimerEnabledSlow`
// gives C1 a better chance to inline this method.
return false;
}
return isTimerEnabledSlow(key);
}
private boolean isTimerEnabledSlow(AbstractKey key) {
if (currentScope != null && currentScope.isTimeEnabled()) {
return true;
}
if (immutable.listMetrics) {
key.ensureInitialized();
}
assert checkNoConcurrentAccess();
EconomicSet<String> unscoped = immutable.unscopedTimers;
return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
}
Determines if a given timer is enabled in the current scope.
/**
* Determines if a given timer is enabled in the current scope.
*/
boolean isCounterEnabled(CounterKeyImpl key) {
if (!metricsEnabled) {
// Pulling this common case out of `isCounterEnabledSlow`
// gives C1 a better chance to inline this method.
return false;
}
return isCounterEnabledSlow(key);
}
private boolean isCounterEnabledSlow(AbstractKey key) {
if (currentScope != null && currentScope.isCountEnabled()) {
return true;
}
if (immutable.listMetrics) {
key.ensureInitialized();
}
assert checkNoConcurrentAccess();
EconomicSet<String> unscoped = immutable.unscopedCounters;
return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
}
boolean isMemUseTrackerEnabled(MemUseTrackerKeyImpl key) {
if (!metricsEnabled) {
// Pulling this common case out of `isMemUseTrackerEnabledSlow`
// gives C1 a better chance to inline this method.
return false;
}
return isMemUseTrackerEnabledSlow(key);
}
private boolean isMemUseTrackerEnabledSlow(AbstractKey key) {
if (currentScope != null && currentScope.isMemUseTrackingEnabled()) {
return true;
}
if (immutable.listMetrics) {
key.ensureInitialized();
}
assert checkNoConcurrentAccess();
EconomicSet<String> unscoped = immutable.unscopedMemUseTrackers;
return unscoped != null && (unscoped.isEmpty() || unscoped.contains(key.getName()));
}
public boolean areMetricsEnabled() {
return metricsEnabled;
}
@Override
public void close() {
closeDumpHandlers(false);
if (description != null) {
printMetrics(description);
}
if (metricsEnabled && metricValues != null && globalMetrics != null) {
globalMetrics.add(this);
}
metricValues = null;
if (sharedChannel != null) {
try {
sharedChannel.realClose();
} catch (IOException ex) {
// ignore.
}
}
}
public void closeDumpHandlers(boolean ignoreErrors) {
if (currentConfig != null) {
currentConfig.closeDumpHandlers(ignoreErrors);
}
}
Records how many times a given method has been compiled.
/**
* Records how many times a given method has been compiled.
*/
private static EconomicMap<Integer, Integer> compilations;
Maintains maximum buffer size used by printMetrics(Description)
to minimize buffer resizing during subsequent calls to this method. /**
* Maintains maximum buffer size used by {@link #printMetrics(Description)} to minimize buffer
* resizing during subsequent calls to this method.
*/
private static int metricsBufSize = 50_000;
Flag that allows the first call to printMetrics(Description)
to delete the file that will be appended to. /**
* Flag that allows the first call to {@link #printMetrics(Description)} to delete the file that
* will be appended to.
*/
private static boolean metricsFileDeleteCheckPerformed;
Prints metric values in this object to the file (if any) specified by DebugOptions.MetricsFile
. /**
* Prints metric values in this object to the file (if any) specified by
* {@link DebugOptions#MetricsFile}.
*/
public void printMetrics(Description desc) {
if (metricValues == null) {
return;
}
String metricsFile = DebugOptions.MetricsFile.getValue(getOptions());
if (metricsFile != null) {
// Use identity to distinguish methods that have been redefined
// or loaded by different class loaders.
Object compilable = desc.compilable;
Integer identity = System.identityHashCode(compilable);
int compilationNr;
synchronized (PRINT_METRICS_LOCK) {
if (!metricsFileDeleteCheckPerformed) {
metricsFileDeleteCheckPerformed = true;
File file = new File(metricsFile);
if (file.exists()) {
// This can return false in case something like /dev/stdout
// is specified. If the file is unwriteable, the file open
// below will fail.
file.delete();
}
}
if (compilations == null) {
compilationNr = 0;
compilations = EconomicMap.create();
} else {
Integer value = compilations.get(identity);
compilationNr = value == null ? 0 : value + 1;
}
compilations.put(identity, compilationNr);
}
// Release the lock while generating the content to reduce contention.
// This means `compilationNr` fields may show up out of order in the file.
ByteArrayOutputStream baos = new ByteArrayOutputStream(metricsBufSize);
PrintStream out = new PrintStream(baos);
if (metricsFile.endsWith(".csv") || metricsFile.endsWith(".CSV")) {
printMetricsCSV(out, compilable, identity, compilationNr, desc.identifier);
} else {
printMetrics(out, compilable, identity, compilationNr, desc.identifier);
}
byte[] content = baos.toByteArray();
Path path = Paths.get(metricsFile);
synchronized (PRINT_METRICS_LOCK) {
metricsBufSize = Math.max(metricsBufSize, content.length);
try {
Files.write(path, content, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (IOException e) {
}
}
}
}
Lock to serialize writes to DebugOptions.MetricsFile
. /**
* Lock to serialize writes to {@link DebugOptions#MetricsFile}.
*/
private static final Object PRINT_METRICS_LOCK = new Object();
Appends metrics in CSV format to out
for a single method compilation. Params: - identity – the identity hash code of
compilable
- compilationNr – where this compilation lies in the ordered sequence of all compilations identified by
identity
- compilationId – the runtime issued identifier for the compilation
/**
* Appends metrics in CSV format to {@code out} for a single method compilation.
*
* @param identity the identity hash code of {@code compilable}
* @param compilationNr where this compilation lies in the ordered sequence of all compilations
* identified by {@code identity}
* @param compilationId the runtime issued identifier for the compilation
*/
private void printMetricsCSV(PrintStream out, Object compilable, Integer identity, int compilationNr, String compilationId) {
String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
String csvFormat = CSVUtil.buildFormatString("%s", "%s", "%d", "%s");
String format = String.format(csvFormat, CSVUtil.Escape.escapeArgs(compilableName, identity, compilationNr, compilationId));
char sep = CSVUtil.SEPARATOR;
format += sep + "%s" + sep + "%s" + sep + "%s";
for (MetricKey key : KeyRegistry.getKeys()) {
int index = ((AbstractKey) key).getIndex();
if (index < metricValues.length) {
Pair<String, String> valueAndUnit = key.toCSVFormat(metricValues[index]);
CSVUtil.Escape.println(out, format, CSVUtil.Escape.escape(key.getName()), valueAndUnit.getLeft(), valueAndUnit.getRight());
}
}
}
Appends metrics in a human readable format to out
for a single method compilation. Params: - identity – the identity hash code of
compilable
- compilationNr – where this compilation lies in the ordered sequence of all compilations identified by
identity
- compilationId – the runtime issued identifier for the compilation
/**
* Appends metrics in a human readable format to {@code out} for a single method compilation.
*
* @param identity the identity hash code of {@code compilable}
* @param compilationNr where this compilation lies in the ordered sequence of all compilations
* identified by {@code identity}
* @param compilationId the runtime issued identifier for the compilation
*/
private void printMetrics(PrintStream out, Object compilable, Integer identity, int compilationNr, String compilationId) {
String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable);
int maxKeyWidth = compilableName.length();
SortedMap<String, String> res = new TreeMap<>();
for (MetricKey key : KeyRegistry.getKeys()) {
int index = ((AbstractKey) key).getIndex();
if (index < metricValues.length && metricValues[index] != 0) {
String name = key.getName();
long value = metricValues[index];
String valueString;
if (key instanceof TimerKey) {
// Report timers in ms
TimerKey timer = (TimerKey) key;
long ms = timer.getTimeUnit().toMillis(value);
if (ms == 0) {
continue;
}
valueString = ms + "ms";
} else {
valueString = String.valueOf(value);
}
res.put(name, valueString);
maxKeyWidth = Math.max(maxKeyWidth, name.length());
}
}
String title = String.format("%s [id:%s compilation:%d compilation_id:%s]", compilableName, identity, compilationNr, compilationId);
out.println(new String(new char[title.length()]).replace('\0', '#'));
out.printf("%s%n", title);
out.println(new String(new char[title.length()]).replace('\0', '~'));
for (Map.Entry<String, String> e : res.entrySet()) {
out.printf("%-" + String.valueOf(maxKeyWidth) + "s = %20s%n", e.getKey(), e.getValue());
}
out.println();
}
@SuppressWarnings({"unused", "unchecked"})
private static <E extends Exception> E rethrowSilently(Class<E> type, Throwable ex) throws E {
throw (E) ex;
}
}