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 method_entry_count = 0;
  35 static int method_exit_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, "zzz") != 0) {
 157     return; // ignore any other MethodEntry events
 158   }
 159   ++method_entry_count;
 160   print_frame_event_info(jvmti, jni, thread, method, "MethodEntry");
 161 
 162   printf("\nMethodEntry: Requesting FramePop notifications for top frame\n");
 163   fflush(0);
 164 
 165   err = jvmti->NotifyFramePop(thread, 0);
 166   check_jvmti_status(jni, err, "MethodEntry: error in JVMTI NotifyFramePop");
 167       
 168   print_method(jvmti, jni, method, 0);
 169   fflush(0);
 170 
 171   unlock_events();
 172 }
 173 
 174 static void JNICALL
 175 MethodExit(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 176            jboolean was_popped_by_exception, jvalue return_value) {
 177   char* mname = NULL;
 178   jvmtiError err;
 179 
 180   lock_events();
 181 
 182   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 183   check_jvmti_status(jni, err, "MethodExit: error in JVMTI GetMethodName call");
 184 
 185   if (strcmp(mname, "zzz") != 0) {
 186     return; // ignore other MethodExit events
 187   }
 188   ++method_exit_count;
 189   print_frame_event_info(jvmti, jni, thread, method, "MethodExit");
 190 
 191   unlock_events();
 192 }
 193 
 194 static void JNICALL
 195 FramePop(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jmethodID method,
 196          jboolean was_popped_by_exception) {
 197   char* mname = NULL;
 198   jvmtiError err;
 199 
 200   lock_events();
 201 
 202   err = jvmti->GetMethodName(method, &mname, NULL, NULL);
 203   check_jvmti_status(jni, err, "FramePop: error in JVMTI GetMethodName call");
 204 
 205   if (strcmp(mname, "zzz") != 0) {
 206     // TBD: produce fatal error
 207     return;
 208   }
 209 
 210   frame_pop_count++;
 211   print_frame_event_info(jvmti, jni, thread, method, "FramePop");
 212 
 213   unlock_events();
 214 }
 215 
 216 JNIEXPORT jint JNICALL
 217 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
 218   jvmtiEventCallbacks callbacks;
 219   jvmtiCapabilities caps;
 220   jvmtiError err;
 221 
 222   printf("Agent_OnLoad started\n");
 223   if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
 224     return JNI_ERR;
 225   }
 226 
 227   memset(&callbacks, 0, sizeof(callbacks));
 228   callbacks.MethodEntry = &MethodEntry;
 229   callbacks.MethodExit  = &MethodExit;
 230   callbacks.FramePop    = &FramePop;
 231 
 232   memset(&caps, 0, sizeof(caps));
 233   caps.can_generate_method_entry_events = 1;
 234   caps.can_generate_method_exit_events = 1;
 235   caps.can_generate_frame_pop_events = 1;
 236 
 237   err = jvmti->AddCapabilities(&caps);
 238   if (err != JVMTI_ERROR_NONE) {
 239     printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
 240   }
 241 
 242   err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
 243   if (err != JVMTI_ERROR_NONE) {
 244     printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
 245   }
 246 
 247   err = jvmti->CreateRawMonitor("Events Monitor", &event_mon);
 248   if (err != JVMTI_ERROR_NONE) {
 249     printf("Agent_OnLoad: Error in JVMTI CreateRawMonitor: %d\n", err);
 250   }
 251 
 252   printf("Agent_OnLoad finished\n");
 253   fflush(0);
 254 
 255   return JNI_OK;
 256 }
 257 
 258 JNIEXPORT void JNICALL
 259 Java_MyPackage_ContFramePopTest_enableEvents(JNIEnv *jni, jclass cls, jthread thread) {
 260   jvmtiError err;
 261 
 262   printf("enableEvents: started\n");
 263   exp_thread = (jthread)jni->NewGlobalRef(thread);
 264 
 265   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, thread);
 266   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable METHOD_ENTRY");
 267 
 268   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, thread);
 269   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable METHOD_EXIT");
 270 
 271   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thread);
 272   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable FRAME_POP");
 273 
 274   printf("enableEvents: finished\n");
 275   fflush(0);
 276 }
 277 
 278 JNIEXPORT jboolean JNICALL
 279 Java_MyPackage_ContFramePopTest_check(JNIEnv *jni, jclass cls) {
 280   jvmtiError err;
 281 
 282   printf("\n");
 283   printf("check: started\n");
 284 
 285   printf("check: method_entry_count: %d\n", method_entry_count);
 286   printf("check: method_exit_count:  %d\n", method_exit_count);
 287   printf("check: frame_pop_count:    %d\n", frame_pop_count);
 288 
 289   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, exp_thread);
 290   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: disable METHOD_ENTRY");
 291 
 292   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, exp_thread);
 293   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: disable METHOD_EXIT");
 294 
 295   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, exp_thread);
 296   check_jvmti_status(jni, err, "error in JVMTI SetEventNotificationMode: disable FRAME_POP");
 297 
 298   printf("check: finished\n");
 299   printf("\n");
 300   fflush(0);
 301 
 302   return (method_entry_count == frame_pop_count &&
 303           method_entry_count == method_exit_count);
 304 }
 305 } // extern "C"