Home | History | Annotate | Download | only in ti-agent
      1 /*
      2  * Copyright (C) 2017 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 "jni.h"
     20 #include "jvmti.h"
     21 
     22 #include "jvmti_helper.h"
     23 #include "scoped_local_ref.h"
     24 #include "test_env.h"
     25 
     26 namespace art {
     27 
     28 namespace common_trace {
     29 
     30 struct TraceData {
     31   jclass test_klass;
     32   jmethodID enter_method;
     33   jmethodID exit_method;
     34   jmethodID field_access;
     35   jmethodID field_modify;
     36   jmethodID single_step;
     37   bool in_callback;
     38   bool access_watch_on_load;
     39   bool modify_watch_on_load;
     40 };
     41 
     42 static void singleStepCB(jvmtiEnv* jvmti,
     43                          JNIEnv* jnienv,
     44                          jthread thread,
     45                          jmethodID method,
     46                          jlocation location) {
     47   TraceData* data = nullptr;
     48   if (JvmtiErrorToException(jnienv, jvmti,
     49                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
     50     return;
     51   }
     52   if (data->in_callback) {
     53     return;
     54   }
     55   CHECK(data->single_step != nullptr);
     56   data->in_callback = true;
     57   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
     58   jnienv->CallStaticVoidMethod(data->test_klass,
     59                                data->single_step,
     60                                thread,
     61                                method_arg,
     62                                static_cast<jlong>(location));
     63   jnienv->DeleteLocalRef(method_arg);
     64   data->in_callback = false;
     65 }
     66 
     67 static void fieldAccessCB(jvmtiEnv* jvmti,
     68                           JNIEnv* jnienv,
     69                           jthread thr ATTRIBUTE_UNUSED,
     70                           jmethodID method,
     71                           jlocation location,
     72                           jclass field_klass,
     73                           jobject object,
     74                           jfieldID field) {
     75   TraceData* data = nullptr;
     76   if (JvmtiErrorToException(jnienv, jvmti,
     77                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
     78     return;
     79   }
     80   if (data->in_callback) {
     81     // Don't do callback for either of these to prevent an infinite loop.
     82     return;
     83   }
     84   CHECK(data->field_access != nullptr);
     85   data->in_callback = true;
     86   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
     87   jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
     88   jnienv->CallStaticVoidMethod(data->test_klass,
     89                                data->field_access,
     90                                method_arg,
     91                                static_cast<jlong>(location),
     92                                field_klass,
     93                                object,
     94                                field_arg);
     95   jnienv->DeleteLocalRef(method_arg);
     96   jnienv->DeleteLocalRef(field_arg);
     97   data->in_callback = false;
     98 }
     99 
    100 static void fieldModificationCB(jvmtiEnv* jvmti,
    101                                 JNIEnv* jnienv,
    102                                 jthread thr ATTRIBUTE_UNUSED,
    103                                 jmethodID method,
    104                                 jlocation location,
    105                                 jclass field_klass,
    106                                 jobject object,
    107                                 jfieldID field,
    108                                 char type_char,
    109                                 jvalue new_value) {
    110   TraceData* data = nullptr;
    111   if (JvmtiErrorToException(jnienv, jvmti,
    112                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    113     return;
    114   }
    115   if (data->in_callback) {
    116     // Don't do callback recursively to prevent an infinite loop.
    117     return;
    118   }
    119   CHECK(data->field_modify != nullptr);
    120   data->in_callback = true;
    121   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    122   jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
    123   jobject value = GetJavaValueByType(jnienv, type_char, new_value);
    124   if (jnienv->ExceptionCheck()) {
    125     data->in_callback = false;
    126     jnienv->DeleteLocalRef(method_arg);
    127     jnienv->DeleteLocalRef(field_arg);
    128     return;
    129   }
    130   jnienv->CallStaticVoidMethod(data->test_klass,
    131                                data->field_modify,
    132                                method_arg,
    133                                static_cast<jlong>(location),
    134                                field_klass,
    135                                object,
    136                                field_arg,
    137                                value);
    138   jnienv->DeleteLocalRef(method_arg);
    139   jnienv->DeleteLocalRef(field_arg);
    140   data->in_callback = false;
    141 }
    142 
    143 static void methodExitCB(jvmtiEnv* jvmti,
    144                          JNIEnv* jnienv,
    145                          jthread thr ATTRIBUTE_UNUSED,
    146                          jmethodID method,
    147                          jboolean was_popped_by_exception,
    148                          jvalue return_value) {
    149   TraceData* data = nullptr;
    150   if (JvmtiErrorToException(jnienv, jvmti,
    151                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    152     return;
    153   }
    154   if (method == data->exit_method || method == data->enter_method || data->in_callback) {
    155     // Don't do callback for either of these to prevent an infinite loop.
    156     return;
    157   }
    158   CHECK(data->exit_method != nullptr);
    159   data->in_callback = true;
    160   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    161   jobject result =
    162       was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value);
    163   if (jnienv->ExceptionCheck()) {
    164     data->in_callback = false;
    165     return;
    166   }
    167   jnienv->CallStaticVoidMethod(data->test_klass,
    168                                data->exit_method,
    169                                method_arg,
    170                                was_popped_by_exception,
    171                                result);
    172   jnienv->DeleteLocalRef(method_arg);
    173   data->in_callback = false;
    174 }
    175 
    176 static void methodEntryCB(jvmtiEnv* jvmti,
    177                           JNIEnv* jnienv,
    178                           jthread thr ATTRIBUTE_UNUSED,
    179                           jmethodID method) {
    180   TraceData* data = nullptr;
    181   if (JvmtiErrorToException(jnienv, jvmti,
    182                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    183     return;
    184   }
    185   CHECK(data->enter_method != nullptr);
    186   if (method == data->exit_method || method == data->enter_method || data->in_callback) {
    187     // Don't do callback for either of these to prevent an infinite loop.
    188     return;
    189   }
    190   data->in_callback = true;
    191   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    192   if (jnienv->ExceptionCheck()) {
    193     return;
    194   }
    195   jnienv->CallStaticVoidMethod(data->test_klass, data->enter_method, method_arg);
    196   jnienv->DeleteLocalRef(method_arg);
    197   data->in_callback = false;
    198 }
    199 
    200 static void classPrepareCB(jvmtiEnv* jvmti,
    201                            JNIEnv* jnienv,
    202                            jthread thr ATTRIBUTE_UNUSED,
    203                            jclass klass) {
    204   TraceData* data = nullptr;
    205   if (JvmtiErrorToException(jnienv, jvmti,
    206                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    207     return;
    208   }
    209   if (data->access_watch_on_load || data->modify_watch_on_load) {
    210     jint nfields;
    211     jfieldID* fields;
    212     if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) {
    213       return;
    214     }
    215     for (jint i = 0; i < nfields; i++) {
    216       jfieldID f = fields[i];
    217       // Ignore errors
    218       if (data->access_watch_on_load) {
    219         jvmti->SetFieldAccessWatch(klass, f);
    220       }
    221 
    222       if (data->modify_watch_on_load) {
    223         jvmti->SetFieldModificationWatch(klass, f);
    224       }
    225     }
    226     jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
    227   }
    228 }
    229 
    230 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) {
    231   TraceData* data = nullptr;
    232   if (JvmtiErrorToException(
    233       env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    234     return;
    235   }
    236   data->access_watch_on_load = true;
    237   // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
    238   if (JvmtiErrorToException(env,
    239                             jvmti_env,
    240                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    241                                                                 JVMTI_EVENT_CLASS_PREPARE,
    242                                                                 nullptr))) {
    243     return;
    244   }
    245   jint nklasses;
    246   jclass* klasses;
    247   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
    248     return;
    249   }
    250   for (jint i = 0; i < nklasses; i++) {
    251     jclass k = klasses[i];
    252 
    253     jint nfields;
    254     jfieldID* fields;
    255     jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
    256     if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
    257       continue;
    258     } else if (JvmtiErrorToException(env, jvmti_env, err)) {
    259       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    260       return;
    261     }
    262     for (jint j = 0; j < nfields; j++) {
    263       jvmti_env->SetFieldAccessWatch(k, fields[j]);
    264     }
    265     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
    266   }
    267   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    268 }
    269 
    270 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) {
    271   TraceData* data = nullptr;
    272   if (JvmtiErrorToException(
    273       env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    274     return;
    275   }
    276   data->modify_watch_on_load = true;
    277   // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
    278   if (JvmtiErrorToException(env,
    279                             jvmti_env,
    280                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    281                                                                 JVMTI_EVENT_CLASS_PREPARE,
    282                                                                 nullptr))) {
    283     return;
    284   }
    285   jint nklasses;
    286   jclass* klasses;
    287   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
    288     return;
    289   }
    290   for (jint i = 0; i < nklasses; i++) {
    291     jclass k = klasses[i];
    292 
    293     jint nfields;
    294     jfieldID* fields;
    295     jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
    296     if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
    297       continue;
    298     } else if (JvmtiErrorToException(env, jvmti_env, err)) {
    299       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    300       return;
    301     }
    302     for (jint j = 0; j < nfields; j++) {
    303       jvmti_env->SetFieldModificationWatch(k, fields[j]);
    304     }
    305     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
    306   }
    307   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    308 }
    309 
    310 static bool GetFieldAndClass(JNIEnv* env,
    311                              jobject ref_field,
    312                              jclass* out_klass,
    313                              jfieldID* out_field) {
    314   *out_field = env->FromReflectedField(ref_field);
    315   if (env->ExceptionCheck()) {
    316     return false;
    317   }
    318   jclass field_klass = env->FindClass("java/lang/reflect/Field");
    319   if (env->ExceptionCheck()) {
    320     return false;
    321   }
    322   jmethodID get_declaring_class_method =
    323       env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;");
    324   if (env->ExceptionCheck()) {
    325     env->DeleteLocalRef(field_klass);
    326     return false;
    327   }
    328   *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method));
    329   if (env->ExceptionCheck()) {
    330     *out_klass = nullptr;
    331     env->DeleteLocalRef(field_klass);
    332     return false;
    333   }
    334   env->DeleteLocalRef(field_klass);
    335   return true;
    336 }
    337 
    338 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
    339     JNIEnv* env,
    340     jclass trace ATTRIBUTE_UNUSED,
    341     jobject field_obj) {
    342   jfieldID field;
    343   jclass klass;
    344   if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
    345     return;
    346   }
    347 
    348   JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field));
    349   env->DeleteLocalRef(klass);
    350 }
    351 
    352 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
    353     JNIEnv* env,
    354     jclass trace ATTRIBUTE_UNUSED,
    355     jobject field_obj) {
    356   jfieldID field;
    357   jclass klass;
    358   if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
    359     return;
    360   }
    361   JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field));
    362   env->DeleteLocalRef(klass);
    363 }
    364 
    365 extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
    366     JNIEnv* env,
    367     jclass trace ATTRIBUTE_UNUSED,
    368     jclass klass,
    369     jobject enter,
    370     jobject exit,
    371     jobject field_access,
    372     jobject field_modify,
    373     jobject single_step,
    374     jthread thr) {
    375   TraceData* data = nullptr;
    376   if (JvmtiErrorToException(env,
    377                             jvmti_env,
    378                             jvmti_env->Allocate(sizeof(TraceData),
    379                                                 reinterpret_cast<unsigned char**>(&data)))) {
    380     return;
    381   }
    382   memset(data, 0, sizeof(TraceData));
    383   data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
    384   data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
    385   data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
    386   data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
    387   data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
    388   data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
    389   data->in_callback = false;
    390 
    391   void* old_data = nullptr;
    392   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
    393     return;
    394   } else if (old_data != nullptr) {
    395     ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
    396     env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
    397     return;
    398   }
    399   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
    400     return;
    401   }
    402 
    403   jvmtiEventCallbacks cb;
    404   memset(&cb, 0, sizeof(cb));
    405   cb.MethodEntry = methodEntryCB;
    406   cb.MethodExit = methodExitCB;
    407   cb.FieldAccess = fieldAccessCB;
    408   cb.FieldModification = fieldModificationCB;
    409   cb.ClassPrepare = classPrepareCB;
    410   cb.SingleStep = singleStepCB;
    411   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
    412     return;
    413   }
    414   if (enter != nullptr &&
    415       JvmtiErrorToException(env,
    416                             jvmti_env,
    417                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    418                                                                 JVMTI_EVENT_METHOD_ENTRY,
    419                                                                 thr))) {
    420     return;
    421   }
    422   if (exit != nullptr &&
    423       JvmtiErrorToException(env,
    424                             jvmti_env,
    425                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    426                                                                 JVMTI_EVENT_METHOD_EXIT,
    427                                                                 thr))) {
    428     return;
    429   }
    430   if (field_access != nullptr &&
    431       JvmtiErrorToException(env,
    432                             jvmti_env,
    433                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    434                                                                 JVMTI_EVENT_FIELD_ACCESS,
    435                                                                 thr))) {
    436     return;
    437   }
    438   if (field_modify != nullptr &&
    439       JvmtiErrorToException(env,
    440                             jvmti_env,
    441                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    442                                                                 JVMTI_EVENT_FIELD_MODIFICATION,
    443                                                                 thr))) {
    444     return;
    445   }
    446   if (single_step != nullptr &&
    447       JvmtiErrorToException(env,
    448                             jvmti_env,
    449                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    450                                                                 JVMTI_EVENT_SINGLE_STEP,
    451                                                                 thr))) {
    452     return;
    453   }
    454 }
    455 
    456 extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
    457     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
    458   if (JvmtiErrorToException(env, jvmti_env,
    459                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    460                                                                 JVMTI_EVENT_FIELD_ACCESS,
    461                                                                 thr))) {
    462     return;
    463   }
    464   if (JvmtiErrorToException(env, jvmti_env,
    465                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    466                                                                 JVMTI_EVENT_FIELD_MODIFICATION,
    467                                                                 thr))) {
    468     return;
    469   }
    470   if (JvmtiErrorToException(env, jvmti_env,
    471                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    472                                                                 JVMTI_EVENT_METHOD_ENTRY,
    473                                                                 thr))) {
    474     return;
    475   }
    476   if (JvmtiErrorToException(env, jvmti_env,
    477                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    478                                                                 JVMTI_EVENT_METHOD_EXIT,
    479                                                                 thr))) {
    480     return;
    481   }
    482   if (JvmtiErrorToException(env, jvmti_env,
    483                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    484                                                                 JVMTI_EVENT_SINGLE_STEP,
    485                                                                 thr))) {
    486     return;
    487   }
    488 }
    489 
    490 }  // namespace common_trace
    491 
    492 
    493 }  // namespace art
    494