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