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 static bool IsInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr) {
     31   void* data;
     32   ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred());
     33   env->ExceptionClear();
     34   jvmti->GetThreadLocalStorage(thr, &data);
     35   if (exc.get() != nullptr) {
     36     env->Throw(exc.get());
     37   }
     38   if (data == nullptr) {
     39     return false;
     40   } else {
     41     return true;
     42   }
     43 }
     44 
     45 static void SetInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr, bool val) {
     46   ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred());
     47   env->ExceptionClear();
     48   jvmti->SetThreadLocalStorage(thr, (val ? reinterpret_cast<void*>(0x1)
     49                                          : reinterpret_cast<void*>(0x0)));
     50   if (exc.get() != nullptr) {
     51     env->Throw(exc.get());
     52   }
     53 }
     54 
     55 class ScopedCallbackState {
     56  public:
     57   ScopedCallbackState(JNIEnv* jnienv, jvmtiEnv* env, jthread thr)
     58       : jnienv_(jnienv), env_(env), thr_(thr) {
     59     CHECK(!IsInCallback(jnienv_, env_, thr_));
     60     SetInCallback(jnienv_, env_, thr_, true);
     61   }
     62   ~ScopedCallbackState() {
     63     CHECK(IsInCallback(jnienv_, env_, thr_));
     64     SetInCallback(jnienv_, env_, thr_, false);
     65   }
     66 
     67  private:
     68   JNIEnv* jnienv_;
     69   jvmtiEnv* env_;
     70   jthread thr_;
     71 };
     72 
     73 struct TraceData {
     74   jclass test_klass;
     75   jmethodID enter_method;
     76   jmethodID exit_method;
     77   jmethodID field_access;
     78   jmethodID field_modify;
     79   jmethodID single_step;
     80   jmethodID thread_start;
     81   jmethodID thread_end;
     82   bool access_watch_on_load;
     83   bool modify_watch_on_load;
     84   jrawMonitorID trace_mon;
     85 
     86   jclass GetTestClass(jvmtiEnv* jvmti, JNIEnv* env) {
     87     if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(trace_mon))) {
     88       return nullptr;
     89     }
     90     jclass out = reinterpret_cast<jclass>(env->NewLocalRef(test_klass));
     91     if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(trace_mon))) {
     92       return nullptr;
     93     }
     94     return out;
     95   }
     96 };
     97 
     98 static void threadStartCB(jvmtiEnv* jvmti,
     99                           JNIEnv* jnienv,
    100                           jthread thread) {
    101   TraceData* data = nullptr;
    102   if (JvmtiErrorToException(jnienv, jvmti,
    103                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    104     return;
    105   }
    106   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    107   if (klass.get() == nullptr) {
    108     return;
    109   }
    110   CHECK(data->thread_start != nullptr);
    111   jnienv->CallStaticVoidMethod(klass.get(), data->thread_start, thread);
    112 }
    113 static void threadEndCB(jvmtiEnv* jvmti,
    114                           JNIEnv* jnienv,
    115                           jthread thread) {
    116   TraceData* data = nullptr;
    117   if (JvmtiErrorToException(jnienv, jvmti,
    118                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    119     return;
    120   }
    121   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    122   if (klass.get() == nullptr) {
    123     return;
    124   }
    125   CHECK(data->thread_end != nullptr);
    126   jnienv->CallStaticVoidMethod(klass.get(), data->thread_end, thread);
    127 }
    128 
    129 static void singleStepCB(jvmtiEnv* jvmti,
    130                          JNIEnv* jnienv,
    131                          jthread thread,
    132                          jmethodID method,
    133                          jlocation location) {
    134   TraceData* data = nullptr;
    135   if (JvmtiErrorToException(jnienv, jvmti,
    136                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    137     return;
    138   }
    139   if (IsInCallback(jnienv, jvmti, thread)) {
    140     return;
    141   }
    142   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    143   if (klass.get() == nullptr) {
    144     return;
    145   }
    146   CHECK(data->single_step != nullptr);
    147   ScopedCallbackState st(jnienv, jvmti, thread);
    148   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    149   jnienv->CallStaticVoidMethod(klass.get(),
    150                                data->single_step,
    151                                thread,
    152                                method_arg,
    153                                static_cast<jlong>(location));
    154   jnienv->DeleteLocalRef(method_arg);
    155 }
    156 
    157 static void fieldAccessCB(jvmtiEnv* jvmti,
    158                           JNIEnv* jnienv,
    159                           jthread thr,
    160                           jmethodID method,
    161                           jlocation location,
    162                           jclass field_klass,
    163                           jobject object,
    164                           jfieldID field) {
    165   TraceData* data = nullptr;
    166   if (JvmtiErrorToException(jnienv, jvmti,
    167                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    168     return;
    169   }
    170   if (IsInCallback(jnienv, jvmti, thr)) {
    171     // Don't do callback for either of these to prevent an infinite loop.
    172     return;
    173   }
    174   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    175   if (klass.get() == nullptr) {
    176     return;
    177   }
    178   CHECK(data->field_access != nullptr);
    179   ScopedCallbackState st(jnienv, jvmti, thr);
    180   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    181   jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
    182   jnienv->CallStaticVoidMethod(klass.get(),
    183                                data->field_access,
    184                                method_arg,
    185                                static_cast<jlong>(location),
    186                                field_klass,
    187                                object,
    188                                field_arg);
    189   jnienv->DeleteLocalRef(method_arg);
    190   jnienv->DeleteLocalRef(field_arg);
    191 }
    192 
    193 static void fieldModificationCB(jvmtiEnv* jvmti,
    194                                 JNIEnv* jnienv,
    195                                 jthread thr,
    196                                 jmethodID method,
    197                                 jlocation location,
    198                                 jclass field_klass,
    199                                 jobject object,
    200                                 jfieldID field,
    201                                 char type_char,
    202                                 jvalue new_value) {
    203   TraceData* data = nullptr;
    204   if (JvmtiErrorToException(jnienv, jvmti,
    205                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    206     return;
    207   }
    208   if (IsInCallback(jnienv, jvmti, thr)) {
    209     // Don't do callback recursively to prevent an infinite loop.
    210     return;
    211   }
    212   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    213   if (klass.get() == nullptr) {
    214     return;
    215   }
    216   CHECK(data->field_modify != nullptr);
    217   ScopedCallbackState st(jnienv, jvmti, thr);
    218   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    219   jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
    220   jobject value = GetJavaValueByType(jnienv, type_char, new_value);
    221   if (jnienv->ExceptionCheck()) {
    222     jnienv->DeleteLocalRef(method_arg);
    223     jnienv->DeleteLocalRef(field_arg);
    224     return;
    225   }
    226   jnienv->CallStaticVoidMethod(klass.get(),
    227                                data->field_modify,
    228                                method_arg,
    229                                static_cast<jlong>(location),
    230                                field_klass,
    231                                object,
    232                                field_arg,
    233                                value);
    234   jnienv->DeleteLocalRef(method_arg);
    235   jnienv->DeleteLocalRef(field_arg);
    236 }
    237 
    238 static void methodExitCB(jvmtiEnv* jvmti,
    239                          JNIEnv* jnienv,
    240                          jthread thr,
    241                          jmethodID method,
    242                          jboolean was_popped_by_exception,
    243                          jvalue return_value) {
    244   TraceData* data = nullptr;
    245   if (JvmtiErrorToException(jnienv, jvmti,
    246                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    247     return;
    248   }
    249   if (method == data->exit_method ||
    250       method == data->enter_method ||
    251       IsInCallback(jnienv, jvmti, thr)) {
    252     // Don't do callback for either of these to prevent an infinite loop.
    253     return;
    254   }
    255   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    256   if (klass.get() == nullptr) {
    257     return;
    258   }
    259   CHECK(data->exit_method != nullptr);
    260   ScopedCallbackState st(jnienv, jvmti, thr);
    261   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    262   jobject result =
    263       was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value);
    264   if (jnienv->ExceptionCheck()) {
    265     return;
    266   }
    267   jnienv->CallStaticVoidMethod(klass.get(),
    268                                data->exit_method,
    269                                method_arg,
    270                                was_popped_by_exception,
    271                                result);
    272   jnienv->DeleteLocalRef(method_arg);
    273 }
    274 
    275 static void methodEntryCB(jvmtiEnv* jvmti,
    276                           JNIEnv* jnienv,
    277                           jthread thr,
    278                           jmethodID method) {
    279   TraceData* data = nullptr;
    280   if (JvmtiErrorToException(jnienv, jvmti,
    281                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    282     return;
    283   }
    284   CHECK(data->enter_method != nullptr);
    285   if (method == data->exit_method ||
    286       method == data->enter_method ||
    287       IsInCallback(jnienv, jvmti, thr)) {
    288     // Don't do callback for either of these to prevent an infinite loop.
    289     return;
    290   }
    291   ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv));
    292   if (klass.get() == nullptr) {
    293     return;
    294   }
    295   ScopedCallbackState st(jnienv, jvmti, thr);
    296   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
    297   if (jnienv->ExceptionCheck()) {
    298     return;
    299   }
    300   jnienv->CallStaticVoidMethod(klass.get(), data->enter_method, method_arg);
    301   jnienv->DeleteLocalRef(method_arg);
    302 }
    303 
    304 static void classPrepareCB(jvmtiEnv* jvmti,
    305                            JNIEnv* jnienv,
    306                            jthread thr ATTRIBUTE_UNUSED,
    307                            jclass klass) {
    308   TraceData* data = nullptr;
    309   if (JvmtiErrorToException(jnienv, jvmti,
    310                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    311     return;
    312   }
    313   if (data->access_watch_on_load || data->modify_watch_on_load) {
    314     jint nfields;
    315     jfieldID* fields;
    316     if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) {
    317       return;
    318     }
    319     for (jint i = 0; i < nfields; i++) {
    320       jfieldID f = fields[i];
    321       // Ignore errors
    322       if (data->access_watch_on_load) {
    323         jvmti->SetFieldAccessWatch(klass, f);
    324       }
    325 
    326       if (data->modify_watch_on_load) {
    327         jvmti->SetFieldModificationWatch(klass, f);
    328       }
    329     }
    330     jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
    331   }
    332 }
    333 
    334 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) {
    335   TraceData* data = nullptr;
    336   if (JvmtiErrorToException(
    337       env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    338     return;
    339   }
    340   data->access_watch_on_load = true;
    341   // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
    342   if (JvmtiErrorToException(env,
    343                             jvmti_env,
    344                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    345                                                                 JVMTI_EVENT_CLASS_PREPARE,
    346                                                                 nullptr))) {
    347     return;
    348   }
    349   jint nklasses;
    350   jclass* klasses;
    351   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
    352     return;
    353   }
    354   for (jint i = 0; i < nklasses; i++) {
    355     jclass k = klasses[i];
    356 
    357     jint nfields;
    358     jfieldID* fields;
    359     jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
    360     if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
    361       continue;
    362     } else if (JvmtiErrorToException(env, jvmti_env, err)) {
    363       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    364       return;
    365     }
    366     for (jint j = 0; j < nfields; j++) {
    367       jvmti_env->SetFieldAccessWatch(k, fields[j]);
    368     }
    369     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
    370   }
    371   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    372 }
    373 
    374 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) {
    375   TraceData* data = nullptr;
    376   if (JvmtiErrorToException(
    377       env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    378     return;
    379   }
    380   data->modify_watch_on_load = true;
    381   // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
    382   if (JvmtiErrorToException(env,
    383                             jvmti_env,
    384                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    385                                                                 JVMTI_EVENT_CLASS_PREPARE,
    386                                                                 nullptr))) {
    387     return;
    388   }
    389   jint nklasses;
    390   jclass* klasses;
    391   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
    392     return;
    393   }
    394   for (jint i = 0; i < nklasses; i++) {
    395     jclass k = klasses[i];
    396 
    397     jint nfields;
    398     jfieldID* fields;
    399     jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
    400     if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
    401       continue;
    402     } else if (JvmtiErrorToException(env, jvmti_env, err)) {
    403       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    404       return;
    405     }
    406     for (jint j = 0; j < nfields; j++) {
    407       jvmti_env->SetFieldModificationWatch(k, fields[j]);
    408     }
    409     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
    410   }
    411   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
    412 }
    413 
    414 static bool GetFieldAndClass(JNIEnv* env,
    415                              jobject ref_field,
    416                              jclass* out_klass,
    417                              jfieldID* out_field) {
    418   *out_field = env->FromReflectedField(ref_field);
    419   if (env->ExceptionCheck()) {
    420     return false;
    421   }
    422   jclass field_klass = env->FindClass("java/lang/reflect/Field");
    423   if (env->ExceptionCheck()) {
    424     return false;
    425   }
    426   jmethodID get_declaring_class_method =
    427       env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;");
    428   if (env->ExceptionCheck()) {
    429     env->DeleteLocalRef(field_klass);
    430     return false;
    431   }
    432   *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method));
    433   if (env->ExceptionCheck()) {
    434     *out_klass = nullptr;
    435     env->DeleteLocalRef(field_klass);
    436     return false;
    437   }
    438   env->DeleteLocalRef(field_klass);
    439   return true;
    440 }
    441 
    442 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
    443     JNIEnv* env,
    444     jclass trace ATTRIBUTE_UNUSED,
    445     jobject field_obj) {
    446   jfieldID field;
    447   jclass klass;
    448   if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
    449     return;
    450   }
    451 
    452   JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field));
    453   env->DeleteLocalRef(klass);
    454 }
    455 
    456 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
    457     JNIEnv* env,
    458     jclass trace ATTRIBUTE_UNUSED,
    459     jobject field_obj) {
    460   jfieldID field;
    461   jclass klass;
    462   if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
    463     return;
    464   }
    465   JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field));
    466   env->DeleteLocalRef(klass);
    467 }
    468 
    469 extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2(
    470     JNIEnv* env,
    471     jclass trace ATTRIBUTE_UNUSED,
    472     jclass klass,
    473     jobject enter,
    474     jobject exit,
    475     jobject field_access,
    476     jobject field_modify,
    477     jobject single_step,
    478     jobject thread_start,
    479     jobject thread_end,
    480     jthread thr) {
    481   TraceData* data = nullptr;
    482   if (JvmtiErrorToException(env,
    483                             jvmti_env,
    484                             jvmti_env->Allocate(sizeof(TraceData),
    485                                                 reinterpret_cast<unsigned char**>(&data)))) {
    486     return;
    487   }
    488   memset(data, 0, sizeof(TraceData));
    489   if (JvmtiErrorToException(env, jvmti_env,
    490                             jvmti_env->CreateRawMonitor("Trace monitor", &data->trace_mon))) {
    491     return;
    492   }
    493   data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
    494   data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
    495   data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
    496   data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
    497   data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
    498   data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
    499   data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr;
    500   data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr;
    501 
    502   TraceData* old_data = nullptr;
    503   if (JvmtiErrorToException(env, jvmti_env,
    504                             jvmti_env->GetEnvironmentLocalStorage(
    505                                 reinterpret_cast<void**>(&old_data)))) {
    506     return;
    507   } else if (old_data != nullptr && old_data->test_klass != nullptr) {
    508     ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
    509     env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
    510     return;
    511   }
    512   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
    513     return;
    514   }
    515 
    516   current_callbacks.MethodEntry = methodEntryCB;
    517   current_callbacks.MethodExit = methodExitCB;
    518   current_callbacks.FieldAccess = fieldAccessCB;
    519   current_callbacks.FieldModification = fieldModificationCB;
    520   current_callbacks.ClassPrepare = classPrepareCB;
    521   current_callbacks.SingleStep = singleStepCB;
    522   current_callbacks.ThreadStart = threadStartCB;
    523   current_callbacks.ThreadEnd = threadEndCB;
    524   if (JvmtiErrorToException(env,
    525                             jvmti_env,
    526                             jvmti_env->SetEventCallbacks(&current_callbacks,
    527                                                          sizeof(current_callbacks)))) {
    528     return;
    529   }
    530   if (enter != nullptr &&
    531       JvmtiErrorToException(env,
    532                             jvmti_env,
    533                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    534                                                                 JVMTI_EVENT_METHOD_ENTRY,
    535                                                                 thr))) {
    536     return;
    537   }
    538   if (exit != nullptr &&
    539       JvmtiErrorToException(env,
    540                             jvmti_env,
    541                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    542                                                                 JVMTI_EVENT_METHOD_EXIT,
    543                                                                 thr))) {
    544     return;
    545   }
    546   if (field_access != nullptr &&
    547       JvmtiErrorToException(env,
    548                             jvmti_env,
    549                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    550                                                                 JVMTI_EVENT_FIELD_ACCESS,
    551                                                                 thr))) {
    552     return;
    553   }
    554   if (field_modify != nullptr &&
    555       JvmtiErrorToException(env,
    556                             jvmti_env,
    557                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    558                                                                 JVMTI_EVENT_FIELD_MODIFICATION,
    559                                                                 thr))) {
    560     return;
    561   }
    562   if (single_step != nullptr &&
    563       JvmtiErrorToException(env,
    564                             jvmti_env,
    565                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    566                                                                 JVMTI_EVENT_SINGLE_STEP,
    567                                                                 thr))) {
    568     return;
    569   }
    570   if (thread_start != nullptr &&
    571       JvmtiErrorToException(env,
    572                             jvmti_env,
    573                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    574                                                                 JVMTI_EVENT_THREAD_START,
    575                                                                 thr))) {
    576     return;
    577   }
    578   if (thread_end != nullptr &&
    579       JvmtiErrorToException(env,
    580                             jvmti_env,
    581                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    582                                                                 JVMTI_EVENT_THREAD_END,
    583                                                                 thr))) {
    584     return;
    585   }
    586 }
    587 
    588 extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
    589     JNIEnv* env,
    590     jclass trace,
    591     jclass klass,
    592     jobject enter,
    593     jobject exit,
    594     jobject field_access,
    595     jobject field_modify,
    596     jobject single_step,
    597     jthread thr) {
    598   Java_art_Trace_enableTracing2(env,
    599                                 trace,
    600                                 klass,
    601                                 enter,
    602                                 exit,
    603                                 field_access,
    604                                 field_modify,
    605                                 single_step,
    606                                 /* thread_start */ nullptr,
    607                                 /* thread_end */ nullptr,
    608                                 thr);
    609   return;
    610 }
    611 
    612 extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
    613     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
    614   TraceData* data = nullptr;
    615   if (JvmtiErrorToException(
    616       env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
    617     return;
    618   }
    619   // If data is null then we haven't ever enabled tracing so we don't need to do anything.
    620   if (data == nullptr || data->test_klass == nullptr) {
    621     return;
    622   }
    623   ScopedLocalRef<jthrowable> err(env, nullptr);
    624   // First disable all the events.
    625   if (JvmtiErrorToException(env, jvmti_env,
    626                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    627                                                                 JVMTI_EVENT_FIELD_ACCESS,
    628                                                                 thr))) {
    629     env->ExceptionDescribe();
    630     err.reset(env->ExceptionOccurred());
    631     env->ExceptionClear();
    632   }
    633   if (JvmtiErrorToException(env, jvmti_env,
    634                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    635                                                                 JVMTI_EVENT_FIELD_MODIFICATION,
    636                                                                 thr))) {
    637     env->ExceptionDescribe();
    638     err.reset(env->ExceptionOccurred());
    639     env->ExceptionClear();
    640   }
    641   if (JvmtiErrorToException(env, jvmti_env,
    642                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    643                                                                 JVMTI_EVENT_METHOD_ENTRY,
    644                                                                 thr))) {
    645     env->ExceptionDescribe();
    646     err.reset(env->ExceptionOccurred());
    647     env->ExceptionClear();
    648   }
    649   if (JvmtiErrorToException(env, jvmti_env,
    650                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    651                                                                 JVMTI_EVENT_METHOD_EXIT,
    652                                                                 thr))) {
    653     env->ExceptionDescribe();
    654     err.reset(env->ExceptionOccurred());
    655     env->ExceptionClear();
    656   }
    657   if (JvmtiErrorToException(env, jvmti_env,
    658                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    659                                                                 JVMTI_EVENT_SINGLE_STEP,
    660                                                                 thr))) {
    661     env->ExceptionDescribe();
    662     err.reset(env->ExceptionOccurred());
    663     env->ExceptionClear();
    664   }
    665   if (JvmtiErrorToException(env, jvmti_env,
    666                             jvmti_env->RawMonitorEnter(data->trace_mon))) {
    667     return;
    668   }
    669   // Clear test_klass so we know this isn't being used
    670   env->DeleteGlobalRef(data->test_klass);
    671   data->test_klass = nullptr;
    672   if (JvmtiErrorToException(env,
    673                             jvmti_env,
    674                             jvmti_env->RawMonitorExit(data->trace_mon))) {
    675     return;
    676   }
    677   if (err.get() != nullptr) {
    678     env->Throw(err.get());
    679   }
    680 }
    681 
    682 }  // namespace common_trace
    683 
    684 
    685 }  // namespace art
    686