/*
* Copyright (c) 2012, 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 org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT;
import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.LOG_METHOD;
import static java.util.FormattableFlags.LEFT_JUSTIFY;
import static java.util.FormattableFlags.UPPERCASE;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.graalvm.compiler.debug.DelegatingDebugConfig.Level;
import org.graalvm.compiler.debug.internal.CounterImpl;
import org.graalvm.compiler.debug.internal.DebugHistogramImpl;
import org.graalvm.compiler.debug.internal.DebugScope;
import org.graalvm.compiler.debug.internal.MemUseTrackerImpl;
import org.graalvm.compiler.debug.internal.TimerImpl;
import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl;
import org.graalvm.compiler.serviceprovider.GraalServices;
import jdk.vm.ci.meta.ResolvedJavaMethod;
/**
* Scope based debugging facility.
*
* This facility is {@linkplain #isEnabled() enabled} if any of the following hold when the
* {@link Debug} class is initialized:
*
*
assertions are enabled for the {@link Debug} class
*
{@link Debug#params}{@code .enable} is {@code true}
*
*/
public class Debug {
private static final Params params = new Params();
static {
// Load the service providers that may want to modify any of the
// parameters encapsulated by the Initialization class below.
for (DebugInitializationParticipant p : GraalServices.load(DebugInitializationParticipant.class)) {
p.apply(params);
}
}
/**
* The parameters for configuring the initialization of {@link Debug} class.
*/
public static class Params {
public boolean enable;
public boolean enableMethodFilter;
public boolean enableUnscopedTimers;
public boolean enableUnscopedCounters;
public boolean enableUnscopedMethodMetrics;
public boolean enableUnscopedMemUseTrackers;
public boolean interceptCount;
public boolean interceptTime;
public boolean interceptMem;
}
@SuppressWarnings("all")
private static boolean initialize() {
boolean assertionsEnabled = false;
assert assertionsEnabled = true;
return assertionsEnabled || params.enable || GraalDebugConfig.Options.ForceDebugEnable.getValue();
}
private static final boolean ENABLED = initialize();
public static boolean isEnabled() {
return ENABLED;
}
public static boolean isDumpEnabledForMethod() {
if (!ENABLED) {
return false;
}
DebugConfig config = DebugScope.getConfig();
if (config == null) {
return false;
}
return config.isDumpEnabledForMethod();
}
public static final int BASIC_LOG_LEVEL = 1;
public static final int INFO_LOG_LEVEL = 2;
public static final int VERBOSE_LOG_LEVEL = 3;
public static final int DETAILED_LOG_LEVEL = 4;
public static boolean isDumpEnabled(int dumpLevel) {
return ENABLED && DebugScope.getInstance().isDumpEnabled(dumpLevel);
}
/**
* Determines if verification is enabled in the current method, regardless of the
* {@linkplain Debug#currentScope() current debug scope}.
*
* @see Debug#verify(Object, String)
*/
public static boolean isVerifyEnabledForMethod() {
if (!ENABLED) {
return false;
}
DebugConfig config = DebugScope.getConfig();
if (config == null) {
return false;
}
return config.isVerifyEnabledForMethod();
}
/**
* Determines if verification is enabled in the {@linkplain Debug#currentScope() current debug
* scope}.
*
* @see Debug#verify(Object, String)
*/
public static boolean isVerifyEnabled() {
return ENABLED && DebugScope.getInstance().isVerifyEnabled();
}
public static boolean isCountEnabled() {
return ENABLED && DebugScope.getInstance().isCountEnabled();
}
public static boolean isTimeEnabled() {
return ENABLED && DebugScope.getInstance().isTimeEnabled();
}
public static boolean isMemUseTrackingEnabled() {
return ENABLED && DebugScope.getInstance().isMemUseTrackingEnabled();
}
public static boolean isLogEnabledForMethod() {
if (!ENABLED) {
return false;
}
DebugConfig config = DebugScope.getConfig();
if (config == null) {
return false;
}
return config.isLogEnabledForMethod();
}
public static boolean isLogEnabled() {
return isLogEnabled(BASIC_LOG_LEVEL);
}
public static boolean isLogEnabled(int logLevel) {
return ENABLED && DebugScope.getInstance().isLogEnabled(logLevel);
}
public static boolean isMethodMeterEnabled() {
return ENABLED && DebugScope.getInstance().isMethodMeterEnabled();
}
@SuppressWarnings("unused")
public static Runnable decorateDebugRoot(Runnable runnable, String name, DebugConfig config) {
return runnable;
}
@SuppressWarnings("unused")
public static Callable decorateDebugRoot(Callable callable, String name, DebugConfig config) {
return callable;
}
@SuppressWarnings("unused")
public static Runnable decorateScope(Runnable runnable, String name, Object... context) {
return runnable;
}
@SuppressWarnings("unused")
public static Callable decorateScope(Callable callable, String name, Object... context) {
return callable;
}
/**
* Gets a string composed of the names in the current nesting of debug
* {@linkplain #scope(Object) scopes} separated by {@code '.'}.
*/
public static String currentScope() {
if (ENABLED) {
return DebugScope.getInstance().getQualifiedName();
} else {
return "";
}
}
/**
* Represents a debug scope entered by {@link Debug#scope(Object)} or
* {@link Debug#sandbox(CharSequence, DebugConfig, Object...)}. Leaving the scope is achieved
* via {@link #close()}.
*/
public interface Scope extends AutoCloseable {
@Override
void close();
}
/**
* 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:
*
*
*
* @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 static Scope scope(Object name, Object[] contextObjects) throws Throwable {
if (ENABLED) {
return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, contextObjects);
} else {
return null;
}
}
/**
* Similar to {@link #scope(Object, Object[])} but without context objects. Therefore the catch
* block can be omitted.
*
* @see #scope(Object, Object[])
*/
public static Scope scope(Object name) {
if (ENABLED) {
return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null);
} else {
return null;
}
}
public static Scope methodMetricsScope(Object name, DebugScope.ExtraInfo metaInfo, boolean newId, Object... context) {
if (ENABLED) {
return DebugScope.getInstance().enhanceWithExtraInfo(convertFormatArg(name).toString(), metaInfo, newId, context);
} else {
return null;
}
}
/**
* @see #scope(Object, Object[])
* @param context an object to be appended to the {@linkplain #context() current} debug context
*/
public static Scope scope(Object name, Object context) throws Throwable {
if (ENABLED) {
return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context);
} else {
return null;
}
}
/**
* @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 static Scope scope(Object name, Object context1, Object context2) throws Throwable {
if (ENABLED) {
return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2);
} else {
return null;
}
}
/**
* @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 static Scope scope(Object name, Object context1, Object context2, Object context3) throws Throwable {
if (ENABLED) {
return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context1, context2, context3);
} 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:
*
*
*
* @param name the name of the new scope
* @param config the debug configuration to use for the new 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 static Scope sandbox(CharSequence name, DebugConfig config, Object... context) throws Throwable {
if (ENABLED) {
DebugConfig sandboxConfig = config == null ? silentConfig() : config;
return DebugScope.getInstance().scope(name, sandboxConfig, context);
} else {
return null;
}
}
public static Scope forceLog() throws Throwable {
ArrayList