--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.hprof.agent/share/native/libhprof/hprof_init.c Tue Aug 26 07:55:08 2014 +0200
@@ -0,0 +1,2145 @@
+/*
+ * Copyright (c) 2003, 2014, 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.
+ */
+
+/*
+ * 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. */
+
+#define DEFAULT_TXT_SUFFIX ".txt"
+#define DEFAULT_OUTPUTFILE "java.hprof"
+#define DEFAULT_OUTPUTTEMP "java.hprof.temp"
+
+/* The only global variable, defined by this library */
+GlobalData *gdata;
+
+/* Experimental options */
+#define EXPERIMENT_NO_EARLY_HOOK 0x1
+
+/* Default trace depth */
+#define DEFAULT_TRACE_DEPTH 4
+
+/* Default sample interval */
+#define DEFAULT_SAMPLE_INTERVAL 10
+
+/* Default cutoff */
+#define DEFAULT_CUTOFF_POINT 0.0001
+
+/* Stringize macros for help. */
+#define _TO_STR(a) #a
+#define TO_STR(a) _TO_STR(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.
+ * In all cases, the last thing they do is Enter/Exit the monitor
+ * 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 */ \
+ jboolean bypass; \
+ rawMonitorEnter(gdata->callbackLock); \
+ if (gdata->vm_death_callback_active) { \
+ /* VM_DEATH is active, we will bypass the CALLBACK CODE */ \
+ bypass = JNI_TRUE; \
+ rawMonitorExit(gdata->callbackLock); \
+ /* Bypassed CALLBACKS block here until VM_DEATH done */ \
+ rawMonitorEnter(gdata->callbackBlock); \
+ rawMonitorExit(gdata->callbackBlock); \
+ } else { \
+ /* We will be executing the CALLBACK CODE in this case */ \
+ gdata->active_callbacks++; \
+ bypass = JNI_FALSE; \
+ rawMonitorExit(gdata->callbackLock); \
+ } \
+ if ( !bypass ) { \
+ /* BODY OF CALLBACK CODE (with no callback locks held) */
+
+#define END_CALLBACK() /* Part of bypass if body */ \
+ rawMonitorEnter(gdata->callbackLock); \
+ gdata->active_callbacks--; \
+ /* If VM_DEATH is active, and last one, send notify. */ \
+ if (gdata->vm_death_callback_active) { \
+ if (gdata->active_callbacks == 0) { \
+ rawMonitorNotifyAll(gdata->callbackLock); \
+ } \
+ } \
+ rawMonitorExit(gdata->callbackLock); \
+ /* Non-Bypassed CALLBACKS block here until VM_DEATH done */ \
+ rawMonitorEnter(gdata->callbackBlock); \
+ rawMonitorExit(gdata->callbackBlock); \
+ } \
+} /* END OF CALLBACK */
+
+/* Forward declarations */
+static void set_callbacks(jboolean on);
+
+/* ------------------------------------------------------------------- */
+/* Global data initialization */
+
+/* Get initialized global data area */
+static GlobalData *
+get_gdata(void)
+{
+ static GlobalData data;
+
+ /* Create initial default values */
+ (void)memset(&data, 0, sizeof(GlobalData));
+
+ data.fd = -1; /* Non-zero file or socket. */
+ data.heap_fd = -1; /* For heap=dump, see hprof_io */
+ data.check_fd = -1; /* For heap=dump, see hprof_io */
+ data.max_trace_depth = DEFAULT_TRACE_DEPTH;
+ data.prof_trace_depth = DEFAULT_TRACE_DEPTH;
+ data.sample_interval = DEFAULT_SAMPLE_INTERVAL;
+ data.lineno_in_traces = JNI_TRUE;
+ data.output_format = 'a'; /* 'b' for binary */
+ data.cutoff_point = DEFAULT_CUTOFF_POINT;
+ data.dump_on_exit = JNI_TRUE;
+ data.gc_start_time = -1L;
+#ifdef DEBUG
+ data.debug = JNI_TRUE;
+ data.coredump = JNI_TRUE;
+#endif
+ data.micro_state_accounting = JNI_FALSE;
+ data.force_output = JNI_TRUE;
+ data.verbose = JNI_TRUE;
+ data.primfields = JNI_TRUE;
+ data.primarrays = JNI_TRUE;
+
+ data.table_serial_number_start = 1;
+ data.class_serial_number_start = 100000;
+ data.thread_serial_number_start = 200000;
+ data.trace_serial_number_start = 300000;
+ data.object_serial_number_start = 400000;
+ data.frame_serial_number_start = 500000;
+ data.gref_serial_number_start = 1;
+
+ data.table_serial_number_counter = data.table_serial_number_start;
+ data.class_serial_number_counter = data.class_serial_number_start;
+ data.thread_serial_number_counter = data.thread_serial_number_start;
+ data.trace_serial_number_counter = data.trace_serial_number_start;
+ data.object_serial_number_counter = data.object_serial_number_start;
+ data.frame_serial_number_counter = data.frame_serial_number_start;
+ data.gref_serial_number_counter = data.gref_serial_number_start;
+
+ data.unknown_thread_serial_num = data.thread_serial_number_counter++;
+ return &data;
+}
+
+/* ------------------------------------------------------------------- */
+/* Error handler callback for the java_crw_demo (classfile read write) functions. */
+
+static void
+my_crw_fatal_error_handler(const char * msg, const char *file, int line)
+{
+ char errmsg[256];
+
+ (void)md_snprintf(errmsg, sizeof(errmsg),
+ "%s [%s:%d]", msg, file, line);
+ errmsg[sizeof(errmsg)-1] = 0;
+ HPROF_ERROR(JNI_TRUE, errmsg);
+}
+
+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
+connect_to_socket(char *hostname, int port)
+{
+ int fd;
+
+ if (port == 0 || port > 65535) {
+ HPROF_ERROR(JNI_FALSE, "invalid port number");
+ return -1;
+ }
+ if (hostname == NULL) {
+ HPROF_ERROR(JNI_FALSE, "hostname is NULL");
+ return -1;
+ }
+
+ /* create a socket */
+ fd = md_connect(hostname, (unsigned short)port);
+ return fd;
+}
+
+/* Accept a filename, and adjust the name so that it is unique for this PID */
+static void
+make_unique_filename(char **filename)
+{
+ int fd;
+
+ /* Find a file that doesn't exist */
+ fd = md_open(*filename);
+ if ( fd >= 0 ) {
+ int pid;
+ char *new_name;
+ char *old_name;
+ char *prefix;
+ char suffix[5];
+ int new_len;
+
+ /* Close the file. */
+ md_close(fd);
+
+ /* Make filename name.PID[.txt] */
+ pid = md_getpid();
+ old_name = *filename;
+ new_len = (int)strlen(old_name)+64;
+ new_name = HPROF_MALLOC(new_len);
+ prefix = old_name;
+ suffix[0] = 0;
+
+ /* Look for .txt suffix if not binary output */
+ if (gdata->output_format != 'b') {
+ char *dot;
+ char *format_suffix;
+
+ format_suffix = DEFAULT_TXT_SUFFIX;
+
+ (void)strcpy(suffix, format_suffix);
+
+ dot = strrchr(old_name, '.');
+ if ( dot != NULL ) {
+ int i;
+ int slen;
+ int match;
+
+ slen = (int)strlen(format_suffix);
+ match = 1;
+ for ( i = 0; i < slen; i++ ) {
+ if ( dot[i]==0 ||
+ tolower(format_suffix[i]) != tolower(dot[i]) ) {
+ match = 0;
+ break;
+ }
+ }
+ if ( match ) {
+ (void)strcpy(suffix, dot);
+ *dot = 0; /* truncates prefix and old_name */
+ }
+ }
+ }
+
+ /* Construct the name */
+ (void)md_snprintf(new_name, new_len,
+ "%s.%d%s", prefix, pid, suffix);
+ *filename = new_name;
+ HPROF_FREE(old_name);
+
+ /* Odds are with Windows, this file may not be so unique. */
+ (void)remove(gdata->output_filename);
+ }
+}
+
+static int
+get_tok(char **src, char *buf, int buflen, int sep)
+{
+ int len;
+ char *p;
+
+ buf[0] = 0;
+ if ( **src == 0 ) {
+ return 0;
+ }
+ p = strchr(*src, sep);
+ if ( p==NULL ) {
+ len = (int)strlen(*src);
+ p = (*src) + len;
+ } else {
+ /*LINTED*/
+ len = (int)(p - (*src));
+ }
+ if ( (len+1) > buflen ) {
+ return 0;
+ }
+ (void)memcpy(buf, *src, len);
+ buf[len] = 0;
+ if ( *p != 0 && *p == sep ) {
+ (*src) = p+1;
+ } else {
+ (*src) = p;
+ }
+ return len;
+}
+
+static jboolean
+setBinarySwitch(char **src, jboolean *ptr)
+{
+ char buf[80];
+
+ if (!get_tok(src, buf, (int)sizeof(buf), ',')) {
+ return JNI_FALSE;
+ }
+ if (strcmp(buf, "y") == 0) {
+ *ptr = JNI_TRUE;
+ } else if (strcmp(buf, "n") == 0) {
+ *ptr = JNI_FALSE;
+ } else {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static void
+print_usage(void)
+{
+
+ (void)fprintf(stdout,
+"\n"
+" HPROF: Heap and CPU Profiling Agent (JVMTI Demonstration Code)\n"
+"\n"
+AGENTNAME " usage: java " AGENTLIB "=[help]|[<option>=<value>, ...]\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"
+"file=<file> write data to file " DEFAULT_OUTPUTFILE "[{" DEFAULT_TXT_SUFFIX "}]\n"
+"net=<host>:<port> send data over a socket off\n"
+"depth=<size> stack trace depth " TO_STR(DEFAULT_TRACE_DEPTH) "\n"
+"interval=<ms> sample interval in ms " TO_STR(DEFAULT_SAMPLE_INTERVAL) "\n"
+"cutoff=<value> output cutoff point " TO_STR(DEFAULT_CUTOFF_POINT) "\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"
+" " TO_STR(LOG_DUMP_MISC) " Misc logging\n"
+" " TO_STR(LOG_DUMP_LISTS) " Dump out the tables\n"
+" " TO_STR(LOG_CHECK_BINARY) " Verify & dump format=b\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"
+" " XRUN " or " AGENTLIB " command line option.\n"
+
+#endif
+
+"\n"
+"Examples\n"
+"--------\n"
+" - Get sample cpu information every 20 millisec, with a stack depth of 3:\n"
+" java " AGENTLIB "=cpu=samples,interval=20,depth=3 classname\n"
+" - Get heap usage information based on the allocation sites:\n"
+" java " AGENTLIB "=heap=sites classname\n"
+
+#ifdef DEBUG
+" - Using the external option addition with csh, log details on all runs:\n"
+" setenv _JAVA_HPROF_OPTIONS \"logflags=0xC\"\n"
+" java " AGENTLIB "=cpu=samples classname\n"
+" is the same as:\n"
+" java " AGENTLIB "=cpu=samples,logflags=0xC classname\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"
+" - Use of the " XRUN " interface can still be used, e.g.\n"
+" java " XRUN ":[help]|[<option>=<value>, ...]\n"
+" will behave exactly the same as:\n"
+" java " AGENTLIB "=[help]|[<option>=<value>, ...]\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 " XRUN " interface will be removed in a future release.\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"
+" " AGENTNAME ", including patches.\n"
+#endif
+
+ );
+}
+
+static void
+option_error(char *description)
+{
+ char errmsg[FILENAME_MAX+80];
+
+ (void)md_snprintf(errmsg, sizeof(errmsg),
+ "%s option error: %s (%s)", AGENTNAME, description, gdata->options);
+ errmsg[sizeof(errmsg)-1] = 0;
+ HPROF_ERROR(JNI_FALSE, errmsg);
+ error_exit_process(1);
+}
+
+static void
+parse_options(char *command_line_options)
+{
+ int file_or_net_option_seen = JNI_FALSE;
+ char *all_options;
+ char *extra_options;
+ char *options;
+ char *default_filename;
+ int ulen;
+
+ if (command_line_options == 0)
+ command_line_options = "";
+
+ if ((strcmp(command_line_options, "help")) == 0) {
+ print_usage();
+ error_exit_process(0);
+ }
+
+ extra_options = getenv("_JAVA_HPROF_OPTIONS");
+ if ( extra_options == NULL ) {
+ extra_options = "";
+ }
+
+ all_options = HPROF_MALLOC((int)strlen(command_line_options) +
+ (int)strlen(extra_options) + 2);
+ gdata->options = all_options;
+ (void)strcpy(all_options, command_line_options);
+ if ( extra_options[0] != 0 ) {
+ if ( all_options[0] != 0 ) {
+ (void)strcat(all_options, ",");
+ }
+ (void)strcat(all_options, extra_options);
+ }
+ options = all_options;
+
+ LOG2("parse_options()", all_options);
+
+ while (*options) {
+ char option[16];
+ char suboption[FILENAME_MAX+1];
+ char *endptr;
+
+ if (!get_tok(&options, option, (int)sizeof(option), '=')) {
+ option_error("general syntax error parsing options");
+ }
+ if (strcmp(option, "file") == 0) {
+ if ( file_or_net_option_seen ) {
+ option_error("file or net options should only appear once");
+ }
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing file=filename");
+ }
+ gdata->utf8_output_filename = HPROF_MALLOC((int)strlen(suboption)+1);
+ (void)strcpy(gdata->utf8_output_filename, suboption);
+ file_or_net_option_seen = JNI_TRUE;
+ } else if (strcmp(option, "net") == 0) {
+ char port_number[16];
+ if (file_or_net_option_seen ) {
+ option_error("file or net options should only appear once");
+ }
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ':')) {
+ option_error("net option missing ':'");
+ }
+ if (!get_tok(&options, port_number, (int)sizeof(port_number), ',')) {
+ option_error("net option missing port");
+ }
+ gdata->net_hostname = HPROF_MALLOC((int)strlen(suboption)+1);
+ (void)strcpy(gdata->net_hostname, suboption);
+ gdata->net_port = (int)strtol(port_number, NULL, 10);
+ file_or_net_option_seen = JNI_TRUE;
+ } else if (strcmp(option, "format") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing format=a|b");
+ }
+ if (strcmp(suboption, "a") == 0) {
+ gdata->output_format = 'a';
+ } else if (strcmp(suboption, "b") == 0) {
+ gdata->output_format = 'b';
+ } else {
+ option_error("format option value must be a|b");
+ }
+ } else if (strcmp(option, "depth") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing depth=DECIMAL");
+ }
+ gdata->max_trace_depth = (int)strtol(suboption, &endptr, 10);
+ if ((endptr != NULL && *endptr != 0) || gdata->max_trace_depth < 0) {
+ option_error("depth option value must be decimal and >= 0");
+ }
+ gdata->prof_trace_depth = gdata->max_trace_depth;
+ } else if (strcmp(option, "interval") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing interval=DECIMAL");
+ }
+ gdata->sample_interval = (int)strtol(suboption, &endptr, 10);
+ if ((endptr != NULL && *endptr != 0) || gdata->sample_interval <= 0) {
+ option_error("interval option value must be decimal and > 0");
+ }
+ } else if (strcmp(option, "cutoff") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing cutoff=DOUBLE");
+ }
+ gdata->cutoff_point = strtod(suboption, &endptr);
+ if ((endptr != NULL && *endptr != 0) || gdata->cutoff_point < 0) {
+ option_error("cutoff option value must be floating point and >= 0");
+ }
+ } else if (strcmp(option, "cpu") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing cpu=y|samples|times|old");
+ }
+ if ((strcmp(suboption, "samples") == 0) ||
+ (strcmp(suboption, "y") == 0)) {
+ gdata->cpu_sampling = JNI_TRUE;
+ } else if (strcmp(suboption, "times") == 0) {
+ gdata->cpu_timing = JNI_TRUE;
+ gdata->old_timing_format = JNI_FALSE;
+ } else if (strcmp(suboption, "old") == 0) {
+ gdata->cpu_timing = JNI_TRUE;
+ gdata->old_timing_format = JNI_TRUE;
+ } else {
+ option_error("cpu option value must be y|samples|times|old");
+ }
+ } else if (strcmp(option, "heap") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("syntax error parsing heap=dump|sites|all");
+ }
+ if (strcmp(suboption, "dump") == 0) {
+ gdata->heap_dump = JNI_TRUE;
+ } else if (strcmp(suboption, "sites") == 0) {
+ gdata->alloc_sites = JNI_TRUE;
+ } else if (strcmp(suboption, "all") == 0) {
+ gdata->heap_dump = JNI_TRUE;
+ gdata->alloc_sites = JNI_TRUE;
+ } else {
+ option_error("heap option value must be dump|sites|all");
+ }
+ } else if( strcmp(option,"lineno") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->lineno_in_traces)) ) {
+ option_error("lineno option value must be y|n");
+ }
+ } else if( strcmp(option,"thread") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->thread_in_traces)) ) {
+ option_error("thread option value must be y|n");
+ }
+ } else if( strcmp(option,"doe") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->dump_on_exit)) ) {
+ option_error("doe option value must be y|n");
+ }
+ } else if( strcmp(option,"msa") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->micro_state_accounting)) ) {
+ option_error("msa option value must be y|n");
+ }
+ } else if( strcmp(option,"force") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->force_output)) ) {
+ option_error("force option value must be y|n");
+ }
+ } else if( strcmp(option,"verbose") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->verbose)) ) {
+ option_error("verbose option value must be y|n");
+ }
+ } else if( strcmp(option,"primfields") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->primfields)) ) {
+ option_error("primfields option value must be y|n");
+ }
+ } else if( strcmp(option,"primarrays") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->primarrays)) ) {
+ option_error("primarrays option value must be y|n");
+ }
+ } else if( strcmp(option,"monitor") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->monitor_tracing)) ) {
+ option_error("monitor option value must be y|n");
+ }
+ } else if( strcmp(option,"gc_okay") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->gc_okay)) ) {
+ option_error("gc_okay option value must be y|n");
+ }
+ } else if (strcmp(option, "logflags") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("logflags option value must be numeric");
+ }
+ gdata->logflags = (int)strtol(suboption, NULL, 0);
+ } else if (strcmp(option, "debugflags") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("debugflags option value must be numeric");
+ }
+ gdata->debugflags = (int)strtol(suboption, NULL, 0);
+ } else if (strcmp(option, "coredump") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->coredump)) ) {
+ option_error("coredump option value must be y|n");
+ }
+ } else if (strcmp(option, "exitpause") == 0) {
+ option_error("The exitpause option was removed, use -XX:OnError='cmd %%p'");
+ } else if (strcmp(option, "errorexit") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->errorexit)) ) {
+ option_error("errorexit option value must be y|n");
+ }
+ } else if (strcmp(option, "pause") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->pause)) ) {
+ option_error("pause option value must be y|n");
+ }
+ } else if (strcmp(option, "debug") == 0) {
+ if ( !setBinarySwitch(&options, &(gdata->debug)) ) {
+ option_error("debug option value must be y|n");
+ }
+ } else if (strcmp(option, "precrash") == 0) {
+ option_error("The precrash option was removed, use -XX:OnError='precrash -p %%p'");
+ } else if (strcmp(option, "X") == 0) {
+ if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) {
+ option_error("X option value must be numeric");
+ }
+ gdata->experiment = (int)strtol(suboption, NULL, 0);
+ } else {
+ char errmsg[80];
+ (void)strcpy(errmsg, "Unknown option: ");
+ (void)strcat(errmsg, option);
+ option_error(errmsg);
+ }
+ }
+
+ if (gdata->output_format == 'b') {
+ 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) {
+ gdata->prof_trace_depth = 2;
+ }
+
+ if (gdata->output_format == 'b') {
+ default_filename = DEFAULT_OUTPUTFILE;
+ } else {
+ default_filename = DEFAULT_OUTPUTFILE DEFAULT_TXT_SUFFIX;
+ }
+
+ if (!file_or_net_option_seen) {
+ gdata->utf8_output_filename = HPROF_MALLOC((int)strlen(default_filename)+1);
+ (void)strcpy(gdata->utf8_output_filename, default_filename);
+ }
+
+ if ( gdata->utf8_output_filename != NULL ) {
+ // Don't attempt to convert output filename.
+ // If fileystem uses the same encoding as the rest of the OS it will work as is.
+ ulen = (int)strlen(gdata->utf8_output_filename);
+ gdata->output_filename = (char*)HPROF_MALLOC(ulen*3+3);
+ (void)strcpy(gdata->output_filename, gdata->utf8_output_filename);
+ }
+
+ /* 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 &&
+ !gdata->heap_dump) {
+ gdata->heap_dump = JNI_TRUE;
+ gdata->alloc_sites = JNI_TRUE;
+ }
+
+ if ( gdata->alloc_sites || gdata->heap_dump ) {
+ gdata->obj_watch = JNI_TRUE;
+ }
+ if ( gdata->obj_watch || gdata->cpu_timing ) {
+ gdata->bci = JNI_TRUE;
+ }
+
+ /* Create files & sockets needed */
+ if (gdata->heap_dump) {
+ char *base;
+ int len;
+
+ /* Get a fast tempfile for the heap information */
+ base = gdata->output_filename;
+ if ( base==NULL ) {
+ base = default_filename;
+ }
+ len = (int)strlen(base);
+ gdata->heapfilename = HPROF_MALLOC(len + 5);
+ (void)strcpy(gdata->heapfilename, base);
+ (void)strcat(gdata->heapfilename, ".TMP");
+ make_unique_filename(&(gdata->heapfilename));
+ (void)remove(gdata->heapfilename);
+ if (gdata->output_format == 'b') {
+ if ( gdata->logflags & LOG_CHECK_BINARY ) {
+ char * check_suffix;
+
+ check_suffix = ".check" DEFAULT_TXT_SUFFIX;
+ gdata->checkfilename =
+ HPROF_MALLOC((int)strlen(default_filename)+
+ (int)strlen(check_suffix)+1);
+ (void)strcpy(gdata->checkfilename, default_filename);
+ (void)strcat(gdata->checkfilename, check_suffix);
+ (void)remove(gdata->checkfilename);
+ gdata->check_fd = md_creat(gdata->checkfilename);
+ }
+ if ( gdata->debug ) {
+ gdata->logflags |= LOG_CHECK_BINARY;
+ }
+ gdata->heap_fd = md_creat_binary(gdata->heapfilename);
+ } else {
+ gdata->heap_fd = md_creat(gdata->heapfilename);
+ }
+ if ( gdata->heap_fd < 0 ) {
+ char errmsg[FILENAME_MAX+80];
+
+ (void)md_snprintf(errmsg, sizeof(errmsg),
+ "can't create temp heap file: %s", gdata->heapfilename);
+ errmsg[sizeof(errmsg)-1] = 0;
+ HPROF_ERROR(JNI_TRUE, errmsg);
+ }
+ }
+
+ if ( gdata->net_port > 0 ) {
+ LOG2("Agent_OnLoad", "Connecting to socket");
+ gdata->fd = connect_to_socket(gdata->net_hostname, gdata->net_port);
+ if (gdata->fd <= 0) {
+ char errmsg[120];
+
+ (void)md_snprintf(errmsg, sizeof(errmsg),
+ "can't connect to %s:%u", gdata->net_hostname, gdata->net_port);
+ errmsg[sizeof(errmsg)-1] = 0;
+ HPROF_ERROR(JNI_FALSE, errmsg);
+ error_exit_process(1);
+ }
+ gdata->socket = JNI_TRUE;
+ } else {
+ /* If going out to a file, obey the force=y|n option */
+ if ( !gdata->force_output ) {
+ make_unique_filename(&(gdata->output_filename));
+ }
+ /* Make doubly sure this file does NOT exist */
+ (void)remove(gdata->output_filename);
+ /* Create the file */
+ if (gdata->output_format == 'b') {
+ gdata->fd = md_creat_binary(gdata->output_filename);
+ } else {
+ gdata->fd = md_creat(gdata->output_filename);
+ }
+ if (gdata->fd < 0) {
+ char errmsg[FILENAME_MAX+80];
+
+ (void)md_snprintf(errmsg, sizeof(errmsg),
+ "can't create profile file: %s", gdata->output_filename);
+ errmsg[sizeof(errmsg)-1] = 0;
+ HPROF_ERROR(JNI_FALSE, errmsg);
+ error_exit_process(1);
+ }
+ }
+
+}
+
+/* ------------------------------------------------------------------- */
+/* Data reset and dump functions */
+
+static void
+reset_all_data(void)
+{
+ if (gdata->cpu_sampling || gdata->cpu_timing || gdata->monitor_tracing) {
+ rawMonitorEnter(gdata->data_access_lock);
+ }
+
+ if (gdata->cpu_sampling || gdata->cpu_timing) {
+ trace_clear_cost();
+ }
+ if (gdata->monitor_tracing) {
+ monitor_clear();
+ }
+
+ if (gdata->cpu_sampling || gdata->cpu_timing || gdata->monitor_tracing) {
+ rawMonitorExit(gdata->data_access_lock);
+ }
+}
+
+static void reset_class_load_status(JNIEnv *env, jthread thread);
+
+static void
+dump_all_data(JNIEnv *env)
+{
+ verbose_message("Dumping");
+ if (gdata->monitor_tracing) {
+ verbose_message(" contended monitor usage ...");
+ tls_dump_monitor_state(env);
+ monitor_write_contended_time(env, gdata->cutoff_point);
+ }
+ if (gdata->heap_dump) {
+ verbose_message(" Java heap ...");
+ /* Update the class table */
+ reset_class_load_status(env, NULL);
+ site_heapdump(env);
+ }
+ if (gdata->alloc_sites) {
+ verbose_message(" allocation sites ...");
+ site_write(env, 0, gdata->cutoff_point);
+ }
+ if (gdata->cpu_sampling) {
+ verbose_message(" CPU usage by sampling running threads ...");
+ trace_output_cost(env, gdata->cutoff_point);
+ }
+ if (gdata->cpu_timing) {
+ if (!gdata->old_timing_format) {
+ verbose_message(" CPU usage by timing methods ...");
+ trace_output_cost(env, gdata->cutoff_point);
+ } else {
+ verbose_message(" CPU usage in old prof format ...");
+ trace_output_cost_in_prof_format(env);
+ }
+ }
+ reset_all_data();
+ io_flush();
+ verbose_message(" done.\n");
+}
+
+/* ------------------------------------------------------------------- */
+/* Dealing with class load and unload status */
+
+static void
+reset_class_load_status(JNIEnv *env, jthread thread)
+{
+
+ WITH_LOCAL_REFS(env, 1) {
+ jint class_count;
+ jclass *classes;
+ jint i;
+
+ /* Get all classes from JVMTI, make sure they are in the class table. */
+ getLoadedClasses(&classes, &class_count);
+
+ /* 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.]
+ */
+ if ( class_count != gdata->class_count ) {
+
+ rawMonitorEnter(gdata->data_access_lock); {
+
+ /* Unmark the classes in the load list */
+ class_all_status_remove(CLASS_IN_LOAD_LIST);
+
+ /* Pretend like it was a class load event */
+ for ( i = 0 ; i < class_count ; i++ ) {
+ jobject loader;
+
+ loader = getClassLoader(classes[i]);
+ event_class_load(env, thread, classes[i], loader);
+ }
+
+ /* Process the classes that have been unloaded */
+ class_do_unloads(env);
+
+ } rawMonitorExit(gdata->data_access_lock);
+
+ }
+
+ /* Free the space and save the count. */
+ jvmtiDeallocate(classes);
+ gdata->class_count = class_count;
+
+ } END_WITH_LOCAL_REFS;
+
+}
+
+/* A GC or Death event has happened, so do some cleanup */
+static void
+object_free_cleanup(JNIEnv *env, jboolean force_class_table_reset)
+{
+ Stack *stack;
+
+ /* Then we process the ObjectFreeStack */
+ rawMonitorEnter(gdata->object_free_lock); {
+ stack = gdata->object_free_stack;
+ gdata->object_free_stack = NULL; /* Will trigger new stack */
+ } rawMonitorExit(gdata->object_free_lock);
+
+ /* Notice we just grabbed the stack of freed objects so
+ * any object free events will create a new stack.
+ */
+ if ( stack != NULL ) {
+ int count;
+ int i;
+
+ count = stack_depth(stack);
+
+ /* If we saw something freed in this GC */
+ if ( count > 0 ) {
+
+ for ( i = 0 ; i < count ; i++ ) {
+ ObjectIndex object_index;
+ jlong tag;
+
+ tag = *(jlong*)stack_element(stack,i);
+ object_index = tag_extract(tag);
+
+ (void)object_free(object_index);
+ }
+
+ /* We reset the class load status (only do this once) */
+ reset_class_load_status(env, NULL);
+ force_class_table_reset = JNI_FALSE;
+
+ }
+
+ /* Just terminate this stack object */
+ stack_term(stack);
+ }
+
+ /* We reset the class load status if we haven't and need to */
+ if ( force_class_table_reset ) {
+ reset_class_load_status(env, NULL);
+ }
+
+}
+
+/* Main function for thread that watches for GC finish events */
+static void JNICALL
+gc_finish_watcher(jvmtiEnv *jvmti, JNIEnv *env, void *p)
+{
+ jboolean active;
+
+ active = JNI_TRUE;
+
+ /* Indicate the watcher thread is active */
+ rawMonitorEnter(gdata->gc_finish_lock); {
+ gdata->gc_finish_active = JNI_TRUE;
+ } rawMonitorExit(gdata->gc_finish_lock);
+
+ /* Loop while active */
+ while ( active ) {
+ jboolean do_cleanup;
+
+ do_cleanup = JNI_FALSE;
+ rawMonitorEnter(gdata->gc_finish_lock); {
+ /* Don't wait if VM_DEATH wants us to quit */
+ if ( gdata->gc_finish_stop_request ) {
+ /* Time to terminate */
+ active = JNI_FALSE;
+ } else {
+ /* Wait for notification to do cleanup, or terminate */
+ rawMonitorWait(gdata->gc_finish_lock, 0);
+ /* After wait, check to see if VM_DEATH wants us to quit */
+ if ( gdata->gc_finish_stop_request ) {
+ /* Time to terminate */
+ active = JNI_FALSE;
+ }
+ }
+ if ( active && gdata->gc_finish > 0 ) {
+ /* Time to cleanup, reset count and prepare for cleanup */
+ gdata->gc_finish = 0;
+ do_cleanup = JNI_TRUE;
+ }
+ } rawMonitorExit(gdata->gc_finish_lock);
+
+ /* 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.
+ */
+ object_free_cleanup(env, JNI_FALSE);
+
+ /* Cleanup the tls table where the Thread objects were GC'd */
+ tls_garbage_collect(env);
+ }
+
+ }
+
+ /* 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.
+ */
+ rawMonitorEnter(gdata->gc_finish_lock); {
+ gdata->gc_finish_active = JNI_FALSE;
+ rawMonitorNotifyAll(gdata->gc_finish_lock);
+ } rawMonitorExit(gdata->gc_finish_lock);
+}
+
+/* ------------------------------------------------------------------- */
+/* JVMTI Event callback functions */
+
+static void
+setup_event_mode(jboolean onload_set_only, jvmtiEventMode state)
+{
+ if ( onload_set_only ) {
+ setEventNotificationMode(state,
+ JVMTI_EVENT_VM_INIT, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_VM_DEATH, NULL);
+ if (gdata->bci) {
+ setEventNotificationMode(state,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
+ }
+ } else {
+ /* Enable all other JVMTI events of interest now. */
+ setEventNotificationMode(state,
+ JVMTI_EVENT_THREAD_START, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_THREAD_END, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_CLASS_LOAD, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_CLASS_PREPARE, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
+ if (gdata->cpu_timing) {
+ setEventNotificationMode(state,
+ JVMTI_EVENT_EXCEPTION_CATCH, NULL);
+ }
+ if (gdata->monitor_tracing) {
+ setEventNotificationMode(state,
+ JVMTI_EVENT_MONITOR_WAIT, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_MONITOR_WAITED, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL);
+ }
+ if (gdata->obj_watch) {
+ setEventNotificationMode(state,
+ JVMTI_EVENT_OBJECT_FREE, NULL);
+ }
+ setEventNotificationMode(state,
+ JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL);
+ setEventNotificationMode(state,
+ JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL);
+ }
+}
+
+/* JVMTI_EVENT_VM_INIT */
+static void JNICALL
+cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
+{
+ rawMonitorEnter(gdata->data_access_lock); {
+
+ LoaderIndex loader_index;
+ ClassIndex cnum;
+ TlsIndex tls_index;
+
+ gdata->jvm_initializing = JNI_TRUE;
+
+ /* Header to use in heap dumps */
+ gdata->header = "JAVA PROFILE 1.0.1";
+ gdata->segmented = JNI_FALSE;
+ if (gdata->output_format == 'b') {
+ /* We need JNI here to call in and get the current maximum memory */
+ gdata->maxMemory = getMaxMemory(env);
+ gdata->maxHeapSegment = (jlong)2000000000;
+ /* More than 2Gig triggers segments and 1.0.2 */
+ if ( gdata->maxMemory >= gdata->maxHeapSegment ) {
+ gdata->header = "JAVA PROFILE 1.0.2";
+ gdata->segmented = JNI_TRUE; /* 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.
+ */
+ io_write_file_header();
+
+ LOG("cbVMInit begin");
+
+ /* Create a system loader entry first */
+ loader_index = loader_find_or_create(NULL,NULL);
+
+ /* Find the thread jclass (does JNI calls) */
+ gdata->thread_cnum = class_find_or_create("Ljava/lang/Thread;",
+ loader_index);
+ class_add_status(gdata->thread_cnum, CLASS_SYSTEM);
+
+ /* Issue fake system thread start */
+ tls_index = tls_find_or_create(env, thread);
+
+ /* Setup the Tracker class (should be first class in table) */
+ tracker_setup_class();
+
+ /* Find selected system classes to keep track of */
+ gdata->system_class_size = 0;
+ cnum = class_find_or_create("Ljava/lang/Object;", loader_index);
+
+ gdata->system_trace_index = tls_get_trace(tls_index, env,
+ gdata->max_trace_depth, JNI_FALSE);
+ gdata->system_object_site_index = site_find_or_create(
+ cnum, gdata->system_trace_index);
+
+ /* Used to ID HPROF generated items */
+ gdata->hprof_trace_index = tls_get_trace(tls_index, env,
+ gdata->max_trace_depth, JNI_FALSE);
+ gdata->hprof_site_index = site_find_or_create(
+ cnum, gdata->hprof_trace_index);
+
+ if ( gdata->logflags & LOG_DUMP_LISTS ) {
+ list_all_tables();
+ }
+
+ /* Prime the class table */
+ reset_class_load_status(env, thread);
+
+ /* Find the tracker jclass and jmethodID's (does JNI calls) */
+ if ( gdata->bci ) {
+ tracker_setup_methods(env);
+ }
+
+ /* Start any agent threads (does JNI, JVMTI, and Java calls) */
+
+ /* Thread to watch for gc_finish events */
+ rawMonitorEnter(gdata->gc_finish_lock); {
+ createAgentThread(env, "HPROF gc_finish watcher",
+ &gc_finish_watcher);
+ } rawMonitorExit(gdata->gc_finish_lock);
+
+ /* Start up listener thread if we need it */
+ if ( gdata->socket ) {
+ listener_init(env);
+ }
+
+ /* Start up cpu sampling thread if we need it */
+ if ( gdata->cpu_sampling ) {
+ /* Note: this could also get started later (see cpu) */
+ cpu_sample_init(env);
+ }
+
+ /* Setup event modes */
+ setup_event_mode(JNI_FALSE, JVMTI_ENABLE);
+
+ /* Engage tracking (sets Java Tracker field so injections call into
+ * agent library).
+ */
+ if ( gdata->bci ) {
+ tracker_engage(env);
+ }
+
+ /* Indicate the VM is initialized now */
+ gdata->jvm_initialized = JNI_TRUE;
+ gdata->jvm_initializing = JNI_FALSE;
+
+ LOG("cbVMInit end");
+
+ } rawMonitorExit(gdata->data_access_lock);
+}
+
+/* JVMTI_EVENT_VM_DEATH */
+static void JNICALL
+cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
+{
+ /*
+ * Use local flag to minimize gdata->dump_lock hold time.
+ */
+ jboolean need_to_dump = JNI_FALSE;
+
+ 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.
+ */
+ rawMonitorEnter(gdata->gc_finish_lock); {
+ /* 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.
+ */
+ gdata->gc_finish_stop_request = JNI_TRUE;
+ rawMonitorNotifyAll(gdata->gc_finish_lock);
+ /* Wait for the gc_finish watcher thread to notify us it's done */
+ while ( gdata->gc_finish_active ) {
+ rawMonitorWait(gdata->gc_finish_lock,0);
+ }
+ } rawMonitorExit(gdata->gc_finish_lock);
+
+ /* 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.
+ */
+ rawMonitorEnter(gdata->callbackBlock); {
+
+ /* 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.
+ */
+
+ rawMonitorEnter(gdata->callbackLock); {
+ /* Turn off native calls */
+ if ( gdata->bci ) {
+ tracker_disengage(env);
+ }
+ gdata->vm_death_callback_active = JNI_TRUE;
+ while (gdata->active_callbacks > 0) {
+ rawMonitorWait(gdata->callbackLock, 0);
+ }
+ } rawMonitorExit(gdata->callbackLock);
+
+ /* Now we know that no threads will die on us, being blocked
+ * on some event callback, at a minimum ThreadEnd.
+ */
+
+ /* Make some basic checks. */
+ rawMonitorEnter(gdata->data_access_lock); {
+ if ( gdata->jvm_initializing ) {
+ HPROF_ERROR(JNI_TRUE, "VM Death during VM Init");
+ return;
+ }
+ if ( !gdata->jvm_initialized ) {
+ HPROF_ERROR(JNI_TRUE, "VM Death before VM Init");
+ return;
+ }
+ if (gdata->jvm_shut_down) {
+ HPROF_ERROR(JNI_TRUE, "VM Death more than once?");
+ return;
+ }
+ } rawMonitorExit(gdata->data_access_lock);
+
+ /* Shutdown the cpu loop thread */
+ if ( gdata->cpu_sampling ) {
+ cpu_sample_term(env);
+ }
+
+ /* Time to dump the final data */
+ rawMonitorEnter(gdata->dump_lock); {
+
+ gdata->jvm_shut_down = JNI_TRUE;
+
+ if (!gdata->dump_in_process) {
+ need_to_dump = JNI_TRUE;
+ gdata->dump_in_process = JNI_TRUE;
+ /*
+ * 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.
+ */
+ }
+
+ } rawMonitorExit(gdata->dump_lock);
+
+ /* Dump everything if we need to */
+ if (gdata->dump_on_exit && need_to_dump) {
+
+ dump_all_data(env);
+ }
+
+ /* 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.
+ */
+ set_callbacks(JNI_FALSE);
+ setup_event_mode(JNI_FALSE, JVMTI_DISABLE);
+ setup_event_mode(JNI_TRUE, JVMTI_DISABLE);
+
+ /* Write tail of file */
+ io_write_file_footer();
+
+ } rawMonitorExit(gdata->callbackBlock);
+
+ /* Shutdown the listener thread and socket, or flush I/O buffers */
+ if (gdata->socket) {
+ listener_term(env);
+ } else {
+ io_flush();
+ }
+
+ /* Close the file descriptors down */
+ if ( gdata->fd >= 0 ) {
+ (void)md_close(gdata->fd);
+ gdata->fd = -1;
+ if ( gdata->logflags & LOG_CHECK_BINARY ) {
+ if (gdata->output_format == 'b' && gdata->output_filename != NULL) {
+ check_binary_file(gdata->output_filename);
+ }
+ }
+ }
+ if ( gdata->heap_fd >= 0 ) {
+ (void)md_close(gdata->heap_fd);
+ gdata->heap_fd = -1;
+ }
+
+ if ( gdata->check_fd >= 0 ) {
+ (void)md_close(gdata->check_fd);
+ gdata->check_fd = -1;
+ }
+
+ /* Remove the temporary heap file */
+ if (gdata->heap_dump) {
+ (void)remove(gdata->heapfilename);
+ }
+
+ /* If logging, dump the tables */
+ if ( gdata->logflags & LOG_DUMP_LISTS ) {
+ list_all_tables();
+ }
+
+ /* Make sure all global references are deleted */
+ class_delete_global_references(env);
+ loader_delete_global_references(env);
+ tls_delete_global_references(env);
+
+}
+
+/* JVMTI_EVENT_THREAD_START */
+static void JNICALL
+cbThreadStart(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
+{
+ LOG3("cbThreadStart", "thread is", (int)(long)(ptrdiff_t)thread);
+
+ BEGIN_CALLBACK() {
+ event_thread_start(env, thread);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_THREAD_END */
+static void JNICALL
+cbThreadEnd(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
+{
+ LOG3("cbThreadEnd", "thread is", (int)(long)(ptrdiff_t)thread);
+
+ BEGIN_CALLBACK() {
+ event_thread_end(env, thread);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
+static void JNICALL
+cbClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv* env,
+ jclass class_being_redefined, jobject loader,
+ const char* name, jobject protection_domain,
+ jint class_data_len, const unsigned char* class_data,
+ jint* new_class_data_len, unsigned char** new_class_data)
+{
+
+ /* WARNING: This will be called before VM_INIT. */
+
+ LOG2("cbClassFileLoadHook:",(name==NULL?"Unknown":name));
+
+ if (!gdata->bci) {
+ return;
+ }
+
+ BEGIN_CALLBACK() {
+ rawMonitorEnter(gdata->data_access_lock); {
+ const char *classname;
+
+ if ( gdata->bci_counter == 0 ) {
+ /* Prime the system classes */
+ class_prime_system_classes();
+ }
+
+ gdata->bci_counter++;
+
+ *new_class_data_len = 0;
+ *new_class_data = NULL;
+
+ /* Name could be NULL */
+ if ( name == NULL ) {
+ classname = ((JavaCrwDemoClassname)
+ (gdata->java_crw_demo_classname_function))
+ (class_data, class_data_len, &my_crw_fatal_error_handler);
+ if ( classname == NULL ) {
+ HPROF_ERROR(JNI_TRUE, "No classname in classfile");
+ }
+ } else {
+ classname = strdup(name);
+ if ( classname == NULL ) {
+ HPROF_ERROR(JNI_TRUE, "Ran out of malloc() space");
+ }
+ }
+
+ /* The tracker class itself? */
+ if ( strcmp(classname, TRACKER_CLASS_NAME) != 0 ) {
+ ClassIndex cnum;
+ int system_class;
+ unsigned char * new_image;
+ long new_length;
+ int len;
+ char *signature;
+ LoaderIndex loader_index;
+
+ LOG2("cbClassFileLoadHook injecting class" , classname);
+
+ /* Define a unique class number for this class */
+ len = (int)strlen(classname);
+ signature = HPROF_MALLOC(len+3);
+ signature[0] = JVM_SIGNATURE_CLASS;
+ (void)memcpy(signature+1, classname, len);
+ signature[len+1] = JVM_SIGNATURE_ENDCLASS;
+ signature[len+2] = 0;
+ loader_index = loader_find_or_create(env,loader);
+ if ( class_being_redefined != NULL ) {
+ cnum = class_find_or_create(signature, loader_index);
+ } else {
+ cnum = class_create(signature, loader_index);
+ }
+ HPROF_FREE(signature);
+ signature = NULL;
+
+ /* Make sure class doesn't get unloaded by accident */
+ class_add_status(cnum, CLASS_IN_LOAD_LIST);
+
+ /* Is it a system class? */
+ system_class = 0;
+ if ( (!gdata->jvm_initialized)
+ && (!gdata->jvm_initializing)
+ && ( ( class_get_status(cnum) & CLASS_SYSTEM) != 0
+ || gdata->bci_counter < 8 ) ) {
+ system_class = 1;
+ LOG2(classname, " is a system class");
+ }
+
+ new_image = NULL;
+ new_length = 0;
+
+ /* Call the class file reader/write demo code */
+ ((JavaCrwDemo)(gdata->java_crw_demo_function))(
+ cnum,
+ classname,
+ class_data,
+ class_data_len,
+ system_class,
+ TRACKER_CLASS_NAME,
+ TRACKER_CLASS_SIG,
+ (gdata->cpu_timing)?TRACKER_CALL_NAME:NULL,
+ (gdata->cpu_timing)?TRACKER_CALL_SIG:NULL,
+ (gdata->cpu_timing)?TRACKER_RETURN_NAME:NULL,
+ (gdata->cpu_timing)?TRACKER_RETURN_SIG:NULL,
+ (gdata->obj_watch)?TRACKER_OBJECT_INIT_NAME:NULL,
+ (gdata->obj_watch)?TRACKER_OBJECT_INIT_SIG:NULL,
+ (gdata->obj_watch)?TRACKER_NEWARRAY_NAME:NULL,
+ (gdata->obj_watch)?TRACKER_NEWARRAY_SIG:NULL,
+ &new_image,
+ &new_length,
+ &my_crw_fatal_error_handler,
+ &class_set_methods);
+
+ if ( new_length > 0 ) {
+ unsigned char *jvmti_space;
+
+ LOG2("cbClassFileLoadHook DID inject this class", classname);
+ jvmti_space = (unsigned char *)jvmtiAllocate((jint)new_length);
+ (void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);
+ *new_class_data_len = (jint)new_length;
+ *new_class_data = jvmti_space; /* VM will deallocate */
+ } else {
+ LOG2("cbClassFileLoadHook DID NOT inject this class", classname);
+ *new_class_data_len = 0;
+ *new_class_data = NULL;
+ }
+ if ( new_image != NULL ) {
+ (void)free((void*)new_image); /* Free malloc() space with free() */
+ }
+ }
+ (void)free((void*)classname);
+ } rawMonitorExit(gdata->data_access_lock);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_CLASS_LOAD */
+static void JNICALL
+cbClassLoad(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass)
+{
+
+ /* WARNING: This MAY be called before VM_INIT. */
+
+ LOG("cbClassLoad");
+
+ BEGIN_CALLBACK() {
+ rawMonitorEnter(gdata->data_access_lock); {
+
+ WITH_LOCAL_REFS(env, 1) {
+ jobject loader;
+
+ loader = getClassLoader(klass);
+ event_class_load(env, thread, klass, loader);
+ } END_WITH_LOCAL_REFS;
+
+ } rawMonitorExit(gdata->data_access_lock);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_CLASS_PREPARE */
+static void JNICALL
+cbClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass)
+{
+
+ /* WARNING: This will be called before VM_INIT. */
+
+ LOG("cbClassPrepare");
+
+ BEGIN_CALLBACK() {
+ rawMonitorEnter(gdata->data_access_lock); {
+
+ WITH_LOCAL_REFS(env, 1) {
+ jobject loader;
+
+ loader = NULL;
+ loader = getClassLoader(klass);
+ event_class_prepare(env, thread, klass, loader);
+ } END_WITH_LOCAL_REFS;
+
+ } rawMonitorExit(gdata->data_access_lock);
+ } END_CALLBACK();
+
+}
+
+/* JVMTI_EVENT_DATA_DUMP_REQUEST */
+static void JNICALL
+cbDataDumpRequest(jvmtiEnv *jvmti)
+{
+ jboolean need_to_dump;
+
+ LOG("cbDataDumpRequest");
+
+ BEGIN_CALLBACK() {
+ need_to_dump = JNI_FALSE;
+ rawMonitorEnter(gdata->dump_lock); {
+ if (!gdata->dump_in_process) {
+ need_to_dump = JNI_TRUE;
+ gdata->dump_in_process = JNI_TRUE;
+ }
+ } rawMonitorExit(gdata->dump_lock);
+
+ if (need_to_dump) {
+ dump_all_data(getEnv());
+
+ rawMonitorEnter(gdata->dump_lock); {
+ gdata->dump_in_process = JNI_FALSE;
+ } rawMonitorExit(gdata->dump_lock);
+
+ if (gdata->cpu_sampling && !gdata->jvm_shut_down) {
+ cpu_sample_on(NULL, 0); /* resume sampling */
+ }
+ }
+ } END_CALLBACK();
+
+}
+
+/* JVMTI_EVENT_EXCEPTION_CATCH */
+static void JNICALL
+cbExceptionCatch(jvmtiEnv *jvmti, JNIEnv* env,
+ jthread thread, jmethodID method, jlocation location,
+ jobject exception)
+{
+ LOG("cbExceptionCatch");
+
+ BEGIN_CALLBACK() {
+ event_exception_catch(env, thread, method, location, exception);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_MONITOR_WAIT */
+static void JNICALL
+cbMonitorWait(jvmtiEnv *jvmti, JNIEnv* env,
+ jthread thread, jobject object, jlong timeout)
+{
+ LOG("cbMonitorWait");
+
+ BEGIN_CALLBACK() {
+ monitor_wait_event(env, thread, object, timeout);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_MONITOR_WAITED */
+static void JNICALL
+cbMonitorWaited(jvmtiEnv *jvmti, JNIEnv* env,
+ jthread thread, jobject object, jboolean timed_out)
+{
+ LOG("cbMonitorWaited");
+
+ BEGIN_CALLBACK() {
+ monitor_waited_event(env, thread, object, timed_out);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
+static void JNICALL
+cbMonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv* env,
+ jthread thread, jobject object)
+{
+ LOG("cbMonitorContendedEnter");
+
+ BEGIN_CALLBACK() {
+ monitor_contended_enter_event(env, thread, object);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
+static void JNICALL
+cbMonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv* env,
+ jthread thread, jobject object)
+{
+ LOG("cbMonitorContendedEntered");
+
+ BEGIN_CALLBACK() {
+ monitor_contended_entered_event(env, thread, object);
+ } END_CALLBACK();
+}
+
+/* JVMTI_EVENT_GARBAGE_COLLECTION_START */
+static void JNICALL
+cbGarbageCollectionStart(jvmtiEnv *jvmti)
+{
+ LOG("cbGarbageCollectionStart");
+
+ /* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
+ * are allowed here (see the JVMTI Spec).
+ */
+
+ gdata->gc_start_time = md_get_timemillis();
+}
+
+/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
+static void JNICALL
+cbGarbageCollectionFinish(jvmtiEnv *jvmti)
+{
+ LOG("cbGarbageCollectionFinish");
+
+ /* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
+ * are allowed here (see the JVMTI Spec).
+ */
+
+ if ( gdata->gc_start_time != -1L ) {
+ gdata->time_in_gc += (md_get_timemillis() - gdata->gc_start_time);
+ gdata->gc_start_time = -1L;
+ }
+
+ /* Increment gc_finish counter, notify watcher thread */
+ rawMonitorEnter(gdata->gc_finish_lock); {
+ /* 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 ) {
+ gdata->gc_finish++;
+ rawMonitorNotifyAll(gdata->gc_finish_lock);
+ }
+ } rawMonitorExit(gdata->gc_finish_lock);
+}
+
+/* JVMTI_EVENT_OBJECT_FREE */
+static void JNICALL
+cbObjectFree(jvmtiEnv *jvmti, jlong tag)
+{
+ LOG3("cbObjectFree", "tag", (int)tag);
+
+ /* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
+ * are allowed here (see the JVMTI Spec).
+ */
+
+ HPROF_ASSERT(tag!=(jlong)0);
+ rawMonitorEnter(gdata->object_free_lock); {
+ if ( !gdata->jvm_shut_down ) {
+ Stack *stack;
+
+ stack = gdata->object_free_stack;
+ if ( stack == NULL ) {
+ gdata->object_free_stack = stack_init(512, 512, sizeof(jlong));
+ stack = gdata->object_free_stack;
+ }
+ stack_push(stack, (void*)&tag);
+ }
+ } rawMonitorExit(gdata->object_free_lock);
+}
+
+static void
+set_callbacks(jboolean on)
+{
+ jvmtiEventCallbacks callbacks;
+
+ (void)memset(&callbacks,0,sizeof(callbacks));
+ if ( ! on ) {
+ setEventCallbacks(&callbacks);
+ return;
+ }
+
+ /* JVMTI_EVENT_VM_INIT */
+ callbacks.VMInit = &cbVMInit;
+ /* JVMTI_EVENT_VM_DEATH */
+ callbacks.VMDeath = &cbVMDeath;
+ /* JVMTI_EVENT_THREAD_START */
+ callbacks.ThreadStart = &cbThreadStart;
+ /* JVMTI_EVENT_THREAD_END */
+ callbacks.ThreadEnd = &cbThreadEnd;
+ /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
+ callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
+ /* JVMTI_EVENT_CLASS_LOAD */
+ callbacks.ClassLoad = &cbClassLoad;
+ /* JVMTI_EVENT_CLASS_PREPARE */
+ callbacks.ClassPrepare = &cbClassPrepare;
+ /* JVMTI_EVENT_DATA_DUMP_REQUEST */
+ callbacks.DataDumpRequest = &cbDataDumpRequest;
+ /* JVMTI_EVENT_EXCEPTION_CATCH */
+ callbacks.ExceptionCatch = &cbExceptionCatch;
+ /* JVMTI_EVENT_MONITOR_WAIT */
+ callbacks.MonitorWait = &cbMonitorWait;
+ /* JVMTI_EVENT_MONITOR_WAITED */
+ callbacks.MonitorWaited = &cbMonitorWaited;
+ /* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
+ callbacks.MonitorContendedEnter = &cbMonitorContendedEnter;
+ /* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
+ callbacks.MonitorContendedEntered = &cbMonitorContendedEntered;
+ /* JVMTI_EVENT_GARBAGE_COLLECTION_START */
+ callbacks.GarbageCollectionStart = &cbGarbageCollectionStart;
+ /* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
+ callbacks.GarbageCollectionFinish = &cbGarbageCollectionFinish;
+ /* JVMTI_EVENT_OBJECT_FREE */
+ callbacks.ObjectFree = &cbObjectFree;
+
+ setEventCallbacks(&callbacks);
+
+}
+
+static void
+getCapabilities(void)
+{
+ jvmtiCapabilities needed_capabilities;
+ jvmtiCapabilities potential_capabilities;
+
+ /* Fill in ones that we must have */
+ (void)memset(&needed_capabilities,0,sizeof(needed_capabilities));
+ needed_capabilities.can_generate_garbage_collection_events = 1;
+ needed_capabilities.can_tag_objects = 1;
+ if (gdata->bci) {
+ needed_capabilities.can_generate_all_class_hook_events = 1;
+ }
+ if (gdata->obj_watch) {
+ needed_capabilities.can_generate_object_free_events = 1;
+ }
+ if (gdata->cpu_timing || gdata->cpu_sampling) {
+ #if 0 /* Not needed until we call JVMTI for CpuTime */
+ needed_capabilities.can_get_thread_cpu_time = 1;
+ needed_capabilities.can_get_current_thread_cpu_time = 1;
+ #endif
+ needed_capabilities.can_generate_exception_events = 1;
+ }
+ if (gdata->monitor_tracing) {
+ #if 0 /* Not needed until we call JVMTI for CpuTime */
+ needed_capabilities.can_get_thread_cpu_time = 1;
+ needed_capabilities.can_get_current_thread_cpu_time = 1;
+ #endif
+ needed_capabilities.can_get_owned_monitor_info = 1;
+ needed_capabilities.can_get_current_contended_monitor = 1;
+ needed_capabilities.can_get_monitor_info = 1;
+ needed_capabilities.can_generate_monitor_events = 1;
+ }
+
+ /* Get potential capabilities */
+ getPotentialCapabilities(&potential_capabilities);
+
+ /* Some capabilities would be nicer to have */
+ needed_capabilities.can_get_source_file_name =
+ potential_capabilities.can_get_source_file_name;
+ needed_capabilities.can_get_line_numbers =
+ potential_capabilities.can_get_line_numbers;
+
+ /* Add the capabilities */
+ addCapabilities(&needed_capabilities);
+
+}
+
+/* Dynamic library loading */
+static void *
+load_library(char *name)
+{
+ char lname[FILENAME_MAX+1];
+ char err_buf[256+FILENAME_MAX+1];
+ char *boot_path;
+ void *handle;
+
+ handle = NULL;
+
+ /* The library may be located in different ways, try both, but
+ * if it comes from outside the SDK/jre it isn't ours.
+ */
+ getSystemProperty("sun.boot.library.path", &boot_path);
+ md_build_library_name(lname, FILENAME_MAX, boot_path, name);
+ if ( strlen(lname) == 0 ) {
+ HPROF_ERROR(JNI_TRUE, "Could not find library");
+ }
+ jvmtiDeallocate(boot_path);
+ handle = md_load_library(lname, err_buf, (int)sizeof(err_buf));
+ if ( handle == NULL ) {
+ /* This may be necessary on Windows. */
+ md_build_library_name(lname, FILENAME_MAX, "", name);
+ if ( strlen(lname) == 0 ) {
+ HPROF_ERROR(JNI_TRUE, "Could not find library");
+ }
+ handle = md_load_library(lname, err_buf, (int)sizeof(err_buf));
+ if ( handle == NULL ) {
+ HPROF_ERROR(JNI_TRUE, err_buf);
+ }
+ }
+ return handle;
+}
+
+/* Lookup dynamic function pointer in shared library */
+static void *
+lookup_library_symbol(void *library, char **symbols, int nsymbols)
+{
+ void *addr;
+ int i;
+
+ addr = NULL;
+ for( i = 0 ; i < nsymbols; i++ ) {
+ addr = md_find_library_entry(library, symbols[i]);
+ if ( addr != NULL ) {
+ break;
+ }
+ }
+ if ( addr == NULL ) {
+ char errmsg[256];
+
+ (void)md_snprintf(errmsg, sizeof(errmsg),
+ "Cannot find library symbol '%s'", symbols[0]);
+ HPROF_ERROR(JNI_TRUE, errmsg);
+ }
+ return addr;
+}
+
+/* ------------------------------------------------------------------- */
+/* The OnLoad interface */
+
+JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
+{
+ char *boot_path = NULL;
+
+ /* See if it's already loaded */
+ if ( gdata!=NULL && gdata->isLoaded==JNI_TRUE ) {
+ HPROF_ERROR(JNI_TRUE, "Cannot load this JVM TI agent twice, check your java command line for duplicate hprof options.");
+ return JNI_ERR;
+ }
+
+ gdata = get_gdata();
+
+ gdata->isLoaded = JNI_TRUE;
+
+ error_setup();
+
+ LOG2("Agent_OnLoad", "gdata setup");
+
+ gdata->jvm = vm;
+
+ /* Get the JVMTI environment */
+ getJvmti();
+
+ /* Lock needed to protect debug_malloc() code, which is not MT safe */
+ #ifdef DEBUG
+ gdata->debug_malloc_lock = createRawMonitor("HPROF debug_malloc lock");
+ #endif
+
+ parse_options(options);
+
+ LOG2("Agent_OnLoad", "Has jvmtiEnv and options parsed");
+
+ /* 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 */
+
+ LOG2("Agent_OnLoad", "Tables initialized");
+
+ if ( gdata->pause ) {
+ error_do_pause();
+ }
+
+ getCapabilities();
+
+ /* Set the JVMTI callback functions (do this only once)*/
+ set_callbacks(JNI_TRUE);
+
+ /* Create basic locks */
+ gdata->dump_lock = createRawMonitor("HPROF dump lock");
+ gdata->data_access_lock = createRawMonitor("HPROF data access lock");
+ gdata->callbackLock = createRawMonitor("HPROF callback lock");
+ gdata->callbackBlock = createRawMonitor("HPROF callback block");
+ gdata->object_free_lock = createRawMonitor("HPROF object free lock");
+ gdata->gc_finish_lock = createRawMonitor("HPROF gc_finish lock");
+
+ /* Set Onload events mode. */
+ setup_event_mode(JNI_TRUE, JVMTI_ENABLE);
+
+ LOG2("Agent_OnLoad", "JVMTI capabilities, callbacks and initial notifications setup");
+
+ /* Used in VM_DEATH to wait for callbacks to complete */
+ gdata->jvm_initializing = JNI_FALSE;
+ gdata->jvm_initialized = JNI_FALSE;
+ gdata->vm_death_callback_active = JNI_FALSE;
+ 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.
+ */
+ gdata->micro_sec_ticks = md_get_microsecs();
+
+ /* Load java_crw_demo library and find function "java_crw_demo" */
+ if ( gdata->bci ) {
+
+ /* Load the library or get the handle to it */
+ gdata->java_crw_demo_library = load_library("java_crw_demo");
+
+ { /* "java_crw_demo" */
+ static char *symbols[] = JAVA_CRW_DEMO_SYMBOLS;
+ gdata->java_crw_demo_function =
+ lookup_library_symbol(gdata->java_crw_demo_library,
+ symbols, (int)(sizeof(symbols)/sizeof(char*)));
+ }
+ { /* "java_crw_demo_classname" */
+ static char *symbols[] = JAVA_CRW_DEMO_CLASSNAME_SYMBOLS;
+ gdata->java_crw_demo_classname_function =
+ lookup_library_symbol(gdata->java_crw_demo_library,
+ symbols, (int)(sizeof(symbols)/sizeof(char*)));
+ }
+ }
+
+ return JNI_OK;
+}
+
+JNIEXPORT void JNICALL
+Agent_OnUnload(JavaVM *vm)
+{
+ Stack *stack;
+
+ LOG("Agent_OnUnload");
+
+ gdata->isLoaded = JNI_FALSE;
+
+ stack = gdata->object_free_stack;
+ gdata->object_free_stack = NULL;
+ if ( stack != NULL ) {
+ stack_term(stack);
+ }
+
+ io_cleanup();
+ loader_cleanup();
+ tls_cleanup();
+ monitor_cleanup();
+ trace_cleanup();
+ site_cleanup();
+ object_cleanup();
+ frame_cleanup();
+ class_cleanup();
+ string_cleanup();
+
+ /* Deallocate any memory in gdata */
+ if ( gdata->net_hostname != NULL ) {
+ HPROF_FREE(gdata->net_hostname);
+ }
+ if ( gdata->utf8_output_filename != NULL ) {
+ HPROF_FREE(gdata->utf8_output_filename);
+ }
+ if ( gdata->output_filename != NULL ) {
+ HPROF_FREE(gdata->output_filename);
+ }
+ if ( gdata->heapfilename != NULL ) {
+ HPROF_FREE(gdata->heapfilename);
+ }
+ if ( gdata->checkfilename != NULL ) {
+ HPROF_FREE(gdata->checkfilename);
+ }
+ if ( gdata->options != NULL ) {
+ HPROF_FREE(gdata->options);
+ }
+
+ /* Verify all allocated memory has been taken care of. */
+ malloc_police();
+
+ /* 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 */
+ destroyRawMonitor(gdata->dump_lock);
+ gdata->dump_lock = NULL;
+ destroyRawMonitor(gdata->data_access_lock);
+ gdata->data_access_lock = NULL;
+ if ( gdata->cpu_sample_lock != NULL ) {
+ destroyRawMonitor(gdata->cpu_sample_lock);
+ gdata->cpu_sample_lock = NULL;
+ }
+ #ifdef DEBUG
+ destroyRawMonitor(gdata->debug_malloc_lock);
+ gdata->debug_malloc_lock = NULL;
+ #endif
+
+ /* Unload java_crw_demo library */
+ if ( gdata->bci && gdata->java_crw_demo_library != NULL ) {
+ md_unload_library(gdata->java_crw_demo_library);
+ gdata->java_crw_demo_library = NULL;
+ }
+
+ /* 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;
+ */
+}