blob: 5d1541b39c84e384f88138b7ee73141a8cb98390 [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 <stdio.h>
33#include <stddef.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "jni.h"
38#include "jvmti.h"
39
40#include "agent_util.h"
41
42/* Global static data */
43typedef struct {
44 jboolean vmDeathCalled;
45 jboolean dumpInProgress;
46 jrawMonitorID lock;
47} GlobalData;
48static GlobalData globalData, *gdata = &globalData;
49
50/* Typedef to hold class details */
51typedef struct {
52 char *signature;
53 int count;
54 int space;
55} ClassDetails;
56
57/* Enter agent monitor protected section */
58static void
59enterAgentMonitor(jvmtiEnv *jvmti)
60{
61 jvmtiError err;
62
63 err = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);
64 check_jvmti_error(jvmti, err, "raw monitor enter");
65}
66
67/* Exit agent monitor protected section */
68static void
69exitAgentMonitor(jvmtiEnv *jvmti)
70{
71 jvmtiError err;
72
73 err = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);
74 check_jvmti_error(jvmti, err, "raw monitor exit");
75}
76
77/* Heap object callback */
78static jint JNICALL
79cbHeapObject(jlong class_tag, jlong size, jlong* tag_ptr, jint length,
80 void* user_data)
81{
82 if ( class_tag != (jlong)0 ) {
83 ClassDetails *d;
84
85 d = (ClassDetails*)(void*)(ptrdiff_t)class_tag;
86 (*((jint*)(user_data)))++;
87 d->count++;
88 d->space += (int)size;
89 }
90 return JVMTI_VISIT_OBJECTS;
91}
92
93/* Compare two ClassDetails */
94static int
95compareDetails(const void *p1, const void *p2)
96{
97 return ((ClassDetails*)p2)->space - ((ClassDetails*)p1)->space;
98}
99
100/* Callback for JVMTI_EVENT_DATA_DUMP_REQUEST (Ctrl-\ or at exit) */
101static void JNICALL
102dataDumpRequest(jvmtiEnv *jvmti)
103{
104 enterAgentMonitor(jvmti); {
105 if ( !gdata->vmDeathCalled && !gdata->dumpInProgress ) {
106 jvmtiHeapCallbacks heapCallbacks;
107 ClassDetails *details;
108 jvmtiError err;
109 jclass *classes;
110 jint totalCount;
111 jint count;
112 jint i;
113
114 gdata->dumpInProgress = JNI_TRUE;
115
116 /* Get all the loaded classes */
117 err = (*jvmti)->GetLoadedClasses(jvmti, &count, &classes);
118 check_jvmti_error(jvmti, err, "get loaded classes");
119
120 /* Setup an area to hold details about these classes */
121 details = (ClassDetails*)calloc(sizeof(ClassDetails), count);
122 if ( details == NULL ) {
123 fatal_error("ERROR: Ran out of malloc space\n");
124 }
125 for ( i = 0 ; i < count ; i++ ) {
126 char *sig;
127
128 /* Get and save the class signature */
129 err = (*jvmti)->GetClassSignature(jvmti, classes[i], &sig, NULL);
130 check_jvmti_error(jvmti, err, "get class signature");
131 if ( sig == NULL ) {
132 fatal_error("ERROR: No class signature found\n");
133 }
134 details[i].signature = strdup(sig);
135 deallocate(jvmti, sig);
136
137 /* Tag this jclass */
138 err = (*jvmti)->SetTag(jvmti, classes[i],
139 (jlong)(ptrdiff_t)(void*)(&details[i]));
140 check_jvmti_error(jvmti, err, "set object tag");
141 }
142
143 /* Iterate through the heap and count up uses of jclass */
144 (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks));
145 heapCallbacks.heap_iteration_callback = &cbHeapObject;
146 totalCount = 0;
147 err = (*jvmti)->IterateThroughHeap(jvmti,
148 JVMTI_HEAP_FILTER_CLASS_UNTAGGED, NULL,
149 &heapCallbacks, (const void *)&totalCount);
150 check_jvmti_error(jvmti, err, "iterate through heap");
151
152 /* Remove tags */
153 for ( i = 0 ; i < count ; i++ ) {
154 /* Un-Tag this jclass */
155 err = (*jvmti)->SetTag(jvmti, classes[i], (jlong)0);
156 check_jvmti_error(jvmti, err, "set object tag");
157 }
158
159 /* Sort details by space used */
160 qsort(details, count, sizeof(ClassDetails), &compareDetails);
161
162 /* Print out sorted table */
163 stdout_message("Heap View, Total of %d objects found.\n\n",
164 totalCount);
165
166 stdout_message("Space Count Class Signature\n");
167 stdout_message("---------- ---------- ----------------------\n");
168
169 for ( i = 0 ; i < count ; i++ ) {
170 if ( details[i].space == 0 || i > 20 ) {
171 break;
172 }
173 stdout_message("%10d %10d %s\n",
174 details[i].space, details[i].count, details[i].signature);
175 }
176 stdout_message("---------- ---------- ----------------------\n\n");
177
178 /* Free up all allocated space */
179 deallocate(jvmti, classes);
180 for ( i = 0 ; i < count ; i++ ) {
181 if ( details[i].signature != NULL ) {
182 free(details[i].signature);
183 }
184 }
185 free(details);
186
187 gdata->dumpInProgress = JNI_FALSE;
188 }
189 } exitAgentMonitor(jvmti);
190}
191
192/* Callback for JVMTI_EVENT_VM_INIT */
193static void JNICALL
194vmInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
195{
196 enterAgentMonitor(jvmti); {
197 jvmtiError err;
198
199 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
200 JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
201 check_jvmti_error(jvmti, err, "set event notification");
202 } exitAgentMonitor(jvmti);
203}
204
205/* Callback for JVMTI_EVENT_VM_DEATH */
206static void JNICALL
207vmDeath(jvmtiEnv *jvmti, JNIEnv *env)
208{
209 jvmtiError err;
210
211 /* Make sure everything has been garbage collected */
212 err = (*jvmti)->ForceGarbageCollection(jvmti);
213 check_jvmti_error(jvmti, err, "force garbage collection");
214
215 /* Disable events and dump the heap information */
216 enterAgentMonitor(jvmti); {
217 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE,
218 JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
219 check_jvmti_error(jvmti, err, "set event notification");
220
221 dataDumpRequest(jvmti);
222
223 gdata->vmDeathCalled = JNI_TRUE;
224 } exitAgentMonitor(jvmti);
225}
226
227/* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
228JNIEXPORT jint JNICALL
229Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
230{
231 jint rc;
232 jvmtiError err;
233 jvmtiCapabilities capabilities;
234 jvmtiEventCallbacks callbacks;
235 jvmtiEnv *jvmti;
236
237 /* Get JVMTI environment */
238 jvmti = NULL;
239 rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
240 if (rc != JNI_OK) {
241 fatal_error("ERROR: Unable to create jvmtiEnv, error=%d\n", rc);
242 return -1;
243 }
244 if ( jvmti == NULL ) {
245 fatal_error("ERROR: No jvmtiEnv* returned from GetEnv\n");
246 }
247
248 /* Get/Add JVMTI capabilities */
249 (void)memset(&capabilities, 0, sizeof(capabilities));
250 capabilities.can_tag_objects = 1;
251 capabilities.can_generate_garbage_collection_events = 1;
252 err = (*jvmti)->AddCapabilities(jvmti, &capabilities);
253 check_jvmti_error(jvmti, err, "add capabilities");
254
255 /* Create the raw monitor */
256 err = (*jvmti)->CreateRawMonitor(jvmti, "agent lock", &(gdata->lock));
257 check_jvmti_error(jvmti, err, "create raw monitor");
258
259 /* Set callbacks and enable event notifications */
260 memset(&callbacks, 0, sizeof(callbacks));
261 callbacks.VMInit = &vmInit;
262 callbacks.VMDeath = &vmDeath;
263 callbacks.DataDumpRequest = &dataDumpRequest;
264 err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
265 check_jvmti_error(jvmti, err, "set event callbacks");
266 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
267 JVMTI_EVENT_VM_INIT, NULL);
268 check_jvmti_error(jvmti, err, "set event notifications");
269 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
270 JVMTI_EVENT_VM_DEATH, NULL);
271 check_jvmti_error(jvmti, err, "set event notifications");
272 return 0;
273}
274
275/* Agent_OnUnload() is called last */
276JNIEXPORT void JNICALL
277Agent_OnUnload(JavaVM *vm)
278{
279}