Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/jvmti/minst/minst.c
38829 views
/*1* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/383940#include "stdlib.h"4142#include "minst.h"43#include "java_crw_demo.h"444546/* ------------------------------------------------------------------- */47/* Some constant maximum sizes */4849#define MAX_TOKEN_LENGTH 8050#define MAX_METHOD_NAME_LENGTH 2565152/* Some constant names that tie to Java class/method names.53* We assume the Java class whose static methods we will be calling54* looks like:55*56* public class Minst {57* private static int engaged;58* private static native void _method_entry(Object thr, int cnum, int mnum);59* public static void method_entry(int cnum, int mnum)60* {61* ...62* }63* }64*65*/6667#define MINST_class Minst /* Name of class we are using */68#define MINST_entry method_entry /* Name of java entry method */69#define MINST_engaged engaged /* Name of java static field */7071/* C macros to create strings from tokens */72#define _STRING(s) #s73#define STRING(s) _STRING(s)7475/* ------------------------------------------------------------------- */7677/* Global agent data structure */7879typedef struct {80/* JVMTI Environment */81jvmtiEnv *jvmti;82jboolean vm_is_dead;83jboolean vm_is_started;84/* Data access Lock */85jrawMonitorID lock;86/* Options */87char *include;88char *exclude;89/* Class Count/ID */90jint ccount;91} GlobalAgentData;9293static GlobalAgentData *gdata;9495/* Enter a critical section by doing a JVMTI Raw Monitor Enter */96static void97enter_critical_section(jvmtiEnv *jvmti)98{99jvmtiError error;100101error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);102check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");103}104105/* Exit a critical section by doing a JVMTI Raw Monitor Exit */106static void107exit_critical_section(jvmtiEnv *jvmti)108{109jvmtiError error;110111error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);112check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");113}114115/* Callback for JVMTI_EVENT_VM_START */116static void JNICALL117cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)118{119enter_critical_section(jvmti); {120/* Indicate VM has started */121gdata->vm_is_started = JNI_TRUE;122} exit_critical_section(jvmti);123}124125/* Callback for JVMTI_EVENT_VM_INIT */126static void JNICALL127cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)128{129enter_critical_section(jvmti); {130jclass klass;131jfieldID field;132133/* Register Natives for class whose methods we use */134klass = (*env)->FindClass(env, STRING(MINST_class));135if ( klass == NULL ) {136fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",137STRING(MINST_class));138}139140/* Engage calls. */141field = (*env)->GetStaticFieldID(env, klass, STRING(MINST_engaged), "I");142if ( field == NULL ) {143fatal_error("ERROR: JNI: Cannot get field from %s\n",144STRING(MINST_class));145}146(*env)->SetStaticIntField(env, klass, field, 1);147} exit_critical_section(jvmti);148}149150/* Callback for JVMTI_EVENT_VM_DEATH */151static void JNICALL152cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)153{154enter_critical_section(jvmti); {155jclass klass;156jfieldID field;157158/* The VM has died. */159stdout_message("VMDeath\n");160161/* Disengage calls in MINST_class. */162klass = (*env)->FindClass(env, STRING(MINST_class));163if ( klass == NULL ) {164fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",165STRING(MINST_class));166}167field = (*env)->GetStaticFieldID(env, klass, STRING(MINST_engaged), "I");168if ( field == NULL ) {169fatal_error("ERROR: JNI: Cannot get field from %s\n",170STRING(MINST_class));171}172(*env)->SetStaticIntField(env, klass, field, -1);173174/* The critical section here is important to hold back the VM death175* until all other callbacks have completed.176*/177178/* Since this critical section could be holding up other threads179* in other event callbacks, we need to indicate that the VM is180* dead so that the other callbacks can short circuit their work.181* We don't expect any further events after VmDeath but we do need182* to be careful that existing threads might be in our own agent183* callback code.184*/185gdata->vm_is_dead = JNI_TRUE;186187} exit_critical_section(jvmti);188189}190191/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */192static void JNICALL193cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,194jclass class_being_redefined, jobject loader,195const char* name, jobject protection_domain,196jint class_data_len, const unsigned char* class_data,197jint* new_class_data_len, unsigned char** new_class_data)198{199enter_critical_section(jvmti); {200/* It's possible we get here right after VmDeath event, be careful */201if ( !gdata->vm_is_dead ) {202203const char *classname;204205/* Name could be NULL */206if ( name == NULL ) {207classname = java_crw_demo_classname(class_data, class_data_len,208NULL);209if ( classname == NULL ) {210fatal_error("ERROR: No classname inside classfile\n");211}212} else {213classname = strdup(name);214if ( classname == NULL ) {215fatal_error("ERROR: Out of malloc memory\n");216}217}218219*new_class_data_len = 0;220*new_class_data = NULL;221222/* The tracker class itself? */223if ( interested((char*)classname, "", gdata->include, gdata->exclude)224&& strcmp(classname, STRING(MINST_class)) != 0 ) {225jint cnum;226int system_class;227unsigned char *new_image;228long new_length;229230/* Get unique number for every class file image loaded */231cnum = gdata->ccount++;232233/* Is it a system class? If the class load is before VmStart234* then we will consider it a system class that should235* be treated carefully. (See java_crw_demo)236*/237system_class = 0;238if ( !gdata->vm_is_started ) {239system_class = 1;240}241242new_image = NULL;243new_length = 0;244245/* Call the class file reader/write demo code */246java_crw_demo(cnum,247classname,248class_data,249class_data_len,250system_class,251STRING(MINST_class), "L" STRING(MINST_class) ";",252STRING(MINST_entry), "(II)V",253NULL, NULL,254NULL, NULL,255NULL, NULL,256&new_image,257&new_length,258NULL,259NULL);260261/* If we got back a new class image, return it back as "the"262* new class image. This must be JVMTI Allocate space.263*/264if ( new_length > 0 ) {265unsigned char *jvmti_space;266267jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length);268(void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);269*new_class_data_len = (jint)new_length;270*new_class_data = jvmti_space; /* VM will deallocate */271}272273/* Always free up the space we get from java_crw_demo() */274if ( new_image != NULL ) {275(void)free((void*)new_image); /* Free malloc() space with free() */276}277}278(void)free((void*)classname);279}280} exit_critical_section(jvmti);281}282283/* Parse the options for this minst agent */284static void285parse_agent_options(char *options)286{287char token[MAX_TOKEN_LENGTH];288char *next;289290/* Parse options and set flags in gdata */291if ( options==NULL ) {292return;293}294295/* Get the first token from the options string. */296next = get_token(options, ",=", token, sizeof(token));297298/* While not at the end of the options string, process this option. */299while ( next != NULL ) {300if ( strcmp(token,"help")==0 ) {301stdout_message("The minst JVMTI demo agent\n");302stdout_message("\n");303stdout_message(" java -agent:minst[=options] ...\n");304stdout_message("\n");305stdout_message("The options are comma separated:\n");306stdout_message("\t help\t\t\t Print help information\n");307stdout_message("\t include=item\t\t Only these classes/methods\n");308stdout_message("\t exclude=item\t\t Exclude these classes/methods\n");309stdout_message("\n");310stdout_message("item\t Qualified class and/or method names\n");311stdout_message("\t\t e.g. (*.<init>;Foobar.method;sun.*)\n");312stdout_message("\n");313exit(0);314} else if ( strcmp(token,"include")==0 ) {315int used;316int maxlen;317318maxlen = MAX_METHOD_NAME_LENGTH;319if ( gdata->include == NULL ) {320gdata->include = (char*)calloc(maxlen+1, 1);321used = 0;322} else {323used = (int)strlen(gdata->include);324gdata->include[used++] = ',';325gdata->include[used] = 0;326gdata->include = (char*)327realloc((void*)gdata->include, used+maxlen+1);328}329if ( gdata->include == NULL ) {330fatal_error("ERROR: Out of malloc memory\n");331}332/* Add this item to the list */333next = get_token(next, ",=", gdata->include+used, maxlen);334/* Check for token scan error */335if ( next==NULL ) {336fatal_error("ERROR: include option error\n");337}338} else if ( strcmp(token,"exclude")==0 ) {339int used;340int maxlen;341342maxlen = MAX_METHOD_NAME_LENGTH;343if ( gdata->exclude == NULL ) {344gdata->exclude = (char*)calloc(maxlen+1, 1);345used = 0;346} else {347used = (int)strlen(gdata->exclude);348gdata->exclude[used++] = ',';349gdata->exclude[used] = 0;350gdata->exclude = (char*)351realloc((void*)gdata->exclude, used+maxlen+1);352}353if ( gdata->exclude == NULL ) {354fatal_error("ERROR: Out of malloc memory\n");355}356/* Add this item to the list */357next = get_token(next, ",=", gdata->exclude+used, maxlen);358/* Check for token scan error */359if ( next==NULL ) {360fatal_error("ERROR: exclude option error\n");361}362} else if ( token[0]!=0 ) {363/* We got a non-empty token and we don't know what it is. */364fatal_error("ERROR: Unknown option: %s\n", token);365}366/* Get the next token (returns NULL if there are no more) */367next = get_token(next, ",=", token, sizeof(token));368}369}370371/* Agent_OnLoad: This is called immediately after the shared library is372* loaded. This is the first code executed.373*/374JNIEXPORT jint JNICALL375Agent_OnLoad(JavaVM *vm, char *options, void *reserved)376{377static GlobalAgentData data;378jvmtiEnv *jvmti;379jvmtiError error;380jint res;381jvmtiCapabilities capabilities;382jvmtiEventCallbacks callbacks;383384/* Setup initial global agent data area385* Use of static/extern data should be handled carefully here.386* We need to make sure that we are able to cleanup after ourselves387* so anything allocated in this library needs to be freed in388* the Agent_OnUnload() function.389*/390(void)memset((void*)&data, 0, sizeof(data));391gdata = &data;392393/* First thing we need to do is get the jvmtiEnv* or JVMTI environment */394res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);395if (res != JNI_OK) {396/* This means that the VM was unable to obtain this version of the397* JVMTI interface, this is a fatal error.398*/399fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"400" is your JDK a 5.0 or newer version?"401" JNIEnv's GetEnv() returned %d\n",402JVMTI_VERSION_1, res);403}404405/* Here we save the jvmtiEnv* for Agent_OnUnload(). */406gdata->jvmti = jvmti;407408/* Parse any options supplied on java command line */409parse_agent_options(options);410411/* Immediately after getting the jvmtiEnv* we need to ask for the412* capabilities this agent will need. In this case we need to make413* sure that we can get all class load hooks.414*/415(void)memset(&capabilities,0, sizeof(capabilities));416capabilities.can_generate_all_class_hook_events = 1;417error = (*jvmti)->AddCapabilities(jvmti, &capabilities);418check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");419420/* Next we need to provide the pointers to the callback functions to421* to this jvmtiEnv*422*/423(void)memset(&callbacks,0, sizeof(callbacks));424/* JVMTI_EVENT_VM_START */425callbacks.VMStart = &cbVMStart;426/* JVMTI_EVENT_VM_INIT */427callbacks.VMInit = &cbVMInit;428/* JVMTI_EVENT_VM_DEATH */429callbacks.VMDeath = &cbVMDeath;430/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */431callbacks.ClassFileLoadHook = &cbClassFileLoadHook;432error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));433check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");434435/* At first the only initial events we are interested in are VM436* initialization, VM death, and Class File Loads.437* Once the VM is initialized we will request more events.438*/439error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,440JVMTI_EVENT_VM_START, (jthread)NULL);441check_jvmti_error(jvmti, error, "Cannot set event notification");442error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,443JVMTI_EVENT_VM_INIT, (jthread)NULL);444check_jvmti_error(jvmti, error, "Cannot set event notification");445error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,446JVMTI_EVENT_VM_DEATH, (jthread)NULL);447check_jvmti_error(jvmti, error, "Cannot set event notification");448error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,449JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);450check_jvmti_error(jvmti, error, "Cannot set event notification");451452/* Here we create a raw monitor for our use in this agent to453* protect critical sections of code.454*/455error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));456check_jvmti_error(jvmti, error, "Cannot create raw monitor");457458/* Add demo jar file to boot classpath */459add_demo_jar_to_bootclasspath(jvmti, "minst");460461/* We return JNI_OK to signify success */462return JNI_OK;463}464465/* Agent_OnUnload: This is called immediately before the shared library is466* unloaded. This is the last code executed.467*/468JNIEXPORT void JNICALL469Agent_OnUnload(JavaVM *vm)470{471/* Make sure all malloc/calloc/strdup space is freed */472if ( gdata->include != NULL ) {473(void)free((void*)gdata->include);474gdata->include = NULL;475}476if ( gdata->exclude != NULL ) {477(void)free((void*)gdata->exclude);478gdata->exclude = NULL;479}480}481482483