Mercurial > hg > release > heapstats-1.1
view agent/src/libmain.cpp @ 19:ac44c043b016
Bug 1717: Reduce memory load operation.
reviewed-by: ykubota
author | Yasumasa Suenaga <suenaga.yasumasa@lab.ntt.co.jp> |
---|---|
date | Fri, 28 Mar 2014 13:40:20 +0900 |
parents | b21d5eef58f0 |
children |
line wrap: on
line source
/*! * \file libmain.cpp * \brief This file is used to common works.<br> * e.g. initialization, finalization, etc... * Copyright (C) 2011-2014 Nippon Telegraph and Telephone Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include <jni.h> #include <jvmti.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <execinfo.h> #include <signal.h> #include "libmain.hpp" #include "logMain.hpp" #include "snapShotMain.hpp" #include "timer.hpp" #include "util.hpp" #include "oopUtil.hpp" #include "jvmInfo.hpp" #include "trapSender.hpp" #include "signalManager.hpp" #include "elapsedTimer.hpp" /* Variables. */ /*! * \brief JVM running performance information. */ TJvmInfo *jvmInfo; /*! * \brief Signal manager to reload configuration by signal. */ TSignalManager *reloadSigMngr = NULL; /*! * \brief Reload configuration timer thread. */ TTimer *intervalSigTimer; /*! * \brief Flag of reload configuration for signal. */ volatile sig_atomic_t flagReloadConfig; /*! * \brief Running flag. */ int flagRunning = 0; /*! * \brief CPU clock ticks. */ long TElapsedTimer::clock_ticks = sysconf(_SC_CLK_TCK); /*! * \brief Datetime of agent initialization. */ unsigned long int TTrapSender::initializeTime; /*! * \brief Mutex for TTrapSender.<br> * <br> * This mutex used in below process.<br> * - TTrapSender::TTrapSender @ trapSender.hpp<br> * To initialize SNMP trap section.<br> * - TTrapSender::~TTrapSender @ trapSender.hpp<br> * To finalize SNMP trap section.<br> * - TTrapSender::sendTrap @ trapSender.hpp<br> * To change SNMP trap section and send SNMP trap.<br> */ pthread_mutex_t TTrapSender::senderMutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /*! * \brief Path of load configuration file at agent initialization. */ char *loadConfigPath = NULL; /* Macros. */ /*! * \brief Check memory duplication macro.<br> * If load a library any number of times,<br> * Then the plural agents use single memory area.<br> * E.g. "java -agentlib:A -agentlib:A -jar X.jar" */ #define CHECK_DOUBLING_RUN \ if (flagRunning != 0) { \ /* Already running another heapstats agent. */ \ PRINT_WARN_MSG("Already running heapstats agent on JVM." \ " This agent is disabled."); \ return SUCCESS; \ } \ /* Setting running flag. */ \ flagRunning = 1; /* Functions. */ /*! * \brief Handle signal express user wanna reload config. * \param signo [in] Number of received signal. * \param siginfo [in] Information of received signal. * \param data [in] Data of received signal. */ void ReloadSigProc(int signo, void *siginfo, void *data) { /* Enable flag. */ flagReloadConfig = 1; NOTIFY_CATCH_SIGNAL; } /*! * \brief Setting enable of JVMTI and extension events. * \param jvmti [in] JVMTI environment object. * \param enable [in] Event notification is enable. * \return Setting process result. */ jint SetEventEnable(jvmtiEnv *jvmti, bool enable) { jint result = SUCCESS; /* Set snapshot component enable. */ result = setEventEnableForSnapShot(jvmti, enable); if (result == SUCCESS) { /* Set logging component enable. */ result = setEventEnableForLog(jvmti, enable); } return result; } /*! * \brief Setting enable of agent each threads. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param enable [in] Event notification is enable. */ void SetThreadEnable(jvmtiEnv *jvmti, JNIEnv *env, bool enable) { /* Change thread enable for snapshot. */ setThreadEnableForSnapShot(jvmti, env, enable); /* Change thread enable for log. */ setThreadEnableForLog(jvmti, env, enable); } /*! * \brief Interval watching for reload config signal. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. */ void ReloadConfigProc(jvmtiEnv *jvmti, JNIEnv *env) { /* If catch reload config signal. */ if (unlikely(flagReloadConfig != 0)) { /* Reload configuration. */ /* If agent is attaching now. */ if (likely(arg.attach)) { /* Suspend events. */ SetEventEnable(jvmti, false); /* Suspend threads. */ SetThreadEnable(jvmti, env, false); } /* Copy older configuration setting. */ TArguments oldArg; memcpy(&oldArg, &arg, sizeof(TArguments)); /* If config file is designated at initialization. */ if (unlikely(loadConfigPath == NULL)) { /* Make default configuration path. */ char confPath[PATH_MAX + 1] = {0}; snprintf(confPath, PATH_MAX, "%s/heapstats.conf", DEFAULT_CONF_DIR); /* Load default config file. */ loadConfiguration(confPath); } else { /* Reload designated config file. */ loadConfiguration(loadConfigPath); } /* Calculate alert limit. */ jlong MaxMemory = jvmInfo->getMaxMemory(); if(MaxMemory == -1){ arg.AlertThreshold = -1; arg.HeapAlertThreshold = -1; } else{ arg.AlertThreshold = MaxMemory * arg.AlertPercentage / 100; arg.HeapAlertThreshold = MaxMemory * arg.HeapAlertPercentage / 100; } /* Inherited configuration setting. */ inheritedSetting(&arg, &oldArg); freeSetting(&oldArg); /* If agent is attaching now. */ if (likely(arg.attach)) { /* Ignore perfomance information during agent dettached. */ jvmInfo->resumeGCinfo(); /* Restart threads. */ SetThreadEnable(jvmti, env, true); } PRINT_INFO_MSG("Reloaded configuration file."); /* Show setting information. */ if (arg.LogLevel >= INFO) { printSetting(); FLUSH_STDOUT; } /* If agent is attaching now. */ if (likely(arg.attach)) { /* Restart events. */ SetEventEnable(jvmti, true); } /* Reset signal flag. */ flagReloadConfig = 0; } } /*! * \brief Interval watching for signals. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param cause [in] Cause of Invoking.<br> * This value is always Interval. */ void intervalSigProc(jvmtiEnv *jvmti, JNIEnv *env, TInvokeCause cause) { ReloadConfigProc(jvmti, env); /* If agent is attaching now and enable log signal. */ if (likely(arg.attach && arg.triggerOnLogSignal)) { intervalSigProcForLog(jvmti, env); } } /*! * \brief JVM initialization event. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param thread [in] Java thread object. */ void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) { /* Get GC information addres. */ #ifdef USE_VMSTRUCTS jvmInfo->detectInfoAddress(); #else jvmInfo->detectInfoAddress(env); #endif /* If failure signal initialization. */ if (unlikely(!TSignalManager::globalInitialize(env))) { PRINT_WARN_MSG("Failure signal handler initialization."); /* Disable many signal. */ arg.triggerOnLogSignal = false; free(arg.reloadSignal); arg.reloadSignal = strdup(""); } /* Initialize signal flag. */ flagReloadConfig = 0; /* Check reload signal is enable. */ bool enableReloadSig = arg.reloadSignal != NULL && strlen(arg.reloadSignal) > 0; /* Start HeapStats agent threads. */ try { reloadSigMngr = new TSignalManager(env, &ReloadSigProc); /* Switch reload signal state. */ if (enableReloadSig) { /* Setting new reload signal. */ if (unlikely(!reloadSigMngr->catchSignal(env, arg.reloadSignal))) { /* Reload configuration signal is disable. */ PRINT_WARN_MSG("Failure register reload configuration signal."); free(arg.reloadSignal); arg.reloadSignal = strdup(""); } } } catch(const char *errMsg) { PRINT_WARN_MSG(errMsg); } catch(...) { PRINT_WARN_MSG("AgentThread start failed!"); } /* Calculate alert limit. */ jlong MaxMemory = jvmInfo->getMaxMemory(); if(MaxMemory == -1){ arg.AlertThreshold = -1; arg.HeapAlertThreshold = -1; } else{ arg.AlertThreshold = MaxMemory * arg.AlertPercentage / 100; arg.HeapAlertThreshold = MaxMemory * arg.HeapAlertPercentage / 100; } /* Invoke JVM initialize event of snapshot function. */ onVMInitForSnapShot(jvmti, env); /* Invoke JVM initialize event of log function. */ onVMInitForLog(jvmti, env); /* If agent is attaching now. */ if(likely(arg.attach)){ /* Start and enable each agent threads. */ SetThreadEnable(jvmti, env, true); /* Set event enable in each function. */ SetEventEnable(jvmti, true); } /* Getting class prepare events. */ if(isError(jvmti, jvmti->SetEventNotificationMode( JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL))){ PRINT_WARN_MSG("HeapStats will be turned off.\n"); SetEventEnable(jvmti, false); SetThreadEnable(jvmti, env, false); FLUSH_STDOUT; return; } /* Show setting information. */ if (arg.LogLevel >= INFO) { printSetting(); FLUSH_STDOUT; } /* Start reload signal watcher. */ try { intervalSigTimer->start(jvmti, env, SIG_WATCHER_INTERVAL); } catch(const char *errMsg) { PRINT_WARN_MSG(errMsg); } } /*! * \brief JVM finalization event. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. */ void JNICALL OnVMDeath(jvmtiEnv *jvmti, JNIEnv *env) { /* If agent is attaching now. */ if (likely(arg.attach)) { /* Stop and disable event notify. */ SetEventEnable(jvmti, false); } /* * Terminate signal watcher thread. * This thread is used by collect log and reload configuration. * So that, need to this thread stop before other all thread stopped. */ intervalSigTimer->terminate(); /* Check reload signal is enable. */ bool enableReloadSig = (reloadSigMngr != NULL) && (arg.reloadSignal != NULL) && (strlen(arg.reloadSignal) > 0); /* If reload log signal is enabled. */ if (likely(reloadSigMngr != NULL)) { /* Terminate reload signal manager. */ if (likely(enableReloadSig)) { reloadSigMngr->ignoreSignal(env, arg.reloadSignal); } reloadSigMngr->terminate(env); delete reloadSigMngr; reloadSigMngr = NULL; } /* Invoke JVM finalize event of snapshot function. */ onVMDeathForSnapShot(jvmti, env); /* Invoke JVM finalize event of log function. */ onVMDeathForLog(jvmti, env); /* If agent is attaching now. */ if (likely(arg.attach)) { /* Stop and disable each thread. */ SetThreadEnable(jvmti, env, false); } /* Signal finalization. */ TSignalManager::globalFinalize(env); } /*! * \brief Abort JVM by force on illegal status. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param causeMsg [in] Message about cause of aborting JVM. * \warning This function is always no return. */ void forcedAbortJVM(jvmtiEnv *jvmti, JNIEnv *env, const char *causeMsg) { /* Terminate all event and thread. */ OnVMDeath(jvmti, env); FLUSH_STDOUT; /* Output last message. */ PRINT_CRIT_MSG_HEADER << "Abort JVM. cause:\"" << causeMsg << "\"" << NEWLINE; FLUSH_STDERR; abort(); } /*! * \brief Setting JVMTI and extension events. * \param jvmti [in] JVMTI environment object. * \return Setting process result. */ jint InitEventSetting(jvmtiEnv *jvmti) { /* Capability setting. */ jvmtiCapabilities capabilities; memset(&capabilities, 0, sizeof(jvmtiCapabilities)); /* Set capablities in snapshot function. */ setCapabilitiesForSnapShot(&capabilities); /* Set capablities in log function. */ setCapabilitiesForLog(&capabilities); /* If failure set capabilities. */ if (isError(jvmti, jvmti->AddCapabilities(&capabilities))) { PRINT_CRIT_MSG("Couldn't get event capabilities."); return CAPABILITIES_SETTING_FAILED; } /* Callback function setting. */ jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); /* Set event callback in snapshot function. */ /* If collect class information when JVM was prepared class. */ callbacks.ClassPrepare = &OnClassPrepare; /* If take snaoshot when user data dump request. */ if (arg.triggerOnDump) { callbacks.DataDumpRequest = &OnDataDumpRequest; } /* If take snaoshot when occurred gc. */ if(arg.triggerOnFullGC){ if(useCMS){ callbacks.GarbageCollectionStart = &OnCMSGCStart; callbacks.GarbageCollectionFinish = &OnCMSGCFinish; } /* FullGC on G1, we process at callbackForG1Full() */ else if(!useG1){ callbacks.GarbageCollectionStart = &OnGarbageCollectionStart; callbacks.GarbageCollectionFinish = &OnGarbageCollectionFinish; } } /* Set event callback in log function. */ /* If collect log when JVM's resource exhausted. */ if (arg.triggerOnLogError) { /* Setting resource exhusted event. */ callbacks.ResourceExhausted = &OnResourceExhausted; } /* If collect log when occurred deadlock. */ if (arg.triggerOnLogLock) { /* Setting monitor contended event. */ callbacks.MonitorContendedEnter = &OnMonitorContendedEnter; } /* Set event callback for init/final. */ callbacks.VMInit = &OnVMInit; callbacks.VMDeath = &OnVMDeath; /* If failure set event callbacks. */ if (isError(jvmti, jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks)))) { PRINT_CRIT_MSG("Couldn't register normal event."); return CALLBACKS_SETTING_FAILED; } /* Enable event notification. */ SWITCH_EVENT_NOTIFICATION(jvmti, JVMTI_EVENT_VM_INIT, JVMTI_ENABLE) SWITCH_EVENT_NOTIFICATION(jvmti, JVMTI_EVENT_VM_DEATH, JVMTI_ENABLE) return SUCCESS; } /*! * \brief Common initialization function. * \param vm [in] JavaVM object. * \param jvmti [out] JVMTI environment object. * \return Initialize process result. */ jint CommonInitialization(JavaVM *vm, jvmtiEnv **jvmti) { /* Check instruction set. */ verifyInstructSet(); /* Show package information. */ PRINT_INFO_MSG(PACKAGE_STRING); PRINT_INFO_MSG("Supported processor features:" #ifdef SSE2 " SSE2" #endif #ifdef SSE3 " SSE3" #endif #ifdef SSE4_1 " SSE4.1" #endif #ifdef AVX " AVX" #endif #if (!defined AVX) && (!defined SSE4_1) && (!defined SSE3) && (!defined SSE2) " None" #endif ); FLUSH_STDOUT; /* Get now date and set time as agent init-time. */ struct timeval tv; gettimeofday(&tv, NULL); TTrapSender::initializeTime = (jlong)tv.tv_sec * 100 + (jlong)tv.tv_usec / 10000; /* Get JVMTI environment object. */ if (vm->GetEnv((void **)jvmti, JVMTI_VERSION_1) != JNI_OK) { PRINT_CRIT_MSG("Get JVMTI environment information failed!"); return GET_ENVIRONMENT_FAILED; } /* Create thread instances that controlled snapshot trigger. */ try { jvmInfo = new TJvmInfo(); intervalSigTimer = new TTimer(&intervalSigProc, "HeapStats Signal Watcher"); } catch(const char *errMsg) { PRINT_CRIT_MSG(errMsg); return AGENT_THREAD_INITIALIZE_FAILED; } catch(...) { PRINT_CRIT_MSG("AgentThread initialize failed!"); return AGENT_THREAD_INITIALIZE_FAILED; } /* Invoke agent initialize of each function. */ jint result; result = onAgentInitForSnapShot(jvmti); if (result == SUCCESS) { result = onAgentInitForLog(jvmti); } return result; } /*! * \brief Agent attach entry points. * \param vm [in] JavaVM object. * \param options [in] Commandline arguments. * \param reserved [in] Reserved. * \return Attach initialization result code. */ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { /* Check memory duplication run. */ CHECK_DOUBLING_RUN; isOnDemandAttached = false; /* Parse arguments. */ if (options == NULL || strlen(options) == 0) { /* Make default configuration path. */ char confPath[PATH_MAX + 1] = {0}; snprintf(confPath, PATH_MAX, "%s/heapstats.conf", DEFAULT_CONF_DIR); loadConfiguration(confPath); } else { loadConfiguration(options); loadConfigPath = strdup(options); } /* JVMTI Event Setup. */ jvmtiEnv *jvmti; int result = CommonInitialization(vm, &jvmti); if (result != SUCCESS) { /* Failure event setup. */ return result; } /* Call common initialize. */ return InitEventSetting(jvmti); } /*! * \brief Common agent unload entry points. * \param vm [in] JavaVM object. */ JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { JNIEnv *env = NULL; /* Get JNI environment object. */ vm->GetEnv((void **)&env, JNI_VERSION_1_6); /* Invoke agent finalize of snapshot function. */ onAgentFinalForSnapShot(env); /* Invoke agent finalize of log function. */ onAgentFinalForLog(env); /* Destroy object is JVM running informations. */ delete jvmInfo; jvmInfo = NULL; /* Destroy object reload signal watcher timer. */ delete intervalSigTimer; intervalSigTimer = NULL; /* Free allocated string. */ freeSetting(&arg); /* Free allocated configuration file path string. */ free(loadConfigPath); loadConfigPath = NULL; } /*! * \brief Ondemand attach's entry points. * \param vm [in] JavaVM object. * \param options [in] Commandline arguments. * \param reserved [in] Reserved. * \return Ondemand-attach initialization result code. */ JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { /* Check memory duplication run. */ CHECK_DOUBLING_RUN; isOnDemandAttached = true; /* Parse arguments. */ if (options == NULL || strlen(options) == 0) { /* Make default configuration path. */ char confPath[PATH_MAX + 1] = {0}; snprintf(confPath, PATH_MAX, "%s/heapstats.conf", DEFAULT_CONF_DIR); loadConfiguration(confPath); } else { loadConfiguration(options); loadConfigPath = strdup(options); } /* Call common process. */ jvmtiEnv *jvmti; jint result = CommonInitialization(vm, &jvmti); if (result != SUCCESS) { return result; } /* Get JNI environment object. */ JNIEnv *env; if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) { PRINT_CRIT_MSG("Get JNI environment information failed!"); return GET_ENVIRONMENT_FAILED; } /* JVMTI Event Setup. */ InitEventSetting(jvmti); /* Call live step initialization. */ OnVMInit(jvmti, env, NULL); jvmInfo->detectDelayInfoAddress(); return SUCCESS; }