Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/jvmti/mtrace/mtrace.c
38829 views
/*1* Copyright (c) 2004, 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 "mtrace.h"43#include "java_crw_demo.h"444546/* ------------------------------------------------------------------- */47/* Some constant maximum sizes */4849#define MAX_TOKEN_LENGTH 1650#define MAX_THREAD_NAME_LENGTH 51251#define MAX_METHOD_NAME_LENGTH 10245253/* Some constant names that tie to Java class/method names.54* We assume the Java class whose static methods we will be calling55* looks like:56*57* public class Mtrace {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* if ( engaged != 0 ) {63* _method_entry(Thread.currentThread(), cnum, mnum);64* }65* }66* private static native void _method_exit(Object thr, int cnum, int mnum);67* public static void method_exit(int cnum, int mnum)68* {69* if ( engaged != 0 ) {70* _method_exit(Thread.currentThread(), cnum, mnum);71* }72* }73* }74*75* The engaged field allows us to inject all classes (even system classes)76* and delay the actual calls to the native code until the VM has reached77* a safe time to call native methods (Past the JVMTI VM_START event).78*79*/8081#define MTRACE_class Mtrace /* Name of class we are using */82#define MTRACE_entry method_entry /* Name of java entry method */83#define MTRACE_exit method_exit /* Name of java exit method */84#define MTRACE_native_entry _method_entry /* Name of java entry native */85#define MTRACE_native_exit _method_exit /* Name of java exit native */86#define MTRACE_engaged engaged /* Name of java static field */8788/* C macros to create strings from tokens */89#define _STRING(s) #s90#define STRING(s) _STRING(s)9192/* ------------------------------------------------------------------- */9394/* Data structure to hold method and class information in agent */9596typedef struct MethodInfo {97const char *name; /* Method name */98const char *signature; /* Method signature */99int calls; /* Method call count */100int returns; /* Method return count */101} MethodInfo;102103typedef struct ClassInfo {104const char *name; /* Class name */105int mcount; /* Method count */106MethodInfo *methods; /* Method information */107int calls; /* Method call count for this class */108} ClassInfo;109110/* Global agent data structure */111112typedef struct {113/* JVMTI Environment */114jvmtiEnv *jvmti;115jboolean vm_is_dead;116jboolean vm_is_started;117/* Data access Lock */118jrawMonitorID lock;119/* Options */120char *include;121char *exclude;122int max_count;123/* ClassInfo Table */124ClassInfo *classes;125jint ccount;126} GlobalAgentData;127128static GlobalAgentData *gdata;129130/* Enter a critical section by doing a JVMTI Raw Monitor Enter */131static void132enter_critical_section(jvmtiEnv *jvmti)133{134jvmtiError error;135136error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);137check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");138}139140/* Exit a critical section by doing a JVMTI Raw Monitor Exit */141static void142exit_critical_section(jvmtiEnv *jvmti)143{144jvmtiError error;145146error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);147check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");148}149150/* Get a name for a jthread */151static void152get_thread_name(jvmtiEnv *jvmti, jthread thread, char *tname, int maxlen)153{154jvmtiThreadInfo info;155jvmtiError error;156157/* Make sure the stack variables are garbage free */158(void)memset(&info,0, sizeof(info));159160/* Assume the name is unknown for now */161(void)strcpy(tname, "Unknown");162163/* Get the thread information, which includes the name */164error = (*jvmti)->GetThreadInfo(jvmti, thread, &info);165check_jvmti_error(jvmti, error, "Cannot get thread info");166167/* The thread might not have a name, be careful here. */168if ( info.name != NULL ) {169int len;170171/* Copy the thread name into tname if it will fit */172len = (int)strlen(info.name);173if ( len < maxlen ) {174(void)strcpy(tname, info.name);175}176177/* Every string allocated by JVMTI needs to be freed */178deallocate(jvmti, (void*)info.name);179}180}181182/* Qsort class compare routine */183static int184class_compar(const void *e1, const void *e2)185{186ClassInfo *c1 = (ClassInfo*)e1;187ClassInfo *c2 = (ClassInfo*)e2;188if ( c1->calls > c2->calls ) return 1;189if ( c1->calls < c2->calls ) return -1;190return 0;191}192193/* Qsort method compare routine */194static int195method_compar(const void *e1, const void *e2)196{197MethodInfo *m1 = (MethodInfo*)e1;198MethodInfo *m2 = (MethodInfo*)e2;199if ( m1->calls > m2->calls ) return 1;200if ( m1->calls < m2->calls ) return -1;201return 0;202}203204/* Callback from java_crw_demo() that gives us mnum mappings */205static void206mnum_callbacks(unsigned cnum, const char **names, const char**sigs, int mcount)207{208ClassInfo *cp;209int mnum;210211if ( cnum >= (unsigned)gdata->ccount ) {212fatal_error("ERROR: Class number out of range\n");213}214if ( mcount == 0 ) {215return;216}217218cp = gdata->classes + (int)cnum;219cp->calls = 0;220cp->mcount = mcount;221cp->methods = (MethodInfo*)calloc(mcount, sizeof(MethodInfo));222if ( cp->methods == NULL ) {223fatal_error("ERROR: Out of malloc memory\n");224}225226for ( mnum = 0 ; mnum < mcount ; mnum++ ) {227MethodInfo *mp;228229mp = cp->methods + mnum;230mp->name = (const char *)strdup(names[mnum]);231if ( mp->name == NULL ) {232fatal_error("ERROR: Out of malloc memory\n");233}234mp->signature = (const char *)strdup(sigs[mnum]);235if ( mp->signature == NULL ) {236fatal_error("ERROR: Out of malloc memory\n");237}238}239}240241/* Java Native Method for entry */242static void243MTRACE_native_entry(JNIEnv *env, jclass klass, jobject thread, jint cnum, jint mnum)244{245enter_critical_section(gdata->jvmti); {246/* It's possible we get here right after VmDeath event, be careful */247if ( !gdata->vm_is_dead ) {248ClassInfo *cp;249MethodInfo *mp;250251if ( cnum >= gdata->ccount ) {252fatal_error("ERROR: Class number out of range\n");253}254cp = gdata->classes + cnum;255if ( mnum >= cp->mcount ) {256fatal_error("ERROR: Method number out of range\n");257}258mp = cp->methods + mnum;259if ( interested((char*)cp->name, (char*)mp->name,260gdata->include, gdata->exclude) ) {261mp->calls++;262cp->calls++;263}264}265} exit_critical_section(gdata->jvmti);266}267268/* Java Native Method for exit */269static void270MTRACE_native_exit(JNIEnv *env, jclass klass, jobject thread, jint cnum, jint mnum)271{272enter_critical_section(gdata->jvmti); {273/* It's possible we get here right after VmDeath event, be careful */274if ( !gdata->vm_is_dead ) {275ClassInfo *cp;276MethodInfo *mp;277278if ( cnum >= gdata->ccount ) {279fatal_error("ERROR: Class number out of range\n");280}281cp = gdata->classes + cnum;282if ( mnum >= cp->mcount ) {283fatal_error("ERROR: Method number out of range\n");284}285mp = cp->methods + mnum;286if ( interested((char*)cp->name, (char*)mp->name,287gdata->include, gdata->exclude) ) {288mp->returns++;289}290}291} exit_critical_section(gdata->jvmti);292}293294/* Callback for JVMTI_EVENT_VM_START */295static void JNICALL296cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)297{298enter_critical_section(jvmti); {299jclass klass;300jfieldID field;301int rc;302303/* Java Native Methods for class */304static JNINativeMethod registry[2] = {305{STRING(MTRACE_native_entry), "(Ljava/lang/Object;II)V",306(void*)&MTRACE_native_entry},307{STRING(MTRACE_native_exit), "(Ljava/lang/Object;II)V",308(void*)&MTRACE_native_exit}309};310311/* The VM has started. */312stdout_message("VMStart\n");313314/* Register Natives for class whose methods we use */315klass = (*env)->FindClass(env, STRING(MTRACE_class));316if ( klass == NULL ) {317fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",318STRING(MTRACE_class));319}320rc = (*env)->RegisterNatives(env, klass, registry, 2);321if ( rc != 0 ) {322fatal_error("ERROR: JNI: Cannot register native methods for %s\n",323STRING(MTRACE_class));324}325326/* Engage calls. */327field = (*env)->GetStaticFieldID(env, klass, STRING(MTRACE_engaged), "I");328if ( field == NULL ) {329fatal_error("ERROR: JNI: Cannot get field from %s\n",330STRING(MTRACE_class));331}332(*env)->SetStaticIntField(env, klass, field, 1);333334/* Indicate VM has started */335gdata->vm_is_started = JNI_TRUE;336337} exit_critical_section(jvmti);338}339340/* Callback for JVMTI_EVENT_VM_INIT */341static void JNICALL342cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)343{344enter_critical_section(jvmti); {345char tname[MAX_THREAD_NAME_LENGTH];346static jvmtiEvent events[] =347{ JVMTI_EVENT_THREAD_START, JVMTI_EVENT_THREAD_END };348int i;349350/* The VM has started. */351get_thread_name(jvmti, thread, tname, sizeof(tname));352stdout_message("VMInit %s\n", tname);353354/* The VM is now initialized, at this time we make our requests355* for additional events.356*/357358for( i=0; i < (int)(sizeof(events)/sizeof(jvmtiEvent)); i++) {359jvmtiError error;360361/* Setup event notification modes */362error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,363events[i], (jthread)NULL);364check_jvmti_error(jvmti, error, "Cannot set event notification");365}366367} exit_critical_section(jvmti);368}369370/* Callback for JVMTI_EVENT_VM_DEATH */371static void JNICALL372cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)373{374enter_critical_section(jvmti); {375jclass klass;376jfieldID field;377378/* The VM has died. */379stdout_message("VMDeath\n");380381/* Disengage calls in MTRACE_class. */382klass = (*env)->FindClass(env, STRING(MTRACE_class));383if ( klass == NULL ) {384fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",385STRING(MTRACE_class));386}387field = (*env)->GetStaticFieldID(env, klass, STRING(MTRACE_engaged), "I");388if ( field == NULL ) {389fatal_error("ERROR: JNI: Cannot get field from %s\n",390STRING(MTRACE_class));391}392(*env)->SetStaticIntField(env, klass, field, 0);393394/* The critical section here is important to hold back the VM death395* until all other callbacks have completed.396*/397398/* Since this critical section could be holding up other threads399* in other event callbacks, we need to indicate that the VM is400* dead so that the other callbacks can short circuit their work.401* We don't expect any further events after VmDeath but we do need402* to be careful that existing threads might be in our own agent403* callback code.404*/405gdata->vm_is_dead = JNI_TRUE;406407/* Dump out stats */408stdout_message("Begin Class Stats\n");409if ( gdata->ccount > 0 ) {410int cnum;411412/* Sort table (in place) by number of method calls into class. */413/* Note: Do not use this table after this qsort! */414qsort(gdata->classes, gdata->ccount, sizeof(ClassInfo),415&class_compar);416417/* Dump out gdata->max_count most called classes */418for ( cnum=gdata->ccount-1 ;419cnum >= 0 && cnum >= gdata->ccount - gdata->max_count;420cnum-- ) {421ClassInfo *cp;422int mnum;423424cp = gdata->classes + cnum;425stdout_message("Class %s %d calls\n", cp->name, cp->calls);426if ( cp->calls==0 ) continue;427428/* Sort method table (in place) by number of method calls. */429/* Note: Do not use this table after this qsort! */430qsort(cp->methods, cp->mcount, sizeof(MethodInfo),431&method_compar);432for ( mnum=cp->mcount-1 ; mnum >= 0 ; mnum-- ) {433MethodInfo *mp;434435mp = cp->methods + mnum;436if ( mp->calls==0 ) continue;437stdout_message("\tMethod %s %s %d calls %d returns\n",438mp->name, mp->signature, mp->calls, mp->returns);439}440}441}442stdout_message("End Class Stats\n");443(void)fflush(stdout);444445} exit_critical_section(jvmti);446447}448449/* Callback for JVMTI_EVENT_THREAD_START */450static void JNICALL451cbThreadStart(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)452{453enter_critical_section(jvmti); {454/* It's possible we get here right after VmDeath event, be careful */455if ( !gdata->vm_is_dead ) {456char tname[MAX_THREAD_NAME_LENGTH];457458get_thread_name(jvmti, thread, tname, sizeof(tname));459stdout_message("ThreadStart %s\n", tname);460}461} exit_critical_section(jvmti);462}463464/* Callback for JVMTI_EVENT_THREAD_END */465static void JNICALL466cbThreadEnd(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)467{468enter_critical_section(jvmti); {469/* It's possible we get here right after VmDeath event, be careful */470if ( !gdata->vm_is_dead ) {471char tname[MAX_THREAD_NAME_LENGTH];472473get_thread_name(jvmti, thread, tname, sizeof(tname));474stdout_message("ThreadEnd %s\n", tname);475}476} exit_critical_section(jvmti);477}478479/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */480static void JNICALL481cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,482jclass class_being_redefined, jobject loader,483const char* name, jobject protection_domain,484jint class_data_len, const unsigned char* class_data,485jint* new_class_data_len, unsigned char** new_class_data)486{487enter_critical_section(jvmti); {488/* It's possible we get here right after VmDeath event, be careful */489if ( !gdata->vm_is_dead ) {490491const char *classname;492493/* Name could be NULL */494if ( name == NULL ) {495classname = java_crw_demo_classname(class_data, class_data_len,496NULL);497if ( classname == NULL ) {498fatal_error("ERROR: No classname inside classfile\n");499}500} else {501classname = strdup(name);502if ( classname == NULL ) {503fatal_error("ERROR: Out of malloc memory\n");504}505}506507*new_class_data_len = 0;508*new_class_data = NULL;509510/* The tracker class itself? */511if ( interested((char*)classname, "", gdata->include, gdata->exclude)512&& strcmp(classname, STRING(MTRACE_class)) != 0 ) {513jint cnum;514int system_class;515unsigned char *new_image;516long new_length;517ClassInfo *cp;518519/* Get unique number for every class file image loaded */520cnum = gdata->ccount++;521522/* Save away class information */523if ( gdata->classes == NULL ) {524gdata->classes = (ClassInfo*)malloc(525gdata->ccount*sizeof(ClassInfo));526} else {527gdata->classes = (ClassInfo*)528realloc((void*)gdata->classes,529gdata->ccount*sizeof(ClassInfo));530}531if ( gdata->classes == NULL ) {532fatal_error("ERROR: Out of malloc memory\n");533}534cp = gdata->classes + cnum;535cp->name = (const char *)strdup(classname);536if ( cp->name == NULL ) {537fatal_error("ERROR: Out of malloc memory\n");538}539cp->calls = 0;540cp->mcount = 0;541cp->methods = NULL;542543/* Is it a system class? If the class load is before VmStart544* then we will consider it a system class that should545* be treated carefully. (See java_crw_demo)546*/547system_class = 0;548if ( !gdata->vm_is_started ) {549system_class = 1;550}551552new_image = NULL;553new_length = 0;554555/* Call the class file reader/write demo code */556java_crw_demo(cnum,557classname,558class_data,559class_data_len,560system_class,561STRING(MTRACE_class), "L" STRING(MTRACE_class) ";",562STRING(MTRACE_entry), "(II)V",563STRING(MTRACE_exit), "(II)V",564NULL, NULL,565NULL, NULL,566&new_image,567&new_length,568NULL,569&mnum_callbacks);570571/* If we got back a new class image, return it back as "the"572* new class image. This must be JVMTI Allocate space.573*/574if ( new_length > 0 ) {575unsigned char *jvmti_space;576577jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length);578(void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);579*new_class_data_len = (jint)new_length;580*new_class_data = jvmti_space; /* VM will deallocate */581}582583/* Always free up the space we get from java_crw_demo() */584if ( new_image != NULL ) {585(void)free((void*)new_image); /* Free malloc() space with free() */586}587}588(void)free((void*)classname);589}590} exit_critical_section(jvmti);591}592593/* Parse the options for this mtrace agent */594static void595parse_agent_options(char *options)596{597char token[MAX_TOKEN_LENGTH];598char *next;599600gdata->max_count = 10; /* Default max=n */601602/* Parse options and set flags in gdata */603if ( options==NULL ) {604return;605}606607/* Get the first token from the options string. */608next = get_token(options, ",=", token, sizeof(token));609610/* While not at the end of the options string, process this option. */611while ( next != NULL ) {612if ( strcmp(token,"help")==0 ) {613stdout_message("The mtrace JVMTI demo agent\n");614stdout_message("\n");615stdout_message(" java -agent:mtrace[=options] ...\n");616stdout_message("\n");617stdout_message("The options are comma separated:\n");618stdout_message("\t help\t\t\t Print help information\n");619stdout_message("\t max=n\t\t Only list top n classes\n");620stdout_message("\t include=item\t\t Only these classes/methods\n");621stdout_message("\t exclude=item\t\t Exclude these classes/methods\n");622stdout_message("\n");623stdout_message("item\t Qualified class and/or method names\n");624stdout_message("\t\t e.g. (*.<init>;Foobar.method;sun.*)\n");625stdout_message("\n");626exit(0);627} else if ( strcmp(token,"max")==0 ) {628char number[MAX_TOKEN_LENGTH];629630/* Get the numeric option */631next = get_token(next, ",=", number, (int)sizeof(number));632/* Check for token scan error */633if ( next==NULL ) {634fatal_error("ERROR: max=n option error\n");635}636/* Save numeric value */637gdata->max_count = atoi(number);638} else if ( strcmp(token,"include")==0 ) {639int used;640int maxlen;641642maxlen = MAX_METHOD_NAME_LENGTH;643if ( gdata->include == NULL ) {644gdata->include = (char*)calloc(maxlen+1, 1);645used = 0;646} else {647used = (int)strlen(gdata->include);648gdata->include[used++] = ',';649gdata->include[used] = 0;650gdata->include = (char*)651realloc((void*)gdata->include, used+maxlen+1);652}653if ( gdata->include == NULL ) {654fatal_error("ERROR: Out of malloc memory\n");655}656/* Add this item to the list */657next = get_token(next, ",=", gdata->include+used, maxlen);658/* Check for token scan error */659if ( next==NULL ) {660fatal_error("ERROR: include option error\n");661}662} else if ( strcmp(token,"exclude")==0 ) {663int used;664int maxlen;665666maxlen = MAX_METHOD_NAME_LENGTH;667if ( gdata->exclude == NULL ) {668gdata->exclude = (char*)calloc(maxlen+1, 1);669used = 0;670} else {671used = (int)strlen(gdata->exclude);672gdata->exclude[used++] = ',';673gdata->exclude[used] = 0;674gdata->exclude = (char*)675realloc((void*)gdata->exclude, used+maxlen+1);676}677if ( gdata->exclude == NULL ) {678fatal_error("ERROR: Out of malloc memory\n");679}680/* Add this item to the list */681next = get_token(next, ",=", gdata->exclude+used, maxlen);682/* Check for token scan error */683if ( next==NULL ) {684fatal_error("ERROR: exclude option error\n");685}686} else if ( token[0]!=0 ) {687/* We got a non-empty token and we don't know what it is. */688fatal_error("ERROR: Unknown option: %s\n", token);689}690/* Get the next token (returns NULL if there are no more) */691next = get_token(next, ",=", token, sizeof(token));692}693}694695/* Agent_OnLoad: This is called immediately after the shared library is696* loaded. This is the first code executed.697*/698JNIEXPORT jint JNICALL699Agent_OnLoad(JavaVM *vm, char *options, void *reserved)700{701static GlobalAgentData data;702jvmtiEnv *jvmti;703jvmtiError error;704jint res;705jvmtiCapabilities capabilities;706jvmtiEventCallbacks callbacks;707708/* Setup initial global agent data area709* Use of static/extern data should be handled carefully here.710* We need to make sure that we are able to cleanup after ourselves711* so anything allocated in this library needs to be freed in712* the Agent_OnUnload() function.713*/714(void)memset((void*)&data, 0, sizeof(data));715gdata = &data;716717/* First thing we need to do is get the jvmtiEnv* or JVMTI environment */718res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);719if (res != JNI_OK) {720/* This means that the VM was unable to obtain this version of the721* JVMTI interface, this is a fatal error.722*/723fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"724" is your JDK a 5.0 or newer version?"725" JNIEnv's GetEnv() returned %d\n",726JVMTI_VERSION_1, res);727}728729/* Here we save the jvmtiEnv* for Agent_OnUnload(). */730gdata->jvmti = jvmti;731732/* Parse any options supplied on java command line */733parse_agent_options(options);734735/* Immediately after getting the jvmtiEnv* we need to ask for the736* capabilities this agent will need. In this case we need to make737* sure that we can get all class load hooks.738*/739(void)memset(&capabilities,0, sizeof(capabilities));740capabilities.can_generate_all_class_hook_events = 1;741error = (*jvmti)->AddCapabilities(jvmti, &capabilities);742check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");743744/* Next we need to provide the pointers to the callback functions to745* to this jvmtiEnv*746*/747(void)memset(&callbacks,0, sizeof(callbacks));748/* JVMTI_EVENT_VM_START */749callbacks.VMStart = &cbVMStart;750/* JVMTI_EVENT_VM_INIT */751callbacks.VMInit = &cbVMInit;752/* JVMTI_EVENT_VM_DEATH */753callbacks.VMDeath = &cbVMDeath;754/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */755callbacks.ClassFileLoadHook = &cbClassFileLoadHook;756/* JVMTI_EVENT_THREAD_START */757callbacks.ThreadStart = &cbThreadStart;758/* JVMTI_EVENT_THREAD_END */759callbacks.ThreadEnd = &cbThreadEnd;760error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));761check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");762763/* At first the only initial events we are interested in are VM764* initialization, VM death, and Class File Loads.765* Once the VM is initialized we will request more events.766*/767error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,768JVMTI_EVENT_VM_START, (jthread)NULL);769check_jvmti_error(jvmti, error, "Cannot set event notification");770error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,771JVMTI_EVENT_VM_INIT, (jthread)NULL);772check_jvmti_error(jvmti, error, "Cannot set event notification");773error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,774JVMTI_EVENT_VM_DEATH, (jthread)NULL);775check_jvmti_error(jvmti, error, "Cannot set event notification");776error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,777JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);778check_jvmti_error(jvmti, error, "Cannot set event notification");779780/* Here we create a raw monitor for our use in this agent to781* protect critical sections of code.782*/783error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));784check_jvmti_error(jvmti, error, "Cannot create raw monitor");785786/* Add demo jar file to boot classpath */787add_demo_jar_to_bootclasspath(jvmti, "mtrace");788789/* We return JNI_OK to signify success */790return JNI_OK;791}792793/* Agent_OnUnload: This is called immediately before the shared library is794* unloaded. This is the last code executed.795*/796JNIEXPORT void JNICALL797Agent_OnUnload(JavaVM *vm)798{799/* Make sure all malloc/calloc/strdup space is freed */800if ( gdata->include != NULL ) {801(void)free((void*)gdata->include);802gdata->include = NULL;803}804if ( gdata->exclude != NULL ) {805(void)free((void*)gdata->exclude);806gdata->exclude = NULL;807}808if ( gdata->classes != NULL ) {809int cnum;810811for ( cnum = 0 ; cnum < gdata->ccount ; cnum++ ) {812ClassInfo *cp;813814cp = gdata->classes + cnum;815(void)free((void*)cp->name);816if ( cp->mcount > 0 ) {817int mnum;818819for ( mnum = 0 ; mnum < cp->mcount ; mnum++ ) {820MethodInfo *mp;821822mp = cp->methods + mnum;823(void)free((void*)mp->name);824(void)free((void*)mp->signature);825}826(void)free((void*)cp->methods);827}828}829(void)free((void*)gdata->classes);830gdata->classes = NULL;831}832}833834835