• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2019 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 "suspend_event_helper.h"
18  
19  #include <inttypes.h>
20  
21  #include <cstdio>
22  #include <memory>
23  #include <string>
24  #include <vector>
25  
26  #include "android-base/logging.h"
27  #include "android-base/stringprintf.h"
28  
29  #include "jni.h"
30  #include "jvmti.h"
31  #include "scoped_local_ref.h"
32  #include "scoped_utf_chars.h"
33  
34  // Test infrastructure
35  #include "jni_binder.h"
36  #include "jni_helper.h"
37  #include "jvmti_helper.h"
38  #include "test_env.h"
39  #include "ti_macros.h"
40  
41  namespace art {
42  namespace common_suspend_event {
43  
44  struct TestData {
45    jlocation target_loc;
46    jmethodID target_method;
47    jclass target_klass;
48    jfieldID target_field;
49    jrawMonitorID notify_monitor;
50    jint frame_pop_offset;
51    jmethodID frame_pop_setup_method;
52    std::vector<std::string> interesting_classes;
53    bool hit_location;
54  
TestDataart::common_suspend_event::TestData55    TestData(jvmtiEnv* jvmti,
56             JNIEnv* env,
57             jlocation loc,
58             jobject meth,
59             jclass klass,
60             jobject field,
61             jobject setup_meth,
62             jint pop_offset,
63             const std::vector<std::string>&& interesting)
64        : target_loc(loc), target_method(meth != nullptr ? env->FromReflectedMethod(meth) : nullptr),
65          target_klass(reinterpret_cast<jclass>(env->NewGlobalRef(klass))),
66          target_field(field != nullptr ? env->FromReflectedField(field) : nullptr),
67          frame_pop_offset(pop_offset),
68          frame_pop_setup_method(setup_meth != nullptr ? env->FromReflectedMethod(setup_meth)
69                                                       : nullptr),
70          interesting_classes(interesting), hit_location(false) {
71      JvmtiErrorToException(
72          env, jvmti, jvmti->CreateRawMonitor("SuspendStopMonitor", &notify_monitor));
73    }
74  
PerformSuspendart::common_suspend_event::TestData75    void PerformSuspend(jvmtiEnv* jvmti, JNIEnv* env) {
76      // Wake up the waiting thread.
77      JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(notify_monitor));
78      hit_location = true;
79      JvmtiErrorToException(env, jvmti, jvmti->RawMonitorNotifyAll(notify_monitor));
80      JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(notify_monitor));
81      // Suspend ourself
82      jvmti->SuspendThread(nullptr);
83    }
84  };
85  
PerformSuspension(jvmtiEnv * jvmti,JNIEnv * env)86  void PerformSuspension(jvmtiEnv* jvmti, JNIEnv* env) {
87    TestData* data;
88    if (JvmtiErrorToException(
89            env,
90            jvmti_env,
91            jvmti->GetThreadLocalStorage(/* thread */ nullptr, reinterpret_cast<void**>(&data)))) {
92      return;
93    }
94    CHECK(data != nullptr);
95    data->PerformSuspend(jvmti, env);
96  }
97  
98  void JNICALL
cbSingleStep(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID meth,jlocation loc)99  cbSingleStep(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, jmethodID meth, jlocation loc) {
100    TestData* data;
101    if (JvmtiErrorToException(
102            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
103      return;
104    }
105    CHECK(data != nullptr);
106    if (meth != data->target_method || loc != data->target_loc) {
107      return;
108    }
109    data->PerformSuspend(jvmti, env);
110  }
111  
cbExceptionCatch(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jlocation location,jobject exception)112  void JNICALL cbExceptionCatch(jvmtiEnv* jvmti,
113                                JNIEnv* env,
114                                jthread thr,
115                                jmethodID method,
116                                [[maybe_unused]] jlocation location,
117                                [[maybe_unused]] jobject exception) {
118    TestData* data;
119    if (JvmtiErrorToException(
120            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
121      return;
122    }
123    CHECK(data != nullptr);
124    if (method != data->target_method) {
125      return;
126    }
127    data->PerformSuspend(jvmti, env);
128  }
129  
cbException(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jlocation location,jobject exception,jmethodID catch_method,jlocation catch_location)130  void JNICALL cbException(jvmtiEnv* jvmti,
131                           JNIEnv* env,
132                           jthread thr,
133                           jmethodID method,
134                           [[maybe_unused]] jlocation location,
135                           [[maybe_unused]] jobject exception,
136                           [[maybe_unused]] jmethodID catch_method,
137                           [[maybe_unused]] jlocation catch_location) {
138    TestData* data;
139    if (JvmtiErrorToException(
140            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
141      return;
142    }
143    CHECK(data != nullptr);
144    if (method != data->target_method) {
145      return;
146    }
147    data->PerformSuspend(jvmti, env);
148  }
149  
cbMethodEntry(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method)150  void JNICALL cbMethodEntry(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, jmethodID method) {
151    TestData* data;
152    if (JvmtiErrorToException(
153            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
154      return;
155    }
156    CHECK(data != nullptr);
157    if (method != data->target_method) {
158      return;
159    }
160    data->PerformSuspend(jvmti, env);
161  }
162  
cbMethodExit(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jboolean was_popped_by_exception,jvalue return_value)163  void JNICALL cbMethodExit(jvmtiEnv* jvmti,
164                            JNIEnv* env,
165                            jthread thr,
166                            jmethodID method,
167                            [[maybe_unused]] jboolean was_popped_by_exception,
168                            [[maybe_unused]] jvalue return_value) {
169    TestData* data;
170    if (JvmtiErrorToException(
171            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
172      return;
173    }
174    CHECK(data != nullptr);
175    if (method != data->target_method) {
176      return;
177    }
178    data->PerformSuspend(jvmti, env);
179  }
180  
cbFieldModification(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jlocation location,jclass field_klass,jobject object,jfieldID field,char signature_type,jvalue new_value)181  void JNICALL cbFieldModification(jvmtiEnv* jvmti,
182                                   JNIEnv* env,
183                                   jthread thr,
184                                   [[maybe_unused]] jmethodID method,
185                                   [[maybe_unused]] jlocation location,
186                                   [[maybe_unused]] jclass field_klass,
187                                   [[maybe_unused]] jobject object,
188                                   jfieldID field,
189                                   [[maybe_unused]] char signature_type,
190                                   [[maybe_unused]] jvalue new_value) {
191    TestData* data;
192    if (JvmtiErrorToException(
193            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
194      return;
195    }
196    CHECK(data != nullptr);
197    if (field != data->target_field) {
198      // TODO What to do here.
199      LOG(FATAL) << "Strange, shouldn't get here!";
200    }
201    data->PerformSuspend(jvmti, env);
202  }
203  
cbFieldAccess(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jlocation location,jclass field_klass,jobject object,jfieldID field)204  void JNICALL cbFieldAccess(jvmtiEnv* jvmti,
205                             JNIEnv* env,
206                             jthread thr,
207                             [[maybe_unused]] jmethodID method,
208                             [[maybe_unused]] jlocation location,
209                             jclass field_klass,
210                             [[maybe_unused]] jobject object,
211                             jfieldID field) {
212    TestData* data;
213    if (JvmtiErrorToException(
214            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
215      return;
216    }
217    CHECK(data != nullptr);
218    if (field != data->target_field || !env->IsSameObject(field_klass, data->target_klass)) {
219      // TODO What to do here.
220      LOG(FATAL) << "Strange, shouldn't get here!";
221    }
222    data->PerformSuspend(jvmti, env);
223  }
224  
225  void JNICALL
cbBreakpointHit(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jlocation loc)226  cbBreakpointHit(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, jmethodID method, jlocation loc) {
227    TestData* data;
228    if (JvmtiErrorToException(
229            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
230      return;
231    }
232    CHECK(data != nullptr);
233    if (data->frame_pop_setup_method == method) {
234      CHECK(loc == 0) << "We should have stopped at location 0";
235      if (JvmtiErrorToException(env, jvmti, jvmti->NotifyFramePop(thr, data->frame_pop_offset))) {
236        return;
237      }
238      return;
239    }
240    if (method != data->target_method || loc != data->target_loc) {
241      // TODO What to do here.
242      LOG(FATAL) << "Strange, shouldn't get here!";
243    }
244    data->PerformSuspend(jvmti, env);
245  }
246  
cbFramePop(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jmethodID method,jboolean was_popped_by_exception)247  void JNICALL cbFramePop(jvmtiEnv* jvmti,
248                          JNIEnv* env,
249                          jthread thr,
250                          [[maybe_unused]] jmethodID method,
251                          [[maybe_unused]] jboolean was_popped_by_exception) {
252    TestData* data;
253    if (JvmtiErrorToException(
254            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
255      return;
256    }
257    CHECK(data != nullptr);
258    data->PerformSuspend(jvmti, env);
259  }
260  
cbClassLoadOrPrepare(jvmtiEnv * jvmti,JNIEnv * env,jthread thr,jclass klass)261  void JNICALL cbClassLoadOrPrepare(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, jclass klass) {
262    TestData* data;
263    if (JvmtiErrorToException(
264            env, jvmti, jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
265      return;
266    }
267    CHECK(data != nullptr);
268    char* name;
269    if (JvmtiErrorToException(env, jvmti, jvmti->GetClassSignature(klass, &name, nullptr))) {
270      return;
271    }
272    std::string name_str(name);
273    if (JvmtiErrorToException(
274            env, jvmti, jvmti->Deallocate(reinterpret_cast<unsigned char*>(name)))) {
275      return;
276    }
277    if (std::find(data->interesting_classes.cbegin(), data->interesting_classes.cend(), name_str) !=
278        data->interesting_classes.cend()) {
279      data->PerformSuspend(jvmti, env);
280    }
281  }
282  
Java_art_SuspendEvents_setupTest(JNIEnv * env,jclass klass)283  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupTest(JNIEnv* env,
284                                                                     [[maybe_unused]] jclass klass) {
285    jvmtiCapabilities caps;
286    memset(&caps, 0, sizeof(caps));
287    // Most of these will already be there but might as well be complete.
288    caps.can_pop_frame = 1;
289    caps.can_force_early_return = 1;
290    caps.can_generate_single_step_events = 1;
291    caps.can_generate_breakpoint_events = 1;
292    caps.can_suspend = 1;
293    caps.can_generate_method_entry_events = 1;
294    caps.can_generate_method_exit_events = 1;
295    caps.can_generate_monitor_events = 1;
296    caps.can_generate_exception_events = 1;
297    caps.can_generate_frame_pop_events = 1;
298    caps.can_generate_field_access_events = 1;
299    caps.can_generate_field_modification_events = 1;
300    caps.can_redefine_classes = 1;
301    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
302      return;
303    }
304    jvmtiEventCallbacks cb;
305    memset(&cb, 0, sizeof(cb));
306    // TODO Add the rest of these.
307    cb.Breakpoint = cbBreakpointHit;
308    cb.SingleStep = cbSingleStep;
309    cb.FieldAccess = cbFieldAccess;
310    cb.FieldModification = cbFieldModification;
311    cb.MethodEntry = cbMethodEntry;
312    cb.MethodExit = cbMethodExit;
313    cb.Exception = cbException;
314    cb.ExceptionCatch = cbExceptionCatch;
315    cb.FramePop = cbFramePop;
316    cb.ClassLoad = cbClassLoadOrPrepare;
317    cb.ClassPrepare = cbClassLoadOrPrepare;
318    JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)));
319  }
320  
DeleteTestData(JNIEnv * env,jthread thr,TestData * data)321  static bool DeleteTestData(JNIEnv* env, jthread thr, TestData* data) {
322    env->DeleteGlobalRef(data->target_klass);
323    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
324      return false;
325    }
326    return JvmtiErrorToException(
327        env, jvmti_env, jvmti_env->Deallocate(reinterpret_cast<uint8_t*>(data)));
328  }
329  
SetupTestData(JNIEnv * env,jobject meth,jlocation loc,jclass target_klass,jobject field,jobject setup_meth,jint pop_offset,const std::vector<std::string> && interesting_names)330  static TestData* SetupTestData(JNIEnv* env,
331                                 jobject meth,
332                                 jlocation loc,
333                                 jclass target_klass,
334                                 jobject field,
335                                 jobject setup_meth,
336                                 jint pop_offset,
337                                 const std::vector<std::string>&& interesting_names) {
338    void* data_ptr;
339    TestData* data;
340    if (JvmtiErrorToException(
341            env,
342            jvmti_env,
343            jvmti_env->Allocate(sizeof(TestData), reinterpret_cast<uint8_t**>(&data_ptr)))) {
344      return nullptr;
345    }
346    data = new (data_ptr) TestData(jvmti_env,
347                                   env,
348                                   loc,
349                                   meth,
350                                   target_klass,
351                                   field,
352                                   setup_meth,
353                                   pop_offset,
354                                   std::move(interesting_names));
355    if (env->ExceptionCheck()) {
356      env->DeleteGlobalRef(data->target_klass);
357      jvmti_env->Deallocate(reinterpret_cast<uint8_t*>(data));
358      return nullptr;
359    }
360    return data;
361  }
362  
SetupTestData(JNIEnv * env,jobject meth,jlocation loc,jclass target_klass,jobject field,jobject setup_meth,jint pop_offset)363  static TestData* SetupTestData(JNIEnv* env,
364                                 jobject meth,
365                                 jlocation loc,
366                                 jclass target_klass,
367                                 jobject field,
368                                 jobject setup_meth,
369                                 jint pop_offset) {
370    std::vector<std::string> empty;
371    return SetupTestData(
372        env, meth, loc, target_klass, field, setup_meth, pop_offset, std::move(empty));
373  }
374  
375  extern "C" JNIEXPORT void JNICALL
Java_art_SuspendEvents_setupSuspendClassEvent(JNIEnv * env,jclass klass,jint event_num,jobjectArray interesting_names,jthread thr)376  Java_art_SuspendEvents_setupSuspendClassEvent(JNIEnv* env,
377                                                [[maybe_unused]] jclass klass,
378                                                jint event_num,
379                                                jobjectArray interesting_names,
380                                                jthread thr) {
381    CHECK(event_num == JVMTI_EVENT_CLASS_LOAD || event_num == JVMTI_EVENT_CLASS_PREPARE);
382    std::vector<std::string> names;
383    jint cnt = env->GetArrayLength(interesting_names);
384    for (jint i = 0; i < cnt; i++) {
385      env->PushLocalFrame(1);
386      jstring name_obj = reinterpret_cast<jstring>(env->GetObjectArrayElement(interesting_names, i));
387      const char* name_chr = env->GetStringUTFChars(name_obj, nullptr);
388      names.push_back(std::string(name_chr));
389      env->ReleaseStringUTFChars(name_obj, name_chr);
390      env->PopLocalFrame(nullptr);
391    }
392    TestData* data;
393    if (JvmtiErrorToException(
394            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
395      return;
396    }
397    CHECK(data == nullptr) << "Data was not cleared!";
398    data = SetupTestData(env, nullptr, 0, nullptr, nullptr, nullptr, 0, std::move(names));
399    if (data == nullptr) {
400      return;
401    }
402    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
403      return;
404    }
405    JvmtiErrorToException(
406        env,
407        jvmti_env,
408        jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, static_cast<jvmtiEvent>(event_num), thr));
409  }
410  
Java_art_SuspendEvents_clearSuspendClassEvent(JNIEnv * env,jclass klass,jthread thr)411  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendClassEvent(
412      JNIEnv* env, [[maybe_unused]] jclass klass, jthread thr) {
413    TestData* data;
414    if (JvmtiErrorToException(
415            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
416      return;
417    }
418    CHECK(data != nullptr);
419    if (JvmtiErrorToException(
420            env,
421            jvmti_env,
422            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CLASS_LOAD, thr))) {
423      return;
424    }
425    if (JvmtiErrorToException(
426            env,
427            jvmti_env,
428            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CLASS_PREPARE, thr))) {
429      return;
430    }
431    DeleteTestData(env, thr, data);
432  }
433  
Java_art_SuspendEvents_setupSuspendSingleStepAt(JNIEnv * env,jclass klass,jobject meth,jlocation loc,jthread thr)434  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupSuspendSingleStepAt(
435      JNIEnv* env, [[maybe_unused]] jclass klass, jobject meth, jlocation loc, jthread thr) {
436    TestData* data;
437    if (JvmtiErrorToException(
438            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
439      return;
440    }
441    CHECK(data == nullptr) << "Data was not cleared!";
442    data = SetupTestData(env, meth, loc, nullptr, nullptr, nullptr, 0);
443    if (data == nullptr) {
444      return;
445    }
446    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
447      return;
448    }
449    JvmtiErrorToException(
450        env,
451        jvmti_env,
452        jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, thr));
453  }
454  
Java_art_SuspendEvents_clearSuspendSingleStepFor(JNIEnv * env,jclass klass,jthread thr)455  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendSingleStepFor(
456      JNIEnv* env, [[maybe_unused]] jclass klass, jthread thr) {
457    TestData* data;
458    if (JvmtiErrorToException(
459            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
460      return;
461    }
462    CHECK(data != nullptr);
463    if (JvmtiErrorToException(
464            env,
465            jvmti_env,
466            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, thr))) {
467      return;
468    }
469    DeleteTestData(env, thr, data);
470  }
471  
Java_art_SuspendEvents_setupSuspendPopFrameEvent(JNIEnv * env,jclass klass,jint offset,jobject breakpoint_func,jthread thr)472  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupSuspendPopFrameEvent(
473      JNIEnv* env, [[maybe_unused]] jclass klass, jint offset, jobject breakpoint_func, jthread thr) {
474    TestData* data;
475    if (JvmtiErrorToException(
476            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
477      return;
478    }
479    CHECK(data == nullptr) << "Data was not cleared!";
480    data = SetupTestData(env, nullptr, 0, nullptr, nullptr, breakpoint_func, offset);
481    CHECK(data != nullptr);
482    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
483      return;
484    }
485    if (JvmtiErrorToException(
486            env,
487            jvmti_env,
488            jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thr))) {
489      return;
490    }
491    if (JvmtiErrorToException(
492            env,
493            jvmti_env,
494            jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, thr))) {
495      return;
496    }
497    if (JvmtiErrorToException(
498            env, jvmti_env, jvmti_env->SetBreakpoint(data->frame_pop_setup_method, 0))) {
499      return;
500    }
501  }
502  
Java_art_SuspendEvents_clearSuspendPopFrameEvent(JNIEnv * env,jclass klass,jthread thr)503  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendPopFrameEvent(
504      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
505    TestData* data;
506    if (JvmtiErrorToException(
507            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
508      return;
509    }
510    CHECK(data != nullptr);
511    if (JvmtiErrorToException(
512            env,
513            jvmti_env,
514            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, thr))) {
515      return;
516    }
517    if (JvmtiErrorToException(
518            env,
519            jvmti_env,
520            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_BREAKPOINT, thr))) {
521      return;
522    }
523    if (JvmtiErrorToException(
524            env, jvmti_env, jvmti_env->ClearBreakpoint(data->frame_pop_setup_method, 0))) {
525      return;
526    }
527    DeleteTestData(env, thr, data);
528  }
529  
Java_art_SuspendEvents_setupSuspendBreakpointFor(JNIEnv * env,jclass klass,jobject meth,jlocation loc,jthread thr)530  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupSuspendBreakpointFor(
531      JNIEnv* env, [[maybe_unused]] jclass klass , jobject meth, jlocation loc, jthread thr) {
532    TestData* data;
533    if (JvmtiErrorToException(
534            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
535      return;
536    }
537    CHECK(data == nullptr) << "Data was not cleared!";
538    data = SetupTestData(env, meth, loc, nullptr, nullptr, nullptr, 0);
539    if (data == nullptr) {
540      return;
541    }
542    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
543      return;
544    }
545    if (JvmtiErrorToException(
546            env,
547            jvmti_env,
548            jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, thr))) {
549      return;
550    }
551    JvmtiErrorToException(
552        env, jvmti_env, jvmti_env->SetBreakpoint(data->target_method, data->target_loc));
553  }
554  
Java_art_SuspendEvents_clearSuspendBreakpointFor(JNIEnv * env,jclass klass,jthread thr)555  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendBreakpointFor(
556      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
557    TestData* data;
558    if (JvmtiErrorToException(
559            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
560      return;
561    }
562    CHECK(data != nullptr);
563    if (JvmtiErrorToException(
564            env,
565            jvmti_env,
566            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_BREAKPOINT, thr))) {
567      return;
568    }
569    if (JvmtiErrorToException(
570            env, jvmti_env, jvmti_env->ClearBreakpoint(data->target_method, data->target_loc))) {
571      return;
572    }
573    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
574      return;
575    }
576    DeleteTestData(env, thr, data);
577  }
578  
Java_art_SuspendEvents_setupSuspendExceptionEvent(JNIEnv * env,jclass klass,jobject method,jboolean is_catch,jthread thr)579  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupSuspendExceptionEvent(
580      JNIEnv* env, [[maybe_unused]] jclass klass , jobject method, jboolean is_catch, jthread thr) {
581    TestData* data;
582    if (JvmtiErrorToException(
583            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
584      return;
585    }
586    CHECK(data == nullptr) << "Data was not cleared!";
587    data = SetupTestData(env, method, 0, nullptr, nullptr, nullptr, 0);
588    if (data == nullptr) {
589      return;
590    }
591    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
592      return;
593    }
594    JvmtiErrorToException(
595        env,
596        jvmti_env,
597        jvmti_env->SetEventNotificationMode(
598            JVMTI_ENABLE, is_catch ? JVMTI_EVENT_EXCEPTION_CATCH : JVMTI_EVENT_EXCEPTION, thr));
599  }
600  
Java_art_SuspendEvents_clearSuspendExceptionEvent(JNIEnv * env,jclass klass,jthread thr)601  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendExceptionEvent(
602      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
603    TestData* data;
604    if (JvmtiErrorToException(
605            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
606      return;
607    }
608    CHECK(data != nullptr);
609    if (JvmtiErrorToException(
610            env,
611            jvmti_env,
612            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_EXCEPTION_CATCH, thr))) {
613      return;
614    }
615    if (JvmtiErrorToException(
616            env,
617            jvmti_env,
618            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_EXCEPTION, thr))) {
619      return;
620    }
621    DeleteTestData(env, thr, data);
622  }
623  
Java_art_SuspendEvents_setupSuspendMethodEvent(JNIEnv * env,jclass klass,jobject method,jboolean enter,jthread thr)624  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupSuspendMethodEvent(
625      JNIEnv* env, [[maybe_unused]] jclass klass , jobject method, jboolean enter, jthread thr) {
626    TestData* data;
627    if (JvmtiErrorToException(
628            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
629      return;
630    }
631    CHECK(data == nullptr) << "Data was not cleared!";
632    data = SetupTestData(env, method, 0, nullptr, nullptr, nullptr, 0);
633    if (data == nullptr) {
634      return;
635    }
636    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
637      return;
638    }
639    JvmtiErrorToException(
640        env,
641        jvmti_env,
642        jvmti_env->SetEventNotificationMode(
643            JVMTI_ENABLE, enter ? JVMTI_EVENT_METHOD_ENTRY : JVMTI_EVENT_METHOD_EXIT, thr));
644  }
645  
Java_art_SuspendEvents_clearSuspendMethodEvent(JNIEnv * env,jclass klass,jthread thr)646  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearSuspendMethodEvent(
647      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
648    TestData* data;
649    if (JvmtiErrorToException(
650            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
651      return;
652    }
653    CHECK(data != nullptr);
654    if (JvmtiErrorToException(
655            env,
656            jvmti_env,
657            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, thr))) {
658      return;
659    }
660    if (JvmtiErrorToException(
661            env,
662            jvmti_env,
663            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, thr))) {
664      return;
665    }
666    DeleteTestData(env, thr, data);
667  }
668  
669  extern "C" JNIEXPORT void JNICALL
Java_art_SuspendEvents_setupFieldSuspendFor(JNIEnv * env,jclass klass,jclass target_klass,jobject field,jboolean access,jthread thr)670  Java_art_SuspendEvents_setupFieldSuspendFor(JNIEnv* env,
671                                              [[maybe_unused]] jclass klass,
672                                              jclass target_klass,
673                                              jobject field,
674                                              jboolean access,
675                                              jthread thr) {
676    TestData* data;
677    if (JvmtiErrorToException(
678            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
679      return;
680    }
681    CHECK(data == nullptr) << "Data was not cleared!";
682    data = SetupTestData(env, nullptr, 0, target_klass, field, nullptr, 0);
683    if (data == nullptr) {
684      return;
685    }
686    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
687      return;
688    }
689    if (JvmtiErrorToException(env,
690                              jvmti_env,
691                              jvmti_env->SetEventNotificationMode(
692                                  JVMTI_ENABLE,
693                                  access ? JVMTI_EVENT_FIELD_ACCESS : JVMTI_EVENT_FIELD_MODIFICATION,
694                                  thr))) {
695      return;
696    }
697    if (access) {
698      JvmtiErrorToException(
699          env, jvmti_env, jvmti_env->SetFieldAccessWatch(data->target_klass, data->target_field));
700    } else {
701      JvmtiErrorToException(
702          env,
703          jvmti_env,
704          jvmti_env->SetFieldModificationWatch(data->target_klass, data->target_field));
705    }
706  }
707  
Java_art_SuspendEvents_clearFieldSuspendFor(JNIEnv * env,jclass klass,jthread thr)708  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearFieldSuspendFor(
709      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
710    TestData* data;
711    if (JvmtiErrorToException(
712            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
713      return;
714    }
715    CHECK(data != nullptr);
716    if (JvmtiErrorToException(
717            env,
718            jvmti_env,
719            jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FIELD_ACCESS, thr))) {
720      return;
721    }
722    if (JvmtiErrorToException(env,
723                              jvmti_env,
724                              jvmti_env->SetEventNotificationMode(
725                                  JVMTI_DISABLE, JVMTI_EVENT_FIELD_MODIFICATION, thr))) {
726      return;
727    }
728    if (JvmtiErrorToException(
729            env,
730            jvmti_env,
731            jvmti_env->ClearFieldModificationWatch(data->target_klass, data->target_field)) &&
732        JvmtiErrorToException(
733            env,
734            jvmti_env,
735            jvmti_env->ClearFieldAccessWatch(data->target_klass, data->target_field))) {
736      return;
737    } else {
738      env->ExceptionClear();
739    }
740    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
741      return;
742    }
743    DeleteTestData(env, thr, data);
744  }
745  
Java_art_SuspendEvents_setupWaitForNativeCall(JNIEnv * env,jclass klass,jthread thr)746  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_setupWaitForNativeCall(
747      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
748    TestData* data;
749    if (JvmtiErrorToException(
750            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
751      return;
752    }
753    CHECK(data == nullptr) << "Data was not cleared!";
754    data = SetupTestData(env, nullptr, 0, nullptr, nullptr, nullptr, 0);
755    if (data == nullptr) {
756      return;
757    }
758    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, data))) {
759      return;
760    }
761  }
762  
Java_art_SuspendEvents_clearWaitForNativeCall(JNIEnv * env,jclass klass,jthread thr)763  extern "C" JNIEXPORT void JNICALL Java_art_SuspendEvents_clearWaitForNativeCall(
764      JNIEnv* env, [[maybe_unused]] jclass klass , jthread thr) {
765    TestData* data;
766    if (JvmtiErrorToException(
767            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
768      return;
769    }
770    CHECK(data != nullptr);
771    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
772      return;
773    }
774    DeleteTestData(env, thr, data);
775  }
776  
777  extern "C" JNIEXPORT void JNICALL
Java_art_SuspendEvents_waitForSuspendHit(JNIEnv * env,jclass klass,jthread thr)778  Java_art_SuspendEvents_waitForSuspendHit(JNIEnv* env, [[maybe_unused]] jclass klass, jthread thr) {
779    TestData* data;
780    if (JvmtiErrorToException(
781            env, jvmti_env, jvmti_env->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
782      return;
783    }
784    CHECK(data != nullptr);
785    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(data->notify_monitor))) {
786      return;
787    }
788    while (!data->hit_location) {
789      if (JvmtiErrorToException(
790              env, jvmti_env, jvmti_env->RawMonitorWait(data->notify_monitor, -1))) {
791        return;
792      }
793    }
794    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(data->notify_monitor))) {
795      return;
796    }
797    jint state = 0;
798    while (!JvmtiErrorToException(env, jvmti_env, jvmti_env->GetThreadState(thr, &state)) &&
799           (state & JVMTI_THREAD_STATE_SUSPENDED) == 0) {
800    }
801  }
802  }  // namespace common_suspend_event
803  }  // namespace art
804