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