/* * 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 "jvmti.h" // Test infrastructure #include "jvmti_helper.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/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 DdmsTrackingData { DdmHandleChunk send_ddm_chunk; jclass test_klass; jmethodID publish_method; }; 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); } } static void JNICALL PublishCB(jvmtiEnv* jvmti, JNIEnv* jnienv, jint type, jint size, jbyte* bytes) { DdmsTrackingData* data = nullptr; if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } ScopedLocalRef<jbyteArray> res(jnienv, jnienv->NewByteArray(size)); jnienv->SetByteArrayRegion(res.get(), 0, size, bytes); jnienv->CallStaticVoidMethod(data->test_klass, data->publish_method, type, res.get()); } extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, jclass, jclass method_klass, jobject publish_method) { 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; } DdmsTrackingData* data = nullptr; if (JvmtiErrorToException(env, jvmti_env, jvmti_env->Allocate(sizeof(DdmsTrackingData), reinterpret_cast<unsigned char**>(&data)))) { return; } memset(data, 0, sizeof(DdmsTrackingData)); data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass)); data->publish_method = env->FromReflectedMethod(publish_method); if (env->ExceptionCheck()) { 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", 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