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 #define FRAMES_TO_NOTIFY_POP 7 
  31 
  32 static jvmtiEnv *jvmti = NULL;
  33 static jthread exp_thread = NULL;
  34 static jrawMonitorID event_mon = NULL;
  35 static int method_entry_count = 0;
  36 static int frame_pop_count = 0;
  37 
  38 static void
  39 lock_events() {
  40   jvmti->RawMonitorEnter(event_mon);
  41 }
  42 
  43 static void
  44 unlock_events() {
  45   jvmti->RawMonitorExit(event_mon);
  46 }
  47 
  48 static void
  49 check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) {
  50   if (err != JVMTI_ERROR_NONE) {
  51     printf("check_jvmti_status: JVMTI function returned error: %d\n", err);
  52     jni->FatalError(msg);
  53   }
  54 }
  55 
  56 static char* get_method_class_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) {
  57   jvmtiError err;
  58   jclass klass = NULL;
  59   char*  cname = NULL;
  60 
  61   err = jvmti->GetMethodDeclaringClass(method, &klass);
  62   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetMethodDeclaringClass");
  63 
  64   err = jvmti->GetClassSignature(klass, &cname, NULL);
  65   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetClassSignature");
  66 
  67   cname[strlen(cname) - 1] = '\0'; // get rid of trailing ';'
  68   return cname + 1;                // get rid of leading 'L'
  69 }
  70 
  71 static void
  72 print_method(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method, jint depth) {
  73   char*  cname = NULL;
  74   char*  mname = NULL;
  75   char*  msign = NULL;
  76   jvmtiError err;
  77 
  78   cname = get_method_class_name(jvmti, jni, method);
  79 
  80   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
  81   check_jvmti_status(jni, err, "print_method: error in JVMTI GetMethodName");
  82 
  83   printf("%2d: %s: %s%s\n", depth, cname, mname, msign);
  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, const char* event_name) {
 104   char* cname = NULL;
 105   char* mname = NULL;
 106   char* msign = NULL;
 107   jvmtiThreadInfo thr_info;
 108   jvmtiError err;
 109 
 110   memset(&thr_info, 0, sizeof(thr_info));
 111   err = jvmti->GetThreadInfo(thread, &thr_info);
 112   check_jvmti_status(jni, err, "event handler: error in JVMTI GetThreadInfo call");
 113   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 114 
 115   cname = get_method_class_name(jvmti, jni, method);
 116 
 117   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
 118   check_jvmti_status(jni, err, "event handler: error in JVMTI GetMethodName call");
 119 
 120   if (strcmp(event_name, "MethodEntry") == 0) {
 121     printf("%s event #%d: thread: %s, method: %s: %s%s\n",
 122            event_name, method_entry_count, thr_name, cname, mname, msign);
 123   } else {
 124     printf("%s event #%d: thread: %s, method: %s: %s%s\n",
 125            event_name, frame_pop_count, thr_name, cname, mname, msign);
 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 MethodEntry(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method) {
 148   char* mname = NULL;
 149   jvmtiError err;
 150 
 151   lock_events();
 152 
 153   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 154   check_jvmti_status(jni, err, "MethodEntry: error in JVMTI GetMethodName call");
 155 
 156   if (strcmp(mname, "yield0") == 0) {
 157     print_frame_event_info(jvmti, jni, thread, method, "MethodEntry");
 158 
 159     printf("\nMethodEntry: Requesting FramePop notifications for %d frames:\n", FRAMES_TO_NOTIFY_POP);
 160     fflush(0);
 161 
 162     // Request FramePop notifications for all continuation frames.
 163     // They all are expected to be cleared as a part of yield protocol.
 164     for (jint depth = 0; depth < FRAMES_TO_NOTIFY_POP; depth++) {
 165       jmethodID frame_method = NULL;
 166       jlocation location = 0LL;
 167 
 168       err = jvmti->NotifyFramePop(thread, depth);
 169       check_jvmti_status(jni, err, "MethodEntry: error in JVMTI NotifyFramePop");
 170       
 171       err = jvmti->GetFrameLocation(thread, depth, &frame_method, &location);
 172       check_jvmti_status(jni, err, "MethodEntry: error in JVMTI GetFrameLocation");
 173 
 174       print_method(jvmti, jni, frame_method, depth);
 175     }
 176     if (++method_entry_count > 1) {
 177       // Disable YIELD events when the second MethodEntry event is posted.
 178       // We want to make sure the FramePop requests are cleared in both cases:
 179       // when YIELD events are enabled (1) and disabled (2).
 180       err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CONTINUATION_YIELD, thread);
 181       check_jvmti_status(jni, err, "MethodEntry: error in JVMTI SetEventNotificationMode: disable CONTINUATION_YIELD");
 182     }
 183   }
 184   fflush(0);
 185   unlock_events();
 186 }
 187 
 188 static void JNICALL
 189 FramePop(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 190          jboolean was_popped_by_exception) {
 191   lock_events();
 192   frame_pop_count++;
 193   print_frame_event_info(jvmti, jni, thread, method, "FramePop");
 194   unlock_events();
 195 }
 196 
 197 static void JNICALL
 198 ContinuationRun(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_count) {
 199   lock_events();
 200   print_cont_event_info(jvmti, jni, thread, frames_count, "ContinuationRun");
 201   unlock_events();
 202 }
 203 
 204 static void JNICALL
 205 ContinuationYield(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_count) {
 206   lock_events();
 207   print_cont_event_info(jvmti, jni, thread, frames_count, "ContinuationYield");
 208   unlock_events();
 209 }
 210 
 211 JNIEXPORT jint JNICALL
 212 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
 213   jvmtiEventCallbacks callbacks;
 214   jvmtiCapabilities caps;
 215   jvmtiError err;
 216 
 217   printf("Agent_OnLoad started\n");
 218   if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
 219     return JNI_ERR;
 220   }
 221 
 222   memset(&callbacks, 0, sizeof(callbacks));
 223   callbacks.MethodEntry       = &MethodEntry;
 224   callbacks.FramePop          = &FramePop;
 225   callbacks.ContinuationRun   = &ContinuationRun;
 226   callbacks.ContinuationYield = &ContinuationYield;
 227 
 228   memset(&caps, 0, sizeof(caps));
 229   caps.can_generate_method_entry_events = 1;
 230   caps.can_generate_frame_pop_events = 1;
 231   caps.can_support_continuations = 1;
 232 
 233   err = jvmti->AddCapabilities(&caps);
 234   if (err != JVMTI_ERROR_NONE) {
 235     printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
 236   }
 237 
 238   err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
 239   if (err != JVMTI_ERROR_NONE) {
 240     printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
 241   }
 242 
 243   err = jvmti->CreateRawMonitor("Events Monitor", &event_mon);
 244   if (err != JVMTI_ERROR_NONE) {
 245     printf("Agent_OnLoad: Error in JVMTI CreateRawMonitor: %d\n", err);
 246   }
 247 
 248   printf("Agent_OnLoad finished\n");
 249   fflush(0);
 250 
 251   return JNI_OK;
 252 }
 253 
 254 JNIEXPORT void JNICALL
 255 Java_MyPackage_ContinuationTest_enableEvents(JNIEnv *jni, jclass cls, jthread thread) {
 256   jvmtiError err;
 257 
 258   printf("enableEvents: started\n");
 259   exp_thread = (jthread)jni->NewGlobalRef(thread);
 260 
 261   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, thread);
 262   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable METHOD_ENTRY");
 263 
 264   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thread);
 265   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable FRAME_POP");
 266 
 267   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CONTINUATION_RUN, thread);
 268   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable CONTINUATION_RUN");
 269 
 270   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CONTINUATION_YIELD, thread);
 271   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable CONTINUATION_YIELD");
 272 
 273   printf("enableEvents: finished\n");
 274   fflush(0);
 275 }
 276 
 277 JNIEXPORT jboolean JNICALL
 278 Java_MyPackage_ContinuationTest_check(JNIEnv *jni, jclass cls) {
 279   jvmtiError err;
 280 
 281   printf("\n");
 282   printf("check: started\n");
 283 
 284   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, exp_thread);
 285   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: disable METHOD_ENTRY");
 286 
 287   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, exp_thread);
 288   check_jvmti_status(jni, err, "error in JVMTI SetEventNotificationMode: disable FRAME_POP");
 289 
 290   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CONTINUATION_RUN, exp_thread);
 291   check_jvmti_status(jni, err, "error in JVMTI SetEventNotificationMode: disable CONTINUATION_RUN");
 292 
 293   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CONTINUATION_YIELD, exp_thread);
 294   check_jvmti_status(jni, err, "error in JVMTI SetEventNotificationMode: disable CONTINUATION_YIELD");
 295 
 296   printf("check: finished\n");
 297   printf("\n");
 298   fflush(0);
 299 
 300   return frame_pop_count == 0;
 301 }
 302 } // extern "C"