/* * 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 com.sun.max.vm.ext.jvmti; import static com.sun.max.vm.ext.jvmti.JVMTIConstants.*; import java.lang.reflect.*; import java.util.*; import com.sun.max.annotate.*; import com.sun.max.vm.actor.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.compiler.*; import com.sun.max.vm.ext.jvmti.JVMTIEvents.E; import com.sun.max.vm.ext.jvmti.JVMTIThreadFunctions.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.layout.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.thread.*; /** * The core implementation of {@link JJVMTI}. The standard idiom is to subclass {@link NullJJVMTICallbacks}, * which extends this class, overriding those {@link JJVMTI.EventCallbacks} that the agent wants to handle. * Checking that the agent has the {@link JVMTICapabilities.E capability} to invoke a given method is made * by calling {@code super.method(...)} which access the automatically generated {@link JJVMTIAgentAdapterChecker}. * The call is inlined away in the generated code of the boot image. */ public class JJVMTIAgentAdapter extends JJVMTIAgentAdapterChecker implements JJVMTI { static { JavaPrototype.registerInitializationCompleteCallback(new InitializationCompleteCallback()); } protected static void registerCriticalMethods(Class<?> klass) { for (Method m : klass.getDeclaredMethods()) { new CriticalMethod(ClassMethodActor.fromJava(m), CallEntryPoint.OPTIMIZED_ENTRY_POINT); } } @HOSTED_ONLY private static class InitializationCompleteCallback implements JavaPrototype.InitializationCompleteCallback { @Override public void initializationComplete() { registerCriticalMethods(JJVMTIAgentAdapter.class); } } protected static final JJVMTIException notImplemented = new JJVMTIException(JVMTI_ERROR_NOT_AVAILABLE); /** * Register an agent. * @param the agent implementation subclass */ public static JJVMTIAgentAdapter register(JJVMTIAgentAdapter agent) { agent.registerEnv(new JVMTI.JavaEnv((JJVMTI.EventCallbacks) agent)); return agent; } protected void registerEnv(JVMTI.JavaEnv env) { this.env = env; JVMTI.setJVMTIJavaEnv(env); } @Override public void includeMaxVMClasses(boolean include) { JVMTI.JVMTI_VM = include; } @Override public void setEventNotificationMode(int mode, E event, Thread thread) throws JJVMTIException { super.setEventNotificationMode(mode, event, thread); int error = JVMTIEvents.setEventNotificationMode(env, mode, event, thread); if (error != JVMTI_ERROR_NONE) { throw new JJVMTIException(error); } } @Override public Thread[] getAllThreads() throws JJVMTIException { super.getAllThreads(); return VmThreadMap.getThreads(true); } @Override public void suspendThread(Thread thread) throws JJVMTIException { super.suspendThread(thread); int error = JVMTIThreadFunctions.suspendThread(env, thread); if (error != JVMTI_ERROR_NONE) { throw new JJVMTIException(error); } } @Override public void resumeThread(Thread thread) throws JJVMTIException { super.resumeThread(thread); int error = JVMTIThreadFunctions.resumeThread(env, thread); if (error != JVMTI_ERROR_NONE) { throw new JJVMTIException(error); } } @Override public void stopThread(Thread thread, Throwable t) throws JJVMTIException { super.stopThread(thread, t); throw notImplemented; // TODO } @Override public void interruptThread(Thread thread) throws JJVMTIException { super.interruptThread(thread); JVMTIThreadFunctions.interruptThread(thread); } @Override public ThreadInfo getThreadInfo(Thread thread) throws JJVMTIException { super.getThreadInfo(thread); return JVMTIThreadFunctions.getThreadInfo(thread); } @Override public void getOwnedMonitorInfo(Thread thread) throws JJVMTIException { super.getOwnedMonitorInfo(thread); throw notImplemented; // TODO } @Override public Object getCurrentContendedMonitor(Thread thread) throws JJVMTIException { super.getCurrentContendedMonitor(thread); throw notImplemented; // TODO } @Override public void runAgentThread(Thread thread, int priority) throws JJVMTIException { super.runAgentThread(thread, priority); JVMTI.runAgentThread(thread, priority); } @Override public ThreadGroup[] getTopThreadGroups() throws JJVMTIException { super.getTopThreadGroups(); ThreadGroup[] result = new ThreadGroup[1]; result[0] = VmThread.systemThreadGroup; return result; } @Override public ThreadGroupInfo getThreadGroupInfo(ThreadGroup tg) throws JJVMTIException { super.getThreadGroupInfo(tg); return new ThreadGroupInfo(tg.getParent(), tg.getName(), tg.getMaxPriority(), tg.isDaemon()); } @Override public ThreadGroupChildrenInfo getThreadGroupChildren(ThreadGroup threadGroup) throws JJVMTIException { super.getThreadGroupChildren(threadGroup); return JVMTIThreadFunctions.getThreadGroupChildren(threadGroup); } @Override public int getFrameCount(Thread thread) throws JJVMTIException { super.getFrameCount(thread); VmThread vmThread = JVMTIThreadFunctions.checkVmThread(thread); if (vmThread == null) { throw new JJVMTIException(JVMTI_ERROR_THREAD_NOT_ALIVE); } FindAppFramesStackTraceVisitor stackTraceVisitor = SingleThreadStackTraceVmOperation.invoke(vmThread); return stackTraceVisitor.stackElements.size(); } @Override public int getThreadState(Thread thread) throws JJVMTIException { super.getThreadState(thread); VmThread vmThread = JVMTIThreadFunctions.checkVmThread(thread); if (vmThread == null) { throw new JJVMTIException(JVMTI_ERROR_THREAD_NOT_ALIVE); } return JVMTIThreadFunctions.getThreadState(vmThread); } @Override public Thread getCurrentThread() throws JJVMTIException { super.getCurrentThread(); return VmThread.current().javaThread(); } @Override public FrameInfo getFrameLocation(Thread thread, int depth) throws JJVMTIException { super.getFrameLocation(thread, depth); return JVMTIThreadFunctions.getFrameLocation(thread, depth); } @Override public void notifyFramePop(Thread thread, int depth) throws JJVMTIException { super.notifyFramePop(thread, depth); throw notImplemented; // TODO } @Override public Object getLocalObject(Thread thread, int depth, int slot) throws JJVMTIException { super.getLocalObject(thread, depth, slot); return JVMTIThreadFunctions.getLocalObject(thread, depth, slot); } @Override public int getLocalInt(Thread thread, int depth, int slot) throws JJVMTIException { super.getLocalInt(thread, depth, slot); return JVMTIThreadFunctions.getLocalInt(thread, depth, slot); } @Override public long getLocalLong(Thread thread, int depth, int slot) throws JJVMTIException { super.getLocalLong(thread, depth, slot); return JVMTIThreadFunctions.getLocalLong(thread, depth, slot); } @Override public float getLocalFloat(Thread thread, int depth, int slot) throws JJVMTIException { super.getLocalFloat(thread, depth, slot); return JVMTIThreadFunctions.getLocalFloat(thread, depth, slot); } @Override public double getLocalDouble(Thread thread, int depth, int slot) throws JJVMTIException { super.getLocalDouble(thread, depth, slot); return JVMTIThreadFunctions.getLocalDouble(thread, depth, slot); } @Override public void setLocalObject(Thread thread, int depth, int slot, Object value) throws JJVMTIException { super.setLocalObject(thread, depth, slot, value); JVMTIThreadFunctions.setLocalObject(thread, depth, slot, value); } @Override public void setLocalInt(Thread thread, int depth, int slot, int value) throws JJVMTIException { super.setLocalInt(thread, depth, slot, value); JVMTIThreadFunctions.setLocalInt(thread, depth, slot, value); } @Override public void setLocalLong(Thread thread, int depth, int slot, long value) throws JJVMTIException { super.setLocalLong(thread, depth, slot, value); JVMTIThreadFunctions.setLocalLong(thread, depth, slot, value); } @Override public void setLocalFloat(Thread thread, int depth, int slot, float value) throws JJVMTIException { super.setLocalFloat(thread, depth, slot, value); JVMTIThreadFunctions.setLocalFloat(thread, depth, slot, value); } @Override public void setLocalDouble(Thread thread, int depth, int slot, double value) throws JJVMTIException { super.setLocalDouble(thread, depth, slot, value); JVMTIThreadFunctions.setLocalDouble(thread, depth, slot, value); } @Override public int getObjectHashCode(Object object) throws JJVMTIException { super.getObjectHashCode(object); return System.identityHashCode(object); } @Override public ObjectMonitorUsage getObjectMonitorUsage(Object object) throws JJVMTIException { super.getObjectMonitorUsage(object); throw notImplemented; // TODO } @Override public void setNativeMethodPrefix(String prefix) throws JJVMTIException { super.setNativeMethodPrefix(prefix); throw notImplemented; // TODO } @Override public void setNativeMethodPrefixes(String[] prefixes) throws JJVMTIException { super.setNativeMethodPrefixes(prefixes); throw notImplemented; // TODO } @Override public void popFrame(Thread thread) throws JJVMTIException { super.popFrame(thread); throw notImplemented; // TODO } @Override public void forceEarlyReturnObject(Thread thread, Object value) throws JJVMTIException { super.forceEarlyReturnObject(thread, value); throw notImplemented; // TODO } @Override public void forceEarlyReturnInt(Thread thread, int value) throws JJVMTIException { super.forceEarlyReturnInt(thread, value); throw notImplemented; // TODO } @Override public void forceEarlyReturnLong(Thread thread, long value) throws JJVMTIException { super.forceEarlyReturnLong(thread, value); throw notImplemented; // TODO } @Override public void forceEarlyReturnFloat(Thread thread, float value) throws JJVMTIException { super.forceEarlyReturnFloat(thread, value); throw notImplemented; // TODO } @Override public void forceEarlyReturnDouble(Thread thread, double value) throws JJVMTIException { super.forceEarlyReturnDouble(thread, value); throw notImplemented; // TODO } @Override public void forceEarlyReturnVoid(Thread thread) throws JJVMTIException { super.forceEarlyReturnVoid(thread); throw notImplemented; // TODO } @Override public int getVersionNumber() throws JJVMTIException { super.getVersionNumber(); return JVMTI_VERSION; } @Override public EnumSet<JVMTICapabilities.E> getCapabilities() throws JJVMTIException { super.getCapabilities(); return env.capabilities; } @Override public int[] suspendThreadList(Thread[] threads) throws JJVMTIException { super.suspendThreadList(threads); return JVMTIThreadFunctions.suspendThreadList(env, threads); } @Override public int[] resumeThreadList(Thread[] threads) throws JJVMTIException { super.resumeThreadList(threads); return JVMTIThreadFunctions.resumeThreadList(env, threads); } @Override public StackInfo[] getAllStackTraces(int maxFrameCount) throws JJVMTIException { super.getAllStackTraces(maxFrameCount); return JVMTIThreadFunctions.getAllStackTraces(maxFrameCount); } @Override public StackInfo[] getThreadListStackTraces(Thread[] threads, int maxFrameCount) throws JJVMTIException { super.getThreadListStackTraces(threads, maxFrameCount); return JVMTIThreadFunctions.getThreadListStackTraces(threads, maxFrameCount); } private static class ObjectThreadLocal extends ThreadLocal<Object> { @Override public Object initialValue() { return null; } } @Override public FrameInfo[] getStackTrace(Thread thread, int startDepth, int maxFrameCount) throws JJVMTIException { super.getStackTrace(thread, startDepth, maxFrameCount); return JVMTIThreadFunctions.getStackTrace(thread, startDepth, maxFrameCount); } @Override public Object getTag(Object object) throws JJVMTIException { super.getTag(object); return env.tags.getTag(object); } @Override public void setTag(Object object, Object tag) throws JJVMTIException { super.setTag(object, tag); env.tags.setTag(object, tag); } @Override public void forceGarbageCollection() throws JJVMTIException { super.forceGarbageCollection(); System.gc(); } @Override public void disposeEnvironment() throws JJVMTIException { super.disposeEnvironment(); JVMTI.disposeJVMTIJavaEnv(env); } @Override public String getErrorName(int error) throws JJVMTIException { super.getErrorName(error); return JVMTIError.getName(error); } @Override public int getJLocationFormat() throws JJVMTIException { super.getJLocationFormat(); return JVMTIConstants.JVMTI_JLOCATION_JVMBCI; } @Override public String[] getSystemProperties() throws JJVMTIException { super.getSystemProperties(); return JVMTISystem.getSystemProperties(); } @Override public String getSystemProperty(String key) throws JJVMTIException { super.getSystemProperty(key); return JVMTISystem.getSystemProperty(key); } @Override public void setSystemProperty(String key, String value) throws JJVMTIException { super.setSystemProperty(key, value); throw new JJVMTIException(JVMTI_ERROR_WRONG_PHASE); } @Override public int getPhase() throws JJVMTIException { super.getPhase(); return JVMTI.getPhase(); } @Override public long getCurrentThreadCpuTime() throws JJVMTIException { super.getCurrentThreadCpuTime(); throw notImplemented; // TODO } @Override public long getThreadCpuTime(Thread thread) throws JJVMTIException { super.getThreadCpuTime(thread); throw notImplemented; // TODO } @Override public long getTime() throws JJVMTIException { super.getTime(); return System.nanoTime(); } @Override public EnumSet<JVMTICapabilities.E> getPotentialCapabilities() throws JJVMTIException { super.getPotentialCapabilities(); return JVMTICapabilities.getPotentialCapabilities(env); } @Override public void addCapabilities(EnumSet<JVMTICapabilities.E> caps) throws JJVMTIException { super.addCapabilities(caps); JVMTICapabilities.addCapabilities(env, caps); } @Override public void relinquishCapabilities(EnumSet<JVMTICapabilities.E> caps) throws JJVMTIException { super.relinquishCapabilities(caps); JVMTICapabilities.relinquishCapabilities(env, caps); } @Override public int getAvailableProcessors() throws JJVMTIException { super.getAvailableProcessors(); return Runtime.getRuntime().availableProcessors(); } @Override public void addToBootstrapClassLoaderSearch(String path) throws JJVMTIException { super.addToBootstrapClassLoaderSearch(path); throw notImplemented; // TODO } @Override public void setVerboseFlag(int flag, boolean value) throws JJVMTIException { super.setVerboseFlag(flag, value); int error = JVMTISystem.setVerboseFlag(flag, value); if (error != JVMTI_ERROR_NONE) { throw new JJVMTIException(error); } } @Override public void addToSystemClassLoaderSearch(String path) throws JJVMTIException { super.addToSystemClassLoaderSearch(path); throw notImplemented; // TODO } @Override public MonitorStackDepthInfo[] getOwnedMonitorStackDepthInfo(Thread thread) throws JJVMTIException { super.getOwnedMonitorStackDepthInfo(thread); throw notImplemented; // TODO } @Override public long getObjectSize(Object object) throws JJVMTIException { super.getObjectSize(object); return Layout.size(Reference.fromJava(object)).toInt(); } @Override public void setBreakpoint(ClassMethodActor method, long location) throws JJVMTIException { super.setBreakpoint(method, location); JVMTIBreakpoints.setBreakpoint(method, location); } @Override public void clearBreakpoint(ClassMethodActor method, long location) throws JJVMTIException { super.clearBreakpoint(method, location); JVMTIBreakpoints.clearBreakpoint(method, location); } @Override public void setFieldAccessWatch(FieldActor field) throws JJVMTIException { super.setFieldAccessWatch(field); JVMTIFieldWatch.setWatch(field, JVMTIFieldWatch.ACCESS_STATE); } @Override public void clearFieldAccessWatch(FieldActor field) throws JJVMTIException { super.clearFieldAccessWatch(field); JVMTIFieldWatch.clearWatch(field, JVMTIFieldWatch.ACCESS_STATE); } @Override public void setFieldModificationWatch(FieldActor field) throws JJVMTIException { super.setFieldModificationWatch(field); JVMTIFieldWatch.setWatch(field, JVMTIFieldWatch.MODIFICATION_STATE); } @Override public void clearFieldModificationWatch(FieldActor field) throws JJVMTIException { super.clearFieldModificationWatch(field); JVMTIFieldWatch.clearWatch(field, JVMTIFieldWatch.MODIFICATION_STATE); } @Override public boolean isModifiableClass(ClassActor klass) throws JJVMTIException { super.isModifiableClass(klass); throw notImplemented; // TODO } @Override public String getClassSignature(ClassActor klass) throws JJVMTIException { super.getClassSignature(klass); return klass.typeDescriptor.string; } @Override public int getClassStatus(ClassActor klass) throws JJVMTIException { super.getClassStatus(klass); return JVMTIClassFunctions.getClassStatus(klass); } @Override public String getSourceFileName(ClassActor klass) throws JJVMTIException { super.getSourceFileName(klass); return klass.sourceFileName; } @Override public int getClassModifiers(ClassActor klass) throws JJVMTIException { super.getClassModifiers(klass); return klass.accessFlags(); } @Override public MethodActor[] getClassMethods(ClassActor klass) throws JJVMTIException { super.getClassMethods(klass); return JVMTIClassFunctions.getClassMethods(klass); } @Override public FieldActor[] getClassFields(ClassActor klass) throws JJVMTIException { super.getClassFields(klass); List<FieldActor> list = klass.getLocalFieldActors(); FieldActor[] result = new FieldActor[list.size()]; list.toArray(result); return result; } @Override public ClassActor[] getImplementedInterfaces(ClassActor klass) throws JJVMTIException { super.getImplementedInterfaces(klass); List<InterfaceActor> interfaceActors = klass.getLocalInterfaceActors(); ClassActor[] result = new ClassActor[interfaceActors.size()]; interfaceActors.toArray(result); return result; } @Override public boolean isInterface(ClassActor klass) throws JJVMTIException { super.isInterface(klass); return ClassActor.isInterface(klass.flags()); } @Override public boolean isArrayClass(ClassActor klass) throws JJVMTIException { super.isArrayClass(klass); return klass.isArrayClass(); } @Override public ClassLoader getClassLoader(ClassActor klass) throws JJVMTIException { super.getClassLoader(klass); return klass.classLoader; } @Override public String getFieldName(FieldActor field) throws JJVMTIException { super.getFieldName(field); return field.name(); } @Override public String getFieldSignature(FieldActor field) throws JJVMTIException { super.getFieldSignature(field); return field.descriptor().string; } @Override public ClassActor getFieldDeclaringClass(FieldActor field) throws JJVMTIException { super.getFieldDeclaringClass(field); return field.holder(); } @Override public int getFieldModifiers(FieldActor field) throws JJVMTIException { super.getFieldModifiers(field); return field.accessFlags(); } @Override public boolean isFieldSynthetic(FieldActor field) throws JJVMTIException { super.isFieldSynthetic(field); return (field.flags() & Actor.ACC_SYNTHETIC) != 0; } @Override public String getMethodName(MethodActor method) throws JJVMTIException { super.getMethodName(method); return method.name(); } @Override public String getMethodSignature(MethodActor method) throws JJVMTIException { super.getMethodSignature(method); return method.descriptor().string; } @Override public String getMethodGenericSignature(MethodActor method) throws JJVMTIException { super.getMethodGenericSignature(method); return method.genericSignatureString(); } @Override public ClassActor getMethodDeclaringClass(MethodActor method) throws JJVMTIException { super.getMethodDeclaringClass(method); return method.holder(); } @Override public int getMethodModifiers(MethodActor method) throws JJVMTIException { super.getMethodModifiers(method); return method.accessFlags(); } @Override public int getMaxLocals(ClassMethodActor method) throws JJVMTIException { super.getMaxLocals(method); return method.codeAttribute().maxLocals; } @Override public int getArgumentsSize(ClassMethodActor method) throws JJVMTIException { super.getArgumentsSize(method); return method.numberOfParameterSlots(); } @Override public LineNumberEntry[] getLineNumberTable(ClassMethodActor method) throws JJVMTIException { super.getLineNumberTable(method); return JVMTIClassFunctions.getLineNumberTable(method); } @Override public MethodLocation getMethodLocation(ClassMethodActor method) throws JJVMTIException { super.getMethodLocation(method); byte[] code = method.codeAttribute().code(); return new MethodLocation(0, code.length - 1); } @Override public LocalVariableEntry[] getLocalVariableTable(ClassMethodActor method) throws JJVMTIException { super.getLocalVariableTable(method); return JVMTIClassFunctions.getLocalVariableTable(method); } @Override public byte[] getBytecodes(ClassMethodActor method) throws JJVMTIException { super.getBytecodes(method); return method.code(); } @Override public boolean isMethodNative(MethodActor method) throws JJVMTIException { super.isMethodNative(method); return method.isNative(); } @Override public boolean isMethodSynthetic(MethodActor method) throws JJVMTIException { super.isMethodSynthetic(method); return (method.flags() & Actor.ACC_SYNTHETIC) != 0; } @Override public ClassActor[] getLoadedClasses() throws JJVMTIException { super.getLoadedClasses(); return JVMTIClassFunctions.getLoadedClassActors(); } @Override public ClassActor[] getClassLoaderClasses(ClassLoader loader) throws JJVMTIException { super.getClassLoaderClasses(loader); return JVMTIClassFunctions.getClassLoaderClasses(loader); } @Override public void redefineClasses(ClassDefinition[] classDefinitions) throws JJVMTIException { super.redefineClasses(classDefinitions); throw notImplemented; // TODO } @Override public String getSourceDebugExtension(ClassActor klass) throws JJVMTIException { super.getSourceDebugExtension(klass); throw new JJVMTI.JJVMTIException(JVMTI_ERROR_ABSENT_INFORMATION); } @Override public boolean isMethodObsolete(MethodActor method) throws JJVMTIException { super.isMethodObsolete(method); throw notImplemented; // TODO } @Override public ClassVersionInfo getClassVersionNumbers(ClassActor klass) throws JJVMTIException { super.getClassVersionNumbers(klass); return new ClassVersionInfo(klass.majorVersion, klass.minorVersion); } @Override public byte[] getConstantPool(ClassActor klass) throws JJVMTIException { super.getConstantPool(klass); throw notImplemented; // TODO } @Override public void retransformClasses(ClassActor[] klasses) throws JJVMTIException { super.retransformClasses(klasses); throw notImplemented; // TODO } @Override public void iterateThroughHeap(int filter, ClassActor classActor, HeapCallbacks heapCallbacks, Object userData) throws JJVMTIException { super.iterateThroughHeap(filter, classActor, heapCallbacks, userData); JVMTIHeapFunctions.iterateThroughHeap(env, filter, classActor, heapCallbacks, userData); } @Override public void iterateThroughHeapMax(int filter, ClassActor classActor, HeapCallbacks heapCallbacks, Object userData) throws JJVMTIException { super.iterateThroughHeapMax(filter, classActor, heapCallbacks, userData); JVMTIHeapFunctions.iterateThroughHeapMax(env, filter, classActor, heapCallbacks, userData); } }