| /* |
| * 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_frame_pop { |
| |
| struct FramePopData { |
| jclass test_klass; |
| jmethodID pop_method; |
| }; |
| |
| static void framePopCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thr, |
| [[maybe_unused]] jmethodID method, |
| jboolean was_popped_by_exception) { |
| FramePopData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| jlong location; |
| jmethodID frame_method; |
| if (JvmtiErrorToException(jnienv, |
| jvmti, |
| jvmti->GetFrameLocation(thr, 0, &frame_method, &location))) { |
| return; |
| } |
| CHECK(data->pop_method != nullptr); |
| jobject method_arg = GetJavaMethod(jvmti, jnienv, frame_method); |
| jnienv->CallStaticVoidMethod(data->test_klass, |
| data->pop_method, |
| method_arg, |
| was_popped_by_exception, |
| location); |
| jnienv->DeleteLocalRef(method_arg); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_FramePop_enableFramePopEvent( |
| JNIEnv* env, jclass, jclass klass, jobject notify_method, jthread thr) { |
| FramePopData* data = nullptr; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->Allocate(sizeof(FramePopData), |
| reinterpret_cast<unsigned char**>(&data)))) { |
| return; |
| } |
| memset(data, 0, sizeof(FramePopData)); |
| data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass)); |
| data->pop_method = env->FromReflectedMethod(notify_method); |
| if (env->ExceptionCheck()) { |
| return; |
| } |
| void* old_data = nullptr; |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { |
| return; |
| } else if (old_data != 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; |
| } |
| jvmtiCapabilities caps; |
| memset(&caps, 0, sizeof(caps)); |
| caps.can_generate_frame_pop_events = 1; |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { |
| return; |
| } |
| current_callbacks.FramePop = framePopCB; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventCallbacks(¤t_callbacks, |
| sizeof(current_callbacks)))) { |
| return; |
| } |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_FRAME_POP, |
| thr)); |
| } |
| |
| extern "C" JNIEXPORT jlong JNICALL Java_art_FramePop_makeJvmtiEnvForFramePop(JNIEnv* env, jclass) { |
| JavaVM* vm; |
| jvmtiEnv* out_jvmti_env = nullptr; |
| if (env->GetJavaVM(&vm) != JNI_OK || |
| vm->GetEnv(reinterpret_cast<void**>(&out_jvmti_env), JVMTI_VERSION_1_0) != JNI_OK) { |
| ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); |
| if (rt_exception.get() == nullptr) { |
| // CNFE should be pending. |
| return 0L; |
| } |
| env->ThrowNew(rt_exception.get(), "Unable to create new jvmti_env"); |
| return 0L; |
| } |
| SetAllCapabilities(out_jvmti_env); |
| return static_cast<jlong>(reinterpret_cast<intptr_t>(out_jvmti_env)); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_FramePop_notifyFramePop( |
| JNIEnv* env, jclass, jthread thr, jint depth) { |
| JvmtiErrorToException(env, jvmti_env, jvmti_env->NotifyFramePop(thr, depth)); |
| } |
| |
| } // namespace common_frame_pop |
| } // namespace art |
| |