/*
*
* 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
*
* - 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.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
/* Main source file, the basic JVMTI connection/startup code. */
#include "hprof.h"
#include "java_crw_demo.h"
/*
* This file contains all the startup logic (Agent_Onload) and
* connection to the JVMTI interface.
* All JVMTI Event callbacks are in this file.
* All setting of global data (gdata) is done here.
* Options are parsed here.
* Option help messages are here.
* Termination handled here (VM_DEATH) and shutdown (Agent_OnUnload).
* Spawning of the cpu sample loop thread and listener thread is done here.
*
* Use of private 'static' data has been limited, most shared static data
* should be found in the GlobalData structure pointed to by gdata
* (see hprof.h).
*
*/
/* The default output filenames. */
/* The only global variable, defined by this library */
/* Experimental options */
/* Default trace depth */
/* Default sample interval */
/* Default cutoff */
/* Stringize macros for help. */
#define _TO_STR(a) #a
/* Macros to surround callback code (non-VM_DEATH callbacks).
* Note that this just keeps a count of the non-VM_DEATH callbacks that
* are currently active, it does not prevent these callbacks from
* operating in parallel. It's the VM_DEATH callback that will wait
* for all these callbacks to either complete and block, or just block.
* We need to hold back these threads so they don't die during the final
* VM_DEATH processing.
* If the VM_DEATH callback is active in the beginning, then this callback
* just blocks to prevent further execution of the thread.
* If the VM_DEATH callback is active at the end, then this callback
* will notify the VM_DEATH callback if it's the last one.
* gdata->callbackBlock, which will block this callback if VM_DEATH
* is running.
*
* WARNING: No not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK
* block, this will mess up the count.
*/
#define BEGIN_CALLBACK() \
{ /* BEGIN OF CALLBACK */ \
if (gdata->vm_death_callback_active) { \
/* VM_DEATH is active, we will bypass the CALLBACK CODE */ \
/* Bypassed CALLBACKS block here until VM_DEATH done */ \
} else { \
/* We will be executing the CALLBACK CODE in this case */ \
gdata->active_callbacks++; \
} \
if ( !bypass ) { \
/* BODY OF CALLBACK CODE (with no callback locks held) */
gdata->active_callbacks--; \
/* If VM_DEATH is active, and last one, send notify. */ \
if (gdata->vm_death_callback_active) { \
if (gdata->active_callbacks == 0) { \
} \
} \
/* Non-Bypassed CALLBACKS block here until VM_DEATH done */ \
} \
} /* END OF CALLBACK */
/* Forward declarations */
/* ------------------------------------------------------------------- */
/* Global data initialization */
/* Get initialized global data area */
static GlobalData *
get_gdata(void)
{
/* Create initial default values */
#ifdef DEBUG
#endif
return &data;
}
/* ------------------------------------------------------------------- */
/* Error handler callback for the java_crw_demo (classfile read write) functions. */
static void
{
}
static void
list_all_tables(void)
{
string_list();
class_list();
frame_list();
site_list();
object_list();
trace_list();
monitor_list();
tls_list();
loader_list();
}
/* ------------------------------------------------------------------- */
/* Option Parsing support */
/**
* Socket connection
*/
/*
* Return a socket connect()ed to a "hostname" that is
* accept()ing heap profile data on "port." Return a value <= 0 if
* such a connection can't be made.
*/
static int
{
int fd;
return -1;
}
return -1;
}
/* create a socket */
return fd;
}
/* Accept a filename, and adjust the name so that it is unique for this PID */
static void
{
int fd;
/* Find a file that doesn't exist */
if ( fd >= 0 ) {
int pid;
char *new_name;
char *old_name;
char *prefix;
int new_len;
/* Close the file. */
/* Make filename name.PID[.txt] */
suffix[0] = 0;
/* Look for .txt suffix if not binary output */
char *dot;
char *format_suffix;
int i;
int slen;
int match;
match = 1;
for ( i = 0; i < slen; i++ ) {
if ( dot[i]==0 ||
match = 0;
break;
}
}
if ( match ) {
*dot = 0; /* truncates prefix and old_name */
}
}
}
/* Construct the name */
/* Odds are with Windows, this file may not be so unique. */
}
}
static int
{
int len;
char *p;
buf[0] = 0;
if ( **src == 0 ) {
return 0;
}
if ( p==NULL ) {
} else {
/*LINTED*/
}
return 0;
}
if ( *p != 0 && *p == sep ) {
(*src) = p+1;
} else {
(*src) = p;
}
return len;
}
static jboolean
{
return JNI_FALSE;
}
} else {
return JNI_FALSE;
}
return JNI_TRUE;
}
static void
print_usage(void)
{
"\n"
" HPROF: Heap and CPU Profiling Agent (JVMTI Demonstration Code)\n"
"\n"
"\n"
"Option Name and Value Description Default\n"
"--------------------- ----------- -------\n"
"heap=dump|sites|all heap profiling all\n"
"cpu=samples|times|old CPU usage off\n"
"monitor=y|n monitor contention n\n"
"format=a|b text(txt) or binary output a\n"
"net=<host>:<port> send data over a socket off\n"
"lineno=y|n line number in traces? y\n"
"thread=y|n thread in traces? n\n"
"doe=y|n dump on exit? y\n"
"msa=y|n Solaris micro state accounting n\n"
"force=y|n force output to <file> y\n"
"verbose=y|n print messages about dumps y\n"
"\n"
"Obsolete Options\n"
"----------------\n"
"gc_okay=y|n\n"
#ifdef DEBUG
"\n"
"DEBUG Option Description Default\n"
"------------ ----------- -------\n"
"primfields=y|n include primitive field values y\n"
"primarrays=y|n include primitive array values y\n"
"debugflags=MASK Various debug flags 0\n"
" 0x01 Report refs in and of unprepared classes\n"
"logflags=MASK Logging to stderr 0\n"
"coredump=y|n Core dump on fatal n\n"
"errorexit=y|n Exit on any error n\n"
"pause=y|n Pause on onload & echo PID n\n"
"debug=y|n Turn on all debug checking n\n"
"X=MASK Internal use only 0\n"
"\n"
"Environment Variables\n"
"---------------------\n"
"_JAVA_HPROF_OPTIONS\n"
" Options can be added externally via this environment variable.\n"
" Anything contained in it will get a comma prepended to it (if needed),\n"
" then it will be added to the end of the options supplied via the\n"
#endif
"\n"
"Examples\n"
"--------\n"
" - Get sample cpu information every 20 millisec, with a stack depth of 3:\n"
" - Get heap usage information based on the allocation sites:\n"
#ifdef DEBUG
" - Using the external option addition with csh, log details on all runs:\n"
" setenv _JAVA_HPROF_OPTIONS \"logflags=0xC\"\n"
" is the same as:\n"
#endif
"\n"
"Notes\n"
"-----\n"
" - The option format=b cannot be used with monitor=y.\n"
" - The option format=b cannot be used with cpu=old|times.\n"
" will behave exactly the same as:\n"
#ifdef DEBUG
" - The debug options and environment variables are available with both java\n"
" and java_g versions.\n"
#endif
"\n"
"Warnings\n"
"--------\n"
" - This is demonstration code for the JVMTI interface and use of BCI,\n"
" it is not an official product or formal part of the JDK.\n"
" - The option format=b is considered experimental, this format may change\n"
" in a future release.\n"
#ifdef DEBUG
" - The obsolete options may be completely removed in a future release.\n"
" - The debug options and environment variables are not considered public\n"
" interfaces and can change or be removed with any type of update of\n"
#endif
);
}
static void
{
}
static void
{
char *all_options;
char *extra_options;
char *options;
char *default_filename;
int ulen;
if (command_line_options == 0)
command_line_options = "";
print_usage();
}
if ( extra_options == NULL ) {
extra_options = "";
}
if ( extra_options[0] != 0 ) {
if ( all_options[0] != 0 ) {
}
}
while (*options) {
char *endptr;
option_error("general syntax error parsing options");
}
if ( file_or_net_option_seen ) {
option_error("file or net options should only appear once");
}
option_error("syntax error parsing file=filename");
}
if (file_or_net_option_seen ) {
option_error("file or net options should only appear once");
}
option_error("net option missing ':'");
}
option_error("net option missing port");
}
option_error("syntax error parsing format=a|b");
}
} else {
option_error("format option value must be a|b");
}
option_error("syntax error parsing depth=DECIMAL");
}
option_error("depth option value must be decimal and >= 0");
}
option_error("syntax error parsing interval=DECIMAL");
}
option_error("interval option value must be decimal and > 0");
}
option_error("syntax error parsing cutoff=DOUBLE");
}
option_error("cutoff option value must be floating point and >= 0");
}
option_error("syntax error parsing cpu=y|samples|times|old");
}
} else {
option_error("cpu option value must be y|samples|times|old");
}
option_error("syntax error parsing heap=dump|sites|all");
}
} else {
option_error("heap option value must be dump|sites|all");
}
option_error("lineno option value must be y|n");
}
option_error("thread option value must be y|n");
}
option_error("doe option value must be y|n");
}
option_error("msa option value must be y|n");
}
option_error("force option value must be y|n");
}
option_error("verbose option value must be y|n");
}
option_error("primfields option value must be y|n");
}
option_error("primarrays option value must be y|n");
}
option_error("monitor option value must be y|n");
}
option_error("gc_okay option value must be y|n");
}
option_error("logflags option value must be numeric");
}
option_error("debugflags option value must be numeric");
}
option_error("coredump option value must be y|n");
}
option_error("The exitpause option was removed, use -XX:OnError='cmd %%p'");
option_error("errorexit option value must be y|n");
}
option_error("pause option value must be y|n");
}
option_error("debug option value must be y|n");
}
option_error("The precrash option was removed, use -XX:OnError='precrash -p %%p'");
option_error("X option value must be numeric");
}
} else {
}
}
if (gdata->cpu_timing) {
option_error("cpu=times|old is not supported with format=b");
}
if (gdata->monitor_tracing) {
option_error("monitor=y is not supported with format=b");
}
}
if (gdata->old_timing_format) {
}
} else {
}
if (!file_or_net_option_seen) {
}
/* UTF-8 to platform encoding (fill in gdata->output_filename) */
#ifdef SKIP_NPT
#else
#endif
}
/* By default we turn on gdata->alloc_sites and gdata->heap_dump */
if ( !gdata->cpu_timing &&
!gdata->cpu_sampling &&
!gdata->monitor_tracing &&
!gdata->alloc_sites &&
}
}
}
/* Create files & sockets needed */
char *base;
int len;
/* Get a fast tempfile for the heap information */
}
char * check_suffix;
}
}
} else {
}
}
}
}
} else {
/* If going out to a file, obey the force=y|n option */
if ( !gdata->force_output ) {
}
/* Make doubly sure this file does NOT exist */
/* Create the file */
} else {
}
}
}
}
/* ------------------------------------------------------------------- */
/* Data reset and dump functions */
static void
reset_all_data(void)
{
}
}
if (gdata->monitor_tracing) {
}
}
}
static void
{
verbose_message("Dumping");
if (gdata->monitor_tracing) {
verbose_message(" contended monitor usage ...");
}
verbose_message(" Java heap ...");
/* Update the class table */
}
if (gdata->alloc_sites) {
verbose_message(" allocation sites ...");
}
if (gdata->cpu_sampling) {
verbose_message(" CPU usage by sampling running threads ...");
}
if (gdata->cpu_timing) {
if (!gdata->old_timing_format) {
verbose_message(" CPU usage by timing methods ...");
} else {
verbose_message(" CPU usage in old prof format ...");
}
}
io_flush();
verbose_message(" done.\n");
}
/* ------------------------------------------------------------------- */
/* Dealing with class load and unload status */
static void
{
jint i;
/* Get all classes from JVMTI, make sure they are in the class table. */
/* We don't know if the class list has changed really, so we
* guess by the class count changing. Don't want to do
* a bunch of work on classes when it's unnecessary.
* I assume that even though we have global references on the
* jclass object that the class is still considered unloaded.
* (e.g. GC of jclass isn't required for it to be included
* in the unloaded list, or not in the load list)
* [Note: Use of Weak references was a performance problem.]
*/
/* Unmark the classes in the load list */
/* Pretend like it was a class load event */
for ( i = 0 ; i < class_count ; i++ ) {
}
/* Process the classes that have been unloaded */
}
/* Free the space and save the count. */
}
/* A GC or Death event has happened, so do some cleanup */
static void
{
/* Then we process the ObjectFreeStack */
/* Notice we just grabbed the stack of freed objects so
* any object free events will create a new stack.
*/
int count;
int i;
/* If we saw something freed in this GC */
if ( count > 0 ) {
for ( i = 0 ; i < count ; i++ ) {
(void)object_free(object_index);
}
/* We reset the class load status (only do this once) */
}
/* Just terminate this stack object */
}
/* We reset the class load status if we haven't and need to */
if ( force_class_table_reset ) {
}
}
/* Main function for thread that watches for GC finish events */
static void JNICALL
{
/* Indicate the watcher thread is active */
/* Loop while active */
while ( active ) {
/* Don't wait if VM_DEATH wants us to quit */
if ( gdata->gc_finish_stop_request ) {
/* Time to terminate */
} else {
/* Wait for notification to do cleanup, or terminate */
/* After wait, check to see if VM_DEATH wants us to quit */
if ( gdata->gc_finish_stop_request ) {
/* Time to terminate */
}
}
/* Time to cleanup, reset count and prepare for cleanup */
}
/* Do the cleanup if requested outside gc_finish_lock */
if ( do_cleanup ) {
/* Free up all freed objects, don't force class table reset
* We cannot let the VM_DEATH complete while we are doing
* this cleanup. So if during this, VM_DEATH happens,
* the VM_DEATH callback should block waiting for this
* loop to terminate, and send a notification to the
* VM_DEATH thread.
*/
/* Cleanup the tls table where the Thread objects were GC'd */
}
}
/* Falling out means VM_DEATH is happening, we need to notify VM_DEATH
* that we are done doing the cleanup. VM_DEATH is waiting on this
* notify.
*/
}
/* ------------------------------------------------------------------- */
/* JVMTI Event callback functions */
static void
{
if ( onload_set_only ) {
}
} else {
/* Enable all other JVMTI events of interest now. */
if (gdata->cpu_timing) {
}
if (gdata->monitor_tracing) {
}
}
}
}
/* JVMTI_EVENT_VM_INIT */
static void JNICALL
{
/* Header to use in heap dumps */
/* We need JNI here to call in and get the current maximum memory */
/* More than 2Gig triggers segments and 1.0.2 */
}
}
/* We write the initial header after the VM initializes now
* because we needed to use JNI to get maxMemory and determine if
* a 1.0.1 or a 1.0.2 header will be used.
* This used to be done in Agent_OnLoad.
*/
LOG("cbVMInit begin");
/* Create a system loader entry first */
/* Find the thread jclass (does JNI calls) */
/* Issue fake system thread start */
/* Setup the Tracker class (should be first class in table) */
/* Find selected system classes to keep track of */
gdata->system_class_size = 0;
/* Used to ID HPROF generated items */
}
/* Prime the class table */
/* Find the tracker jclass and jmethodID's (does JNI calls) */
}
/* Start any agent threads (does JNI, JVMTI, and Java calls) */
/* Thread to watch for gc_finish events */
/* Start up listener thread if we need it */
}
/* Start up cpu sampling thread if we need it */
if ( gdata->cpu_sampling ) {
/* Note: this could also get started later (see cpu) */
}
/* Setup event modes */
/* Engage tracking (sets Java Tracker field so injections call into
* agent library).
*/
}
/* Indicate the VM is initialized now */
LOG("cbVMInit end");
}
/* JVMTI_EVENT_VM_DEATH */
static void JNICALL
{
/*
* Use local flag to minimize gdata->dump_lock hold time.
*/
LOG("cbVMDeath");
/* Shutdown thread watching gc_finish, outside CALLBACK locks.
* We need to make sure the watcher thread is done doing any cleanup
* work before we continue here.
*/
/* Notify watcher thread to finish up, it will send
* another notify when done. If the watcher thread is busy
* cleaning up, it will detect gc_finish_stop_request when it's done.
* Then it sets gc_finish_active to JNI_FALSE and will notify us.
* If the watcher thread is waiting to be notified, then the
* notification wakes it up.
* We do not want to do the VM_DEATH while the gc_finish
* watcher thread is in the middle of a cleanup.
*/
/* Wait for the gc_finish watcher thread to notify us it's done */
while ( gdata->gc_finish_active ) {
}
/* The gc_finish watcher thread should be done now, or done shortly. */
/* BEGIN_CALLBACK/END_CALLBACK handling. */
/* The callbackBlock prevents any active callbacks from returning
* back to the VM, and also blocks all new callbacks.
* We want to prevent any threads from premature death, so
* that we don't have worry about that during thread queries
* in this final dump process.
*/
/* We need to wait for all callbacks actively executing to block
* on exit, and new ones will block on entry.
* The BEGIN_CALLBACK/END_CALLBACK macros keep track of callbacks
* that are active.
* Once the last active callback is done, it will notify this
* thread and block.
*/
/* Turn off native calls */
}
while (gdata->active_callbacks > 0) {
}
/* Now we know that no threads will die on us, being blocked
* on some event callback, at a minimum ThreadEnd.
*/
/* Make some basic checks. */
if ( gdata->jvm_initializing ) {
return;
}
if ( !gdata->jvm_initialized ) {
return;
}
if (gdata->jvm_shut_down) {
return;
}
/* Shutdown the cpu loop thread */
if ( gdata->cpu_sampling ) {
}
/* Time to dump the final data */
if (!gdata->dump_in_process) {
/*
* Setting gdata->dump_in_process will cause cpu sampling to pause
* (if we are sampling). We don't resume sampling after the
* dump_all_data() call below because the VM is shutting
* down.
*/
}
/* Dump everything if we need to */
}
/* Disable all events and callbacks now, all of them.
* NOTE: It's important that this be done after the dump
* it prevents other threads from messing up the data
* because they will block on ThreadStart and ThreadEnd
* events due to the CALLBACK block.
*/
/* Write tail of file */
/* Shutdown the listener thread and socket, or flush I/O buffers */
} else {
io_flush();
}
/* Close the file descriptors down */
}
}
}
}
}
/* Remove the temporary heap file */
}
/* If logging, dump the tables */
}
/* Make sure all global references are deleted */
}
/* JVMTI_EVENT_THREAD_START */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_THREAD_END */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
static void JNICALL
{
/* WARNING: This will be called before VM_INIT. */
return;
}
BEGIN_CALLBACK() {
const char *classname;
if ( gdata->bci_counter == 0 ) {
/* Prime the system classes */
}
gdata->bci_counter++;
*new_class_data_len = 0;
*new_class_data = NULL;
/* Name could be NULL */
}
} else {
}
}
/* The tracker class itself? */
int system_class;
unsigned char * new_image;
long new_length;
int len;
char *signature;
/* Define a unique class number for this class */
signature[0] = JVM_SIGNATURE_CLASS;
if ( class_being_redefined != NULL ) {
} else {
}
/* Make sure class doesn't get unloaded by accident */
/* Is it a system class? */
system_class = 0;
if ( (!gdata->jvm_initialized)
&& (!gdata->jvm_initializing)
system_class = 1;
}
new_length = 0;
cnum,
if ( new_length > 0 ) {
unsigned char *jvmti_space;
} else {
*new_class_data_len = 0;
*new_class_data = NULL;
}
}
}
} END_CALLBACK();
}
/* JVMTI_EVENT_CLASS_LOAD */
static void JNICALL
{
/* WARNING: This MAY be called before VM_INIT. */
LOG("cbClassLoad");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_CLASS_PREPARE */
static void JNICALL
{
/* WARNING: This will be called before VM_INIT. */
LOG("cbClassPrepare");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_DATA_DUMP_REQUEST */
static void JNICALL
{
LOG("cbDataDumpRequest");
BEGIN_CALLBACK() {
if (!gdata->dump_in_process) {
}
if (need_to_dump) {
dump_all_data(getEnv());
}
}
} END_CALLBACK();
}
/* JVMTI_EVENT_EXCEPTION_CATCH */
static void JNICALL
{
LOG("cbExceptionCatch");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_MONITOR_WAIT */
static void JNICALL
{
LOG("cbMonitorWait");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_MONITOR_WAITED */
static void JNICALL
{
LOG("cbMonitorWaited");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
static void JNICALL
{
LOG("cbMonitorContendedEnter");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
static void JNICALL
{
LOG("cbMonitorContendedEntered");
BEGIN_CALLBACK() {
} END_CALLBACK();
}
/* JVMTI_EVENT_GARBAGE_COLLECTION_START */
static void JNICALL
{
LOG("cbGarbageCollectionStart");
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
* are allowed here (see the JVMTI Spec).
*/
}
/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
static void JNICALL
{
LOG("cbGarbageCollectionFinish");
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
* are allowed here (see the JVMTI Spec).
*/
}
/* Increment gc_finish counter, notify watcher thread */
/* If VM_DEATH is trying to shut it down, don't do anything at all.
* Never send notify if VM_DEATH wants the watcher thread to quit.
*/
if ( gdata->gc_finish_active ) {
}
}
/* JVMTI_EVENT_OBJECT_FREE */
static void JNICALL
{
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
* are allowed here (see the JVMTI Spec).
*/
if ( !gdata->jvm_shut_down ) {
}
}
}
static void
{
if ( ! on ) {
return;
}
/* JVMTI_EVENT_VM_INIT */
/* JVMTI_EVENT_VM_DEATH */
/* JVMTI_EVENT_THREAD_START */
/* JVMTI_EVENT_THREAD_END */
/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
/* JVMTI_EVENT_CLASS_LOAD */
/* JVMTI_EVENT_CLASS_PREPARE */
/* JVMTI_EVENT_DATA_DUMP_REQUEST */
/* JVMTI_EVENT_EXCEPTION_CATCH */
/* JVMTI_EVENT_MONITOR_WAIT */
/* JVMTI_EVENT_MONITOR_WAITED */
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
/* JVMTI_EVENT_GARBAGE_COLLECTION_START */
/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
/* JVMTI_EVENT_OBJECT_FREE */
}
static void
getCapabilities(void)
{
/* Fill in ones that we must have */
}
}
#if 0 /* Not needed until we call JVMTI for CpuTime */
#endif
}
if (gdata->monitor_tracing) {
#if 0 /* Not needed until we call JVMTI for CpuTime */
#endif
}
/* Get potential capabilities */
/* Some capabilities would be nicer to have */
/* Add the capabilities */
}
/* Dynamic library loading */
static void *
{
char *boot_path;
void *handle;
/* The library may be located in different ways, try both, but
*/
/* This may be necessary on Windows. */
}
}
return handle;
}
/* Lookup dynamic function pointer in shared library */
static void *
{
void *addr;
int i;
for( i = 0 ; i < nsymbols; i++ ) {
break;
}
}
"Cannot find library symbol '%s'", symbols[0]);
}
return addr;
}
/* ------------------------------------------------------------------- */
/* The OnLoad interface */
{
/* See if it's already loaded */
HPROF_ERROR(JNI_TRUE, "Cannot load this JVM TI agent twice, check your java command line for duplicate hprof options.");
return JNI_ERR;
}
error_setup();
/* Get the JVMTI environment */
getJvmti();
#ifndef SKIP_NPT
/* Load in NPT library for character conversions */
}
}
#endif
/* Lock needed to protect debug_malloc() code, which is not MT safe */
#ifdef DEBUG
#endif
/* Initialize machine dependent code (micro state accounting) */
md_init();
string_init(); /* Table index values look like: 0x10000000 */
class_init(); /* Table index values look like: 0x20000000 */
tls_init(); /* Table index values look like: 0x30000000 */
trace_init(); /* Table index values look like: 0x40000000 */
object_init(); /* Table index values look like: 0x50000000 */
site_init(); /* Table index values look like: 0x60000000 */
frame_init(); /* Table index values look like: 0x70000000 */
monitor_init(); /* Table index values look like: 0x80000000 */
loader_init(); /* Table index values look like: 0x90000000 */
}
/* Set the JVMTI callback functions (do this only once)*/
/* Create basic locks */
/* Set Onload events mode. */
/* Used in VM_DEATH to wait for callbacks to complete */
gdata->active_callbacks = 0;
/* Write the header information */
io_setup();
/* We sample the start time now so that the time increments can be
* placed in the various heap dump segments in micro seconds.
*/
/* Load java_crw_demo library and find function "java_crw_demo" */
/* Load the library or get the handle to it */
{ /* "java_crw_demo" */
}
{ /* "java_crw_demo_classname" */
}
}
return JNI_OK;
}
{
LOG("Agent_OnUnload");
}
io_cleanup();
tls_cleanup();
site_cleanup();
/* Deallocate any memory in gdata */
}
}
}
}
}
}
/* Verify all allocated memory has been taken care of. */
/* Cleanup is hard to do when other threads might still be running
* so we skip destroying some raw monitors which still might be in use
* and we skip disposal of the jvmtiEnv* which might still be needed.
* Only raw monitors that could be held by other threads are left
* alone. So we explicitly do NOT do this:
* destroyRawMonitor(gdata->callbackLock);
* destroyRawMonitor(gdata->callbackBlock);
* destroyRawMonitor(gdata->gc_finish_lock);
* destroyRawMonitor(gdata->object_free_lock);
* destroyRawMonitor(gdata->listener_loop_lock);
* destroyRawMonitor(gdata->cpu_loop_lock);
* disposeEnvironment();
* gdata->jvmti = NULL;
*/
/* Destroy basic locks */
}
#ifdef DEBUG
#endif
/* Unload java_crw_demo library */
}
/* You would think you could clear out gdata and set it to NULL, but
* turns out that isn't a good idea. Some of the threads could be
* blocked inside the CALLBACK*() macros, where they got blocked up
* waiting for the VM_DEATH callback to complete. They only have
* some raw monitor actions to do, but they need access to gdata to do it.
* So do not do this:
* (void)memset(gdata, 0, sizeof(GlobalData));
* gdata = NULL;
*/
}