/*
|
* Copyright (C) 2013 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 <inttypes.h>
|
|
#include <cstdio>
|
#include <memory>
|
#include <string>
|
#include <vector>
|
|
#include "android-base/logging.h"
|
#include "android-base/stringprintf.h"
|
|
#include "jni.h"
|
#include "jvmti.h"
|
#include "scoped_local_ref.h"
|
#include "scoped_utf_chars.h"
|
|
// Test infrastructure
|
#include "jni_binder.h"
|
#include "jni_helper.h"
|
#include "jvmti_helper.h"
|
#include "test_env.h"
|
#include "ti_macros.h"
|
|
namespace art {
|
namespace Test1953PopFrame {
|
|
struct TestData {
|
jlocation target_loc;
|
jmethodID target_method;
|
jclass target_klass;
|
jfieldID target_field;
|
jrawMonitorID notify_monitor;
|
jint frame_pop_offset;
|
jmethodID frame_pop_setup_method;
|
std::vector<std::string> interesting_classes;
|
bool hit_location;
|
|
TestData(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jlocation loc,
|
jobject meth,
|
jclass klass,
|
jobject field,
|
jobject setup_meth,
|
jint pop_offset,
|
const std::vector<std::string>&& interesting)
|
: target_loc(loc),
|
target_method(meth != nullptr ? env->FromReflectedMethod(meth) : nullptr),
|
target_klass(reinterpret_cast<jclass>(env->NewGlobalRef(klass))),
|
target_field(field != nullptr ? env->FromReflectedField(field) : nullptr),
|
frame_pop_offset(pop_offset),
|
frame_pop_setup_method(setup_meth != nullptr ? env->FromReflectedMethod(setup_meth)
|
: nullptr),
|
interesting_classes(interesting),
|
hit_location(false) {
|
JvmtiErrorToException(env, jvmti, jvmti->CreateRawMonitor("SuspendStopMonitor",
|
¬ify_monitor));
|
}
|
|
void PerformSuspend(jvmtiEnv* jvmti, JNIEnv* env) {
|
// Wake up the waiting thread.
|
JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(notify_monitor));
|
hit_location = true;
|
JvmtiErrorToException(env, jvmti, jvmti->RawMonitorNotifyAll(notify_monitor));
|
JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(notify_monitor));
|
// Suspend ourself
|
jvmti->SuspendThread(nullptr);
|
}
|
};
|
|
void JNICALL cbSingleStep(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID meth,
|
jlocation loc) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (meth != data->target_method || loc != data->target_loc) {
|
return;
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbExceptionCatch(jvmtiEnv *jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method,
|
jlocation location ATTRIBUTE_UNUSED,
|
jobject exception ATTRIBUTE_UNUSED) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (method != data->target_method) {
|
return;
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbException(jvmtiEnv *jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method,
|
jlocation location ATTRIBUTE_UNUSED,
|
jobject exception ATTRIBUTE_UNUSED,
|
jmethodID catch_method ATTRIBUTE_UNUSED,
|
jlocation catch_location ATTRIBUTE_UNUSED) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (method != data->target_method) {
|
return;
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbMethodEntry(jvmtiEnv *jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (method != data->target_method) {
|
return;
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbMethodExit(jvmtiEnv *jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method,
|
jboolean was_popped_by_exception ATTRIBUTE_UNUSED,
|
jvalue return_value ATTRIBUTE_UNUSED) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (method != data->target_method) {
|
return;
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbFieldModification(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method ATTRIBUTE_UNUSED,
|
jlocation location ATTRIBUTE_UNUSED,
|
jclass field_klass ATTRIBUTE_UNUSED,
|
jobject object ATTRIBUTE_UNUSED,
|
jfieldID field,
|
char signature_type ATTRIBUTE_UNUSED,
|
jvalue new_value ATTRIBUTE_UNUSED) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (field != data->target_field) {
|
// TODO What to do here.
|
LOG(FATAL) << "Strange, shouldn't get here!";
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbFieldAccess(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method ATTRIBUTE_UNUSED,
|
jlocation location ATTRIBUTE_UNUSED,
|
jclass field_klass,
|
jobject object ATTRIBUTE_UNUSED,
|
jfieldID field) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (field != data->target_field || !env->IsSameObject(field_klass, data->target_klass)) {
|
// TODO What to do here.
|
LOG(FATAL) << "Strange, shouldn't get here!";
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbBreakpointHit(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method,
|
jlocation loc) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (data->frame_pop_setup_method == method) {
|
CHECK(loc == 0) << "We should have stopped at location 0";
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->NotifyFramePop(thr, data->frame_pop_offset))) {
|
return;
|
}
|
return;
|
}
|
if (method != data->target_method || loc != data->target_loc) {
|
// TODO What to do here.
|
LOG(FATAL) << "Strange, shouldn't get here!";
|
}
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbFramePop(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jmethodID method ATTRIBUTE_UNUSED,
|
jboolean was_popped_by_exception ATTRIBUTE_UNUSED) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
data->PerformSuspend(jvmti, env);
|
}
|
|
void JNICALL cbClassLoadOrPrepare(jvmtiEnv* jvmti,
|
JNIEnv* env,
|
jthread thr,
|
jclass klass) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
char* name;
|
if (JvmtiErrorToException(env, jvmti, jvmti->GetClassSignature(klass, &name, nullptr))) {
|
return;
|
}
|
std::string name_str(name);
|
if (JvmtiErrorToException(env,
|
jvmti,
|
jvmti->Deallocate(reinterpret_cast<unsigned char*>(name)))) {
|
return;
|
}
|
if (std::find(data->interesting_classes.cbegin(),
|
data->interesting_classes.cend(),
|
name_str) != data->interesting_classes.cend()) {
|
data->PerformSuspend(jvmti, env);
|
}
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupTest(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
|
jvmtiCapabilities caps;
|
memset(&caps, 0, sizeof(caps));
|
// Most of these will already be there but might as well be complete.
|
caps.can_pop_frame = 1;
|
caps.can_generate_single_step_events = 1;
|
caps.can_generate_breakpoint_events = 1;
|
caps.can_suspend = 1;
|
caps.can_generate_method_entry_events = 1;
|
caps.can_generate_method_exit_events = 1;
|
caps.can_generate_monitor_events = 1;
|
caps.can_generate_exception_events = 1;
|
caps.can_generate_frame_pop_events = 1;
|
caps.can_generate_field_access_events = 1;
|
caps.can_generate_field_modification_events = 1;
|
caps.can_redefine_classes = 1;
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
|
return;
|
}
|
jvmtiEventCallbacks cb;
|
memset(&cb, 0, sizeof(cb));
|
// TODO Add the rest of these.
|
cb.Breakpoint = cbBreakpointHit;
|
cb.SingleStep = cbSingleStep;
|
cb.FieldAccess = cbFieldAccess;
|
cb.FieldModification = cbFieldModification;
|
cb.MethodEntry = cbMethodEntry;
|
cb.MethodExit = cbMethodExit;
|
cb.Exception = cbException;
|
cb.ExceptionCatch = cbExceptionCatch;
|
cb.FramePop = cbFramePop;
|
cb.ClassLoad = cbClassLoadOrPrepare;
|
cb.ClassPrepare = cbClassLoadOrPrepare;
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)));
|
}
|
|
static bool DeleteTestData(JNIEnv* env, jthread thr, TestData* data) {
|
env->DeleteGlobalRef(data->target_klass);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
|
return false;
|
}
|
return JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->Deallocate(reinterpret_cast<uint8_t*>(data)));
|
}
|
|
static TestData* SetupTestData(JNIEnv* env,
|
jobject meth,
|
jlocation loc,
|
jclass target_klass,
|
jobject field,
|
jobject setup_meth,
|
jint pop_offset,
|
const std::vector<std::string>&& interesting_names) {
|
void* data_ptr;
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->Allocate(sizeof(TestData),
|
reinterpret_cast<uint8_t**>(&data_ptr)))) {
|
return nullptr;
|
}
|
data = new (data_ptr) TestData(jvmti_env,
|
env,
|
loc,
|
meth,
|
target_klass,
|
field,
|
setup_meth,
|
pop_offset,
|
std::move(interesting_names));
|
if (env->ExceptionCheck()) {
|
env->DeleteGlobalRef(data->target_klass);
|
jvmti_env->Deallocate(reinterpret_cast<uint8_t*>(data));
|
return nullptr;
|
}
|
return data;
|
}
|
|
static TestData* SetupTestData(JNIEnv* env,
|
jobject meth,
|
jlocation loc,
|
jclass target_klass,
|
jobject field,
|
jobject setup_meth,
|
jint pop_offset) {
|
std::vector<std::string> empty;
|
return SetupTestData(
|
env, meth, loc, target_klass, field, setup_meth, pop_offset, std::move(empty));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupSuspendClassEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jint event_num,
|
jobjectArray interesting_names,
|
jthread thr) {
|
CHECK(event_num == JVMTI_EVENT_CLASS_LOAD || event_num == JVMTI_EVENT_CLASS_PREPARE);
|
std::vector<std::string> names;
|
jint cnt = env->GetArrayLength(interesting_names);
|
for (jint i = 0; i < cnt; i++) {
|
env->PushLocalFrame(1);
|
jstring name_obj = reinterpret_cast<jstring>(env->GetObjectArrayElement(interesting_names, i));
|
const char* name_chr = env->GetStringUTFChars(name_obj, nullptr);
|
names.push_back(std::string(name_chr));
|
env->ReleaseStringUTFChars(name_obj, name_chr);
|
env->PopLocalFrame(nullptr);
|
}
|
TestData* data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, nullptr, 0, nullptr, nullptr, nullptr, 0, std::move(names));
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
static_cast<jvmtiEvent>(event_num),
|
thr));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearSuspendClassEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_CLASS_LOAD,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_CLASS_PREPARE,
|
thr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupSuspendSingleStepAt(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jobject meth,
|
jlocation loc,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, meth, loc, nullptr, nullptr, nullptr, 0);
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
JVMTI_EVENT_SINGLE_STEP,
|
thr));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearSuspendSingleStepFor(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_SINGLE_STEP,
|
thr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupSuspendPopFrameEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jint offset,
|
jobject breakpoint_func,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, nullptr, 0, nullptr, nullptr, breakpoint_func, offset);
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
JVMTI_EVENT_FRAME_POP,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
JVMTI_EVENT_BREAKPOINT,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetBreakpoint(data->frame_pop_setup_method, 0))) {
|
return;
|
}
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearSuspendPopFrameEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_FRAME_POP,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_BREAKPOINT,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->ClearBreakpoint(data->frame_pop_setup_method, 0))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupSuspendBreakpointFor(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jobject meth,
|
jlocation loc,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, meth, loc, nullptr, nullptr, nullptr, 0);
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
|
JVMTI_EVENT_BREAKPOINT,
|
thr))) {
|
return;
|
}
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(data->target_method,
|
data->target_loc));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearSuspendBreakpointFor(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_BREAKPOINT,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->ClearBreakpoint(data->target_method,
|
data->target_loc))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupSuspendExceptionEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jobject method,
|
jboolean is_catch,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(
|
thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, method, 0, nullptr, nullptr, nullptr, 0);
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(
|
JVMTI_ENABLE,
|
is_catch ? JVMTI_EVENT_EXCEPTION_CATCH : JVMTI_EVENT_EXCEPTION,
|
thr));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearSuspendExceptionEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_EXCEPTION_CATCH,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_EXCEPTION,
|
thr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupSuspendMethodEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jobject method,
|
jboolean enter,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(
|
thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, method, 0, nullptr, nullptr, nullptr, 0);
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(
|
JVMTI_ENABLE,
|
enter ? JVMTI_EVENT_METHOD_ENTRY : JVMTI_EVENT_METHOD_EXIT,
|
thr));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearSuspendMethodEvent(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_METHOD_EXIT,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_METHOD_ENTRY,
|
thr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupFieldSuspendFor(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jclass target_klass,
|
jobject field,
|
jboolean access,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(
|
thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, nullptr, 0, target_klass, field, nullptr, 0);
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(
|
JVMTI_ENABLE,
|
access ? JVMTI_EVENT_FIELD_ACCESS : JVMTI_EVENT_FIELD_MODIFICATION,
|
thr))) {
|
return;
|
}
|
if (access) {
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(data->target_klass,
|
data->target_field));
|
} else {
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(data->target_klass,
|
data->target_field));
|
}
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearFieldSuspendFor(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_FIELD_ACCESS,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
|
JVMTI_EVENT_FIELD_MODIFICATION,
|
thr))) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->ClearFieldModificationWatch(
|
data->target_klass, data->target_field)) &&
|
JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->ClearFieldAccessWatch(
|
data->target_klass, data->target_field))) {
|
return;
|
} else {
|
env->ExceptionClear();
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_setupWaitForNativeCall(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(
|
thr, reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data == nullptr) << "Data was not cleared!";
|
data = SetupTestData(env, nullptr, 0, nullptr, nullptr, nullptr, 0);
|
if (data == nullptr) {
|
return;
|
}
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, data))) {
|
return;
|
}
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_clearWaitForNativeCall(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
|
return;
|
}
|
DeleteTestData(env, thr, data);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_waitForSuspendHit(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(thr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(data->notify_monitor))) {
|
return;
|
}
|
while (!data->hit_location) {
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorWait(data->notify_monitor, -1))) {
|
return;
|
}
|
}
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(data->notify_monitor))) {
|
return;
|
}
|
jint state = 0;
|
while (!JvmtiErrorToException(env, jvmti_env, jvmti_env->GetThreadState(thr, &state)) &&
|
(state & JVMTI_THREAD_STATE_SUSPENDED) == 0) { }
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_popFrame(JNIEnv* env,
|
jclass klass ATTRIBUTE_UNUSED,
|
jthread thr) {
|
JvmtiErrorToException(env, jvmti_env, jvmti_env->PopFrame(thr));
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_00024NativeCalledObject_calledFunction(
|
JNIEnv* env, jobject thiz) {
|
env->PushLocalFrame(1);
|
jclass klass = env->GetObjectClass(thiz);
|
jfieldID cnt = env->GetFieldID(klass, "cnt", "I");
|
env->SetIntField(thiz, cnt, env->GetIntField(thiz, cnt) + 1);
|
env->PopLocalFrame(nullptr);
|
TestData *data;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetThreadLocalStorage(/* thread */ nullptr,
|
reinterpret_cast<void**>(&data)))) {
|
return;
|
}
|
CHECK(data != nullptr);
|
data->PerformSuspend(jvmti_env, env);
|
}
|
|
extern "C" JNIEXPORT
|
void JNICALL Java_art_Test1953_00024NativeCallerObject_run(
|
JNIEnv* env, jobject thiz) {
|
env->PushLocalFrame(1);
|
jclass klass = env->GetObjectClass(thiz);
|
jfieldID baseCnt = env->GetFieldID(klass, "baseCnt", "I");
|
env->SetIntField(thiz, baseCnt, env->GetIntField(thiz, baseCnt) + 1);
|
jmethodID called = env->GetMethodID(klass, "calledFunction", "()V");
|
env->CallVoidMethod(thiz, called);
|
env->PopLocalFrame(nullptr);
|
}
|
|
extern "C" JNIEXPORT
|
jboolean JNICALL Java_art_Test1953_isClassLoaded(JNIEnv* env, jclass, jstring name) {
|
ScopedUtfChars chr(env, name);
|
if (env->ExceptionCheck()) {
|
return false;
|
}
|
jint cnt = 0;
|
jclass* klasses = nullptr;
|
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&cnt, &klasses))) {
|
return false;
|
}
|
bool res = false;
|
for (jint i = 0; !res && i < cnt; i++) {
|
char* sig;
|
if (JvmtiErrorToException(env,
|
jvmti_env,
|
jvmti_env->GetClassSignature(klasses[i], &sig, nullptr))) {
|
return false;
|
}
|
res = (strcmp(sig, chr.c_str()) == 0);
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
|
}
|
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
|
return res;
|
}
|
|
} // namespace Test1953PopFrame
|
} // namespace art
|