All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.tck.instrumentation.DebugALot Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.tck.instrumentation;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.graalvm.options.OptionCategory;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;

import com.oracle.truffle.api.Option;
import com.oracle.truffle.api.debug.DebugScope;
import com.oracle.truffle.api.debug.DebugStackFrame;
import com.oracle.truffle.api.debug.DebugValue;
import com.oracle.truffle.api.debug.Debugger;
import com.oracle.truffle.api.debug.DebuggerSession;
import com.oracle.truffle.api.debug.SuspendAnchor;
import com.oracle.truffle.api.debug.SuspendedCallback;
import com.oracle.truffle.api.debug.SuspendedEvent;
import com.oracle.truffle.api.debug.SuspensionFilter;
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.source.SourceSection;

/**
 * A total debugging instrument that steps through the whole guest language execution and asks for
 * all debugger-related information. Debugging information is printed to the environment output.
 */
@Registration(name = "Debug a lot", id = DebugALot.ID)
public class DebugALot extends TruffleInstrument implements SuspendedCallback {

    static final String ID = "debugalot";

    private boolean failFast;
    private PrintWriter logger;
    private boolean doEval;
    private volatile boolean hasFailed;
    private Throwable error;

    @Option(name = "", help = "Start debugging logger.", category = OptionCategory.EXPERT) //
    static final OptionKey DebugALot = new OptionKey<>(true);

    @Option(name = "Eval", help = "Whether to test evaluations. (default:false)", category = OptionCategory.EXPERT) //
    static final OptionKey Eval = new OptionKey<>(false);

    @Option(name = "FailFast", help = "Fail fast, give up after the first error. (default:false)", category = OptionCategory.EXPERT) //
    static final OptionKey FailFast = new OptionKey<>(false);

    @Option(name = "LogFile", help = "File to print the debugger log into. (default:standard output)", category = OptionCategory.EXPERT) //
    static final OptionKey LogFile = new OptionKey<>("");

    @Override
    protected void onCreate(Env env) {
        Boolean debugALot = env.getOptions().get(DebugALot);
        failFast = env.getOptions().get(FailFast);
        doEval = env.getOptions().get(Eval);
        boolean isLogFile = env.getOptions().hasBeenSet(LogFile);
        if (!(Boolean.TRUE.equals(debugALot) || failFast || doEval || isLogFile)) {
            return;
        }
        if (isLogFile) {
            String logFilePath = env.getOptions().get(LogFile);
            try {
                logger = new PrintWriter(new FileWriter(logFilePath));
            } catch (IOException ioex) {
                logger = new PrintWriter(env.out());
                logger.print(ioex.getLocalizedMessage());
            }
        } else {
            logger = new PrintWriter(env.out());
        }
        Debugger debugger = env.lookup(env.getInstruments().get("debugger"), Debugger.class);
        DebuggerSession debuggerSession = debugger.startSession(this);
        debuggerSession.suspendNextExecution();
        debuggerSession.setSteppingFilter(SuspensionFilter.newBuilder().ignoreLanguageContextInitialization(true).build());
    }

    @Override
    protected void onFinalize(Env env) {
        logger.print("Executed successfully: ");
        logger.print(Boolean.toString(!hasFailed).toUpperCase());
        logger.flush();
        if (error != null) {
            throw new AssertionError("Failure", error);
        }
    }

    @Override
    protected OptionDescriptors getOptionDescriptors() {
        return new DebugALotOptionDescriptors();
    }

    @Override
    public void onSuspend(SuspendedEvent event) {
        try {
            logSuspendLocation(event.isLanguageContextInitialized(), event.getSuspendAnchor(), event.getSourceSection());
            logFrames(event.getStackFrames());
        } catch (Throwable t) {
            hasFailed = true;
            try {
                logThrowable(t);
            } catch (Throwable lt) {
                lt.printStackTrace(logger);
                if (lt instanceof ThreadDeath) {
                    throw lt;
                }
            }
            if (t instanceof ThreadDeath) {
                throw t;
            }
            if (failFast) {
                error = t;
            }
        }
        logger.flush();
        if (failFast && hasFailed) {
            event.prepareContinue();
        } else {
            event.prepareStepInto(1);
        }
    }

    private void logSuspendLocation(boolean initialized, SuspendAnchor suspendAnchor, SourceSection sourceSection) {
        if (!initialized) {
            logger.print("Uninitialized: ");
        }
        logger.print(suspendAnchor);
        if (sourceSection == null) {
            throw new NullPointerException("No source section is available at suspend location.");
        }
        logSourceSection(sourceSection);
    }

    private void logSourceSection(SourceSection sourceSection) {
        if (sourceSection == null) {
            logger.println(" ");
            return;
        }
        logger.print(" [");
        logger.print(sourceSection.getStartLine());
        logger.print(':');
        logger.print(sourceSection.getStartColumn());
        logger.print('-');
        logger.print(sourceSection.getEndLine());
        logger.print(':');
        logger.print(sourceSection.getEndColumn());
        logger.print("] in ");
        logger.println(sourceSection.getSource().getURI());
    }

    private void logSourceSection(org.graalvm.polyglot.SourceSection sourceSection) {
        if (sourceSection == null) {
            logger.println(" ");
            return;
        }
        logger.print(" [");
        logger.print(sourceSection.getStartLine());
        logger.print(':');
        logger.print(sourceSection.getStartColumn());
        logger.print('-');
        logger.print(sourceSection.getEndLine());
        logger.print(':');
        logger.print(sourceSection.getEndColumn());
        logger.print("] in ");
        logger.println(sourceSection.getSource().getURI());
    }

    private void logFrames(Iterable stackFrames) {
        logger.print("Stack: ");
        List frames = new ArrayList<>();
        for (DebugStackFrame frame : stackFrames) {
            frames.add(frame);
        }
        logger.print(frames.size());
        logger.println((frames.size() == 1) ? " frame" : " frames");
        for (int i = 0; i < frames.size(); i++) {
            logger.print(i + 1);
            logger.print(". ");
            int offset = Integer.toString(i + 1).length() + 2;
            String framePrefix = getPrefix(offset);
            logFrame(framePrefix, frames.get(i));
        }
    }

    private void logFrame(String prefix, DebugStackFrame frame) {
        logger.print(frame.getName());
        if (frame.isInternal()) {
            logger.print(" [Internal]");
        }
        logSourceSection(frame.getSourceSection());
        List scopes = new ArrayList<>();
        for (DebugScope scope = frame.getScope(); scope != null; scope = scope.getParent()) {
            scopes.add(scope);
        }
        logger.print(prefix);
        logger.print("Scopes: ");
        logger.println(scopes.size());
        for (int i = 0; i < scopes.size(); i++) {
            logger.print(prefix);
            logger.print(i + 1);
            logger.print(". ");
            int offset = prefix.length() + Integer.toString(i + 1).length() + 2;
            String scopePrefix = getPrefix(offset);
            logScope(scopePrefix, scopes.get(i), (i == 0) ? frame : null);
        }
    }

    private void logScope(String prefix, DebugScope scope, DebugStackFrame frameForEval) {
        logger.print(scope.getName());
        if (scope.isFunctionScope()) {
            logger.println(" [Function]");
        } else {
            logger.println();
        }
        Iterable variables = scope.getDeclaredValues();
        logger.print(prefix);
        logger.print("Variables: ");
        List values = new ArrayList<>();
        for (DebugValue v : variables) {
            values.add(v);
        }
        logger.println(values.size());
        logValues(prefix, values);

        if (frameForEval != null && doEval) {
            testEval(prefix, frameForEval, values);
        }
    }

    private void logValues(String prefix, List values) {
        for (int i = 0; i < values.size(); i++) {
            logger.print(prefix);
            logger.print(i + 1);
            logger.print(". ");
            DebugValue v = values.get(i);
            logger.print(v.getName());
            logger.print(" = ");
            logger.println(v.toDisplayString(false));
            int offset = prefix.length() + Integer.toString(i + 1).length() + 2;
            String valuePrefix = getPrefix(offset);
            logValue(valuePrefix, v);
        }
    }

    private void logValue(String prefix, DebugValue v) {
        LanguageInfo language = v.getOriginalLanguage();
        if (language != null) {
            logger.print(prefix);
            logger.print("From: ");
            logger.println(language.getId());
        }
        DebugValue metaObject = v.getMetaObject();
        if (metaObject != null) {
            logger.print(prefix);
            logger.print("Type: ");
            logger.println(metaObject.toDisplayString(false));
        }
        SourceSection sourceLocation = v.getSourceLocation();
        if (sourceLocation != null) {
            logger.print(prefix);
            logger.print("SourceSection: ");
            logSourceSection(sourceLocation);
        }
        if (v.isArray()) {
            List array = v.getArray();
            int length = array.size();
            logger.print(prefix);
            logger.print("Array of length: ");
            logger.println(Integer.toString(length));
            for (int i = 0; i < length && i < 10; i++) {
                logger.print(prefix);
                logger.print("  element #");
                logger.print(Integer.toString(i));
                logger.print(" : ");
                logger.println(array.get(i).toDisplayString(false));
            }
        }
        Collection properties = v.getProperties();
        logger.print(prefix);
        if (properties == null || properties.isEmpty()) {
            logger.println("Properties: none");
        } else {
            logger.print("Properties: ");
            logger.println(Integer.toString(properties.size()));
        }
        logger.print(prefix);
        logger.print("Internal: ");
        logger.println(v.isInternal());
        logger.print(prefix);
        logger.print("Readable: ");
        logger.println(v.isReadable());
        logger.print(prefix);
        logger.print("Writable: ");
        logger.println(v.isWritable());
    }

    private void testEval(String prefix, DebugStackFrame frame, List values) {
        for (DebugValue v : values) {
            DebugValue ev = frame.eval(v.getName());
            String value = v.toDisplayString(false);
            String evalue = ev.toDisplayString(false);
            if (!value.equals(evalue)) {
                hasFailed = true;
                logger.print(prefix);
                logger.print("ERROR: local value '");
                logger.print(v.getName());
                logger.print("' has value '");
                logger.print(v.toDisplayString(false));
                logger.print("' but evaluated to '");
                logger.print(ev.toDisplayString(false));
                logger.println("'");
            }
        }
    }

    private void logThrowable(Throwable t) {
        logger.print("\nERROR: Thrown: '");
        logger.print(t.getLocalizedMessage());
        logger.print("', throwable class = ");
        logger.println(t.getClass());
        if (t instanceof PolyglotException) {
            PolyglotException pe = (PolyglotException) t;
            logger.print("  Polyglot Message: '");
            logger.print(pe.getMessage());
            logger.println("'");
            logger.print("  canceled = ");
            logger.print(pe.isCancelled());
            logger.print(", exited = ");
            logger.print(pe.isExit());
            logger.print(", guest ex. = ");
            logger.print(pe.isGuestException());
            logger.print(", host ex. = ");
            logger.print(pe.isHostException());
            logger.print(", incompl. source = ");
            logger.print(pe.isIncompleteSource());
            logger.print(", internal = ");
            logger.print(pe.isInternalError());
            logger.print(", syntax error = ");
            logger.println(pe.isSyntaxError());
            logger.print("  Source Section: ");
            logSourceSection(pe.getSourceLocation());
            if (pe.isExit()) {
                logger.print("  Exit Status = ");
                logger.println(pe.getExitStatus());
            }
            if (pe.isGuestException()) {
                Value guestObject = pe.getGuestObject();
                logger.print("  Guest Object = ");
                logger.println(guestObject.toString());
            }
            if (pe.isHostException()) {
                logger.println("  Host Exception:");
                pe.asHostException().printStackTrace(logger);
            }
            logger.println("  Polyglot Stack Trace:");
            for (PolyglotException.StackFrame sf : pe.getPolyglotStackTrace()) {
                logger.print("    Language ID: ");
                logger.println(sf.getLanguage().getId());
                logger.print("    Root Name: ");
                logger.println(sf.getRootName());
                logger.print("    Source Location: ");
                logSourceSection(sf.getSourceLocation());
                logger.print("    Guest Frame: ");
                logger.println(sf.isGuestFrame());
                logger.print("    Host Frame: ");
                if (sf.isHostFrame()) {
                    logger.println(sf.toHostFrame());
                } else {
                    logger.println(false);
                }
            }
        } else {
            t.printStackTrace(logger);
        }
    }

    private static String getPrefix(int length) {
        char[] prefixChars = new char[length];
        Arrays.fill(prefixChars, ' ');
        return new String(prefixChars);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy