Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/jvmti/heapTracker/heapTracker.c
38829 views
1
/*
2
* Copyright (c) 2004, 2011, 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
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
Agent_OnUnload(JavaVM *vm)
1014
{
1015
/* Skip any cleanup, VM is about to die anyway */
1016
}
1017
1018