diff options
author | 2018-02-20 11:15:54 -0800 | |
---|---|---|
committer | 2018-02-21 08:39:53 -0800 | |
commit | c758875461cca47d1b342c3c56a218a704b04365 (patch) | |
tree | 59827b439d9b0008a80bf9cb3193b94633c3bb91 | |
parent | 535d381b8b14faa8711a003ce3de3f70ac7ac08e (diff) |
Add get_class_loader_class_descriptors JVMTI extension method
This adds a new JVMTI extension:
com.android.art.class.get_class_loader_class_descriptors. This will
enumerate all of the classes known to be loadable with a given
class-loader as the defining class loader. The function gets this by
looking at all dex-files associated with the given class-loader.
Bug: 73504235
Test: ./test.py --host -j50
Change-Id: Ie54223cd40109056396ba609f92b6c02d81dd4ab
-rw-r--r-- | openjdkjvmti/ti_class.cc | 104 | ||||
-rw-r--r-- | openjdkjvmti/ti_class.h | 5 | ||||
-rw-r--r-- | openjdkjvmti/ti_class_loader-inl.h | 76 | ||||
-rw-r--r-- | openjdkjvmti/ti_class_loader.cc | 87 | ||||
-rw-r--r-- | openjdkjvmti/ti_class_loader.h | 13 | ||||
-rw-r--r-- | openjdkjvmti/ti_extension.cc | 26 | ||||
-rw-r--r-- | test/1946-list-descriptors/descriptors.cc | 140 | ||||
-rw-r--r-- | test/1946-list-descriptors/expected.txt | 1 | ||||
-rw-r--r-- | test/1946-list-descriptors/info.txt | 1 | ||||
-rwxr-xr-x | test/1946-list-descriptors/run | 17 | ||||
-rw-r--r-- | test/1946-list-descriptors/src-art/art/Test1946.java | 124 | ||||
-rw-r--r-- | test/1946-list-descriptors/src/Main.java | 21 | ||||
-rw-r--r-- | test/1946-list-descriptors/src/art/Test1946.java | 23 | ||||
-rw-r--r-- | test/Android.bp | 1 |
14 files changed, 586 insertions, 53 deletions
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 4d54d756d5..d510ae5dd4 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -69,7 +69,7 @@ #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" -#include "ti_class_loader.h" +#include "ti_class_loader-inl.h" #include "ti_phase.h" #include "ti_redefine.h" #include "utils.h" @@ -862,6 +862,108 @@ jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +// Copies unique class descriptors into the classes list from dex_files. +static jvmtiError CopyClassDescriptors(jvmtiEnv* env, + const std::vector<const art::DexFile*>& dex_files, + /*out*/jint* count_ptr, + /*out*/char*** classes) { + jvmtiError res = OK; + std::set<art::StringPiece> unique_descriptors; + std::vector<const char*> descriptors; + auto add_descriptor = [&](const char* desc) { + // Don't add duplicates. + if (res == OK && unique_descriptors.find(desc) == unique_descriptors.end()) { + // The desc will remain valid since we hold a ref to the class_loader. + unique_descriptors.insert(desc); + descriptors.push_back(CopyString(env, desc, &res).release()); + } + }; + for (const art::DexFile* dex_file : dex_files) { + uint32_t num_defs = dex_file->NumClassDefs(); + for (uint32_t i = 0; i < num_defs; i++) { + add_descriptor(dex_file->GetClassDescriptor(dex_file->GetClassDef(i))); + } + } + char** out_data = nullptr; + if (res == OK) { + res = env->Allocate(sizeof(char*) * descriptors.size(), + reinterpret_cast<unsigned char**>(&out_data)); + } + if (res != OK) { + env->Deallocate(reinterpret_cast<unsigned char*>(out_data)); + // Failed to allocate. Cleanup everything. + for (const char* data : descriptors) { + env->Deallocate(reinterpret_cast<unsigned char*>(const_cast<char*>(data))); + } + descriptors.clear(); + return res; + } + // Everything is good. + memcpy(out_data, descriptors.data(), sizeof(char*) * descriptors.size()); + *count_ptr = static_cast<jint>(descriptors.size()); + *classes = out_data; + return OK; +} + +jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env, + jobject loader, + /*out*/jint* count_ptr, + /*out*/char*** classes) { + art::Thread* self = art::Thread::Current(); + if (env == nullptr) { + return ERR(INVALID_ENVIRONMENT); + } else if (self == nullptr) { + return ERR(UNATTACHED_THREAD); + } else if (count_ptr == nullptr || classes == nullptr) { + return ERR(NULL_POINTER); + } + art::JNIEnvExt* jnienv = self->GetJniEnv(); + if (loader == nullptr || + jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_BootClassLoader)) { + // We can just get the dex files directly for the boot class path. + return CopyClassDescriptors(env, + art::Runtime::Current()->GetClassLinker()->GetBootClassPath(), + count_ptr, + classes); + } + if (!jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_ClassLoader)) { + return ERR(ILLEGAL_ARGUMENT); + } else if (!jnienv->IsInstanceOf(loader, + art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) { + LOG(ERROR) << "GetClassLoaderClassDescriptors is only implemented for BootClassPath and " + << "dalvik.system.BaseDexClassLoader class loaders"; + // TODO Possibly return OK With no classes would be better since these ones cannot have any + // real classes associated with them. + return ERR(NOT_IMPLEMENTED); + } + + art::ScopedObjectAccess soa(self); + art::StackHandleScope<1> hs(self); + art::Handle<art::mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader))); + std::vector<const art::DexFile*> dex_files; + ClassLoaderHelper::VisitDexFileObjects( + self, + class_loader, + [&] (art::ObjPtr<art::mirror::Object> dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::StackHandleScope<2> hs(self); + art::Handle<art::mirror::Object> h_dex_file(hs.NewHandle(dex_file)); + art::Handle<art::mirror::LongArray> cookie( + hs.NewHandle(ClassLoaderHelper::GetDexFileCookie(h_dex_file))); + size_t num_elements = cookie->GetLength(); + // We need to skip over the oat_file that's the first element. The other elements are all + // dex files. + for (size_t i = 1; i < num_elements; i++) { + dex_files.push_back( + reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(cookie->Get(i)))); + } + // Iterate over all dex files. + return true; + }); + // We hold the loader so the dex files won't go away until after this call at worst. + return CopyClassDescriptors(env, dex_files, count_ptr, classes); +} + jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, jobject initiating_loader, jint* class_count_ptr, diff --git a/openjdkjvmti/ti_class.h b/openjdkjvmti/ti_class.h index dd99e3621f..7e427a06f2 100644 --- a/openjdkjvmti/ti_class.h +++ b/openjdkjvmti/ti_class.h @@ -75,6 +75,11 @@ class ClassUtil { jint* class_count_ptr, jclass** classes_ptr); + static jvmtiError GetClassLoaderClassDescriptors(jvmtiEnv* env, + jobject loader, + jint* count_ptr, + char*** classes); + static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr); static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr); diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h new file mode 100644 index 0000000000..95278f4b2d --- /dev/null +++ b/openjdkjvmti/ti_class_loader-inl.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_ +#define ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_ + +#include "ti_class_loader.h" +#include "art_field-inl.h" +#include "handle.h" +#include "handle_scope.h" +#include "jni_internal.h" +#include "mirror/object.h" +#include "mirror/object_array-inl.h" +#include "well_known_classes.h" + +namespace openjdkjvmti { + +template<typename Visitor> +inline void ClassLoaderHelper::VisitDexFileObjects(art::Thread* self, + art::Handle<art::mirror::ClassLoader> loader, + const Visitor& visitor) { + art::StackHandleScope<1> hs(self); + art::ArtField* element_dex_file_field = art::jni::DecodeArtField( + art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + + art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list( + hs.NewHandle(GetDexElementList(self, loader))); + if (dex_elements_list == nullptr) { + return; + } + + size_t num_elements = dex_elements_list->GetLength(); + // Iterate over the DexPathList$Element to find the right one + for (size_t i = 0; i < num_elements; i++) { + art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i); + CHECK(!current_element.IsNull()); + art::ObjPtr<art::mirror::Object> dex_file(element_dex_file_field->GetObject(current_element)); + if (!dex_file.IsNull()) { + if (!visitor(dex_file)) { + return; + } + } + } +} + +} // namespace openjdkjvmti + +#endif // ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_ diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index d594d6ee29..3df5de909d 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -29,7 +29,7 @@ * questions. */ -#include "ti_class_loader.h" +#include "ti_class_loader-inl.h" #include <limits> @@ -134,45 +134,28 @@ art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie( return new_cookie.Get(); } -// TODO This should return the actual source java.lang.DexFile object for the klass being loaded. -art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject( - art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) { - const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;"; - const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;"; - const char* dex_file_name = "Ldalvik/system/DexFile;"; - const char* dex_path_list_name = "Ldalvik/system/DexPathList;"; - const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;"; - - CHECK(!self->IsExceptionPending()); - art::StackHandleScope<5> hs(self); - art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); +art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> ClassLoaderHelper::GetDexElementList( + art::Thread* self, + art::Handle<art::mirror::ClassLoader> loader) { + art::StackHandleScope<4> hs(self); - art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>( - nullptr)); - art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass( - self, dex_class_loader_name, null_loader))); + art::Handle<art::mirror::Class> + base_dex_loader_class(hs.NewHandle(self->DecodeJObject( + art::WellKnownClasses::dalvik_system_BaseDexClassLoader)->AsClass())); // Get all the ArtFields so we can look in the BaseDexClassLoader - art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField( - "pathList", dex_path_list_name); - CHECK(path_list_field != nullptr); - + art::ArtField* path_list_field = art::jni::DecodeArtField( + art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList); art::ArtField* dex_path_list_element_field = - class_linker->FindClass(self, dex_path_list_name, null_loader) - ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name); - CHECK(dex_path_list_element_field != nullptr); - - art::ArtField* element_dex_file_field = - class_linker->FindClass(self, dex_path_list_element_name, null_loader) - ->FindDeclaredInstanceField("dexFile", dex_file_name); - CHECK(element_dex_file_field != nullptr); + art::jni::DecodeArtField(art::WellKnownClasses::dalvik_system_DexPathList_dexElements); // Check if loader is a BaseDexClassLoader art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass())); // Currently only base_dex_loader is allowed to actually define classes but if this changes in the // future we should make sure to support all class loader types. if (!loader_class->IsSubClass(base_dex_loader_class.Get())) { - LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only " + LOG(ERROR) << "The classloader " << loader_class->PrettyClass() << " is not a " + << base_dex_loader_class->PrettyClass() << " which is currently the only " << "supported class loader type!"; return nullptr; } @@ -180,28 +163,28 @@ art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject( art::Handle<art::mirror::Object> path_list( hs.NewHandle(path_list_field->GetObject(loader.Get()))); CHECK(path_list != nullptr); - CHECK(!self->IsExceptionPending()); - art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( - dex_path_list_element_field->GetObject(path_list.Get())-> - AsObjectArray<art::mirror::Object>())); - CHECK(!self->IsExceptionPending()); - CHECK(dex_elements_list != nullptr); - size_t num_elements = dex_elements_list->GetLength(); - // Iterate over the DexPathList$Element to find the right one - for (size_t i = 0; i < num_elements; i++) { - art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i); - CHECK(!current_element.IsNull()); - // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class - // comes from but it is more annoying because we would need to find this class. It is not - // necessary for proper function since we just need to be in front of the classes old dex file - // in the path. - art::ObjPtr<art::mirror::Object> first_dex_file( - element_dex_file_field->GetObject(current_element)); - if (!first_dex_file.IsNull()) { - return first_dex_file; - } - } - return nullptr; + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list = + dex_path_list_element_field->GetObject(path_list.Get())->AsObjectArray<art::mirror::Object>(); + return dex_elements_list; +} + +// TODO This should return the actual source java.lang.DexFile object for the klass being loaded. +art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject( + art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) { + art::ObjPtr<art::mirror::Object> res = nullptr; + VisitDexFileObjects(self, + loader, + [&] (art::ObjPtr<art::mirror::Object> dex_file) { + res = dex_file; + // Just stop at the first one. + // TODO It would be cleaner to put the art::DexFile into the + // dalvik.system.DexFile the class comes from but it is more annoying + // because we would need to find this class. It is not necessary for proper + // function since we just need to be in front of the classes old dex file in + // the path. + return false; + }); + return res; } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index ceb7b331de..5c9497b0b5 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -82,6 +82,14 @@ class ClassLoaderHelper { art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) REQUIRES_SHARED(art::Locks::mutator_lock_); + // Calls visitor on each java.lang.DexFile associated with the given loader. The visitor should + // return true to continue on to the next DexFile or false to stop iterating. + template<typename Visitor> + static inline void VisitDexFileObjects(art::Thread* self, + art::Handle<art::mirror::ClassLoader> loader, + const Visitor& visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_); + static art::ObjPtr<art::mirror::LongArray> GetDexFileCookie( art::Handle<art::mirror::Object> java_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_); @@ -93,6 +101,11 @@ class ClassLoaderHelper { static void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, art::ObjPtr<art::mirror::LongArray> new_cookie) REQUIRES(art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + static art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetDexElementList( + art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) + REQUIRES_SHARED(art::Locks::mutator_lock_); }; } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index 79a8cd6304..5b1a16c3ff 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -36,6 +36,7 @@ #include "art_jvmti.h" #include "events.h" #include "ti_allocator.h" +#include "ti_class.h" #include "ti_ddms.h" #include "ti_heap.h" #include "thread-inl.h" @@ -226,6 +227,31 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, return error; } + // GetClassLoaderClassDescriptors extension + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::GetClassLoaderClassDescriptors), + "com.android.art.class.get_class_loader_class_descriptors", + "Retrieves a list of all the classes (as class descriptors) that the given class loader is" + " capable of being the defining class loader for. The return format is a list of" + " null-terminated descriptor strings of the form \"L/java/lang/Object;\". Each descriptor" + " will be in the list at most once. If the class_loader is null the bootclassloader will be" + " used. If the class_loader is not null it must either be a java.lang.BootClassLoader, a" + " dalvik.system.BaseDexClassLoader or a derived type. The data_out list and all elements" + " must be deallocated by the caller.", + { + { "class_loader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, true }, + { "class_descriptor_count_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_out", JVMTI_KIND_ALLOC_ALLOC_BUF, JVMTI_TYPE_CCHAR, false }, + }, + { + ERR(NULL_POINTER), + ERR(ILLEGAL_ARGUMENT), + ERR(OUT_OF_MEMORY), + ERR(NOT_IMPLEMENTED), + }); + if (error != ERR(NONE)) { + return error; + } // Copy into output buffer. *extension_count_ptr = ext_vector.size(); diff --git a/test/1946-list-descriptors/descriptors.cc b/test/1946-list-descriptors/descriptors.cc new file mode 100644 index 0000000000..01b306dea5 --- /dev/null +++ b/test/1946-list-descriptors/descriptors.cc @@ -0,0 +1,140 @@ +/* + * 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 "scoped_local_ref.h" +#include "test_env.h" + +namespace art { +namespace Test1946Descriptors { + +typedef jvmtiError (*GetDescriptorList)(jvmtiEnv* env, jobject loader, jint* cnt, char*** descs); + +struct DescriptorData { + GetDescriptorList get_descriptor_list; +}; + +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...); +} + +static void Cleanup(char** data, jint cnt) { + for (jint i = 0; i < cnt; i++) { + Dealloc(data[i]); + } + Dealloc(data); +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1946_getClassloaderDescriptors( + JNIEnv* env, jclass, jobject loader) { + DescriptorData* data = nullptr; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + return nullptr; + } + if (data == nullptr || data->get_descriptor_list == nullptr) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Alloc tracking data not initialized."); + return nullptr; + } + char** classes = nullptr; + jint cnt = -1; + if (JvmtiErrorToException(env, jvmti_env, data->get_descriptor_list(jvmti_env, + loader, + &cnt, + &classes))) { + return nullptr; + } + ScopedLocalRef<jobjectArray> arr(env, env->NewObjectArray(cnt, + env->FindClass("java/lang/String"), + nullptr)); + if (env->ExceptionCheck()) { + Cleanup(classes, cnt); + return nullptr; + } + + for (jint i = 0; i < cnt; i++) { + env->SetObjectArrayElement(arr.get(), i, env->NewStringUTF(classes[i])); + if (env->ExceptionCheck()) { + Cleanup(classes, cnt); + return nullptr; + } + } + Cleanup(classes, cnt); + return arr.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_Test1946_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; + } + DescriptorData* data = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->Allocate(sizeof(DescriptorData), + reinterpret_cast<unsigned char**>(&data)))) { + return; + } + memset(data, 0, sizeof(DescriptorData)); + // 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.class.get_class_loader_class_descriptors", cur_info->id) == 0) { + data->get_descriptor_list = reinterpret_cast<GetDescriptorList>(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->get_descriptor_list == nullptr) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions."); + return; + } + JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data)); + return; +} + +} // namespace Test1946Descriptors +} // namespace art diff --git a/test/1946-list-descriptors/expected.txt b/test/1946-list-descriptors/expected.txt new file mode 100644 index 0000000000..53e0935967 --- /dev/null +++ b/test/1946-list-descriptors/expected.txt @@ -0,0 +1 @@ +Passed! diff --git a/test/1946-list-descriptors/info.txt b/test/1946-list-descriptors/info.txt new file mode 100644 index 0000000000..924e0b3a16 --- /dev/null +++ b/test/1946-list-descriptors/info.txt @@ -0,0 +1 @@ +Tests the jvmti-extension to get the classes contained in class-loaders. diff --git a/test/1946-list-descriptors/run b/test/1946-list-descriptors/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1946-list-descriptors/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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. + +./default-run "$@" --jvmti diff --git a/test/1946-list-descriptors/src-art/art/Test1946.java b/test/1946-list-descriptors/src-art/art/Test1946.java new file mode 100644 index 0000000000..3e5ec65daf --- /dev/null +++ b/test/1946-list-descriptors/src-art/art/Test1946.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 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. + */ + +package art; + +import java.util.*; +import java.lang.reflect.*; +import java.nio.ByteBuffer; +import dalvik.system.InMemoryDexClassLoader; + +public class Test1946 { + // Base64 encoded dex file containing the following classes. Note the class E cannot be loaded. + // public class A {} + // public class B {} + // public class C {} + // public class D {} + // public class E extends ClassNotThere {} + private static final byte[] TEST_CLASSES = Base64.getDecoder().decode( + "ZGV4CjAzNQDzTO8rVDlKlz80vQF4NLYV5MjMMjHlOtRoAwAAcAAAAHhWNBIAAAAAAAAAAOACAAAO" + + "AAAAcAAAAAgAAACoAAAAAQAAAMgAAAAAAAAAAAAAAAcAAADUAAAABQAAAAwBAAC8AQAArAEAACQC" + + "AAAsAgAANAIAADwCAABEAgAATAIAAFQCAABZAgAAXgIAAGMCAAB0AgAAeQIAAH4CAACSAgAABgAA" + + "AAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAANAAAABwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA" + + "AgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAB" + + "AAAAAAAAAK4CAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAIAAAAAAAAAuAIAAAAAAAACAAAAAAAAAAYA" + + "AAAAAAAAAwAAAAAAAADCAgAAAAAAAAQAAAAAAAAABgAAAAAAAAAEAAAAAAAAAMwCAAAAAAAABQAA" + + "AAAAAAADAAAAAAAAAAUAAAAAAAAA1gIAAAAAAAABAAEAAQAAAJUCAAAEAAAAcBAGAAAADgABAAEA" + + "AQAAAJoCAAAEAAAAcBAGAAAADgABAAEAAQAAAJ8CAAAEAAAAcBAGAAAADgABAAEAAQAAAKQCAAAE" + + "AAAAcBAGAAAADgABAAEAAQAAAKkCAAAEAAAAcBADAAAADgAGPGluaXQ+AAZBLmphdmEABkIuamF2" + + "YQAGQy5qYXZhAAZELmphdmEABkUuamF2YQADTEE7AANMQjsAA0xDOwAPTENsYXNzTm90VGhlcmU7" + + "AANMRDsAA0xFOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEABw4AAQAHDgABAAcOAAEABw4AAQAH" + + "DgAAAAEAAICABKwDAAABAAGAgATEAwAAAQACgIAE3AMAAAEABICABPQDAAABAAWAgASMBAsAAAAA" + + "AAAAAQAAAAAAAAABAAAADgAAAHAAAAACAAAACAAAAKgAAAADAAAAAQAAAMgAAAAFAAAABwAAANQA" + + "AAAGAAAABQAAAAwBAAABIAAABQAAAKwBAAACIAAADgAAACQCAAADIAAABQAAAJUCAAAAIAAABQAA" + + "AK4CAAAAEAAAAQAAAOACAAA="); + public class TMP1 {} + public class TMP2 {} + public class TMP3 extends ArrayList {} + + private static void check(boolean b, String msg) { + if (!b) { + throw new Error("Test failed! " + msg); + } + } + + private static <T> void checkEq(T[] full, T[] sub, String msg) { + List<T> f = Arrays.asList(full); + check(full.length == sub.length, "not equal length"); + msg = Arrays.toString(full) + " is not same as " + Arrays.toString(sub) + ": " + msg; + check(Arrays.asList(full).containsAll(Arrays.asList(sub)), msg); + } + + private static <T> void checkSubset(T[] full, T[] sub, String msg) { + msg = Arrays.toString(full) + " does not contain all of " + Arrays.toString(sub) + ": " + msg; + check(Arrays.asList(full).containsAll(Arrays.asList(sub)), msg); + } + + public static void run() throws Exception { + initializeTest(); + // Check a few random classes in BCP. + checkSubset(getClassloaderDescriptors(null), + new String[] { "Ljava/lang/String;", "Ljava/util/TreeSet;" }, + "Missing entries for null classloader."); + // Make sure that null is the same as BootClassLoader + checkEq(getClassloaderDescriptors(null), + getClassloaderDescriptors(Object.class.getClassLoader()), "Object not in bcp!"); + // Check the current class loader gets expected classes. + checkSubset(getClassloaderDescriptors(Test1946.class.getClassLoader()), + new String[] { + "Lart/Test1946;", + "Lart/Test1946$TMP1;", + "Lart/Test1946$TMP2;", + "Lart/Test1946$TMP3;" + }, + "Missing entries for current class classloader."); + // Check that the result is exactly what we expect and includes classes that fail verification. + checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES, + ClassLoader.getSystemClassLoader())), + new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" }, + "Unexpected classes in custom classloader"); + checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES, + Object.class.getClassLoader())), + new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" }, + "Unexpected classes in custom classloader"); + checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES, + Test1946.class.getClassLoader())), + new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" }, + "Unexpected classes in custom classloader"); + // Check we only get 1 copy of each descriptor. + checkEq(getClassloaderDescriptors(makeClassLoaderFrom(Arrays.asList(TEST_CLASSES, TEST_CLASSES), + Test1946.class.getClassLoader())), + new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" }, + "Unexpected classes in custom classloader"); + System.out.println("Passed!"); + } + + private static ClassLoader makeClassLoaderFrom(byte[] data, ClassLoader parent) throws Exception { + return new InMemoryDexClassLoader(ByteBuffer.wrap(data), parent); + } + + private static ClassLoader makeClassLoaderFrom(List<byte[]> data, ClassLoader parent) + throws Exception { + ArrayList<ByteBuffer> bufs = new ArrayList<>(); + for (byte[] d : data) { + bufs.add(ByteBuffer.wrap(d)); + } + return new InMemoryDexClassLoader(bufs.toArray(new ByteBuffer[0]), parent); + } + + private static native void initializeTest(); + private static native String[] getClassloaderDescriptors(ClassLoader loader); +} diff --git a/test/1946-list-descriptors/src/Main.java b/test/1946-list-descriptors/src/Main.java new file mode 100644 index 0000000000..7d6f7ce986 --- /dev/null +++ b/test/1946-list-descriptors/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 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. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1946.run(); + } +} diff --git a/test/1946-list-descriptors/src/art/Test1946.java b/test/1946-list-descriptors/src/art/Test1946.java new file mode 100644 index 0000000000..9636957d6d --- /dev/null +++ b/test/1946-list-descriptors/src/art/Test1946.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 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. + */ + +package art; + +public class Test1946 { + public static void run() { + System.out.println("Failed! This should not be run. The test is in src-art/art/Test1946.java"); + } +} diff --git a/test/Android.bp b/test/Android.bp index 902f4edf8f..5558cd481b 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -267,6 +267,7 @@ art_cc_defaults { "1941-dispose-stress/dispose_stress.cc", "1942-suspend-raw-monitor-exit/native_suspend_monitor.cc", "1943-suspend-raw-monitor-wait/native_suspend_monitor.cc", + "1946-list-descriptors/descriptors.cc", ], header_libs: [ "jni_headers", |