686 lines
24 KiB
C++
686 lines
24 KiB
C++
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "common_helper.h"
|
|
|
|
#include "jni.h"
|
|
#include "jvmti.h"
|
|
|
|
#include "jvmti_helper.h"
|
|
#include "scoped_local_ref.h"
|
|
#include "test_env.h"
|
|
|
|
namespace art {
|
|
|
|
namespace common_trace {
|
|
|
|
static bool IsInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr) {
|
|
void* data;
|
|
ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
jvmti->GetThreadLocalStorage(thr, &data);
|
|
if (exc.get() != nullptr) {
|
|
env->Throw(exc.get());
|
|
}
|
|
if (data == nullptr) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static void SetInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr, bool val) {
|
|
ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
jvmti->SetThreadLocalStorage(thr, (val ? reinterpret_cast<void*>(0x1)
|
|
: reinterpret_cast<void*>(0x0)));
|
|
if (exc.get() != nullptr) {
|
|
env->Throw(exc.get());
|
|
}
|
|
}
|
|
|
|
class ScopedCallbackState {
|
|
public:
|
|
ScopedCallbackState(JNIEnv* jnienv, jvmtiEnv* env, jthread thr)
|
|
: jnienv_(jnienv), env_(env), thr_(thr) {
|
|
CHECK(!IsInCallback(jnienv_, env_, thr_));
|
|
SetInCallback(jnienv_, env_, thr_, true);
|
|
}
|
|
~ScopedCallbackState() {
|
|
CHECK(IsInCallback(jnienv_, env_, thr_));
|
|
SetInCallback(jnienv_, env_, thr_, false);
|
|
}
|
|
|
|
private:
|
|
JNIEnv* jnienv_;
|
|
jvmtiEnv* env_;
|
|
jthread thr_;
|
|
};
|
|
|
|
struct TraceData {
|
|
jclass test_klass;
|
|
jmethodID enter_method;
|
|
jmethodID exit_method;
|
|
jmethodID field_access;
|
|
jmethodID field_modify;
|
|
jmethodID single_step;
|
|
jmethodID thread_start;
|
|
jmethodID thread_end;
|
|
bool access_watch_on_load;
|
|
bool modify_watch_on_load;
|
|
jrawMonitorID trace_mon;
|
|
|
|
jclass GetTestClass(jvmtiEnv* jvmti, JNIEnv* env) {
|
|
if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(trace_mon))) {
|
|
return nullptr;
|
|
}
|
|
jclass out = reinterpret_cast<jclass>(env->NewLocalRef(test_klass));
|
|
if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(trace_mon))) {
|
|
return nullptr;
|
|
}
|
|
return out;
|
|
}
|
|
};
|
|
|
|
static void threadStartCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thread) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
CHECK(data->thread_start != nullptr);
|
|
jnienv->CallStaticVoidMethod(klass.get(), data->thread_start, thread);
|
|
}
|
|
static void threadEndCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thread) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
CHECK(data->thread_end != nullptr);
|
|
jnienv->CallStaticVoidMethod(klass.get(), data->thread_end, thread);
|
|
}
|
|
|
|
static void singleStepCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thread,
|
|
jmethodID method,
|
|
jlocation location) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
if (IsInCallback(jnienv, jvmti, thread)) {
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
CHECK(data->single_step != nullptr);
|
|
ScopedCallbackState st(jnienv, jvmti, thread);
|
|
jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
|
|
jnienv->CallStaticVoidMethod(klass.get(),
|
|
data->single_step,
|
|
thread,
|
|
method_arg,
|
|
static_cast<jlong>(location));
|
|
jnienv->DeleteLocalRef(method_arg);
|
|
}
|
|
|
|
static void fieldAccessCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thr,
|
|
jmethodID method,
|
|
jlocation location,
|
|
jclass field_klass,
|
|
jobject object,
|
|
jfieldID field) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
if (IsInCallback(jnienv, jvmti, thr)) {
|
|
// Don't do callback for either of these to prevent an infinite loop.
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
CHECK(data->field_access != nullptr);
|
|
ScopedCallbackState st(jnienv, jvmti, thr);
|
|
jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
|
|
jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
|
|
jnienv->CallStaticVoidMethod(klass.get(),
|
|
data->field_access,
|
|
method_arg,
|
|
static_cast<jlong>(location),
|
|
field_klass,
|
|
object,
|
|
field_arg);
|
|
jnienv->DeleteLocalRef(method_arg);
|
|
jnienv->DeleteLocalRef(field_arg);
|
|
}
|
|
|
|
static void fieldModificationCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thr,
|
|
jmethodID method,
|
|
jlocation location,
|
|
jclass field_klass,
|
|
jobject object,
|
|
jfieldID field,
|
|
char type_char,
|
|
jvalue new_value) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
if (IsInCallback(jnienv, jvmti, thr)) {
|
|
// Don't do callback recursively to prevent an infinite loop.
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
CHECK(data->field_modify != nullptr);
|
|
ScopedCallbackState st(jnienv, jvmti, thr);
|
|
jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
|
|
jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
|
|
jobject value = GetJavaValueByType(jnienv, type_char, new_value);
|
|
if (jnienv->ExceptionCheck()) {
|
|
jnienv->DeleteLocalRef(method_arg);
|
|
jnienv->DeleteLocalRef(field_arg);
|
|
return;
|
|
}
|
|
jnienv->CallStaticVoidMethod(klass.get(),
|
|
data->field_modify,
|
|
method_arg,
|
|
static_cast<jlong>(location),
|
|
field_klass,
|
|
object,
|
|
field_arg,
|
|
value);
|
|
jnienv->DeleteLocalRef(method_arg);
|
|
jnienv->DeleteLocalRef(field_arg);
|
|
}
|
|
|
|
static void methodExitCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thr,
|
|
jmethodID method,
|
|
jboolean was_popped_by_exception,
|
|
jvalue return_value) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
if (method == data->exit_method ||
|
|
method == data->enter_method ||
|
|
IsInCallback(jnienv, jvmti, thr)) {
|
|
// Don't do callback for either of these to prevent an infinite loop.
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
CHECK(data->exit_method != nullptr);
|
|
ScopedCallbackState st(jnienv, jvmti, thr);
|
|
jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
|
|
jobject result =
|
|
was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value);
|
|
if (jnienv->ExceptionCheck()) {
|
|
return;
|
|
}
|
|
jnienv->CallStaticVoidMethod(klass.get(),
|
|
data->exit_method,
|
|
method_arg,
|
|
was_popped_by_exception,
|
|
result);
|
|
jnienv->DeleteLocalRef(method_arg);
|
|
}
|
|
|
|
static void methodEntryCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thr,
|
|
jmethodID method) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
CHECK(data->enter_method != nullptr);
|
|
if (method == data->exit_method ||
|
|
method == data->enter_method ||
|
|
IsInCallback(jnienv, jvmti, thr)) {
|
|
// Don't do callback for either of these to prevent an infinite loop.
|
|
return;
|
|
}
|
|
ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
|
|
if (klass.get() == nullptr) {
|
|
return;
|
|
}
|
|
ScopedCallbackState st(jnienv, jvmti, thr);
|
|
jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
|
|
if (jnienv->ExceptionCheck()) {
|
|
return;
|
|
}
|
|
jnienv->CallStaticVoidMethod(klass.get(), data->enter_method, method_arg);
|
|
jnienv->DeleteLocalRef(method_arg);
|
|
}
|
|
|
|
static void classPrepareCB(jvmtiEnv* jvmti,
|
|
JNIEnv* jnienv,
|
|
jthread thr ATTRIBUTE_UNUSED,
|
|
jclass klass) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(jnienv, jvmti,
|
|
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
if (data->access_watch_on_load || data->modify_watch_on_load) {
|
|
jint nfields;
|
|
jfieldID* fields;
|
|
if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) {
|
|
return;
|
|
}
|
|
for (jint i = 0; i < nfields; i++) {
|
|
jfieldID f = fields[i];
|
|
// Ignore errors
|
|
if (data->access_watch_on_load) {
|
|
jvmti->SetFieldAccessWatch(klass, f);
|
|
}
|
|
|
|
if (data->modify_watch_on_load) {
|
|
jvmti->SetFieldModificationWatch(klass, f);
|
|
}
|
|
}
|
|
jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
|
|
}
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(
|
|
env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
data->access_watch_on_load = true;
|
|
// We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
|
|
if (JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_CLASS_PREPARE,
|
|
nullptr))) {
|
|
return;
|
|
}
|
|
jint nklasses;
|
|
jclass* klasses;
|
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
|
|
return;
|
|
}
|
|
for (jint i = 0; i < nklasses; i++) {
|
|
jclass k = klasses[i];
|
|
|
|
jint nfields;
|
|
jfieldID* fields;
|
|
jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
|
|
if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
|
|
continue;
|
|
} else if (JvmtiErrorToException(env, jvmti_env, err)) {
|
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
|
|
return;
|
|
}
|
|
for (jint j = 0; j < nfields; j++) {
|
|
jvmti_env->SetFieldAccessWatch(k, fields[j]);
|
|
}
|
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
|
|
}
|
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(
|
|
env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
data->modify_watch_on_load = true;
|
|
// We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
|
|
if (JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_CLASS_PREPARE,
|
|
nullptr))) {
|
|
return;
|
|
}
|
|
jint nklasses;
|
|
jclass* klasses;
|
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
|
|
return;
|
|
}
|
|
for (jint i = 0; i < nklasses; i++) {
|
|
jclass k = klasses[i];
|
|
|
|
jint nfields;
|
|
jfieldID* fields;
|
|
jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
|
|
if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
|
|
continue;
|
|
} else if (JvmtiErrorToException(env, jvmti_env, err)) {
|
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
|
|
return;
|
|
}
|
|
for (jint j = 0; j < nfields; j++) {
|
|
jvmti_env->SetFieldModificationWatch(k, fields[j]);
|
|
}
|
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
|
|
}
|
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
|
|
}
|
|
|
|
static bool GetFieldAndClass(JNIEnv* env,
|
|
jobject ref_field,
|
|
jclass* out_klass,
|
|
jfieldID* out_field) {
|
|
*out_field = env->FromReflectedField(ref_field);
|
|
if (env->ExceptionCheck()) {
|
|
return false;
|
|
}
|
|
jclass field_klass = env->FindClass("java/lang/reflect/Field");
|
|
if (env->ExceptionCheck()) {
|
|
return false;
|
|
}
|
|
jmethodID get_declaring_class_method =
|
|
env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;");
|
|
if (env->ExceptionCheck()) {
|
|
env->DeleteLocalRef(field_klass);
|
|
return false;
|
|
}
|
|
*out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method));
|
|
if (env->ExceptionCheck()) {
|
|
*out_klass = nullptr;
|
|
env->DeleteLocalRef(field_klass);
|
|
return false;
|
|
}
|
|
env->DeleteLocalRef(field_klass);
|
|
return true;
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
|
|
JNIEnv* env,
|
|
jclass trace ATTRIBUTE_UNUSED,
|
|
jobject field_obj) {
|
|
jfieldID field;
|
|
jclass klass;
|
|
if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
|
|
return;
|
|
}
|
|
|
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field));
|
|
env->DeleteLocalRef(klass);
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
|
|
JNIEnv* env,
|
|
jclass trace ATTRIBUTE_UNUSED,
|
|
jobject field_obj) {
|
|
jfieldID field;
|
|
jclass klass;
|
|
if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
|
|
return;
|
|
}
|
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field));
|
|
env->DeleteLocalRef(klass);
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2(
|
|
JNIEnv* env,
|
|
jclass trace ATTRIBUTE_UNUSED,
|
|
jclass klass,
|
|
jobject enter,
|
|
jobject exit,
|
|
jobject field_access,
|
|
jobject field_modify,
|
|
jobject single_step,
|
|
jobject thread_start,
|
|
jobject thread_end,
|
|
jthread thr) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->Allocate(sizeof(TraceData),
|
|
reinterpret_cast<unsigned char**>(&data)))) {
|
|
return;
|
|
}
|
|
memset(data, 0, sizeof(TraceData));
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->CreateRawMonitor("Trace monitor", &data->trace_mon))) {
|
|
return;
|
|
}
|
|
data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
|
|
data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
|
|
data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
|
|
data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
|
|
data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
|
|
data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
|
|
data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr;
|
|
data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr;
|
|
|
|
TraceData* old_data = nullptr;
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->GetEnvironmentLocalStorage(
|
|
reinterpret_cast<void**>(&old_data)))) {
|
|
return;
|
|
} else if (old_data != nullptr && old_data->test_klass != nullptr) {
|
|
ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
|
|
env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
|
|
return;
|
|
}
|
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
|
|
return;
|
|
}
|
|
|
|
current_callbacks.MethodEntry = methodEntryCB;
|
|
current_callbacks.MethodExit = methodExitCB;
|
|
current_callbacks.FieldAccess = fieldAccessCB;
|
|
current_callbacks.FieldModification = fieldModificationCB;
|
|
current_callbacks.ClassPrepare = classPrepareCB;
|
|
current_callbacks.SingleStep = singleStepCB;
|
|
current_callbacks.ThreadStart = threadStartCB;
|
|
current_callbacks.ThreadEnd = threadEndCB;
|
|
if (JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventCallbacks(¤t_callbacks,
|
|
sizeof(current_callbacks)))) {
|
|
return;
|
|
}
|
|
if (enter != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_METHOD_ENTRY,
|
|
thr))) {
|
|
return;
|
|
}
|
|
if (exit != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_METHOD_EXIT,
|
|
thr))) {
|
|
return;
|
|
}
|
|
if (field_access != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_FIELD_ACCESS,
|
|
thr))) {
|
|
return;
|
|
}
|
|
if (field_modify != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_FIELD_MODIFICATION,
|
|
thr))) {
|
|
return;
|
|
}
|
|
if (single_step != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_SINGLE_STEP,
|
|
thr))) {
|
|
return;
|
|
}
|
|
if (thread_start != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_THREAD_START,
|
|
thr))) {
|
|
return;
|
|
}
|
|
if (thread_end != nullptr &&
|
|
JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
|
JVMTI_EVENT_THREAD_END,
|
|
thr))) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
|
|
JNIEnv* env,
|
|
jclass trace,
|
|
jclass klass,
|
|
jobject enter,
|
|
jobject exit,
|
|
jobject field_access,
|
|
jobject field_modify,
|
|
jobject single_step,
|
|
jthread thr) {
|
|
Java_art_Trace_enableTracing2(env,
|
|
trace,
|
|
klass,
|
|
enter,
|
|
exit,
|
|
field_access,
|
|
field_modify,
|
|
single_step,
|
|
/* thread_start */ nullptr,
|
|
/* thread_end */ nullptr,
|
|
thr);
|
|
return;
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
|
|
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
|
|
TraceData* data = nullptr;
|
|
if (JvmtiErrorToException(
|
|
env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
|
|
return;
|
|
}
|
|
// If data is null then we haven't ever enabled tracing so we don't need to do anything.
|
|
if (data == nullptr || data->test_klass == nullptr) {
|
|
return;
|
|
}
|
|
ScopedLocalRef<jthrowable> err(env, nullptr);
|
|
// First disable all the events.
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
|
JVMTI_EVENT_FIELD_ACCESS,
|
|
thr))) {
|
|
env->ExceptionDescribe();
|
|
err.reset(env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
}
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
|
JVMTI_EVENT_FIELD_MODIFICATION,
|
|
thr))) {
|
|
env->ExceptionDescribe();
|
|
err.reset(env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
}
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
|
JVMTI_EVENT_METHOD_ENTRY,
|
|
thr))) {
|
|
env->ExceptionDescribe();
|
|
err.reset(env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
}
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
|
JVMTI_EVENT_METHOD_EXIT,
|
|
thr))) {
|
|
env->ExceptionDescribe();
|
|
err.reset(env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
}
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
|
JVMTI_EVENT_SINGLE_STEP,
|
|
thr))) {
|
|
env->ExceptionDescribe();
|
|
err.reset(env->ExceptionOccurred());
|
|
env->ExceptionClear();
|
|
}
|
|
if (JvmtiErrorToException(env, jvmti_env,
|
|
jvmti_env->RawMonitorEnter(data->trace_mon))) {
|
|
return;
|
|
}
|
|
// Clear test_klass so we know this isn't being used
|
|
env->DeleteGlobalRef(data->test_klass);
|
|
data->test_klass = nullptr;
|
|
if (JvmtiErrorToException(env,
|
|
jvmti_env,
|
|
jvmti_env->RawMonitorExit(data->trace_mon))) {
|
|
return;
|
|
}
|
|
if (err.get() != nullptr) {
|
|
env->Throw(err.get());
|
|
}
|
|
}
|
|
|
|
} // namespace common_trace
|
|
|
|
|
|
} // namespace art
|