2626 lines
71 KiB
C++
2626 lines
71 KiB
C++
// jvmti.cc - JVMTI implementation
|
|
|
|
/* Copyright (C) 2006, 2007 Free Software Foundation
|
|
|
|
This file is part of libgcj.
|
|
|
|
This software is copyrighted work licensed under the terms of the
|
|
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
|
|
details. */
|
|
|
|
#include <config.h>
|
|
#include <platform.h>
|
|
|
|
#include <jvm.h>
|
|
#include <java-threads.h>
|
|
#include <java-gc.h>
|
|
#include <java-interp.h>
|
|
#include <jvmti.h>
|
|
#include "jvmti-int.h"
|
|
|
|
#include <gcj/method.h>
|
|
|
|
#include <gnu/classpath/SystemProperties.h>
|
|
#include <gnu/gcj/runtime/BootClassLoader.h>
|
|
#include <gnu/gcj/jvmti/Breakpoint.h>
|
|
#include <gnu/gcj/jvmti/BreakpointManager.h>
|
|
|
|
#include <java/lang/Class.h>
|
|
#include <java/lang/ClassLoader.h>
|
|
#include <java/lang/OutOfMemoryError.h>
|
|
#include <java/lang/Thread.h>
|
|
#include <java/lang/ThreadGroup.h>
|
|
#include <java/lang/Thread$State.h>
|
|
#include <java/lang/Throwable.h>
|
|
#include <java/lang/VMClassLoader.h>
|
|
#include <java/lang/reflect/Field.h>
|
|
#include <java/lang/reflect/Modifier.h>
|
|
#include <java/util/Collection.h>
|
|
#include <java/util/HashMap.h>
|
|
#include <java/util/concurrent/locks/Lock.h>
|
|
#include <java/util/concurrent/locks/ReentrantReadWriteLock.h>
|
|
#include <java/net/URL.h>
|
|
|
|
static void check_enabled_events (void);
|
|
static void check_enabled_event (jvmtiEvent);
|
|
|
|
namespace JVMTI
|
|
{
|
|
// Is JVMTI enabled? (i.e., any jvmtiEnv created?)
|
|
bool enabled;
|
|
|
|
// Event notifications
|
|
bool VMInit = false;
|
|
bool VMDeath = false;
|
|
bool ThreadStart = false;
|
|
bool ThreadEnd = false;
|
|
bool ClassFileLoadHook = false;
|
|
bool ClassLoad = false;
|
|
bool ClassPrepare = false;
|
|
bool VMStart = false;
|
|
bool Exception = false;
|
|
bool ExceptionCatch = false;
|
|
bool SingleStep = false;
|
|
bool FramePop = false;
|
|
bool Breakpoint = false;
|
|
bool FieldAccess = false;
|
|
bool FieldModification = false;
|
|
bool MethodEntry = false;
|
|
bool MethodExit = false;
|
|
bool NativeMethodBind = false;
|
|
bool CompiledMethodLoad = false;
|
|
bool CompiledMethodUnload = false;
|
|
bool DynamicCodeGenerated = false;
|
|
bool DataDumpRequest = false;
|
|
bool reserved72 = false;
|
|
bool MonitorWait = false;
|
|
bool MonitorWaited = false;
|
|
bool MonitorContendedEnter = false;
|
|
bool MonitorContendedEntered = false;
|
|
bool reserved77 = false;
|
|
bool reserved78 = false;
|
|
bool reserved79 = false;
|
|
bool reserved80 = false;
|
|
bool GarbageCollectionStart = false;
|
|
bool GarbageCollectionFinish = false;
|
|
bool ObjectFree = false;
|
|
bool VMObjectAlloc = false;
|
|
};
|
|
|
|
extern struct JNINativeInterface _Jv_JNIFunctions;
|
|
|
|
struct _Jv_rawMonitorID
|
|
{
|
|
_Jv_Mutex_t mutex;
|
|
_Jv_ConditionVariable_t condition;
|
|
};
|
|
|
|
/* A simple linked list of all JVMTI environments. Since
|
|
events must be delivered to environments in the order
|
|
in which the environments were created, new environments
|
|
are added to the end of the list. */
|
|
struct jvmti_env_list
|
|
{
|
|
jvmtiEnv *env;
|
|
struct jvmti_env_list *next;
|
|
};
|
|
static struct jvmti_env_list *_jvmtiEnvironments = NULL;
|
|
static java::util::concurrent::locks::
|
|
ReentrantReadWriteLock *_envListLock = NULL;
|
|
#define FOREACH_ENVIRONMENT(Ele) \
|
|
for (Ele = _jvmtiEnvironments; Ele != NULL; Ele = Ele->next)
|
|
|
|
// Some commonly-used checks
|
|
|
|
#define THREAD_DEFAULT_TO_CURRENT(Ajthread) \
|
|
do \
|
|
{ \
|
|
if (Ajthread == NULL) \
|
|
Ajthread = java::lang::Thread::currentThread (); \
|
|
} \
|
|
while (0)
|
|
|
|
#define THREAD_CHECK_VALID(Athread) \
|
|
do \
|
|
{ \
|
|
if (!java::lang::Thread::class$.isAssignableFrom (&(Athread->class$))) \
|
|
return JVMTI_ERROR_INVALID_THREAD; \
|
|
} \
|
|
while (0)
|
|
|
|
#define THREAD_CHECK_IS_ALIVE(Athread) \
|
|
do \
|
|
{ \
|
|
if (!Athread->isAlive ()) \
|
|
return JVMTI_ERROR_THREAD_NOT_ALIVE; \
|
|
} \
|
|
while (0)
|
|
|
|
// FIXME: if current phase is not set in Phases,
|
|
// return JVMTI_ERROR_WRONG_PHASE
|
|
#define REQUIRE_PHASE(Env, Phases)
|
|
|
|
#define NULL_CHECK(Ptr) \
|
|
do \
|
|
{ \
|
|
if (Ptr == NULL) \
|
|
return JVMTI_ERROR_NULL_POINTER; \
|
|
} \
|
|
while (0)
|
|
|
|
#define ILLEGAL_ARGUMENT(Cond) \
|
|
do \
|
|
{ \
|
|
if ((Cond)) \
|
|
return JVMTI_ERROR_ILLEGAL_ARGUMENT; \
|
|
} \
|
|
while (0)
|
|
|
|
#define CHECK_FOR_NATIVE_METHOD(AjmethodID) \
|
|
do \
|
|
{ \
|
|
jboolean is_native; \
|
|
jvmtiError jerr = env->IsMethodNative (AjmethodID, &is_native); \
|
|
if (jerr != JVMTI_ERROR_NONE) \
|
|
return jerr; \
|
|
if (is_native) \
|
|
return JVMTI_ERROR_NATIVE_METHOD; \
|
|
} \
|
|
while (0)
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SuspendThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
|
|
{
|
|
using namespace java::lang;
|
|
|
|
THREAD_DEFAULT_TO_CURRENT (thread);
|
|
THREAD_CHECK_VALID (thread);
|
|
THREAD_CHECK_IS_ALIVE (thread);
|
|
|
|
_Jv_Thread_t *data = _Jv_ThreadGetData (thread);
|
|
_Jv_SuspendThread (data);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_ResumeThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
|
|
{
|
|
using namespace java::lang;
|
|
|
|
THREAD_DEFAULT_TO_CURRENT (thread);
|
|
THREAD_CHECK_VALID (thread);
|
|
THREAD_CHECK_IS_ALIVE (thread);
|
|
|
|
_Jv_Thread_t *data = _Jv_ThreadGetData (thread);
|
|
_Jv_ResumeThread (data);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_InterruptThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
|
|
{
|
|
using namespace java::lang;
|
|
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
// FIXME: capability handling? 'can_signal_thread'
|
|
if (thread == NULL)
|
|
return JVMTI_ERROR_INVALID_THREAD;
|
|
|
|
THREAD_CHECK_VALID (thread);
|
|
THREAD_CHECK_IS_ALIVE (thread);
|
|
thread->interrupt();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
// This method performs the common tasks to get and set variables of all types.
|
|
// It is called by the _Jv_JVMTI_Get/SetLocalInt/Object/.... methods.
|
|
static jvmtiError
|
|
getLocalFrame (jvmtiEnv *env, jthread thread, jint depth, jint slot, char type,
|
|
_Jv_InterpFrame **iframe)
|
|
{
|
|
using namespace java::lang;
|
|
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
|
|
ILLEGAL_ARGUMENT (depth < 0);
|
|
|
|
THREAD_DEFAULT_TO_CURRENT (thread);
|
|
THREAD_CHECK_VALID (thread);
|
|
THREAD_CHECK_IS_ALIVE (thread);
|
|
|
|
_Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
|
|
|
|
for (int i = 0; i < depth; i++)
|
|
{
|
|
frame = frame->next;
|
|
|
|
if (frame == NULL)
|
|
return JVMTI_ERROR_NO_MORE_FRAMES;
|
|
}
|
|
|
|
if (frame->frame_type == frame_native)
|
|
return JVMTI_ERROR_OPAQUE_FRAME;
|
|
|
|
jint max_locals;
|
|
jvmtiError jerr = env->GetMaxLocals (reinterpret_cast<jmethodID>
|
|
(frame->self->get_method ()),
|
|
&max_locals);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
_Jv_InterpFrame *tmp_iframe = reinterpret_cast<_Jv_InterpFrame *> (frame);
|
|
|
|
// The second slot taken up by a long type is marked as type 'x' meaning it
|
|
// is not valid for access since it holds only the 4 low bytes of the value.
|
|
if (tmp_iframe->locals_type[slot] == 'x')
|
|
return JVMTI_ERROR_INVALID_SLOT;
|
|
|
|
if (tmp_iframe->locals_type[slot] != type)
|
|
return JVMTI_ERROR_TYPE_MISMATCH;
|
|
|
|
// Check for invalid slots, if the type is a long type, we must check that
|
|
// the next slot is valid as well.
|
|
if (slot < 0 || slot >= max_locals
|
|
|| ((type == 'l' || type == 'd') && slot + 1 >= max_locals))
|
|
return JVMTI_ERROR_INVALID_SLOT;
|
|
|
|
*iframe = tmp_iframe;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jobject *value)
|
|
{
|
|
NULL_CHECK (value);
|
|
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
*value = frame->locals[slot].o;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jobject value)
|
|
{
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
frame->locals[slot].o = value;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jint *value)
|
|
{
|
|
NULL_CHECK (value);
|
|
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
*value = frame->locals[slot].i;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jint value)
|
|
{
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
frame->locals[slot].i = value;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jlong *value)
|
|
{
|
|
NULL_CHECK (value);
|
|
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
#if SIZEOF_VOID_P==8
|
|
*value = frame->locals[slot].l;
|
|
#else
|
|
_Jv_word2 val;
|
|
val.ia[0] = frame->locals[slot].ia[0];
|
|
val.ia[1] = frame->locals[slot + 1].ia[0];
|
|
*value = val.l;
|
|
#endif
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jlong value)
|
|
{
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
#if SIZEOF_VOID_P==8
|
|
frame->locals[slot].l = value;
|
|
#else
|
|
_Jv_word2 val;
|
|
val.l = value;
|
|
frame->locals[slot].ia[0] = val.ia[0];
|
|
frame->locals[slot + 1].ia[0] = val.ia[1];
|
|
#endif
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jfloat *value)
|
|
{
|
|
NULL_CHECK (value);
|
|
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
*value = frame->locals[slot].f;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jfloat value)
|
|
{
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
frame->locals[slot].f = value;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jdouble *value)
|
|
{
|
|
NULL_CHECK (value);
|
|
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
#if SIZEOF_VOID_P==8
|
|
*value = frame->locals[slot].d;
|
|
#else
|
|
_Jv_word2 val;
|
|
val.ia[0] = frame->locals[slot].ia[0];
|
|
val.ia[1] = frame->locals[slot + 1].ia[0];
|
|
*value = val.d;
|
|
#endif
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot,
|
|
jdouble value)
|
|
{
|
|
_Jv_InterpFrame *frame;
|
|
jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
#if SIZEOF_VOID_P==8
|
|
frame->locals[slot].d = value;
|
|
#else
|
|
_Jv_word2 val;
|
|
val.d = value;
|
|
frame->locals[slot].ia[0] = val.ia[0];
|
|
frame->locals[slot + 1].ia[0] = val.ia[1];
|
|
#endif
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt,
|
|
jthread **threads)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (thread_cnt);
|
|
NULL_CHECK (threads);
|
|
|
|
using namespace java::lang;
|
|
|
|
ThreadGroup *root_grp = ThreadGroup::root;
|
|
jint estimate = root_grp->activeCount ();
|
|
|
|
JArray<Thread *> *thr_arr;
|
|
|
|
// Allocate some extra space since threads can be created between calls
|
|
try
|
|
{
|
|
thr_arr = reinterpret_cast<JArray<Thread *> *> (JvNewObjectArray
|
|
((estimate * 2),
|
|
&Thread::class$, NULL));
|
|
}
|
|
catch (java::lang::OutOfMemoryError *err)
|
|
{
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
*thread_cnt = root_grp->enumerate (thr_arr);
|
|
|
|
jvmtiError jerr = env->Allocate ((jlong) ((*thread_cnt) * sizeof (jthread)),
|
|
(unsigned char **) threads);
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
// Transfer the threads to the result array
|
|
jthread *tmp_arr = reinterpret_cast<jthread *> (elements (thr_arr));
|
|
|
|
memcpy ((*threads), tmp_arr, (*thread_cnt));
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetFrameCount (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
|
|
jint *frame_count)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
|
|
NULL_CHECK (frame_count);
|
|
|
|
using namespace java::lang;
|
|
|
|
THREAD_DEFAULT_TO_CURRENT (thread);
|
|
THREAD_CHECK_VALID (thread);
|
|
THREAD_CHECK_IS_ALIVE (thread);
|
|
|
|
_Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
|
|
(*frame_count) = frame->depth ();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetThreadState (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
|
|
jint *thread_state_ptr)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
|
|
THREAD_DEFAULT_TO_CURRENT (thread);
|
|
THREAD_CHECK_VALID (thread);
|
|
NULL_CHECK (thread_state_ptr);
|
|
|
|
jint state = 0;
|
|
if (thread->isAlive ())
|
|
{
|
|
state |= JVMTI_THREAD_STATE_ALIVE;
|
|
|
|
_Jv_Thread_t *data = _Jv_ThreadGetData (thread);
|
|
if (_Jv_IsThreadSuspended (data))
|
|
state |= JVMTI_THREAD_STATE_SUSPENDED;
|
|
|
|
if (thread->isInterrupted ())
|
|
state |= JVMTI_THREAD_STATE_INTERRUPTED;
|
|
|
|
_Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
|
|
if (frame != NULL && frame->frame_type == frame_native)
|
|
state |= JVMTI_THREAD_STATE_IN_NATIVE;
|
|
|
|
using namespace java::lang;
|
|
Thread$State *ts = thread->getState ();
|
|
if (ts == Thread$State::RUNNABLE)
|
|
state |= JVMTI_THREAD_STATE_RUNNABLE;
|
|
else if (ts == Thread$State::BLOCKED)
|
|
state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
|
|
else if (ts == Thread$State::TIMED_WAITING
|
|
|| ts == Thread$State::WAITING)
|
|
{
|
|
state |= JVMTI_THREAD_STATE_WAITING;
|
|
state |= ((ts == Thread$State::WAITING)
|
|
? JVMTI_THREAD_STATE_WAITING_INDEFINITELY
|
|
: JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT);
|
|
|
|
/* FIXME: We don't have a way to tell
|
|
the caller why the thread is suspended,
|
|
i.e., JVMTI_THREAD_STATE_SLEEPING,
|
|
JVMTI_THREAD_STATE_PARKED, and
|
|
JVMTI_THREAD_STATE_IN_OBJECT_WAIT
|
|
are never set. */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using namespace java::lang;
|
|
Thread$State *ts = thread->getState ();
|
|
if (ts == Thread$State::TERMINATED)
|
|
state |= JVMTI_THREAD_STATE_TERMINATED;
|
|
}
|
|
|
|
*thread_state_ptr = state;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_CreateRawMonitor (MAYBE_UNUSED jvmtiEnv *env, const char *name,
|
|
jrawMonitorID *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (name);
|
|
NULL_CHECK (result);
|
|
*result = (jrawMonitorID) _Jv_MallocUnchecked (sizeof (_Jv_rawMonitorID));
|
|
if (*result == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
_Jv_MutexInit (&(*result)->mutex);
|
|
_Jv_CondInit (&(*result)->condition);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_DestroyRawMonitor (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
|
|
// Note we have no better way of knowing whether this object is
|
|
// really a raw monitor.
|
|
if (monitor == NULL)
|
|
return JVMTI_ERROR_INVALID_MONITOR;
|
|
// FIXME: perform checks on monitor, release it if this thread owns
|
|
// it.
|
|
#ifdef _Jv_HaveMutexDestroy
|
|
_Jv_MutexDestroy (&monitor->mutex);
|
|
#endif
|
|
_Jv_Free (monitor);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_RawMonitorEnter (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
|
|
{
|
|
if (monitor == NULL)
|
|
return JVMTI_ERROR_INVALID_MONITOR;
|
|
_Jv_MutexLock (&monitor->mutex);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_RawMonitorExit (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
|
|
{
|
|
if (monitor == NULL)
|
|
return JVMTI_ERROR_INVALID_MONITOR;
|
|
if (_Jv_MutexUnlock (&monitor->mutex))
|
|
return JVMTI_ERROR_NOT_MONITOR_OWNER;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_RawMonitorWait (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor,
|
|
jlong millis)
|
|
{
|
|
if (monitor == NULL)
|
|
return JVMTI_ERROR_INVALID_MONITOR;
|
|
int r = _Jv_CondWait (&monitor->condition, &monitor->mutex, millis, 0);
|
|
if (r == _JV_NOT_OWNER)
|
|
return JVMTI_ERROR_NOT_MONITOR_OWNER;
|
|
if (r == _JV_INTERRUPTED)
|
|
return JVMTI_ERROR_INTERRUPT;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_RawMonitorNotify (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
|
|
{
|
|
if (monitor == NULL)
|
|
return JVMTI_ERROR_INVALID_MONITOR;
|
|
if (_Jv_CondNotify (&monitor->condition, &monitor->mutex) == _JV_NOT_OWNER)
|
|
return JVMTI_ERROR_NOT_MONITOR_OWNER;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_RawMonitorNotifyAll (MAYBE_UNUSED jvmtiEnv *env,
|
|
jrawMonitorID monitor)
|
|
{
|
|
if (monitor == NULL)
|
|
return JVMTI_ERROR_INVALID_MONITOR;
|
|
if (_Jv_CondNotifyAll (&monitor->condition, &monitor->mutex)
|
|
== _JV_NOT_OWNER)
|
|
return JVMTI_ERROR_NOT_MONITOR_OWNER;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetBreakpoint (jvmtiEnv *env, jmethodID method, jlocation location)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
|
|
using namespace gnu::gcj::jvmti;
|
|
Breakpoint *bp
|
|
= BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method),
|
|
location);
|
|
if (bp == NULL)
|
|
{
|
|
jclass klass;
|
|
jvmtiError err = env->GetMethodDeclaringClass (method, &klass);
|
|
if (err != JVMTI_ERROR_NONE)
|
|
return err;
|
|
|
|
if (!_Jv_IsInterpretedClass (klass))
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
|
|
_Jv_MethodBase *base = _Jv_FindInterpreterMethod (klass, method);
|
|
if (base == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
|
|
jint flags;
|
|
err = env->GetMethodModifiers (method, &flags);
|
|
if (err != JVMTI_ERROR_NONE)
|
|
return err;
|
|
|
|
if (flags & java::lang::reflect::Modifier::NATIVE)
|
|
return JVMTI_ERROR_NATIVE_METHOD;
|
|
|
|
_Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> (base);
|
|
if (imeth->get_insn (location) == NULL)
|
|
return JVMTI_ERROR_INVALID_LOCATION;
|
|
|
|
// Now the breakpoint can be safely installed
|
|
bp = BreakpointManager::newBreakpoint (reinterpret_cast<jlong> (method),
|
|
location);
|
|
}
|
|
else
|
|
{
|
|
// Duplicate breakpoints are not permitted by JVMTI
|
|
return JVMTI_ERROR_DUPLICATE;
|
|
}
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_ClearBreakpoint (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
|
|
jlocation location)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
|
|
using namespace gnu::gcj::jvmti;
|
|
|
|
Breakpoint *bp
|
|
= BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method),
|
|
location);
|
|
if (bp == NULL)
|
|
return JVMTI_ERROR_NOT_FOUND;
|
|
|
|
BreakpointManager::deleteBreakpoint (reinterpret_cast<jlong> (method), location);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_Allocate (MAYBE_UNUSED jvmtiEnv *env, jlong size,
|
|
unsigned char **result)
|
|
{
|
|
ILLEGAL_ARGUMENT (size < 0);
|
|
NULL_CHECK (result);
|
|
if (size == 0)
|
|
*result = NULL;
|
|
else
|
|
{
|
|
*result = (unsigned char *) _Jv_MallocUnchecked (size);
|
|
if (*result == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_Deallocate (MAYBE_UNUSED jvmtiEnv *env, unsigned char *mem)
|
|
{
|
|
if (mem != NULL)
|
|
_Jv_Free (mem);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetClassStatus (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jint *status_ptr)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (status_ptr);
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
|
|
if (klass->isArray ())
|
|
*status_ptr = JVMTI_CLASS_STATUS_ARRAY;
|
|
else if (klass->isPrimitive ())
|
|
*status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE;
|
|
else
|
|
{
|
|
jbyte state = _Jv_GetClassState (klass);
|
|
*status_ptr = 0;
|
|
if (state >= JV_STATE_LINKED)
|
|
(*status_ptr) |= JVMTI_CLASS_STATUS_VERIFIED;
|
|
if (state >= JV_STATE_PREPARED)
|
|
(*status_ptr) |= JVMTI_CLASS_STATUS_PREPARED;
|
|
if (state == JV_STATE_ERROR || state == JV_STATE_PHANTOM)
|
|
(*status_ptr) |= JVMTI_CLASS_STATUS_ERROR;
|
|
else if (state == JV_STATE_DONE)
|
|
(*status_ptr) |= JVMTI_CLASS_STATUS_INITIALIZED;
|
|
}
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetClassModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jint *mods)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
// Don't bother checking KLASS' type.
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
NULL_CHECK (mods);
|
|
*mods = klass->getModifiers();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetClassMethods (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jint *count_ptr, jmethodID **methods_ptr)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
// FIXME: capability can_maintain_original_method_order
|
|
// Don't bother checking KLASS' type.
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
NULL_CHECK (count_ptr);
|
|
NULL_CHECK (methods_ptr);
|
|
*count_ptr = JvNumMethods(klass);
|
|
|
|
*methods_ptr
|
|
= (jmethodID *) _Jv_MallocUnchecked (*count_ptr * sizeof (jmethodID));
|
|
if (*methods_ptr == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
|
|
jmethodID start = JvGetFirstMethod (klass);
|
|
for (jint i = 0; i < *count_ptr; ++i)
|
|
// FIXME: correct?
|
|
(*methods_ptr)[i] = start + i;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_IsInterface (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jboolean *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
NULL_CHECK (result);
|
|
*result = klass->isInterface();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_IsArrayClass (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jboolean *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
NULL_CHECK (result);
|
|
*result = klass->isArray();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetClassLoader (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jobject *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
NULL_CHECK (result);
|
|
*result = klass->getClassLoaderInternal();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetObjectHashCode (MAYBE_UNUSED jvmtiEnv *env, jobject obj,
|
|
jint *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (obj == NULL)
|
|
return JVMTI_ERROR_INVALID_OBJECT;
|
|
NULL_CHECK (result);
|
|
*result = _Jv_HashCode (obj);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetFieldModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jfieldID field, jint *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
if (field == NULL)
|
|
return JVMTI_ERROR_INVALID_FIELDID;
|
|
NULL_CHECK (result);
|
|
*result = field->getModifiers();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_IsFieldSynthetic (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
|
|
jfieldID field, jboolean *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (klass == NULL)
|
|
return JVMTI_ERROR_INVALID_CLASS;
|
|
if (field == NULL)
|
|
return JVMTI_ERROR_INVALID_FIELDID;
|
|
NULL_CHECK (result);
|
|
|
|
// FIXME: capability can_get_synthetic_attribute
|
|
*result = ((field->getModifiers() & java::lang::reflect::Modifier::SYNTHETIC)
|
|
!= 0);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetMethodName (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
|
|
char **name_ptr, char **signature_ptr,
|
|
char **generic_ptr)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
|
|
if (method == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
|
|
if (name_ptr != NULL)
|
|
{
|
|
int len = static_cast<int> (method->name->len ());
|
|
*name_ptr = (char *) _Jv_MallocUnchecked (len + 1);
|
|
if (*name_ptr == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
strncpy (*name_ptr, method->name->chars (), len);
|
|
(*name_ptr)[len] = '\0';
|
|
}
|
|
|
|
if (signature_ptr != NULL)
|
|
{
|
|
int len = static_cast<int> (method->signature->len ());
|
|
*signature_ptr = (char *) _Jv_MallocUnchecked (len + 1);
|
|
if (*signature_ptr == NULL)
|
|
{
|
|
if (name_ptr != NULL)
|
|
_Jv_Free (*name_ptr);
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
strncpy (*signature_ptr, method->signature->chars (), len);
|
|
(*signature_ptr)[len] = '\0';
|
|
}
|
|
|
|
if (generic_ptr != NULL)
|
|
{
|
|
*generic_ptr = NULL;
|
|
}
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetMethodModifiers (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
|
|
jint *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (method == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
NULL_CHECK (result);
|
|
|
|
// FIXME: mask off some internal bits...
|
|
*result = method->accflags;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLineNumberTable (jvmtiEnv *env, jmethodID method,
|
|
jint *entry_count_ptr,
|
|
jvmtiLineNumberEntry **table_ptr)
|
|
{
|
|
NULL_CHECK (entry_count_ptr);
|
|
NULL_CHECK (table_ptr);
|
|
|
|
jclass klass;
|
|
jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
_Jv_MethodBase *base = _Jv_FindInterpreterMethod (klass, method);
|
|
if (base == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
|
|
if (java::lang::reflect::Modifier::isNative (method->accflags)
|
|
|| !_Jv_IsInterpretedClass (klass))
|
|
return JVMTI_ERROR_NATIVE_METHOD;
|
|
|
|
_Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> (base);
|
|
jlong start, end;
|
|
jintArray lines = NULL;
|
|
jlongArray indices = NULL;
|
|
imeth->get_line_table (start, end, lines, indices);
|
|
if (lines == NULL)
|
|
return JVMTI_ERROR_ABSENT_INFORMATION;
|
|
|
|
jvmtiLineNumberEntry *table;
|
|
jsize len = lines->length * sizeof (jvmtiLineNumberEntry);
|
|
table = (jvmtiLineNumberEntry *) _Jv_MallocUnchecked (len);
|
|
if (table == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
|
|
jint *line = elements (lines);
|
|
jlong *index = elements (indices);
|
|
for (int i = 0; i < lines->length; ++i)
|
|
{
|
|
table[i].start_location = index[i];
|
|
table[i].line_number = line[i];
|
|
}
|
|
|
|
*table_ptr = table;
|
|
*entry_count_ptr = lines->length;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetLocalVariableTable (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
|
|
jint *num_locals,
|
|
jvmtiLocalVariableEntry **locals)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (num_locals);
|
|
NULL_CHECK (locals);
|
|
|
|
CHECK_FOR_NATIVE_METHOD(method);
|
|
|
|
jclass klass;
|
|
jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
_Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *>
|
|
(_Jv_FindInterpreterMethod (klass, method));
|
|
|
|
if (imeth == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
|
|
jerr = env->GetMaxLocals (method, num_locals);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
jerr = env->Allocate (static_cast<jlong>
|
|
((*num_locals) * sizeof (jvmtiLocalVariableEntry)),
|
|
reinterpret_cast<unsigned char **> (locals));
|
|
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
//the slot in the methods local_var_table to get
|
|
int table_slot = 0;
|
|
char *name;
|
|
char *sig;
|
|
char *generic_sig;
|
|
|
|
while (table_slot < *num_locals
|
|
&& imeth->get_local_var_table (&name, &sig, &generic_sig,
|
|
&((((*locals)[table_slot].start_location))),
|
|
&((*locals)[table_slot].length),
|
|
&((*locals)[table_slot].slot),
|
|
table_slot)
|
|
>= 0)
|
|
{
|
|
char **str_ptr = &(*locals)[table_slot].name;
|
|
jerr = env->Allocate (static_cast<jlong> (strlen (name) + 1),
|
|
reinterpret_cast<unsigned char **> (str_ptr));
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
strcpy ((*locals)[table_slot].name, name);
|
|
|
|
str_ptr = &(*locals)[table_slot].signature;
|
|
jerr = env->Allocate (static_cast<jlong> (strlen (sig) + 1),
|
|
reinterpret_cast<unsigned char **> (str_ptr));
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
strcpy ((*locals)[table_slot].signature, sig);
|
|
|
|
str_ptr = &(*locals)[table_slot].generic_signature;
|
|
jerr = env->Allocate (static_cast<jlong> (strlen (generic_sig) + 1),
|
|
reinterpret_cast<unsigned char **> (str_ptr));
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
strcpy ((*locals)[table_slot].generic_signature, generic_sig);
|
|
|
|
table_slot++;
|
|
}
|
|
|
|
if (table_slot == 0)
|
|
return JVMTI_ERROR_ABSENT_INFORMATION;
|
|
|
|
// If there are double or long variables in the table, the the table will be
|
|
// smaller than the max number of slots, so correct for this here.
|
|
if ((table_slot) < *num_locals)
|
|
*num_locals = table_slot;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_IsMethodNative (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
|
|
jboolean *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (method == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
NULL_CHECK (result);
|
|
|
|
*result = ((method->accflags & java::lang::reflect::Modifier::NATIVE) != 0);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_IsMethodSynthetic (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
|
|
jboolean *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (method == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
NULL_CHECK (result);
|
|
|
|
// FIXME capability can_get_synthetic_attribute
|
|
|
|
*result = ((method->accflags & java::lang::reflect::Modifier::SYNTHETIC)
|
|
!= 0);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetMaxLocals (jvmtiEnv *env, jmethodID method, jint *max_locals)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (max_locals);
|
|
|
|
CHECK_FOR_NATIVE_METHOD (method);
|
|
|
|
jclass klass;
|
|
jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
_Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *>
|
|
(_Jv_FindInterpreterMethod (klass, method));
|
|
|
|
if (imeth == NULL)
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
|
|
*max_locals = imeth->get_max_locals ();
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetArgumentsSize (jvmtiEnv *env, jmethodID method, jint *size)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (size);
|
|
|
|
CHECK_FOR_NATIVE_METHOD (method);
|
|
|
|
jvmtiError jerr;
|
|
char *sig;
|
|
jint num_slots = 0;
|
|
|
|
jerr = env->GetMethodName (method, NULL, &sig, NULL);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
// If the method is non-static add a slot for the "this" pointer.
|
|
if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0)
|
|
num_slots++;
|
|
|
|
for (int i = 0; sig[i] != ')'; i++)
|
|
{
|
|
if (sig[i] == 'Z' || sig[i] == 'B' || sig[i] == 'C' || sig[i] == 'S'
|
|
|| sig[i] == 'I' || sig[i] == 'F')
|
|
num_slots++;
|
|
else if (sig[i] == 'J' || sig[i] == 'D')
|
|
{
|
|
// If this is an array of wide types it uses a single slot
|
|
if (i > 0 && sig[i - 1] == '[')
|
|
num_slots++;
|
|
else
|
|
num_slots += 2;
|
|
}
|
|
else if (sig[i] == 'L')
|
|
{
|
|
num_slots++;
|
|
while (sig[i] != ';')
|
|
i++;
|
|
}
|
|
}
|
|
|
|
*size = num_slots;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetMethodDeclaringClass (MAYBE_UNUSED jvmtiEnv *env,
|
|
jmethodID method,
|
|
jclass *declaring_class_ptr)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (declaring_class_ptr);
|
|
|
|
jclass klass = _Jv_GetMethodDeclaringClass (method);
|
|
if (klass != NULL)
|
|
{
|
|
*declaring_class_ptr = klass;
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
return JVMTI_ERROR_INVALID_METHODID;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetClassLoaderClasses (MAYBE_UNUSED jvmtiEnv *env,
|
|
jobject init_loader,
|
|
jint *count_ptr,
|
|
jclass **result_ptr)
|
|
{
|
|
using namespace java::lang;
|
|
using namespace java::util;
|
|
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (count_ptr);
|
|
NULL_CHECK (result_ptr);
|
|
|
|
ClassLoader *loader = (ClassLoader *) init_loader;
|
|
if (loader == NULL)
|
|
loader = VMClassLoader::bootLoader;
|
|
|
|
Collection *values = loader->loadedClasses->values();
|
|
jobjectArray array = values->toArray();
|
|
*count_ptr = array->length;
|
|
jobject *elts = elements (array);
|
|
jclass *result
|
|
= (jclass *) _Jv_MallocUnchecked (*count_ptr * sizeof (jclass));
|
|
if (result == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
|
|
// FIXME: JNI references...
|
|
memcpy (result, elts, *count_ptr * sizeof (jclass));
|
|
|
|
*result_ptr = result;
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetStackTrace (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
|
|
jint start_depth, jint max_frames,
|
|
jvmtiFrameInfo *frames, jint *frame_count)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
|
|
ILLEGAL_ARGUMENT (max_frames < 0);
|
|
|
|
NULL_CHECK (frames);
|
|
NULL_CHECK (frame_count);
|
|
|
|
using namespace java::lang;
|
|
|
|
THREAD_DEFAULT_TO_CURRENT (thread);
|
|
THREAD_CHECK_VALID (thread);
|
|
THREAD_CHECK_IS_ALIVE (thread);
|
|
|
|
jvmtiError jerr = env->GetFrameCount (thread, frame_count);
|
|
if (jerr != JVMTI_ERROR_NONE)
|
|
return jerr;
|
|
|
|
// start_depth can be either a positive number, indicating the depth of the
|
|
// stack at which to begin the trace, or a negative number indicating the
|
|
// number of frames at the bottom of the stack to exclude. These checks
|
|
// ensure that it is a valid value in either case
|
|
|
|
ILLEGAL_ARGUMENT (start_depth >= (*frame_count));
|
|
ILLEGAL_ARGUMENT (start_depth < (-(*frame_count)));
|
|
|
|
_Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
|
|
|
|
// If start_depth is negative use this to determine at what depth to start
|
|
// the trace by adding it to the length of the call stack. This allows the
|
|
// use of the same frame "discarding" mechanism as for a positive start_depth
|
|
if (start_depth < 0)
|
|
start_depth = *frame_count + start_depth;
|
|
|
|
// If start_depth > 0 "remove" start_depth frames from the beginning
|
|
// of the stack before beginning the trace by moving along the frame list.
|
|
while (start_depth > 0)
|
|
{
|
|
frame = frame->next;
|
|
start_depth--;
|
|
(*frame_count)--;
|
|
}
|
|
|
|
// Now check to see if the array supplied by the agent is large enough to
|
|
// hold frame_count frames, after adjustment for start_depth.
|
|
if ((*frame_count) > max_frames)
|
|
(*frame_count) = max_frames;
|
|
|
|
for (int i = 0; i < (*frame_count); i++)
|
|
{
|
|
frames[i].method = frame->self->get_method ();
|
|
|
|
// Set the location in the frame, native frames have location = -1
|
|
if (frame->frame_type == frame_interpreter)
|
|
{
|
|
_Jv_InterpMethod *imeth
|
|
= static_cast<_Jv_InterpMethod *> (frame->self);
|
|
_Jv_InterpFrame *interp_frame
|
|
= static_cast<_Jv_InterpFrame *> (frame);
|
|
frames[i].location = imeth->insn_index (interp_frame->get_pc ());
|
|
}
|
|
else
|
|
frames[i].location = -1;
|
|
|
|
frame = frame->next;
|
|
}
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_ForceGarbageCollection (MAYBE_UNUSED jvmtiEnv *env)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
|
|
_Jv_RunGC();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetJNIFunctionTable (MAYBE_UNUSED jvmtiEnv *env,
|
|
const jniNativeInterface *function_table)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (function_table);
|
|
memcpy (&_Jv_JNIFunctions, function_table, sizeof (jniNativeInterface));
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetJNIFunctionTable (MAYBE_UNUSED jvmtiEnv *env,
|
|
jniNativeInterface **function_table)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (function_table);
|
|
*function_table
|
|
= (jniNativeInterface *) _Jv_MallocUnchecked (sizeof (jniNativeInterface));
|
|
if (*function_table == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
memcpy (*function_table, &_Jv_JNIFunctions, sizeof (jniNativeInterface));
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env)
|
|
{
|
|
NULL_CHECK (env);
|
|
|
|
if (_jvmtiEnvironments == NULL)
|
|
return JVMTI_ERROR_INVALID_ENVIRONMENT;
|
|
else
|
|
{
|
|
_envListLock->writeLock ()->lock ();
|
|
if (_jvmtiEnvironments->env == env)
|
|
{
|
|
struct jvmti_env_list *next = _jvmtiEnvironments->next;
|
|
_Jv_Free (_jvmtiEnvironments);
|
|
_jvmtiEnvironments = next;
|
|
}
|
|
else
|
|
{
|
|
struct jvmti_env_list *e = _jvmtiEnvironments;
|
|
while (e->next != NULL && e->next->env != env)
|
|
e = e->next;
|
|
if (e->next == NULL)
|
|
{
|
|
_envListLock->writeLock ()->unlock ();
|
|
return JVMTI_ERROR_INVALID_ENVIRONMENT;
|
|
}
|
|
|
|
struct jvmti_env_list *next = e->next->next;
|
|
_Jv_Free (e->next);
|
|
e->next = next;
|
|
}
|
|
_envListLock->writeLock ()->unlock ();
|
|
}
|
|
|
|
_Jv_Free (env);
|
|
|
|
check_enabled_events ();
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetSystemProperty (MAYBE_UNUSED jvmtiEnv *env, const char *property,
|
|
char **result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
|
|
NULL_CHECK (property);
|
|
NULL_CHECK (result);
|
|
|
|
jstring name = JvNewStringUTF(property);
|
|
jstring result_str = gnu::classpath::SystemProperties::getProperty(name);
|
|
|
|
if (result_str == NULL)
|
|
return JVMTI_ERROR_NOT_AVAILABLE;
|
|
|
|
int len = JvGetStringUTFLength (result_str);
|
|
*result = (char *) _Jv_MallocUnchecked (len + 1);
|
|
if (*result == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
JvGetStringUTFRegion (result_str, 0, result_str->length(), *result);
|
|
(*result)[len] = '\0';
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetSystemProperty (MAYBE_UNUSED jvmtiEnv *env, const char *property,
|
|
const char *value)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD);
|
|
|
|
NULL_CHECK (property);
|
|
if (value == NULL)
|
|
{
|
|
// FIXME: When would a property not be writeable?
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
jstring prop_str = JvNewStringUTF(property);
|
|
jstring value_str = JvNewStringUTF(value);
|
|
gnu::classpath::SystemProperties::setProperty(prop_str, value_str);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetTime (MAYBE_UNUSED jvmtiEnv *env, jlong *nanos_ptr)
|
|
{
|
|
NULL_CHECK (nanos_ptr);
|
|
*nanos_ptr = _Jv_platform_nanotime();
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetAvailableProcessors (MAYBE_UNUSED jvmtiEnv *env,
|
|
jint *nprocessors_ptr)
|
|
{
|
|
NULL_CHECK (nprocessors_ptr);
|
|
#ifdef _SC_NPROCESSORS_ONLN
|
|
*nprocessors_ptr = sysconf(_SC_NPROCESSORS_ONLN);
|
|
#else
|
|
*nprocessors_ptr = 1;
|
|
#endif
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_AddToBootstrapClassLoaderSearch (MAYBE_UNUSED jvmtiEnv *env,
|
|
const char *segment)
|
|
{
|
|
using namespace java::lang;
|
|
using namespace java::net;
|
|
using namespace gnu::gcj::runtime;
|
|
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD);
|
|
NULL_CHECK (segment);
|
|
|
|
jstring str_segment = JvNewStringUTF(segment);
|
|
URL *url;
|
|
try
|
|
{
|
|
url = new URL(JvNewStringUTF("file"), NULL, str_segment);
|
|
}
|
|
catch (jthrowable ignore)
|
|
{
|
|
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
BootClassLoader *loader = VMClassLoader::bootLoader;
|
|
// Don't call this too early.
|
|
// assert (loader != NULL);
|
|
loader->addURL(url);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetVerboseFlag (MAYBE_UNUSED jvmtiEnv *env, jvmtiVerboseFlag flag,
|
|
jboolean value)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case JVMTI_VERBOSE_OTHER:
|
|
case JVMTI_VERBOSE_GC:
|
|
case JVMTI_VERBOSE_JNI:
|
|
// Ignore.
|
|
break;
|
|
case JVMTI_VERBOSE_CLASS:
|
|
gcj::verbose_class_flag = value;
|
|
break;
|
|
default:
|
|
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetObjectSize (MAYBE_UNUSED jvmtiEnv *env, jobject object,
|
|
jlong *result)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
|
|
if (object == NULL)
|
|
return JVMTI_ERROR_INVALID_OBJECT;
|
|
NULL_CHECK (result);
|
|
|
|
jclass klass = object->getClass();
|
|
if (klass->isArray())
|
|
{
|
|
jclass comp = klass->getComponentType();
|
|
jint base
|
|
= (jint) (_Jv_uintptr_t) _Jv_GetArrayElementFromElementType(NULL,
|
|
klass->getComponentType());
|
|
// FIXME: correct for primitive types?
|
|
jint compSize = comp->size();
|
|
__JArray *array = (__JArray *) object;
|
|
*result = base + array->length * compSize;
|
|
}
|
|
else
|
|
{
|
|
// Note that if OBJECT is a String then it may (if
|
|
// str->data==str) take more space. Do we care?
|
|
*result = klass->size();
|
|
}
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
/* An event is enabled only if it has both an event handler
|
|
and it is enabled in the environment. */
|
|
static void
|
|
check_enabled_event (jvmtiEvent type)
|
|
{
|
|
bool *enabled;
|
|
int offset;
|
|
|
|
#define GET_OFFSET(Event) \
|
|
do \
|
|
{ \
|
|
enabled = &JVMTI::Event; \
|
|
offset = offsetof (jvmtiEventCallbacks, Event); \
|
|
} \
|
|
while (0)
|
|
|
|
switch (type)
|
|
{
|
|
case JVMTI_EVENT_VM_INIT:
|
|
GET_OFFSET (VMInit);
|
|
break;
|
|
|
|
case JVMTI_EVENT_VM_DEATH:
|
|
GET_OFFSET (VMDeath);
|
|
break;
|
|
|
|
case JVMTI_EVENT_THREAD_START:
|
|
GET_OFFSET (ThreadStart);
|
|
break;
|
|
|
|
case JVMTI_EVENT_THREAD_END:
|
|
GET_OFFSET (ThreadEnd);
|
|
break;
|
|
|
|
case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
|
|
GET_OFFSET (ClassFileLoadHook);
|
|
break;
|
|
|
|
case JVMTI_EVENT_CLASS_LOAD:
|
|
GET_OFFSET (ClassLoad);
|
|
break;
|
|
|
|
case JVMTI_EVENT_CLASS_PREPARE:
|
|
GET_OFFSET (ClassPrepare);
|
|
break;
|
|
|
|
case JVMTI_EVENT_VM_START:
|
|
GET_OFFSET (VMStart);
|
|
break;
|
|
|
|
case JVMTI_EVENT_EXCEPTION:
|
|
GET_OFFSET (Exception);
|
|
break;
|
|
|
|
case JVMTI_EVENT_EXCEPTION_CATCH:
|
|
GET_OFFSET (ExceptionCatch);
|
|
break;
|
|
|
|
case JVMTI_EVENT_SINGLE_STEP:
|
|
GET_OFFSET (SingleStep);
|
|
break;
|
|
|
|
case JVMTI_EVENT_FRAME_POP:
|
|
GET_OFFSET (FramePop);
|
|
break;
|
|
|
|
case JVMTI_EVENT_BREAKPOINT:
|
|
GET_OFFSET (Breakpoint);
|
|
break;
|
|
|
|
case JVMTI_EVENT_FIELD_ACCESS:
|
|
GET_OFFSET (FieldAccess);
|
|
break;
|
|
|
|
case JVMTI_EVENT_FIELD_MODIFICATION:
|
|
GET_OFFSET (FieldModification);
|
|
break;
|
|
|
|
case JVMTI_EVENT_METHOD_ENTRY:
|
|
GET_OFFSET (MethodEntry);
|
|
break;
|
|
|
|
case JVMTI_EVENT_METHOD_EXIT:
|
|
GET_OFFSET (MethodExit);
|
|
break;
|
|
|
|
case JVMTI_EVENT_NATIVE_METHOD_BIND:
|
|
GET_OFFSET (NativeMethodBind);
|
|
break;
|
|
|
|
case JVMTI_EVENT_COMPILED_METHOD_LOAD:
|
|
GET_OFFSET (CompiledMethodLoad);
|
|
break;
|
|
|
|
case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
|
|
GET_OFFSET (CompiledMethodUnload);
|
|
break;
|
|
|
|
case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
|
|
GET_OFFSET (DynamicCodeGenerated);
|
|
break;
|
|
|
|
case JVMTI_EVENT_DATA_DUMP_REQUEST:
|
|
GET_OFFSET (DataDumpRequest);
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_WAIT:
|
|
GET_OFFSET (MonitorWait);
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_WAITED:
|
|
GET_OFFSET (MonitorWaited);
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
|
|
GET_OFFSET (MonitorContendedEnter);
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
|
|
GET_OFFSET (MonitorContendedEntered);
|
|
break;
|
|
|
|
case JVMTI_EVENT_GARBAGE_COLLECTION_START:
|
|
GET_OFFSET (GarbageCollectionStart);
|
|
break;
|
|
|
|
case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
|
|
GET_OFFSET (GarbageCollectionFinish);
|
|
break;
|
|
|
|
case JVMTI_EVENT_OBJECT_FREE:
|
|
GET_OFFSET (ObjectFree);
|
|
break;
|
|
|
|
case JVMTI_EVENT_VM_OBJECT_ALLOC:
|
|
GET_OFFSET (VMObjectAlloc);
|
|
break;
|
|
|
|
default:
|
|
fprintf (stderr,
|
|
"libgcj: check_enabled_event for unknown JVMTI event (%d)\n",
|
|
(int) type);
|
|
return;
|
|
}
|
|
#undef GET_OFFSET
|
|
|
|
int index = EVENT_INDEX (type); // safe since caller checks this
|
|
|
|
if (_jvmtiEnvironments != NULL)
|
|
{
|
|
_envListLock->readLock ()->lock ();
|
|
struct jvmti_env_list *e;
|
|
FOREACH_ENVIRONMENT (e)
|
|
{
|
|
char *addr
|
|
= reinterpret_cast<char *> (&e->env->callbacks) + offset;
|
|
void **callback = reinterpret_cast<void **> (addr);
|
|
if (e->env->enabled[index] && *callback != NULL)
|
|
{
|
|
*enabled = true;
|
|
_envListLock->readLock ()->unlock ();
|
|
return;
|
|
}
|
|
}
|
|
|
|
_envListLock->readLock ()->unlock ();
|
|
}
|
|
|
|
*enabled = false;
|
|
}
|
|
|
|
static void
|
|
check_enabled_events ()
|
|
{
|
|
check_enabled_event (JVMTI_EVENT_VM_INIT);
|
|
check_enabled_event (JVMTI_EVENT_VM_DEATH);
|
|
check_enabled_event (JVMTI_EVENT_THREAD_START);
|
|
check_enabled_event (JVMTI_EVENT_THREAD_END);
|
|
check_enabled_event (JVMTI_EVENT_CLASS_FILE_LOAD_HOOK);
|
|
check_enabled_event (JVMTI_EVENT_CLASS_LOAD);
|
|
check_enabled_event (JVMTI_EVENT_CLASS_PREPARE);
|
|
check_enabled_event (JVMTI_EVENT_VM_START);
|
|
check_enabled_event (JVMTI_EVENT_EXCEPTION);
|
|
check_enabled_event (JVMTI_EVENT_EXCEPTION_CATCH);
|
|
check_enabled_event (JVMTI_EVENT_SINGLE_STEP);
|
|
check_enabled_event (JVMTI_EVENT_FRAME_POP);
|
|
check_enabled_event (JVMTI_EVENT_BREAKPOINT);
|
|
check_enabled_event (JVMTI_EVENT_FIELD_ACCESS);
|
|
check_enabled_event (JVMTI_EVENT_FIELD_MODIFICATION);
|
|
check_enabled_event (JVMTI_EVENT_METHOD_ENTRY);
|
|
check_enabled_event (JVMTI_EVENT_METHOD_EXIT);
|
|
check_enabled_event (JVMTI_EVENT_NATIVE_METHOD_BIND);
|
|
check_enabled_event (JVMTI_EVENT_COMPILED_METHOD_LOAD);
|
|
check_enabled_event (JVMTI_EVENT_COMPILED_METHOD_UNLOAD);
|
|
check_enabled_event (JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
|
|
check_enabled_event (JVMTI_EVENT_DATA_DUMP_REQUEST);
|
|
check_enabled_event (JVMTI_EVENT_MONITOR_WAIT);
|
|
check_enabled_event (JVMTI_EVENT_MONITOR_WAITED);
|
|
check_enabled_event (JVMTI_EVENT_MONITOR_CONTENDED_ENTER);
|
|
check_enabled_event (JVMTI_EVENT_MONITOR_CONTENDED_ENTERED);
|
|
check_enabled_event (JVMTI_EVENT_GARBAGE_COLLECTION_START);
|
|
check_enabled_event (JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
|
|
check_enabled_event (JVMTI_EVENT_OBJECT_FREE);
|
|
check_enabled_event (JVMTI_EVENT_VM_OBJECT_ALLOC);
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetEventNotificationMode (jvmtiEnv *env, jvmtiEventMode mode,
|
|
jvmtiEvent type, jthread event_thread, ...)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
|
|
|
|
if (event_thread != NULL)
|
|
{
|
|
THREAD_CHECK_VALID (event_thread);
|
|
THREAD_CHECK_IS_ALIVE (event_thread);
|
|
}
|
|
|
|
bool enabled;
|
|
switch (mode)
|
|
{
|
|
case JVMTI_DISABLE:
|
|
enabled = false;
|
|
break;
|
|
case JVMTI_ENABLE:
|
|
enabled = true;
|
|
break;
|
|
|
|
default:
|
|
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case JVMTI_EVENT_VM_INIT:
|
|
case JVMTI_EVENT_VM_DEATH:
|
|
case JVMTI_EVENT_THREAD_START:
|
|
case JVMTI_EVENT_VM_START:
|
|
case JVMTI_EVENT_COMPILED_METHOD_LOAD:
|
|
case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
|
|
case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
|
|
case JVMTI_EVENT_DATA_DUMP_REQUEST:
|
|
ILLEGAL_ARGUMENT (event_thread != NULL);
|
|
break;
|
|
|
|
case JVMTI_EVENT_THREAD_END:
|
|
case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
|
|
case JVMTI_EVENT_CLASS_LOAD:
|
|
case JVMTI_EVENT_CLASS_PREPARE:
|
|
case JVMTI_EVENT_EXCEPTION:
|
|
case JVMTI_EVENT_EXCEPTION_CATCH:
|
|
case JVMTI_EVENT_SINGLE_STEP:
|
|
case JVMTI_EVENT_FRAME_POP:
|
|
case JVMTI_EVENT_BREAKPOINT:
|
|
case JVMTI_EVENT_FIELD_ACCESS:
|
|
case JVMTI_EVENT_FIELD_MODIFICATION:
|
|
case JVMTI_EVENT_METHOD_ENTRY:
|
|
case JVMTI_EVENT_METHOD_EXIT:
|
|
case JVMTI_EVENT_NATIVE_METHOD_BIND:
|
|
case JVMTI_EVENT_MONITOR_WAIT:
|
|
case JVMTI_EVENT_MONITOR_WAITED:
|
|
case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
|
|
case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
|
|
case JVMTI_EVENT_GARBAGE_COLLECTION_START:
|
|
case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
|
|
case JVMTI_EVENT_OBJECT_FREE:
|
|
case JVMTI_EVENT_VM_OBJECT_ALLOC:
|
|
break;
|
|
|
|
default:
|
|
return JVMTI_ERROR_INVALID_EVENT_TYPE;
|
|
}
|
|
|
|
env->thread[EVENT_INDEX(type)] = event_thread;
|
|
env->enabled[EVENT_INDEX(type)] = enabled;
|
|
check_enabled_event (type);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_SetEventCallbacks (jvmtiEnv *env,
|
|
const jvmtiEventCallbacks *callbacks,
|
|
jint size_of_callbacks)
|
|
{
|
|
REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
|
|
ILLEGAL_ARGUMENT (size_of_callbacks < 0);
|
|
|
|
// Copy the list of callbacks into the environment
|
|
memcpy (&env->callbacks, callbacks, sizeof (jvmtiEventCallbacks));
|
|
|
|
/* Check which events are now enabeld (JVMTI makes no requirements
|
|
about the order in which SetEventCallbacks and SetEventNotifications
|
|
are called. So we must check all events here. */
|
|
check_enabled_events ();
|
|
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
static jvmtiError JNICALL
|
|
_Jv_JVMTI_GetErrorName (MAYBE_UNUSED jvmtiEnv *env, jvmtiError error,
|
|
char **name_ptr)
|
|
{
|
|
NULL_CHECK (name_ptr);
|
|
|
|
const char *name;
|
|
switch (error)
|
|
{
|
|
case JVMTI_ERROR_NONE:
|
|
name = "none";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NULL_POINTER:
|
|
name = "null pointer";
|
|
break;
|
|
|
|
case JVMTI_ERROR_OUT_OF_MEMORY:
|
|
name = "out of memory";
|
|
break;
|
|
|
|
case JVMTI_ERROR_ACCESS_DENIED:
|
|
name = "access denied";
|
|
break;
|
|
|
|
case JVMTI_ERROR_WRONG_PHASE:
|
|
name = "wrong phase";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INTERNAL:
|
|
name = "internal error";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNATTACHED_THREAD:
|
|
name = "unattached thread";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_ENVIRONMENT:
|
|
name = "invalid environment";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_PRIORITY:
|
|
name = "invalid priority";
|
|
break;
|
|
|
|
case JVMTI_ERROR_THREAD_NOT_SUSPENDED:
|
|
name = "thread not suspended";
|
|
break;
|
|
|
|
case JVMTI_ERROR_THREAD_SUSPENDED:
|
|
name = "thread suspended";
|
|
break;
|
|
|
|
case JVMTI_ERROR_THREAD_NOT_ALIVE:
|
|
name = "thread not alive";
|
|
break;
|
|
|
|
case JVMTI_ERROR_CLASS_NOT_PREPARED:
|
|
name = "class not prepared";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NO_MORE_FRAMES:
|
|
name = "no more frames";
|
|
break;
|
|
|
|
case JVMTI_ERROR_OPAQUE_FRAME:
|
|
name = "opaque frame";
|
|
break;
|
|
|
|
case JVMTI_ERROR_DUPLICATE:
|
|
name = "duplicate";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NOT_FOUND:
|
|
name = "not found";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NOT_MONITOR_OWNER:
|
|
name = "not monitor owner";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INTERRUPT:
|
|
name = "interrupted";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNMODIFIABLE_CLASS:
|
|
name = "unmodifiable class";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NOT_AVAILABLE:
|
|
name = "not available";
|
|
break;
|
|
|
|
case JVMTI_ERROR_ABSENT_INFORMATION:
|
|
name = "absent information";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_EVENT_TYPE:
|
|
name = "invalid event type";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NATIVE_METHOD:
|
|
name = "native method";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_THREAD:
|
|
name = "invalid thread";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_THREAD_GROUP:
|
|
name = "invalid thread group";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_OBJECT:
|
|
name = "invalid object";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_CLASS:
|
|
name = "invalid class";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_METHODID:
|
|
name = "invalid method ID";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_LOCATION:
|
|
name = "invalid location";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_FIELDID:
|
|
name = "invalid field ID";
|
|
break;
|
|
|
|
case JVMTI_ERROR_TYPE_MISMATCH:
|
|
name = "type mismatch";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_SLOT:
|
|
name = "invalid slot";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_MONITOR:
|
|
name = "invalid monitor";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_CLASS_FORMAT:
|
|
name = "invalid class format";
|
|
break;
|
|
|
|
case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
|
|
name = "circular class definition";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
|
|
name = "unsupported redefinition: method added";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
|
|
name = "unsupported redefinition: schema changed";
|
|
break;
|
|
|
|
case JVMTI_ERROR_INVALID_TYPESTATE:
|
|
name = "invalid type state";
|
|
break;
|
|
|
|
case JVMTI_ERROR_FAILS_VERIFICATION:
|
|
name = "fails verification";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
|
|
name = "unsupported redefinition: hierarchy changed";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
|
|
name = "unsupported redefinition: method deleted";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_VERSION:
|
|
name = "unsupported version";
|
|
break;
|
|
|
|
case JVMTI_ERROR_NAMES_DONT_MATCH:
|
|
name = "names do not match";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
|
|
name = "unsupported redefinition: class modifiers changed";
|
|
break;
|
|
|
|
case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
|
|
name = "unsupported redefinition: method modifiers changed";
|
|
break;
|
|
|
|
case JVMTI_ERROR_MUST_POSSESS_CAPABILITY:
|
|
name = "must possess capability";
|
|
break;
|
|
|
|
case JVMTI_ERROR_ILLEGAL_ARGUMENT:
|
|
name = "illegal argument";
|
|
break;
|
|
|
|
default:
|
|
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
*name_ptr = (char *) _Jv_MallocUnchecked (strlen (name) + 1);
|
|
if (*name_ptr == NULL)
|
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
|
|
|
strcpy (*name_ptr, name);
|
|
return JVMTI_ERROR_NONE;
|
|
}
|
|
|
|
#define RESERVED NULL
|
|
#define UNIMPLEMENTED NULL
|
|
|
|
struct _Jv_jvmtiEnv _Jv_JVMTI_Interface =
|
|
{
|
|
RESERVED, // reserved1
|
|
_Jv_JVMTI_SetEventNotificationMode, // SetEventNotificationMode
|
|
RESERVED, // reserved3
|
|
_Jv_JVMTI_GetAllThreads, // GetAllThreads
|
|
_Jv_JVMTI_SuspendThread, // SuspendThread
|
|
_Jv_JVMTI_ResumeThread, // ResumeThread
|
|
UNIMPLEMENTED, // StopThread
|
|
_Jv_JVMTI_InterruptThread, // InterruptThread
|
|
UNIMPLEMENTED, // GetThreadInfo
|
|
UNIMPLEMENTED, // GetOwnedMonitorInfo
|
|
UNIMPLEMENTED, // GetCurrentContendedMonitor
|
|
UNIMPLEMENTED, // RunAgentThread
|
|
UNIMPLEMENTED, // GetTopThreadGroups
|
|
UNIMPLEMENTED, // GetThreadGroupInfo
|
|
UNIMPLEMENTED, // GetThreadGroupChildren
|
|
_Jv_JVMTI_GetFrameCount, // GetFrameCount
|
|
_Jv_JVMTI_GetThreadState, // GetThreadState
|
|
RESERVED, // reserved18
|
|
UNIMPLEMENTED, // GetFrameLocation
|
|
UNIMPLEMENTED, // NotifyPopFrame
|
|
_Jv_JVMTI_GetLocalObject, // GetLocalObject
|
|
_Jv_JVMTI_GetLocalInt, // GetLocalInt
|
|
_Jv_JVMTI_GetLocalLong, // GetLocalLong
|
|
_Jv_JVMTI_GetLocalFloat, // GetLocalFloat
|
|
_Jv_JVMTI_GetLocalDouble, // GetLocalDouble
|
|
_Jv_JVMTI_SetLocalObject, // SetLocalObject
|
|
_Jv_JVMTI_SetLocalInt, // SetLocalInt
|
|
_Jv_JVMTI_SetLocalLong, // SetLocalLong
|
|
_Jv_JVMTI_SetLocalFloat, // SetLocalFloat
|
|
_Jv_JVMTI_SetLocalDouble, // SetLocalDouble
|
|
_Jv_JVMTI_CreateRawMonitor, // CreateRawMonitor
|
|
_Jv_JVMTI_DestroyRawMonitor, // DestroyRawMonitor
|
|
_Jv_JVMTI_RawMonitorEnter, // RawMonitorEnter
|
|
_Jv_JVMTI_RawMonitorExit, // RawMonitorExit
|
|
_Jv_JVMTI_RawMonitorWait, // RawMonitorWait
|
|
_Jv_JVMTI_RawMonitorNotify, // RawMonitorNotify
|
|
_Jv_JVMTI_RawMonitorNotifyAll, // RawMonitorNotifyAll
|
|
_Jv_JVMTI_SetBreakpoint, // SetBreakpoint
|
|
_Jv_JVMTI_ClearBreakpoint, // ClearBreakpoint
|
|
RESERVED, // reserved40
|
|
UNIMPLEMENTED, // SetFieldAccessWatch
|
|
UNIMPLEMENTED, // ClearFieldAccessWatch
|
|
UNIMPLEMENTED, // SetFieldModificationWatch
|
|
UNIMPLEMENTED, // ClearFieldModificationWatch
|
|
RESERVED, // reserved45
|
|
_Jv_JVMTI_Allocate, // Allocate
|
|
_Jv_JVMTI_Deallocate, // Deallocate
|
|
UNIMPLEMENTED, // GetClassSignature
|
|
_Jv_JVMTI_GetClassStatus, // GetClassStatus
|
|
UNIMPLEMENTED, // GetSourceFileName
|
|
_Jv_JVMTI_GetClassModifiers, // GetClassModifiers
|
|
_Jv_JVMTI_GetClassMethods, // GetClassMethods
|
|
UNIMPLEMENTED, // GetClassFields
|
|
UNIMPLEMENTED, // GetImplementedInterfaces
|
|
_Jv_JVMTI_IsInterface, // IsInterface
|
|
_Jv_JVMTI_IsArrayClass, // IsArrayClass
|
|
_Jv_JVMTI_GetClassLoader, // GetClassLoader
|
|
_Jv_JVMTI_GetObjectHashCode, // GetObjectHashCode
|
|
UNIMPLEMENTED, // GetObjectMonitorUsage
|
|
UNIMPLEMENTED, // GetFieldName
|
|
UNIMPLEMENTED, // GetFieldDeclaringClass
|
|
_Jv_JVMTI_GetFieldModifiers, // GetFieldModifiers
|
|
_Jv_JVMTI_IsFieldSynthetic, // IsFieldSynthetic
|
|
_Jv_JVMTI_GetMethodName, // GetMethodName
|
|
_Jv_JVMTI_GetMethodDeclaringClass, // GetMethodDeclaringClass
|
|
_Jv_JVMTI_GetMethodModifiers, // GetMethodModifers
|
|
RESERVED, // reserved67
|
|
_Jv_JVMTI_GetMaxLocals, // GetMaxLocals
|
|
_Jv_JVMTI_GetArgumentsSize, // GetArgumentsSize
|
|
_Jv_JVMTI_GetLineNumberTable, // GetLineNumberTable
|
|
UNIMPLEMENTED, // GetMethodLocation
|
|
_Jv_JVMTI_GetLocalVariableTable, // GetLocalVariableTable
|
|
RESERVED, // reserved73
|
|
RESERVED, // reserved74
|
|
UNIMPLEMENTED, // GetBytecodes
|
|
_Jv_JVMTI_IsMethodNative, // IsMethodNative
|
|
_Jv_JVMTI_IsMethodSynthetic, // IsMethodSynthetic
|
|
UNIMPLEMENTED, // GetLoadedClasses
|
|
_Jv_JVMTI_GetClassLoaderClasses, // GetClassLoaderClasses
|
|
UNIMPLEMENTED, // PopFrame
|
|
RESERVED, // reserved81
|
|
RESERVED, // reserved82
|
|
RESERVED, // reserved83
|
|
RESERVED, // reserved84
|
|
RESERVED, // reserved85
|
|
RESERVED, // reserved86
|
|
UNIMPLEMENTED, // RedefineClasses
|
|
UNIMPLEMENTED, // GetVersionNumber
|
|
UNIMPLEMENTED, // GetCapabilities
|
|
UNIMPLEMENTED, // GetSourceDebugExtension
|
|
UNIMPLEMENTED, // IsMethodObsolete
|
|
UNIMPLEMENTED, // SuspendThreadList
|
|
UNIMPLEMENTED, // ResumeThreadList
|
|
RESERVED, // reserved94
|
|
RESERVED, // reserved95
|
|
RESERVED, // reserved96
|
|
RESERVED, // reserved97
|
|
RESERVED, // reserved98
|
|
RESERVED, // reserved99
|
|
UNIMPLEMENTED, // GetAllStackTraces
|
|
UNIMPLEMENTED, // GetThreadListStackTraces
|
|
UNIMPLEMENTED, // GetThreadLocalStorage
|
|
UNIMPLEMENTED, // SetThreadLocalStorage
|
|
_Jv_JVMTI_GetStackTrace, // GetStackTrace
|
|
RESERVED, // reserved105
|
|
UNIMPLEMENTED, // GetTag
|
|
UNIMPLEMENTED, // SetTag
|
|
_Jv_JVMTI_ForceGarbageCollection, // ForceGarbageCollection
|
|
UNIMPLEMENTED, // IterateOverObjectsReachable
|
|
UNIMPLEMENTED, // IterateOverReachableObjects
|
|
UNIMPLEMENTED, // IterateOverHeap
|
|
UNIMPLEMENTED, // IterateOverInstanceOfClass
|
|
RESERVED, // reserved113
|
|
UNIMPLEMENTED, // GetObjectsWithTags
|
|
RESERVED, // reserved115
|
|
RESERVED, // reserved116
|
|
RESERVED, // reserved117
|
|
RESERVED, // reserved118
|
|
RESERVED, // reserved119
|
|
_Jv_JVMTI_SetJNIFunctionTable, // SetJNIFunctionTable
|
|
_Jv_JVMTI_GetJNIFunctionTable, // GetJNIFunctionTable
|
|
_Jv_JVMTI_SetEventCallbacks, // SetEventCallbacks
|
|
UNIMPLEMENTED, // GenerateEvents
|
|
UNIMPLEMENTED, // GetExtensionFunctions
|
|
UNIMPLEMENTED, // GetExtensionEvents
|
|
UNIMPLEMENTED, // SetExtensionEventCallback
|
|
_Jv_JVMTI_DisposeEnvironment, // DisposeEnvironment
|
|
_Jv_JVMTI_GetErrorName, // GetErrorName
|
|
UNIMPLEMENTED, // GetJLocationFormat
|
|
UNIMPLEMENTED, // GetSystemProperties
|
|
_Jv_JVMTI_GetSystemProperty, // GetSystemProperty
|
|
_Jv_JVMTI_SetSystemProperty, // SetSystemProperty
|
|
UNIMPLEMENTED, // GetPhase
|
|
UNIMPLEMENTED, // GetCurrentThreadCpuTimerInfo
|
|
UNIMPLEMENTED, // GetCurrentThreadCpuTime
|
|
UNIMPLEMENTED, // GetThreadCpuTimerInfo
|
|
UNIMPLEMENTED, // GetThreadCpuTime
|
|
UNIMPLEMENTED, // GetTimerInfo
|
|
_Jv_JVMTI_GetTime, // GetTime
|
|
UNIMPLEMENTED, // GetPotentialCapabilities
|
|
RESERVED, // reserved141
|
|
UNIMPLEMENTED, // AddCapabilities
|
|
UNIMPLEMENTED, // RelinquishCapabilities
|
|
_Jv_JVMTI_GetAvailableProcessors, // GetAvailableProcessors
|
|
RESERVED, // reserved145
|
|
RESERVED, // reserved146
|
|
UNIMPLEMENTED, // GetEnvironmentLocalStorage
|
|
UNIMPLEMENTED, // SetEnvironmentLocalStorage
|
|
_Jv_JVMTI_AddToBootstrapClassLoaderSearch, // AddToBootstrapClassLoaderSearch
|
|
_Jv_JVMTI_SetVerboseFlag, // SetVerboseFlag
|
|
RESERVED, // reserved151
|
|
RESERVED, // reserved152
|
|
RESERVED, // reserved153
|
|
_Jv_JVMTI_GetObjectSize // GetObjectSize
|
|
};
|
|
|
|
_Jv_JVMTIEnv *
|
|
_Jv_GetJVMTIEnv (void)
|
|
{
|
|
_Jv_JVMTIEnv *env
|
|
= (_Jv_JVMTIEnv *) _Jv_MallocUnchecked (sizeof (_Jv_JVMTIEnv));
|
|
env->p = &_Jv_JVMTI_Interface;
|
|
struct jvmti_env_list *element
|
|
= (struct jvmti_env_list *) _Jv_MallocUnchecked (sizeof (struct jvmti_env_list));
|
|
element->env = env;
|
|
element->next = NULL;
|
|
|
|
_envListLock->writeLock ()->lock ();
|
|
if (_jvmtiEnvironments == NULL)
|
|
_jvmtiEnvironments = element;
|
|
else
|
|
{
|
|
struct jvmti_env_list *e;
|
|
for (e = _jvmtiEnvironments; e->next != NULL; e = e->next)
|
|
;
|
|
e->next = element;
|
|
}
|
|
_envListLock->writeLock ()->unlock ();
|
|
|
|
/* Mark JVMTI active. This is used to force the interpreter
|
|
to use either debugging or non-debugging code. Once JVMTI
|
|
has been enabled, the non-debug interpreter cannot be used. */
|
|
JVMTI::enabled = true;
|
|
return env;
|
|
}
|
|
|
|
void
|
|
_Jv_JVMTI_Init ()
|
|
{
|
|
_jvmtiEnvironments = NULL;
|
|
_envListLock
|
|
= new java::util::concurrent::locks::ReentrantReadWriteLock ();
|
|
|
|
// No environments, so this should set all JVMTI:: members to false
|
|
check_enabled_events ();
|
|
}
|
|
|
|
static void
|
|
post_event (jvmtiEnv *env, jvmtiEvent type, jthread event_thread, va_list args)
|
|
{
|
|
#define ARG(Type,Name) Type Name = (Type) va_arg (args, Type)
|
|
|
|
#define GET_BOOLEAN_ARG(Name) \
|
|
ARG (int, b); \
|
|
jboolean Name = (b == 0) ? false : true
|
|
|
|
#define GET_CHAR_ARG(Name) \
|
|
ARG (int, c); \
|
|
char Name = static_cast<char> (c)
|
|
|
|
switch (type)
|
|
{
|
|
case JVMTI_EVENT_VM_INIT:
|
|
if (env->callbacks.VMInit != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
env->callbacks.VMInit (env, jni_env, event_thread);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_VM_DEATH:
|
|
if (env->callbacks.VMDeath != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
env->callbacks.VMDeath (env, jni_env);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_THREAD_START:
|
|
if (env->callbacks.ThreadStart != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
env->callbacks.ThreadStart (env, jni_env, event_thread);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_THREAD_END:
|
|
if (env->callbacks.ThreadEnd != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
env->callbacks.ThreadEnd (env, jni_env, event_thread);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
|
|
if (env->callbacks.ClassFileLoadHook != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jclass, class_being_redefined);
|
|
ARG (jobject, loader);
|
|
ARG (const char *, name);
|
|
ARG (jobject, protection_domain);
|
|
ARG (jint, class_data_len);
|
|
ARG (const unsigned char *, class_data);
|
|
ARG (jint *, new_class_data_len);
|
|
ARG (unsigned char **, new_class_data);
|
|
env->callbacks.ClassFileLoadHook (env, jni_env,
|
|
class_being_redefined, loader,
|
|
name, protection_domain,
|
|
class_data_len, class_data,
|
|
new_class_data_len,
|
|
new_class_data);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_CLASS_LOAD:
|
|
if (env->callbacks.ClassLoad != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jclass, klass);
|
|
env->callbacks.ClassLoad (env, jni_env, event_thread, klass);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_CLASS_PREPARE:
|
|
if (env->callbacks.ClassPrepare != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jclass, klass);
|
|
env->callbacks.ClassPrepare (env, jni_env, event_thread, klass);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_VM_START:
|
|
if (env->callbacks.VMStart != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
env->callbacks.VMStart (env, jni_env);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_EXCEPTION:
|
|
if (env->callbacks.Exception != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (jlocation, location);
|
|
ARG (jobject, exception);
|
|
ARG (jmethodID, catch_method);
|
|
ARG (jlocation, catch_location);
|
|
env->callbacks.Exception (env, jni_env, event_thread, method,
|
|
location, exception, catch_method,
|
|
catch_location);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_EXCEPTION_CATCH:
|
|
if (env->callbacks.ExceptionCatch != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (jlocation, location);
|
|
ARG (jobject, exception);
|
|
env->callbacks.ExceptionCatch (env, jni_env, event_thread, method,
|
|
location, exception);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_SINGLE_STEP:
|
|
if (env->callbacks.SingleStep != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (jlocation, location);
|
|
env->callbacks.SingleStep (env, jni_env, event_thread, method,
|
|
location);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_FRAME_POP:
|
|
if (env->callbacks.FramePop != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
GET_BOOLEAN_ARG (was_popped_by_exception);
|
|
env->callbacks.FramePop (env, jni_env, event_thread, method,
|
|
was_popped_by_exception);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_BREAKPOINT:
|
|
if (env->callbacks.Breakpoint != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (jlocation, location);
|
|
env->callbacks.Breakpoint (env, jni_env, event_thread, method,
|
|
location);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_FIELD_ACCESS:
|
|
if (env->callbacks.FieldAccess != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (jlocation, location);
|
|
ARG (jclass, field_class);
|
|
ARG (jobject, object);
|
|
ARG (jfieldID, field);
|
|
env->callbacks.FieldAccess (env, jni_env, event_thread, method,
|
|
location, field_class, object, field);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_FIELD_MODIFICATION:
|
|
if (env->callbacks.FieldModification != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (jlocation, location);
|
|
ARG (jclass, field_class);
|
|
ARG (jobject, object);
|
|
ARG (jfieldID, field);
|
|
GET_CHAR_ARG (signature_type);
|
|
ARG (jvalue, new_value);
|
|
env->callbacks.FieldModification (env, jni_env, event_thread, method,
|
|
location, field_class, object,
|
|
field, signature_type, new_value);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_METHOD_ENTRY:
|
|
if (env->callbacks.MethodEntry != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
env->callbacks.MethodEntry (env, jni_env, event_thread, method);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_METHOD_EXIT:
|
|
if (env->callbacks.MethodExit != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
GET_BOOLEAN_ARG (was_popped_by_exception);
|
|
ARG (jvalue, return_value);
|
|
env->callbacks.MethodExit (env, jni_env, event_thread, method,
|
|
was_popped_by_exception, return_value);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_NATIVE_METHOD_BIND:
|
|
if (env->callbacks.NativeMethodBind != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jmethodID, method);
|
|
ARG (void *, address);
|
|
ARG (void **, new_address_ptr);
|
|
env->callbacks.NativeMethodBind (env, jni_env, event_thread, method,
|
|
address, new_address_ptr);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_COMPILED_METHOD_LOAD:
|
|
if (env->callbacks.CompiledMethodLoad != NULL)
|
|
{
|
|
ARG (jmethodID, method);
|
|
ARG (jint, code_size);
|
|
ARG (const void *, code_addr);
|
|
ARG (jint, map_length);
|
|
ARG (const jvmtiAddrLocationMap *, map);
|
|
ARG (const void *, compile_info);
|
|
env->callbacks.CompiledMethodLoad (env, method, code_size, code_addr,
|
|
map_length, map, compile_info);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
|
|
if (env->callbacks.CompiledMethodUnload != NULL)
|
|
{
|
|
ARG (jmethodID, method);
|
|
ARG (const void *, code_addr);
|
|
env->callbacks.CompiledMethodUnload (env, method, code_addr);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
|
|
if (env->callbacks.DynamicCodeGenerated != NULL)
|
|
{
|
|
ARG (const char *, name);
|
|
ARG (const void *, address);
|
|
ARG (jint, length);
|
|
env->callbacks.DynamicCodeGenerated (env, name, address, length);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_DATA_DUMP_REQUEST:
|
|
if (env->callbacks.DataDumpRequest != NULL)
|
|
{
|
|
env->callbacks.DataDumpRequest (env);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_WAIT:
|
|
if (env->callbacks.MonitorWait != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jobject, object);
|
|
ARG (jlong, timeout);
|
|
env->callbacks.MonitorWait (env, jni_env, event_thread, object,
|
|
timeout);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_WAITED:
|
|
if (env->callbacks.MonitorWaited != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jobject, object);
|
|
GET_BOOLEAN_ARG (timed_out);
|
|
env->callbacks.MonitorWaited (env, jni_env, event_thread, object,
|
|
timed_out);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
|
|
if (env->callbacks.MonitorContendedEnter != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jobject, object);
|
|
env->callbacks.MonitorContendedEnter (env, jni_env, event_thread,
|
|
object);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
|
|
if (env->callbacks.MonitorContendedEntered != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jobject, object);
|
|
env->callbacks.MonitorContendedEntered (env, jni_env, event_thread,
|
|
object);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_GARBAGE_COLLECTION_START:
|
|
if (env->callbacks.GarbageCollectionStart != NULL)
|
|
{
|
|
env->callbacks.GarbageCollectionStart (env);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
|
|
if (env->callbacks.GarbageCollectionFinish != NULL)
|
|
{
|
|
env->callbacks.GarbageCollectionFinish (env);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_OBJECT_FREE:
|
|
if (env->callbacks.ObjectFree != NULL)
|
|
{
|
|
ARG (jlong, tag);
|
|
env->callbacks.ObjectFree (env, tag);
|
|
}
|
|
break;
|
|
|
|
case JVMTI_EVENT_VM_OBJECT_ALLOC:
|
|
if (env->callbacks.VMObjectAlloc != NULL)
|
|
{
|
|
ARG (JNIEnv *, jni_env);
|
|
ARG (jobject, object);
|
|
ARG (jclass, object_class);
|
|
ARG (jlong, size);
|
|
env->callbacks.VMObjectAlloc (env, jni_env, event_thread,
|
|
object, object_class, size);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf (stderr, "libgcj: post of unknown JVMTI event (%d)\n",
|
|
(int) type);
|
|
break;
|
|
}
|
|
va_end (args);
|
|
#undef ARG
|
|
#undef GET_BOOLEAN_ARG
|
|
#undef GET_CHAR_ARG
|
|
}
|
|
|
|
/* Post an event to requesting JVMTI environments
|
|
*
|
|
* This function should not be called without consulting the
|
|
* JVMTI_REQUESTED_EVENT macro first (for speed). It does no real
|
|
* harm (other than kill speed), since this function will still
|
|
* only send the event if it was properly requested by an environment.
|
|
*/
|
|
void
|
|
_Jv_JVMTI_PostEvent (jvmtiEvent type, jthread event_thread, ...)
|
|
{
|
|
va_list args;
|
|
va_start (args, event_thread);
|
|
|
|
_envListLock->readLock ()->lock ();
|
|
struct jvmti_env_list *e;
|
|
FOREACH_ENVIRONMENT (e)
|
|
{
|
|
/* Events are only posted if the event was explicitly enabled,
|
|
it has a registered event handler, and the event thread
|
|
matches (either globally or restricted to a specific thread).
|
|
Here we check all but the event handler, which will be handled
|
|
by post_event. */
|
|
if (e->env->enabled[EVENT_INDEX(type)]
|
|
&& (e->env->thread[EVENT_INDEX(type)] == NULL
|
|
|| e->env->thread[EVENT_INDEX(type)] == event_thread))
|
|
{
|
|
post_event (e->env, type, event_thread, args);
|
|
}
|
|
}
|
|
_envListLock->readLock ()->unlock ();
|
|
va_end (args);
|
|
}
|