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 ATTRIBUTE_UNUSED,jclass klass)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 
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 ATTRIBUTE_UNUSED,jobject field_obj)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 
Java_art_Trace_watchFieldAccess(JNIEnv * env,jclass trace ATTRIBUTE_UNUSED,jobject field_obj)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 
Java_art_Trace_enableTracing2(JNIEnv * env,jclass trace ATTRIBUTE_UNUSED,jclass klass,jobject enter,jobject exit,jobject field_access,jobject field_modify,jobject single_step,jobject thread_start,jobject thread_end,jthread thr)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 
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)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 
Java_art_Trace_disableTracing(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jthread thr)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