/*
* Copyright ( C ) 2019 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 "suspend_event_helper.h"
#include <inttypes.h>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "common_helper.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 common_suspend_event {
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;
jmethodID method_exit_callback;
std::vector<std::string> interesting_classes;
bool suspend_on_method_entry_exit;
bool hit_location;
TestData(jvmtiEnv* jvmti,
JNIEnv* env,
jlocation loc,
jobject meth,
jclass klass,
jobject field,
jobject setup_meth,
jint pop_offset,
jobject method_callback,
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),
method_exit_callback(method_callback != nullptr ? env->FromReflectedMethod(method_callback)
: nullptr),
interesting_classes(interesting),
suspend_on_method_entry_exit(true ),
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 PerformSuspension(jvmtiEnv* jvmti, JNIEnv* env) {
TestData* data;
if (JvmtiErrorToException(
env,
jvmti_env,
jvmti->GetThreadLocalStorage(/* thread */ nullptr, reinterpret_cast<void**>(&data)))) {
return ;
}
CHECK(data != nullptr);
data->PerformSuspend(jvmti, env);
}
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,
[[maybe_unused]] jlocation location,
[[maybe_unused]] jobject exception) {
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,
[[maybe_unused]] jlocation location,
[[maybe_unused]] jobject exception,
[[maybe_unused]] jmethodID catch_method,
[[maybe_unused]] jlocation catch_location) {
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,
[[maybe_unused]] jboolean was_popped_by_exception,
[[maybe_unused]] jvalue return_value) {
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 suspendOnceCallback(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, jmethodID method) {
TestData* data;
if (JvmtiErrorToException(
env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast <void **>(&data)))) {
return ;
}
if (data == nullptr) {
return ;
}
if (method != data->target_method) {
return ;
}
env->CallStaticVoidMethod(
data->target_klass, data->method_exit_callback, GetJavaMethod(jvmti, env, method));
if (!data->hit_location && data->suspend_on_method_entry_exit) {
data->PerformSuspend(jvmti, env);
}
}
void JNICALL cbMethodEntrySuspendOnce(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, jmethodID method) {
suspendOnceCallback(jvmti, env, thr, method);
}
void JNICALL cbMethodExitSuspendOnce(jvmtiEnv* jvmti,
JNIEnv* env,
jthread thr,
jmethodID method,
[[maybe_unused]] jboolean was_popped_by_exception,
[[maybe_unused]] jvalue return_value) {
suspendOnceCallback(jvmti, env, thr, method);
}
void JNICALL cbFieldModification(jvmtiEnv* jvmti,
JNIEnv* env,
jthread thr,
[[maybe_unused]] jmethodID method,
[[maybe_unused]] jlocation location,
[[maybe_unused]] jclass field_klass,
[[maybe_unused]] jobject object,
jfieldID field,
[[maybe_unused]] char signature_type,
[[maybe_unused]] jvalue new_value) {
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,
[[maybe_unused]] jmethodID method,
[[maybe_unused]] jlocation location,
jclass field_klass,
[[maybe_unused]] jobject object,
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,
[[maybe_unused]] jmethodID method,
[[maybe_unused]] jboolean was_popped_by_exception) {
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_SuspendEvents_setupTest(JNIEnv* env,
[[maybe_unused]] jclass klass) {
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_force_early_return = 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)));
}
extern "C" JNIEXPORT void JNICALL
Java_art_SuspendEvents_setupMethodExitTest(JNIEnv* env, [[maybe_unused]] jclass klass) {
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_force_early_return = 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));
cb.MethodEntry = cbMethodEntrySuspendOnce;
cb.MethodExit = cbMethodExitSuspendOnce;
cb.Exception = cbException;
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,
jobject method_exit_callback,
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,
method_exit_callback,
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, nullptr, std::move(empty));
}
extern "C" JNIEXPORT void JNICALL
Java_art_SuspendEvents_setupSuspendClassEvent(JNIEnv* env,
[[maybe_unused]] jclass klass,
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 ,
/*method_exit_callback*/ nullptr,
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_SuspendEvents_clearSuspendClassEvent(
JNIEnv* env, [[maybe_unused]] jclass klass, 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_SuspendEvents_setupSuspendSingleStepAt(
JNIEnv* env, [[maybe_unused]] jclass klass, 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_SuspendEvents_clearSuspendSingleStepFor(
JNIEnv* env, [[maybe_unused]] jclass klass, 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_SuspendEvents_setupSuspendPopFrameEvent(
JNIEnv* env, [[maybe_unused]] jclass klass, 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_SuspendEvents_clearSuspendPopFrameEvent(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_setupSuspendBreakpointFor(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_clearSuspendBreakpointFor(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_setupSuspendExceptionEvent(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_enableMethodExitEvents(
JNIEnv* env, [[maybe_unused]] jclass klass , jclass target_class, jobject method, jthread thr) {
TestData* data;
if (JvmtiErrorToException(
env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast <void **>(&data)))) {
return ;
}
if (data != nullptr) {
data->suspend_on_method_entry_exit = false ;
if (data->target_klass == nullptr) {
data->target_klass = reinterpret_cast <jclass>(env->NewGlobalRef(target_class));
} else if (!env->IsSameObject(data->target_klass, target_class)) {
LOG(ERROR) << "No support for setting events for different target classes" ;
}
data->method_exit_callback = env->FromReflectedMethod(method);
}
JvmtiErrorToException(
env,
jvmti_env,
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, thr));
}
extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendExceptionEvent(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_disableExceptionEvents(
JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
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 ;
}
}
extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupSuspendMethodEvent(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_setupSuspendMethodEventWithCallback(JNIEnv* env,
[[maybe_unused]] jclass klass,
jobject method,
jboolean enter,
jthread thr,
jclass target_class,
jobject callback_method) {
TestData* data;
if (JvmtiErrorToException(
env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast <void **>(&data)))) {
return ;
}
CHECK(data == nullptr) << "Data was not cleared!" ;
std::vector<std::string> empty;
data = SetupTestData(
env, method, 0 , target_class, nullptr, nullptr, 0 , callback_method, std::move(empty));
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_SuspendEvents_clearSuspendMethodEvent(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_setupFieldSuspendFor(JNIEnv* env,
[[maybe_unused]] jclass klass,
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_SuspendEvents_clearFieldSuspendFor(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_setupWaitForNativeCall(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_clearWaitForNativeCall(
JNIEnv* env, [[maybe_unused]] jclass klass , 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_SuspendEvents_waitForSuspendHit(JNIEnv* env, [[maybe_unused]] jclass klass, 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_SuspendEvents_clearTestData(
JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
TestData* data;
if (JvmtiErrorToException(
env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast <void **>(&data)))) {
return ;
}
if (data != nullptr) {
DeleteTestData(env, thr, data);
}
}
} // namespace common_suspend_event
} // namespace art
Messung V0.5 in Prozent C=91 H=92 G=91
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet am 2026-06-29)
¤
*© Formatika GbR, Deutschland