X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fjvmti.cc;h=7c7923b2e8d81cf9189dadbf436a4c40b1a32681;hb=40469349494074e364a785ec1796e2be694a0483;hp=22b76fb25ffb9aa7074cc623d60a181575d6764f;hpb=8e7a741f32fb3fad67e3f7b6833f5ff7a6ca0ed1;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/jvmti.cc b/libjava/jvmti.cc index 22b76fb25ff..7c7923b2e8d 100644 --- a/libjava/jvmti.cc +++ b/libjava/jvmti.cc @@ -1,6 +1,6 @@ // jvmti.cc - JVMTI implementation -/* Copyright (C) 2006 Free Software Foundation +/* Copyright (C) 2006, 2007, 2010 Free Software Foundation This file is part of libgcj. @@ -14,24 +14,79 @@ details. */ #include #include #include +#include #include +#include "jvmti-int.h" #include #include #include +#include +#include + #include #include -#include +#include #include +#include +#include #include #include #include #include #include #include +#include +#include #include +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 @@ -50,7 +105,8 @@ struct jvmti_env_list struct jvmti_env_list *next; }; static struct jvmti_env_list *_jvmtiEnvironments = NULL; -static java::lang::Object *_envListLock = NULL; +static java::util::concurrent::locks:: +ReentrantReadWriteLock *_envListLock = NULL; #define FOREACH_ENVIRONMENT(Ele) \ for (Ele = _jvmtiEnvironments; Ele != NULL; Ele = Ele->next) @@ -100,18 +156,28 @@ static java::lang::Object *_envListLock = NULL; } \ 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 *t = reinterpret_cast (thread); - THREAD_CHECK_VALID (t); - THREAD_CHECK_IS_ALIVE (t); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); - _Jv_Thread_t *data = _Jv_ThreadGetData (t); + _Jv_Thread_t *data = _Jv_ThreadGetData (thread); _Jv_SuspendThread (data); return JVMTI_ERROR_NONE; } @@ -122,12 +188,10 @@ _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); - Thread *t = reinterpret_cast (thread); - THREAD_CHECK_VALID (t); - THREAD_CHECK_IS_ALIVE (t); - - _Jv_Thread_t *data = _Jv_ThreadGetData (t); + _Jv_Thread_t *data = _Jv_ThreadGetData (thread); _Jv_ResumeThread (data); return JVMTI_ERROR_NONE; } @@ -142,10 +206,378 @@ _Jv_JVMTI_InterruptThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) if (thread == NULL) return JVMTI_ERROR_INVALID_THREAD; - Thread *real_thread = reinterpret_cast (thread); - THREAD_CHECK_VALID (real_thread); - THREAD_CHECK_IS_ALIVE (real_thread); - real_thread->interrupt(); + 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 + (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 *thr_arr; + + // Allocate some extra space since threads can be created between calls + try + { + thr_arr = reinterpret_cast *> (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 (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; } @@ -237,6 +669,72 @@ _Jv_JVMTI_RawMonitorNotifyAll (MAYBE_UNUSED jvmtiEnv *env, } 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 (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 (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 (method), + location); + if (bp == NULL) + return JVMTI_ERROR_NOT_FOUND; + + BreakpointManager::deleteBreakpoint (reinterpret_cast (method), location); + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_Allocate (MAYBE_UNUSED jvmtiEnv *env, jlong size, unsigned char **result) { @@ -262,6 +760,36 @@ _Jv_JVMTI_Deallocate (MAYBE_UNUSED jvmtiEnv *env, unsigned char *mem) } 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) { @@ -380,6 +908,48 @@ _Jv_JVMTI_IsFieldSynthetic (MAYBE_UNUSED jvmtiEnv *env, jclass klass, } 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 (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 (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) { @@ -394,6 +964,136 @@ _Jv_JVMTI_GetMethodModifiers (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, } 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 + ((*num_locals) * sizeof (jvmtiLocalVariableEntry)), + reinterpret_cast (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 (strlen (name) + 1), + reinterpret_cast (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 (strlen (sig) + 1), + reinterpret_cast (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 (strlen (generic_sig) + 1), + reinterpret_cast (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) { @@ -423,6 +1123,93 @@ _Jv_JVMTI_IsMethodSynthetic (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, } 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, @@ -457,6 +1244,80 @@ _Jv_JVMTI_GetClassLoaderClasses (MAYBE_UNUSED jvmtiEnv *env, } 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); @@ -497,7 +1358,7 @@ _Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env) return JVMTI_ERROR_INVALID_ENVIRONMENT; else { - JvSynchronize dummy (_envListLock); + _envListLock->writeLock ()->lock (); if (_jvmtiEnvironments->env == env) { struct jvmti_env_list *next = _jvmtiEnvironments->next; @@ -510,15 +1371,22 @@ _Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env) while (e->next != NULL && e->next->env != env) e = e->next; if (e->next == NULL) - return JVMTI_ERROR_INVALID_ENVIRONMENT; + { + _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; } @@ -665,7 +1533,305 @@ _Jv_JVMTI_GetObjectSize (MAYBE_UNUSED jvmtiEnv *env, jobject object, return JVMTI_ERROR_NONE; } -jvmtiError +/* 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 (&e->env->callbacks) + offset; + void **callback = reinterpret_cast (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) { @@ -884,9 +2050,9 @@ _Jv_JVMTI_GetErrorName (MAYBE_UNUSED jvmtiEnv *env, jvmtiError error, struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = { RESERVED, // reserved1 - UNIMPLEMENTED, // SetEventNotification + _Jv_JVMTI_SetEventNotificationMode, // SetEventNotificationMode RESERVED, // reserved3 - UNIMPLEMENTED, // GetAllThreads + _Jv_JVMTI_GetAllThreads, // GetAllThreads _Jv_JVMTI_SuspendThread, // SuspendThread _Jv_JVMTI_ResumeThread, // ResumeThread UNIMPLEMENTED, // StopThread @@ -898,21 +2064,21 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetTopThreadGroups UNIMPLEMENTED, // GetThreadGroupInfo UNIMPLEMENTED, // GetThreadGroupChildren - UNIMPLEMENTED, // GetFrameCount - UNIMPLEMENTED, // GetThreadState + _Jv_JVMTI_GetFrameCount, // GetFrameCount + _Jv_JVMTI_GetThreadState, // 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_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 @@ -920,8 +2086,8 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = _Jv_JVMTI_RawMonitorWait, // RawMonitorWait _Jv_JVMTI_RawMonitorNotify, // RawMonitorNotify _Jv_JVMTI_RawMonitorNotifyAll, // RawMonitorNotifyAll - UNIMPLEMENTED, // SetBreakpoint - UNIMPLEMENTED, // ClearBreakpoint + _Jv_JVMTI_SetBreakpoint, // SetBreakpoint + _Jv_JVMTI_ClearBreakpoint, // ClearBreakpoint RESERVED, // reserved40 UNIMPLEMENTED, // SetFieldAccessWatch UNIMPLEMENTED, // ClearFieldAccessWatch @@ -931,7 +2097,7 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = _Jv_JVMTI_Allocate, // Allocate _Jv_JVMTI_Deallocate, // Deallocate UNIMPLEMENTED, // GetClassSignature - UNIMPLEMENTED, // GetClassStatus + _Jv_JVMTI_GetClassStatus, // GetClassStatus UNIMPLEMENTED, // GetSourceFileName _Jv_JVMTI_GetClassModifiers, // GetClassModifiers _Jv_JVMTI_GetClassMethods, // GetClassMethods @@ -946,15 +2112,15 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetFieldDeclaringClass _Jv_JVMTI_GetFieldModifiers, // GetFieldModifiers _Jv_JVMTI_IsFieldSynthetic, // IsFieldSynthetic - UNIMPLEMENTED, // GetMethodName - UNIMPLEMENTED, // GetMethodDeclaringClass + _Jv_JVMTI_GetMethodName, // GetMethodName + _Jv_JVMTI_GetMethodDeclaringClass, // GetMethodDeclaringClass _Jv_JVMTI_GetMethodModifiers, // GetMethodModifers RESERVED, // reserved67 - UNIMPLEMENTED, // GetMaxLocals - UNIMPLEMENTED, // GetArgumentsSize - UNIMPLEMENTED, // GetLineNumberTable + _Jv_JVMTI_GetMaxLocals, // GetMaxLocals + _Jv_JVMTI_GetArgumentsSize, // GetArgumentsSize + _Jv_JVMTI_GetLineNumberTable, // GetLineNumberTable UNIMPLEMENTED, // GetMethodLocation - UNIMPLEMENTED, // GetLocalVariableTable + _Jv_JVMTI_GetLocalVariableTable, // GetLocalVariableTable RESERVED, // reserved73 RESERVED, // reserved74 UNIMPLEMENTED, // GetBytecodes @@ -986,7 +2152,7 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetThreadListStackTraces UNIMPLEMENTED, // GetThreadLocalStorage UNIMPLEMENTED, // SetThreadLocalStorage - UNIMPLEMENTED, // GetStackTrace + _Jv_JVMTI_GetStackTrace, // GetStackTrace RESERVED, // reserved105 UNIMPLEMENTED, // GetTag UNIMPLEMENTED, // SetTag @@ -1004,7 +2170,7 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = RESERVED, // reserved119 _Jv_JVMTI_SetJNIFunctionTable, // SetJNIFunctionTable _Jv_JVMTI_GetJNIFunctionTable, // GetJNIFunctionTable - UNIMPLEMENTED, // SetEventCallbacks + _Jv_JVMTI_SetEventCallbacks, // SetEventCallbacks UNIMPLEMENTED, // GenerateEvents UNIMPLEMENTED, // GetExtensionFunctions UNIMPLEMENTED, // GetExtensionEvents @@ -1044,26 +2210,29 @@ _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; - { - 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; - } - } + _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; } @@ -1071,5 +2240,387 @@ void _Jv_JVMTI_Init () { _jvmtiEnvironments = NULL; - _envListLock = new java::lang::Object (); + _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 (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); }