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

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

Go to download

The core library for Fibers on Java, compatible with Java 11-16. Forked from puniverse/quasar

There is a newer version: 10.0.6
Show newest version
/*
 * Copyright (c) 2013-2016, 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 java.lang.reflect.Constructor;
// import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Iterator;

import static co.paralleluniverse.common.reflection.ReflectionUtil.accessible;
import static co.paralleluniverse.common.util.Exceptions.rethrow;

/**
 * This classes uses internal HotSpot data to retrieve a more detailed stacktrace from a {@link Throwable}.
 * 
 * Works only on HotSpot, versions 8 and 9.
 * 
 * @author pron
 */
class ExtendedStackTraceHotSpot extends ExtendedStackTrace {
    /*
     * hotspot/src/share/vm/classfile/javaClasses.hpp
     * hotspot/src/share/vm/classfile/javaClasses.cpp
     */
    private ExtendedStackTraceElement[] est;

    ExtendedStackTraceHotSpot(Throwable t) {
        super(t);
    }

    @Override
    public Iterator iterator() {
        return new Iterator() {
            private Object chunk = getBacktrace(t);
            private int j = -1;
            private int i = -1;

            @Override
            public boolean hasNext() {
                if (j + 1 >= TRACE_CHUNK_SIZE) {
                    j = -1;
                    chunk = getNext(chunk);
                    if (chunk == null)
                        return false;
                }
                return getDeclaringClass(chunk, j + 1) != null;
            }

            @Override
            public ExtendedStackTraceElement next() {
                return getStackTraceElement(getStackTraceElement0(++i), chunk, ++j);
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    @Override
    public ExtendedStackTraceElement[] get() {
        synchronized (this) {
            if (est == null) {
                est = new ExtendedStackTraceElement[getStackTraceDepth()];
                int i = 0;
                for (ExtendedStackTraceElement e : this)
                    est[i++] = e;
            }
            return est;
        }
    }

    private int getStackTraceDepth() {
        Object chunk = getBacktrace(t);
        int depth = 0;
        if (chunk != null) {
            // Iterate over chunks and count full ones
            while (true) {
                Object next = getNext(chunk);
                if (next == null)
                    break;
                depth += TRACE_CHUNK_SIZE;
                chunk = next;
            }
            // Count element in remaining partial chunk.  NULL value for mirror
            // marks the end of the stack trace elements that are saved.
            for (int j = 0; j < TRACE_CHUNK_SIZE; j++) {
                Class c = getDeclaringClass(chunk, j);
                if (c == null)
                    break;
                depth++;
            }
        }
//        assert depth == getStackTraceDepth0();
        return depth;
    }

    private ExtendedStackTraceElement getStackTraceElement(int i) {
        int skipChunks = i / TRACE_CHUNK_SIZE;
        int j = i % TRACE_CHUNK_SIZE;
        Object chunk = getBacktrace(t);
        while (chunk != null && skipChunks > 0) {
            chunk = getNext(chunk);
            skipChunks--;
        }
        return getStackTraceElement(getStackTraceElement0(i), chunk, j);
    }

    private ExtendedStackTraceElement getStackTraceElement(StackTraceElement ste, Object chunk, int j) {
        return new HotSpotExtendedStackTraceElement(ste, getDeclaringClass(chunk, j), getMethod(chunk, j), getBci(chunk, j));
    }

    @Override
    protected Member getMethod(ExtendedStackTraceElement este) {
        final HotSpotExtendedStackTraceElement heste = (HotSpotExtendedStackTraceElement) este;
        Member[] ms = getMethods(heste.getDeclaringClass());
        for (Member m : ms) {
            if (heste.methodSlot == getSlot(m))
                return m;
        }
        return null;
    }

    private class HotSpotExtendedStackTraceElement extends BasicExtendedStackTraceElement {
        private final int methodSlot;

        HotSpotExtendedStackTraceElement(StackTraceElement ste, Class clazz, int methodSlot, int bci) {
            super(ste, clazz, null, bci);
            this.methodSlot = methodSlot;
        }
    }

    /*
    private int getStackTraceDepth0() {
        try {
            return (Integer) getStackTraceDepth.invoke(t);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        } catch (InvocationTargetException e) {
            throw rethrow(e);
        }
    }
    */

    private StackTraceElement getStackTraceElement0(int i) {
        try {
        	if (getStackTraceElement!=null){
		        return (StackTraceElement) getStackTraceElement.invoke(t, i);
	        } else if (stackTrace!=null){
		        return ((StackTraceElement[])stackTrace.get(t))[i];
	        } else {
        		throw new AssertionError("unsupported");
	        }
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        } catch (InvocationTargetException e) {
            throw rethrow(e);
        }
    }

    private static int getSlot(/*Executable*/Member m) {
        try {
            if (m instanceof Constructor)
                return ctorSlot.getInt((Constructor) m);
            else if (m instanceof Field)
                return fieldSlot.getInt((Field) m);
            return methodSlot.getInt((Method) m);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }

    private static Object getBacktrace(Throwable t) {
        // the JVM blocks access to Throwable.backtrace via reflection
        return (Object[]) UNSAFE.getObject(t, BACKTRACE_FIELD_OFFSET);
//        try {
//            return (Object[]) backtrace.get(t);
//        } catch (IllegalAccessException e) {
//            throw new AssertionError(e);
//        }
    }

    private static Class getDeclaringClass(Object chunk, int j) {
        return (Class) ((Object[]) ((Object[]) chunk)[TRACE_CLASSES_OFFSET])[j];
    }

    private static short getMethod(Object chunk, int j) {
        return ((short[]) ((Object[]) chunk)[TRACE_METHOD_OFFSET])[j];
    }

    private static short getBci(Object chunk, int j) {
        int bciAndVersion = ((int[]) ((Object[]) chunk)[TRACE_BCIS_OFFSET])[j];
        short bci = (short) (bciAndVersion >>> 16);
        short version = (short) (bciAndVersion & 0xffff);
        return bci;
    }

    private static Object getNext(Object chunk) {
        return (Object[]) ((Object[]) chunk)[((Object[]) chunk).length - 1];
    }

    // array of arrays; each content array contains trace_chunck_size elements. trace_next_offset points to next array of arrays
    private static final long BACKTRACE_FIELD_OFFSET; // 
    private static final int TRACE_METHOD_OFFSET = 0;  // shorts -- index into class's methods; should be equal to Method.slot
    private static final int TRACE_BCIS_OFFSET = 1;    // ints 
    private static final int TRACE_CLASSES_OFFSET = 2; // object array containing classes
    // private static final int TRACE_CPREFS_OFFSET = 3; // short -- index into constant pool: method name if method is null
    // private static final int TRACE_NEXT_OFFSET = 4; // this elements points to next array of arrays
    private static final int TRACE_CHUNK_SIZE = 32; // maximum num of elements in each array

    // private static final Field backtrace; // the JVM blocks access to Throwable.backtrace via reflection
//    private static final Method getStackTraceDepth;
    private static Method getStackTraceElement;
    private static Field stackTrace;
    private static final Field methodSlot;
    private static final Field ctorSlot;
    private static final Field fieldSlot;
    private static final sun.misc.Unsafe UNSAFE = UtilUnsafe.getUnsafe();

    static {
        try {
            final String javaVersion = System.getProperty("java.version");
            if (!javaVersion.startsWith("1.8") && !javaVersion.startsWith("8.") && !javaVersion.startsWith("1.9") && !javaVersion.startsWith("9.")
                && !javaVersion.startsWith("11") && !javaVersion.startsWith("12") && !javaVersion.startsWith("13"))
                throw new IllegalStateException("UnsupportedJavaVersion");
            if (!System.getProperty("java.vm.name").toLowerCase().contains("hotspot") && !System.getProperty("java.vm.name").toLowerCase().contains("OpenJDK"))
                throw new IllegalStateException("Not HotSpot");
            // the JVM blocks access to Throwable.backtrace via reflection
            // backtrace = ReflectionUtil.accessible(Throwable.class.getDeclaredField("backtrace"));
//            getStackTraceDepth = accessible(Throwable.class.getDeclaredMethod("getStackTraceDepth"));
	          try {
		          getStackTraceElement = accessible(Throwable.class.getDeclaredMethod("getStackTraceElement", int.class));
	          } catch (NoSuchMethodException ignored) {
	          	stackTrace = accessible(Throwable.class.getDeclaredField("stackTrace"));
	          }
            methodSlot = accessible(Method.class.getDeclaredField("slot"));
            ctorSlot = accessible(Constructor.class.getDeclaredField("slot"));
            fieldSlot = accessible(Field.class.getDeclaredField("slot"));

            BACKTRACE_FIELD_OFFSET = guessBacktraceFieldOffset();

            sanityCheck();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }

    private static long guessBacktraceFieldOffset() {
        Field[] fs = Throwable.class.getDeclaredFields();
        Field second = null;
        for (Field f : fs) {
            if (getSlot(f) == 2) {
                second = f;
                break;
            }
        }
        if (second == null)
            throw new IllegalStateException();
        long secondOffest = UNSAFE.objectFieldOffset(second);
        if (secondOffest == 16)
            return 12; // compressed oops
        if (secondOffest == 24)
            return 16; // no compressed oops
        else
            throw new IllegalStateException("secondOffset: " + secondOffest); // unfamiliar
    }

    private static void sanityCheck() {
        Throwable t = new Throwable();
        Object[] chunk = (Object[]) getBacktrace(t);
        if (((Object[]) chunk[TRACE_CLASSES_OFFSET]).length != TRACE_CHUNK_SIZE)
            throw new IllegalStateException();
        if (((short[]) chunk[TRACE_METHOD_OFFSET]).length != TRACE_CHUNK_SIZE)
            throw new IllegalStateException();
        if (((int[]) chunk[TRACE_BCIS_OFFSET]).length != TRACE_CHUNK_SIZE)
            throw new IllegalStateException();
        final Object[] nextChunk = (Object[]) getNext(chunk);
        if (nextChunk != null && nextChunk.length != chunk.length)
            throw new IllegalStateException();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy