1 /*
   2  * Copyright (c) 2003, 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 <stdlib.h>
  25 #include <stdio.h>
  26 #include <stdarg.h>
  27 #include <string.h>
  28 #include <ctype.h>
  29 
  30 /*************************************************************/
  31 
  32 #include "jvmti.h"
  33 
  34 /*************************************************************/
  35 
  36 #include "nsk_tools.h"
  37 #include "jni_tools.h"
  38 #include "jvmti_tools.h"
  39 #include "JVMTITools.h"
  40 
  41 /*************************************************************/
  42 
  43 extern "C" {
  44 
  45 /*************************************************************/
  46 
  47 #define NSK_JVMTI_WAITTIME 2
  48 
  49 #define NSK_JVMTI_MAX_OPTIONS       10
  50 #define NSK_JVMTI_OPTION_START      '-'
  51 #define NSK_JVMTI_OPTION_VAL_SEP    '='
  52 
  53 #define NSK_JVMTI_OPT_PATH_TO_NEW_BYTE_CODE "pathToNewByteCode"
  54 #define PATH_FORMAT "%s%02d/%s"
  55 #define DIR_NAME "newclass"
  56 
  57 static volatile int redefineAttempted = NSK_FALSE;
  58 static volatile int redefineSucceed = NSK_FALSE;
  59 static volatile int agentFailed = NSK_FALSE;
  60 
  61 static struct {
  62     struct {
  63         int count;
  64         char* names[NSK_JVMTI_MAX_OPTIONS];
  65         char* values[NSK_JVMTI_MAX_OPTIONS];
  66         char* string;
  67     } options;
  68     int waittime;
  69 } context;
  70 
  71 /*************************************************************/
  72 
  73 static int check_option(int dashed, const char name[], const char value[]) {
  74     if (strcmp("verbose", name) == 0) {
  75         if (strlen(value) > 0) {
  76             nsk_complain("nsk_jvmti_parseOptions(): unexpected value in option: %s=%s\n", name, value);
  77             return NSK_FALSE;
  78         }
  79         nsk_setVerboseMode(NSK_TRUE);
  80     } else if (strcmp("trace", name) == 0) {
  81         if (strlen(value) <= 0) {
  82             nsk_complain("nsk_jvmti_parseOptions(): no value for option: %s\n", name);
  83             return NSK_FALSE;
  84         }
  85         if (strcmp("none", value) == 0) {
  86             nsk_setTraceMode(NSK_TRACE_NONE);
  87         } else if (strcmp("before", value) == 0) {
  88             nsk_setTraceMode(NSK_TRACE_BEFORE);
  89         } else if (strcmp("after", value) == 0) {
  90             nsk_setTraceMode(NSK_TRACE_AFTER);
  91         } else if (strcmp("all", value) == 0) {
  92             nsk_setTraceMode(NSK_TRACE_ALL);
  93         } else {
  94             nsk_complain("nsk_jvmti_parseOptions(): uexpected value in option: %s=%s\n", name, value);
  95             return NSK_FALSE;
  96         }
  97         nsk_setVerboseMode(NSK_TRUE);
  98     } else if (strcmp("waittime", name) == 0) {
  99         if (strlen(value) <= 0) {
 100             nsk_complain("nsk_jvmti_parseOptions(): no value for option: %s\n", name);
 101             return NSK_FALSE;
 102         }
 103         {
 104             char* end = NULL;
 105             long n = strtol(value, &end, 10);
 106             if (end == NULL || end == value || *end != '\0') {
 107                 nsk_complain("nsk_jvmti_parseOptions(): not integer value in option: %s=%s\n", name, value);
 108                 return NSK_FALSE;
 109             }
 110             if (n < 0) {
 111                 nsk_complain("nsk_jvmti_parseOptions(): negative value in option: %s=%s\n", name, value);
 112                 return NSK_FALSE;
 113             }
 114             context.waittime = (int)n;
 115         }
 116     } else if (dashed) {
 117         nsk_complain("nsk_jvmti_parseOptions(): unknown option: %c%s\n",
 118                                                         NSK_JVMTI_OPTION_START, name);
 119         return NSK_FALSE;
 120     }
 121     return NSK_TRUE;
 122 }
 123 
 124 static int add_option(const char opt[], int opt_len, const char val[], int val_len) {
 125     char* name;
 126     char* value;
 127 
 128     int success = NSK_TRUE;
 129     int dashed_opt = NSK_FALSE;
 130 
 131     if (opt[0] == NSK_JVMTI_OPTION_START) {
 132         dashed_opt = NSK_TRUE;
 133         opt++;
 134         opt_len--;
 135     }
 136     if (opt_len <= 0) {
 137         nsk_complain("nsk_jvmti_parseOptions(): found empty option\n");
 138         return NSK_FALSE;
 139     }
 140 
 141     name = (char*)malloc(opt_len + 1);
 142     value = (char*)malloc(val_len + 1);
 143 
 144     if (name == NULL || value == NULL) {
 145         nsk_complain("nsk_jvmti_parseOptions(): out of memory\n");
 146         success = NSK_FALSE;
 147     } else {
 148         strncpy(name, opt, opt_len);
 149         name[opt_len] = '\0';
 150         strncpy(value, val, val_len);
 151         value[val_len] = '\0';
 152 
 153         if (!check_option(dashed_opt, name, value)) {
 154             success = NSK_FALSE;
 155         }
 156     }
 157 
 158     if (success) {
 159         if (context.options.count >= NSK_JVMTI_MAX_OPTIONS) {
 160             nsk_complain("nsk_jvmti_parseOptions(): too many options for parsing\n");
 161             success = NSK_FALSE;
 162         } else {
 163             context.options.names[context.options.count] = name;
 164             context.options.values[context.options.count] = value;
 165             context.options.count++;
 166         }
 167     }
 168 
 169     if (!success) {
 170         if (name != NULL)
 171             free(name);
 172         if (value != NULL)
 173             free(value);
 174     }
 175 
 176     return success;
 177 }
 178 
 179 static void nsk_jvmti_free() {
 180     if (context.options.count > 0) {
 181         int i;
 182         for (i = 0; i < context.options.count; i++) {
 183             free(context.options.names[i]);
 184             free(context.options.values[i]);
 185         }
 186         context.options.count = 0;
 187     }
 188     if (context.options.string != NULL) {
 189         free(context.options.string);
 190         context.options.string = NULL;
 191     }
 192 }
 193 
 194 int isOptSep(char c) {
 195     return isspace(c) || c == '~';
 196 }
 197 
 198 
 199 /**
 200  *
 201  * The current option will not perform more than one
 202  * single option which given, this is due to places explained
 203  * in this question.
 204  *
 205  **/
 206 
 207  /*
 208   * This whole play can be reduced with simple StringTokenizer (strtok).
 209   *
 210   */
 211 
 212 int nsk_jvmti_parseOptions(const char options[]) {
 213     size_t len;
 214     const char* opt;
 215     int success = NSK_TRUE;
 216 
 217     context.options.string = NULL;
 218     context.options.count = 0;
 219     context.waittime = 2;
 220 
 221     if (options == NULL)
 222         return NSK_TRUE;
 223 
 224     len = strlen(options);
 225     context.options.string = (char*)malloc(len + 2);
 226 
 227     if (context.options.string == NULL) {
 228             nsk_complain("nsk_jvmti_parseOptions(): out of memory\n");
 229             return NSK_FALSE;
 230     }
 231     strncpy(context.options.string, options, len);
 232     context.options.string[len] = '\0';
 233     context.options.string[len+1] = '\0';
 234 
 235     for (opt = context.options.string; ; ) {
 236         const char* opt_end;
 237         const char* val_sep;
 238         int opt_len=0;
 239         int val_len=0;
 240                 int exit=1;
 241 
 242         while (*opt != '\0' && isOptSep(*opt)) opt++;
 243         if (*opt == '\0') break;
 244 
 245         val_sep = NULL;
 246         /*
 247             This should break when the first option it encounters other wise
 248         */
 249         for (opt_end = opt, opt_len=0; !(*opt_end == '\0' || isOptSep(*opt_end)); opt_end++,opt_len++) {
 250             if (*opt_end == NSK_JVMTI_OPTION_VAL_SEP) {
 251                 val_sep = opt_end;
 252                 exit=0;
 253                 break;
 254             }
 255         }
 256 
 257         if (exit == 1) break;
 258 
 259         /* now scan for the search  for the option value end.
 260 
 261         */
 262         exit =1;
 263         opt_end++;
 264         val_sep++;
 265         /**
 266          * I was expecting this jvmti_parseOptions(),
 267          * should be for multiple options as well.
 268          * If this break is not there then It will expects
 269          * to have. so a space should be sufficient as well.
 270          */
 271         for(val_len=0; !(*opt_end == '\0' || isOptSep(*opt_end)); opt_end++,val_len++) {
 272             //if (*opt_end == NSK_JVMTI_OPTION_START) {
 273             //    break;
 274             //}
 275         }
 276 
 277         if (!add_option(opt, opt_len, val_sep, val_len)) {
 278             success = NSK_FALSE;
 279             break;
 280         }
 281         opt_end++;
 282         opt = opt_end;
 283     }
 284 
 285     if (!success) {
 286         nsk_jvmti_free();
 287     }
 288 
 289     return success;
 290 }
 291 
 292 
 293 /*************************************************************/
 294 
 295 /**
 296  * Returns value of given option name; or NULL if no such option found.
 297  * If search name is NULL then complains an error and returns NULL.
 298  */
 299 const char* nsk_jvmti_findOptionValue(const char name[]) {
 300     int i;
 301 
 302     if (name == NULL) {
 303         nsk_complain("nsk_jvmti_findOptionValue(): option name is NULL\n");
 304         return NULL;
 305     }
 306 
 307     for (i = 0; i < context.options.count; i++) {
 308         if (strcmp(name, context.options.names[i]) == 0)
 309             return context.options.values[i];
 310     }
 311     return NULL;
 312 }
 313 
 314 /**
 315  * Returns string value of given option; or defaultValue if no such option found.
 316  * If options is specified but has empty value then complains an error and returns NULL.
 317  */
 318 const char* nsk_jvmti_findOptionStringValue(const char name[], const char* defaultValue) {
 319     const char* value;
 320 
 321     if (name == NULL) {
 322         nsk_complain("nsk_jvmti_findOptionStringValue(): option name is NULL\n");
 323         return NULL;
 324     }
 325 
 326     value = nsk_jvmti_findOptionValue(name);
 327     if (value == NULL) {
 328         return defaultValue;
 329     }
 330 
 331     if (strlen(value) <= 0) {
 332         nsk_complain("nsk_jvmti_findOptionStringValue(): empty value of option: %s=%s\n",
 333                                                                             name, value);
 334         return NULL;
 335     }
 336     return value;
 337 }
 338 
 339 /**
 340  * Returns integer value of given option; or defaultValue if no such option found.
 341  * If options is specified but has no integer value then complains an error and returns -1.
 342  */
 343 int nsk_jvmti_findOptionIntValue(const char name[], int defaultValue) {
 344     const char* value;
 345 
 346     if (name == NULL) {
 347         nsk_complain("nsk_jvmti_findOptionIntValue(): option name is NULL\n");
 348         return -1;
 349     }
 350 
 351     value = nsk_jvmti_findOptionValue(name);
 352     if (value == NULL) {
 353         return defaultValue;
 354     }
 355 
 356     if (strlen(value) <= 0) {
 357         nsk_complain("nsk_jvmti_findOptionIntValue(): empty value of option: %s=%s\n",
 358                                                                             name, value);
 359         return -1;
 360     }
 361 
 362     {
 363         char* endptr = NULL;
 364         int n = strtol(value, &endptr, 10);
 365 
 366         if (endptr == NULL || *endptr != '\0') {
 367             nsk_complain("nsk_jvmti_findOptionIntValue(): not integer value of option: %s=%s\n",
 368                                                                             name, value);
 369             return -1;
 370         }
 371         return n;
 372     }
 373 }
 374 
 375 /**
 376  * Returns number of parsed options.
 377  */
 378 int nsk_jvmti_getOptionsCount() {
 379     return context.options.count;
 380 }
 381 
 382 /**
 383  * Returns name of i-th parsed option.
 384  * If no such option then complains an error and returns NULL.
 385  */
 386 const char* nsk_jvmti_getOptionName(int i) {
 387     if (i < 0 || i >= context.options.count) {
 388         nsk_complain("nsk_jvmti_getOptionName(): option index out of bounds: %d\n", i);
 389         return NULL;
 390     }
 391     return context.options.names[i];
 392 }
 393 
 394 /**
 395  * Returns value of i-th parsed option.
 396  * If no such option then complains an error and returns NULL.
 397  */
 398 const char* nsk_jvmti_getOptionValue(int i) {
 399     if (i < 0 || i >= context.options.count) {
 400         nsk_complain("nsk_jvmti_getOptionValue(): option index out of bounds: %d\n", i);
 401         return NULL;
 402     }
 403     return context.options.values[i];
 404 }
 405 
 406 /*************************************************************/
 407 
 408 /**
 409  * Returns value of -waittime option or default value if not specified.
 410  */
 411 int  nsk_jvmti_getWaitTime() {
 412     return context.waittime;
 413 }
 414 
 415 /**
 416  * Sets specified waittime value.
 417  */
 418 void nsk_jvmti_setWaitTime(int waittime) {
 419     context.waittime = waittime;
 420 }
 421 
 422 /*************************************************************/
 423 
 424 int nsk_jvmti_lverify(int positive, jvmtiError error, jvmtiError expected,
 425                         const char file[], int line, const char format[], ...)
 426 {
 427     int failure=0;
 428     int negative = !positive;
 429     int errorCode = (int)error;
 430     const char* errorName = TranslateError(error);
 431     va_list ap;
 432     va_start(ap,format);
 433     nsk_lvtrace(NSK_TRACE_AFTER,file,line,format,ap);
 434     if (negative || expected != JVMTI_ERROR_NONE)
 435         nsk_ltrace(NSK_TRACE_AFTER,file,line,
 436             "  jvmti error: code=%d, name=%s\n",errorCode,errorName);
 437     if ((error == expected) == negative) {
 438         nsk_lvcomplain(file,line,format,ap);
 439         nsk_printf("#   jvmti error: code=%d, name=%s\n",errorCode,errorName);
 440         if (expected != JVMTI_ERROR_NONE)
 441             nsk_printf("#   error expected: code=%d, name=%s\n",
 442                 expected, TranslateError(expected));
 443         failure=1;
 444     };
 445     va_end(ap);
 446     return !failure;
 447 }
 448 
 449 /*************************************************************/
 450 
 451 JNIEXPORT jstring JNICALL
 452 Java_nsk_share_jvmti_ArgumentHandler_getAgentOptionsString(JNIEnv *jni, jobject obj) {
 453     jstring str_obj = NULL;
 454 
 455     if (!NSK_JNI_VERIFY(jni, (str_obj = jni->NewStringUTF(context.options.string)) != NULL)) {
 456         return NULL;
 457     }
 458     return str_obj;
 459 }
 460 
 461 /*************************************************************/
 462 
 463 /**
 464   * This method will try to redefine the class (classToRedefine) by loading
 465   * physical file.  <b>pathToNewByteCode</b> option which is passed
 466   * on OnLoad Phase also used.
 467   *
 468   * So This method will do a file read pathToByteCode+fileName+.class (total path).
 469   * Constrcuts a class objects and does a redefine of the class.
 470   * On successfull redefine this method will return eaither JNI_TRUE or JNI_FALSE
 471   *
 472   * Hint::
 473   *     1)
 474   *      If there are many redefine on same testcase, then please try to use
 475   *      integer value (newclass00, newclass01, newclass02 , ....) way.
 476   *
 477   *     2) When you compile these please do keep, a metatag on testcase as
 478   *     # build : native classes classes.redef
 479   *
 480   *     3) When you do build these classes are psysically located in build as.
 481   *
 482   * TESTBASE/bin/newclass0* directory.
 483   * eg: for nks/jvmti/scenarios/hotswap/HS204/hs204t001 you should see
 484   * TESTBASE/bin/newclass0* /nsk/hotswap/HS204/hs204t001/MyClass.class
 485   *
 486   */
 487 
 488 int nsk_jvmti_redefineClass(jvmtiEnv * jvmti,
 489         jclass classToRedefine,
 490         const char * fileName) {
 491     redefineAttempted = NSK_TRUE;
 492     if ( nsk_jvmti_findOptionValue(NSK_JVMTI_OPT_PATH_TO_NEW_BYTE_CODE)
 493             == NULL  ) {
 494         nsk_printf("#   error expected: %s \n",
 495                 NSK_JVMTI_OPT_PATH_TO_NEW_BYTE_CODE );
 496         nsk_printf("Hint :: missing java -agentlib:agentlib=%s=DirName, ($TESTBASE/bin) \n",
 497                 NSK_JVMTI_OPT_PATH_TO_NEW_BYTE_CODE );
 498         return NSK_FALSE;
 499     }
 500     if ( fileName == NULL) {
 501         nsk_printf("# error file name expected did not found \n");
 502         return NSK_FALSE;
 503     }
 504     {
 505         char file [1024];
 506         //= "DEFAULT";
 507         sprintf(file,"%s/%s.class",
 508                 nsk_jvmti_findOptionValue(NSK_JVMTI_OPT_PATH_TO_NEW_BYTE_CODE),
 509                 fileName);
 510         nsk_printf("# info :: File = %s \n",file);
 511 
 512         {
 513             FILE *bytecode;
 514             unsigned char * classBytes;
 515             jvmtiError error;
 516             jint size;
 517 
 518             bytecode = fopen(file, "rb");
 519             error= JVMTI_ERROR_NONE;
 520             if ( bytecode == NULL ) {
 521                 nsk_printf("# error **Agent::error opening file %s \n",file);
 522                 return NSK_FALSE;
 523             }
 524 
 525             nsk_printf("#  info **Agent:: opening file %s \n",file);
 526             fseek(bytecode, 0, SEEK_END);
 527             size = ftell(bytecode);
 528             nsk_printf("# info file size= %ld\n",ftell(bytecode));
 529             rewind(bytecode);
 530             error = jvmti->Allocate(size,&classBytes);
 531             if ( error != JVMTI_ERROR_NONE) {
 532                 nsk_printf(" Failed to create memory %s \n",TranslateError(error));
 533                 return NSK_FALSE;
 534             }
 535 
 536             if ( ((jint) fread( classBytes, 1,size,bytecode )) != size ) {
 537                 nsk_printf(" # error failed to read all the bytes , could be less or more \n");
 538                 return NSK_FALSE;
 539             } else {
 540                 nsk_printf(" File red completely \n");
 541             }
 542             fclose(bytecode);
 543             {
 544                 jvmtiClassDefinition classDef;
 545                 classDef.klass = classToRedefine;
 546                 classDef.class_byte_count= size;
 547                 classDef.class_bytes = classBytes;
 548                 error = jvmti->RedefineClasses(1,&classDef);
 549                 if ( error != JVMTI_ERROR_NONE ) {
 550                     nsk_printf("# error occured while redefining %s ",
 551                             TranslateError(error) );
 552                     return NSK_FALSE;
 553                 }
 554             }
 555         }
 556     }
 557     redefineSucceed= NSK_TRUE;
 558     return NSK_TRUE;
 559 }
 560 
 561 JNIEXPORT jboolean JNICALL
 562 Java_nsk_share_jvmti_RedefineAgent_redefineAttempted(JNIEnv *jni,  jobject obj) {
 563 
 564     if (redefineAttempted == NSK_TRUE) {
 565         return JNI_TRUE;
 566     }else {
 567         return JNI_FALSE;
 568     }
 569 }
 570 
 571 
 572 JNIEXPORT jboolean JNICALL
 573 Java_nsk_share_jvmti_RedefineAgent_isRedefined(JNIEnv * jni,  jobject obj ) {
 574 
 575     if (redefineSucceed == NSK_TRUE) {
 576         return JNI_TRUE;
 577     }else {
 578         return JNI_FALSE;
 579     }
 580 }
 581 /**
 582  * This jni method is a Java wrapper for agent status.
 583  */
 584 JNIEXPORT jboolean JNICALL
 585 Java_nsk_share_jvmti_RedefineAgent_agentStatus(JNIEnv * jni,  jobject obj ) {
 586     if ( agentFailed == NSK_TRUE) {
 587         return JNI_FALSE;
 588     } else {
 589         return JNI_TRUE;
 590     }
 591 }
 592 
 593 void nsk_jvmti_getFileName(int redefineCnt, const char * dir,  char * buf, size_t bufsize) {
 594    snprintf(buf, bufsize, PATH_FORMAT, DIR_NAME, redefineCnt, dir);
 595    buf[bufsize-1] = '\0';
 596 }
 597 
 598 int nsk_jvmti_enableNotification(jvmtiEnv *jvmti,jvmtiEvent event, jthread thread) {
 599     jvmtiError rc=JVMTI_ERROR_NONE;
 600     rc = jvmti->SetEventNotificationMode(JVMTI_ENABLE, event, thread);
 601     if (rc != JVMTI_ERROR_NONE) {
 602         nsk_printf("# error Failed to set Notification for Event \n ");
 603         return NSK_FALSE;
 604     }
 605     return NSK_TRUE;
 606 }
 607 
 608 int nsk_jvmti_disableNotification(jvmtiEnv *jvmti,jvmtiEvent event, jthread thread) {
 609   jvmtiError rc=JVMTI_ERROR_NONE;
 610   rc = jvmti->SetEventNotificationMode(JVMTI_DISABLE, event, thread);
 611   if (rc != JVMTI_ERROR_NONE) {
 612       nsk_printf(" Failed to disaable Notification for Event ");
 613       return NSK_FALSE;
 614   }
 615   return NSK_TRUE;
 616 }
 617 
 618 void nsk_jvmti_agentFailed() {
 619     agentFailed = NSK_TRUE;
 620 }
 621 
 622 int isThreadExpected(jvmtiEnv *jvmti, jthread thread) {
 623     static const char *vm_jfr_buffer_thread_name = "VM JFR Buffer Thread";
 624     static const char *jfr_request_timer_thread_name = "JFR request timer";
 625 
 626     jvmtiThreadInfo threadinfo;
 627     NSK_JVMTI_VERIFY(jvmti->GetThreadInfo(thread, &threadinfo));
 628 
 629     if (strcmp(threadinfo.name, vm_jfr_buffer_thread_name) == 0)
 630         return 0;
 631 
 632     if (strcmp(threadinfo.name, jfr_request_timer_thread_name) == 0)
 633         return 0;
 634 
 635     return 1;
 636 }
 637 
 638 jint createRawMonitor(jvmtiEnv *env, const char *name, jrawMonitorID *monitor) {
 639     jvmtiError error = env->CreateRawMonitor(name, monitor);
 640     if (!NSK_JVMTI_VERIFY(error)) {
 641         return JNI_ERR;
 642     }
 643     return JNI_OK;
 644 }
 645 
 646 void exitOnError(jvmtiError error) {
 647     if (!NSK_JVMTI_VERIFY(error)) {
 648         exit(error);
 649     }
 650 }
 651 
 652 void rawMonitorEnter(jvmtiEnv *env, jrawMonitorID monitor) {
 653     jvmtiError error = env->RawMonitorEnter(monitor);
 654     exitOnError(error);
 655 }
 656 
 657 void rawMonitorExit(jvmtiEnv *env, jrawMonitorID monitor) {
 658     jvmtiError error = env->RawMonitorExit(monitor);
 659     exitOnError(error);
 660 }
 661 
 662 void rawMonitorNotify(jvmtiEnv *env, jrawMonitorID monitor) {
 663     jvmtiError error = env->RawMonitorNotify(monitor);
 664     exitOnError(error);
 665 }
 666 
 667 void rawMonitorWait(jvmtiEnv *env, jrawMonitorID monitor, jlong millis) {
 668     jvmtiError error = env->RawMonitorWait(monitor, millis);
 669     exitOnError(error);
 670 }
 671 
 672 void getPhase(jvmtiEnv *env, jvmtiPhase *phase) {
 673     jvmtiError error = env->GetPhase(phase);
 674     exitOnError(error);
 675 }
 676 
 677 /*************************************************************/
 678 
 679 }