1 /*
   2  * Copyright (c) 2004, 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 "heapTracker.h"
  44 #include "java_crw_demo.h"
  45 
  46 #include "jni.h"
  47 #include "jvmti.h"
  48 
  49 #include "agent_util.h"
  50 
  51 /* -------------------------------------------------------------------
  52  * Some constant names that tie to Java class/method names.
  53  *    We assume the Java class whose static methods we will be calling
  54  *    looks like:
  55  *
  56  * public class HeapTracker {
  57  *     private static int engaged;
  58  *     private static native void _newobj(Object thr, Object o);
  59  *     public static void newobj(Object o)
  60  *     {
  61  *              if ( engaged != 0 ) {
  62  *               _newobj(Thread.currentThread(), o);
  63  *           }
  64  *     }
  65  *     private static native void _newarr(Object thr, Object a);
  66  *     public static void newarr(Object a)
  67  *     {
  68  *            if ( engaged != 0 ) {
  69  *               _newarr(Thread.currentThread(), a);
  70  *           }
  71  *     }
  72  * }
  73  *
  74  *    The engaged field allows us to inject all classes (even system classes)
  75  *    and delay the actual calls to the native code until the VM has reached
  76  *    a safe time to call native methods (Past the JVMTI VM_START event).
  77  *
  78  */
  79 
  80 #define HEAP_TRACKER_class           HeapTracker /* Name of class we are using */
  81 #define HEAP_TRACKER_newobj        newobj   /* Name of java init method */
  82 #define HEAP_TRACKER_newarr        newarr   /* Name of java newarray method */
  83 #define HEAP_TRACKER_native_newobj _newobj  /* Name of java newobj native */
  84 #define HEAP_TRACKER_native_newarr _newarr  /* Name of java newarray native */
  85 #define HEAP_TRACKER_engaged       engaged  /* Name of static field switch */
  86 
  87 /* C macros to create strings from tokens */
  88 #define _STRING(s) #s
  89 #define STRING(s) _STRING(s)
  90 
  91 /* ------------------------------------------------------------------- */
  92 
  93 /* Flavors of traces (to separate out stack traces) */
  94 
  95 typedef enum {
  96     TRACE_FIRST                        = 0,
  97     TRACE_USER                        = 0,
  98     TRACE_BEFORE_VM_START        = 1,
  99     TRACE_BEFORE_VM_INIT        = 2,
 100     TRACE_VM_OBJECT                = 3,
 101     TRACE_MYSTERY                = 4,
 102     TRACE_LAST                        = 4
 103 } TraceFlavor;
 104 
 105 static char * flavorDesc[] = {
 106     "",
 107     "before VM_START",
 108     "before VM_INIT",
 109     "VM_OBJECT",
 110     "unknown"
 111 };
 112 
 113 /* Trace (Stack Trace) */
 114 
 115 #define MAX_FRAMES 6
 116 typedef struct Trace {
 117     /* Number of frames (includes HEAP_TRACKER methods) */
 118     jint           nframes;
 119     /* Frames from GetStackTrace() (2 extra for HEAP_TRACKER methods) */
 120     jvmtiFrameInfo frames[MAX_FRAMES+2];
 121     /* Used to make some traces unique */
 122     TraceFlavor    flavor;
 123 } Trace;
 124 
 125 /* Trace information (more than one object will have this as a tag) */
 126 
 127 typedef struct TraceInfo {
 128     /* Trace where this object was allocated from */
 129     Trace             trace;
 130     /* 64 bit hash code that attempts to identify this specific trace */
 131     jlong             hashCode;
 132     /* Total space taken up by objects allocated from this trace */
 133     jlong             totalSpace;
 134     /* Total count of objects ever allocated from this trace */
 135     int               totalCount;
 136     /* Total live objects that were allocated from this trace */
 137     int               useCount;
 138     /* The next TraceInfo in the hash bucket chain */
 139     struct TraceInfo *next;
 140 } TraceInfo;
 141 
 142 /* Global agent data structure */
 143 
 144 typedef struct {
 145     /* JVMTI Environment */
 146     jvmtiEnv      *jvmti;
 147     /* State of the VM flags */
 148     jboolean       vmStarted;
 149     jboolean       vmInitialized;
 150     jboolean       vmDead;
 151     /* Options */
 152     int            maxDump;
 153     /* Data access Lock */
 154     jrawMonitorID  lock;
 155     /* Counter on classes where BCI has been applied */
 156     jint           ccount;
 157     /* Hash table to lookup TraceInfo's via Trace's */
 158     #define HASH_INDEX_BIT_WIDTH 12 /* 4096 */
 159     #define HASH_BUCKET_COUNT (1<<HASH_INDEX_BIT_WIDTH)
 160     #define HASH_INDEX_MASK (HASH_BUCKET_COUNT-1)
 161     TraceInfo     *hashBuckets[HASH_BUCKET_COUNT];
 162     /* Count of TraceInfo's allocated */
 163     int            traceInfoCount;
 164     /* Pre-defined traces for the system and mystery situations */
 165     TraceInfo     *emptyTrace[TRACE_LAST+1];
 166 } GlobalAgentData;
 167 
 168 static GlobalAgentData *gdata;
 169 
 170 /* Enter a critical section by doing a JVMTI Raw Monitor Enter */
 171 static void
 172 enterCriticalSection(jvmtiEnv *jvmti)
 173 {
 174     jvmtiError error;
 175 
 176     error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);
 177     check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
 178 }
 179 
 180 /* Exit a critical section by doing a JVMTI Raw Monitor Exit */
 181 static void
 182 exitCriticalSection(jvmtiEnv *jvmti)
 183 {
 184     jvmtiError error;
 185 
 186     error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);
 187     check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
 188 }
 189 
 190 /* Update stats on a TraceInfo */
 191 static TraceInfo *
 192 updateStats(TraceInfo *tinfo)
 193 {
 194     tinfo->totalCount++;
 195     tinfo->useCount++;
 196     return tinfo;
 197 }
 198 
 199 /* Get TraceInfo for empty stack */
 200 static TraceInfo *
 201 emptyTrace(TraceFlavor flavor)
 202 {
 203     return updateStats(gdata->emptyTrace[flavor]);
 204 }
 205 
 206 /* Allocate new TraceInfo */
 207 static TraceInfo *
 208 newTraceInfo(Trace *trace, jlong hashCode, TraceFlavor flavor)
 209 {
 210     TraceInfo *tinfo;
 211 
 212     tinfo = (TraceInfo*)calloc(1, sizeof(TraceInfo));
 213     if ( tinfo == NULL ) {
 214         fatal_error("ERROR: Ran out of malloc() space\n");
 215     } else {
 216         int hashIndex;
 217 
 218         tinfo->trace = *trace;
 219         tinfo->trace.flavor = flavor;
 220         tinfo->hashCode = hashCode;
 221         gdata->traceInfoCount++;
 222         hashIndex = (int)(hashCode & HASH_INDEX_MASK);
 223         tinfo->next = gdata->hashBuckets[hashIndex];
 224         gdata->hashBuckets[hashIndex] = tinfo;
 225     }
 226     return tinfo;
 227 }
 228 
 229 /* Create hash code for a Trace */
 230 static jlong
 231 hashTrace(Trace *trace)
 232 {
 233     jlong hashCode;
 234     int   i;
 235 
 236     hashCode = 0;
 237     for ( i = 0 ; i < trace->nframes ; i++ ) {
 238         hashCode = (hashCode << 3) +
 239                 (jlong)(ptrdiff_t)(void*)(trace->frames[i].method);
 240         hashCode = (hashCode << 2) +
 241                 (jlong)(trace->frames[i].location);
 242     }
 243     hashCode = (hashCode << 3) + trace->nframes;
 244     hashCode += trace->flavor;
 245     return hashCode;
 246 }
 247 
 248 /* Lookup or create a new TraceInfo */
 249 static TraceInfo *
 250 lookupOrEnter(jvmtiEnv *jvmti, Trace *trace, TraceFlavor flavor)
 251 {
 252     TraceInfo *tinfo;
 253     jlong      hashCode;
 254 
 255     /* Calculate hash code (outside critical section to lessen contention) */
 256     hashCode = hashTrace(trace);
 257 
 258     /* Do a lookup in the hash table */
 259     enterCriticalSection(jvmti); {
 260         TraceInfo *prev;
 261         int        hashIndex;
 262 
 263         /* Start with first item in hash buck chain */
 264         prev = NULL;
 265         hashIndex = (int)(hashCode & HASH_INDEX_MASK);
 266         tinfo = gdata->hashBuckets[hashIndex];
 267         while ( tinfo != NULL ) {
 268             if ( tinfo->hashCode == hashCode &&
 269                  memcmp(trace, &(tinfo->trace), sizeof(Trace))==0 ) {
 270                  /* We found one that matches, move to head of bucket chain */
 271                  if ( prev != NULL ) {
 272                      /* Remove from list and add to head of list */
 273                      prev->next = tinfo->next;
 274                      tinfo->next = gdata->hashBuckets[hashIndex];
 275                      gdata->hashBuckets[hashIndex] = tinfo;
 276                  }
 277                  /* Break out */
 278                  break;
 279             }
 280             prev = tinfo;
 281             tinfo = tinfo->next;
 282         }
 283 
 284         /* If we didn't find anything we need to enter a new entry */
 285         if ( tinfo == NULL ) {
 286             /* Create new hash table element */
 287             tinfo = newTraceInfo(trace, hashCode, flavor);
 288         }
 289 
 290         /* Update stats */
 291         (void)updateStats(tinfo);
 292 
 293     } exitCriticalSection(jvmti);
 294 
 295     return tinfo;
 296 }
 297 
 298 /* Get TraceInfo for this allocation */
 299 static TraceInfo *
 300 findTraceInfo(jvmtiEnv *jvmti, jthread thread, TraceFlavor flavor)
 301 {
 302     TraceInfo *tinfo;
 303     jvmtiError error;
 304 
 305     tinfo = NULL;
 306     if ( thread != NULL ) {
 307         static Trace  empty;
 308         Trace         trace;
 309 
 310         /* Before VM_INIT thread could be NULL, watch out */
 311         trace = empty;
 312         error = (*jvmti)->GetStackTrace(jvmti, thread, 0, MAX_FRAMES+2,
 313                             trace.frames, &(trace.nframes));
 314         /* If we get a PHASE error, the VM isn't ready, or it died */
 315         if ( error == JVMTI_ERROR_WRONG_PHASE ) {
 316             /* It is assumed this is before VM_INIT */
 317             if ( flavor == TRACE_USER ) {
 318                 tinfo = emptyTrace(TRACE_BEFORE_VM_INIT);
 319             } else {
 320                 tinfo = emptyTrace(flavor);
 321             }
 322         } else {
 323             check_jvmti_error(jvmti, error, "Cannot get stack trace");
 324             /* Lookup this entry */
 325             tinfo = lookupOrEnter(jvmti, &trace, flavor);
 326         }
 327     } else {
 328         /* If thread==NULL, it's assumed this is before VM_START */
 329         if ( flavor == TRACE_USER ) {
 330             tinfo = emptyTrace(TRACE_BEFORE_VM_START);
 331         } else {
 332             tinfo = emptyTrace(flavor);
 333         }
 334     }
 335     return tinfo;
 336 }
 337 
 338 /* Tag an object with a TraceInfo pointer. */
 339 static void
 340 tagObjectWithTraceInfo(jvmtiEnv *jvmti, jobject object, TraceInfo *tinfo)
 341 {
 342     jvmtiError error;
 343     jlong      tag;
 344 
 345     /* Tag this object with this TraceInfo pointer */
 346     tag = (jlong)(ptrdiff_t)(void*)tinfo;
 347     error = (*jvmti)->SetTag(jvmti, object, tag);
 348     check_jvmti_error(jvmti, error, "Cannot tag object");
 349 }
 350 
 351 /* Java Native Method for Object.<init> */
 352 static void JNICALL
 353 HEAP_TRACKER_native_newobj(JNIEnv *env, jclass klass, jthread thread, jobject o)
 354 {
 355     TraceInfo *tinfo;
 356 
 357     if ( gdata->vmDead ) {
 358         return;
 359     }
 360     tinfo = findTraceInfo(gdata->jvmti, thread, TRACE_USER);
 361     tagObjectWithTraceInfo(gdata->jvmti, o, tinfo);
 362 }
 363 
 364 /* Java Native Method for newarray */
 365 static void JNICALL
 366 HEAP_TRACKER_native_newarr(JNIEnv *env, jclass klass, jthread thread, jobject a)
 367 {
 368     TraceInfo *tinfo;
 369 
 370     if ( gdata->vmDead ) {
 371         return;
 372     }
 373     tinfo = findTraceInfo(gdata->jvmti, thread, TRACE_USER);
 374     tagObjectWithTraceInfo(gdata->jvmti, a, tinfo);
 375 }
 376 
 377 /* Callback for JVMTI_EVENT_VM_START */
 378 static void JNICALL
 379 cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)
 380 {
 381     enterCriticalSection(jvmti); {
 382         jclass klass;
 383         jfieldID field;
 384         jint rc;
 385 
 386         /* Java Native Methods for class */
 387         static JNINativeMethod registry[2] = {
 388             {STRING(HEAP_TRACKER_native_newobj), "(Ljava/lang/Object;Ljava/lang/Object;)V",
 389                 (void*)&HEAP_TRACKER_native_newobj},
 390             {STRING(HEAP_TRACKER_native_newarr), "(Ljava/lang/Object;Ljava/lang/Object;)V",
 391                 (void*)&HEAP_TRACKER_native_newarr}
 392         };
 393 
 394         /* Register Natives for class whose methods we use */
 395         klass = (*env)->FindClass(env, STRING(HEAP_TRACKER_class));
 396         if ( klass == NULL ) {
 397             fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",
 398                         STRING(HEAP_TRACKER_class));
 399         }
 400         rc = (*env)->RegisterNatives(env, klass, registry, 2);
 401         if ( rc != 0 ) {
 402             fatal_error("ERROR: JNI: Cannot register natives for class %s\n",
 403                         STRING(HEAP_TRACKER_class));
 404         }
 405 
 406         /* Engage calls. */
 407         field = (*env)->GetStaticFieldID(env, klass, STRING(HEAP_TRACKER_engaged), "I");
 408         if ( field == NULL ) {
 409             fatal_error("ERROR: JNI: Cannot get field from %s\n",
 410                         STRING(HEAP_TRACKER_class));
 411         }
 412         (*env)->SetStaticIntField(env, klass, field, 1);
 413 
 414         /* Indicate VM has started */
 415         gdata->vmStarted = JNI_TRUE;
 416 
 417     } exitCriticalSection(jvmti);
 418 }
 419 
 420 /* Iterate Through Heap callback */
 421 static jint JNICALL
 422 cbObjectTagger(jlong class_tag, jlong size, jlong* tag_ptr, jint length,
 423                void *user_data)
 424 {
 425     TraceInfo *tinfo;
 426 
 427     tinfo = emptyTrace(TRACE_BEFORE_VM_INIT);
 428     *tag_ptr = (jlong)(ptrdiff_t)(void*)tinfo;
 429     return JVMTI_VISIT_OBJECTS;
 430 }
 431 
 432 /* Callback for JVMTI_EVENT_VM_INIT */
 433 static void JNICALL
 434 cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
 435 {
 436     jvmtiHeapCallbacks heapCallbacks;
 437     jvmtiError         error;
 438 
 439     /* Iterate through heap, find all untagged objects allocated before this */
 440     (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks));
 441     heapCallbacks.heap_iteration_callback = &cbObjectTagger;
 442     error = (*jvmti)->IterateThroughHeap(jvmti, JVMTI_HEAP_FILTER_TAGGED,
 443                                          NULL, &heapCallbacks, NULL);
 444     check_jvmti_error(jvmti, error, "Cannot iterate through heap");
 445 
 446     enterCriticalSection(jvmti); {
 447 
 448         /* Indicate VM is initialized */
 449         gdata->vmInitialized = JNI_TRUE;
 450 
 451     } exitCriticalSection(jvmti);
 452 }
 453 
 454 /* Iterate Through Heap callback */
 455 static jint JNICALL
 456 cbObjectSpaceCounter(jlong class_tag, jlong size, jlong* tag_ptr, jint length,
 457                      void *user_data)
 458 {
 459     TraceInfo *tinfo;
 460 
 461     tinfo = (TraceInfo*)(ptrdiff_t)(*tag_ptr);
 462     if ( tinfo == NULL ) {
 463         tinfo = emptyTrace(TRACE_MYSTERY);
 464         *tag_ptr = (jlong)(ptrdiff_t)(void*)tinfo;
 465     }
 466     tinfo->totalSpace += size;
 467     return JVMTI_VISIT_OBJECTS;
 468 }
 469 
 470 /* Qsort compare function */
 471 static int
 472 compareInfo(const void *p1, const void *p2)
 473 {
 474     TraceInfo *tinfo1, *tinfo2;
 475 
 476     tinfo1 = *((TraceInfo**)p1);
 477     tinfo2 = *((TraceInfo**)p2);
 478     return (int)(tinfo2->totalSpace - tinfo1->totalSpace);
 479 }
 480 
 481 /* Frame to text */
 482 static void
 483 frameToString(jvmtiEnv *jvmti, char *buf, int buflen, jvmtiFrameInfo *finfo)
 484 {
 485     jvmtiError           error;
 486     jclass               klass;
 487     char                *signature;
 488     char                *methodname;
 489     char                *methodsig;
 490     jboolean             isNative;
 491     char                *filename;
 492     int                  lineCount;
 493     jvmtiLineNumberEntry*lineTable;
 494     int                  lineNumber;
 495 
 496     /* Initialize defaults */
 497     buf[0]     = 0;
 498     klass      = NULL;
 499     signature  = NULL;
 500     methodname = NULL;
 501     methodsig  = NULL;
 502     isNative   = JNI_FALSE;
 503     filename   = NULL;
 504     lineCount  = 0;
 505     lineTable  = NULL;
 506     lineNumber = 0;
 507 
 508     /* Get jclass object for the jmethodID */
 509     error = (*jvmti)->GetMethodDeclaringClass(jvmti, finfo->method, &klass);
 510     check_jvmti_error(jvmti, error, "Cannot get method's class");
 511 
 512     /* Get the class signature */
 513     error = (*jvmti)->GetClassSignature(jvmti, klass, &signature, NULL);
 514     check_jvmti_error(jvmti, error, "Cannot get class signature");
 515 
 516     /* Skip all this if it's our own Tracker method */
 517     if ( strcmp(signature, "L" STRING(HEAP_TRACKER_class) ";" ) == 0 ) {
 518         deallocate(jvmti, signature);
 519         return;
 520     }
 521 
 522     /* Get the name and signature for the method */
 523     error = (*jvmti)->GetMethodName(jvmti, finfo->method,
 524                 &methodname, &methodsig, NULL);
 525     check_jvmti_error(jvmti, error, "Cannot method name");
 526 
 527     /* Check to see if it's a native method, which means no lineNumber */
 528     error = (*jvmti)->IsMethodNative(jvmti, finfo->method, &isNative);
 529     check_jvmti_error(jvmti, error, "Cannot get method native status");
 530 
 531     /* Get source file name */
 532     error = (*jvmti)->GetSourceFileName(jvmti, klass, &filename);
 533     if ( error != JVMTI_ERROR_NONE && error != JVMTI_ERROR_ABSENT_INFORMATION ) {
 534         check_jvmti_error(jvmti, error, "Cannot get source filename");
 535     }
 536 
 537     /* Get lineNumber if we can */
 538     if ( !isNative ) {
 539         int i;
 540 
 541         /* Get method line table */
 542         error = (*jvmti)->GetLineNumberTable(jvmti, finfo->method, &lineCount, &lineTable);
 543         if ( error == JVMTI_ERROR_NONE ) {
 544             /* Search for line */
 545             lineNumber = lineTable[0].line_number;
 546             for ( i = 1 ; i < lineCount ; i++ ) {
 547                 if ( finfo->location < lineTable[i].start_location ) {
 548                     break;
 549                 }
 550                 lineNumber = lineTable[i].line_number;
 551             }
 552         } else if ( error != JVMTI_ERROR_ABSENT_INFORMATION ) {
 553             check_jvmti_error(jvmti, error, "Cannot get method line table");
 554         }
 555     }
 556 
 557     /* Create string for this frame location.
 558      *    NOTE: These char* quantities are mUTF (Modified UTF-8) bytes
 559      *          and should actually be converted to the default system
 560      *          character encoding. Sending them to things like
 561      *          printf() without converting them is actually an I18n
 562      *          (Internationalization) error.
 563      */
 564     (void)sprintf(buf, "%s.%s@%d[%s:%d]",
 565             (signature==NULL?"UnknownClass":signature),
 566             (methodname==NULL?"UnknownMethod":methodname),
 567             (int)finfo->location,
 568             (filename==NULL?"UnknownFile":filename),
 569             lineNumber);
 570 
 571     /* Free up JVMTI space allocated by the above calls */
 572     deallocate(jvmti, signature);
 573     deallocate(jvmti, methodname);
 574     deallocate(jvmti, methodsig);
 575     deallocate(jvmti, filename);
 576     deallocate(jvmti, lineTable);
 577 }
 578 
 579 /* Print the information */
 580 static void
 581 printTraceInfo(jvmtiEnv *jvmti, int index, TraceInfo* tinfo)
 582 {
 583     if ( tinfo == NULL ) {
 584         fatal_error("%d: NULL ENTRY ERROR\n", index);
 585         return;
 586     }
 587 
 588     stdout_message("%2d: %7d bytes %5d objects %5d live %s",
 589                 index, (int)tinfo->totalSpace, tinfo->totalCount,
 590                 tinfo->useCount, flavorDesc[tinfo->trace.flavor]);
 591 
 592     if (  tinfo->trace.nframes > 0 ) {
 593         int i;
 594         int fcount;
 595 
 596         fcount = 0;
 597         stdout_message(" stack=(");
 598         for ( i = 0 ; i < tinfo->trace.nframes ; i++ ) {
 599             char buf[4096];
 600 
 601             frameToString(jvmti, buf, (int)sizeof(buf), tinfo->trace.frames+i);
 602             if ( buf[0] == 0 ) {
 603                 continue; /* Skip the ones that are from Tracker class */
 604             }
 605             fcount++;
 606             stdout_message("%s", buf);
 607             if ( i < (tinfo->trace.nframes-1) ) {
 608                 stdout_message(",");
 609             }
 610         }
 611         stdout_message(") nframes=%d\n", fcount);
 612     } else {
 613         stdout_message(" stack=<empty>\n");
 614     }
 615 }
 616 
 617 /* Callback for JVMTI_EVENT_VM_DEATH */
 618 static void JNICALL
 619 cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
 620 {
 621     jvmtiHeapCallbacks heapCallbacks;
 622     jvmtiError         error;
 623 
 624     /* These are purposely done outside the critical section */
 625 
 626     /* Force garbage collection now so we get our ObjectFree calls */
 627     error = (*jvmti)->ForceGarbageCollection(jvmti);
 628     check_jvmti_error(jvmti, error, "Cannot force garbage collection");
 629 
 630     /* Iterate through heap and find all objects */
 631     (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks));
 632     heapCallbacks.heap_iteration_callback = &cbObjectSpaceCounter;
 633     error = (*jvmti)->IterateThroughHeap(jvmti, 0, NULL, &heapCallbacks, NULL);
 634     check_jvmti_error(jvmti, error, "Cannot iterate through heap");
 635 
 636     /* Process VM Death */
 637     enterCriticalSection(jvmti); {
 638         jclass              klass;
 639         jfieldID            field;
 640         jvmtiEventCallbacks callbacks;
 641 
 642         /* Disengage calls in HEAP_TRACKER_class. */
 643         klass = (*env)->FindClass(env, STRING(HEAP_TRACKER_class));
 644         if ( klass == NULL ) {
 645             fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",
 646                         STRING(HEAP_TRACKER_class));
 647         }
 648         field = (*env)->GetStaticFieldID(env, klass, STRING(HEAP_TRACKER_engaged), "I");
 649         if ( field == NULL ) {
 650             fatal_error("ERROR: JNI: Cannot get field from %s\n",
 651                         STRING(HEAP_TRACKER_class));
 652         }
 653         (*env)->SetStaticIntField(env, klass, field, 0);
 654 
 655         /* The critical section here is important to hold back the VM death
 656          *    until all other callbacks have completed.
 657          */
 658 
 659         /* Clear out all callbacks. */
 660         (void)memset(&callbacks,0, sizeof(callbacks));
 661         error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks,
 662                                             (jint)sizeof(callbacks));
 663         check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
 664 
 665         /* Since this critical section could be holding up other threads
 666          *   in other event callbacks, we need to indicate that the VM is
 667          *   dead so that the other callbacks can short circuit their work.
 668          *   We don't expect an further events after VmDeath but we do need
 669          *   to be careful that existing threads might be in our own agent
 670          *   callback code.
 671          */
 672         gdata->vmDead = JNI_TRUE;
 673 
 674         /* Dump all objects */
 675         if ( gdata->traceInfoCount > 0 ) {
 676             TraceInfo **list;
 677             int         count;
 678             int         i;
 679 
 680             stdout_message("Dumping heap trace information\n");
 681 
 682             /* Create single array of pointers to TraceInfo's, sort, and
 683              *   print top gdata->maxDump top space users.
 684              */
 685             list = (TraceInfo**)calloc(gdata->traceInfoCount,
 686                                               sizeof(TraceInfo*));
 687             if ( list == NULL ) {
 688                 fatal_error("ERROR: Ran out of malloc() space\n");
 689             }
 690             count = 0;
 691             for ( i = 0 ; i < HASH_BUCKET_COUNT ; i++ ) {
 692                 TraceInfo *tinfo;
 693 
 694                 tinfo = gdata->hashBuckets[i];
 695                 while ( tinfo != NULL ) {
 696                     if ( count < gdata->traceInfoCount ) {
 697                         list[count++] = tinfo;
 698                     }
 699                     tinfo = tinfo->next;
 700                 }
 701             }
 702             if ( count != gdata->traceInfoCount ) {
 703                 fatal_error("ERROR: Count found by iterate doesn't match ours:"
 704                         " count=%d != traceInfoCount==%d\n",
 705                         count, gdata->traceInfoCount);
 706             }
 707             qsort(list, count, sizeof(TraceInfo*), &compareInfo);
 708             for ( i = 0 ; i < count ; i++ ) {
 709                 if ( i >= gdata->maxDump ) {
 710                     break;
 711                 }
 712                 printTraceInfo(jvmti, i+1, list[i]);
 713             }
 714             (void)free(list);
 715         }
 716 
 717     } exitCriticalSection(jvmti);
 718 
 719 }
 720 
 721 /* Callback for JVMTI_EVENT_VM_OBJECT_ALLOC */
 722 static void JNICALL
 723 cbVMObjectAlloc(jvmtiEnv *jvmti, JNIEnv *env, jthread thread,
 724                 jobject object, jclass object_klass, jlong size)
 725 {
 726     TraceInfo *tinfo;
 727 
 728     if ( gdata->vmDead ) {
 729         return;
 730     }
 731     tinfo = findTraceInfo(jvmti, thread, TRACE_VM_OBJECT);
 732     tagObjectWithTraceInfo(jvmti, object, tinfo);
 733 }
 734 
 735 /* Callback for JVMTI_EVENT_OBJECT_FREE */
 736 static void JNICALL
 737 cbObjectFree(jvmtiEnv *jvmti, jlong tag)
 738 {
 739     TraceInfo *tinfo;
 740 
 741     if ( gdata->vmDead ) {
 742         return;
 743     }
 744 
 745     /* The object tag is actually a pointer to a TraceInfo structure */
 746     tinfo = (TraceInfo*)(void*)(ptrdiff_t)tag;
 747 
 748     /* Decrement the use count */
 749     tinfo->useCount--;
 750 }
 751 
 752 /* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
 753 static void JNICALL
 754 cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,
 755                 jclass class_being_redefined, jobject loader,
 756                 const char* name, jobject protection_domain,
 757                 jint class_data_len, const unsigned char* class_data,
 758                 jint* new_class_data_len, unsigned char** new_class_data)
 759 {
 760     enterCriticalSection(jvmti); {
 761         /* It's possible we get here right after VmDeath event, be careful */
 762         if ( !gdata->vmDead ) {
 763 
 764             const char * classname;
 765 
 766             /* Name can be NULL, make sure we avoid SEGV's */
 767             if ( name == NULL ) {
 768                 classname = java_crw_demo_classname(class_data, class_data_len,
 769                                 NULL);
 770                 if ( classname == NULL ) {
 771                     fatal_error("ERROR: No classname in classfile\n");
 772                 }
 773             } else {
 774                 classname = strdup(name);
 775                 if ( classname == NULL ) {
 776                     fatal_error("ERROR: Ran out of malloc() space\n");
 777                 }
 778             }
 779 
 780             *new_class_data_len = 0;
 781             *new_class_data     = NULL;
 782 
 783             /* The tracker class itself? */
 784             if ( strcmp(classname, STRING(HEAP_TRACKER_class)) != 0 ) {
 785                 jint           cnum;
 786                 int            systemClass;
 787                 unsigned char *newImage;
 788                 long           newLength;
 789 
 790                 /* Get number for every class file image loaded */
 791                 cnum = gdata->ccount++;
 792 
 793                 /* Is it a system class? If the class load is before VmStart
 794                  *   then we will consider it a system class that should
 795                  *   be treated carefully. (See java_crw_demo)
 796                  */
 797                 systemClass = 0;
 798                 if ( !gdata->vmStarted ) {
 799                     systemClass = 1;
 800                 }
 801 
 802                 newImage = NULL;
 803                 newLength = 0;
 804 
 805                 /* Call the class file reader/write demo code */
 806                 java_crw_demo(cnum,
 807                     classname,
 808                     class_data,
 809                     class_data_len,
 810                     systemClass,
 811                     STRING(HEAP_TRACKER_class),
 812                     "L" STRING(HEAP_TRACKER_class) ";",
 813                     NULL, NULL,
 814                     NULL, NULL,
 815                     STRING(HEAP_TRACKER_newobj), "(Ljava/lang/Object;)V",
 816                     STRING(HEAP_TRACKER_newarr), "(Ljava/lang/Object;)V",
 817                     &newImage,
 818                     &newLength,
 819                     NULL,
 820                     NULL);
 821 
 822                 /* If we got back a new class image, return it back as "the"
 823                  *   new class image. This must be JVMTI Allocate space.
 824                  */
 825                 if ( newLength > 0 ) {
 826                     unsigned char *jvmti_space;
 827 
 828                     jvmti_space = (unsigned char *)allocate(jvmti, (jint)newLength);
 829                     (void)memcpy((void*)jvmti_space, (void*)newImage, (int)newLength);
 830                     *new_class_data_len = (jint)newLength;
 831                     *new_class_data     = jvmti_space; /* VM will deallocate */
 832                 }
 833 
 834                 /* Always free up the space we get from java_crw_demo() */
 835                 if ( newImage != NULL ) {
 836                     (void)free((void*)newImage); /* Free malloc() space with free() */
 837                 }
 838             }
 839 
 840             (void)free((void*)classname);
 841         }
 842     } exitCriticalSection(jvmti);
 843 }
 844 
 845 /* Parse the options for this heapTracker agent */
 846 static void
 847 parse_agent_options(char *options)
 848 {
 849     #define MAX_TOKEN_LENGTH        16
 850     char  token[MAX_TOKEN_LENGTH];
 851     char *next;
 852 
 853     /* Defaults */
 854     gdata->maxDump = 20;
 855 
 856     /* Parse options and set flags in gdata */
 857     if ( options==NULL ) {
 858         return;
 859     }
 860 
 861     /* Get the first token from the options string. */
 862     next = get_token(options, ",=", token, (int)sizeof(token));
 863 
 864     /* While not at the end of the options string, process this option. */
 865     while ( next != NULL ) {
 866         if ( strcmp(token,"help")==0 ) {
 867             stdout_message("The heapTracker JVMTI demo agent\n");
 868             stdout_message("\n");
 869             stdout_message(" java -agent:heapTracker[=options] ...\n");
 870             stdout_message("\n");
 871             stdout_message("The options are comma separated:\n");
 872             stdout_message("\t help\t\t\t Print help information\n");
 873             stdout_message("\t maxDump=n\t\t\t How many TraceInfo's to dump\n");
 874             stdout_message("\n");
 875             exit(0);
 876         } else if ( strcmp(token,"maxDump")==0 ) {
 877             char  number[MAX_TOKEN_LENGTH];
 878 
 879             next = get_token(next, ",=", number, (int)sizeof(number));
 880             if ( next == NULL ) {
 881                 fatal_error("ERROR: Cannot parse maxDump=number: %s\n", options);
 882             }
 883             gdata->maxDump = atoi(number);
 884         } else if ( token[0]!=0 ) {
 885             /* We got a non-empty token and we don't know what it is. */
 886             fatal_error("ERROR: Unknown option: %s\n", token);
 887         }
 888         /* Get the next token (returns NULL if there are no more) */
 889         next = get_token(next, ",=", token, (int)sizeof(token));
 890     }
 891 }
 892 
 893 /* Agent_OnLoad: This is called immediately after the shared library is
 894  *   loaded. This is the first code executed.
 895  */
 896 JNIEXPORT jint JNICALL
 897 DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
 898 {
 899     static GlobalAgentData data;
 900     jvmtiEnv              *jvmti;
 901     jvmtiError             error;
 902     jint                   res;
 903     TraceFlavor            flavor;
 904     jvmtiCapabilities      capabilities;
 905     jvmtiEventCallbacks    callbacks;
 906     static Trace           empty;
 907 
 908     /* Setup initial global agent data area
 909      *   Use of static/extern data should be handled carefully here.
 910      *   We need to make sure that we are able to cleanup after ourselves
 911      *     so anything allocated in this library needs to be freed in
 912      *     the Agent_OnUnload() function.
 913      */
 914     (void)memset((void*)&data, 0, sizeof(data));
 915     gdata = &data;
 916 
 917     /* First thing we need to do is get the jvmtiEnv* or JVMTI environment */
 918     res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);
 919     if (res != JNI_OK) {
 920         /* This means that the VM was unable to obtain this version of the
 921          *   JVMTI interface, this is a fatal error.
 922          */
 923         fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"
 924                 " is your JDK a 5.0 or newer version?"
 925                 " JNIEnv's GetEnv() returned %d\n",
 926                JVMTI_VERSION_1, res);
 927     }
 928 
 929     /* Here we save the jvmtiEnv* for Agent_OnUnload(). */
 930     gdata->jvmti = jvmti;
 931 
 932     /* Parse any options supplied on java command line */
 933     parse_agent_options(options);
 934 
 935     /* Immediately after getting the jvmtiEnv* we need to ask for the
 936      *   capabilities this agent will need.
 937      */
 938     (void)memset(&capabilities,0, sizeof(capabilities));
 939     capabilities.can_generate_all_class_hook_events = 1;
 940     capabilities.can_tag_objects  = 1;
 941     capabilities.can_generate_object_free_events  = 1;
 942     capabilities.can_get_source_file_name  = 1;
 943     capabilities.can_get_line_numbers  = 1;
 944     capabilities.can_generate_vm_object_alloc_events  = 1;
 945     error = (*jvmti)->AddCapabilities(jvmti, &capabilities);
 946     check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");
 947 
 948     /* Next we need to provide the pointers to the callback functions to
 949      *   to this jvmtiEnv*
 950      */
 951     (void)memset(&callbacks,0, sizeof(callbacks));
 952     /* JVMTI_EVENT_VM_START */
 953     callbacks.VMStart           = &cbVMStart;
 954     /* JVMTI_EVENT_VM_INIT */
 955     callbacks.VMInit            = &cbVMInit;
 956     /* JVMTI_EVENT_VM_DEATH */
 957     callbacks.VMDeath           = &cbVMDeath;
 958     /* JVMTI_EVENT_OBJECT_FREE */
 959     callbacks.ObjectFree        = &cbObjectFree;
 960     /* JVMTI_EVENT_VM_OBJECT_ALLOC */
 961     callbacks.VMObjectAlloc     = &cbVMObjectAlloc;
 962     /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
 963     callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
 964     error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
 965     check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
 966 
 967     /* At first the only initial events we are interested in are VM
 968      *   initialization, VM death, and Class File Loads.
 969      *   Once the VM is initialized we will request more events.
 970      */
 971     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 972                           JVMTI_EVENT_VM_START, (jthread)NULL);
 973     check_jvmti_error(jvmti, error, "Cannot set event notification");
 974     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 975                           JVMTI_EVENT_VM_INIT, (jthread)NULL);
 976     check_jvmti_error(jvmti, error, "Cannot set event notification");
 977     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 978                           JVMTI_EVENT_VM_DEATH, (jthread)NULL);
 979     check_jvmti_error(jvmti, error, "Cannot set event notification");
 980     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 981                           JVMTI_EVENT_OBJECT_FREE, (jthread)NULL);
 982     check_jvmti_error(jvmti, error, "Cannot set event notification");
 983     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 984                           JVMTI_EVENT_VM_OBJECT_ALLOC, (jthread)NULL);
 985     check_jvmti_error(jvmti, error, "Cannot set event notification");
 986     error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
 987                           JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
 988     check_jvmti_error(jvmti, error, "Cannot set event notification");
 989 
 990     /* Here we create a raw monitor for our use in this agent to
 991      *   protect critical sections of code.
 992      */
 993     error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));
 994     check_jvmti_error(jvmti, error, "Cannot create raw monitor");
 995 
 996     /* Create the TraceInfo for various flavors of empty traces */
 997     for ( flavor = TRACE_FIRST ; flavor <= TRACE_LAST ; flavor++ ) {
 998         gdata->emptyTrace[flavor] =
 999                newTraceInfo(&empty, hashTrace(&empty), flavor);
1000     }
1001 
1002     /* Add jar file to boot classpath */
1003     add_demo_jar_to_bootclasspath(jvmti, "heapTracker");
1004 
1005     /* We return JNI_OK to signify success */
1006     return JNI_OK;
1007 }
1008 
1009 /* Agent_OnUnload: This is called immediately before the shared library is
1010  *   unloaded. This is the last code executed.
1011  */
1012 JNIEXPORT void JNICALL
1013 DEF_Agent_OnUnload(JavaVM *vm)
1014 {
1015     /* Skip any cleanup, VM is about to die anyway */
1016 }