1 /*
   2  * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  *
   8  *   - Redistributions of source code must retain the above copyright
   9  *     notice, this list of conditions and the following disclaimer.
  10  *
  11  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 /*
  33  * This source code is provided to illustrate the usage of a given feature
  34  * or technique and has been deliberately simplified. Additional steps
  35  * required for a production-quality application, such as security checks,
  36  * input validation and proper error handling, might not be present in
  37  * this sample code.
  38  */
  39 
  40 
  41 #include "stdlib.h"
  42 
  43 #include "minst.h"
  44 #include "java_crw_demo.h"
  45 
  46 
  47 /* ------------------------------------------------------------------- */
  48 /* Some constant maximum sizes */
  49 
  50 #define MAX_TOKEN_LENGTH        80
  51 #define MAX_METHOD_NAME_LENGTH  256
  52 
  53 /* Some constant names that tie to Java class/method names.
  54  *    We assume the Java class whose static methods we will be calling
  55  *    looks like:
  56  *
  57  * public class Minst {
  58  *     private static int engaged;
  59  *     private static native void _method_entry(Object thr, int cnum, int mnum);
  60  *     public static void method_entry(int cnum, int mnum)
  61  *     {
  62  *         ...
  63  *     }
  64  * }
  65  *
  66  */
  67 
  68 #define MINST_class        Minst            /* Name of class we are using */
  69 #define MINST_entry        method_entry    /* Name of java entry method */
  70 #define MINST_engaged      engaged         /* Name of java static field */
  71 
  72 /* C macros to create strings from tokens */
  73 #define _STRING(s) #s
  74 #define STRING(s) _STRING(s)
  75 
  76 /* ------------------------------------------------------------------- */
  77 
  78 /* Global agent data structure */
  79 
  80 typedef struct {
  81     /* JVMTI Environment */
  82     jvmtiEnv      *jvmti;
  83     jboolean       vm_is_dead;
  84     jboolean       vm_is_started;
  85     /* Data access Lock */
  86     jrawMonitorID  lock;
  87     /* Options */
  88     char           *include;
  89     char           *exclude;
  90     /* Class Count/ID */
  91     jint            ccount;
  92 } GlobalAgentData;
  93 
  94 static GlobalAgentData *gdata;
  95 
  96 /* Enter a critical section by doing a JVMTI Raw Monitor Enter */
  97 static void
  98 enter_critical_section(jvmtiEnv *jvmti)
  99 {
 100     jvmtiError error;
 101 
 102     error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);
 103     check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
 104 }
 105 
 106 /* Exit a critical section by doing a JVMTI Raw Monitor Exit */
 107 static void
 108 exit_critical_section(jvmtiEnv *jvmti)
 109 {
 110     jvmtiError error;
 111 
 112     error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);
 113     check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
 114 }
 115 
 116 /* Callback for JVMTI_EVENT_VM_START */
 117 static void JNICALL
 118 cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)
 119 {
 120     enter_critical_section(jvmti); {
 121         /* Indicate VM has started */
 122         gdata->vm_is_started = JNI_TRUE;
 123     } exit_critical_section(jvmti);
 124 }
 125 
 126 /* Callback for JVMTI_EVENT_VM_INIT */
 127 static void JNICALL
 128 cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
 129 {
 130     enter_critical_section(jvmti); {
 131         jclass   klass;
 132         jfieldID field;
 133 
 134         /* Register Natives for class whose methods we use */
 135         klass = (*env)->FindClass(env, STRING(MINST_class));
 136         if ( klass == NULL ) {
 137             fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",
 138                         STRING(MINST_class));
 139         }
 140 
 141         /* Engage calls. */
 142         field = (*env)->GetStaticFieldID(env, klass, STRING(MINST_engaged), "I");
 143         if ( field == NULL ) {
 144             fatal_error("ERROR: JNI: Cannot get field from %s\n",
 145                         STRING(MINST_class));
 146         }
 147         (*env)->SetStaticIntField(env, klass, field, 1);
 148     } exit_critical_section(jvmti);
 149 }
 150 
 151 /* Callback for JVMTI_EVENT_VM_DEATH */
 152 static void JNICALL
 153 cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
 154 {
 155     enter_critical_section(jvmti); {
 156         jclass   klass;
 157         jfieldID field;
 158 
 159         /* The VM has died. */
 160         stdout_message("VMDeath\n");
 161 
 162         /* Disengage calls in MINST_class. */
 163         klass = (*env)->FindClass(env, STRING(MINST_class));
 164         if ( klass == NULL ) {
 165             fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",
 166                         STRING(MINST_class));
 167         }
 168         field = (*env)->GetStaticFieldID(env, klass, STRING(MINST_engaged), "I");
 169         if ( field == NULL ) {
 170             fatal_error("ERROR: JNI: Cannot get field from %s\n",
 171                         STRING(MINST_class));
 172         }
 173         (*env)->SetStaticIntField(env, klass, field, -1);
 174 
 175         /* The critical section here is important to hold back the VM death
 176          *    until all other callbacks have completed.
 177          */
 178 
 179         /* Since this critical section could be holding up other threads
 180          *   in other event callbacks, we need to indicate that the VM is
 181          *   dead so that the other callbacks can short circuit their work.
 182          *   We don't expect any further events after VmDeath but we do need
 183          *   to be careful that existing threads might be in our own agent
 184          *   callback code.
 185          */
 186         gdata->vm_is_dead = JNI_TRUE;
 187 
 188     } exit_critical_section(jvmti);
 189 
 190 }
 191 
 192 /* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
 193 static void JNICALL
 194 cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,
 195                 jclass class_being_redefined, jobject loader,
 196                 const char* name, jobject protection_domain,
 197                 jint class_data_len, const unsigned char* class_data,
 198                 jint* new_class_data_len, unsigned char** new_class_data)
 199 {
 200     enter_critical_section(jvmti); {
 201         /* It's possible we get here right after VmDeath event, be careful */
 202         if ( !gdata->vm_is_dead ) {
 203 
 204             const char *classname;
 205 
 206             /* Name could be NULL */
 207             if ( name == NULL ) {
 208                 classname = java_crw_demo_classname(class_data, class_data_len,
 209                         NULL);
 210                 if ( classname == NULL ) {
 211                     fatal_error("ERROR: No classname inside classfile\n");
 212                 }
 213             } else {
 214                 classname = strdup(name);
 215                 if ( classname == NULL ) {
 216                     fatal_error("ERROR: Out of malloc memory\n");
 217                 }
 218             }
 219 
 220             *new_class_data_len = 0;
 221             *new_class_data     = NULL;
 222 
 223             /* The tracker class itself? */
 224             if ( interested((char*)classname, "", gdata->include, gdata->exclude)
 225                   &&  strcmp(classname, STRING(MINST_class)) != 0 ) {
 226                 jint           cnum;
 227                 int            system_class;
 228                 unsigned char *new_image;
 229                 long           new_length;
 230 
 231                 /* Get unique number for every class file image loaded */
 232                 cnum = gdata->ccount++;
 233 
 234                 /* Is it a system class? If the class load is before VmStart
 235                  *   then we will consider it a system class that should
 236                  *   be treated carefully. (See java_crw_demo)
 237                  */
 238                 system_class = 0;
 239                 if ( !gdata->vm_is_started ) {
 240                     system_class = 1;
 241                 }
 242 
 243                 new_image = NULL;
 244                 new_length = 0;
 245 
 246                 /* Call the class file reader/write demo code */
 247                 java_crw_demo(cnum,
 248                     classname,
 249                     class_data,
 250                     class_data_len,
 251                     system_class,
 252                     STRING(MINST_class), "L" STRING(MINST_class) ";",
 253                     STRING(MINST_entry), "(II)V",
 254                     NULL, NULL,
 255                     NULL, NULL,
 256                     NULL, NULL,
 257                     &new_image,
 258                     &new_length,
 259                     NULL,
 260                     NULL);
 261 
 262                 /* If we got back a new class image, return it back as "the"
 263                  *   new class image. This must be JVMTI Allocate space.
 264                  */
 265                 if ( new_length > 0 ) {
 266                     unsigned char *jvmti_space;
 267 
 268                     jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length);
 269                     (void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);
 270                     *new_class_data_len = (jint)new_length;
 271                     *new_class_data     = jvmti_space; /* VM will deallocate */
 272                 }
 273 
 274                 /* Always free up the space we get from java_crw_demo() */
 275                 if ( new_image != NULL ) {
 276                     (void)free((void*)new_image); /* Free malloc() space with free() */
 277                 }
 278             }
 279             (void)free((void*)classname);
 280         }
 281     } exit_critical_section(jvmti);
 282 }
 283 
 284 /* Parse the options for this minst agent */
 285 static void
 286 parse_agent_options(char *options)
 287 {
 288     char token[MAX_TOKEN_LENGTH];
 289     char *next;
 290 
 291     /* Parse options and set flags in gdata */
 292     if ( options==NULL ) {
 293         return;
 294     }
 295 
 296     /* Get the first token from the options string. */
 297     next = get_token(options, ",=", token, sizeof(token));
 298 
 299     /* While not at the end of the options string, process this option. */
 300     while ( next != NULL ) {
 301         if ( strcmp(token,"help")==0 ) {
 302             stdout_message("The minst JVMTI demo agent\n");
 303             stdout_message("\n");
 304             stdout_message(" java -agent:minst[=options] ...\n");
 305             stdout_message("\n");
 306             stdout_message("The options are comma separated:\n");
 307             stdout_message("\t help\t\t\t Print help information\n");
 308             stdout_message("\t include=item\t\t Only these classes/methods\n");
 309             stdout_message("\t exclude=item\t\t Exclude these classes/methods\n");
 310             stdout_message("\n");
 311             stdout_message("item\t Qualified class and/or method names\n");
 312             stdout_message("\t\t e.g. (*.<init>;Foobar.method;sun.*)\n");
 313             stdout_message("\n");
 314             exit(0);
 315         } else if ( strcmp(token,"include")==0 ) {
 316             int   used;
 317             int   maxlen;
 318 
 319             maxlen = MAX_METHOD_NAME_LENGTH;
 320             if ( gdata->include == NULL ) {
 321                 gdata->include = (char*)calloc(maxlen+1, 1);
 322                 used = 0;
 323             } else {
 324                 used  = (int)strlen(gdata->include);
 325                 gdata->include[used++] = ',';
 326                 gdata->include[used] = 0;
 327                 gdata->include = (char*)
 328                              realloc((void*)gdata->include, used+maxlen+1);
 329             }
 330             if ( gdata->include == NULL ) {
 331                 fatal_error("ERROR: Out of malloc memory\n");
 332             }
 333             /* Add this item to the list */
 334             next = get_token(next, ",=", gdata->include+used, maxlen);
 335             /* Check for token scan error */
 336             if ( next==NULL ) {
 337                 fatal_error("ERROR: include option error\n");
 338             }
 339         } else if ( strcmp(token,"exclude")==0 ) {
 340             int   used;
 341             int   maxlen;
 342 
 343             maxlen = MAX_METHOD_NAME_LENGTH;
 344             if ( gdata->exclude == NULL ) {
 345                 gdata->exclude = (char*)calloc(maxlen+1, 1);
 346                 used = 0;
 347             } else {
 348                 used  = (int)strlen(gdata->exclude);
 349                 gdata->exclude[used++] = ',';
 350                 gdata->exclude[used] = 0;
 351                 gdata->exclude = (char*)
 352                              realloc((void*)gdata->exclude, used+maxlen+1);
 353             }
 354             if ( gdata->exclude == NULL ) {
 355                 fatal_error("ERROR: Out of malloc memory\n");
 356             }
 357             /* Add this item to the list */
 358             next = get_token(next, ",=", gdata->exclude+used, maxlen);
 359             /* Check for token scan error */
 360             if ( next==NULL ) {
 361                 fatal_error("ERROR: exclude option error\n");
 362             }
 363         } else if ( token[0]!=0 ) {
 364             /* We got a non-empty token and we don't know what it is. */
 365             fatal_error("ERROR: Unknown option: %s\n", token);
 366         }
 367         /* Get the next token (returns NULL if there are no more) */
 368         next = get_token(next, ",=", token, sizeof(token));
 369     }
 370 }
 371 
 372 /* Agent_OnLoad: This is called immediately after the shared library is
 373  *   loaded. This is the first code executed.
 374  */
 375 JNIEXPORT jint JNICALL
 376 DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 377 {
 378     static GlobalAgentData data;
 379     jvmtiEnv              *jvmti;
 380     jvmtiError             error;
 381     jint                   res;
 382     jvmtiCapabilities      capabilities;
 383     jvmtiEventCallbacks    callbacks;
 384 
 385     /* Setup initial global agent data area
 386      *   Use of static/extern data should be handled carefully here.
 387      *   We need to make sure that we are able to cleanup after ourselves
 388      *     so anything allocated in this library needs to be freed in
 389      *     the Agent_OnUnload() function.
 390      */
 391     (void)memset((void*)&data, 0, sizeof(data));
 392     gdata = &data;
 393 
 394     /* First thing we need to do is get the jvmtiEnv* or JVMTI environment */
 395     res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);
 396     if (res != JNI_OK) {
 397         /* This means that the VM was unable to obtain this version of the
 398          *   JVMTI interface, this is a fatal error.
 399          */
 400         fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"
 401                 " is your JDK a 5.0 or newer version?"
 402                 " JNIEnv's GetEnv() returned %d\n",
 403                JVMTI_VERSION_1, res);
 404     }
 405 
 406     /* Here we save the jvmtiEnv* for Agent_OnUnload(). */
 407     gdata->jvmti = jvmti;
 408 
 409     /* Parse any options supplied on java command line */
 410     parse_agent_options(options);
 411 
 412     /* Immediately after getting the jvmtiEnv* we need to ask for the
 413      *   capabilities this agent will need. In this case we need to make
 414      *   sure that we can get all class load hooks.
 415      */
 416     (void)memset(&capabilities,0, sizeof(capabilities));
 417     capabilities.can_generate_all_class_hook_events  = 1;
 418     error = (*jvmti)->AddCapabilities(jvmti, &capabilities);
 419     check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");
 420 
 421     /* Next we need to provide the pointers to the callback functions to
 422      *   to this jvmtiEnv*
 423      */
 424     (void)memset(&callbacks,0, sizeof(callbacks));
 425     /* JVMTI_EVENT_VM_START */
 426     callbacks.VMStart           = &cbVMStart;
 427     /* JVMTI_EVENT_VM_INIT */
 428     callbacks.VMInit           = &cbVMInit;
 429     /* JVMTI_EVENT_VM_DEATH */
 430     callbacks.VMDeath           = &cbVMDeath;
 431     /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
 432     callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
 433     error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
 434     check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
 435 
 436     /* At first the only initial events we are interested in are VM
 437      *   initialization, VM death, and Class File Loads.
 438      *   Once the VM is initialized we will request more events.
 439      */
 440     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 441                           JVMTI_EVENT_VM_START, (jthread)NULL);
 442     check_jvmti_error(jvmti, error, "Cannot set event notification");
 443     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 444                           JVMTI_EVENT_VM_INIT, (jthread)NULL);
 445     check_jvmti_error(jvmti, error, "Cannot set event notification");
 446     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 447                           JVMTI_EVENT_VM_DEATH, (jthread)NULL);
 448     check_jvmti_error(jvmti, error, "Cannot set event notification");
 449     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 450                           JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
 451     check_jvmti_error(jvmti, error, "Cannot set event notification");
 452 
 453     /* Here we create a raw monitor for our use in this agent to
 454      *   protect critical sections of code.
 455      */
 456     error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));
 457     check_jvmti_error(jvmti, error, "Cannot create raw monitor");
 458 
 459     /* Add demo jar file to boot classpath */
 460     add_demo_jar_to_bootclasspath(jvmti, "minst");
 461 
 462     /* We return JNI_OK to signify success */
 463     return JNI_OK;
 464 }
 465 
 466 /* Agent_OnUnload: This is called immediately before the shared library is
 467  *   unloaded. This is the last code executed.
 468  */
 469 JNIEXPORT void JNICALL
 470 DEF_Agent_OnUnload(JavaVM *vm)
 471 {
 472     /* Make sure all malloc/calloc/strdup space is freed */
 473     if ( gdata->include != NULL ) {
 474         (void)free((void*)gdata->include);
 475         gdata->include = NULL;
 476     }
 477     if ( gdata->exclude != NULL ) {
 478         (void)free((void*)gdata->exclude);
 479         gdata->exclude = NULL;
 480     }
 481 }