1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <jni.h> 18 #include <jvmti.h> 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <type_traits> 23 24 #include <iostream> 25 #include <sstream> 26 27 namespace android { 28 namespace signature { 29 namespace cts { 30 namespace api { 31 32 static jvmtiEnv* jvmti_env; 33 static jvmtiError (*get_descriptor_list)(jvmtiEnv* env, jobject loader, jint* cnt, char*** descs); 34 35 template <typename T> 36 static void Dealloc(T* t) { 37 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t)); 38 } 39 40 template <typename T, typename ...Rest> 41 static void Dealloc(T* t, Rest... rs) { 42 Dealloc(t); 43 Dealloc(rs...); 44 } 45 46 static void DeallocParams(jvmtiParamInfo* params, jint n_params) { 47 for (jint i = 0; i < n_params; i++) { 48 Dealloc(params[i].name); 49 } 50 } 51 52 static void Cleanup(char** data, jint cnt) { 53 for (jint i = 0; i < cnt; i++) { 54 Dealloc(data[i]); 55 } 56 Dealloc(data); 57 } 58 59 template<typename T> 60 class ScopedJvmtiReference { 61 static_assert(std::is_pointer<T>::value, "T must be a pointer type"); 62 63 public: 64 ScopedJvmtiReference() : ref_(nullptr) {} 65 66 ~ScopedJvmtiReference() { 67 if (ref_ != nullptr) { 68 Dealloc(ref_); 69 } 70 } 71 72 // Return the pointer value. 73 T Get() { return ref_; }; 74 75 // Return a pointer to the pointer value. 76 T* GetPtr() { return &ref_; }; 77 78 private: 79 T ref_; 80 }; 81 82 static void abortIfExceptionPending(JNIEnv* env) { 83 if (env->ExceptionCheck()) { 84 abort(); 85 } 86 } 87 88 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, 89 __attribute__((unused)) char* options, 90 __attribute__((unused)) void* reserved) { 91 jint jvmError = vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_2); 92 if (jvmError != JNI_OK) { 93 return jvmError; 94 } 95 return JVMTI_ERROR_NONE; 96 } 97 98 extern "C" JNIEXPORT jobjectArray JNICALL Java_android_signature_cts_api_BootClassPathClassesProvider_getClassloaderDescriptors( 99 JNIEnv* env, jclass, jobject loader) { 100 if (get_descriptor_list == nullptr) { 101 jclass rt_exception = env->FindClass("java/lang/RuntimeException"); 102 env->ThrowNew(rt_exception, "get_class_loader_class_descriptor extension is not ready."); 103 return nullptr; 104 } 105 char** classes = nullptr; 106 jint cnt = -1; 107 jvmtiError error = get_descriptor_list(jvmti_env, loader, &cnt, &classes); 108 if (error != JVMTI_ERROR_NONE) { 109 jclass rt_exception = env->FindClass("java/lang/RuntimeException"); 110 env->ThrowNew(rt_exception, "Error while executing get_class_loader_class_descriptor."); 111 return nullptr; 112 } 113 114 jobjectArray arr = env->NewObjectArray(cnt, env->FindClass("java/lang/String"), nullptr); 115 if (env->ExceptionCheck()) { 116 Cleanup(classes, cnt); 117 return nullptr; 118 } 119 120 for (jint i = 0; i < cnt; i++) { 121 env->SetObjectArrayElement(arr, i, env->NewStringUTF(classes[i])); 122 if (env->ExceptionCheck()) { 123 Cleanup(classes, cnt); 124 return nullptr; 125 } 126 } 127 Cleanup(classes, cnt); 128 return arr; 129 } 130 131 extern "C" JNIEXPORT void JNICALL Java_android_signature_cts_api_BootClassPathClassesProvider_initialize(JNIEnv* env, jclass) { 132 jint functionInfosCount = 0; 133 jvmtiExtensionFunctionInfo* functionInfos = nullptr; 134 135 jvmtiError err = jvmti_env->GetExtensionFunctions(&functionInfosCount, &functionInfos); 136 if (err != JVMTI_ERROR_NONE) { 137 jclass rt_exception = env->FindClass("java/lang/RuntimeException"); 138 env->ThrowNew(rt_exception, "Failed to get JVMTI extension APIs"); 139 return; 140 } 141 142 for (jint i = 0; i < functionInfosCount; i++) { 143 jvmtiExtensionFunctionInfo* curInfo = &functionInfos[i]; 144 if (strcmp("com.android.art.class.get_class_loader_class_descriptors", curInfo->id) == 0) { 145 get_descriptor_list = reinterpret_cast<jvmtiError (*)(jvmtiEnv*, jobject, jint*, char***)>(curInfo->func); 146 } 147 DeallocParams(curInfo->params, curInfo->param_count); 148 Dealloc(curInfo->id, curInfo->short_description, curInfo->params, curInfo->errors); 149 } 150 Dealloc(functionInfos); 151 152 if (get_descriptor_list == nullptr) { 153 jclass rt_exception = env->FindClass("java/lang/RuntimeException"); 154 env->ThrowNew(rt_exception, "Failed to find get_class_loader_class_descriptors extension"); 155 return; 156 } 157 } 158 159 extern "C" JNIEXPORT jobjectArray JNICALL 160 Java_android_signature_cts_api_BootClassPathClassesProvider_getClassMemberNamesAndTypes( 161 JNIEnv* env, jclass, jclass klass, jboolean getFields) { 162 jvmtiError error; 163 164 jint count; 165 ScopedJvmtiReference<jfieldID*> fids; 166 ScopedJvmtiReference<jmethodID*> mids; 167 168 // Request a list of field/method IDs using JVMTI. 169 error = (getFields != JNI_FALSE) ? jvmti_env->GetClassFields(klass, &count, fids.GetPtr()) 170 : jvmti_env->GetClassMethods(klass, &count, mids.GetPtr()); 171 if (error != JVMTI_ERROR_NONE) { 172 std::stringstream ss; 173 ss << "Error while executing " 174 << ((getFields != JNI_FALSE) ? "GetClassFields" : "GetClassMethods") 175 << ", error code: " << static_cast<unsigned>(error); 176 std::string error = ss.str(); 177 jclass rt_exception = env->FindClass("java/lang/RuntimeException"); 178 env->ThrowNew(rt_exception, error.c_str()); 179 return nullptr; 180 } 181 182 jobjectArray names = env->NewObjectArray(count, env->FindClass("java/lang/String"), nullptr); 183 abortIfExceptionPending(env); 184 jobjectArray types = env->NewObjectArray(count, env->FindClass("java/lang/String"), nullptr); 185 abortIfExceptionPending(env); 186 187 // Convert IDs to names and types using JVMTI. 188 for (jint i = 0; i < count; ++i) { 189 ScopedJvmtiReference<char*> name; 190 ScopedJvmtiReference<char*> type; 191 192 error = (getFields != JNI_FALSE) 193 ? jvmti_env->GetFieldName(klass, fids.Get()[i], name.GetPtr(), type.GetPtr(), nullptr) 194 : jvmti_env->GetMethodName(mids.Get()[i], name.GetPtr(), type.GetPtr(), nullptr); 195 if (error != JVMTI_ERROR_NONE) { 196 std::stringstream ss; 197 ss << "Error while executing " 198 << ((getFields != JNI_FALSE) ? "GetFieldName" : "GetMethodName") 199 << ", error code: " << static_cast<unsigned>(error); 200 std::string error = ss.str(); 201 jclass rt_exception = env->FindClass("java/lang/RuntimeException"); 202 env->ThrowNew(rt_exception, error.c_str()); 203 return nullptr; 204 } 205 206 env->SetObjectArrayElement(names, i, env->NewStringUTF(name.Get())); 207 abortIfExceptionPending(env); 208 env->SetObjectArrayElement(types, i, env->NewStringUTF(type.Get())); 209 abortIfExceptionPending(env); 210 } 211 212 // Return as a array size 2 x count, where result[0] is an array of names and 213 // result[1] is an array of types. 214 jobjectArray result = env->NewObjectArray( 215 /* count */ 2, env->FindClass("[Ljava/lang/String;"), nullptr); 216 abortIfExceptionPending(env); 217 env->SetObjectArrayElement(result, 0, names); 218 abortIfExceptionPending(env); 219 env->SetObjectArrayElement(result, 1, types); 220 abortIfExceptionPending(env); 221 222 return result; 223 } 224 225 } // namespace api 226 } // namespace cts 227 } // namespace signature 228 } // namespace android 229