author | bobv |
Mon, 19 Oct 2015 13:41:09 -0400 | |
changeset 33653 | c1ee09fe3274 |
parent 25859 | 3317bb8137f4 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
33653
c1ee09fe3274
8136556: Add the ability to perform static builds of MacOSX x64 binaries
bobv
parents:
25859
diff
changeset
|
2 |
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
* |
|
5506 | 15 |
* - Neither the name of Oracle nor the names of its |
2 | 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 |
||
10292
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
32 |
/* |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
33 |
* This source code is provided to illustrate the usage of a given feature |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
34 |
* or technique and has been deliberately simplified. Additional steps |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
35 |
* required for a production-quality application, such as security checks, |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
36 |
* input validation and proper error handling, might not be present in |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
37 |
* this sample code. |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
38 |
*/ |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
39 |
|
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
9035
diff
changeset
|
40 |
|
2 | 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> */ |
|
7798
be6c7160f8fc
6927816: Demo crash in heaptracker with Non-Sun JDK due to possible violation of JNI spec
alanb
parents:
5506
diff
changeset
|
352 |
static void JNICALL |
2 | 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 */ |
|
7798
be6c7160f8fc
6927816: Demo crash in heaptracker with Non-Sun JDK due to possible violation of JNI spec
alanb
parents:
5506
diff
changeset
|
365 |
static void JNICALL |
2 | 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 |
|
33653
c1ee09fe3274
8136556: Add the ability to perform static builds of MacOSX x64 binaries
bobv
parents:
25859
diff
changeset
|
897 |
DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) |
2 | 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 |
|
33653
c1ee09fe3274
8136556: Add the ability to perform static builds of MacOSX x64 binaries
bobv
parents:
25859
diff
changeset
|
1013 |
DEF_Agent_OnUnload(JavaVM *vm) |
2 | 1014 |
{ |
1015 |
/* Skip any cleanup, VM is about to die anyway */ |
|
1016 |
} |