blob: 1d4742dd2ee34a882abad52f0dc44e29aa43c995 [file] [log] [blame]
/*
* Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "jni.h"
#include "jvmti.h"
#include "agent_util.h"
/* Global static data */
typedef struct {
jboolean vmDeathCalled;
jboolean dumpInProgress;
jrawMonitorID lock;
} GlobalData;
static GlobalData globalData, *gdata = &globalData;
/* Typedef to hold class details */
typedef struct {
char *signature;
int count;
int space;
} ClassDetails;
/* Enter agent monitor protected section */
static void
enterAgentMonitor(jvmtiEnv *jvmti)
{
jvmtiError err;
err = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);
check_jvmti_error(jvmti, err, "raw monitor enter");
}
/* Exit agent monitor protected section */
static void
exitAgentMonitor(jvmtiEnv *jvmti)
{
jvmtiError err;
err = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);
check_jvmti_error(jvmti, err, "raw monitor exit");
}
/* Heap object callback */
static jint JNICALL
cbHeapObject(jlong class_tag, jlong size, jlong* tag_ptr, jint length,
void* user_data)
{
if ( class_tag != (jlong)0 ) {
ClassDetails *d;
d = (ClassDetails*)(void*)(ptrdiff_t)class_tag;
(*((jint*)(user_data)))++;
d->count++;
d->space += (int)size;
}
return JVMTI_VISIT_OBJECTS;
}
/* Compare two ClassDetails */
static int
compareDetails(const void *p1, const void *p2)
{
return ((ClassDetails*)p2)->space - ((ClassDetails*)p1)->space;
}
/* Callback for JVMTI_EVENT_DATA_DUMP_REQUEST (Ctrl-\ or at exit) */
static void JNICALL
dataDumpRequest(jvmtiEnv *jvmti)
{
enterAgentMonitor(jvmti); {
if ( !gdata->vmDeathCalled && !gdata->dumpInProgress ) {
jvmtiHeapCallbacks heapCallbacks;
ClassDetails *details;
jvmtiError err;
jclass *classes;
jint totalCount;
jint count;
jint i;
gdata->dumpInProgress = JNI_TRUE;
/* Get all the loaded classes */
err = (*jvmti)->GetLoadedClasses(jvmti, &count, &classes);
check_jvmti_error(jvmti, err, "get loaded classes");
/* Setup an area to hold details about these classes */
details = (ClassDetails*)calloc(sizeof(ClassDetails), count);
if ( details == NULL ) {
fatal_error("ERROR: Ran out of malloc space\n");
}
for ( i = 0 ; i < count ; i++ ) {
char *sig;
/* Get and save the class signature */
err = (*jvmti)->GetClassSignature(jvmti, classes[i], &sig, NULL);
check_jvmti_error(jvmti, err, "get class signature");
if ( sig == NULL ) {
fatal_error("ERROR: No class signature found\n");
}
details[i].signature = strdup(sig);
deallocate(jvmti, sig);
/* Tag this jclass */
err = (*jvmti)->SetTag(jvmti, classes[i],
(jlong)(ptrdiff_t)(void*)(&details[i]));
check_jvmti_error(jvmti, err, "set object tag");
}
/* Iterate through the heap and count up uses of jclass */
(void)memset(&heapCallbacks, 0, sizeof(heapCallbacks));
heapCallbacks.heap_iteration_callback = &cbHeapObject;
totalCount = 0;
err = (*jvmti)->IterateThroughHeap(jvmti,
JVMTI_HEAP_FILTER_CLASS_UNTAGGED, NULL,
&heapCallbacks, (const void *)&totalCount);
check_jvmti_error(jvmti, err, "iterate through heap");
/* Remove tags */
for ( i = 0 ; i < count ; i++ ) {
/* Un-Tag this jclass */
err = (*jvmti)->SetTag(jvmti, classes[i], (jlong)0);
check_jvmti_error(jvmti, err, "set object tag");
}
/* Sort details by space used */
qsort(details, count, sizeof(ClassDetails), &compareDetails);
/* Print out sorted table */
stdout_message("Heap View, Total of %d objects found.\n\n",
totalCount);
stdout_message("Space Count Class Signature\n");
stdout_message("---------- ---------- ----------------------\n");
for ( i = 0 ; i < count ; i++ ) {
if ( details[i].space == 0 || i > 20 ) {
break;
}
stdout_message("%10d %10d %s\n",
details[i].space, details[i].count, details[i].signature);
}
stdout_message("---------- ---------- ----------------------\n\n");
/* Free up all allocated space */
deallocate(jvmti, classes);
for ( i = 0 ; i < count ; i++ ) {
if ( details[i].signature != NULL ) {
free(details[i].signature);
}
}
free(details);
gdata->dumpInProgress = JNI_FALSE;
}
} exitAgentMonitor(jvmti);
}
/* Callback for JVMTI_EVENT_VM_INIT */
static void JNICALL
vmInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
{
enterAgentMonitor(jvmti); {
jvmtiError err;
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
check_jvmti_error(jvmti, err, "set event notification");
} exitAgentMonitor(jvmti);
}
/* Callback for JVMTI_EVENT_VM_DEATH */
static void JNICALL
vmDeath(jvmtiEnv *jvmti, JNIEnv *env)
{
jvmtiError err;
/* Make sure everything has been garbage collected */
err = (*jvmti)->ForceGarbageCollection(jvmti);
check_jvmti_error(jvmti, err, "force garbage collection");
/* Disable events and dump the heap information */
enterAgentMonitor(jvmti); {
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE,
JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
check_jvmti_error(jvmti, err, "set event notification");
dataDumpRequest(jvmti);
gdata->vmDeathCalled = JNI_TRUE;
} exitAgentMonitor(jvmti);
}
/* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError err;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
jvmtiEnv *jvmti;
/* Get JVMTI environment */
jvmti = NULL;
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK) {
fatal_error("ERROR: Unable to create jvmtiEnv, error=%d\n", rc);
return -1;
}
if ( jvmti == NULL ) {
fatal_error("ERROR: No jvmtiEnv* returned from GetEnv\n");
}
/* Get/Add JVMTI capabilities */
(void)memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_tag_objects = 1;
capabilities.can_generate_garbage_collection_events = 1;
err = (*jvmti)->AddCapabilities(jvmti, &capabilities);
check_jvmti_error(jvmti, err, "add capabilities");
/* Create the raw monitor */
err = (*jvmti)->CreateRawMonitor(jvmti, "agent lock", &(gdata->lock));
check_jvmti_error(jvmti, err, "create raw monitor");
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMInit = &vmInit;
callbacks.VMDeath = &vmDeath;
callbacks.DataDumpRequest = &dataDumpRequest;
err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
check_jvmti_error(jvmti, err, "set event callbacks");
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_VM_INIT, NULL);
check_jvmti_error(jvmti, err, "set event notifications");
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_VM_DEATH, NULL);
check_jvmti_error(jvmti, err, "set event notifications");
return 0;
}
/* Agent_OnUnload() is called last */
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm)
{
}