1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include <string.h>
  25 #include "jvmti.h"
  26 
  27 /*
  28  * The goal of this test is to single step into Continuation.doContinue(). The
  29  * expectation is that we will end up in Continuation.yield0() right after the
  30  * return from Continuation.doYield(). There have been bugs where yield0() was
  31  * compiled and we didn't get a single step event when resuming execution in it.
  32  * After confirming the yield0() single stepping, we turn off single stepping
  33  * and run to completion. There have been jvmti _cur_stack_depth asserts related
  34  * to doing this, although they were never reproduced with this test.
  35  *
  36  * Setting up a single step into Continuation.doContinue() is a bit tricky. It
  37  * is called from Continuation.run(), so the first step is to setup a breakpoint
  38  * at the start of run(). After it is hit, we setup another breakpoint at the
  39  * start of Continuation.isStarted(), which is called just before the doContinue()
  40  * call. Once it is hit, we enable single stepping. From isStarted() it should only
  41  * take about 14 single step to reach Continuation.yield0(). If we don't reach it by
  42  * 50 steps, the test fails.
  43  *
  44  * There's also a NotifyFramePop that is done. The is related to trying to trigger
  45  * the _cur_stack_depth assert.
  46  */
  47 extern "C" {
  48 
  49 #define MAX_FRAME_COUNT 20
  50 
  51 static jvmtiEnv *jvmti = NULL;
  52 static jthread exp_thread = NULL;
  53 static jrawMonitorID event_mon = NULL;
  54 static int breakpoint_count = 0;
  55 static int single_step_count = 0;
  56 static int method_entry_count = 0;
  57 static int method_exit_count = 0;
  58 static int frame_pop_count = 0;
  59 static jboolean passed = JNI_FALSE;
  60 
  61 static jmethodID *java_lang_Continuation_methods = NULL;
  62 jint java_lang_Continuation_method_count = 0;
  63 jclass java_lang_Continuation_class = NULL;
  64 
  65 static void
  66 lock_events() {
  67   jvmti->RawMonitorEnter(event_mon);
  68 }
  69 
  70 static void
  71 unlock_events() {
  72   jvmti->RawMonitorExit(event_mon);
  73 }
  74 
  75 static void
  76 check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) {
  77   if (err != JVMTI_ERROR_NONE) {
  78     printf("check_jvmti_status: JVMTI function returned error: %d\n", err);
  79     jni->FatalError(msg);
  80   }
  81 }
  82 
  83 static char* get_method_class_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) {
  84   jvmtiError err;
  85   jclass klass = NULL;
  86   char*  cname = NULL;
  87 
  88   err = jvmti->GetMethodDeclaringClass(method, &klass);
  89   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetMethodDeclaringClass");
  90 
  91   err = jvmti->GetClassSignature(klass, &cname, NULL);
  92   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetClassSignature");
  93 
  94   cname[strlen(cname) - 1] = '\0'; // get rid of trailing ';'
  95   return cname + 1;                // get rid of leading 'L'
  96 }
  97 
  98 static void
  99 print_method(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method, jint depth) {
 100   char*  cname = NULL;
 101   char*  mname = NULL;
 102   char*  msign = NULL;
 103   jvmtiError err;
 104 
 105   cname = get_method_class_name(jvmti, jni, method);
 106 
 107   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
 108   check_jvmti_status(jni, err, "print_method: error in JVMTI GetMethodName");
 109 
 110   printf("%2d: %s: %s%s\n", depth, cname, mname, msign);
 111   fflush(0);
 112 }
 113 
 114 static void
 115 print_stack_trace(jvmtiEnv *jvmti, JNIEnv* jni) {
 116   jvmtiFrameInfo frames[MAX_FRAME_COUNT];
 117   jint count = 0;
 118   jvmtiError err;
 119 
 120   err = jvmti->GetStackTrace(NULL, 0, MAX_FRAME_COUNT, frames, &count);
 121   check_jvmti_status(jni, err, "print_stack_trace: error in JVMTI GetStackTrace");
 122 
 123   printf("JVMTI Stack Trace: frame count: %d\n", count);
 124   for (int depth = 0; depth < count; depth++) {
 125     print_method(jvmti, jni, frames[depth].method, depth);
 126   }
 127   printf("\n");
 128 }
 129 
 130 static void
 131 print_frame_event_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 132                        const char* event_name, int event_count) {
 133   char* cname = NULL;
 134   char* mname = NULL;
 135   char* msign = NULL;
 136   jvmtiThreadInfo thr_info;
 137   jvmtiError err;
 138 
 139   memset(&thr_info, 0, sizeof(thr_info));
 140   err = jvmti->GetThreadInfo(thread, &thr_info);
 141   check_jvmti_status(jni, err, "event handler: error in JVMTI GetThreadInfo call");
 142   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 143 
 144   cname = get_method_class_name(jvmti, jni, method);
 145 
 146   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
 147   check_jvmti_status(jni, err, "event handler: error in JVMTI GetMethodName call");
 148 
 149   printf("%s event #%d: thread: %s, method: %s: %s%s\n",
 150          event_name, event_count, thr_name, cname, mname, msign);
 151 
 152   if (strcmp(event_name, "SingleStep") != 0) {
 153     print_stack_trace(jvmti, jni);
 154   }
 155   fflush(0);
 156 }
 157 
 158 static void
 159 print_cont_event_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_cnt, const char* event_name) {
 160   jvmtiThreadInfo thr_info;
 161   jvmtiError err;
 162 
 163   memset(&thr_info, 0, sizeof(thr_info));
 164   err = jvmti->GetThreadInfo(thread, &thr_info);
 165   check_jvmti_status(jni, err, "event handler failed during JVMTI GetThreadInfo call");
 166 
 167   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 168   printf("\n%s event: thread: %s, frames: %d\n\n", event_name, thr_name, frames_cnt);
 169 
 170   print_stack_trace(jvmti, jni);
 171   fflush(0);
 172 }
 173 
 174 static void
 175 setOrClearBreakpoint(JNIEnv *jni, const char *methodName, jboolean set)
 176 {
 177   jlocation location = (jlocation)0L;
 178   jmethodID method = NULL;
 179   jvmtiError err;
 180   jint method_count = java_lang_Continuation_method_count;
 181 
 182   // Find the jmethodID of the specified method
 183   while (--method_count >= 0) {
 184     jmethodID meth = java_lang_Continuation_methods[method_count];
 185     char* mname = NULL;
 186 
 187     err = jvmti->GetMethodName(meth, &mname, NULL, NULL);
 188     check_jvmti_status(jni, err, "setupBreakpoint: error in JVMTI GetMethodName call");
 189 
 190     if (strcmp(mname, methodName) == 0) {
 191       printf("setupBreakpoint: found method %s() to set a breakpoint\n", mname);
 192       fflush(0);
 193       method = meth;
 194     }
 195   }
 196   if (method == NULL) {
 197       printf("setupBreakpoint: not found method %s() to set a breakpoint\n", methodName);
 198       jni->FatalError("Error in setupBreakpoint: not found method");
 199   }
 200 
 201   if (set) {
 202       err = jvmti->SetBreakpoint(method, location);
 203   } else {
 204       err = jvmti->ClearBreakpoint(method, location);
 205   }
 206   check_jvmti_status(jni, err, "breakP: error in JVMTI SetBreakpoint");
 207 }
 208 
 209 static void
 210 setBreakpoint(JNIEnv *jni, const char *methodName)
 211 {
 212     setOrClearBreakpoint(jni, methodName, JNI_TRUE);
 213 }
 214 
 215 static void
 216 clearBreakpoint(JNIEnv *jni, const char *methodName)
 217 {
 218     setOrClearBreakpoint(jni, methodName, JNI_FALSE);
 219 }
 220 
 221 static jboolean runBreakpointHit = JNI_FALSE;
 222 static jboolean isStartedBreakpointHit = JNI_FALSE;
 223 
 224 static void JNICALL
 225 Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 226            jmethodID method, jlocation location) {
 227   char* mname = NULL;
 228   jvmtiError err;
 229 
 230   lock_events();
 231 
 232   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 233   check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetMethodName call");
 234 
 235   if (strcmp(mname, "run") == 0) {
 236       // We hit our Continuation.run() breakpoint. Now setup the Continuation.isStarted() breakpoint.
 237       if (runBreakpointHit) {
 238           unlock_events();
 239           return; // ignore if we've already seen one
 240       }
 241       print_frame_event_info(jvmti, jni, thread, method,
 242                              "Breakpoint", ++breakpoint_count);
 243       runBreakpointHit = JNI_TRUE;
 244       clearBreakpoint(jni, "run");
 245       setBreakpoint(jni, "isStarted");
 246       err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, thread);
 247       check_jvmti_status(jni, err, "Breakpoint: error in JVMTI SetEventNotificationMode: enable METHOD_EXIT");
 248   } else if (strcmp(mname, "isStarted") == 0) {
 249       // We hit our Continuation.isStarted() breakpoint. Now setup single stepping so we can
 250       // step into Continuation.doContinue().
 251       if (isStartedBreakpointHit) {
 252           unlock_events();
 253           return; // ignore if we've already seen one
 254       }
 255       print_frame_event_info(jvmti, jni, thread, method,
 256                              "Breakpoint", ++breakpoint_count);
 257       isStartedBreakpointHit = JNI_TRUE;
 258       clearBreakpoint(jni, "isStarted");
 259       err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, thread);
 260       check_jvmti_status(jni, err, "Breakpoint: error in JVMTI SetEventNotificationMode: enable SINGLE_STEP");
 261       err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_BREAKPOINT, NULL);
 262       check_jvmti_status(jni, err, "Breakpoint: error in JVMTI SetEventNotificationMode: enable BREAKPOINT");
 263       err = jvmti->NotifyFramePop(thread, 0);
 264       check_jvmti_status(jni, err, "Breakpoint: error in JVMTI NotifyFramePop0");
 265   } else {
 266       printf(" Breakpoint: unexpected breakpoint in method %s()\n", mname);
 267   }
 268 
 269   unlock_events();
 270 }
 271 
 272 static void JNICALL
 273 SingleStep(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 274            jmethodID method, jlocation location) {
 275   char* mname = NULL;
 276   jvmtiError err;
 277 
 278   lock_events();
 279 
 280   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 281   check_jvmti_status(jni, err, "SingleStep: error in JVMTI GetMethodName call");
 282 
 283   print_frame_event_info(jvmti, jni, thread, method,
 284                          "SingleStep", ++single_step_count);
 285   if (strcmp(mname, "yield0") == 0) {
 286       // We single stepped into yield0 within 50 steps. Turn off single stepping and let the test complete.
 287       printf("SingleStep: entered yield0()\n");
 288       print_frame_event_info(jvmti, jni, thread, method,
 289                              "SingleStep Passed", single_step_count);
 290       err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, thread);
 291       check_jvmti_status(jni, err, "SingleStep: error in JVMTI SetEventNotificationMode: enable SINGLE_STEP");
 292       passed = JNI_TRUE;
 293   } else if (single_step_count >= 50) {
 294       // We didn't enter Continuation.yield0() within 50 single steps. The test has failed.
 295       printf("FAILED: SingleStep: never entered method yield0()\n");
 296       print_frame_event_info(jvmti, jni, thread, method,
 297                              "SingleStep 50", single_step_count);
 298       err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, thread);
 299       check_jvmti_status(jni, err, "SingleStep: error in JVMTI SetEventNotificationMode: enable SINGLE_STEP");
 300   }
 301   unlock_events();
 302 }
 303 
 304 static void JNICALL
 305 MethodEntry(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method) {
 306   lock_events();
 307   method_entry_count++;
 308   //print_frame_event_info(jvmti, jni, thread, method, "MethodEntry", method_entry_count);
 309   unlock_events();
 310 }
 311 
 312 static void JNICALL
 313 MethodExit(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 314            jboolean was_popped_by_exception, jvalue return_value) {
 315   lock_events();
 316   method_exit_count++;
 317   //print_frame_event_info(jvmti, jni, thread, method, "MethodExit", method_entry_count);
 318   unlock_events();
 319 }
 320 
 321 static void JNICALL
 322 FramePop(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 323          jboolean was_popped_by_exception) {
 324   lock_events();
 325   frame_pop_count++;
 326   print_frame_event_info(jvmti, jni, thread, method, "FramePop", frame_pop_count);
 327   unlock_events();
 328 }
 329 
 330 static void JNICALL
 331 FiberScheduled(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jobject fiber) {
 332   lock_events();
 333   //processFiberEvent(jvmti, jni, thread, fiber, "FiberScheduled");
 334   unlock_events();
 335 }
 336 
 337 static void JNICALL
 338 FiberTerminated(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jobject fiber) {
 339   lock_events();
 340   //processFiberEvent(jvmti, jni, thread, fiber, "FiberTerminated");
 341   unlock_events();
 342 }
 343 
 344 static void JNICALL
 345 FiberMount(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jobject fiber) {
 346   lock_events();
 347   //processFiberEvent(jvmti, jni, thread, fiber, "FiberMount");
 348   unlock_events();
 349 }
 350 
 351 static void JNICALL
 352 FiberUnmount(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jobject fiber) {
 353   lock_events();
 354   //processFiberEvent(jvmti, jni, thread, fiber, "FiberUnmount");
 355   unlock_events();
 356 }
 357 
 358 static void JNICALL
 359 ContinuationRun(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_count) {
 360   lock_events();
 361   //print_cont_event_info(jvmti, jni, thread, frames_count, "ContinuationRun");
 362   unlock_events();
 363 }
 364 
 365 static void JNICALL
 366 ContinuationYield(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_count) {
 367   lock_events();
 368   //print_cont_event_info(jvmti, jni, thread, frames_count, "ContinuationYield");
 369   unlock_events();
 370 }
 371 
 372 JNIEXPORT jint JNICALL
 373 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
 374   jvmtiEventCallbacks callbacks;
 375   jvmtiCapabilities caps;
 376   jvmtiError err;
 377 
 378   printf("Agent_OnLoad started\n");
 379   if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
 380     return JNI_ERR;
 381   }
 382 
 383   memset(&callbacks, 0, sizeof(callbacks));
 384   callbacks.Breakpoint  = &Breakpoint;
 385   callbacks.SingleStep  = &SingleStep;
 386   callbacks.FramePop    = &FramePop;
 387   callbacks.MethodEntry = &MethodEntry;
 388   callbacks.MethodExit = &MethodExit;
 389   callbacks.FiberScheduled  = &FiberScheduled;
 390   callbacks.FiberTerminated = &FiberTerminated;
 391   callbacks.FiberMount   = &FiberMount;
 392   callbacks.FiberUnmount = &FiberUnmount;
 393   callbacks.ContinuationRun   = &ContinuationRun;
 394   callbacks.ContinuationYield = &ContinuationYield;
 395 
 396   memset(&caps, 0, sizeof(caps));
 397   caps.can_generate_breakpoint_events = 1;
 398   caps.can_generate_single_step_events = 1;
 399   caps.can_generate_frame_pop_events = 1;
 400   caps.can_generate_method_entry_events = 1;
 401   caps.can_generate_method_exit_events = 1;
 402   caps.can_support_fibers = 1;
 403   caps.can_support_continuations = 1;
 404 
 405   err = jvmti->AddCapabilities(&caps);
 406   if (err != JVMTI_ERROR_NONE) {
 407     printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
 408   }
 409 
 410   err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
 411   if (err != JVMTI_ERROR_NONE) {
 412     printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
 413   }
 414 
 415   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIBER_SCHEDULED, NULL);
 416   if (err != JVMTI_ERROR_NONE) {
 417     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 418   }
 419 
 420   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIBER_TERMINATED, NULL);
 421   if (err != JVMTI_ERROR_NONE) {
 422     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 423   }
 424 
 425   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIBER_MOUNT, NULL);
 426   if (err != JVMTI_ERROR_NONE) {
 427     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 428   }
 429 
 430   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIBER_UNMOUNT, NULL);
 431   if (err != JVMTI_ERROR_NONE) {
 432     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 433   }
 434 
 435   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CONTINUATION_RUN, NULL);
 436   if (err != JVMTI_ERROR_NONE) {
 437       printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 438   }
 439 
 440   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CONTINUATION_YIELD, NULL);
 441   if (err != JVMTI_ERROR_NONE) {
 442       printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 443   }
 444 
 445   err = jvmti->CreateRawMonitor("Events Monitor", &event_mon);
 446   if (err != JVMTI_ERROR_NONE) {
 447     printf("Agent_OnLoad: Error in JVMTI CreateRawMonitor: %d\n", err);
 448   }
 449 
 450   printf("Agent_OnLoad finished\n");
 451   fflush(0);
 452 
 453   return JNI_OK;
 454 }
 455 
 456 JNIEXPORT void JNICALL
 457 Java_DoContinueSingleStepTest_enableEvents(JNIEnv *jni, jclass klass, jthread thread, jclass contKlass) {
 458   jvmtiError err;
 459 
 460   printf("enableEvents: started\n");
 461 
 462   java_lang_Continuation_class = (jclass)jni->NewGlobalRef(contKlass);
 463   err = jvmti->GetClassMethods(contKlass, &java_lang_Continuation_method_count, &java_lang_Continuation_methods);
 464   check_jvmti_status(jni, err, "enableEvents: error in JVMTI GetClassMethods");
 465 
 466   setBreakpoint(jni, "run");
 467 
 468   // Enable Breakpoint events globally
 469   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
 470   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable BREAKPOINT");
 471 
 472   printf("enableEvents: finished\n");
 473   fflush(0);
 474 }
 475 
 476 JNIEXPORT jboolean JNICALL
 477 Java_DoContinueSingleStepTest_check(JNIEnv *jni, jclass cls) {
 478   printf("\n");
 479   printf("check: started\n");
 480 
 481   printf("check: breakpoint_count:   %d\n", breakpoint_count);
 482   printf("check: single_step_count:  %d\n", single_step_count);
 483 
 484   printf("check: finished\n");
 485   printf("\n");
 486   fflush(0);
 487 
 488   return passed;
 489 }
 490 } // extern "C"