| // jvmti.cc - JVMTI implementation |
| |
| /* Copyright (C) 2006, 2007, 2010 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 <java-interp.h> |
| #include <jvmti.h> |
| #include "jvmti-int.h" |
| |
| #include <gcj/method.h> |
| |
| #include <gnu/classpath/SystemProperties.h> |
| #include <gnu/gcj/runtime/BootClassLoader.h> |
| #include <gnu/gcj/jvmti/Breakpoint.h> |
| #include <gnu/gcj/jvmti/BreakpointManager.h> |
| |
| #include <java/lang/Class.h> |
| #include <java/lang/ClassLoader.h> |
| #include <java/lang/OutOfMemoryError.h> |
| #include <java/lang/Thread.h> |
| #include <java/lang/ThreadGroup.h> |
| #include <java/lang/Thread$State.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/util/concurrent/locks/Lock.h> |
| #include <java/util/concurrent/locks/ReentrantReadWriteLock.h> |
| #include <java/net/URL.h> |
| |
| static void check_enabled_events (void); |
| static void check_enabled_event (jvmtiEvent); |
| |
| namespace JVMTI |
| { |
| // Is JVMTI enabled? (i.e., any jvmtiEnv created?) |
| bool enabled; |
| |
| // Event notifications |
| 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::util::concurrent::locks:: |
| ReentrantReadWriteLock *_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) |
| |
| #define CHECK_FOR_NATIVE_METHOD(AjmethodID) \ |
| do \ |
| { \ |
| jboolean is_native; \ |
| jvmtiError jerr = env->IsMethodNative (AjmethodID, &is_native); \ |
| if (jerr != JVMTI_ERROR_NONE) \ |
| return jerr; \ |
| if (is_native) \ |
| return JVMTI_ERROR_NATIVE_METHOD; \ |
| } \ |
| while (0) |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SuspendThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) |
| { |
| using namespace java::lang; |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| THREAD_CHECK_VALID (thread); |
| THREAD_CHECK_IS_ALIVE (thread); |
| |
| _Jv_Thread_t *data = _Jv_ThreadGetData (thread); |
| _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_CHECK_VALID (thread); |
| THREAD_CHECK_IS_ALIVE (thread); |
| |
| _Jv_Thread_t *data = _Jv_ThreadGetData (thread); |
| _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_CHECK_VALID (thread); |
| THREAD_CHECK_IS_ALIVE (thread); |
| thread->interrupt(); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| // This method performs the common tasks to get and set variables of all types. |
| // It is called by the _Jv_JVMTI_Get/SetLocalInt/Object/.... methods. |
| static jvmtiError |
| getLocalFrame (jvmtiEnv *env, jthread thread, jint depth, jint slot, char type, |
| _Jv_InterpFrame **iframe) |
| { |
| using namespace java::lang; |
| |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| |
| ILLEGAL_ARGUMENT (depth < 0); |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| THREAD_CHECK_VALID (thread); |
| THREAD_CHECK_IS_ALIVE (thread); |
| |
| _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); |
| |
| for (int i = 0; i < depth; i++) |
| { |
| frame = frame->next; |
| |
| if (frame == NULL) |
| return JVMTI_ERROR_NO_MORE_FRAMES; |
| } |
| |
| if (frame->frame_type == frame_native) |
| return JVMTI_ERROR_OPAQUE_FRAME; |
| |
| jint max_locals; |
| jvmtiError jerr = env->GetMaxLocals (reinterpret_cast<jmethodID> |
| (frame->self->get_method ()), |
| &max_locals); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| _Jv_InterpFrame *tmp_iframe = reinterpret_cast<_Jv_InterpFrame *> (frame); |
| |
| // The second slot taken up by a long type is marked as type 'x' meaning it |
| // is not valid for access since it holds only the 4 low bytes of the value. |
| if (tmp_iframe->locals_type[slot] == 'x') |
| return JVMTI_ERROR_INVALID_SLOT; |
| |
| if (tmp_iframe->locals_type[slot] != type) |
| return JVMTI_ERROR_TYPE_MISMATCH; |
| |
| // Check for invalid slots, if the type is a long type, we must check that |
| // the next slot is valid as well. |
| if (slot < 0 || slot >= max_locals |
| || ((type == 'l' || type == 'd') && slot + 1 >= max_locals)) |
| return JVMTI_ERROR_INVALID_SLOT; |
| |
| *iframe = tmp_iframe; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jobject *value) |
| { |
| NULL_CHECK (value); |
| |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| *value = frame->locals[slot].o; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jobject value) |
| { |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| frame->locals[slot].o = value; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jint *value) |
| { |
| NULL_CHECK (value); |
| |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| *value = frame->locals[slot].i; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jint value) |
| { |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| frame->locals[slot].i = value; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jlong *value) |
| { |
| NULL_CHECK (value); |
| |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| #if SIZEOF_VOID_P==8 |
| *value = frame->locals[slot].l; |
| #else |
| _Jv_word2 val; |
| val.ia[0] = frame->locals[slot].ia[0]; |
| val.ia[1] = frame->locals[slot + 1].ia[0]; |
| *value = val.l; |
| #endif |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jlong value) |
| { |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| #if SIZEOF_VOID_P==8 |
| frame->locals[slot].l = value; |
| #else |
| _Jv_word2 val; |
| val.l = value; |
| frame->locals[slot].ia[0] = val.ia[0]; |
| frame->locals[slot + 1].ia[0] = val.ia[1]; |
| #endif |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jfloat *value) |
| { |
| NULL_CHECK (value); |
| |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| *value = frame->locals[slot].f; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jfloat value) |
| { |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| frame->locals[slot].f = value; |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jdouble *value) |
| { |
| NULL_CHECK (value); |
| |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| #if SIZEOF_VOID_P==8 |
| *value = frame->locals[slot].d; |
| #else |
| _Jv_word2 val; |
| val.ia[0] = frame->locals[slot].ia[0]; |
| val.ia[1] = frame->locals[slot + 1].ia[0]; |
| *value = val.d; |
| #endif |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_SetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot, |
| jdouble value) |
| { |
| _Jv_InterpFrame *frame; |
| jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| #if SIZEOF_VOID_P==8 |
| frame->locals[slot].d = value; |
| #else |
| _Jv_word2 val; |
| val.d = value; |
| frame->locals[slot].ia[0] = val.ia[0]; |
| frame->locals[slot + 1].ia[0] = val.ia[1]; |
| #endif |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt, |
| jthread **threads) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| NULL_CHECK (thread_cnt); |
| NULL_CHECK (threads); |
| |
| using namespace java::lang; |
| |
| ThreadGroup *root_grp = ThreadGroup::root; |
| jint estimate = root_grp->activeCount (); |
| |
| JArray<Thread *> *thr_arr; |
| |
| // Allocate some extra space since threads can be created between calls |
| try |
| { |
| thr_arr = reinterpret_cast<JArray<Thread *> *> (JvNewObjectArray |
| ((estimate * 2), |
| &Thread::class$, NULL)); |
| } |
| catch (java::lang::OutOfMemoryError *err) |
| { |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| } |
| |
| *thread_cnt = root_grp->enumerate (thr_arr); |
| |
| jvmtiError jerr = env->Allocate ((jlong) ((*thread_cnt) * sizeof (jthread)), |
| (unsigned char **) threads); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| // Transfer the threads to the result array |
| jthread *tmp_arr = reinterpret_cast<jthread *> (elements (thr_arr)); |
| |
| memcpy ((*threads), tmp_arr, (*thread_cnt)); |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetFrameCount (MAYBE_UNUSED jvmtiEnv *env, jthread thread, |
| jint *frame_count) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| |
| NULL_CHECK (frame_count); |
| |
| using namespace java::lang; |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| THREAD_CHECK_VALID (thread); |
| THREAD_CHECK_IS_ALIVE (thread); |
| |
| _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); |
| (*frame_count) = frame->depth (); |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetThreadState (MAYBE_UNUSED jvmtiEnv *env, jthread thread, |
| jint *thread_state_ptr) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| THREAD_CHECK_VALID (thread); |
| NULL_CHECK (thread_state_ptr); |
| |
| jint state = 0; |
| if (thread->isAlive ()) |
| { |
| state |= JVMTI_THREAD_STATE_ALIVE; |
| |
| _Jv_Thread_t *data = _Jv_ThreadGetData (thread); |
| if (_Jv_IsThreadSuspended (data)) |
| state |= JVMTI_THREAD_STATE_SUSPENDED; |
| |
| if (thread->isInterrupted ()) |
| state |= JVMTI_THREAD_STATE_INTERRUPTED; |
| |
| _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); |
| if (frame != NULL && frame->frame_type == frame_native) |
| state |= JVMTI_THREAD_STATE_IN_NATIVE; |
| |
| using namespace java::lang; |
| Thread$State *ts = thread->getState (); |
| if (ts == Thread$State::RUNNABLE) |
| state |= JVMTI_THREAD_STATE_RUNNABLE; |
| else if (ts == Thread$State::BLOCKED) |
| state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; |
| else if (ts == Thread$State::TIMED_WAITING |
| || ts == Thread$State::WAITING) |
| { |
| state |= JVMTI_THREAD_STATE_WAITING; |
| state |= ((ts == Thread$State::WAITING) |
| ? JVMTI_THREAD_STATE_WAITING_INDEFINITELY |
| : JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT); |
| |
| /* FIXME: We don't have a way to tell |
| the caller why the thread is suspended, |
| i.e., JVMTI_THREAD_STATE_SLEEPING, |
| JVMTI_THREAD_STATE_PARKED, and |
| JVMTI_THREAD_STATE_IN_OBJECT_WAIT |
| are never set. */ |
| } |
| } |
| else |
| { |
| using namespace java::lang; |
| Thread$State *ts = thread->getState (); |
| if (ts == Thread$State::TERMINATED) |
| state |= JVMTI_THREAD_STATE_TERMINATED; |
| } |
| |
| *thread_state_ptr = state; |
| 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_SetBreakpoint (jvmtiEnv *env, jmethodID method, jlocation location) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| |
| using namespace gnu::gcj::jvmti; |
| Breakpoint *bp |
| = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method), |
| location); |
| if (bp == NULL) |
| { |
| jclass klass; |
| jvmtiError err = env->GetMethodDeclaringClass (method, &klass); |
| if (err != JVMTI_ERROR_NONE) |
| return err; |
| |
| if (!_Jv_IsInterpretedClass (klass)) |
| return JVMTI_ERROR_INVALID_CLASS; |
| |
| _Jv_MethodBase *base = _Jv_FindInterpreterMethod (klass, method); |
| if (base == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| |
| jint flags; |
| err = env->GetMethodModifiers (method, &flags); |
| if (err != JVMTI_ERROR_NONE) |
| return err; |
| |
| if (flags & java::lang::reflect::Modifier::NATIVE) |
| return JVMTI_ERROR_NATIVE_METHOD; |
| |
| _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> (base); |
| if (imeth->get_insn (location) == NULL) |
| return JVMTI_ERROR_INVALID_LOCATION; |
| |
| // Now the breakpoint can be safely installed |
| bp = BreakpointManager::newBreakpoint (reinterpret_cast<jlong> (method), |
| location); |
| } |
| else |
| { |
| // Duplicate breakpoints are not permitted by JVMTI |
| return JVMTI_ERROR_DUPLICATE; |
| } |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_ClearBreakpoint (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, |
| jlocation location) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| |
| using namespace gnu::gcj::jvmti; |
| |
| Breakpoint *bp |
| = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method), |
| location); |
| if (bp == NULL) |
| return JVMTI_ERROR_NOT_FOUND; |
| |
| BreakpointManager::deleteBreakpoint (reinterpret_cast<jlong> (method), location); |
| 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_GetClassStatus (MAYBE_UNUSED jvmtiEnv *env, jclass klass, |
| jint *status_ptr) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| NULL_CHECK (status_ptr); |
| if (klass == NULL) |
| return JVMTI_ERROR_INVALID_CLASS; |
| |
| if (klass->isArray ()) |
| *status_ptr = JVMTI_CLASS_STATUS_ARRAY; |
| else if (klass->isPrimitive ()) |
| *status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE; |
| else |
| { |
| jbyte state = _Jv_GetClassState (klass); |
| *status_ptr = 0; |
| if (state >= JV_STATE_LINKED) |
| (*status_ptr) |= JVMTI_CLASS_STATUS_VERIFIED; |
| if (state >= JV_STATE_PREPARED) |
| (*status_ptr) |= JVMTI_CLASS_STATUS_PREPARED; |
| if (state == JV_STATE_ERROR || state == JV_STATE_PHANTOM) |
| (*status_ptr) |= JVMTI_CLASS_STATUS_ERROR; |
| else if (state == JV_STATE_DONE) |
| (*status_ptr) |= JVMTI_CLASS_STATUS_INITIALIZED; |
| } |
| |
| 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_GetMethodName (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, |
| char **name_ptr, char **signature_ptr, |
| char **generic_ptr) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| |
| if (method == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| |
| if (name_ptr != NULL) |
| { |
| int len = static_cast<int> (method->name->len ()); |
| *name_ptr = (char *) _Jv_MallocUnchecked (len + 1); |
| if (*name_ptr == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| strncpy (*name_ptr, method->name->chars (), len); |
| (*name_ptr)[len] = '\0'; |
| } |
| |
| if (signature_ptr != NULL) |
| { |
| int len = static_cast<int> (method->signature->len ()); |
| *signature_ptr = (char *) _Jv_MallocUnchecked (len + 1); |
| if (*signature_ptr == NULL) |
| { |
| if (name_ptr != NULL) |
| _Jv_Free (*name_ptr); |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| } |
| strncpy (*signature_ptr, method->signature->chars (), len); |
| (*signature_ptr)[len] = '\0'; |
| } |
| |
| if (generic_ptr != NULL) |
| { |
| *generic_ptr = NULL; |
| } |
| |
| 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_GetLineNumberTable (jvmtiEnv *env, jmethodID method, |
| jint *entry_count_ptr, |
| jvmtiLineNumberEntry **table_ptr) |
| { |
| NULL_CHECK (entry_count_ptr); |
| NULL_CHECK (table_ptr); |
| |
| jclass klass; |
| jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| _Jv_MethodBase *base = _Jv_FindInterpreterMethod (klass, method); |
| if (base == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| |
| if (java::lang::reflect::Modifier::isNative (method->accflags) |
| || !_Jv_IsInterpretedClass (klass)) |
| return JVMTI_ERROR_NATIVE_METHOD; |
| |
| _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> (base); |
| jlong start, end; |
| jintArray lines = NULL; |
| jlongArray indices = NULL; |
| imeth->get_line_table (start, end, lines, indices); |
| if (lines == NULL) |
| return JVMTI_ERROR_ABSENT_INFORMATION; |
| |
| jvmtiLineNumberEntry *table; |
| jsize len = lines->length * sizeof (jvmtiLineNumberEntry); |
| table = (jvmtiLineNumberEntry *) _Jv_MallocUnchecked (len); |
| if (table == NULL) |
| return JVMTI_ERROR_OUT_OF_MEMORY; |
| |
| jint *line = elements (lines); |
| jlong *index = elements (indices); |
| for (int i = 0; i < lines->length; ++i) |
| { |
| table[i].start_location = index[i]; |
| table[i].line_number = line[i]; |
| } |
| |
| *table_ptr = table; |
| *entry_count_ptr = lines->length; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetLocalVariableTable (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, |
| jint *num_locals, |
| jvmtiLocalVariableEntry **locals) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| NULL_CHECK (num_locals); |
| NULL_CHECK (locals); |
| |
| CHECK_FOR_NATIVE_METHOD(method); |
| |
| jclass klass; |
| jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> |
| (_Jv_FindInterpreterMethod (klass, method)); |
| |
| if (imeth == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| |
| jerr = env->GetMaxLocals (method, num_locals); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| jerr = env->Allocate (static_cast<jlong> |
| ((*num_locals) * sizeof (jvmtiLocalVariableEntry)), |
| reinterpret_cast<unsigned char **> (locals)); |
| |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| //the slot in the methods local_var_table to get |
| int table_slot = 0; |
| char *name; |
| char *sig; |
| char *generic_sig; |
| |
| while (table_slot < *num_locals |
| && imeth->get_local_var_table (&name, &sig, &generic_sig, |
| &((((*locals)[table_slot].start_location))), |
| &((*locals)[table_slot].length), |
| &((*locals)[table_slot].slot), |
| table_slot) |
| >= 0) |
| { |
| char **str_ptr = &(*locals)[table_slot].name; |
| jerr = env->Allocate (static_cast<jlong> (strlen (name) + 1), |
| reinterpret_cast<unsigned char **> (str_ptr)); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| strcpy ((*locals)[table_slot].name, name); |
| |
| str_ptr = &(*locals)[table_slot].signature; |
| jerr = env->Allocate (static_cast<jlong> (strlen (sig) + 1), |
| reinterpret_cast<unsigned char **> (str_ptr)); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| strcpy ((*locals)[table_slot].signature, sig); |
| |
| str_ptr = &(*locals)[table_slot].generic_signature; |
| jerr = env->Allocate (static_cast<jlong> (strlen (generic_sig) + 1), |
| reinterpret_cast<unsigned char **> (str_ptr)); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| strcpy ((*locals)[table_slot].generic_signature, generic_sig); |
| |
| table_slot++; |
| } |
| |
| if (table_slot == 0) |
| return JVMTI_ERROR_ABSENT_INFORMATION; |
| |
| // If there are double or long variables in the table, the the table will be |
| // smaller than the max number of slots, so correct for this here. |
| if ((table_slot) < *num_locals) |
| *num_locals = table_slot; |
| |
| 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_GetMaxLocals (jvmtiEnv *env, jmethodID method, jint *max_locals) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| NULL_CHECK (max_locals); |
| |
| CHECK_FOR_NATIVE_METHOD (method); |
| |
| jclass klass; |
| jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> |
| (_Jv_FindInterpreterMethod (klass, method)); |
| |
| if (imeth == NULL) |
| return JVMTI_ERROR_INVALID_METHODID; |
| |
| *max_locals = imeth->get_max_locals (); |
| |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetArgumentsSize (jvmtiEnv *env, jmethodID method, jint *size) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); |
| NULL_CHECK (size); |
| |
| CHECK_FOR_NATIVE_METHOD (method); |
| |
| jvmtiError jerr; |
| char *sig; |
| jint num_slots = 0; |
| |
| jerr = env->GetMethodName (method, NULL, &sig, NULL); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| // If the method is non-static add a slot for the "this" pointer. |
| if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0) |
| num_slots++; |
| |
| for (int i = 0; sig[i] != ')'; i++) |
| { |
| if (sig[i] == 'Z' || sig[i] == 'B' || sig[i] == 'C' || sig[i] == 'S' |
| || sig[i] == 'I' || sig[i] == 'F') |
| num_slots++; |
| else if (sig[i] == 'J' || sig[i] == 'D') |
| { |
| // If this is an array of wide types it uses a single slot |
| if (i > 0 && sig[i - 1] == '[') |
| num_slots++; |
| else |
| num_slots += 2; |
| } |
| else if (sig[i] == 'L') |
| { |
| num_slots++; |
| while (sig[i] != ';') |
| i++; |
| } |
| } |
| |
| *size = num_slots; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| static jvmtiError JNICALL |
| _Jv_JVMTI_GetMethodDeclaringClass (MAYBE_UNUSED jvmtiEnv *env, |
| jmethodID method, |
| jclass *declaring_class_ptr) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| NULL_CHECK (declaring_class_ptr); |
| |
| jclass klass = _Jv_GetMethodDeclaringClass (method); |
| if (klass != NULL) |
| { |
| *declaring_class_ptr = klass; |
| return JVMTI_ERROR_NONE; |
| } |
| |
| return JVMTI_ERROR_INVALID_METHODID; |
| } |
| |
| 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_GetStackTrace (MAYBE_UNUSED jvmtiEnv *env, jthread thread, |
| jint start_depth, jint max_frames, |
| jvmtiFrameInfo *frames, jint *frame_count) |
| { |
| REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); |
| |
| ILLEGAL_ARGUMENT (max_frames < 0); |
| |
| NULL_CHECK (frames); |
| NULL_CHECK (frame_count); |
| |
| using namespace java::lang; |
| |
| THREAD_DEFAULT_TO_CURRENT (thread); |
| THREAD_CHECK_VALID (thread); |
| THREAD_CHECK_IS_ALIVE (thread); |
| |
| jvmtiError jerr = env->GetFrameCount (thread, frame_count); |
| if (jerr != JVMTI_ERROR_NONE) |
| return jerr; |
| |
| // start_depth can be either a positive number, indicating the depth of the |
| // stack at which to begin the trace, or a negative number indicating the |
| // number of frames at the bottom of the stack to exclude. These checks |
| // ensure that it is a valid value in either case |
| |
| ILLEGAL_ARGUMENT (start_depth >= (*frame_count)); |
| ILLEGAL_ARGUMENT (start_depth < (-(*frame_count))); |
| |
| _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); |
| |
| // If start_depth is negative use this to determine at what depth to start |
| // the trace by adding it to the length of the call stack. This allows the |
| // use of the same frame "discarding" mechanism as for a positive start_depth |
| if (start_depth < 0) |
| start_depth = *frame_count + start_depth; |
| |
| // If start_depth > 0 "remove" start_depth frames from the beginning |
| // of the stack before beginning the trace by moving along the frame list. |
| while (start_depth > 0) |
| { |
| frame = frame->next; |
| start_depth--; |
| (*frame_count)--; |
| } |
| |
| // Now check to see if the array supplied by the agent is large enough to |
| // hold frame_count frames, after adjustment for start_depth. |
| if ((*frame_count) > max_frames) |
| (*frame_count) = max_frames; |
| |
| for (int i = 0; i < (*frame_count); i++) |
| { |
| frames[i].method = frame->self->get_method (); |
| |
| // Set the location in the frame, native frames have location = -1 |
| if (frame->frame_type == frame_interpreter) |
| { |
| _Jv_InterpMethod *imeth |
| = static_cast<_Jv_InterpMethod *> (frame->self); |
| _Jv_InterpFrame *interp_frame |
| = static_cast<_Jv_InterpFrame *> (frame); |
| frames[i].location = imeth->insn_index (interp_frame->get_pc ()); |
| } |
| else |
| frames[i].location = -1; |
| |
| frame = frame->next; |
| } |
| |
| 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 |
| { |
| _envListLock->writeLock ()->lock (); |
| 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) |
| { |
| _envListLock->writeLock ()->unlock (); |
| return JVMTI_ERROR_INVALID_ENVIRONMENT; |
| } |
| |
| struct jvmti_env_list *next = e->next->next; |
| _Jv_Free (e->next); |
| e->next = next; |
| } |
| _envListLock->writeLock ()->unlock (); |
| } |
| |
| _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 |
| |
| if (_jvmtiEnvironments != NULL) |
| { |
| _envListLock->readLock ()->lock (); |
| 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; |
| _envListLock->readLock ()->unlock (); |
| return; |
| } |
| } |
| |
| _envListLock->readLock ()->unlock (); |
| } |
| |
| *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) |
| { |
| THREAD_CHECK_VALID (event_thread); |
| THREAD_CHECK_IS_ALIVE (event_thread); |
| } |
| |
| 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 |
| _Jv_JVMTI_GetAllThreads, // 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 |
| _Jv_JVMTI_GetFrameCount, // GetFrameCount |
| _Jv_JVMTI_GetThreadState, // GetThreadState |
| RESERVED, // reserved18 |
| UNIMPLEMENTED, // GetFrameLocation |
| UNIMPLEMENTED, // NotifyPopFrame |
| _Jv_JVMTI_GetLocalObject, // GetLocalObject |
| _Jv_JVMTI_GetLocalInt, // GetLocalInt |
| _Jv_JVMTI_GetLocalLong, // GetLocalLong |
| _Jv_JVMTI_GetLocalFloat, // GetLocalFloat |
| _Jv_JVMTI_GetLocalDouble, // GetLocalDouble |
| _Jv_JVMTI_SetLocalObject, // SetLocalObject |
| _Jv_JVMTI_SetLocalInt, // SetLocalInt |
| _Jv_JVMTI_SetLocalLong, // SetLocalLong |
| _Jv_JVMTI_SetLocalFloat, // SetLocalFloat |
| _Jv_JVMTI_SetLocalDouble, // 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 |
| _Jv_JVMTI_SetBreakpoint, // SetBreakpoint |
| _Jv_JVMTI_ClearBreakpoint, // ClearBreakpoint |
| RESERVED, // reserved40 |
| UNIMPLEMENTED, // SetFieldAccessWatch |
| UNIMPLEMENTED, // ClearFieldAccessWatch |
| UNIMPLEMENTED, // SetFieldModificationWatch |
| UNIMPLEMENTED, // ClearFieldModificationWatch |
| RESERVED, // reserved45 |
| _Jv_JVMTI_Allocate, // Allocate |
| _Jv_JVMTI_Deallocate, // Deallocate |
| UNIMPLEMENTED, // GetClassSignature |
| _Jv_JVMTI_GetClassStatus, // 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 |
| _Jv_JVMTI_GetMethodName, // GetMethodName |
| _Jv_JVMTI_GetMethodDeclaringClass, // GetMethodDeclaringClass |
| _Jv_JVMTI_GetMethodModifiers, // GetMethodModifers |
| RESERVED, // reserved67 |
| _Jv_JVMTI_GetMaxLocals, // GetMaxLocals |
| _Jv_JVMTI_GetArgumentsSize, // GetArgumentsSize |
| _Jv_JVMTI_GetLineNumberTable, // GetLineNumberTable |
| UNIMPLEMENTED, // GetMethodLocation |
| _Jv_JVMTI_GetLocalVariableTable, // 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 |
| _Jv_JVMTI_GetStackTrace, // 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)); |
| memset (env, 0, sizeof (_Jv_JVMTIEnv)); |
| env->p = &_Jv_JVMTI_Interface; |
| struct jvmti_env_list *element |
| = (struct jvmti_env_list *) _Jv_MallocUnchecked (sizeof (struct jvmti_env_list)); |
| element->env = env; |
| element->next = NULL; |
| |
| _envListLock->writeLock ()->lock (); |
| if (_jvmtiEnvironments == NULL) |
| _jvmtiEnvironments = element; |
| else |
| { |
| struct jvmti_env_list *e; |
| for (e = _jvmtiEnvironments; e->next != NULL; e = e->next) |
| ; |
| e->next = element; |
| } |
| _envListLock->writeLock ()->unlock (); |
| |
| /* Mark JVMTI active. This is used to force the interpreter |
| to use either debugging or non-debugging code. Once JVMTI |
| has been enabled, the non-debug interpreter cannot be used. */ |
| JVMTI::enabled = true; |
| return env; |
| } |
| |
| void |
| _Jv_JVMTI_Init () |
| { |
| _jvmtiEnvironments = NULL; |
| _envListLock |
| = new java::util::concurrent::locks::ReentrantReadWriteLock (); |
| |
| // 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); |
| |
| _envListLock->readLock ()->lock (); |
| 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); |
| } |
| } |
| _envListLock->readLock ()->unlock (); |
| va_end (args); |
| } |