/*
 * 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 java.io.PrintStream;
import java.util.Iterator;

import org.graalvm.compiler.debug.DebugContext.DisabledScope;

import jdk.vm.ci.meta.JavaMethod;

public final class ScopeImpl implements DebugContext.Scope {

    private final class IndentImpl implements Indent {

        private static final String INDENTATION_INCREMENT = "  ";

        final String indent;
        final IndentImpl parentIndent;

        boolean isEmitted() {
            return emitted;
        }

        private boolean emitted;

        IndentImpl(IndentImpl parentIndent) {
            this.parentIndent = parentIndent;
            this.indent = (parentIndent == null ? "" : parentIndent.indent + INDENTATION_INCREMENT);
        }

        private void printScopeName(StringBuilder str, boolean isCurrent) {
            if (!emitted) {
                boolean mustPrint = true;
                if (parentIndent != null) {
                    if (!parentIndent.isEmitted()) {
                        parentIndent.printScopeName(str, false);
                        mustPrint = false;
                    }
                }
                /*
                 * Always print the current scope, scopes with context and any scope whose parent
                 * didn't print. This ensure the first new scope always shows up.
                 */
                if (isCurrent || printContext(null) != 0 || mustPrint) {
                    str.append(indent).append("[thread:").append(Thread.currentThread().getId()).append("] scope: ").append(getQualifiedName()).append(System.lineSeparator());
                }
                printContext(str);
                emitted = true;
            }
        }

        
Print or count the context objects for the current scope.
/** * Print or count the context objects for the current scope. */
private int printContext(StringBuilder str) { int count = 0; if (context != null && context.length > 0) { // Include some context in the scope output for (Object contextObj : context) { if (contextObj instanceof JavaMethodContext || contextObj instanceof JavaMethod) { if (str != null) { str.append(indent).append("Context: ").append(contextObj).append(System.lineSeparator()); } count++; } } } return count; } public void log(int logLevel, String msg, Object... args) { if (isLogEnabled(logLevel)) { StringBuilder str = new StringBuilder(); printScopeName(str, true); str.append(indent); String result = args.length == 0 ? msg : String.format(msg, args); String lineSep = System.lineSeparator(); str.append(result.replace(lineSep, lineSep.concat(indent))); str.append(lineSep); output.append(str); lastUsedIndent = this; } } IndentImpl indent() { lastUsedIndent = new IndentImpl(this); return lastUsedIndent; } @Override public void close() { if (parentIndent != null) { lastUsedIndent = parentIndent; } } } private final DebugContext owner; private final ScopeImpl parent; private final boolean sandbox; private IndentImpl lastUsedIndent; private boolean isEmptyScope() { return emptyScope; } private final boolean emptyScope; private final Object[] context; private String qualifiedName; private final String unqualifiedName; private static final char SCOPE_SEP = '.'; private boolean countEnabled; private boolean timeEnabled; private boolean memUseTrackingEnabled; private boolean verifyEnabled; private int currentDumpLevel; private int currentLogLevel; private PrintStream output; private boolean interceptDisabled; ScopeImpl(DebugContext owner, Thread thread) { this(owner, thread.getName(), null, false); } private ScopeImpl(DebugContext owner, String unqualifiedName, ScopeImpl parent, boolean sandbox, Object... context) { this.owner = owner; this.parent = parent; this.sandbox = sandbox; this.context = context; this.unqualifiedName = unqualifiedName; if (parent != null) { emptyScope = unqualifiedName.equals(""); this.interceptDisabled = parent.interceptDisabled; } else { if (unqualifiedName.isEmpty()) { throw new IllegalArgumentException("root scope name must be non-empty"); } emptyScope = false; } this.output = TTY.out; assert context != null; } @Override public void close() { owner.currentScope = parent; owner.lastClosedScope = this; } boolean isTopLevel() { return parent == null; } boolean isDumpEnabled(int dumpLevel) { assert dumpLevel >= 0; return currentDumpLevel >= dumpLevel; } boolean isVerifyEnabled() { return verifyEnabled; } boolean isLogEnabled(int logLevel) { assert logLevel > 0; return currentLogLevel >= logLevel; } boolean isCountEnabled() { return countEnabled; } boolean isTimeEnabled() { return timeEnabled; } boolean isMemUseTrackingEnabled() { return memUseTrackingEnabled; } public void log(int logLevel, String msg, Object... args) { assert owner.checkNoConcurrentAccess(); if (isLogEnabled(logLevel)) { getLastUsedIndent().log(logLevel, msg, args); } } public void dump(int dumpLevel, Object object, String formatString, Object... args) { assert isDumpEnabled(dumpLevel); if (isDumpEnabled(dumpLevel)) { DebugConfig config = getConfig(); if (config != null) { for (DebugDumpHandler dumpHandler : config.dumpHandlers()) { dumpHandler.dump(owner, object, formatString, args); } } } } private DebugConfig getConfig() { return owner.currentConfig; }
See Also:
  • verify.verify(Object, String)
/** * @see DebugContext#verify(Object, String) */
public void verify(Object object, String formatString, Object... args) { if (isVerifyEnabled()) { DebugConfig config = getConfig(); if (config != null) { String message = String.format(formatString, args); for (DebugVerifyHandler handler : config.verifyHandlers()) { handler.verify(owner, object, message); } } } }
Creates and enters a new scope which is either a child of the current scope or a disjoint top level scope.
Params:
  • name – the name of the new scope
  • sandboxConfig – the configuration to use for a new top level scope, or null if the new scope should be a child scope
  • newContextObjects – objects to be appended to the debug context
Returns:the new scope which will be exited when its close() method is called
/** * Creates and enters a new scope which is either a child of the current scope or a disjoint top * level scope. * * @param name the name of the new scope * @param sandboxConfig the configuration to use for a new top level scope, or null if the new * scope should be a child scope * @param newContextObjects objects to be appended to the debug context * @return the new scope which will be exited when its {@link #close()} method is called */
public ScopeImpl scope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) { ScopeImpl newScope = null; if (sandboxConfig != null) { newScope = new ScopeImpl(owner, name.toString(), this, true, newContextObjects); } else { newScope = this.createChild(name.toString(), newContextObjects); } newScope.updateFlags(owner.currentConfig); return newScope; } @SuppressWarnings({"unchecked", "unused"}) private static <E extends Exception> RuntimeException silenceException(Class<E> type, Throwable ex) throws E { throw (E) ex; } public RuntimeException handle(Throwable e) { try { if (owner.lastClosedScope instanceof ScopeImpl) { ScopeImpl lastClosed = (ScopeImpl) owner.lastClosedScope; assert lastClosed.parent == this : "DebugContext.handle() used without closing a scope opened by DebugContext.scope(...) or DebugContext.sandbox(...) " + "or an exception occurred while opening a scope"; if (e != owner.lastExceptionThrown) { RuntimeException newException = null; // Make the scope in which the exception was thrown // the current scope again. owner.currentScope = lastClosed; // When this try block exits, the above action will be undone try (ScopeImpl s = lastClosed) { newException = s.interceptException(e); } // Checks that the action really is undone assert owner.currentScope == this; assert lastClosed == owner.lastClosedScope; if (newException == null) { owner.lastExceptionThrown = e; } else { owner.lastExceptionThrown = newException; throw newException; } } } else if (owner.lastClosedScope == null) { throw new AssertionError("DebugContext.handle() used without closing a scope opened by DebugContext.scope(...) or DebugContext.sandbox(...) " + "or an exception occurred while opening a scope"); } else { assert owner.lastClosedScope instanceof DisabledScope : owner.lastClosedScope; } } catch (Throwable t) { t.initCause(e); throw t; } if (e instanceof Error) { throw (Error) e; } if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw silenceException(RuntimeException.class, e); } void updateFlags(DebugConfigImpl config) { if (config == null) { countEnabled = false; memUseTrackingEnabled = false; timeEnabled = false; verifyEnabled = false; currentDumpLevel = -1; // Be pragmatic: provide a default log stream to prevent a crash if the stream is not // set while logging output = TTY.out; } else if (isEmptyScope()) { countEnabled = parent.countEnabled; memUseTrackingEnabled = parent.memUseTrackingEnabled; timeEnabled = parent.timeEnabled; verifyEnabled = parent.verifyEnabled; output = parent.output; currentDumpLevel = parent.currentDumpLevel; currentLogLevel = parent.currentLogLevel; } else { countEnabled = config.isCountEnabled(this); memUseTrackingEnabled = config.isMemUseTrackingEnabled(this); timeEnabled = config.isTimeEnabled(this); verifyEnabled = config.isVerifyEnabled(this); output = config.output(); currentDumpLevel = config.getDumpLevel(this); currentLogLevel = config.getLogLevel(this); } } DebugCloseable disableIntercept() { boolean previous = interceptDisabled; interceptDisabled = true; return new DebugCloseable() { @Override public void close() { interceptDisabled = previous; } }; } @SuppressWarnings("try") private RuntimeException interceptException(final Throwable e) { if (!interceptDisabled && owner.currentConfig != null) { try (ScopeImpl s = scope("InterceptException", null, e)) { return owner.currentConfig.interceptException(owner, e); } catch (Throwable t) { return new RuntimeException("Exception while intercepting exception", t); } } return null; } private ScopeImpl createChild(String newName, Object[] newContext) { return new ScopeImpl(owner, newName, this, false, newContext); } @Override public Iterable<Object> getCurrentContext() { final ScopeImpl scope = this; return new Iterable<Object>() { @Override public Iterator<Object> iterator() { return new Iterator<Object>() { ScopeImpl currentScope = scope; int objectIndex; @Override public boolean hasNext() { selectScope(); return currentScope != null; } private void selectScope() { while (currentScope != null && currentScope.context.length <= objectIndex) { currentScope = currentScope.sandbox ? null : currentScope.parent; objectIndex = 0; } } @Override public Object next() { selectScope(); if (currentScope != null) { return currentScope.context[objectIndex++]; } throw new IllegalStateException("May only be called if there is a next element."); } @Override public void remove() { throw new UnsupportedOperationException("This iterator is read only."); } }; } }; } @Override public String getQualifiedName() { if (qualifiedName == null) { if (parent == null) { qualifiedName = unqualifiedName; } else { qualifiedName = parent.getQualifiedName(); if (!isEmptyScope()) { qualifiedName += SCOPE_SEP + unqualifiedName; } } } return qualifiedName; } Indent pushIndentLogger() { lastUsedIndent = getLastUsedIndent().indent(); return lastUsedIndent; } private IndentImpl getLastUsedIndent() { if (lastUsedIndent == null) { if (parent != null) { lastUsedIndent = new IndentImpl(parent.getLastUsedIndent()); } else { lastUsedIndent = new IndentImpl(null); } } return lastUsedIndent; } }