| /* |
| * 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 <queue> |
| #include <vector> |
| |
| #include "jvmti.h" |
| |
| // Test infrastructure |
| #include "jvmti_helper.h" |
| #include "scoped_local_ref.h" |
| #include "scoped_primitive_array.h" |
| #include "test_env.h" |
| |
| namespace art { |
| namespace Test1940DdmExt { |
| |
| using DdmHandleChunk = jvmtiError(*)(jvmtiEnv* env, |
| jint type_in, |
| jint len_in, |
| const jbyte* data_in, |
| jint* type_out, |
| jint* len_data_out, |
| jbyte** data_out); |
| |
| struct DdmCallbackData { |
| public: |
| DdmCallbackData(jint type, jint size, jbyte* data) : type_(type), data_(data, data + size) {} |
| jint type_; |
| std::vector<jbyte> data_; |
| }; |
| struct DdmsTrackingData { |
| DdmHandleChunk send_ddm_chunk; |
| jrawMonitorID callback_mon; |
| std::queue<DdmCallbackData> callbacks_received; |
| }; |
| |
| template <typename T> |
| static void Dealloc(T* t) { |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t)); |
| } |
| |
| template <typename T, typename ...Rest> |
| static void Dealloc(T* t, Rest... rs) { |
| Dealloc(t); |
| Dealloc(rs...); |
| } |
| |
| extern "C" JNIEXPORT jobject JNICALL Java_art_Test1940_processChunk(JNIEnv* env, |
| jclass, |
| jobject chunk) { |
| DdmsTrackingData* data = nullptr; |
| if (JvmtiErrorToException( |
| env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return nullptr; |
| } |
| CHECK(chunk != nullptr); |
| CHECK(data != nullptr); |
| CHECK(data->send_ddm_chunk != nullptr); |
| ScopedLocalRef<jclass> chunk_class(env, env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk")); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| jfieldID type_field_id = env->GetFieldID(chunk_class.get(), "type", "I"); |
| jfieldID offset_field_id = env->GetFieldID(chunk_class.get(), "offset", "I"); |
| jfieldID length_field_id = env->GetFieldID(chunk_class.get(), "length", "I"); |
| jfieldID data_field_id = env->GetFieldID(chunk_class.get(), "data", "[B"); |
| jint type = env->GetIntField(chunk, type_field_id); |
| jint off = env->GetIntField(chunk, offset_field_id); |
| jint len = env->GetIntField(chunk, length_field_id); |
| ScopedLocalRef<jbyteArray> chunk_buf( |
| env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk, data_field_id))); |
| if (env->ExceptionCheck()) { |
| return nullptr; |
| } |
| ScopedByteArrayRO byte_data(env, chunk_buf.get()); |
| jint out_type; |
| jint out_size; |
| jbyte* out_data; |
| if (JvmtiErrorToException(env, jvmti_env, data->send_ddm_chunk(jvmti_env, |
| type, |
| len, |
| &byte_data[off], |
| /*out*/&out_type, |
| /*out*/&out_size, |
| /*out*/&out_data))) { |
| return nullptr; |
| } else { |
| ScopedLocalRef<jbyteArray> chunk_data(env, env->NewByteArray(out_size)); |
| env->SetByteArrayRegion(chunk_data.get(), 0, out_size, out_data); |
| Dealloc(out_data); |
| ScopedLocalRef<jobject> res(env, env->NewObject(chunk_class.get(), |
| env->GetMethodID(chunk_class.get(), |
| "<init>", |
| "(I[BII)V"), |
| out_type, |
| chunk_data.get(), |
| 0, |
| out_size)); |
| return res.release(); |
| } |
| } |
| |
| static void DeallocParams(jvmtiParamInfo* params, jint n_params) { |
| for (jint i = 0; i < n_params; i++) { |
| Dealloc(params[i].name); |
| } |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test1940_publishListen(JNIEnv* env, |
| jclass test_klass, |
| jobject publish) { |
| jmethodID publish_method = env->FromReflectedMethod(publish); |
| DdmsTrackingData* data = nullptr; |
| if (JvmtiErrorToException( |
| env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| std::vector<DdmCallbackData> callbacks; |
| while (true) { |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(data->callback_mon))) { |
| return; |
| } |
| while (data->callbacks_received.empty()) { |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorWait(data->callback_mon, 0))) { |
| CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->RawMonitorExit(data->callback_mon)); |
| return; |
| } |
| } |
| while (!data->callbacks_received.empty()) { |
| callbacks.emplace_back(std::move(data->callbacks_received.front())); |
| data->callbacks_received.pop(); |
| } |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(data->callback_mon))) { |
| return; |
| } |
| for (auto cb : callbacks) { |
| ScopedLocalRef<jbyteArray> res(env, env->NewByteArray(cb.data_.size())); |
| env->SetByteArrayRegion(res.get(), 0, cb.data_.size(), cb.data_.data()); |
| env->CallStaticVoidMethod(test_klass, publish_method, cb.type_, res.get()); |
| } |
| callbacks.clear(); |
| } |
| } |
| |
| static void JNICALL PublishCB(jvmtiEnv* jvmti, jint type, jint size, jbyte* bytes) { |
| DdmsTrackingData* data = nullptr; |
| CHECK_EQ(JVMTI_ERROR_NONE, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data))); |
| CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorEnter(data->callback_mon)); |
| data->callbacks_received.emplace(type, size, bytes); |
| CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorNotifyAll(data->callback_mon)); |
| CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorExit(data->callback_mon)); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, jclass) { |
| 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; |
| } |
| void* mem = nullptr; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->Allocate(sizeof(DdmsTrackingData), |
| reinterpret_cast<unsigned char**>(&mem)))) { |
| return; |
| } |
| DdmsTrackingData* data = new (mem) DdmsTrackingData{}; |
| if (JvmtiErrorToException( |
| env, jvmti_env, jvmti_env->CreateRawMonitor("callback-mon", &data->callback_mon))) { |
| return; |
| } |
| // Get the extensions. |
| jint n_ext = 0; |
| jvmtiExtensionFunctionInfo* infos = nullptr; |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) { |
| return; |
| } |
| for (jint i = 0; i < n_ext; i++) { |
| jvmtiExtensionFunctionInfo* cur_info = &infos[i]; |
| if (strcmp("com.android.art.internal.ddm.process_chunk", cur_info->id) == 0) { |
| data->send_ddm_chunk = reinterpret_cast<DdmHandleChunk>(cur_info->func); |
| } |
| // Cleanup the cur_info |
| DeallocParams(cur_info->params, cur_info->param_count); |
| Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors); |
| } |
| // Cleanup the array. |
| Dealloc(infos); |
| if (data->send_ddm_chunk == nullptr) { |
| ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); |
| env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions."); |
| return; |
| } |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { |
| return; |
| } |
| |
| jint event_index = -1; |
| bool found_event = false; |
| jvmtiExtensionEventInfo* events = nullptr; |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionEvents(&n_ext, &events))) { |
| return; |
| } |
| for (jint i = 0; i < n_ext; i++) { |
| jvmtiExtensionEventInfo* cur_info = &events[i]; |
| if (strcmp("com.android.art.internal.ddm.publish_chunk_safe", cur_info->id) == 0) { |
| found_event = true; |
| event_index = cur_info->extension_event_index; |
| } |
| // Cleanup the cur_info |
| DeallocParams(cur_info->params, cur_info->param_count); |
| Dealloc(cur_info->id, cur_info->short_description, cur_info->params); |
| } |
| // Cleanup the array. |
| Dealloc(events); |
| if (!found_event) { |
| ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); |
| env->ThrowNew(rt_exception.get(), "Unable to find ddms extension event."); |
| return; |
| } |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetExtensionEventCallback( |
| event_index, reinterpret_cast<jvmtiExtensionEvent>(PublishCB))); |
| return; |
| } |
| |
| } // namespace Test1940DdmExt |
| } // namespace art |