X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fjvmti.cc;h=7c7923b2e8d81cf9189dadbf436a4c40b1a32681;hb=2c622b2eac33e9946e2ad459663f7c993d043e2a;hp=8bebef8c219faa503267faec015c3008c7e76e8b;hpb=aa705a651c253c091706d2f38247c212a02f5154;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/jvmti.cc b/libjava/jvmti.cc index 8bebef8c219..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. @@ -9,9 +9,2040 @@ Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ #include +#include #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 + +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 + (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; +} + +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 (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) +{ + 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 (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) +{ + 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 + ((*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) +{ + 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 (&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) +{ + 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 @@ -19,13 +2050,13 @@ details. */ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = { RESERVED, // reserved1 - UNIMPLEMENTED, // SetEventNotification + _Jv_JVMTI_SetEventNotificationMode, // SetEventNotificationMode RESERVED, // reserved3 - UNIMPLEMENTED, // GetAllThreads - UNIMPLEMENTED, // SuspendThread - UNIMPLEMENTED, // ResumeThread + _Jv_JVMTI_GetAllThreads, // GetAllThreads + _Jv_JVMTI_SuspendThread, // SuspendThread + _Jv_JVMTI_ResumeThread, // ResumeThread UNIMPLEMENTED, // StopThread - UNIMPLEMENTED, // InterruptThread + _Jv_JVMTI_InterruptThread, // InterruptThread UNIMPLEMENTED, // GetThreadInfo UNIMPLEMENTED, // GetOwnedMonitorInfo UNIMPLEMENTED, // GetCurrentContendedMonitor @@ -33,70 +2064,70 @@ 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 - UNIMPLEMENTED, // CreateRawMonitor - UNIMPLEMENTED, // DestroyRawMonitor - UNIMPLEMENTED, // RawMonitorEnter - UNIMPLEMENTED, // RawMonitorExit - UNIMPLEMENTED, // RawMonitorWait - UNIMPLEMENTED, // RawMonitorNotify - UNIMPLEMENTED, // RawMonitorNotifyAll - UNIMPLEMENTED, // SetBreakpoint - UNIMPLEMENTED, // CleareBreakpoint + _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 - UNIMPLEMENTED, // Allocate - UNIMPLEMENTED, // Deallocate + _Jv_JVMTI_Allocate, // Allocate + _Jv_JVMTI_Deallocate, // Deallocate UNIMPLEMENTED, // GetClassSignature - UNIMPLEMENTED, // GetClassStatus + _Jv_JVMTI_GetClassStatus, // GetClassStatus UNIMPLEMENTED, // GetSourceFileName - UNIMPLEMENTED, // GetClassModifiers - UNIMPLEMENTED, // GetClassMethods + _Jv_JVMTI_GetClassModifiers, // GetClassModifiers + _Jv_JVMTI_GetClassMethods, // GetClassMethods UNIMPLEMENTED, // GetClassFields UNIMPLEMENTED, // GetImplementedInterfaces - UNIMPLEMENTED, // IsInterface - UNIMPLEMENTED, // IsArrayClass - UNIMPLEMENTED, // GetClassLoader - UNIMPLEMENTED, // GetObjectHashCode + _Jv_JVMTI_IsInterface, // IsInterface + _Jv_JVMTI_IsArrayClass, // IsArrayClass + _Jv_JVMTI_GetClassLoader, // GetClassLoader + _Jv_JVMTI_GetObjectHashCode, // GetObjectHashCode UNIMPLEMENTED, // GetObjectMonitorUsage UNIMPLEMENTED, // GetFieldName UNIMPLEMENTED, // GetFieldDeclaringClass - UNIMPLEMENTED, // GetFieldModifiers - UNIMPLEMENTED, // IsFieldSynthetic - UNIMPLEMENTED, // GetMethodName - UNIMPLEMENTED, // GetMethodDeclaringClass - UNIMPLEMENTED, // GetMethodModiifers + _Jv_JVMTI_GetFieldModifiers, // GetFieldModifiers + _Jv_JVMTI_IsFieldSynthetic, // IsFieldSynthetic + _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 - UNIMPLEMENTED, // IsMethodNative - UNIMPLEMENTED, // IsMethodSynthetic + _Jv_JVMTI_IsMethodNative, // IsMethodNative + _Jv_JVMTI_IsMethodSynthetic, // IsMethodSynthetic UNIMPLEMENTED, // GetLoadedClasses - UNIMPLEMENTED, // GetClassLoaderClasses + _Jv_JVMTI_GetClassLoaderClasses, // GetClassLoaderClasses UNIMPLEMENTED, // PopFrame RESERVED, // reserved81 RESERVED, // reserved82 @@ -121,11 +2152,11 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetThreadListStackTraces UNIMPLEMENTED, // GetThreadLocalStorage UNIMPLEMENTED, // SetThreadLocalStorage - UNIMPLEMENTED, // GetStackTrace + _Jv_JVMTI_GetStackTrace, // GetStackTrace RESERVED, // reserved105 UNIMPLEMENTED, // GetTag UNIMPLEMENTED, // SetTag - UNIMPLEMENTED, // ForceGarbageCollection + _Jv_JVMTI_ForceGarbageCollection, // ForceGarbageCollection UNIMPLEMENTED, // IterateOverObjectsReachable UNIMPLEMENTED, // IterateOverReachableObjects UNIMPLEMENTED, // IterateOverHeap @@ -137,41 +2168,41 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = RESERVED, // reserved117 RESERVED, // reserved118 RESERVED, // reserved119 - UNIMPLEMENTED, // SetJNIFunctionTable - UNIMPLEMENTED, // GetJNIFunctionTable - UNIMPLEMENTED, // SetEventCallbacks + _Jv_JVMTI_SetJNIFunctionTable, // SetJNIFunctionTable + _Jv_JVMTI_GetJNIFunctionTable, // GetJNIFunctionTable + _Jv_JVMTI_SetEventCallbacks, // SetEventCallbacks UNIMPLEMENTED, // GenerateEvents UNIMPLEMENTED, // GetExtensionFunctions UNIMPLEMENTED, // GetExtensionEvents UNIMPLEMENTED, // SetExtensionEventCallback - UNIMPLEMENTED, // DisposeEnvironment - UNIMPLEMENTED, // GetErrorName + _Jv_JVMTI_DisposeEnvironment, // DisposeEnvironment + _Jv_JVMTI_GetErrorName, // GetErrorName UNIMPLEMENTED, // GetJLocationFormat UNIMPLEMENTED, // GetSystemProperties - UNIMPLEMENTED, // GetSystemProperty - UNIMPLEMENTED, // SetSystemProperty + _Jv_JVMTI_GetSystemProperty, // GetSystemProperty + _Jv_JVMTI_SetSystemProperty, // SetSystemProperty UNIMPLEMENTED, // GetPhase UNIMPLEMENTED, // GetCurrentThreadCpuTimerInfo UNIMPLEMENTED, // GetCurrentThreadCpuTime UNIMPLEMENTED, // GetThreadCpuTimerInfo UNIMPLEMENTED, // GetThreadCpuTime UNIMPLEMENTED, // GetTimerInfo - UNIMPLEMENTED, // GetTime + _Jv_JVMTI_GetTime, // GetTime UNIMPLEMENTED, // GetPotentialCapabilities RESERVED, // reserved141 UNIMPLEMENTED, // AddCapabilities UNIMPLEMENTED, // RelinquishCapabilities - UNIMPLEMENTED, // GetAvailableProcessors + _Jv_JVMTI_GetAvailableProcessors, // GetAvailableProcessors RESERVED, // reserved145 RESERVED, // reserved146 UNIMPLEMENTED, // GetEnvironmentLocalStorage UNIMPLEMENTED, // SetEnvironmentLocalStorage - UNIMPLEMENTED, // AddToBootstrapClassLoaderSearch - UNIMPLEMENTED, // SetVerboseFlag + _Jv_JVMTI_AddToBootstrapClassLoaderSearch, // AddToBootstrapClassLoaderSearch + _Jv_JVMTI_SetVerboseFlag, // SetVerboseFlag RESERVED, // reserved151 RESERVED, // reserved152 RESERVED, // reserved153 - UNIMPLEMENTED // GetObjectSize + _Jv_JVMTI_GetObjectSize // GetObjectSize }; _Jv_JVMTIEnv * @@ -179,6 +2210,417 @@ _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 (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); +}