Home | History | Annotate | Download | only in jni
      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