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.HashMap;
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;
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;
final Immutable immutable;
boolean metricsEnabled;
DebugConfigImpl currentConfig;
ScopeImpl currentScope;
CloseableCounter currentTimer;
CloseableCounter currentMemUseTracker;
Scope lastClosedScope;
Throwable lastExceptionThrown;
private IgvDumpChannel sharedChannel;
private GraphOutput<?, ?> parentOutput;
private long[] metricValues;
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;
}
}
public static Map<Object, Object> addVersionProperties(Map<Object, Object> properties) {
return Versions.VERSIONS.withVersions(properties);
}
static final class Immutable {
private static final Immutable[] CACHE = new Immutable[5];
final OptionValues options;
final boolean scopesEnabled;
final boolean listMetrics;
final EconomicSet<String> unscopedCounters;
final EconomicSet<String> unscopedTimers;
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;
}
}
public OptionValues getOptions() {
return immutable.options;
}
static class Activated extends ThreadLocal<DebugContext> {
}
private static final Activated activated = new Activated();
public static class Activation implements AutoCloseable {
private final DebugContext parent;
Activation(DebugContext parent) {
this.parent = parent;
}
@Override
public void close() {
activated.set(parent);
}
}
public Activation activate() {
Activation res = new Activation(activated.get());
activated.set(this);
return res;
}
private static final DebugContext DISABLED = new DebugContext(NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, new Immutable(), NO_CONFIG_CUSTOMIZERS);
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);
}
public static DebugContext forCurrentThread() {
DebugContext current = activated.get();
if (current == null) {
return DISABLED;
}
return current;
}
private final GlobalMetrics globalMetrics;
public static class Description {
final Object compilable;
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;
public Description getDescription() {
return description;
}
public GlobalMetrics getGlobalMetrics() {
return globalMetrics;
}
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));
}
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));
}
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);
}
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);
}
}
public static final int ENABLED_LEVEL = 0;
public static final int BASIC_LEVEL = 1;
public static final int INFO_LEVEL = 2;
public static final int VERBOSE_LEVEL = 3;
public static final int DETAILED_LEVEL = 4;
public static final int VERY_DETAILED_LEVEL = 5;
public boolean isDumpEnabled(int dumpLevel) {
return currentScope != null && currentScope.isDumpEnabled(dumpLevel);
}
public boolean isVerifyEnabledForMethod() {
if (currentScope == null) {
return false;
}
if (currentConfig == null) {
return false;
}
return currentConfig.isVerifyEnabledForMethod(currentScope);
}
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);
}
public String getCurrentScopeName() {
if (currentScope != null) {
return currentScope.getQualifiedName();
} else {
return "";
}
}
public DebugContext.Scope scope(Object name, Object[] contextObjects) throws Throwable {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null, contextObjects);
} else {
return null;
}
}
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();
}
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:");
}
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;
}
public DebugContext.Scope scope(Object name, Object context) throws Throwable {
if (currentScope != null) {
return enterScope(convertFormatArg(name).toString(), null, context);
} else {
return null;
}
}
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;
}
}
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;
}
}
public DebugContext.Scope withContext(Object context) throws Throwable {
if (currentScope != null) {
return enterScope("", null, context);
} else {
return null;
}
}
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;
}
}
public boolean inNestedScope() {
if (immutable.scopesEnabled) {
if (currentScope == null) {
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;
}
}
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;
}
public DebugCloseable disableIntercept() {
if (currentScope != null) {
return currentScope.disableIntercept();
}
return null;
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
@Deprecated
public void log(String format, Object[] args) {
assert false : "shouldn't use this";
log(BASIC_LEVEL, format, args);
}
@Deprecated
public void log(int logLevel, String format, Object[] args) {
assert false : "shouldn't use this";
logv(logLevel, format, args);
}
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);
}
}
@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);
}
}
public void verify(Object object, String message) {
if (currentScope != null && currentScope.isVerifyEnabled()) {
currentScope.verify(object, message);
}
}
public void verify(Object object, String format, Object arg) {
if (currentScope != null && currentScope.isVerifyEnabled()) {
currentScope.verify(object, format, arg);
}
}
@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);
}
}
public Indent indent() {
if (currentScope != null) {
return currentScope.pushIndentLogger();
}
return null;
}
public Indent logAndIndent(String msg) {
return logAndIndent(BASIC_LEVEL, msg);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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;
}
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();
}
@Deprecated
public void logAndIndent(String format, Object[] args) {
assert false : "shouldn't use this";
logAndIndent(BASIC_LEVEL, format, args);
}
@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();
}
}
@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;
}
@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;
}
public static MemUseTrackerKey memUseTracker(CharSequence name) {
return createMemUseTracker("%s", name, null);
}
public static MemUseTrackerKey memUseTracker(String format, Object arg) {
return createMemUseTracker(format, arg, null);
}
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);
}
public static CounterKey counter(CharSequence name) {
return createCounter("%s", name, null);
}
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;
}
}
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);
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;
}
public static CounterKey counter(String format, Object arg) {
return createCounter(format, arg, null);
}
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;
}
public static TimerKey timer(CharSequence name) {
return createTimer("%s", name, null);
}
public static TimerKey timer(String format, Object arg) {
return createTimer(format, arg, null);
}
public static TimerKey timer(String format, Object arg1, Object arg2) {
return createTimer(format, arg1, arg2);
}
private static String getBaseName(Class<?> c) {
String simpleName = c.getSimpleName();
if (simpleName.length() < 6) {
for (int i = 0; i < simpleName.length(); i++) {
if (!Character.isLowerCase(simpleName.charAt(0))) {
return simpleName;
}
}
return c.getName();
}
return simpleName;
}
private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
@Override
protected String computeValue(Class<?> c) {
String baseName = getBaseName(c);
if (Character.isLowerCase(baseName.charAt(0))) {
baseName = c.getName();
}
Class<?> enclosingClass = c.getEnclosingClass();
if (enclosingClass != null) {
String prefix = "";
while (enclosingClass != null) {
prefix = getBaseName(enclosingClass) + "_" + prefix;
enclosingClass = enclosingClass.getEnclosingClass();
}
return prefix + baseName;
} else {
return baseName;
}
}
};
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);
}
public interface Scope extends AutoCloseable {
String getQualifiedName();
Iterable<Object> getCurrentContext();
@Override
void close();
}
boolean isTimerEnabled(TimerKeyImpl key) {
if (!metricsEnabled) {
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()));
}
boolean isCounterEnabled(CounterKeyImpl key) {
if (!metricsEnabled) {
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) {
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) {