• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsInCallback(JNIEnv * env,jvmtiEnv * jvmti,jthread thr)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 
SetInCallback(JNIEnv * env,jvmtiEnv * jvmti,jthread thr,bool val)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:
ScopedCallbackState(JNIEnv * jnienv,jvmtiEnv * env,jthread thr)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   }
~ScopedCallbackState()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 
GetTestClassart::common_trace::TraceData86   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 
threadStartCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thread)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 }
threadEndCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thread)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 
singleStepCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thread,jmethodID method,jlocation location)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 
fieldAccessCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thr,jmethodID method,jlocation location,jclass field_klass,jobject object,jfieldID field)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 
fieldModificationCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thr,jmethodID method,jlocation location,jclass field_klass,jobject object,jfieldID field,char type_char,jvalue new_value)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 
methodExitCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thr,jmethodID method,jboolean was_popped_by_exception,jvalue return_value)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 
methodEntryCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thr,jmethodID method)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 
classPrepareCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thr,jclass klass)304 static void classPrepareCB(jvmtiEnv* jvmti,
305                            JNIEnv* jnienv,
306                            [[maybe_unused]] jthread thr,
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 
Java_art_Trace_watchAllFieldAccesses(JNIEnv * env)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 
Java_art_Trace_watchAllFieldModifications(JNIEnv * env)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 
GetFieldAndClass(JNIEnv * env,jobject ref_field,jclass * out_klass,jfieldID * out_field)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 
Java_art_Trace_watchFieldModification(JNIEnv * env,jclass trace,jobject field_obj)442 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
443     JNIEnv* env,
444     [[maybe_unused]] jclass trace,
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 
Java_art_Trace_watchFieldAccess(JNIEnv * env,jclass trace,jobject field_obj)456 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
457     JNIEnv* env,
458     [[maybe_unused]] jclass trace,
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 
Java_art_Trace_nativeEnableFramePopEvents(JNIEnv * env)469 extern "C" JNIEXPORT void JNICALL Java_art_Trace_nativeEnableFramePopEvents(JNIEnv* env) {
470   jvmtiCapabilities caps;
471   memset(&caps, 0, sizeof(caps));
472   caps.can_pop_frame = 1;
473   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
474     return;
475   }
476 }
477 
Java_art_Trace_enableTracing2(JNIEnv * env,jclass trace,jclass klass,jobject enter,jobject exit,jobject field_access,jobject field_modify,jobject single_step,jobject thread_start,jobject thread_end,jthread thr)478 extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2(
479     JNIEnv* env,
480     [[maybe_unused]] jclass trace,
481     jclass klass,
482     jobject enter,
483     jobject exit,
484     jobject field_access,
485     jobject field_modify,
486     jobject single_step,
487     jobject thread_start,
488     jobject thread_end,
489     jthread thr) {
490   TraceData* data = nullptr;
491   if (JvmtiErrorToException(env,
492                             jvmti_env,
493                             jvmti_env->Allocate(sizeof(TraceData),
494                                                 reinterpret_cast<unsigned char**>(&data)))) {
495     return;
496   }
497   memset(data, 0, sizeof(TraceData));
498   if (JvmtiErrorToException(env, jvmti_env,
499                             jvmti_env->CreateRawMonitor("Trace monitor", &data->trace_mon))) {
500     return;
501   }
502   data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
503   data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
504   data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
505   data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
506   data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
507   data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
508   data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr;
509   data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr;
510 
511   TraceData* old_data = nullptr;
512   if (JvmtiErrorToException(env, jvmti_env,
513                             jvmti_env->GetEnvironmentLocalStorage(
514                                 reinterpret_cast<void**>(&old_data)))) {
515     return;
516   } else if (old_data != nullptr && old_data->test_klass != nullptr) {
517     ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
518     env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
519     return;
520   }
521   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
522     return;
523   }
524 
525   current_callbacks.MethodEntry = methodEntryCB;
526   current_callbacks.MethodExit = methodExitCB;
527   current_callbacks.FieldAccess = fieldAccessCB;
528   current_callbacks.FieldModification = fieldModificationCB;
529   current_callbacks.ClassPrepare = classPrepareCB;
530   current_callbacks.SingleStep = singleStepCB;
531   current_callbacks.ThreadStart = threadStartCB;
532   current_callbacks.ThreadEnd = threadEndCB;
533   if (JvmtiErrorToException(env,
534                             jvmti_env,
535                             jvmti_env->SetEventCallbacks(&current_callbacks,
536                                                          sizeof(current_callbacks)))) {
537     return;
538   }
539   if (enter != nullptr &&
540       JvmtiErrorToException(env,
541                             jvmti_env,
542                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
543                                                                 JVMTI_EVENT_METHOD_ENTRY,
544                                                                 thr))) {
545     return;
546   }
547   if (exit != nullptr &&
548       JvmtiErrorToException(env,
549                             jvmti_env,
550                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
551                                                                 JVMTI_EVENT_METHOD_EXIT,
552                                                                 thr))) {
553     return;
554   }
555   if (field_access != nullptr &&
556       JvmtiErrorToException(env,
557                             jvmti_env,
558                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
559                                                                 JVMTI_EVENT_FIELD_ACCESS,
560                                                                 thr))) {
561     return;
562   }
563   if (field_modify != nullptr &&
564       JvmtiErrorToException(env,
565                             jvmti_env,
566                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
567                                                                 JVMTI_EVENT_FIELD_MODIFICATION,
568                                                                 thr))) {
569     return;
570   }
571   if (single_step != nullptr &&
572       JvmtiErrorToException(env,
573                             jvmti_env,
574                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
575                                                                 JVMTI_EVENT_SINGLE_STEP,
576                                                                 thr))) {
577     return;
578   }
579   if (thread_start != nullptr &&
580       JvmtiErrorToException(env,
581                             jvmti_env,
582                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
583                                                                 JVMTI_EVENT_THREAD_START,
584                                                                 thr))) {
585     return;
586   }
587   if (thread_end != nullptr &&
588       JvmtiErrorToException(env,
589                             jvmti_env,
590                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
591                                                                 JVMTI_EVENT_THREAD_END,
592                                                                 thr))) {
593     return;
594   }
595 }
596 
Java_art_Trace_enableTracing(JNIEnv * env,jclass trace,jclass klass,jobject enter,jobject exit,jobject field_access,jobject field_modify,jobject single_step,jthread thr)597 extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
598     JNIEnv* env,
599     jclass trace,
600     jclass klass,
601     jobject enter,
602     jobject exit,
603     jobject field_access,
604     jobject field_modify,
605     jobject single_step,
606     jthread thr) {
607   Java_art_Trace_enableTracing2(env,
608                                 trace,
609                                 klass,
610                                 enter,
611                                 exit,
612                                 field_access,
613                                 field_modify,
614                                 single_step,
615                                 /* thread_start */ nullptr,
616                                 /* thread_end */ nullptr,
617                                 thr);
618   return;
619 }
620 
Java_art_Trace_disableTracing(JNIEnv * env,jclass klass,jthread thr)621 extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
622     JNIEnv* env, [[maybe_unused]] jclass klass, jthread thr) {
623   TraceData* data = nullptr;
624   if (JvmtiErrorToException(
625       env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
626     return;
627   }
628   // If data is null then we haven't ever enabled tracing so we don't need to do anything.
629   if (data == nullptr || data->test_klass == nullptr) {
630     return;
631   }
632   ScopedLocalRef<jthrowable> err(env, nullptr);
633   // First disable all the events.
634   if (JvmtiErrorToException(env, jvmti_env,
635                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
636                                                                 JVMTI_EVENT_FIELD_ACCESS,
637                                                                 thr))) {
638     env->ExceptionDescribe();
639     err.reset(env->ExceptionOccurred());
640     env->ExceptionClear();
641   }
642   if (JvmtiErrorToException(env, jvmti_env,
643                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
644                                                                 JVMTI_EVENT_FIELD_MODIFICATION,
645                                                                 thr))) {
646     env->ExceptionDescribe();
647     err.reset(env->ExceptionOccurred());
648     env->ExceptionClear();
649   }
650   if (JvmtiErrorToException(env, jvmti_env,
651                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
652                                                                 JVMTI_EVENT_METHOD_ENTRY,
653                                                                 thr))) {
654     env->ExceptionDescribe();
655     err.reset(env->ExceptionOccurred());
656     env->ExceptionClear();
657   }
658   if (JvmtiErrorToException(env, jvmti_env,
659                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
660                                                                 JVMTI_EVENT_METHOD_EXIT,
661                                                                 thr))) {
662     env->ExceptionDescribe();
663     err.reset(env->ExceptionOccurred());
664     env->ExceptionClear();
665   }
666   if (JvmtiErrorToException(env, jvmti_env,
667                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
668                                                                 JVMTI_EVENT_SINGLE_STEP,
669                                                                 thr))) {
670     env->ExceptionDescribe();
671     err.reset(env->ExceptionOccurred());
672     env->ExceptionClear();
673   }
674   if (JvmtiErrorToException(env, jvmti_env,
675                             jvmti_env->RawMonitorEnter(data->trace_mon))) {
676     return;
677   }
678   // Clear test_klass so we know this isn't being used
679   env->DeleteGlobalRef(data->test_klass);
680   data->test_klass = nullptr;
681   if (JvmtiErrorToException(env,
682                             jvmti_env,
683                             jvmti_env->RawMonitorExit(data->trace_mon))) {
684     return;
685   }
686   if (err.get() != nullptr) {
687     env->Throw(err.get());
688   }
689 }
690 
691 }  // namespace common_trace
692 
693 
694 }  // namespace art
695