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 }