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

co.paralleluniverse.common.util.Debug Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
 * 
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *  
 *   or (per the licensee's choosing)
 *  
 * under the terms of the GNU Lesser General Public License version 3.0
 * as published by the Free Software Foundation.
 */
package co.paralleluniverse.common.util;

import co.paralleluniverse.common.monitoring.FlightRecorder;
import co.paralleluniverse.strands.Strand;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * @author pron
 */
public class Debug {
    private static final boolean debugMode = SystemProperties.isEmptyOrTrue("co.paralleluniverse.debugMode");
    private static final String FLIGHT_RECORDER_DUMP_FILE = System.getProperty("co.paralleluniverse.flightRecorderDumpFile");
    private static final FlightRecorder flightRecorder = (debugMode && SystemProperties.isEmptyOrTrue("co.paralleluniverse.globalFlightRecorder") ? new FlightRecorder("PUNIVERSE-FLIGHT-RECORDER") : null);
    private static boolean recordStackTraces = false;
    private static final boolean assertionsEnabled;
    private static final boolean unitTest;
    private static final boolean ci;
    private static final boolean debugger;
    private static final AtomicBoolean requestShutdown = new AtomicBoolean();
    private static final Lock dumpLock = new ReentrantLock();
    private static final Condition dumpDone = dumpLock.newCondition();
    private static boolean dumped;

    static {
        boolean ea = false;
        assert (ea = true);
        assertionsEnabled = ea;

        boolean isUnitTest = false;
        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : stack) {
            if (ste.getClassName().startsWith("org.junit")
                    || ste.getClassName().startsWith("junit.framework")
                    || ste.getClassName().contains("JUnitTestClassExecuter")) {
                isUnitTest = true;
                break;
            }
        }
        unitTest = isUnitTest;

        ci = (isEnvTrue("CI") || isEnvTrue("CONTINUOUS_INTEGRATION") || isEnvTrue("TRAVIS"));
        if (debugMode) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    dumpRecorder();
                }
            });
        }

        debugger = ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("-agentlib:jdwp");
    }

    public static boolean isDebug() {
        return debugMode;
    }

    public static boolean isCI() {
        return ci;
    }

    public static boolean isDebugger() {
        return debugger;
    }

    public static boolean isAssertionsEnabled() {
        return assertionsEnabled;
    }

    public static boolean isRecordStackTraces() {
        return recordStackTraces;
    }

    public static boolean isUnitTest() {
        return unitTest;
    }

    public static void setRecordStackTraces(boolean recordStackTraces) {
        Debug.recordStackTraces = recordStackTraces;
    }

    public static String getDumpFile() {
        return FLIGHT_RECORDER_DUMP_FILE;
    }

    public static FlightRecorder getGlobalFlightRecorder() {
        return flightRecorder;
    }

    public static void exit() {
        exit(0, null, null);
    }

    public static void exit(int code) {
        exit(code, null, null);
    }

    public static void exit(int code, String filename) {
        exit(code, null, filename);
    }

    public static void exit(Throwable t, String filename) {
        exit(1, t, filename);
    }

    public static void exit(Throwable t) {
        exit(1, t, null);
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public static void exit(int code, Throwable t, String filename) {
        final Strand currentStrand = Strand.currentStrand();
        if (flightRecorder != null) {
            flightRecorder.record(1, "DEBUG EXIT REQUEST ON STRAND " + currentStrand + ": " + Arrays.toString(currentStrand.getStackTrace()));
            if (t != null)
                flightRecorder.record(1, "CAUSED BY " + t + ": " + Arrays.toString(currentStrand.getStackTrace()));
            flightRecorder.stop();
        }

        if (requestShutdown.compareAndSet(false, true)) {
            System.err.println("DEBUG EXIT REQUEST ON STRAND " + currentStrand
                    + (currentStrand.isFiber() ? " (THREAD " + Thread.currentThread() + ")" : "")
                    + ": SHUTTING DOWN THE JVM.");
            Thread.dumpStack();
            if (t != null) {
                System.err.println("CAUSED BY " + t);
                t.printStackTrace();
            }
            dumpRecorder(filename);
            if (!isUnitTest()) // Calling System.exit() in gradle unit tests breaks gradle
                System.exit(code);
        }
    }

    public static void record(int level, Object payload) {
        if (!isDebug())
            return;
        if (getGlobalFlightRecorder() == null)
            return;
        getGlobalFlightRecorder().record(level, payload);
    }

    public static void record(int level, Object... payload) {
        if (!isDebug())
            return;
        if (getGlobalFlightRecorder() == null)
            return;
        getGlobalFlightRecorder().record(level, payload);
    }

    public static void dumpRecorder() {
        dumpRecorder(null);
    }

    public static void dumpRecorder(String filename) {
        dumpLock.lock();
        try {
            if (!dumped) {
                if (filename == null) {
                    filename = getDumpFile();
                    if (filename == null || filename.trim().equals(""))
                        filename = null;
                }
                if (filename == null) {
                    System.err.println("NO ERROR LOG FILE SPECIFIED.");
                    return;
                }
                if (flightRecorder != null)
                    flightRecorder.dump(filename);
                dumped = true;
                dumpDone.signalAll();
            } else {
                while (!dumped)
                    dumpDone.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            dumpLock.unlock();
        }

    }

    public static void dumpAfter(final long millis) {
        dumpAfter(millis, FLIGHT_RECORDER_DUMP_FILE);
    }

    public static void dumpAfter(final long millis, final String filename) {
        if (!debugMode)
            return;
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(millis);
                    dumpRecorder(filename);
                } catch (InterruptedException e) {
                }
            }
        }, "DEBUG").start();
    }

    public interface StackTraceFilter {
        boolean filter(StackTraceElement ste);
    }
    private static final StackTraceFilter UNITTEST_FILTER = new StackTraceFilter() {
        @Override
        public boolean filter(StackTraceElement ste) {
            return !(ste.getClassName().startsWith("org.mockito")
                    || ste.getClassName().startsWith("org.junit")
                    || ste.getClassName().startsWith("org.apache.tools.ant.taskdefs.optional.junit"));
        }
    };

    public static void dumpStack() {
        dumpStack(System.out, new Exception("Stack trace"), UNITTEST_FILTER);
    }

    public static void dumpStack(PrintStream s, Throwable t) {
        dumpStack(s, t, UNITTEST_FILTER);
    }

    public static void dumpStack(PrintStream s, Throwable t, StackTraceFilter filter) {
        synchronized (s) {
            s.println(t);
            StackTraceElement[] trace = t.getStackTrace();
            for (int i = 0; i < trace.length; i++) {
                if (filter.filter(trace[i]))
                    s.println("\tat " + trace[i]);
            }

            Throwable ourCause = t.getCause();
            if (ourCause != null)
                printStackTraceAsCause(s, trace, ourCause, filter);
        }
    }

    public static String toString(Object x) {
        if (x == null)
            return "null";
        if (x instanceof boolean[])
            return Arrays.toString((boolean[])x);
        if (x instanceof char[])
            return Arrays.toString((char[])x);
        if (x instanceof byte[])
            return Arrays.toString((byte[])x);
        if (x instanceof short[])
            return Arrays.toString((short[])x);
        if (x instanceof int[])
            return Arrays.toString((int[])x);
        if (x instanceof long[])
            return Arrays.toString((long[])x);
        if (x instanceof float[])
            return Arrays.toString((float[])x);
        if (x instanceof double[])
            return Arrays.toString((double[])x);
        if (x instanceof Object[])
            return Arrays.toString((Object[])x);
        return x.toString();
    }
    
    /**
     * Print our stack trace as a cause for the specified stack trace.
     */
    private static void printStackTraceAsCause(PrintStream s, StackTraceElement[] causedTrace, Throwable t, StackTraceFilter filter) {
        // assert Thread.holdsLock(s);

        // Compute number of frames in common between this and caused
        StackTraceElement[] trace = t.getStackTrace();
        int m = trace.length - 1, n = causedTrace.length - 1;
        while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) {
            m--;
            n--;
        }
        int framesInCommon = trace.length - 1 - m;

        s.println("Caused by: " + t);
        for (int i = 0; i <= m; i++) {
            if (filter.filter(trace[i]))
                s.println("\tat " + trace[i]);
        }
        if (framesInCommon != 0)
            s.println("\t... " + framesInCommon + " more");

        // Recurse if we have a cause
        Throwable ourCause = t.getCause();
        if (ourCause != null)
            printStackTraceAsCause(s, trace, ourCause, filter);
    }

    private static boolean isEnvTrue(String envVar) {
        final String ev = System.getenv(envVar);
        if (ev == null)
            return false;
        try {
            return Boolean.parseBoolean(ev);
        } catch (Exception e) {
            return false;
        }
    }

    public static String getPackageVersion(String packageName) {
        try {
            Package aPackage = Package.getPackage(packageName);
            if (aPackage != null) {
                return aPackage.getImplementationVersion();
            }
        } catch (Exception e) {
        }
        return null;
    }

    public static Path getJarOfClass(String className) {
        return getJarOfClass(findClass(className));
    }

    public static Path getJarOfClass(Class clazz) {
        try {
            if (clazz != null) {
                final URL resource = clazz.getClassLoader().getResource(clazz.getName().replace('.', '/') + ".class");
                if (resource != null) {
                    String p = resource.toString();
                    int idx = p.lastIndexOf('!');
                    if (idx > 0)
                        return Paths.get(new URI(p.substring(0, idx)));
                }
            }
        } catch (Exception e) {
        }
        return null;
    }

    public static String whereIs(String className) {
        return whereIs(findClass(className));
    }

    public static String whereIs(Class clazz) {
        if (clazz == null)
            return null;
        final String resource = clazz.getName().replace('.', '/') + ".class";
        URL url = clazz.getResource(resource);
        if (url == null)
            url = (clazz.getClassLoader() != null ? clazz.getClassLoader() : ClassLoader.getSystemClassLoader()).getResource(resource);
        return url != null ? url.toString() : null;
    }

    private static Class findClass(String className) {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
        }
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
        }
        return null;
    }

    public static String getClassLoaderInfo(ClassLoader cl) {
        final StringBuilder sb = new StringBuilder();
        int indent = 0;
        while (cl != null) {
            indent(sb, indent).append(cl.toString()).append('\n');
            if (cl instanceof URLClassLoader)
                indent(sb, indent).append("URLs: ").append(Arrays.toString(((URLClassLoader) cl).getURLs())).append('\n');
            cl = cl.getParent();
            indent += 4;
        }

        return sb.toString();
    }

    public static void printStackTrace(int num, java.io.PrintStream out) {
        StackTraceElement[] trace = new Throwable().getStackTrace();
        if (trace == null)
            out.println("No stack trace");
        else {
            for (int i = 0; i < num && i < trace.length; i++)
                out.println("\tat " + trace[i]);
        }
    }

    public static void printStackTrace(int num, java.io.PrintWriter out) {
        StackTraceElement[] trace = new Throwable().getStackTrace();
        if (trace == null)
            out.println("No stack trace");
        else {
            for (int i = 0; i < num && i < trace.length; i++)
                out.println("\tat " + trace[i]);
        }
    }

    private static StringBuilder indent(StringBuilder sb, int indent) {
        for (int i = 0; i < indent; i++)
            sb.append(' ');
        return sb;
    }

    private Debug() {
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy