co.paralleluniverse.common.util.Debug Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quasar-core Show documentation
Show all versions of quasar-core Show documentation
Fibers, Channels and Actors for the JVM
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() {
}
}