Home | History | Annotate | Download | only in ti-agent
      1 /*
      2  * Copyright (C) 2016 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 "common_helper.h"
     18 
     19 #include <cstdio>
     20 #include <deque>
     21 #include <map>
     22 #include <sstream>
     23 #include <string>
     24 #include <vector>
     25 
     26 #include "jni.h"
     27 #include "jvmti.h"
     28 
     29 #include "jvmti_helper.h"
     30 #include "test_env.h"
     31 
     32 namespace art {
     33 
     34 static void SetupCommonRedefine();
     35 static void SetupCommonRetransform();
     36 static void SetupCommonTransform();
     37 template <bool is_redefine>
     38 static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
     39                                          JNIEnv* env,
     40                                          jint num_targets,
     41                                          jclass* target,
     42                                          jvmtiError res) {
     43   std::stringstream err;
     44   char* error = nullptr;
     45   jvmti->GetErrorName(res, &error);
     46   err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
     47   if (num_targets > 1) {
     48     err << "es";
     49   }
     50   err << " <";
     51   for (jint i = 0; i < num_targets; i++) {
     52     char* signature = nullptr;
     53     char* generic = nullptr;
     54     jvmti->GetClassSignature(target[i], &signature, &generic);
     55     if (i != 0) {
     56       err << ", ";
     57     }
     58     err << signature;
     59     jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
     60     jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
     61   }
     62   err << "> due to " << error;
     63   std::string message = err.str();
     64   jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
     65   env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
     66 }
     67 
     68 #define CONFIGURATION_COMMON_REDEFINE 0
     69 #define CONFIGURATION_COMMON_RETRANSFORM 1
     70 #define CONFIGURATION_COMMON_TRANSFORM 2
     71 
     72 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
     73                                                                                    jclass,
     74                                                                                    jint type) {
     75   switch (type) {
     76     case CONFIGURATION_COMMON_REDEFINE: {
     77       SetupCommonRedefine();
     78       return;
     79     }
     80     case CONFIGURATION_COMMON_RETRANSFORM: {
     81       SetupCommonRetransform();
     82       return;
     83     }
     84     case CONFIGURATION_COMMON_TRANSFORM: {
     85       SetupCommonTransform();
     86       return;
     87     }
     88     default: {
     89       LOG(FATAL) << "Unknown test configuration: " << type;
     90     }
     91   }
     92 }
     93 
     94 namespace common_redefine {
     95 
     96 static void throwRedefinitionError(jvmtiEnv* jvmti,
     97                                    JNIEnv* env,
     98                                    jint num_targets,
     99                                    jclass* target,
    100                                    jvmtiError res) {
    101   return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
    102 }
    103 
    104 static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
    105                                  JNIEnv* env,
    106                                  jint num_redefines,
    107                                  jclass* targets,
    108                                  jbyteArray* class_file_bytes,
    109                                  jbyteArray* dex_file_bytes) {
    110   std::vector<jvmtiClassDefinition> defs;
    111   for (jint i = 0; i < num_redefines; i++) {
    112     jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
    113     jint len = static_cast<jint>(env->GetArrayLength(desired_array));
    114     const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
    115         env->GetByteArrayElements(desired_array, nullptr));
    116     defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
    117   }
    118   jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
    119   if (res != JVMTI_ERROR_NONE) {
    120     throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
    121   }
    122 }
    123 
    124 static void DoClassRedefine(jvmtiEnv* jvmti_env,
    125                             JNIEnv* env,
    126                             jclass target,
    127                             jbyteArray class_file_bytes,
    128                             jbyteArray dex_file_bytes) {
    129   return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
    130 }
    131 
    132 // Magic JNI export that classes can use for redefining classes.
    133 // To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
    134 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
    135     JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
    136   DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
    137 }
    138 
    139 // Magic JNI export that classes can use for redefining classes.
    140 // To use classes should declare this as a native function with signature
    141 // ([Ljava/lang/Class;[[B[[B)V
    142 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefinition(
    143     JNIEnv* env,
    144     jclass,
    145     jobjectArray targets,
    146     jobjectArray class_file_bytes,
    147     jobjectArray dex_file_bytes) {
    148   std::vector<jclass> classes;
    149   std::vector<jbyteArray> class_files;
    150   std::vector<jbyteArray> dex_files;
    151   jint len = env->GetArrayLength(targets);
    152   if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
    153     env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
    154                   "the three array arguments passed to this function have different lengths!");
    155     return;
    156   }
    157   for (jint i = 0; i < len; i++) {
    158     classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
    159     dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
    160     class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
    161   }
    162   return DoMultiClassRedefine(jvmti_env,
    163                               env,
    164                               len,
    165                               classes.data(),
    166                               class_files.data(),
    167                               dex_files.data());
    168 }
    169 
    170 // Get all capabilities except those related to retransformation.
    171 jint OnLoad(JavaVM* vm,
    172             char* options ATTRIBUTE_UNUSED,
    173             void* reserved ATTRIBUTE_UNUSED) {
    174   if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
    175     printf("Unable to get jvmti env!\n");
    176     return 1;
    177   }
    178   SetupCommonRedefine();
    179   return 0;
    180 }
    181 
    182 }  // namespace common_redefine
    183 
    184 namespace common_retransform {
    185 
    186 struct CommonTransformationResult {
    187   std::vector<unsigned char> class_bytes;
    188   std::vector<unsigned char> dex_bytes;
    189 
    190   CommonTransformationResult(size_t class_size, size_t dex_size)
    191       : class_bytes(class_size), dex_bytes(dex_size) {}
    192 
    193   CommonTransformationResult() = default;
    194   CommonTransformationResult(CommonTransformationResult&&) = default;
    195   CommonTransformationResult(CommonTransformationResult&) = default;
    196 };
    197 
    198 // Map from class name to transformation result.
    199 std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
    200 bool gPopTransformations = true;
    201 
    202 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_addCommonTransformationResult(
    203     JNIEnv* env, jclass, jstring class_name, jbyteArray class_array, jbyteArray dex_array) {
    204   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
    205   std::string name_str(name_chrs);
    206   env->ReleaseStringUTFChars(class_name, name_chrs);
    207   CommonTransformationResult trans(env->GetArrayLength(class_array),
    208                                    env->GetArrayLength(dex_array));
    209   if (env->ExceptionOccurred()) {
    210     return;
    211   }
    212   env->GetByteArrayRegion(class_array,
    213                           0,
    214                           env->GetArrayLength(class_array),
    215                           reinterpret_cast<jbyte*>(trans.class_bytes.data()));
    216   if (env->ExceptionOccurred()) {
    217     return;
    218   }
    219   env->GetByteArrayRegion(dex_array,
    220                           0,
    221                           env->GetArrayLength(dex_array),
    222                           reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
    223   if (env->ExceptionOccurred()) {
    224     return;
    225   }
    226   if (gTransformations.find(name_str) == gTransformations.end()) {
    227     std::deque<CommonTransformationResult> list;
    228     gTransformations[name_str] = std::move(list);
    229   }
    230   gTransformations[name_str].push_back(std::move(trans));
    231 }
    232 
    233 // The hook we are using.
    234 void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
    235                                                     JNIEnv* jni_env ATTRIBUTE_UNUSED,
    236                                                     jclass class_being_redefined ATTRIBUTE_UNUSED,
    237                                                     jobject loader ATTRIBUTE_UNUSED,
    238                                                     const char* name,
    239                                                     jobject protection_domain ATTRIBUTE_UNUSED,
    240                                                     jint class_data_len ATTRIBUTE_UNUSED,
    241                                                     const unsigned char* class_dat ATTRIBUTE_UNUSED,
    242                                                     jint* new_class_data_len,
    243                                                     unsigned char** new_class_data) {
    244   std::string name_str(name);
    245   if (gTransformations.find(name_str) != gTransformations.end() &&
    246       gTransformations[name_str].size() > 0) {
    247     CommonTransformationResult& res = gTransformations[name_str][0];
    248     const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
    249     unsigned char* new_data;
    250     CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
    251     memcpy(new_data, desired_array.data(), desired_array.size());
    252     *new_class_data = new_data;
    253     *new_class_data_len = desired_array.size();
    254     if (gPopTransformations) {
    255       gTransformations[name_str].pop_front();
    256     }
    257   }
    258 }
    259 
    260 extern "C" JNIEXPORT void Java_art_Redefinition_setPopRetransformations(JNIEnv*,
    261                                                                         jclass,
    262                                                                         jboolean enable) {
    263   gPopTransformations = enable;
    264 }
    265 
    266 extern "C" JNIEXPORT void Java_art_Redefinition_popTransformationFor(JNIEnv* env,
    267                                                                          jclass,
    268                                                                          jstring class_name) {
    269   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
    270   std::string name_str(name_chrs);
    271   env->ReleaseStringUTFChars(class_name, name_chrs);
    272   if (gTransformations.find(name_str) != gTransformations.end() &&
    273       gTransformations[name_str].size() > 0) {
    274     gTransformations[name_str].pop_front();
    275   } else {
    276     std::stringstream err;
    277     err << "No transformations found for class " << name_str;
    278     std::string message = err.str();
    279     env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
    280   }
    281 }
    282 
    283 extern "C" JNIEXPORT void Java_art_Redefinition_enableCommonRetransformation(JNIEnv* env,
    284                                                                                  jclass,
    285                                                                                  jboolean enable) {
    286   jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
    287                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
    288                                                        nullptr);
    289   if (res != JVMTI_ERROR_NONE) {
    290     JvmtiErrorToException(env, jvmti_env, res);
    291   }
    292 }
    293 
    294 static void throwRetransformationError(jvmtiEnv* jvmti,
    295                                        JNIEnv* env,
    296                                        jint num_targets,
    297                                        jclass* targets,
    298                                        jvmtiError res) {
    299   return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
    300 }
    301 
    302 static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
    303   std::vector<jclass> classes;
    304   jint len = env->GetArrayLength(targets);
    305   for (jint i = 0; i < len; i++) {
    306     classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
    307   }
    308   jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
    309   if (res != JVMTI_ERROR_NONE) {
    310     throwRetransformationError(jvmti_env, env, len, classes.data(), res);
    311   }
    312 }
    313 
    314 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransformation(
    315     JNIEnv* env, jclass, jobjectArray targets) {
    316   jvmtiCapabilities caps;
    317   jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
    318   if (caps_err != JVMTI_ERROR_NONE) {
    319     env->ThrowNew(env->FindClass("java/lang/Exception"),
    320                   "Unable to get current jvmtiEnv capabilities");
    321     return;
    322   }
    323 
    324   // Allocate a new environment if we don't have the can_retransform_classes capability needed to
    325   // call the RetransformClasses function.
    326   jvmtiEnv* real_env = nullptr;
    327   if (caps.can_retransform_classes != 1) {
    328     JavaVM* vm = nullptr;
    329     if (env->GetJavaVM(&vm) != 0 ||
    330         vm->GetEnv(reinterpret_cast<void**>(&real_env), JVMTI_VERSION_1_0) != 0) {
    331       env->ThrowNew(env->FindClass("java/lang/Exception"),
    332                     "Unable to create temporary jvmtiEnv for RetransformClasses call.");
    333       return;
    334     }
    335     SetStandardCapabilities(real_env);
    336   } else {
    337     real_env = jvmti_env;
    338   }
    339   DoClassRetransformation(real_env, env, targets);
    340   if (caps.can_retransform_classes != 1) {
    341     real_env->DisposeEnvironment();
    342   }
    343 }
    344 
    345 // Get all capabilities except those related to retransformation.
    346 jint OnLoad(JavaVM* vm,
    347             char* options ATTRIBUTE_UNUSED,
    348             void* reserved ATTRIBUTE_UNUSED) {
    349   if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
    350     printf("Unable to get jvmti env!\n");
    351     return 1;
    352   }
    353   SetupCommonRetransform();
    354   return 0;
    355 }
    356 
    357 }  // namespace common_retransform
    358 
    359 namespace common_transform {
    360 
    361 // Get all capabilities except those related to retransformation.
    362 jint OnLoad(JavaVM* vm,
    363             char* options ATTRIBUTE_UNUSED,
    364             void* reserved ATTRIBUTE_UNUSED) {
    365   if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
    366     printf("Unable to get jvmti env!\n");
    367     return 1;
    368   }
    369   SetupCommonTransform();
    370   return 0;
    371 }
    372 
    373 }  // namespace common_transform
    374 
    375 static void SetupCommonRedefine() {
    376   jvmtiCapabilities caps = GetStandardCapabilities();
    377   caps.can_retransform_classes = 0;
    378   caps.can_retransform_any_class = 0;
    379   jvmti_env->AddCapabilities(&caps);
    380 }
    381 
    382 static void SetupCommonRetransform() {
    383   SetStandardCapabilities(jvmti_env);
    384   current_callbacks.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
    385   jvmtiError res = jvmti_env->SetEventCallbacks(&current_callbacks, sizeof(current_callbacks));
    386   CHECK_EQ(res, JVMTI_ERROR_NONE);
    387   common_retransform::gTransformations.clear();
    388 }
    389 
    390 static void SetupCommonTransform() {
    391   // Don't set the retransform caps
    392   jvmtiCapabilities caps = GetStandardCapabilities();
    393   caps.can_retransform_classes = 0;
    394   caps.can_retransform_any_class = 0;
    395   jvmti_env->AddCapabilities(&caps);
    396 
    397   // Use the same callback as the retransform test.
    398   current_callbacks.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
    399   jvmtiError res = jvmti_env->SetEventCallbacks(&current_callbacks, sizeof(current_callbacks));
    400   CHECK_EQ(res, JVMTI_ERROR_NONE);
    401   common_retransform::gTransformations.clear();
    402 }
    403 
    404 }  // namespace art
    405