/* * @(#)hprof_init.c 1.94 10/03/23 * * Copyright (c) 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: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution 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 or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ /* 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, unsigned short 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, 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]|[