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 extern "C" {
  28 
  29 #define MAX_FRAME_COUNT 20
  30 
  31 static jvmtiEnv *jvmti = NULL;
  32 static jthread exp_thread = NULL;
  33 static jrawMonitorID event_mon = NULL;
  34 static int breakpoint_count = 0;
  35 static int single_step_count = 0;
  36 
  37 static void
  38 lock_events() {
  39   jvmti->RawMonitorEnter(event_mon);
  40 }
  41 
  42 static void
  43 unlock_events() {
  44   jvmti->RawMonitorExit(event_mon);
  45 }
  46 
  47 static void
  48 check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) {
  49   if (err != JVMTI_ERROR_NONE) {
  50     printf("check_jvmti_status: JVMTI function returned error: %d\n", err);
  51     jni->FatalError(msg);
  52   }
  53 }
  54 
  55 static char* get_method_class_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) {
  56   jvmtiError err;
  57   jclass klass = NULL;
  58   char*  cname = NULL;
  59 
  60   err = jvmti->GetMethodDeclaringClass(method, &klass);
  61   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetMethodDeclaringClass");
  62 
  63   err = jvmti->GetClassSignature(klass, &cname, NULL);
  64   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetClassSignature");
  65 
  66   cname[strlen(cname) - 1] = '\0'; // get rid of trailing ';'
  67   return cname + 1;                // get rid of leading 'L'
  68 }
  69 
  70 static void
  71 print_method(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method, jint depth) {
  72   char*  cname = NULL;
  73   char*  mname = NULL;
  74   char*  msign = NULL;
  75   jvmtiError err;
  76 
  77   cname = get_method_class_name(jvmti, jni, method);
  78 
  79   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
  80   check_jvmti_status(jni, err, "print_method: error in JVMTI GetMethodName");
  81 
  82   printf("%2d: %s: %s%s\n", depth, cname, mname, msign);
  83   fflush(0);
  84 }
  85 
  86 static void
  87 print_stack_trace(jvmtiEnv *jvmti, JNIEnv* jni) {
  88   jvmtiFrameInfo frames[MAX_FRAME_COUNT];
  89   jint count = 0;
  90   jvmtiError err;
  91 
  92   err = jvmti->GetStackTrace(NULL, 0, MAX_FRAME_COUNT, frames, &count);
  93   check_jvmti_status(jni, err, "print_stack_trace: error in JVMTI GetStackTrace");
  94 
  95   printf("JVMTI Stack Trace: frame count: %d\n", count);
  96   for (int depth = 0; depth < count; depth++) {
  97     print_method(jvmti, jni, frames[depth].method, depth);
  98   }
  99   printf("\n");
 100 }
 101 
 102 static void
 103 print_frame_event_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 104                        const char* event_name, int event_count) {
 105   char* cname = NULL;
 106   char* mname = NULL;
 107   char* msign = NULL;
 108   jvmtiThreadInfo thr_info;
 109   jvmtiError err;
 110 
 111   memset(&thr_info, 0, sizeof(thr_info));
 112   err = jvmti->GetThreadInfo(thread, &thr_info);
 113   check_jvmti_status(jni, err, "event handler: error in JVMTI GetThreadInfo call");
 114   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 115 
 116   cname = get_method_class_name(jvmti, jni, method);
 117 
 118   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
 119   check_jvmti_status(jni, err, "event handler: error in JVMTI GetMethodName call");
 120 
 121   printf("\n%s event #%d: thread: %s, method: %s: %s%s\n",
 122          event_name, event_count, thr_name, cname, mname, msign);
 123 
 124   if (strcmp(event_name, "SingleStep") != 0) {
 125     print_stack_trace(jvmti, jni);
 126   }
 127   fflush(0);
 128 }
 129 
 130 static void
 131 print_cont_event_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_cnt, const char* event_name) {
 132   jvmtiThreadInfo thr_info;
 133   jvmtiError err;
 134 
 135   memset(&thr_info, 0, sizeof(thr_info));
 136   err = jvmti->GetThreadInfo(thread, &thr_info);
 137   check_jvmti_status(jni, err, "event handler failed during JVMTI GetThreadInfo call");
 138 
 139   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 140   printf("\n%s event: thread: %s, frames: %d\n\n", event_name, thr_name, frames_cnt);
 141 
 142   print_stack_trace(jvmti, jni);
 143   fflush(0);
 144 }
 145 
 146 static void JNICALL
 147 Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 148            jmethodID method, jlocation location) {
 149   char* mname = NULL;
 150   jvmtiError err;
 151 
 152   lock_events();
 153 
 154   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 155   check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetMethodName call");
 156 
 157   if (strcmp(mname, "yield0") != 0) {
 158     return; // ignore unrelated events
 159   }
 160   print_frame_event_info(jvmti, jni, thread, method,
 161                          "Breakpoint", ++breakpoint_count);
 162 
 163   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, thread);
 164   check_jvmti_status(jni, err, "Breakpoint: error in JVMTI SetEventNotificationMode: enable SINGLE_STEP");
 165 
 166   unlock_events();
 167 }
 168 
 169 static void JNICALL
 170 SingleStep(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 171            jmethodID method, jlocation location) {
 172   char* mname = NULL;
 173   jvmtiError err;
 174 
 175   lock_events();
 176 
 177   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 178   check_jvmti_status(jni, err, "SingleStep: error in JVMTI GetMethodName call");
 179 
 180   if (strcmp(mname, "yield0") != 0) {
 181     return; // ignore unrelated events
 182   }
 183   print_frame_event_info(jvmti, jni, thread, method,
 184                          "SingleStep", ++single_step_count);
 185 
 186   unlock_events();
 187 }
 188 
 189 
 190 JNIEXPORT jint JNICALL
 191 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
 192   jvmtiEventCallbacks callbacks;
 193   jvmtiCapabilities caps;
 194   jvmtiError err;
 195 
 196   printf("Agent_OnLoad started\n");
 197   if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
 198     return JNI_ERR;
 199   }
 200 
 201   memset(&callbacks, 0, sizeof(callbacks));
 202   callbacks.Breakpoint  = &Breakpoint;
 203   callbacks.SingleStep  = &SingleStep;
 204 
 205   memset(&caps, 0, sizeof(caps));
 206   caps.can_generate_breakpoint_events = 1;
 207   caps.can_generate_single_step_events = 1;
 208 
 209   err = jvmti->AddCapabilities(&caps);
 210   if (err != JVMTI_ERROR_NONE) {
 211     printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
 212   }
 213 
 214   err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
 215   if (err != JVMTI_ERROR_NONE) {
 216     printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
 217   }
 218 
 219   err = jvmti->CreateRawMonitor("Events Monitor", &event_mon);
 220   if (err != JVMTI_ERROR_NONE) {
 221     printf("Agent_OnLoad: Error in JVMTI CreateRawMonitor: %d\n", err);
 222   }
 223 
 224   printf("Agent_OnLoad finished\n");
 225   fflush(0);
 226 
 227   return JNI_OK;
 228 }
 229 
 230 JNIEXPORT void JNICALL
 231 Java_MyPackage_ContYieldBreakPointTest_enableEvents(JNIEnv *jni, jclass klass, jthread thread, jclass contKlass) {
 232   jint method_count = 0;
 233   jmethodID* methods = NULL;
 234   jmethodID method = NULL;
 235   jlocation location = (jlocation)0L;
 236   jvmtiError err;
 237 
 238   printf("enableEvents: started\n");
 239 
 240   err = jvmti->GetClassMethods(contKlass, &method_count, &methods);
 241   check_jvmti_status(jni, err, "enableEvents: error in JVMTI GetClassMethods");
 242 
 243   // Find jmethodID of Continuation.yield0()
 244   while (--method_count >= 0) {
 245     jmethodID meth = methods[method_count];
 246     char* mname = NULL;
 247 
 248     err = jvmti->GetMethodName(meth, &mname, NULL, NULL);
 249     check_jvmti_status(jni, err, "enableEvents: error in JVMTI GetMethodName call");
 250 
 251     if (strcmp(mname, "yield0") == 0) {
 252       printf("enableEvents: found method %s() to set a breakpoint\n", mname);
 253       fflush(0);
 254       method = meth;
 255     }
 256   }
 257   if (method == NULL) {
 258     jni->FatalError("Error in enableEvents: not found method fibTest()");
 259   }
 260 
 261   err = jvmti->SetBreakpoint(method, location);
 262   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetBreakpoint");
 263 
 264   // Enable Breakpoint events globally
 265   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
 266   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable BREAKPOINT");
 267 
 268   printf("enableEvents: finished\n");
 269   fflush(0);
 270 }
 271 
 272 JNIEXPORT jboolean JNICALL
 273 Java_MyPackage_ContYieldBreakPointTest_check(JNIEnv *jni, jclass cls) {
 274   printf("\n");
 275   printf("check: started\n");
 276 
 277   printf("check: breakpoint_count:   %d\n", breakpoint_count);
 278   printf("check: single_step_count:  %d\n", single_step_count);
 279 
 280   printf("check: finished\n");
 281   printf("\n");
 282   fflush(0);
 283 
 284   // Getting this far without a crash or assert means the test passed.
 285   return JNI_TRUE;
 286 }
 287 } // extern "C"