Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/jvmti/heapTracker/heapTracker.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 "heapTracker.h"43#include "java_crw_demo.h"4445#include "jni.h"46#include "jvmti.h"4748#include "agent_util.h"4950/* -------------------------------------------------------------------51* Some constant names that tie to Java class/method names.52* We assume the Java class whose static methods we will be calling53* looks like:54*55* public class HeapTracker {56* private static int engaged;57* private static native void _newobj(Object thr, Object o);58* public static void newobj(Object o)59* {60* if ( engaged != 0 ) {61* _newobj(Thread.currentThread(), o);62* }63* }64* private static native void _newarr(Object thr, Object a);65* public static void newarr(Object a)66* {67* if ( engaged != 0 ) {68* _newarr(Thread.currentThread(), a);69* }70* }71* }72*73* The engaged field allows us to inject all classes (even system classes)74* and delay the actual calls to the native code until the VM has reached75* a safe time to call native methods (Past the JVMTI VM_START event).76*77*/7879#define HEAP_TRACKER_class HeapTracker /* Name of class we are using */80#define HEAP_TRACKER_newobj newobj /* Name of java init method */81#define HEAP_TRACKER_newarr newarr /* Name of java newarray method */82#define HEAP_TRACKER_native_newobj _newobj /* Name of java newobj native */83#define HEAP_TRACKER_native_newarr _newarr /* Name of java newarray native */84#define HEAP_TRACKER_engaged engaged /* Name of static field switch */8586/* C macros to create strings from tokens */87#define _STRING(s) #s88#define STRING(s) _STRING(s)8990/* ------------------------------------------------------------------- */9192/* Flavors of traces (to separate out stack traces) */9394typedef enum {95TRACE_FIRST = 0,96TRACE_USER = 0,97TRACE_BEFORE_VM_START = 1,98TRACE_BEFORE_VM_INIT = 2,99TRACE_VM_OBJECT = 3,100TRACE_MYSTERY = 4,101TRACE_LAST = 4102} TraceFlavor;103104static char * flavorDesc[] = {105"",106"before VM_START",107"before VM_INIT",108"VM_OBJECT",109"unknown"110};111112/* Trace (Stack Trace) */113114#define MAX_FRAMES 6115typedef struct Trace {116/* Number of frames (includes HEAP_TRACKER methods) */117jint nframes;118/* Frames from GetStackTrace() (2 extra for HEAP_TRACKER methods) */119jvmtiFrameInfo frames[MAX_FRAMES+2];120/* Used to make some traces unique */121TraceFlavor flavor;122} Trace;123124/* Trace information (more than one object will have this as a tag) */125126typedef struct TraceInfo {127/* Trace where this object was allocated from */128Trace trace;129/* 64 bit hash code that attempts to identify this specific trace */130jlong hashCode;131/* Total space taken up by objects allocated from this trace */132jlong totalSpace;133/* Total count of objects ever allocated from this trace */134int totalCount;135/* Total live objects that were allocated from this trace */136int useCount;137/* The next TraceInfo in the hash bucket chain */138struct TraceInfo *next;139} TraceInfo;140141/* Global agent data structure */142143typedef struct {144/* JVMTI Environment */145jvmtiEnv *jvmti;146/* State of the VM flags */147jboolean vmStarted;148jboolean vmInitialized;149jboolean vmDead;150/* Options */151int maxDump;152/* Data access Lock */153jrawMonitorID lock;154/* Counter on classes where BCI has been applied */155jint ccount;156/* Hash table to lookup TraceInfo's via Trace's */157#define HASH_INDEX_BIT_WIDTH 12 /* 4096 */158#define HASH_BUCKET_COUNT (1<<HASH_INDEX_BIT_WIDTH)159#define HASH_INDEX_MASK (HASH_BUCKET_COUNT-1)160TraceInfo *hashBuckets[HASH_BUCKET_COUNT];161/* Count of TraceInfo's allocated */162int traceInfoCount;163/* Pre-defined traces for the system and mystery situations */164TraceInfo *emptyTrace[TRACE_LAST+1];165} GlobalAgentData;166167static GlobalAgentData *gdata;168169/* Enter a critical section by doing a JVMTI Raw Monitor Enter */170static void171enterCriticalSection(jvmtiEnv *jvmti)172{173jvmtiError error;174175error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);176check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");177}178179/* Exit a critical section by doing a JVMTI Raw Monitor Exit */180static void181exitCriticalSection(jvmtiEnv *jvmti)182{183jvmtiError error;184185error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);186check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");187}188189/* Update stats on a TraceInfo */190static TraceInfo *191updateStats(TraceInfo *tinfo)192{193tinfo->totalCount++;194tinfo->useCount++;195return tinfo;196}197198/* Get TraceInfo for empty stack */199static TraceInfo *200emptyTrace(TraceFlavor flavor)201{202return updateStats(gdata->emptyTrace[flavor]);203}204205/* Allocate new TraceInfo */206static TraceInfo *207newTraceInfo(Trace *trace, jlong hashCode, TraceFlavor flavor)208{209TraceInfo *tinfo;210211tinfo = (TraceInfo*)calloc(1, sizeof(TraceInfo));212if ( tinfo == NULL ) {213fatal_error("ERROR: Ran out of malloc() space\n");214} else {215int hashIndex;216217tinfo->trace = *trace;218tinfo->trace.flavor = flavor;219tinfo->hashCode = hashCode;220gdata->traceInfoCount++;221hashIndex = (int)(hashCode & HASH_INDEX_MASK);222tinfo->next = gdata->hashBuckets[hashIndex];223gdata->hashBuckets[hashIndex] = tinfo;224}225return tinfo;226}227228/* Create hash code for a Trace */229static jlong230hashTrace(Trace *trace)231{232jlong hashCode;233int i;234235hashCode = 0;236for ( i = 0 ; i < trace->nframes ; i++ ) {237hashCode = (hashCode << 3) +238(jlong)(ptrdiff_t)(void*)(trace->frames[i].method);239hashCode = (hashCode << 2) +240(jlong)(trace->frames[i].location);241}242hashCode = (hashCode << 3) + trace->nframes;243hashCode += trace->flavor;244return hashCode;245}246247/* Lookup or create a new TraceInfo */248static TraceInfo *249lookupOrEnter(jvmtiEnv *jvmti, Trace *trace, TraceFlavor flavor)250{251TraceInfo *tinfo;252jlong hashCode;253254/* Calculate hash code (outside critical section to lessen contention) */255hashCode = hashTrace(trace);256257/* Do a lookup in the hash table */258enterCriticalSection(jvmti); {259TraceInfo *prev;260int hashIndex;261262/* Start with first item in hash buck chain */263prev = NULL;264hashIndex = (int)(hashCode & HASH_INDEX_MASK);265tinfo = gdata->hashBuckets[hashIndex];266while ( tinfo != NULL ) {267if ( tinfo->hashCode == hashCode &&268memcmp(trace, &(tinfo->trace), sizeof(Trace))==0 ) {269/* We found one that matches, move to head of bucket chain */270if ( prev != NULL ) {271/* Remove from list and add to head of list */272prev->next = tinfo->next;273tinfo->next = gdata->hashBuckets[hashIndex];274gdata->hashBuckets[hashIndex] = tinfo;275}276/* Break out */277break;278}279prev = tinfo;280tinfo = tinfo->next;281}282283/* If we didn't find anything we need to enter a new entry */284if ( tinfo == NULL ) {285/* Create new hash table element */286tinfo = newTraceInfo(trace, hashCode, flavor);287}288289/* Update stats */290(void)updateStats(tinfo);291292} exitCriticalSection(jvmti);293294return tinfo;295}296297/* Get TraceInfo for this allocation */298static TraceInfo *299findTraceInfo(jvmtiEnv *jvmti, jthread thread, TraceFlavor flavor)300{301TraceInfo *tinfo;302jvmtiError error;303304tinfo = NULL;305if ( thread != NULL ) {306static Trace empty;307Trace trace;308309/* Before VM_INIT thread could be NULL, watch out */310trace = empty;311error = (*jvmti)->GetStackTrace(jvmti, thread, 0, MAX_FRAMES+2,312trace.frames, &(trace.nframes));313/* If we get a PHASE error, the VM isn't ready, or it died */314if ( error == JVMTI_ERROR_WRONG_PHASE ) {315/* It is assumed this is before VM_INIT */316if ( flavor == TRACE_USER ) {317tinfo = emptyTrace(TRACE_BEFORE_VM_INIT);318} else {319tinfo = emptyTrace(flavor);320}321} else {322check_jvmti_error(jvmti, error, "Cannot get stack trace");323/* Lookup this entry */324tinfo = lookupOrEnter(jvmti, &trace, flavor);325}326} else {327/* If thread==NULL, it's assumed this is before VM_START */328if ( flavor == TRACE_USER ) {329tinfo = emptyTrace(TRACE_BEFORE_VM_START);330} else {331tinfo = emptyTrace(flavor);332}333}334return tinfo;335}336337/* Tag an object with a TraceInfo pointer. */338static void339tagObjectWithTraceInfo(jvmtiEnv *jvmti, jobject object, TraceInfo *tinfo)340{341jvmtiError error;342jlong tag;343344/* Tag this object with this TraceInfo pointer */345tag = (jlong)(ptrdiff_t)(void*)tinfo;346error = (*jvmti)->SetTag(jvmti, object, tag);347check_jvmti_error(jvmti, error, "Cannot tag object");348}349350/* Java Native Method for Object.<init> */351static void JNICALL352HEAP_TRACKER_native_newobj(JNIEnv *env, jclass klass, jthread thread, jobject o)353{354TraceInfo *tinfo;355356if ( gdata->vmDead ) {357return;358}359tinfo = findTraceInfo(gdata->jvmti, thread, TRACE_USER);360tagObjectWithTraceInfo(gdata->jvmti, o, tinfo);361}362363/* Java Native Method for newarray */364static void JNICALL365HEAP_TRACKER_native_newarr(JNIEnv *env, jclass klass, jthread thread, jobject a)366{367TraceInfo *tinfo;368369if ( gdata->vmDead ) {370return;371}372tinfo = findTraceInfo(gdata->jvmti, thread, TRACE_USER);373tagObjectWithTraceInfo(gdata->jvmti, a, tinfo);374}375376/* Callback for JVMTI_EVENT_VM_START */377static void JNICALL378cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)379{380enterCriticalSection(jvmti); {381jclass klass;382jfieldID field;383jint rc;384385/* Java Native Methods for class */386static JNINativeMethod registry[2] = {387{STRING(HEAP_TRACKER_native_newobj), "(Ljava/lang/Object;Ljava/lang/Object;)V",388(void*)&HEAP_TRACKER_native_newobj},389{STRING(HEAP_TRACKER_native_newarr), "(Ljava/lang/Object;Ljava/lang/Object;)V",390(void*)&HEAP_TRACKER_native_newarr}391};392393/* Register Natives for class whose methods we use */394klass = (*env)->FindClass(env, STRING(HEAP_TRACKER_class));395if ( klass == NULL ) {396fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",397STRING(HEAP_TRACKER_class));398}399rc = (*env)->RegisterNatives(env, klass, registry, 2);400if ( rc != 0 ) {401fatal_error("ERROR: JNI: Cannot register natives for class %s\n",402STRING(HEAP_TRACKER_class));403}404405/* Engage calls. */406field = (*env)->GetStaticFieldID(env, klass, STRING(HEAP_TRACKER_engaged), "I");407if ( field == NULL ) {408fatal_error("ERROR: JNI: Cannot get field from %s\n",409STRING(HEAP_TRACKER_class));410}411(*env)->SetStaticIntField(env, klass, field, 1);412413/* Indicate VM has started */414gdata->vmStarted = JNI_TRUE;415416} exitCriticalSection(jvmti);417}418419/* Iterate Through Heap callback */420static jint JNICALL421cbObjectTagger(jlong class_tag, jlong size, jlong* tag_ptr, jint length,422void *user_data)423{424TraceInfo *tinfo;425426tinfo = emptyTrace(TRACE_BEFORE_VM_INIT);427*tag_ptr = (jlong)(ptrdiff_t)(void*)tinfo;428return JVMTI_VISIT_OBJECTS;429}430431/* Callback for JVMTI_EVENT_VM_INIT */432static void JNICALL433cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)434{435jvmtiHeapCallbacks heapCallbacks;436jvmtiError error;437438/* Iterate through heap, find all untagged objects allocated before this */439(void)memset(&heapCallbacks, 0, sizeof(heapCallbacks));440heapCallbacks.heap_iteration_callback = &cbObjectTagger;441error = (*jvmti)->IterateThroughHeap(jvmti, JVMTI_HEAP_FILTER_TAGGED,442NULL, &heapCallbacks, NULL);443check_jvmti_error(jvmti, error, "Cannot iterate through heap");444445enterCriticalSection(jvmti); {446447/* Indicate VM is initialized */448gdata->vmInitialized = JNI_TRUE;449450} exitCriticalSection(jvmti);451}452453/* Iterate Through Heap callback */454static jint JNICALL455cbObjectSpaceCounter(jlong class_tag, jlong size, jlong* tag_ptr, jint length,456void *user_data)457{458TraceInfo *tinfo;459460tinfo = (TraceInfo*)(ptrdiff_t)(*tag_ptr);461if ( tinfo == NULL ) {462tinfo = emptyTrace(TRACE_MYSTERY);463*tag_ptr = (jlong)(ptrdiff_t)(void*)tinfo;464}465tinfo->totalSpace += size;466return JVMTI_VISIT_OBJECTS;467}468469/* Qsort compare function */470static int471compareInfo(const void *p1, const void *p2)472{473TraceInfo *tinfo1, *tinfo2;474475tinfo1 = *((TraceInfo**)p1);476tinfo2 = *((TraceInfo**)p2);477return (int)(tinfo2->totalSpace - tinfo1->totalSpace);478}479480/* Frame to text */481static void482frameToString(jvmtiEnv *jvmti, char *buf, int buflen, jvmtiFrameInfo *finfo)483{484jvmtiError error;485jclass klass;486char *signature;487char *methodname;488char *methodsig;489jboolean isNative;490char *filename;491int lineCount;492jvmtiLineNumberEntry*lineTable;493int lineNumber;494495/* Initialize defaults */496buf[0] = 0;497klass = NULL;498signature = NULL;499methodname = NULL;500methodsig = NULL;501isNative = JNI_FALSE;502filename = NULL;503lineCount = 0;504lineTable = NULL;505lineNumber = 0;506507/* Get jclass object for the jmethodID */508error = (*jvmti)->GetMethodDeclaringClass(jvmti, finfo->method, &klass);509check_jvmti_error(jvmti, error, "Cannot get method's class");510511/* Get the class signature */512error = (*jvmti)->GetClassSignature(jvmti, klass, &signature, NULL);513check_jvmti_error(jvmti, error, "Cannot get class signature");514515/* Skip all this if it's our own Tracker method */516if ( strcmp(signature, "L" STRING(HEAP_TRACKER_class) ";" ) == 0 ) {517deallocate(jvmti, signature);518return;519}520521/* Get the name and signature for the method */522error = (*jvmti)->GetMethodName(jvmti, finfo->method,523&methodname, &methodsig, NULL);524check_jvmti_error(jvmti, error, "Cannot method name");525526/* Check to see if it's a native method, which means no lineNumber */527error = (*jvmti)->IsMethodNative(jvmti, finfo->method, &isNative);528check_jvmti_error(jvmti, error, "Cannot get method native status");529530/* Get source file name */531error = (*jvmti)->GetSourceFileName(jvmti, klass, &filename);532if ( error != JVMTI_ERROR_NONE && error != JVMTI_ERROR_ABSENT_INFORMATION ) {533check_jvmti_error(jvmti, error, "Cannot get source filename");534}535536/* Get lineNumber if we can */537if ( !isNative ) {538int i;539540/* Get method line table */541error = (*jvmti)->GetLineNumberTable(jvmti, finfo->method, &lineCount, &lineTable);542if ( error == JVMTI_ERROR_NONE ) {543/* Search for line */544lineNumber = lineTable[0].line_number;545for ( i = 1 ; i < lineCount ; i++ ) {546if ( finfo->location < lineTable[i].start_location ) {547break;548}549lineNumber = lineTable[i].line_number;550}551} else if ( error != JVMTI_ERROR_ABSENT_INFORMATION ) {552check_jvmti_error(jvmti, error, "Cannot get method line table");553}554}555556/* Create string for this frame location.557* NOTE: These char* quantities are mUTF (Modified UTF-8) bytes558* and should actually be converted to the default system559* character encoding. Sending them to things like560* printf() without converting them is actually an I18n561* (Internationalization) error.562*/563(void)sprintf(buf, "%s.%s@%d[%s:%d]",564(signature==NULL?"UnknownClass":signature),565(methodname==NULL?"UnknownMethod":methodname),566(int)finfo->location,567(filename==NULL?"UnknownFile":filename),568lineNumber);569570/* Free up JVMTI space allocated by the above calls */571deallocate(jvmti, signature);572deallocate(jvmti, methodname);573deallocate(jvmti, methodsig);574deallocate(jvmti, filename);575deallocate(jvmti, lineTable);576}577578/* Print the information */579static void580printTraceInfo(jvmtiEnv *jvmti, int index, TraceInfo* tinfo)581{582if ( tinfo == NULL ) {583fatal_error("%d: NULL ENTRY ERROR\n", index);584return;585}586587stdout_message("%2d: %7d bytes %5d objects %5d live %s",588index, (int)tinfo->totalSpace, tinfo->totalCount,589tinfo->useCount, flavorDesc[tinfo->trace.flavor]);590591if ( tinfo->trace.nframes > 0 ) {592int i;593int fcount;594595fcount = 0;596stdout_message(" stack=(");597for ( i = 0 ; i < tinfo->trace.nframes ; i++ ) {598char buf[4096];599600frameToString(jvmti, buf, (int)sizeof(buf), tinfo->trace.frames+i);601if ( buf[0] == 0 ) {602continue; /* Skip the ones that are from Tracker class */603}604fcount++;605stdout_message("%s", buf);606if ( i < (tinfo->trace.nframes-1) ) {607stdout_message(",");608}609}610stdout_message(") nframes=%d\n", fcount);611} else {612stdout_message(" stack=<empty>\n");613}614}615616/* Callback for JVMTI_EVENT_VM_DEATH */617static void JNICALL618cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)619{620jvmtiHeapCallbacks heapCallbacks;621jvmtiError error;622623/* These are purposely done outside the critical section */624625/* Force garbage collection now so we get our ObjectFree calls */626error = (*jvmti)->ForceGarbageCollection(jvmti);627check_jvmti_error(jvmti, error, "Cannot force garbage collection");628629/* Iterate through heap and find all objects */630(void)memset(&heapCallbacks, 0, sizeof(heapCallbacks));631heapCallbacks.heap_iteration_callback = &cbObjectSpaceCounter;632error = (*jvmti)->IterateThroughHeap(jvmti, 0, NULL, &heapCallbacks, NULL);633check_jvmti_error(jvmti, error, "Cannot iterate through heap");634635/* Process VM Death */636enterCriticalSection(jvmti); {637jclass klass;638jfieldID field;639jvmtiEventCallbacks callbacks;640641/* Disengage calls in HEAP_TRACKER_class. */642klass = (*env)->FindClass(env, STRING(HEAP_TRACKER_class));643if ( klass == NULL ) {644fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",645STRING(HEAP_TRACKER_class));646}647field = (*env)->GetStaticFieldID(env, klass, STRING(HEAP_TRACKER_engaged), "I");648if ( field == NULL ) {649fatal_error("ERROR: JNI: Cannot get field from %s\n",650STRING(HEAP_TRACKER_class));651}652(*env)->SetStaticIntField(env, klass, field, 0);653654/* The critical section here is important to hold back the VM death655* until all other callbacks have completed.656*/657658/* Clear out all callbacks. */659(void)memset(&callbacks,0, sizeof(callbacks));660error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks,661(jint)sizeof(callbacks));662check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");663664/* Since this critical section could be holding up other threads665* in other event callbacks, we need to indicate that the VM is666* dead so that the other callbacks can short circuit their work.667* We don't expect an further events after VmDeath but we do need668* to be careful that existing threads might be in our own agent669* callback code.670*/671gdata->vmDead = JNI_TRUE;672673/* Dump all objects */674if ( gdata->traceInfoCount > 0 ) {675TraceInfo **list;676int count;677int i;678679stdout_message("Dumping heap trace information\n");680681/* Create single array of pointers to TraceInfo's, sort, and682* print top gdata->maxDump top space users.683*/684list = (TraceInfo**)calloc(gdata->traceInfoCount,685sizeof(TraceInfo*));686if ( list == NULL ) {687fatal_error("ERROR: Ran out of malloc() space\n");688}689count = 0;690for ( i = 0 ; i < HASH_BUCKET_COUNT ; i++ ) {691TraceInfo *tinfo;692693tinfo = gdata->hashBuckets[i];694while ( tinfo != NULL ) {695if ( count < gdata->traceInfoCount ) {696list[count++] = tinfo;697}698tinfo = tinfo->next;699}700}701if ( count != gdata->traceInfoCount ) {702fatal_error("ERROR: Count found by iterate doesn't match ours:"703" count=%d != traceInfoCount==%d\n",704count, gdata->traceInfoCount);705}706qsort(list, count, sizeof(TraceInfo*), &compareInfo);707for ( i = 0 ; i < count ; i++ ) {708if ( i >= gdata->maxDump ) {709break;710}711printTraceInfo(jvmti, i+1, list[i]);712}713(void)free(list);714}715716} exitCriticalSection(jvmti);717718}719720/* Callback for JVMTI_EVENT_VM_OBJECT_ALLOC */721static void JNICALL722cbVMObjectAlloc(jvmtiEnv *jvmti, JNIEnv *env, jthread thread,723jobject object, jclass object_klass, jlong size)724{725TraceInfo *tinfo;726727if ( gdata->vmDead ) {728return;729}730tinfo = findTraceInfo(jvmti, thread, TRACE_VM_OBJECT);731tagObjectWithTraceInfo(jvmti, object, tinfo);732}733734/* Callback for JVMTI_EVENT_OBJECT_FREE */735static void JNICALL736cbObjectFree(jvmtiEnv *jvmti, jlong tag)737{738TraceInfo *tinfo;739740if ( gdata->vmDead ) {741return;742}743744/* The object tag is actually a pointer to a TraceInfo structure */745tinfo = (TraceInfo*)(void*)(ptrdiff_t)tag;746747/* Decrement the use count */748tinfo->useCount--;749}750751/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */752static void JNICALL753cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,754jclass class_being_redefined, jobject loader,755const char* name, jobject protection_domain,756jint class_data_len, const unsigned char* class_data,757jint* new_class_data_len, unsigned char** new_class_data)758{759enterCriticalSection(jvmti); {760/* It's possible we get here right after VmDeath event, be careful */761if ( !gdata->vmDead ) {762763const char * classname;764765/* Name can be NULL, make sure we avoid SEGV's */766if ( name == NULL ) {767classname = java_crw_demo_classname(class_data, class_data_len,768NULL);769if ( classname == NULL ) {770fatal_error("ERROR: No classname in classfile\n");771}772} else {773classname = strdup(name);774if ( classname == NULL ) {775fatal_error("ERROR: Ran out of malloc() space\n");776}777}778779*new_class_data_len = 0;780*new_class_data = NULL;781782/* The tracker class itself? */783if ( strcmp(classname, STRING(HEAP_TRACKER_class)) != 0 ) {784jint cnum;785int systemClass;786unsigned char *newImage;787long newLength;788789/* Get number for every class file image loaded */790cnum = gdata->ccount++;791792/* Is it a system class? If the class load is before VmStart793* then we will consider it a system class that should794* be treated carefully. (See java_crw_demo)795*/796systemClass = 0;797if ( !gdata->vmStarted ) {798systemClass = 1;799}800801newImage = NULL;802newLength = 0;803804/* Call the class file reader/write demo code */805java_crw_demo(cnum,806classname,807class_data,808class_data_len,809systemClass,810STRING(HEAP_TRACKER_class),811"L" STRING(HEAP_TRACKER_class) ";",812NULL, NULL,813NULL, NULL,814STRING(HEAP_TRACKER_newobj), "(Ljava/lang/Object;)V",815STRING(HEAP_TRACKER_newarr), "(Ljava/lang/Object;)V",816&newImage,817&newLength,818NULL,819NULL);820821/* If we got back a new class image, return it back as "the"822* new class image. This must be JVMTI Allocate space.823*/824if ( newLength > 0 ) {825unsigned char *jvmti_space;826827jvmti_space = (unsigned char *)allocate(jvmti, (jint)newLength);828(void)memcpy((void*)jvmti_space, (void*)newImage, (int)newLength);829*new_class_data_len = (jint)newLength;830*new_class_data = jvmti_space; /* VM will deallocate */831}832833/* Always free up the space we get from java_crw_demo() */834if ( newImage != NULL ) {835(void)free((void*)newImage); /* Free malloc() space with free() */836}837}838839(void)free((void*)classname);840}841} exitCriticalSection(jvmti);842}843844/* Parse the options for this heapTracker agent */845static void846parse_agent_options(char *options)847{848#define MAX_TOKEN_LENGTH 16849char token[MAX_TOKEN_LENGTH];850char *next;851852/* Defaults */853gdata->maxDump = 20;854855/* Parse options and set flags in gdata */856if ( options==NULL ) {857return;858}859860/* Get the first token from the options string. */861next = get_token(options, ",=", token, (int)sizeof(token));862863/* While not at the end of the options string, process this option. */864while ( next != NULL ) {865if ( strcmp(token,"help")==0 ) {866stdout_message("The heapTracker JVMTI demo agent\n");867stdout_message("\n");868stdout_message(" java -agent:heapTracker[=options] ...\n");869stdout_message("\n");870stdout_message("The options are comma separated:\n");871stdout_message("\t help\t\t\t Print help information\n");872stdout_message("\t maxDump=n\t\t\t How many TraceInfo's to dump\n");873stdout_message("\n");874exit(0);875} else if ( strcmp(token,"maxDump")==0 ) {876char number[MAX_TOKEN_LENGTH];877878next = get_token(next, ",=", number, (int)sizeof(number));879if ( next == NULL ) {880fatal_error("ERROR: Cannot parse maxDump=number: %s\n", options);881}882gdata->maxDump = atoi(number);883} else if ( token[0]!=0 ) {884/* We got a non-empty token and we don't know what it is. */885fatal_error("ERROR: Unknown option: %s\n", token);886}887/* Get the next token (returns NULL if there are no more) */888next = get_token(next, ",=", token, (int)sizeof(token));889}890}891892/* Agent_OnLoad: This is called immediately after the shared library is893* loaded. This is the first code executed.894*/895JNIEXPORT jint JNICALL896Agent_OnLoad(JavaVM *vm, char *options, void *reserved)897{898static GlobalAgentData data;899jvmtiEnv *jvmti;900jvmtiError error;901jint res;902TraceFlavor flavor;903jvmtiCapabilities capabilities;904jvmtiEventCallbacks callbacks;905static Trace empty;906907/* Setup initial global agent data area908* Use of static/extern data should be handled carefully here.909* We need to make sure that we are able to cleanup after ourselves910* so anything allocated in this library needs to be freed in911* the Agent_OnUnload() function.912*/913(void)memset((void*)&data, 0, sizeof(data));914gdata = &data;915916/* First thing we need to do is get the jvmtiEnv* or JVMTI environment */917res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);918if (res != JNI_OK) {919/* This means that the VM was unable to obtain this version of the920* JVMTI interface, this is a fatal error.921*/922fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"923" is your JDK a 5.0 or newer version?"924" JNIEnv's GetEnv() returned %d\n",925JVMTI_VERSION_1, res);926}927928/* Here we save the jvmtiEnv* for Agent_OnUnload(). */929gdata->jvmti = jvmti;930931/* Parse any options supplied on java command line */932parse_agent_options(options);933934/* Immediately after getting the jvmtiEnv* we need to ask for the935* capabilities this agent will need.936*/937(void)memset(&capabilities,0, sizeof(capabilities));938capabilities.can_generate_all_class_hook_events = 1;939capabilities.can_tag_objects = 1;940capabilities.can_generate_object_free_events = 1;941capabilities.can_get_source_file_name = 1;942capabilities.can_get_line_numbers = 1;943capabilities.can_generate_vm_object_alloc_events = 1;944error = (*jvmti)->AddCapabilities(jvmti, &capabilities);945check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");946947/* Next we need to provide the pointers to the callback functions to948* to this jvmtiEnv*949*/950(void)memset(&callbacks,0, sizeof(callbacks));951/* JVMTI_EVENT_VM_START */952callbacks.VMStart = &cbVMStart;953/* JVMTI_EVENT_VM_INIT */954callbacks.VMInit = &cbVMInit;955/* JVMTI_EVENT_VM_DEATH */956callbacks.VMDeath = &cbVMDeath;957/* JVMTI_EVENT_OBJECT_FREE */958callbacks.ObjectFree = &cbObjectFree;959/* JVMTI_EVENT_VM_OBJECT_ALLOC */960callbacks.VMObjectAlloc = &cbVMObjectAlloc;961/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */962callbacks.ClassFileLoadHook = &cbClassFileLoadHook;963error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));964check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");965966/* At first the only initial events we are interested in are VM967* initialization, VM death, and Class File Loads.968* Once the VM is initialized we will request more events.969*/970error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,971JVMTI_EVENT_VM_START, (jthread)NULL);972check_jvmti_error(jvmti, error, "Cannot set event notification");973error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,974JVMTI_EVENT_VM_INIT, (jthread)NULL);975check_jvmti_error(jvmti, error, "Cannot set event notification");976error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,977JVMTI_EVENT_VM_DEATH, (jthread)NULL);978check_jvmti_error(jvmti, error, "Cannot set event notification");979error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,980JVMTI_EVENT_OBJECT_FREE, (jthread)NULL);981check_jvmti_error(jvmti, error, "Cannot set event notification");982error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,983JVMTI_EVENT_VM_OBJECT_ALLOC, (jthread)NULL);984check_jvmti_error(jvmti, error, "Cannot set event notification");985error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,986JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);987check_jvmti_error(jvmti, error, "Cannot set event notification");988989/* Here we create a raw monitor for our use in this agent to990* protect critical sections of code.991*/992error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));993check_jvmti_error(jvmti, error, "Cannot create raw monitor");994995/* Create the TraceInfo for various flavors of empty traces */996for ( flavor = TRACE_FIRST ; flavor <= TRACE_LAST ; flavor++ ) {997gdata->emptyTrace[flavor] =998newTraceInfo(&empty, hashTrace(&empty), flavor);999}10001001/* Add jar file to boot classpath */1002add_demo_jar_to_bootclasspath(jvmti, "heapTracker");10031004/* We return JNI_OK to signify success */1005return JNI_OK;1006}10071008/* Agent_OnUnload: This is called immediately before the shared library is1009* unloaded. This is the last code executed.1010*/1011JNIEXPORT void JNICALL1012Agent_OnUnload(JavaVM *vm)1013{1014/* Skip any cleanup, VM is about to die anyway */1015}101610171018