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 frame_pop_count = 0;
  36 static int method_entry_count = 0;
  37 static int method_exit_count = 0;
  38 static int single_step_count = 0;
  39 
  40 static void
  41 lock_events() {
  42   jvmti->RawMonitorEnter(event_mon);
  43 }
  44 
  45 static void
  46 unlock_events() {
  47   jvmti->RawMonitorExit(event_mon);
  48 }
  49 
  50 static void
  51 check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) {
  52   if (err != JVMTI_ERROR_NONE) {
  53     printf("check_jvmti_status: JVMTI function returned error: %d\n", err);
  54     jni->FatalError(msg);
  55   }
  56 }
  57 
  58 static char* get_method_class_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) {
  59   jvmtiError err;
  60   jclass klass = NULL;
  61   char*  cname = NULL;
  62 
  63   err = jvmti->GetMethodDeclaringClass(method, &klass);
  64   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetMethodDeclaringClass");
  65 
  66   err = jvmti->GetClassSignature(klass, &cname, NULL);
  67   check_jvmti_status(jni, err, "get_method_class_name: error in JVMTI GetClassSignature");
  68 
  69   cname[strlen(cname) - 1] = '\0'; // get rid of trailing ';'
  70   return cname + 1;                // get rid of leading 'L'
  71 }
  72 
  73 static void
  74 print_method(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method, jint depth) {
  75   char*  cname = NULL;
  76   char*  mname = NULL;
  77   char*  msign = NULL;
  78   jvmtiError err;
  79 
  80   cname = get_method_class_name(jvmti, jni, method);
  81 
  82   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
  83   check_jvmti_status(jni, err, "print_method: error in JVMTI GetMethodName");
  84 
  85   printf("%2d: %s: %s%s\n", depth, cname, mname, msign);
  86   fflush(0);
  87 }
  88 
  89 static void
  90 print_stack_trace(jvmtiEnv *jvmti, JNIEnv* jni) { 
  91   jvmtiFrameInfo frames[MAX_FRAME_COUNT];
  92   jint count = 0;
  93   jvmtiError err;
  94 
  95   err = jvmti->GetStackTrace(NULL, 0, MAX_FRAME_COUNT, frames, &count);
  96   check_jvmti_status(jni, err, "print_stack_trace: error in JVMTI GetStackTrace");
  97 
  98   printf("JVMTI Stack Trace: frame count: %d\n", count);
  99   for (int depth = 0; depth < count; depth++) {
 100     print_method(jvmti, jni, frames[depth].method, depth);
 101   }
 102   printf("\n");
 103 }
 104 
 105 static void
 106 print_frame_event_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 107                        const char* event_name, int event_count) {
 108   char* cname = NULL;
 109   char* mname = NULL;
 110   char* msign = NULL;
 111   jvmtiThreadInfo thr_info;
 112   jvmtiError err;
 113 
 114   memset(&thr_info, 0, sizeof(thr_info));
 115   err = jvmti->GetThreadInfo(thread, &thr_info);
 116   check_jvmti_status(jni, err, "event handler: error in JVMTI GetThreadInfo call");
 117   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 118 
 119   cname = get_method_class_name(jvmti, jni, method);
 120 
 121   err = jvmti->GetMethodName(method, &mname, &msign, NULL);
 122   check_jvmti_status(jni, err, "event handler: error in JVMTI GetMethodName call");
 123 
 124   printf("\n%s event #%d: thread: %s, method: %s: %s%s\n",
 125          event_name, event_count, thr_name, cname, mname, msign);
 126 
 127   if (strcmp(event_name, "SingleStep") != 0) {
 128     print_stack_trace(jvmti, jni);
 129   }
 130   fflush(0);
 131 }
 132 
 133 static void
 134 print_cont_event_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint frames_cnt, const char* event_name) {
 135   jvmtiThreadInfo thr_info;
 136   jvmtiError err;
 137 
 138   memset(&thr_info, 0, sizeof(thr_info));
 139   err = jvmti->GetThreadInfo(thread, &thr_info);
 140   check_jvmti_status(jni, err, "event handler failed during JVMTI GetThreadInfo call");
 141 
 142   const char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 143   printf("\n%s event: thread: %s, frames: %d\n\n", event_name, thr_name, frames_cnt);
 144 
 145   print_stack_trace(jvmti, jni);
 146   fflush(0);
 147 }
 148 
 149 static void JNICALL
 150 MethodEntry(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method) {
 151   char* mname = NULL;
 152   jvmtiError err;
 153 
 154   lock_events();
 155 
 156   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 157   check_jvmti_status(jni, err, "MethodEntry: error in JVMTI GetMethodName call");
 158 
 159   if (strcmp(mname, "getNextFib") != 0) {
 160     return; // ignore unrelated events
 161   }
 162 
 163   printf("\nMethodEntry: Requesting FramePop notifications for top frame\n");
 164 
 165   err = jvmti->NotifyFramePop(thread, 0);
 166   check_jvmti_status(jni, err, "MethodEntry: error in JVMTI NotifyFramePop");
 167 
 168   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thread);
 169   check_jvmti_status(jni, err, "MethodEntry: error in JVMTI SetEventNotificationMode: enable FRAME_POP");
 170 
 171   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, thread);
 172   check_jvmti_status(jni, err, "MethodEntry: error in JVMTI SetEventNotificationMode: enable METHOD_EXIT");
 173 
 174   print_frame_event_info(jvmti, jni, thread, method,
 175                          "MethodEntry", ++method_entry_count);
 176 
 177   unlock_events();
 178 }
 179 
 180 static void JNICALL
 181 MethodExit(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 182            jboolean was_popped_by_exception, jvalue return_value) {
 183   char* mname = NULL;
 184   jvmtiError err;
 185 
 186   lock_events();
 187 
 188   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 189   check_jvmti_status(jni, err, "MethodExit: error in JVMTI GetMethodName call");
 190 
 191   if (strcmp(mname, "getNextFib") != 0) {
 192     return; // ignore unelated events
 193   }
 194   print_frame_event_info(jvmti, jni, thread, method,
 195                          "MethodExit", ++method_exit_count);
 196 
 197   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, thread);
 198   check_jvmti_status(jni, err, "MethodExit: error in JVMTI SetEventNotificationMode: disable METHOD_EXIT");
 199 
 200   unlock_events();
 201 }
 202 
 203 static void JNICALL
 204 Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 205            jmethodID method, jlocation location) {
 206   char* mname = NULL;
 207   jvmtiError err;
 208 
 209   lock_events();
 210 
 211   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 212   check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetMethodName call");
 213 
 214   if (strcmp(mname, "fibTest") != 0) {
 215     return; // ignore unrelated events
 216   }
 217   print_frame_event_info(jvmti, jni, thread, method,
 218                          "Breakpoint", ++breakpoint_count);
 219 
 220   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, thread);
 221   check_jvmti_status(jni, err, "Breakpoint: error in JVMTI SetEventNotificationMode: enable SINGLE_STEP");
 222 
 223   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, thread);
 224   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable METHOD_ENTRY");
 225 
 226   unlock_events();
 227 }
 228 
 229 static void JNICALL
 230 SingleStep(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 231            jmethodID method, jlocation location) {
 232   char* mname = NULL;
 233   jvmtiError err;
 234 
 235   lock_events();
 236 
 237   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 238   check_jvmti_status(jni, err, "SingleStep: error in JVMTI GetMethodName call");
 239 
 240   if (strcmp(mname, "getNextFib") != 0) {
 241     return; // ignore unrelated events 
 242   }
 243   print_frame_event_info(jvmti, jni, thread, method,
 244                          "SingleStep", ++single_step_count);
 245 
 246   unlock_events();
 247 }
 248 
 249 static void JNICALL
 250 FramePop(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 251          jmethodID method, jboolean was_popped_by_exception) {
 252   char* mname = NULL;
 253   jvmtiError err;
 254 
 255   lock_events();
 256 
 257   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 258   check_jvmti_status(jni, err, "FramePop: error in JVMTI GetMethodName call");
 259 
 260   if (strcmp(mname, "getNextFib") != 0) {
 261     return; // ignore unrelated events
 262   }
 263 
 264   print_frame_event_info(jvmti, jni, thread, method,
 265                          "FramePop", ++frame_pop_count);
 266 
 267   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, NULL);
 268   check_jvmti_status(jni, err, "FramePop: error in JVMTI SetEventNotificationMode: disable SINGLE_STEP");
 269 
 270   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, NULL);
 271   check_jvmti_status(jni, err, "FramePop: error in JVMTI SetEventNotificationMode: disable FRAME_POP");
 272 
 273   unlock_events();
 274 }
 275 
 276 JNIEXPORT jint JNICALL
 277 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
 278   jvmtiEventCallbacks callbacks;
 279   jvmtiCapabilities caps;
 280   jvmtiError err;
 281 
 282   printf("Agent_OnLoad started\n");
 283   if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
 284     return JNI_ERR;
 285   }
 286 
 287   memset(&callbacks, 0, sizeof(callbacks));
 288   callbacks.Breakpoint  = &Breakpoint;
 289   callbacks.FramePop    = &FramePop;
 290   callbacks.MethodEntry = &MethodEntry;
 291   callbacks.MethodExit  = &MethodExit;
 292   callbacks.SingleStep  = &SingleStep;
 293 
 294   memset(&caps, 0, sizeof(caps));
 295   caps.can_generate_breakpoint_events = 1;
 296   caps.can_generate_frame_pop_events = 1;
 297   caps.can_generate_method_entry_events = 1;
 298   caps.can_generate_method_exit_events = 1;
 299   caps.can_generate_single_step_events = 1;
 300 
 301   err = jvmti->AddCapabilities(&caps);
 302   if (err != JVMTI_ERROR_NONE) {
 303     printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
 304   }
 305 
 306   err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
 307   if (err != JVMTI_ERROR_NONE) {
 308     printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
 309   }
 310 
 311   err = jvmti->CreateRawMonitor("Events Monitor", &event_mon);
 312   if (err != JVMTI_ERROR_NONE) {
 313     printf("Agent_OnLoad: Error in JVMTI CreateRawMonitor: %d\n", err);
 314   }
 315 
 316   printf("Agent_OnLoad finished\n");
 317   fflush(0);
 318 
 319   return JNI_OK;
 320 }
 321 
 322 JNIEXPORT void JNICALL
 323 Java_MyPackage_ContStackDepthTest_enableEvents(JNIEnv *jni, jclass klass, jthread thread) {
 324   jint method_count = 0;
 325   jmethodID* methods = NULL;
 326   jmethodID method = NULL;
 327   jlocation location = (jlocation)0L;
 328   jvmtiError err;
 329 
 330   printf("enableEvents: started\n");
 331   exp_thread = (jthread)jni->NewGlobalRef(thread);
 332 
 333   err = jvmti->GetClassMethods(klass, &method_count, &methods);
 334   check_jvmti_status(jni, err, "enableEvents: error in JVMTI GetClassMethods");
 335 
 336   // Find jmethodID of fibTest()
 337   while (--method_count >= 0) {
 338     jmethodID meth = methods[method_count];
 339     char* mname = NULL;
 340 
 341     err = jvmti->GetMethodName(meth, &mname, NULL, NULL);
 342     check_jvmti_status(jni, err, "enableEvents: error in JVMTI GetMethodName call");
 343 
 344     if (strcmp(mname, "fibTest") == 0) {
 345       printf("enableEvents: found method fibTest() to set a breakpoint\n");
 346       fflush(0);
 347       method = meth;
 348     } 
 349   }
 350   if (method == NULL) {
 351     jni->FatalError("Error in enableEvents: not found method fibTest()");
 352   }
 353 
 354   err = jvmti->SetBreakpoint(method, location);
 355   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetBreakpoint");
 356 
 357   // Enable Breakpoint events globally
 358   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
 359   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable BREAKPOINT");
 360 
 361   printf("enableEvents: finished\n");
 362   fflush(0);
 363 }
 364 
 365 JNIEXPORT jboolean JNICALL
 366 Java_MyPackage_ContStackDepthTest_check(JNIEnv *jni, jclass cls) {
 367   jvmtiError err;
 368 
 369   printf("\n");
 370   printf("check: started\n");
 371 
 372   printf("check: breakpoint_count:   %d\n", breakpoint_count);
 373   printf("check: frame_pop_count:    %d\n", frame_pop_count);
 374   printf("check: method_entry_count: %d\n", method_entry_count);
 375   printf("check: method_exit_count:  %d\n", method_exit_count);
 376   printf("check: single_step_count:  %d\n", single_step_count);
 377 
 378   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, exp_thread);
 379   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: disable METHOD_ENTRY");
 380 
 381   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, exp_thread);
 382   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: disable METHOD_EXIT");
 383 
 384   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, exp_thread);
 385   check_jvmti_status(jni, err, "error in JVMTI SetEventNotificationMode: disable FRAME_POP");
 386 
 387   printf("check: finished\n");
 388   printf("\n");
 389   fflush(0);
 390 
 391   return (frame_pop_count == method_entry_count &&
 392           frame_pop_count == method_exit_count);
 393 }
 394 } // extern "C"