1 /* 2 * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41 #include <stdio.h> 42 #include <stddef.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 #include "jni.h" 47 #include "jvmti.h" 48 49 #include "agent_util.h" 50 51 /* Global static data */ 52 typedef struct { 53 jboolean vmDeathCalled; 54 jboolean dumpInProgress; 55 jrawMonitorID lock; 56 } GlobalData; 57 static GlobalData globalData, *gdata = &globalData; 58 59 /* Typedef to hold class details */ 60 typedef struct { 61 char *signature; 62 int count; 63 int space; 64 } ClassDetails; 65 66 /* Enter agent monitor protected section */ 67 static void 68 enterAgentMonitor(jvmtiEnv *jvmti) 69 { 70 jvmtiError err; 71 72 err = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock); 73 check_jvmti_error(jvmti, err, "raw monitor enter"); 74 } 75 76 /* Exit agent monitor protected section */ 77 static void 78 exitAgentMonitor(jvmtiEnv *jvmti) 79 { 80 jvmtiError err; 81 82 err = (*jvmti)->RawMonitorExit(jvmti, gdata->lock); 83 check_jvmti_error(jvmti, err, "raw monitor exit"); 84 } 85 86 /* Heap object callback */ 87 static jint JNICALL 88 cbHeapObject(jlong class_tag, jlong size, jlong* tag_ptr, jint length, 89 void* user_data) 90 { 91 if ( class_tag != (jlong)0 ) { 92 ClassDetails *d; 93 94 d = (ClassDetails*)(void*)(ptrdiff_t)class_tag; 95 (*((jint*)(user_data)))++; 96 d->count++; 97 d->space += (int)size; 98 } 99 return JVMTI_VISIT_OBJECTS; 100 } 101 102 /* Compare two ClassDetails */ 103 static int 104 compareDetails(const void *p1, const void *p2) 105 { 106 return ((ClassDetails*)p2)->space - ((ClassDetails*)p1)->space; 107 } 108 109 /* Callback for JVMTI_EVENT_DATA_DUMP_REQUEST (Ctrl-\ or at exit) */ 110 static void JNICALL 111 dataDumpRequest(jvmtiEnv *jvmti) 112 { 113 enterAgentMonitor(jvmti); { 114 if ( !gdata->vmDeathCalled && !gdata->dumpInProgress ) { 115 jvmtiHeapCallbacks heapCallbacks; 116 ClassDetails *details; 117 jvmtiError err; 118 jclass *classes; 119 jint totalCount; 120 jint count; 121 jint i; 122 123 gdata->dumpInProgress = JNI_TRUE; 124 125 /* Get all the loaded classes */ 126 err = (*jvmti)->GetLoadedClasses(jvmti, &count, &classes); 127 check_jvmti_error(jvmti, err, "get loaded classes"); 128 129 /* Setup an area to hold details about these classes */ 130 details = (ClassDetails*)calloc(sizeof(ClassDetails), count); 131 if ( details == NULL ) { 132 fatal_error("ERROR: Ran out of malloc space\n"); 133 } 134 for ( i = 0 ; i < count ; i++ ) { 135 char *sig; 136 137 /* Get and save the class signature */ 138 err = (*jvmti)->GetClassSignature(jvmti, classes[i], &sig, NULL); 139 check_jvmti_error(jvmti, err, "get class signature"); 140 if ( sig == NULL ) { 141 fatal_error("ERROR: No class signature found\n"); 142 } 143 details[i].signature = strdup(sig); 144 deallocate(jvmti, sig); 145 146 /* Tag this jclass */ 147 err = (*jvmti)->SetTag(jvmti, classes[i], 148 (jlong)(ptrdiff_t)(void*)(&details[i])); 149 check_jvmti_error(jvmti, err, "set object tag"); 150 } 151 152 /* Iterate through the heap and count up uses of jclass */ 153 (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks)); 154 heapCallbacks.heap_iteration_callback = &cbHeapObject; 155 totalCount = 0; 156 err = (*jvmti)->IterateThroughHeap(jvmti, 157 JVMTI_HEAP_FILTER_CLASS_UNTAGGED, NULL, 158 &heapCallbacks, (const void *)&totalCount); 159 check_jvmti_error(jvmti, err, "iterate through heap"); 160 161 /* Remove tags */ 162 for ( i = 0 ; i < count ; i++ ) { 163 /* Un-Tag this jclass */ 164 err = (*jvmti)->SetTag(jvmti, classes[i], (jlong)0); 165 check_jvmti_error(jvmti, err, "set object tag"); 166 } 167 168 /* Sort details by space used */ 169 qsort(details, count, sizeof(ClassDetails), &compareDetails); 170 171 /* Print out sorted table */ 172 stdout_message("Heap View, Total of %d objects found.\n\n", 173 totalCount); 174 175 stdout_message("Space Count Class Signature\n"); 176 stdout_message("---------- ---------- ----------------------\n"); 177 178 for ( i = 0 ; i < count ; i++ ) { 179 if ( details[i].space == 0 || i > 20 ) { 180 break; 181 } 182 stdout_message("%10d %10d %s\n", 183 details[i].space, details[i].count, details[i].signature); 184 } 185 stdout_message("---------- ---------- ----------------------\n\n"); 186 187 /* Free up all allocated space */ 188 deallocate(jvmti, classes); 189 for ( i = 0 ; i < count ; i++ ) { 190 if ( details[i].signature != NULL ) { 191 free(details[i].signature); 192 } 193 } 194 free(details); 195 196 gdata->dumpInProgress = JNI_FALSE; 197 } 198 } exitAgentMonitor(jvmti); 199 } 200 201 /* Callback for JVMTI_EVENT_VM_INIT */ 202 static void JNICALL 203 vmInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) 204 { 205 enterAgentMonitor(jvmti); { 206 jvmtiError err; 207 208 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 209 JVMTI_EVENT_DATA_DUMP_REQUEST, NULL); 210 check_jvmti_error(jvmti, err, "set event notification"); 211 } exitAgentMonitor(jvmti); 212 } 213 214 /* Callback for JVMTI_EVENT_VM_DEATH */ 215 static void JNICALL 216 vmDeath(jvmtiEnv *jvmti, JNIEnv *env) 217 { 218 jvmtiError err; 219 220 /* Make sure everything has been garbage collected */ 221 err = (*jvmti)->ForceGarbageCollection(jvmti); 222 check_jvmti_error(jvmti, err, "force garbage collection"); 223 224 /* Disable events and dump the heap information */ 225 enterAgentMonitor(jvmti); { 226 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE, 227 JVMTI_EVENT_DATA_DUMP_REQUEST, NULL); 228 check_jvmti_error(jvmti, err, "set event notification"); 229 230 dataDumpRequest(jvmti); 231 232 gdata->vmDeathCalled = JNI_TRUE; 233 } exitAgentMonitor(jvmti); 234 } 235 236 /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */ 237 JNIEXPORT jint JNICALL 238 DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) 239 { 240 jint rc; 241 jvmtiError err; 242 jvmtiCapabilities capabilities; 243 jvmtiEventCallbacks callbacks; 244 jvmtiEnv *jvmti; 245 246 /* Get JVMTI environment */ 247 jvmti = NULL; 248 rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION); 249 if (rc != JNI_OK) { 250 fatal_error("ERROR: Unable to create jvmtiEnv, error=%d\n", rc); 251 return -1; 252 } 253 if ( jvmti == NULL ) { 254 fatal_error("ERROR: No jvmtiEnv* returned from GetEnv\n"); 255 } 256 257 /* Get/Add JVMTI capabilities */ 258 (void)memset(&capabilities, 0, sizeof(capabilities)); 259 capabilities.can_tag_objects = 1; 260 capabilities.can_generate_garbage_collection_events = 1; 261 err = (*jvmti)->AddCapabilities(jvmti, &capabilities); 262 check_jvmti_error(jvmti, err, "add capabilities"); 263 264 /* Create the raw monitor */ 265 err = (*jvmti)->CreateRawMonitor(jvmti, "agent lock", &(gdata->lock)); 266 check_jvmti_error(jvmti, err, "create raw monitor"); 267 268 /* Set callbacks and enable event notifications */ 269 memset(&callbacks, 0, sizeof(callbacks)); 270 callbacks.VMInit = &vmInit; 271 callbacks.VMDeath = &vmDeath; 272 callbacks.DataDumpRequest = &dataDumpRequest; 273 err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks)); 274 check_jvmti_error(jvmti, err, "set event callbacks"); 275 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 276 JVMTI_EVENT_VM_INIT, NULL); 277 check_jvmti_error(jvmti, err, "set event notifications"); 278 err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 279 JVMTI_EVENT_VM_DEATH, NULL); 280 check_jvmti_error(jvmti, err, "set event notifications"); 281 return 0; 282 } 283 284 /* Agent_OnUnload() is called last */ 285 JNIEXPORT void JNICALL 286 DEF_Agent_OnUnload(JavaVM *vm) 287 { 288 }