1 /*
   2  * Copyright (c) 2018, 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 #ifdef __cplusplus
  28 extern "C" {
  29 #endif
  30 
  31 #define MAX_FRAME_COUNT 30
  32 #define MAX_WORKER_THREADS 10
  33 
  34 typedef struct Tinfo {
  35   jboolean just_scheduled;
  36   jboolean was_run;
  37   jboolean was_yield;
  38   char* thr_name;
  39 } Tinfo;
  40 
  41 static const int MAX_EVENTS_TO_PROCESS = 20;
  42 static jvmtiEnv *jvmti = NULL;
  43 static jrawMonitorID events_monitor = NULL;
  44 static Tinfo tinfo[MAX_WORKER_THREADS];
  45 static jboolean continuation_events_enabled = JNI_FALSE;
  46 
  47 static void
  48 lock_events() {
  49   (*jvmti)->RawMonitorEnter(jvmti, events_monitor);
  50 }
  51 
  52 static void
  53 unlock_events() {
  54   (*jvmti)->RawMonitorExit(jvmti, events_monitor);
  55 }
  56 
  57 static Tinfo*
  58 find_tinfo(JNIEnv* jni, char* thr_name) {
  59   Tinfo* inf = NULL;
  60   int idx = 0;
  61 
  62   // Find slot with named worker thread or empty slot
  63   for (; idx < MAX_WORKER_THREADS; idx++) {
  64     inf = &tinfo[idx];
  65     if (inf->thr_name == NULL) {
  66       inf->thr_name = thr_name;
  67       break;
  68     }
  69     if (strcmp(inf->thr_name, thr_name) == 0) {
  70       break;
  71     }
  72   }
  73   if (idx >= MAX_WORKER_THREADS) {
  74     (*jni)->FatalError(jni, "find_tinfo: found more than 10 worker threads!");
  75   }
  76   return inf; // return slot
  77 }
  78 
  79 static char*
  80 get_method_class_name(jvmtiEnv *jvmti, JNIEnv *jni, jmethodID method) {
  81   jvmtiError err;
  82   jclass klass = NULL;
  83   char*  cname = NULL;
  84 
  85   err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
  86   if (err != JVMTI_ERROR_NONE) {
  87     (*jni)->FatalError(jni, "get_method_class_name: error in JVMTI GetMethodDeclaringClass");
  88   }
  89   err = (*jvmti)->GetClassSignature(jvmti, klass, &cname, NULL);
  90   if (err != JVMTI_ERROR_NONE) {
  91     (*jni)->FatalError(jni, "get_method_class_name: error in JVMTI GetClassSignature");
  92   }
  93   cname[strlen(cname) - 1] = '\0'; // get rid of trailing ';'
  94   return cname + 1;                // get rid of leading 'L'
  95 }
  96 
  97 static void
  98 print_method(jvmtiEnv *jvmti, JNIEnv *jni, jmethodID method, jint depth) {
  99   char*  cname = NULL;
 100   char*  mname = NULL;
 101   char*  msign = NULL;
 102   jvmtiError err;
 103 
 104   cname = get_method_class_name(jvmti, jni, method);
 105 
 106   err = (*jvmti)->GetMethodName(jvmti, method, &mname, &msign, NULL);
 107   if (err != JVMTI_ERROR_NONE) {
 108     (*jni)->FatalError(jni, "print_method: error in JVMTI GetMethodName");
 109   }
 110   printf("%2d: %s: %s%s\n", depth, cname, mname, msign);
 111 }
 112 
 113 static void
 114 print_stack_trace(jvmtiEnv *jvmti, JNIEnv *jni, int count, jvmtiFrameInfo *frames) {
 115   printf("JVMTI Stack Trace: frame count: %d\n", count);
 116   for (int depth = 0; depth < count; depth++) {
 117     print_method(jvmti, jni, frames[depth].method, depth);
 118   }
 119   printf("\n");
 120 }
 121 
 122 static void
 123 print_fiber_event_info(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name) {
 124   jvmtiThreadInfo thr_info;
 125   jvmtiError err = (*jvmti)->GetThreadInfo(jvmti, thread, &thr_info);
 126 
 127   if (err != JVMTI_ERROR_NONE) {
 128     (*jni)->FatalError(jni, "event handler failed during JVMTI GetThreadInfo call");
 129   }
 130   char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 131   printf("\n#### %s event: thread: %s, fiber: %p\n", event_name, thr_name, fiber);
 132 
 133   Tinfo* inf = find_tinfo(jni, thr_name); // Find slot with named worker thread
 134 
 135   if (strcmp(event_name, "FiberScheduled") == 0) {
 136     inf->just_scheduled = JNI_TRUE;
 137   }
 138   else {
 139     if (inf->thr_name == NULL && strcmp(event_name, "FiberTerminated") != 0) {
 140       (*jni)->FatalError(jni, "Fiber event: worker thread not found!");
 141     }
 142     if (strcmp(event_name, "FiberMount") == 0) {
 143       if (!inf->just_scheduled) { // There is no ContinuationRun for just scheduled fibers
 144         if (inf->was_yield) {
 145           (*jni)->FatalError(jni, "FiberMount: event with ContinuationYield before!");
 146         }
 147         if (continuation_events_enabled && !inf->was_run) {
 148           (*jni)->FatalError(jni, "FiberMount: event without ContinuationRun before!");
 149         }
 150       }
 151     }
 152     if (strcmp(event_name, "FiberUnmount") == 0) {
 153       if (inf->just_scheduled) {
 154         (*jni)->FatalError(jni, "FiberUnmount: event without FiberMount before!");
 155       }
 156       if (inf->was_run) {
 157         (*jni)->FatalError(jni, "FiberUnmount: event with ContinuationRun before!");
 158       }
 159       if (continuation_events_enabled && !inf->was_yield) {
 160         (*jni)->FatalError(jni, "FiberUnmount: event without ContinuationYield before!");
 161       }
 162     }
 163     inf->just_scheduled = JNI_FALSE;
 164   }
 165   inf->was_run = JNI_FALSE;
 166   inf->was_yield = JNI_FALSE;
 167 }
 168 
 169 static void
 170 print_cont_event_info(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jint frames_cnt, char* event_name) {
 171   static int cont_events_cnt = 0;
 172   if (cont_events_cnt++ > MAX_EVENTS_TO_PROCESS) {
 173     return; // No need to test all events
 174   }
 175 
 176   jvmtiThreadInfo thr_info;
 177   jvmtiError err = (*jvmti)->GetThreadInfo(jvmti, thread, &thr_info);
 178 
 179   if (err != JVMTI_ERROR_NONE) {
 180     (*jni)->FatalError(jni, "event handler failed during JVMTI GetThreadInfo call");
 181   }
 182   char* thr_name = (thr_info.name == NULL) ? "<Unnamed thread>" : thr_info.name;
 183   printf("\n#### %s event: thread: %s, frames count: %d\n", event_name, thr_name, frames_cnt);
 184 
 185   Tinfo* inf = find_tinfo(jni, thr_name); // Find slot with named worker thread
 186   if (inf->thr_name == NULL) {
 187     (*jni)->FatalError(jni, "Continuation event: worker thread not found!");
 188   }
 189   if (strcmp(event_name, "ContinuationRun") == 0) {
 190     inf->was_run = JNI_TRUE;
 191     inf->was_yield = JNI_FALSE;
 192   }
 193   if (strcmp(event_name, "ContinuationYield") == 0) {
 194     inf->was_run = JNI_FALSE;
 195     inf->was_yield = JNI_TRUE;
 196   }
 197 }
 198 
 199 static void
 200 test_IsFiber(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name) {
 201   jboolean is_fiber = JNI_FALSE;
 202   jvmtiError err;
 203 
 204   printf("\n");
 205 
 206   // #1: Test JVMTI IsFiber function with NULL fiber
 207 
 208   err = (*jvmti)->IsFiber(jvmti, NULL, &is_fiber);
 209   if (err != JVMTI_ERROR_NONE) {
 210     (*jni)->FatalError(jni, "event handler: failed during JVMTI IsFiber call");
 211   }
 212   if (is_fiber != JNI_FALSE) {
 213     (*jni)->FatalError(jni, "event handler: JVMTI IsFiber with NULL fiber failed to return JNI_FALSE");
 214   }
 215   printf("JVMTI IsFiber with NULL fiber returned JNI_FALSE as expected\n");
 216 
 217   // #2: Test JVMTI IsFiber function with a bad fiber
 218 
 219   err = (*jvmti)->IsFiber(jvmti, thread, &is_fiber);
 220   if (err != JVMTI_ERROR_NONE) {
 221     (*jni)->FatalError(jni, "event handler: failed during JVMTI IsFiber call");
 222   }
 223   if (is_fiber != JNI_FALSE) {
 224     (*jni)->FatalError(jni, "event handler: JVMTI IsFiber with bad fiber failed to return JNI_FALSE");
 225   }
 226   printf("JVMTI IsFiber with bad fiber returned JNI_FALSE as expected\n");
 227 
 228   // #3: Test JVMTI IsFiber function with a good fiber
 229 
 230   err = (*jvmti)->IsFiber(jvmti, fiber, &is_fiber);
 231   if (err != JVMTI_ERROR_NONE) {
 232     (*jni)->FatalError(jni, "event handler: failed during JVMTI IsFiber call");
 233   }
 234   if (is_fiber != JNI_TRUE) {
 235     (*jni)->FatalError(jni, "event handler: JVMTI IsFiber with good fiber failed to return JNI_TRUE");
 236   }
 237   printf("JVMTI IsFiber with good fiber returned JNI_TRUE as expected\n");
 238 }
 239 
 240 static void
 241 test_GetThreadFiber(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name) {
 242   jobject thread_fiber = NULL;
 243   jvmtiError err;
 244 
 245   printf("\n");
 246 
 247   // #1: Test JVMTI GetThreadFiber function NULL thread (current)
 248 
 249   err = (*jvmti)->GetThreadFiber(jvmti, NULL, &thread_fiber);
 250   if (err != JVMTI_ERROR_NONE) {
 251     (*jni)->FatalError(jni, "event handler: JVMTI GetThreadFiber with NULL thread (current) returned error status");
 252   }
 253   if (thread_fiber == NULL) {
 254     (*jni)->FatalError(jni, "event handler: JVMTI GetThreadFiber with NULL thread (current) failed to return non-NULL fiber");
 255   }
 256   printf("JVMTI GetThreadFiber with NULL thread (current) returned non-NULL fiber as expected\n");
 257 
 258   // #2: Test JVMTI GetThreadFiber function a bad thread
 259 
 260   err = (*jvmti)->GetThreadFiber(jvmti, fiber, &thread_fiber);
 261   if (err != JVMTI_ERROR_INVALID_THREAD) {
 262     (*jni)->FatalError(jni, "event handler: JVMTI GetThreadFiber with bad thread failed to return JVMTI_ERROR_INVALID_THREAD");
 263   }
 264 
 265   // #3: Test JVMTI GetThreadFiber function with a good thread
 266 
 267   err = (*jvmti)->GetThreadFiber(jvmti, thread, &thread_fiber);
 268   if (err != JVMTI_ERROR_NONE) {
 269     (*jni)->FatalError(jni, "event handler: failed during JVMTI GetThreadFiber call");
 270   }
 271   if (thread_fiber == NULL) {
 272     (*jni)->FatalError(jni, "event handler: JVMTI GetThreadFiber with good thread failed to return non-NULL fiber");
 273   }
 274   printf("JVMTI GetThreadFiber with good thread returned non-NULL fiber as expected\n");
 275 }
 276 
 277 static void
 278 test_GetFiberThread(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name) {
 279   jthread fiber_thread = NULL;
 280   jvmtiError err;
 281 
 282   printf("\n");
 283 
 284   // #1: Test JVMTI GetFiberThread function with NULL fiber
 285 
 286   err = (*jvmti)->GetFiberThread(jvmti, NULL, &fiber_thread);
 287   if (err != JVMTI_ERROR_INVALID_FIBER) {
 288     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberThread with NULL fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 289   }
 290 
 291   // #2: Test JVMTI GetFiberThread function with a bad fiber
 292 
 293   err = (*jvmti)->GetFiberThread(jvmti, thread, &fiber_thread);
 294   if (err != JVMTI_ERROR_INVALID_FIBER) {
 295     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberThread with bad fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 296   }
 297 
 298   // #3: Test JVMTI GetFiberThread function with a good fiber
 299 
 300   err = (*jvmti)->GetFiberThread(jvmti, fiber, &fiber_thread);
 301   if (err != JVMTI_ERROR_NONE) {
 302     (*jni)->FatalError(jni, "event handler: failed during JVMTI GetFiberThread call");
 303   }
 304   if (fiber_thread == NULL) {
 305     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberThread with good fiber failed to return non-NULL carrier thread");
 306   }
 307   printf("JVMTI GetFiberThread with good fiber returned non-NULL carrier thread as expected\n");
 308 }
 309 
 310 static int
 311 test_GetFiberFrameCount(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name) {
 312   int frame_count = -1;
 313   jvmtiError err;
 314 
 315   // #1: Test JVMTI GetFiberFrameCount function with NULL fiber
 316 
 317   err = (*jvmti)->GetFiberFrameCount(jvmti, NULL, &frame_count);
 318   if (err != JVMTI_ERROR_INVALID_FIBER) {
 319       printf("JVMTI GetFiberFrameCount with NULL fiber returned error: %d\n", err);
 320     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameCount with NULL fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 321   }
 322 
 323   // #2: Test JVMTI GetFiberFrameCount function with a bad fiber
 324 
 325   err = (*jvmti)->GetFiberFrameCount(jvmti, thread, &frame_count);
 326   if (err != JVMTI_ERROR_INVALID_FIBER) {
 327       printf("JVMTI GetFiberFrameCount with bad fiber returned error: %d\n", err);
 328     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameCount with bad fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 329   }
 330 
 331   // #3: Test JVMTI GetFiberFrameCount function with NULL count_ptr pointer
 332 
 333   err = (*jvmti)->GetFiberFrameCount(jvmti, fiber, NULL);
 334   if (err != JVMTI_ERROR_NULL_POINTER) {
 335       printf("JVMTI GetFiberFrameCount with bad fiber returned error: %d\n", err);
 336     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameCount with NULL count_ptr pointer failed to return JVMTI_ERROR_NULL_POINTER");
 337   }
 338 
 339   // #4: Test JVMTI GetFiberFrameCount function with a good fiber
 340 
 341   err = (*jvmti)->GetFiberFrameCount(jvmti, fiber, &frame_count);
 342   if (err != JVMTI_ERROR_NONE) {
 343     printf("JVMTI GetFiberFrameCount with good fiber returned error: %d\n", err);
 344     (*jni)->FatalError(jni, "event handler: failed during JVMTI GetFiberFrameCount call");
 345   }
 346   if (frame_count < 0) {
 347     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameCount with good fiber returned negative frame_count\n");
 348   }
 349   printf("JVMTI GetFiberFrameCount with good fiber returned frame_count: %d\n", frame_count);
 350 
 351   return frame_count;
 352 }
 353 
 354 static void
 355 test_GetFiberFrameLocation(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name, int frame_count) {
 356   jmethodID method = NULL;
 357   jlocation location = -1;
 358   jvmtiError err;
 359 
 360   // #1: Test JVMTI GetFiberFrameLocation function with NULL fiber
 361 
 362   err = (*jvmti)->GetFiberFrameLocation(jvmti, NULL, 0, &method, &location);
 363   if (err != JVMTI_ERROR_INVALID_FIBER) {
 364     printf("JVMTI GetFiberFrameCount with NULL fiber returned error: %d\n", err);
 365     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation with NULL fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 366   }
 367 
 368   // #2: Test JVMTI GetFiberFrameLocation function with a bad fiber
 369 
 370   err = (*jvmti)->GetFiberFrameLocation(jvmti, thread, 0, &method, &location);
 371   if (err != JVMTI_ERROR_INVALID_FIBER) {
 372     printf("JVMTI GetFiberFrameCount with bad fiber returned error: %d\n", err);
 373     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation with bad fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 374   }
 375 
 376   // #3: Test JVMTI GetFiberFrameLocation function with negative frame depth
 377   err = (*jvmti)->GetFiberFrameLocation(jvmti, fiber, -1, &method, &location);
 378   if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
 379     printf("JVMTI GetFiberFrameCount with negative frame depth returned error: %d\n", err);
 380     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation with negative frame depth failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT");
 381   }
 382 
 383   // #4: Test JVMTI GetFiberFrameLocation function with NULL method_ptr
 384   err = (*jvmti)->GetFiberFrameLocation(jvmti, fiber, 0, NULL, &location);
 385   if (err != JVMTI_ERROR_NULL_POINTER) {
 386     printf("JVMTI GetFiberFrameCount with NULL method_ptr returned error: %d\n", err);
 387     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation with NULL method_ptr failed to return JVMTI_ERROR_NULL_POINTER");
 388   }
 389 
 390   // #5: Test JVMTI GetFiberFrameLocation function with NULL location_ptr
 391   err = (*jvmti)->GetFiberFrameLocation(jvmti, fiber, 0, &method, NULL);
 392   if (err != JVMTI_ERROR_NULL_POINTER) {
 393     printf("JVMTI GetFiberFrameCount with NULL location_ptr returned error: %d\n", err);
 394     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation with NULL location_ptr failed to return JVMTI_ERROR_NULL_POINTER");
 395   }
 396 
 397   // #6: Test JVMTI GetFiberFrameLocation function with a good fiber
 398 
 399   if (frame_count == 0) {
 400     err = (*jvmti)->GetFiberFrameLocation(jvmti, fiber, 0, &method, &location);
 401     if (err != JVMTI_ERROR_NO_MORE_FRAMES) {
 402       printf("JVMTI GetFiberFrameLocation for empty stack returned error: %d\n", err);
 403       (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation for empty stack failed to return JVMTI_ERROR_NO_MORE_FRAMES");
 404     }
 405     printf("JVMTI GetFiberFrameLocation for empty stack returned JVMTI_ERROR_NO_MORE_FRAMES as expected\n");
 406   } else {
 407     err = (*jvmti)->GetFiberFrameLocation(jvmti, fiber, 0, &method, &location);
 408     if (err != JVMTI_ERROR_NONE) {
 409       printf("JVMTI GetFiberFrameLocation with good fiber returned error: %d\n", err);
 410       (*jni)->FatalError(jni, "event handler: failed during JVMTI GetFiberFrameCount call");
 411     }
 412     if (location < 0) {
 413       (*jni)->FatalError(jni, "event handler: JVMTI GetFiberFrameLocation with good fiber returned negative location\n");
 414     }
 415     printf("JVMTI GetFiberFrameLocation with good fiber returned location: %d\n", (int) location);
 416   }
 417 }
 418 
 419 static void
 420 test_GetFiberStackTrace(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name, int frame_count) {
 421   jvmtiFrameInfo frames[MAX_FRAME_COUNT];
 422   int count = -1;
 423   jmethodID method = NULL;
 424   jvmtiError err;
 425 
 426   printf("\n");
 427 
 428   // #1: Test JVMTI GetFiberStackTrace function with NULL fiber
 429 
 430   err = (*jvmti)->GetFiberStackTrace(jvmti, NULL, 0, MAX_FRAME_COUNT, frames, &count);
 431   if (err != JVMTI_ERROR_INVALID_FIBER) {
 432     printf("JVMTI GetFiberStackTrace with NULL fiber returned error: %d\n", err);
 433     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace with NULL fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 434   }
 435 
 436   // #2: Test JVMTI GetFiberStackTrace function with a bad fiber
 437 
 438   err = (*jvmti)->GetFiberStackTrace(jvmti, thread, 0, MAX_FRAME_COUNT, frames, &count);
 439   if (err != JVMTI_ERROR_INVALID_FIBER) {
 440     printf("JVMTI GetFiberStackTrace with bad fiber returned error: %d\n", err);
 441     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace with bad fiber failed to return JVMTI_ERROR_INVALID_FIBER");
 442   }
 443 
 444   // #3: Test JVMTI GetFiberStackTrace function with bad start_depth
 445   err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, -(frame_count + 1), MAX_FRAME_COUNT, frames, &count);
 446   if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
 447     printf("JVMTI GetFiberStackTrace with very negative start_depth returned error: %d\n", err);
 448     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace with verynegative start_depth failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT");
 449   }
 450   err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, (frame_count + 1), MAX_FRAME_COUNT, frames, &count);
 451   if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
 452     printf("JVMTI GetFiberStackTrace with very big start_depth returned error: %d\n", err);
 453     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace with very big start_depth failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT");
 454   }
 455 
 456   // #4: Test JVMTI GetFiberStackTrace function with negative max_frame_count
 457   err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, 0, -1, frames, &count);
 458   if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
 459     printf("JVMTI GetFiberStackTrace with negative max_frame_count returned error: %d\n", err);
 460     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace with negative max_frame_count failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT");
 461   }
 462 
 463   // #5: Test JVMTI GetFiberStackTrace function with NULL frame_buffer pointer
 464   err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, 0, MAX_FRAME_COUNT, NULL, &count);
 465   if (err != JVMTI_ERROR_NULL_POINTER) {
 466     printf("JVMTI GetFiberStackTrace with NULL frame_buffer pointer returned error: %d\n", err);
 467     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace witt NULL frame_buffer pointer failed to return JVMTI_ERROR_NULL_POINTER");
 468   }
 469 
 470   // #6: Test JVMTI GetFiberStackTrace function with NULL count_ptr pointer
 471   err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, 0, MAX_FRAME_COUNT, frames, NULL);
 472   if (err != JVMTI_ERROR_NULL_POINTER) {
 473     printf("JVMTI GetFiberStackTrace with NULL count_ptr pointer returned error: %d\n", err);
 474     (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace witt NULL count_ptr pointer failed to return JVMTI_ERROR_NULL_POINTER");
 475   }
 476 
 477   // #7: Test JVMTI GetFiberStackTrace function with a good fiber
 478 
 479   if (frame_count == 0) {
 480     err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, 1, MAX_FRAME_COUNT, frames, &count);
 481     if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
 482       printf("JVMTI GetFiberStackTrace for empty stack returned error: %d\n", err);
 483       (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace for empty stack failed to return JVMTI_ERROR_ILLEGAL_ARGUMENT");
 484     }
 485   } else {
 486     err = (*jvmti)->GetFiberStackTrace(jvmti, fiber, 0, MAX_FRAME_COUNT, frames, &count);
 487     if (err != JVMTI_ERROR_NONE) {
 488       printf("JVMTI GetFiberStackTrace with good fiber returned error: %d\n", err);
 489       (*jni)->FatalError(jni, "event handler: failed during JVMTI GetFiberStackTrace call");
 490     }
 491     if (count <= 0) {
 492       (*jni)->FatalError(jni, "event handler: JVMTI GetFiberStackTrace with good fiber returned negative frame count\n");
 493     }
 494     print_stack_trace(jvmti, jni, count, frames);
 495   }
 496 }
 497 
 498 static void
 499 processFiberEvent(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber, char* event_name) {
 500   static int fiber_events_cnt = 0;
 501 
 502   if (strcmp(event_name, "FiberTerminated") != 0 &&
 503       strcmp(event_name, "FiberScheduled")  != 0) {
 504     if (fiber_events_cnt++ > MAX_EVENTS_TO_PROCESS) {
 505       return; // No need to test all events
 506     }
 507   }
 508 
 509   print_fiber_event_info(jvmti, jni, thread, fiber, event_name);
 510   test_IsFiber(jvmti, jni, thread, fiber, event_name);
 511 
 512   if (strcmp(event_name, "FiberTerminated") == 0) {
 513     return; // skip further testing as GetThreadFiber can return NULL
 514   }
 515 
 516   test_GetThreadFiber(jvmti, jni, thread, fiber, event_name);
 517   test_GetFiberThread(jvmti, jni, thread, fiber, event_name);
 518 
 519   if (strcmp(event_name, "FiberScheduled") == 0) {
 520     return; // skip testing of GetFiberFrame* for FiberScheduled events
 521   }
 522   int frame_count = test_GetFiberFrameCount(jvmti, jni, thread, fiber, event_name);
 523   test_GetFiberFrameLocation(jvmti, jni, thread, fiber, event_name, frame_count);
 524   test_GetFiberStackTrace(jvmti, jni, thread, fiber, event_name, frame_count);
 525 }
 526 
 527 static void JNICALL
 528 FiberScheduled(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber) {
 529   jobject mounted_fiber = NULL;
 530   jvmtiError err;
 531 
 532   lock_events();
 533 
 534   processFiberEvent(jvmti, jni, thread, fiber, "FiberScheduled");
 535 
 536   err = (*jvmti)->GetThreadFiber(jvmti, thread, &mounted_fiber);
 537   if (err != JVMTI_ERROR_NONE) {
 538     (*jni)->FatalError(jni, "FiberScheduled event handler: failed during JVMTI GetThreadFiber call");
 539   }
 540   if (!(*jni)->IsSameObject(jni, mounted_fiber, fiber)) {
 541     (*jni)->FatalError(jni, "FiberScheduled event handler: JVMTI GetThreadFiber failed to return proper fiber");
 542   }
 543 
 544   unlock_events();
 545 }
 546 
 547 static void JNICALL
 548 FiberTerminated(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber) {
 549   jobject mounted_fiber = NULL;
 550   jvmtiError err;
 551 
 552   lock_events();
 553 
 554   processFiberEvent(jvmti, jni, thread, fiber, "FiberTerminated");
 555 
 556   err = (*jvmti)->GetThreadFiber(jvmti, thread, &mounted_fiber);
 557   if (err != JVMTI_ERROR_NONE) {
 558     (*jni)->FatalError(jni, "FiberTerminated event handler: failed during JVMTI GetThreadFiber call");
 559   }
 560   if (mounted_fiber != NULL) {
 561     (*jni)->FatalError(jni, "FiberTerminated event handler: JVMTI GetThreadFiber failed to return NULL fiber");
 562   }
 563 
 564   unlock_events();
 565 }
 566 
 567 static void JNICALL
 568 FiberMount(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber) {
 569   lock_events();
 570   processFiberEvent(jvmti, jni, thread, fiber, "FiberMount");
 571   unlock_events();
 572 }
 573 
 574 static void JNICALL
 575 FiberUnmount(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject fiber) {
 576   lock_events();
 577   processFiberEvent(jvmti, jni, thread, fiber, "FiberUnmount");
 578   unlock_events();
 579 }
 580 
 581 static void JNICALL
 582 ContinuationRun(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jint frames_count) {
 583   lock_events();
 584   print_cont_event_info(jvmti, jni, thread, frames_count, "ContinuationRun");
 585   unlock_events();
 586 }
 587 
 588 static void JNICALL
 589 ContinuationYield(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jint frames_count) {
 590   lock_events();
 591   print_cont_event_info(jvmti, jni, thread, frames_count, "ContinuationYield");
 592   unlock_events();
 593 }
 594 
 595 extern JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options,
 596                                            void *reserved) {
 597   jvmtiEventCallbacks callbacks;
 598   jvmtiCapabilities caps;
 599   jvmtiError err;
 600 
 601   printf("Agent_OnLoad started\n");
 602   if ((*jvm)->GetEnv(jvm, (void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
 603     return JNI_ERR;
 604   }
 605 
 606   if (strcmp(options, "EnableContinuationEvents") == 0) {
 607     continuation_events_enabled = JNI_TRUE;
 608   } else if (strcmp(options, "DisableContinuationEvents") == 0) {
 609     continuation_events_enabled = JNI_FALSE;
 610   } else {
 611     printf("bad option passed to Agent_OnLoad: \"%s\"\n", options);
 612     return 2;
 613   }
 614 
 615   memset(&callbacks, 0, sizeof(callbacks));
 616   callbacks.FiberScheduled  = &FiberScheduled;
 617   callbacks.FiberTerminated = &FiberTerminated;
 618   callbacks.FiberMount   = &FiberMount;
 619   callbacks.FiberUnmount = &FiberUnmount;
 620   callbacks.ContinuationRun   = &ContinuationRun;
 621   callbacks.ContinuationYield = &ContinuationYield;
 622 
 623   memset(&caps, 0, sizeof(caps));
 624   caps.can_support_fibers = 1;
 625   if (continuation_events_enabled) {
 626     caps.can_support_continuations = 1;
 627   }
 628   err = (*jvmti)->AddCapabilities(jvmti, &caps);
 629   if (err != JVMTI_ERROR_NONE) {
 630     printf("error in JVMTI AddCapabilities: %d\n", err);
 631   }
 632 
 633   err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(jvmtiEventCallbacks));
 634   if (err != JVMTI_ERROR_NONE) {
 635     printf("error in JVMTI SetEventCallbacks: %d\n", err);
 636   }
 637 
 638   err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIBER_SCHEDULED, NULL);
 639   if (err != JVMTI_ERROR_NONE) {
 640     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 641   }
 642 
 643   err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIBER_TERMINATED, NULL);
 644   if (err != JVMTI_ERROR_NONE) {
 645     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 646   }
 647 
 648   err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIBER_MOUNT, NULL);
 649   if (err != JVMTI_ERROR_NONE) {
 650     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 651   }
 652 
 653   err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIBER_UNMOUNT, NULL);
 654   if (err != JVMTI_ERROR_NONE) {
 655     printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 656   }
 657 
 658   if (continuation_events_enabled) {
 659     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CONTINUATION_RUN, NULL);
 660     if (err != JVMTI_ERROR_NONE) {
 661       printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 662     }
 663 
 664     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CONTINUATION_YIELD, NULL);
 665     if (err != JVMTI_ERROR_NONE) {
 666       printf("error in JVMTI SetEventNotificationMode: %d\n", err);
 667     }
 668   }
 669 
 670   (*jvmti)->CreateRawMonitor(jvmti, "Events Monitor", &events_monitor);
 671   printf("Agent_OnLoad finished\n");
 672   return 0;
 673 }
 674 
 675 #ifdef __cplusplus
 676 }
 677 #endif