| // jvmti.cc - JVMTI implementation |
| |
| /* Copyright (C) 2006 Free Software Foundation |
| |
| This file is part of libgcj. |
| |
| This software is copyrighted work licensed under the terms of the |
| Libgcj License. Please consult the file "LIBGCJ_LICENSE" for |
| details. */ |
| |
| #include <config.h> |
| #include <platform.h> |
| |
| #include <jvm.h> |
| #include <java-threads.h> |
| #include <java-gc.h> |
| #include <jvmti.h> |
| #include "jvmti-int.h" |
| |
| #include <gcj/method.h> |
| |
| #include <gnu/classpath/SystemProperties.h> |
| #include <gnu/gcj/runtime/BootClassLoader.h> |
| #include <java/lang/Class.h> |
| #include <java/lang/ClassLoader.h> |
| #include <java/lang/Object.h> |
| #include <java/lang/Thread.h> |
| #include <java/lang/Throwable.h> |
| #include <java/lang/VMClassLoader.h> |
| #include <java/lang/reflect/Field.h> |
| #include <java/lang/reflect/Modifier.h> |
| #include <java/util/Collection.h> |
| #include <java/util/HashMap.h> |
| #include <java/net/URL.h> |
| |
| static void check_enabled_events (void); |
| static void check_enabled_event (jvmtiEvent); |
| |
| namespace JVMTI |
| { |
| bool VMInit = false; |
| bool VMDeath = false; |
| bool ThreadStart = false; |
| bool ThreadEnd = false; |
| bool ClassFileLoadHook = false; |
| bool ClassLoad = false; |
| bool ClassPrepare = false; |
| bool VMStart = false; |
| bool Exception = false; |
| bool ExceptionCatch = false; |
| bool SingleStep = false; |
| bool FramePop = false; |
| bool Breakpoint = false; |
| bool FieldAccess = false; |
| bool FieldModification = false; |
| bool MethodEntry = false; |
| bool MethodExit = false; |
| bool NativeMethodBind = false; |
| bool CompiledMethodLoad = false; |
| bool CompiledMethodUnload = false; |
| bool DynamicCodeGenerated = false; |
| bool DataDumpRequest = false; |
| bool reserved72 = false; |
| bool MonitorWait = false; |
| bool MonitorWaited = false; |
| bool MonitorContendedEnter = false; |
| bool MonitorContendedEntered = false; |
| bool reserved77 = false; |
| bool reserved78 = false; |
| bool reserved79 = false; |
| bool reserved80 = false; |
| bool GarbageCollectionStart = false; |
| bool GarbageCollectionFinish = false; |
| bool ObjectFree = false; |
| bool VMObjectAlloc = false; |
| }; |
| |
| extern struct JNINativeInterface _Jv_JNIFunctions; |
| |
| struct _Jv_rawMonitorID |
| { |
| _Jv_Mutex_t mutex; |
| _Jv_ConditionVariable_t condition; |
| }; |
| |
| /* A simple linked list of all JVMTI environments. Since |
| events must be delivered to environments in the order |
| in which the environments were created, new environments |
| are added to the end of the list. */ |
| struct jvmti_env_list |
| { |
| jvmtiEnv *env; |
| struct jvmti_env_list *next; |
| }; |
| static struct jvmti_env_list *_jvmtiEnvironments = NULL; |
| static java::lang::Object *_envListLock = NULL; |
| #define FOREACH_ENVIRONMENT(Ele) \ |
| for (Ele = _jvmtiEnvironments; Ele != NULL; Ele = Ele->next) |
| |
| // Some commonly-used checks |
| |
| #define THREAD_DEFAULT_TO_CURRENT(Ajthread) \ |
| do \ |
| { \ |
| if (Ajthread == NULL) \ |
| Ajthread = java::lang::Thread::currentThread (); \ |
| } \ |
| while (0) |
| |
| #define THREAD_CHECK_VALID(Athread) \ |
| do \ |
| { \ |
| if (!java::lang::Thread::class$.isAssignableFrom (&(Athread->class$))) \ |
| return JVMTI_ERROR_INVALID_THREAD; \ |
| } \ |
| while (0) |
| |
| #define THREAD_CHECK_IS_ALIVE(Athread) \ |
| do \ |
| { \ |
| if (!Athread->isAlive ()) \ |
| return JVMTI_ERROR_THREAD_NOT_ALIVE; \ |
| } \ |
| while (0) |
| |
| // FIXME: if current phase is not set in Phases, |
| // return JVMTI_ERROR_WRONG_PHASE |
| #define REQUIRE_PHASE(Env, Phases) |
| |
| #define NULL_CHECK(Ptr) \ |
| do \ |
| { \ |
| if (Ptr == NULL) \ |
| return JVMTI_ERROR_NULL_POINTER; \ |
| } \ |
| while (0) |
| |
| #define ILLEGAL_ARGUMENT(Cond) \ |
| do \ |
| { \ |
| if ((Cond)) \ |
| return JVMTI_ERROR_ILLEGAL_ARGUMENT; \ |
| } \ |
| while (0) |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SuspendThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) |
| { |
| using namespace java::lang; |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| |
| Thread *t = reinterpret_cast<Thread *> (thread); |
| THREAD_CHECK_VALID (t); |
| THREAD_CHECK_IS_ALIVE (t); |
| |
| _Jv_Thread_t *data = _Jv_ThreadGetData (t); |
| _Jv_SuspendThread (data); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_ResumeThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) |
| { |
| using namespace java::lang; |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| |
| Thread *t = reinterpret_cast<Thread *> (thread); |
| THREAD_CHECK_VALID (t); |
| THREAD_CHECK_IS_ALIVE (t); |
| |
| _Jv_Thread_t *data = _Jv_ThreadGetData (t); |
| _Jv_ResumeThread (data); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_InterruptThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) |
| { |
| using namespace java::lang; |
| |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| // FIXME: capability handling? 'can_signal_thread' |
| if (thread == NULL) |
| return JVMTI_ERROR_INVALID_THREAD; |
| |
| Thread *real_thread = reinterpret_cast<Thread *> (thread); |
| THREAD_CHECK_VALID (real_thread); |
| THREAD_CHECK_IS_ALIVE (real_thread); |
| real_thread->interrupt(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_CreateRawMonitor (MAYBE_UNUSED jvmtiEnv *env, const char *name, |
| jrawMonitorID *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE); |
| NULL_CHECK (name); |
| NULL_CHECK (result); |
| *result = (jrawMonitorID) _Jv_MallocUnchecked (sizeof (_Jv_rawMonitorID)); |
| if (*result == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| _Jv_MutexInit (&(*result)->mutex); |
| _Jv_CondInit (&(*result)->condition); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_DestroyRawMonitor (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE); |
| // Note we have no better way of knowing whether this object is |
| // really a raw monitor. |
| if (monitor == NULL) |
| return JVMTI_ERROR_INVALID_MONITOR; |
| // FIXME: perform checks on monitor, release it if this thread owns |
| // it. |
| #ifdef _Jv_HaveMutexDestroy |
| _Jv_MutexDestroy (&monitor->mutex); |
| #endif |
| _Jv_Free (monitor); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_RawMonitorEnter (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor) |
| { |
| if (monitor == NULL) |
| return JVMTI_ERROR_INVALID_MONITOR; |
| _Jv_MutexLock (&monitor->mutex); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_RawMonitorExit (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor) |
| { |
| if (monitor == NULL) |
| return JVMTI_ERROR_INVALID_MONITOR; |
| if (_Jv_MutexUnlock (&monitor->mutex)) |
| return JVMTI_ERROR_NOT_MONITOR_OWNER; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_RawMonitorWait (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor, |
| jlong millis) |
| { |
| if (monitor == NULL) |
| return JVMTI_ERROR_INVALID_MONITOR; |
| int r = _Jv_CondWait (&monitor->condition, &monitor->mutex, millis, 0); |
| if (r == _JV_NOT_OWNER) |
| return JVMTI_ERROR_NOT_MONITOR_OWNER; |
| if (r == _JV_INTERRUPTED) |
| return JVMTI_ERROR_INTERRUPT; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_RawMonitorNotify (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor) |
| { |
| if (monitor == NULL) |
| return JVMTI_ERROR_INVALID_MONITOR; |
| if (_Jv_CondNotify (&monitor->condition, &monitor->mutex) == _JV_NOT_OWNER) |
| return JVMTI_ERROR_NOT_MONITOR_OWNER; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_RawMonitorNotifyAll (MAYBE_UNUSED jvmtiEnv *env, |
| jrawMonitorID monitor) |
| { |
| if (monitor == NULL) |
| return JVMTI_ERROR_INVALID_MONITOR; |
| if (_Jv_CondNotifyAll (&monitor->condition, &monitor->mutex) |
| == _JV_NOT_OWNER) |
| return JVMTI_ERROR_NOT_MONITOR_OWNER; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_Allocate (MAYBE_UNUSED jvmtiEnv *env, jlong size, |
| unsigned char **result) |
| { |
| ILLEGAL_ARGUMENT (size < 0); |
| NULL_CHECK (result); |
| if (size == 0) |
| *result = NULL; |
| else |
| { |
| *result = (unsigned char *) _Jv_MallocUnchecked (size); |
| if (*result == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| } |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_Deallocate (MAYBE_UNUSED jvmtiEnv *env, unsigned char *mem) |
| { |
| if (mem != NULL) |
| _Jv_Free (mem); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetClassModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jint *mods) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| // Don't bother checking KLASS' type. |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| NULL_CHECK (mods); |
| *mods = klass->getModifiers(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetClassMethods (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jint *count_ptr, jmethodID **methods_ptr) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| // FIXME: capability can_maintain_original_method_order |
| // Don't bother checking KLASS' type. |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| NULL_CHECK (count_ptr); |
| NULL_CHECK (methods_ptr); |
| *count_ptr = JvNumMethods(klass); |
| |
| *methods_ptr |
| = (jmethodID *) _Jv_MallocUnchecked (*count_ptr * sizeof (jmethodID)); |
| if (*methods_ptr == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| |
| jmethodID start = JvGetFirstMethod (klass); |
| for (jint i = 0; i < *count_ptr; ++i) |
| // FIXME: correct? |
| (*methods_ptr)[i] = start + i; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_IsInterface (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jboolean *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| NULL_CHECK (result); |
| *result = klass->isInterface(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_IsArrayClass (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jboolean *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| NULL_CHECK (result); |
| *result = klass->isArray(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetClassLoader (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jobject *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| NULL_CHECK (result); |
| *result = klass->getClassLoaderInternal(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetObjectHashCode (MAYBE_UNUSED jvmtiEnv *env, jobject obj, |
| jint *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (obj == NULL) |
| return JVMTI_ERROR_INVALID_OBJECT; |
| NULL_CHECK (result); |
| *result = _Jv_HashCode (obj); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetFieldModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jfieldID field, jint *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| if (field == NULL) |
| return JVMTI_ERROR_INVALID_FIELDID; |
| NULL_CHECK (result); |
| *result = field->getModifiers(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_IsFieldSynthetic (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jfieldID field, jboolean *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| if (field == NULL) |
| return JVMTI_ERROR_INVALID_FIELDID; |
| NULL_CHECK (result); |
| |
| // FIXME: capability can_get_synthetic_attribute |
| *result = ((field->getModifiers() & java::lang::reflect::Modifier::SYNTHETIC) |
| != 0); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetMethodModifiers (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, |
| jint *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (method == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| NULL_CHECK (result); |
| |
| // FIXME: mask off some internal bits... |
| *result = method->accflags; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_IsMethodNative (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, |
| jboolean *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (method == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| NULL_CHECK (result); |
| |
| *result = ((method->accflags & java::lang::reflect::Modifier::NATIVE) != 0); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_IsMethodSynthetic (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, |
| jboolean *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (method == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| NULL_CHECK (result); |
| |
| // FIXME capability can_get_synthetic_attribute |
| |
| *result = ((method->accflags & java::lang::reflect::Modifier::SYNTHETIC) |
| != 0); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetClassLoaderClasses (MAYBE_UNUSED jvmtiEnv *env, |
| jobject init_loader, |
| jint *count_ptr, |
| jclass **result_ptr) |
| { |
| using namespace java::lang; |
| using namespace java::util; |
| |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| NULL_CHECK (count_ptr); |
| NULL_CHECK (result_ptr); |
| |
| ClassLoader *loader = (ClassLoader *) init_loader; |
| if (loader == NULL) |
| loader = VMClassLoader::bootLoader; |
| |
| Collection *values = loader->loadedClasses->values(); |
| jobjectArray array = values->toArray(); |
| *count_ptr = array->length; |
| jobject *elts = elements (array); |
| jclass *result |
| = (jclass *) _Jv_MallocUnchecked (*count_ptr * sizeof (jclass)); |
| if (result == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| |
| // FIXME: JNI references... |
| memcpy (result, elts, *count_ptr * sizeof (jclass)); |
| |
| *result_ptr = result; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_ForceGarbageCollection (MAYBE_UNUSED jvmtiEnv *env) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| _Jv_RunGC(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetJNIFunctionTable (MAYBE_UNUSED jvmtiEnv *env, |
| const jniNativeInterface *function_table) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| NULL_CHECK (function_table); |
| memcpy (&_Jv_JNIFunctions, function_table, sizeof (jniNativeInterface)); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetJNIFunctionTable (MAYBE_UNUSED jvmtiEnv *env, |
| jniNativeInterface **function_table) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| NULL_CHECK (function_table); |
| *function_table |
| = (jniNativeInterface *) _Jv_MallocUnchecked (sizeof (jniNativeInterface)); |
| if (*function_table == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| memcpy (*function_table, &_Jv_JNIFunctions, sizeof (jniNativeInterface)); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env) |
| { |
| NULL_CHECK (env); |
| |
| if (_jvmtiEnvironments == NULL) |
| return JVMTI_ERROR_INVALID_ENVIRONMENT; |
| else |
| { |
| JvSynchronize dummy (_envListLock); |
| if (_jvmtiEnvironments->env == env) |
| { |
| struct jvmti_env_list *next = _jvmtiEnvironments->next; |
| _Jv_Free (_jvmtiEnvironments); |
| _jvmtiEnvironments = next; |
| } |
| else |
| { |
| struct jvmti_env_list *e = _jvmtiEnvironments; |
| while (e->next != NULL && e->next->env != env) |
| e = e->next; |
| if (e->next == NULL) |
| return JVMTI_ERROR_INVALID_ENVIRONMENT; |
| |
| struct jvmti_env_list *next = e->next->next; |
| _Jv_Free (e->next); |
| e->next = next; |
| } |
| } |
| |
| _Jv_Free (env); |
| |
| check_enabled_events (); |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetSystemProperty (MAYBE_UNUSED jvmtiEnv *env, const char *property, |
| char **result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE); |
| NULL_CHECK (property); |
| NULL_CHECK (result); |
| |
| jstring name = JvNewStringUTF(property); |
| jstring result_str = gnu::classpath::SystemProperties::getProperty(name); |
| |
| if (result_str == NULL) |
| return JVMTI_ERROR_NOT_AVAILABLE; |
| |
| int len = JvGetStringUTFLength (result_str); |
| *result = (char *) _Jv_MallocUnchecked (len + 1); |
| if (*result == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| JvGetStringUTFRegion (result_str, 0, result_str->length(), *result); |
| (*result)[len] = '\0'; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetSystemProperty (MAYBE_UNUSED jvmtiEnv *env, const char *property, |
| const char *value) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD); |
| |
| NULL_CHECK (property); |
| if (value == NULL) |
| { |
| // FIXME: When would a property not be writeable? |
| return JVMTI_ERROR_NONE; |
| } |
| |
| jstring prop_str = JvNewStringUTF(property); |
| jstring value_str = JvNewStringUTF(value); |
| gnu::classpath::SystemProperties::setProperty(prop_str, value_str); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetTime (MAYBE_UNUSED jvmtiEnv *env, jlong *nanos_ptr) |
| { |
| NULL_CHECK (nanos_ptr); |
| *nanos_ptr = _Jv_platform_nanotime(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetAvailableProcessors (MAYBE_UNUSED jvmtiEnv *env, |
| jint *nprocessors_ptr) |
| { |
| NULL_CHECK (nprocessors_ptr); |
| #ifdef _SC_NPROCESSORS_ONLN |
| *nprocessors_ptr = sysconf(_SC_NPROCESSORS_ONLN); |
| #else |
| *nprocessors_ptr = 1; |
| #endif |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_AddToBootstrapClassLoaderSearch (MAYBE_UNUSED jvmtiEnv *env, |
| const char *segment) |
| { |
| using namespace java::lang; |
| using namespace java::net; |
| using namespace gnu::gcj::runtime; |
| |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD); |
| NULL_CHECK (segment); |
| |
| jstring str_segment = JvNewStringUTF(segment); |
| URL *url; |
| try |
| { |
| url = new URL(JvNewStringUTF("file"), NULL, str_segment); |
| } |
| catch (jthrowable ignore) |
| { |
| return JVMTI_ERROR_ILLEGAL_ARGUMENT; |
| } |
| |
| BootClassLoader *loader = VMClassLoader::bootLoader; |
| // Don't call this too early. |
| // assert (loader != NULL); |
| loader->addURL(url); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetVerboseFlag (MAYBE_UNUSED jvmtiEnv *env, jvmtiVerboseFlag flag, |
| jboolean value) |
| { |
| switch (flag) |
| { |
| case JVMTI_VERBOSE_OTHER: |
| case JVMTI_VERBOSE_GC: |
| case JVMTI_VERBOSE_JNI: |
| // Ignore. |
| break; |
| case JVMTI_VERBOSE_CLASS: |
| gcj::verbose_class_flag = value; |
| break; |
| default: |
| return JVMTI_ERROR_ILLEGAL_ARGUMENT; |
| } |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetObjectSize (MAYBE_UNUSED jvmtiEnv *env, jobject object, |
| jlong *result) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| if (object == NULL) |
| return JVMTI_ERROR_INVALID_OBJECT; |
| NULL_CHECK (result); |
| |
| jclass klass = object->getClass(); |
| if (klass->isArray()) |
| { |
| jclass comp = klass->getComponentType(); |
| jint base |
| = (jint) (_Jv_uintptr_t) _Jv_GetArrayElementFromElementType(NULL, |
| klass->getComponentType()); |
| // FIXME: correct for primitive types? |
| jint compSize = comp->size(); |
| __JArray *array = (__JArray *) object; |
| *result = base + array->length * compSize; |
| } |
| else |
| { |
| // Note that if OBJECT is a String then it may (if |
| // str->data==str) take more space. Do we care? |
| *result = klass->size(); |
| } |
| return JVMTI_ERROR_NONE; |
| } |
| |
| /* An event is enabled only if it has both an event handler |
| and it is enabled in the environment. */ |
| static void |
| check_enabled_event (jvmtiEvent type) |
| { |
| bool *enabled; |
| int offset; |
| |
| #define GET_OFFSET(Event) \ |
| do \ |
| { \ |
| enabled = &JVMTI::Event; \ |
| offset = offsetof (jvmtiEventCallbacks, Event); \ |
| } \ |
| while (0) |
| |
| switch (type) |
| { |
| case JVMTI_EVENT_VM_INIT: |
| GET_OFFSET (VMInit); |
| break; |
| |
| case JVMTI_EVENT_VM_DEATH: |
| GET_OFFSET (VMDeath); |
| break; |
| |
| case JVMTI_EVENT_THREAD_START: |
| GET_OFFSET (ThreadStart); |
| break; |
| |
| case JVMTI_EVENT_THREAD_END: |
| GET_OFFSET (ThreadEnd); |
| break; |
| |
| case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK: |
| GET_OFFSET (ClassFileLoadHook); |
| break; |
| |
| case JVMTI_EVENT_CLASS_LOAD: |
| GET_OFFSET (ClassLoad); |
| break; |
| |
| case JVMTI_EVENT_CLASS_PREPARE: |
| GET_OFFSET (ClassPrepare); |
| break; |
| |
| case JVMTI_EVENT_VM_START: |
| GET_OFFSET (VMStart); |
| break; |
| |
| case JVMTI_EVENT_EXCEPTION: |
| GET_OFFSET (Exception); |
| break; |
| |
| case JVMTI_EVENT_EXCEPTION_CATCH: |
| GET_OFFSET (ExceptionCatch); |
| break; |
| |
| case JVMTI_EVENT_SINGLE_STEP: |
| GET_OFFSET (SingleStep); |
| break; |
| |
| case JVMTI_EVENT_FRAME_POP: |
| GET_OFFSET (FramePop); |
| break; |
| |
| case JVMTI_EVENT_BREAKPOINT: |
| GET_OFFSET (Breakpoint); |
| break; |
| |
| case JVMTI_EVENT_FIELD_ACCESS: |
| GET_OFFSET (FieldAccess); |
| break; |
| |
| case JVMTI_EVENT_FIELD_MODIFICATION: |
| GET_OFFSET (FieldModification); |
| break; |
| |
| case JVMTI_EVENT_METHOD_ENTRY: |
| GET_OFFSET (MethodEntry); |
| break; |
| |
| case JVMTI_EVENT_METHOD_EXIT: |
| GET_OFFSET (MethodExit); |
| break; |
| |
| case JVMTI_EVENT_NATIVE_METHOD_BIND: |
| GET_OFFSET (NativeMethodBind); |
| break; |
| |
| case JVMTI_EVENT_COMPILED_METHOD_LOAD: |
| GET_OFFSET (CompiledMethodLoad); |
| break; |
| |
| case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: |
| GET_OFFSET (CompiledMethodUnload); |
| break; |
| |
| case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: |
| GET_OFFSET (DynamicCodeGenerated); |
| break; |
| |
| case JVMTI_EVENT_DATA_DUMP_REQUEST: |
| GET_OFFSET (DataDumpRequest); |
| break; |
| |
| case JVMTI_EVENT_MONITOR_WAIT: |
| GET_OFFSET (MonitorWait); |
| break; |
| |
| case JVMTI_EVENT_MONITOR_WAITED: |
| GET_OFFSET (MonitorWaited); |
| break; |
| |
| case JVMTI_EVENT_MONITOR_CONTENDED_ENTER: |
| GET_OFFSET (MonitorContendedEnter); |
| break; |
| |
| case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED: |
| GET_OFFSET (MonitorContendedEntered); |
| break; |
| |
| case JVMTI_EVENT_GARBAGE_COLLECTION_START: |
| GET_OFFSET (GarbageCollectionStart); |
| break; |
| |
| case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: |
| GET_OFFSET (GarbageCollectionFinish); |
| break; |
| |
| case JVMTI_EVENT_OBJECT_FREE: |
| GET_OFFSET (ObjectFree); |
| break; |
| |
| case JVMTI_EVENT_VM_OBJECT_ALLOC: |
| GET_OFFSET (VMObjectAlloc); |
| break; |
| |
| default: |
| fprintf (stderr, |
| "libgcj: check_enabled_event for unknown JVMTI event (%d)\n", |
| (int) type); |
| return; |
| } |
| #undef GET_OFFSET |
| |
| int index = EVENT_INDEX (type); // safe since caller checks this |
| |
| JvSynchronize dummy (_envListLock); |
| struct jvmti_env_list *e; |
| FOREACH_ENVIRONMENT (e) |
| { |
| char *addr |
| = reinterpret_cast<char *> (&e->env->callbacks) + offset; |
| void **callback = reinterpret_cast<void **> (addr); |
| if (e->env->enabled[index] && *callback != NULL) |
| { |
| *enabled = true; |
| return; |
| } |
| } |
| |
| *enabled = false; |
| } |
| |
| static void |
| check_enabled_events () |
| { |
| check_enabled_event (JVMTI_EVENT_VM_INIT); |
| check_enabled_event (JVMTI_EVENT_VM_DEATH); |
| check_enabled_event (JVMTI_EVENT_THREAD_START); |
| check_enabled_event (JVMTI_EVENT_THREAD_END); |
| check_enabled_event (JVMTI_EVENT_CLASS_FILE_LOAD_HOOK); |
| check_enabled_event (JVMTI_EVENT_CLASS_LOAD); |
| check_enabled_event (JVMTI_EVENT_CLASS_PREPARE); |
| check_enabled_event (JVMTI_EVENT_VM_START); |
| check_enabled_event (JVMTI_EVENT_EXCEPTION); |
| check_enabled_event (JVMTI_EVENT_EXCEPTION_CATCH); |
| check_enabled_event (JVMTI_EVENT_SINGLE_STEP); |
| check_enabled_event (JVMTI_EVENT_FRAME_POP); |
| check_enabled_event (JVMTI_EVENT_BREAKPOINT); |
| check_enabled_event (JVMTI_EVENT_FIELD_ACCESS); |
| check_enabled_event (JVMTI_EVENT_FIELD_MODIFICATION); |
| check_enabled_event (JVMTI_EVENT_METHOD_ENTRY); |
| check_enabled_event (JVMTI_EVENT_METHOD_EXIT); |
| check_enabled_event (JVMTI_EVENT_NATIVE_METHOD_BIND); |
| check_enabled_event (JVMTI_EVENT_COMPILED_METHOD_LOAD); |
| check_enabled_event (JVMTI_EVENT_COMPILED_METHOD_UNLOAD); |
| check_enabled_event (JVMTI_EVENT_DYNAMIC_CODE_GENERATED); |
| check_enabled_event (JVMTI_EVENT_DATA_DUMP_REQUEST); |
| check_enabled_event (JVMTI_EVENT_MONITOR_WAIT); |
| check_enabled_event (JVMTI_EVENT_MONITOR_WAITED); |
| check_enabled_event (JVMTI_EVENT_MONITOR_CONTENDED_ENTER); |
| check_enabled_event (JVMTI_EVENT_MONITOR_CONTENDED_ENTERED); |
| check_enabled_event (JVMTI_EVENT_GARBAGE_COLLECTION_START); |
| check_enabled_event (JVMTI_EVENT_GARBAGE_COLLECTION_FINISH); |
| check_enabled_event (JVMTI_EVENT_OBJECT_FREE); |
| check_enabled_event (JVMTI_EVENT_VM_OBJECT_ALLOC); |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetEventNotificationMode (jvmtiEnv *env, jvmtiEventMode mode, |
| jvmtiEvent type, jthread event_thread, ...) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE); |
| |
| if (event_thread != NULL) |
| { |
| using namespace java::lang; |
| Thread *t = reinterpret_cast<Thread *> (event_thread); |
| THREAD_CHECK_VALID (t); |
| THREAD_CHECK_IS_ALIVE (t); |
| } |
| |
| bool enabled; |
| switch (mode) |
| { |
| case JVMTI_DISABLE: |
| enabled = false; |
| break; |
| case JVMTI_ENABLE: |
| enabled = true; |
| break; |
| |
| default: |
| return JVMTI_ERROR_ILLEGAL_ARGUMENT; |
| } |
| |
| switch (type) |
| { |
| case JVMTI_EVENT_VM_INIT: |
| case JVMTI_EVENT_VM_DEATH: |
| case JVMTI_EVENT_THREAD_START: |
| case JVMTI_EVENT_VM_START: |
| case JVMTI_EVENT_COMPILED_METHOD_LOAD: |
| case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: |
| case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: |
| case JVMTI_EVENT_DATA_DUMP_REQUEST: |
| ILLEGAL_ARGUMENT (event_thread != NULL); |
| break; |
| |
| case JVMTI_EVENT_THREAD_END: |
| case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK: |
| case JVMTI_EVENT_CLASS_LOAD: |
| case JVMTI_EVENT_CLASS_PREPARE: |
| case JVMTI_EVENT_EXCEPTION: |
| case JVMTI_EVENT_EXCEPTION_CATCH: |
| case JVMTI_EVENT_SINGLE_STEP: |
| case JVMTI_EVENT_FRAME_POP: |
| case JVMTI_EVENT_BREAKPOINT: |
| case JVMTI_EVENT_FIELD_ACCESS: |
| case JVMTI_EVENT_FIELD_MODIFICATION: |
| case JVMTI_EVENT_METHOD_ENTRY: |
| case JVMTI_EVENT_METHOD_EXIT: |
| case JVMTI_EVENT_NATIVE_METHOD_BIND: |
| case JVMTI_EVENT_MONITOR_WAIT: |
| case JVMTI_EVENT_MONITOR_WAITED: |
| case JVMTI_EVENT_MONITOR_CONTENDED_ENTER: |
| case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED: |
| case JVMTI_EVENT_GARBAGE_COLLECTION_START: |
| case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: |
| case JVMTI_EVENT_OBJECT_FREE: |
| case JVMTI_EVENT_VM_OBJECT_ALLOC: |
| break; |
| |
| default: |
| return JVMTI_ERROR_INVALID_EVENT_TYPE; |
| } |
| |
| env->thread[EVENT_INDEX(type)] = event_thread; |
| env->enabled[EVENT_INDEX(type)] = enabled; |
| check_enabled_event (type); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetEventCallbacks (jvmtiEnv *env, |
| const jvmtiEventCallbacks *callbacks, |
| jint size_of_callbacks) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE); |
| ILLEGAL_ARGUMENT (size_of_callbacks < 0); |
| |
| // Copy the list of callbacks into the environment |
| memcpy (&env->callbacks, callbacks, sizeof (jvmtiEventCallbacks)); |
| |
| /* Check which events are now enabeld (JVMTI makes no requirements |
| about the order in which SetEventCallbacks and SetEventNotifications |
| are called. So we must check all events here. */ |
| check_enabled_events (); |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetErrorName (MAYBE_UNUSED jvmtiEnv *env, jvmtiError error, |
| char **name_ptr) |
| { |
| NULL_CHECK (name_ptr); |
| |
| const char *name; |
| switch (error) |
| { |
| case JVMTI_ERROR_NONE: |
| name = "none"; |
| break; |
| |
| case JVMTI_ERROR_NULL_POINTER: |
| name = "null pointer"; |
| break; |
| |
| case JVMTI_ERROR_OUT_OF_MEMORY: |
| name = "out of memory"; |
| break; |
| |
| case JVMTI_ERROR_ACCESS_DENIED: |
| name = "access denied"; |
| break; |
| |
| case JVMTI_ERROR_WRONG_PHASE: |
| name = "wrong phase"; |
| break; |
| |
| case JVMTI_ERROR_INTERNAL: |
| name = "internal error"; |
| break; |
| |
| case JVMTI_ERROR_UNATTACHED_THREAD: |
| name = "unattached thread"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_ENVIRONMENT: |
| name = "invalid environment"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_PRIORITY: |
| name = "invalid priority"; |
| break; |
| |
| case JVMTI_ERROR_THREAD_NOT_SUSPENDED: |
| name = "thread not suspended"; |
| break; |
| |
| case JVMTI_ERROR_THREAD_SUSPENDED: |
| name = "thread suspended"; |
| break; |
| |
| case JVMTI_ERROR_THREAD_NOT_ALIVE: |
| name = "thread not alive"; |
| break; |
| |
| case JVMTI_ERROR_CLASS_NOT_PREPARED: |
| name = "class not prepared"; |
| break; |
| |
| case JVMTI_ERROR_NO_MORE_FRAMES: |
| name = "no more frames"; |
| break; |
| |
| case JVMTI_ERROR_OPAQUE_FRAME: |
| name = "opaque frame"; |
| break; |
| |
| case JVMTI_ERROR_DUPLICATE: |
| name = "duplicate"; |
| break; |
| |
| case JVMTI_ERROR_NOT_FOUND: |
| name = "not found"; |
| break; |
| |
| case JVMTI_ERROR_NOT_MONITOR_OWNER: |
| name = "not monitor owner"; |
| break; |
| |
| case JVMTI_ERROR_INTERRUPT: |
| name = "interrupted"; |
| break; |
| |
| case JVMTI_ERROR_UNMODIFIABLE_CLASS: |
| name = "unmodifiable class"; |
| break; |
| |
| case JVMTI_ERROR_NOT_AVAILABLE: |
| name = "not available"; |
| break; |
| |
| case JVMTI_ERROR_ABSENT_INFORMATION: |
| name = "absent information"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_EVENT_TYPE: |
| name = "invalid event type"; |
| break; |
| |
| case JVMTI_ERROR_NATIVE_METHOD: |
| name = "native method"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_THREAD: |
| name = "invalid thread"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_THREAD_GROUP: |
| name = "invalid thread group"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_OBJECT: |
| name = "invalid object"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_CLASS: |
| name = "invalid class"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_METHODID: |
| name = "invalid method ID"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_LOCATION: |
| name = "invalid location"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_FIELDID: |
| name = "invalid field ID"; |
| break; |
| |
| case JVMTI_ERROR_TYPE_MISMATCH: |
| name = "type mismatch"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_SLOT: |
| name = "invalid slot"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_MONITOR: |
| name = "invalid monitor"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_CLASS_FORMAT: |
| name = "invalid class format"; |
| break; |
| |
| case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION: |
| name = "circular class definition"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED: |
| name = "unsupported redefinition: method added"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED: |
| name = "unsupported redefinition: schema changed"; |
| break; |
| |
| case JVMTI_ERROR_INVALID_TYPESTATE: |
| name = "invalid type state"; |
| break; |
| |
| case JVMTI_ERROR_FAILS_VERIFICATION: |
| name = "fails verification"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED: |
| name = "unsupported redefinition: hierarchy changed"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED: |
| name = "unsupported redefinition: method deleted"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_VERSION: |
| name = "unsupported version"; |
| break; |
| |
| case JVMTI_ERROR_NAMES_DONT_MATCH: |
| name = "names do not match"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED: |
| name = "unsupported redefinition: class modifiers changed"; |
| break; |
| |
| case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED: |
| name = "unsupported redefinition: method modifiers changed"; |
| break; |
| |
| case JVMTI_ERROR_MUST_POSSESS_CAPABILITY: |
| name = "must possess capability"; |
| break; |
| |
| case JVMTI_ERROR_ILLEGAL_ARGUMENT: |
| name = "illegal argument"; |
| break; |
| |
| default: |
| return JVMTI_ERROR_ILLEGAL_ARGUMENT; |
| } |
| |
| *name_ptr = (char *) _Jv_MallocUnchecked (strlen (name) + 1); |
| if (*name_ptr == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| |
| strcpy (*name_ptr, name); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| #define RESERVED NULL |
| #define UNIMPLEMENTED NULL |
| |
| struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = |
| { |
| RESERVED, // reserved1 |
| _Jv_JVMTI_SetEventNotificationMode, // SetEventNotificationMode |
| RESERVED, // reserved3 |
| UNIMPLEMENTED, // GetAllThreads |
| _Jv_JVMTI_SuspendThread, // SuspendThread |
| _Jv_JVMTI_ResumeThread, // ResumeThread |
| UNIMPLEMENTED, // StopThread |
| _Jv_JVMTI_InterruptThread, // InterruptThread |
| UNIMPLEMENTED, // GetThreadInfo |
| UNIMPLEMENTED, // GetOwnedMonitorInfo |
| UNIMPLEMENTED, // GetCurrentContendedMonitor |
| UNIMPLEMENTED, // RunAgentThread |
| UNIMPLEMENTED, // GetTopThreadGroups |
| UNIMPLEMENTED, // GetThreadGroupInfo |
| UNIMPLEMENTED, // GetThreadGroupChildren |
| UNIMPLEMENTED, // GetFrameCount |
| UNIMPLEMENTED, // GetThreadState |
| RESERVED, // reserved18 |
| UNIMPLEMENTED, // GetFrameLocation |
| UNIMPLEMENTED, // NotifyPopFrame |
| UNIMPLEMENTED, // GetLocalObject |
| UNIMPLEMENTED, // GetLocalInt |
| UNIMPLEMENTED, // GetLocalLong |
| UNIMPLEMENTED, // GetLocalFloat |
| UNIMPLEMENTED, // GetLocalDouble |
| UNIMPLEMENTED, // SetLocalObject |
| UNIMPLEMENTED, // SetLocalInt |
| UNIMPLEMENTED, // SetLocalLong |
| UNIMPLEMENTED, // SetLocalFloat |
| UNIMPLEMENTED, // SetLocalDouble |
| _Jv_JVMTI_CreateRawMonitor, // CreateRawMonitor |
| _Jv_JVMTI_DestroyRawMonitor, // DestroyRawMonitor |
| _Jv_JVMTI_RawMonitorEnter, // RawMonitorEnter |
| _Jv_JVMTI_RawMonitorExit, // RawMonitorExit |
| _Jv_JVMTI_RawMonitorWait, // RawMonitorWait |
| _Jv_JVMTI_RawMonitorNotify, // RawMonitorNotify |
| _Jv_JVMTI_RawMonitorNotifyAll, // RawMonitorNotifyAll |
| UNIMPLEMENTED, // SetBreakpoint |
| UNIMPLEMENTED, // ClearBreakpoint |
| RESERVED, // reserved40 |
| UNIMPLEMENTED, // SetFieldAccessWatch |
| UNIMPLEMENTED, // ClearFieldAccessWatch |
| UNIMPLEMENTED, // SetFieldModificationWatch |
| UNIMPLEMENTED, // ClearFieldModificationWatch |
| RESERVED, // reserved45 |
| _Jv_JVMTI_Allocate, // Allocate |
| _Jv_JVMTI_Deallocate, // Deallocate |
| UNIMPLEMENTED, // GetClassSignature |
| UNIMPLEMENTED, // GetClassStatus |
| UNIMPLEMENTED, // GetSourceFileName |
| _Jv_JVMTI_GetClassModifiers, // GetClassModifiers |
| _Jv_JVMTI_GetClassMethods, // GetClassMethods |
| UNIMPLEMENTED, // GetClassFields |
| UNIMPLEMENTED, // GetImplementedInterfaces |
| _Jv_JVMTI_IsInterface, // IsInterface |
| _Jv_JVMTI_IsArrayClass, // IsArrayClass |
| _Jv_JVMTI_GetClassLoader, // GetClassLoader |
| _Jv_JVMTI_GetObjectHashCode, // GetObjectHashCode |
| UNIMPLEMENTED, // GetObjectMonitorUsage |
| UNIMPLEMENTED, // GetFieldName |
| UNIMPLEMENTED, // GetFieldDeclaringClass |
| _Jv_JVMTI_GetFieldModifiers, // GetFieldModifiers |
| _Jv_JVMTI_IsFieldSynthetic, // IsFieldSynthetic |
| UNIMPLEMENTED, // GetMethodName |
| UNIMPLEMENTED, // GetMethodDeclaringClass |
| _Jv_JVMTI_GetMethodModifiers, // GetMethodModifers |
| RESERVED, // reserved67 |
| UNIMPLEMENTED, // GetMaxLocals |
| UNIMPLEMENTED, // GetArgumentsSize |
| UNIMPLEMENTED, // GetLineNumberTable |
| UNIMPLEMENTED, // GetMethodLocation |
| UNIMPLEMENTED, // GetLocalVariableTable |
| RESERVED, // reserved73 |
| RESERVED, // reserved74 |
| UNIMPLEMENTED, // GetBytecodes |
| _Jv_JVMTI_IsMethodNative, // IsMethodNative |
| _Jv_JVMTI_IsMethodSynthetic, // IsMethodSynthetic |
| UNIMPLEMENTED, // GetLoadedClasses |
| _Jv_JVMTI_GetClassLoaderClasses, // GetClassLoaderClasses |
| UNIMPLEMENTED, // PopFrame |
| RESERVED, // reserved81 |
| RESERVED, // reserved82 |
| RESERVED, // reserved83 |
| RESERVED, // reserved84 |
| RESERVED, // reserved85 |
| RESERVED, // reserved86 |
| UNIMPLEMENTED, // RedefineClasses |
| UNIMPLEMENTED, // GetVersionNumber |
| UNIMPLEMENTED, // GetCapabilities |
| UNIMPLEMENTED, // GetSourceDebugExtension |
| UNIMPLEMENTED, // IsMethodObsolete |
| UNIMPLEMENTED, // SuspendThreadList |
| UNIMPLEMENTED, // ResumeThreadList |
| RESERVED, // reserved94 |
| RESERVED, // reserved95 |
| RESERVED, // reserved96 |
| RESERVED, // reserved97 |
| RESERVED, // reserved98 |
| RESERVED, // reserved99 |
| UNIMPLEMENTED, // GetAllStackTraces |
| UNIMPLEMENTED, // GetThreadListStackTraces |
| UNIMPLEMENTED, // GetThreadLocalStorage |
| UNIMPLEMENTED, // SetThreadLocalStorage |
| UNIMPLEMENTED, // GetStackTrace |
| RESERVED, // reserved105 |
| UNIMPLEMENTED, // GetTag |
| UNIMPLEMENTED, // SetTag |
| _Jv_JVMTI_ForceGarbageCollection, // ForceGarbageCollection |
| UNIMPLEMENTED, // IterateOverObjectsReachable |
| UNIMPLEMENTED, // IterateOverReachableObjects |
| UNIMPLEMENTED, // IterateOverHeap |
| UNIMPLEMENTED, // IterateOverInstanceOfClass |
| RESERVED, // reserved113 |
| UNIMPLEMENTED, // GetObjectsWithTags |
| RESERVED, // reserved115 |
| RESERVED, // reserved116 |
| RESERVED, // reserved117 |
| RESERVED, // reserved118 |
| RESERVED, // reserved119 |
| _Jv_JVMTI_SetJNIFunctionTable, // SetJNIFunctionTable |
| _Jv_JVMTI_GetJNIFunctionTable, // GetJNIFunctionTable |
| _Jv_JVMTI_SetEventCallbacks, // SetEventCallbacks |
| UNIMPLEMENTED, // GenerateEvents |
| UNIMPLEMENTED, // GetExtensionFunctions |
| UNIMPLEMENTED, // GetExtensionEvents |
| UNIMPLEMENTED, // SetExtensionEventCallback |
| _Jv_JVMTI_DisposeEnvironment, // DisposeEnvironment |
| _Jv_JVMTI_GetErrorName, // GetErrorName |
| UNIMPLEMENTED, // GetJLocationFormat |
| UNIMPLEMENTED, // GetSystemProperties |
| _Jv_JVMTI_GetSystemProperty, // GetSystemProperty |
| _Jv_JVMTI_SetSystemProperty, // SetSystemProperty |
| UNIMPLEMENTED, // GetPhase |
| UNIMPLEMENTED, // GetCurrentThreadCpuTimerInfo |
| UNIMPLEMENTED, // GetCurrentThreadCpuTime |
| UNIMPLEMENTED, // GetThreadCpuTimerInfo |
| UNIMPLEMENTED, // GetThreadCpuTime |
| UNIMPLEMENTED, // GetTimerInfo |
| _Jv_JVMTI_GetTime, // GetTime |
| UNIMPLEMENTED, // GetPotentialCapabilities |
| RESERVED, // reserved141 |
| UNIMPLEMENTED, // AddCapabilities |
| UNIMPLEMENTED, // RelinquishCapabilities |
| _Jv_JVMTI_GetAvailableProcessors, // GetAvailableProcessors |
| RESERVED, // reserved145 |
| RESERVED, // reserved146 |
| UNIMPLEMENTED, // GetEnvironmentLocalStorage |
| UNIMPLEMENTED, // SetEnvironmentLocalStorage |
| _Jv_JVMTI_AddToBootstrapClassLoaderSearch, // AddToBootstrapClassLoaderSearch |
| _Jv_JVMTI_SetVerboseFlag, // SetVerboseFlag |
| RESERVED, // reserved151 |
| RESERVED, // reserved152 |
| RESERVED, // reserved153 |
| _Jv_JVMTI_GetObjectSize // GetObjectSize |
| }; |
| |
| _Jv_JVMTIEnv * |
| _Jv_GetJVMTIEnv (void) |
| { |
| _Jv_JVMTIEnv *env |
| = (_Jv_JVMTIEnv *) _Jv_MallocUnchecked (sizeof (_Jv_JVMTIEnv)); |
| env->p = &_Jv_JVMTI_Interface; |
| |
| { |
| JvSynchronize dummy (_envListLock); |
| struct jvmti_env_list *element |
| = (struct jvmti_env_list *) _Jv_MallocUnchecked (sizeof (struct jvmti_env_list)); |
| element->env = env; |
| element->next = NULL; |
| |
| if (_jvmtiEnvironments == NULL) |
| _jvmtiEnvironments = element; |
| else |
| { |
| struct jvmti_env_list *e; |
| for (e = _jvmtiEnvironments; e->next != NULL; e = e->next) |
| ; |
| e->next = element; |
| } |
| } |
| |
| return env; |
| } |
| |
| void |
| _Jv_JVMTI_Init () |
| { |
| _jvmtiEnvironments = NULL; |
| _envListLock = new java::lang::Object (); |
| |
| // No environments, so this should set all JVMTI:: members to false |
| check_enabled_events (); |
| } |
| |
| static void |
| post_event (jvmtiEnv *env, jvmtiEvent type, jthread event_thread, va_list args) |
| { |
| #define ARG(Type,Name) Type Name = (Type) va_arg (args, Type) |
| |
| #define GET_BOOLEAN_ARG(Name) \ |
| ARG (int, b); \ |
| jboolean Name = (b == 0) ? false : true |
| |
| #define GET_CHAR_ARG(Name) \ |
| ARG (int, c); \ |
| char Name = static_cast<char> (c) |
| |
| switch (type) |
| { |
| case JVMTI_EVENT_VM_INIT: |
| if (env->callbacks.VMInit != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| env->callbacks.VMInit (env, jni_env, event_thread); |
| } |
| break; |
| |
| case JVMTI_EVENT_VM_DEATH: |
| if (env->callbacks.VMDeath != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| env->callbacks.VMDeath (env, jni_env); |
| } |
| break; |
| |
| case JVMTI_EVENT_THREAD_START: |
| if (env->callbacks.ThreadStart != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| env->callbacks.ThreadStart (env, jni_env, event_thread); |
| } |
| break; |
| |
| case JVMTI_EVENT_THREAD_END: |
| if (env->callbacks.ThreadEnd != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| env->callbacks.ThreadEnd (env, jni_env, event_thread); |
| } |
| break; |
| |
| case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK: |
| if (env->callbacks.ClassFileLoadHook != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jclass, class_being_redefined); |
| ARG (jobject, loader); |
| ARG (const char *, name); |
| ARG (jobject, protection_domain); |
| ARG (jint, class_data_len); |
| ARG (const unsigned char *, class_data); |
| ARG (jint *, new_class_data_len); |
| ARG (unsigned char **, new_class_data); |
| env->callbacks.ClassFileLoadHook (env, jni_env, |
| class_being_redefined, loader, |
| name, protection_domain, |
| class_data_len, class_data, |
| new_class_data_len, |
| new_class_data); |
| } |
| break; |
| |
| case JVMTI_EVENT_CLASS_LOAD: |
| if (env->callbacks.ClassLoad != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jclass, klass); |
| env->callbacks.ClassLoad (env, jni_env, event_thread, klass); |
| } |
| break; |
| |
| case JVMTI_EVENT_CLASS_PREPARE: |
| if (env->callbacks.ClassPrepare != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jclass, klass); |
| env->callbacks.ClassPrepare (env, jni_env, event_thread, klass); |
| } |
| break; |
| |
| case JVMTI_EVENT_VM_START: |
| if (env->callbacks.VMStart != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| env->callbacks.VMStart (env, jni_env); |
| } |
| break; |
| |
| case JVMTI_EVENT_EXCEPTION: |
| if (env->callbacks.Exception != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (jlocation, location); |
| ARG (jobject, exception); |
| ARG (jmethodID, catch_method); |
| ARG (jlocation, catch_location); |
| env->callbacks.Exception (env, jni_env, event_thread, method, |
| location, exception, catch_method, |
| catch_location); |
| } |
| break; |
| |
| case JVMTI_EVENT_EXCEPTION_CATCH: |
| if (env->callbacks.ExceptionCatch != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (jlocation, location); |
| ARG (jobject, exception); |
| env->callbacks.ExceptionCatch (env, jni_env, event_thread, method, |
| location, exception); |
| } |
| break; |
| |
| case JVMTI_EVENT_SINGLE_STEP: |
| if (env->callbacks.SingleStep != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (jlocation, location); |
| env->callbacks.SingleStep (env, jni_env, event_thread, method, |
| location); |
| } |
| break; |
| |
| case JVMTI_EVENT_FRAME_POP: |
| if (env->callbacks.FramePop != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| GET_BOOLEAN_ARG (was_popped_by_exception); |
| env->callbacks.FramePop (env, jni_env, event_thread, method, |
| was_popped_by_exception); |
| } |
| break; |
| |
| case JVMTI_EVENT_BREAKPOINT: |
| if (env->callbacks.Breakpoint != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (jlocation, location); |
| env->callbacks.Breakpoint (env, jni_env, event_thread, method, |
| location); |
| } |
| break; |
| |
| case JVMTI_EVENT_FIELD_ACCESS: |
| if (env->callbacks.FieldAccess != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (jlocation, location); |
| ARG (jclass, field_class); |
| ARG (jobject, object); |
| ARG (jfieldID, field); |
| env->callbacks.FieldAccess (env, jni_env, event_thread, method, |
| location, field_class, object, field); |
| } |
| break; |
| |
| case JVMTI_EVENT_FIELD_MODIFICATION: |
| if (env->callbacks.FieldModification != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (jlocation, location); |
| ARG (jclass, field_class); |
| ARG (jobject, object); |
| ARG (jfieldID, field); |
| GET_CHAR_ARG (signature_type); |
| ARG (jvalue, new_value); |
| env->callbacks.FieldModification (env, jni_env, event_thread, method, |
| location, field_class, object, |
| field, signature_type, new_value); |
| } |
| break; |
| |
| case JVMTI_EVENT_METHOD_ENTRY: |
| if (env->callbacks.MethodEntry != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| env->callbacks.MethodEntry (env, jni_env, event_thread, method); |
| } |
| break; |
| |
| case JVMTI_EVENT_METHOD_EXIT: |
| if (env->callbacks.MethodExit != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| GET_BOOLEAN_ARG (was_popped_by_exception); |
| ARG (jvalue, return_value); |
| env->callbacks.MethodExit (env, jni_env, event_thread, method, |
| was_popped_by_exception, return_value); |
| } |
| break; |
| |
| case JVMTI_EVENT_NATIVE_METHOD_BIND: |
| if (env->callbacks.NativeMethodBind != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jmethodID, method); |
| ARG (void *, address); |
| ARG (void **, new_address_ptr); |
| env->callbacks.NativeMethodBind (env, jni_env, event_thread, method, |
| address, new_address_ptr); |
| } |
| break; |
| |
| case JVMTI_EVENT_COMPILED_METHOD_LOAD: |
| if (env->callbacks.CompiledMethodLoad != NULL) |
| { |
| ARG (jmethodID, method); |
| ARG (jint, code_size); |
| ARG (const void *, code_addr); |
| ARG (jint, map_length); |
| ARG (const jvmtiAddrLocationMap *, map); |
| ARG (const void *, compile_info); |
| env->callbacks.CompiledMethodLoad (env, method, code_size, code_addr, |
| map_length, map, compile_info); |
| } |
| break; |
| |
| case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: |
| if (env->callbacks.CompiledMethodUnload != NULL) |
| { |
| ARG (jmethodID, method); |
| ARG (const void *, code_addr); |
| env->callbacks.CompiledMethodUnload (env, method, code_addr); |
| } |
| break; |
| |
| case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: |
| if (env->callbacks.DynamicCodeGenerated != NULL) |
| { |
| ARG (const char *, name); |
| ARG (const void *, address); |
| ARG (jint, length); |
| env->callbacks.DynamicCodeGenerated (env, name, address, length); |
| } |
| break; |
| |
| case JVMTI_EVENT_DATA_DUMP_REQUEST: |
| if (env->callbacks.DataDumpRequest != NULL) |
| { |
| env->callbacks.DataDumpRequest (env); |
| } |
| break; |
| |
| case JVMTI_EVENT_MONITOR_WAIT: |
| if (env->callbacks.MonitorWait != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jobject, object); |
| ARG (jlong, timeout); |
| env->callbacks.MonitorWait (env, jni_env, event_thread, object, |
| timeout); |
| } |
| break; |
| |
| case JVMTI_EVENT_MONITOR_WAITED: |
| if (env->callbacks.MonitorWaited != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jobject, object); |
| GET_BOOLEAN_ARG (timed_out); |
| env->callbacks.MonitorWaited (env, jni_env, event_thread, object, |
| timed_out); |
| } |
| break; |
| |
| case JVMTI_EVENT_MONITOR_CONTENDED_ENTER: |
| if (env->callbacks.MonitorContendedEnter != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jobject, object); |
| env->callbacks.MonitorContendedEnter (env, jni_env, event_thread, |
| object); |
| } |
| break; |
| |
| case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED: |
| if (env->callbacks.MonitorContendedEntered != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jobject, object); |
| env->callbacks.MonitorContendedEntered (env, jni_env, event_thread, |
| object); |
| } |
| break; |
| |
| case JVMTI_EVENT_GARBAGE_COLLECTION_START: |
| if (env->callbacks.GarbageCollectionStart != NULL) |
| { |
| env->callbacks.GarbageCollectionStart (env); |
| } |
| break; |
| |
| case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: |
| if (env->callbacks.GarbageCollectionFinish != NULL) |
| { |
| env->callbacks.GarbageCollectionFinish (env); |
| } |
| break; |
| |
| case JVMTI_EVENT_OBJECT_FREE: |
| if (env->callbacks.ObjectFree != NULL) |
| { |
| ARG (jlong, tag); |
| env->callbacks.ObjectFree (env, tag); |
| } |
| break; |
| |
| case JVMTI_EVENT_VM_OBJECT_ALLOC: |
| if (env->callbacks.VMObjectAlloc != NULL) |
| { |
| ARG (JNIEnv *, jni_env); |
| ARG (jobject, object); |
| ARG (jclass, object_class); |
| ARG (jlong, size); |
| env->callbacks.VMObjectAlloc (env, jni_env, event_thread, |
| object, object_class, size); |
| } |
| break; |
| |
| default: |
| fprintf (stderr, "libgcj: post of unknown JVMTI event (%d)\n", |
| (int) type); |
| break; |
| } |
| va_end (args); |
| #undef ARG |
| #undef GET_BOOLEAN_ARG |
| #undef GET_CHAR_ARG |
| } |
| |
| /* Post an event to requesting JVMTI environments |
| * |
| * This function should not be called without consulting the |
| * JVMTI_REQUESTED_EVENT macro first (for speed). It does no real |
| * harm (other than kill speed), since this function will still |
| * only send the event if it was properly requested by an environment. |
| */ |
| void |
| _Jv_JVMTI_PostEvent (jvmtiEvent type, jthread event_thread, ...) |
| { |
| va_list args; |
| va_start (args, event_thread); |
| |
| JvSynchronize dummy (_envListLock); |
| struct jvmti_env_list *e; |
| FOREACH_ENVIRONMENT (e) |
| { |
| /* Events are only posted if the event was explicitly enabled, |
| it has a registered event handler, and the event thread |
| matches (either globally or restricted to a specific thread). |
| Here we check all but the event handler, which will be handled |
| by post_event. */ |
| if (e->env->enabled[EVENT_INDEX(type)] |
| && (e->env->thread[EVENT_INDEX(type)] == NULL |
| || e->env->thread[EVENT_INDEX(type)] == event_thread)) |
| { |
| post_event (e->env, type, event_thread, args); |
| } |
| } |
| |
| va_end (args); |
| } |