Mercurial > hg > release > heapstats-1.0
view agent/src/util.cpp @ 72:24fe0024d3e2
Bug 3339: [BACKOUT] Bug 3338 for HeapStats 1.0
Reviewed-by: yasuenag
author | KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp> |
---|---|
date | Fri, 10 Mar 2017 11:58:18 +0900 |
parents | 5c6af4ec5c40 |
children |
line wrap: on
line source
/*! * \file util.cpp * \brief This file is utilities. * Copyright (C) 2011-2017 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 <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <pcre.h> #include <iostream> #include <fstream> #include <string.h> #include <locale> #include <climits> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <time.h> #include "util.hpp" #include "fsUtil.hpp" #include "jvmInfo.hpp" /* Extern variables. */ /*! * \brief Agent attach mode flag. * Value is true, if agent is attached by on-demand-attach. * Value is false, if agent is attached by command line. */ bool isOnDemandAttached = false; /*! * \brief HeapStats configurations. */ TArguments arg; /*! * \brief SSE2 instruction usable flag. */ bool usableSSE2 = false; /*! * \brief SSE3 instruction usable flag. */ bool usableSSE3 = false; /*! * \brief SSSE3 instruction usable flag. */ bool usableSSSE3 = false; /*! * \brief SSE4.1 instruction usable flag. */ bool usableSSE4_1 = false; /*! * \brief SSE4.2 instruction usable flag. */ bool usableSSE4_2 = false; /*! * \brief Intel AVX instruction usable flag. */ bool usableAVX = false; /*! * \brief Page size got from sysconf. */ long systemPageSize = #ifdef _SC_PAGESIZE sysconf(_SC_PAGESIZE); #else #ifdef _SC_PAGE_SIZE sysconf(_SC_PAGE_SIZE); #else /* Suppose system page size to be 4KiByte. */ 4 * 1024; #endif #endif /* Inner variables. */ /*! * \brief Header of comment in configuration file. */ const char* COMMENT_HEADER = "//"; /*! * \brief Length of comment header. */ const int COMMENT_HEADER_LEN = 2; /*! * \brief JVMTI error detector. * \param jvmti [in] JVMTI envrionment object. * \param error [in] JVMTI error code. * \return Param "error" is error code?(true/false) */ bool isError(jvmtiEnv *jvmti, jvmtiError error){ /* If param "error" is error code. */ if (unlikely(error != JVMTI_ERROR_NONE)) { /* Get and output error message. */ char *errStr = NULL; jvmti->GetErrorName(error, &errStr); PRINT_WARN_MSG(errStr); jvmti->Deallocate((unsigned char*)errStr); return true; } return false; } /*! * \brief Set config to default. */ void initSetting(void){ /* Default Setting. */ arg.attach = true; arg.FileName = strdup("heapstats_snapshot.dat"); arg.heapLogFile = strdup("heapstats_log.csv"); arg.archiveFile = strdup("heapstats_analyze.zip"); arg.RankLevel = 5; arg.LogLevel = INFO; arg.reduceSnapShot = true; arg.triggerOnFullGC = true; arg.triggerOnDump = true; arg.triggerOnLogError = true; arg.triggerOnLogSignal = true; arg.triggerOnLogLock = false; arg.order = DELTA; arg.AlertPercentage = 50; arg.AlertThreshold = 0; /* Alert disabled. */ arg.HeapAlertPercentage= 95; arg.HeapAlertThreshold = 0; /* Alert disabled. */ arg.TimerInterval = 0; /* Timer disabled. */ arg.LogInterval = 300000; /* 5min. */ arg.firstCollect = true; arg.isFirstCollected = false; /* Don't collected yet, */ arg.logSignalNormal = strdup(""); /* Signal disabled. */ arg.logSignalAll = strdup("USR2"); arg.reloadSignal = strdup("HUP"); arg.snmpSend = true; arg.snmpTarget = strdup("localhost"); arg.snmpComName = strdup("public"); arg.logDir = strdup("./tmp"); arg.archiveCommand = strdup("/usr/bin/zip %archivefile% -jr %logdir%"); } /*! * \brief Free config memory. * \param targetArg [in,out] Deallocate target settings. */ void freeSetting(TArguments *targetArg){ free(targetArg->FileName); targetArg->FileName = NULL; free(targetArg->heapLogFile); targetArg->heapLogFile = NULL; free(targetArg->archiveFile); targetArg->archiveFile = NULL; free(targetArg->logSignalNormal); targetArg->logSignalNormal = NULL; free(targetArg->logSignalAll); targetArg->logSignalAll = NULL; free(targetArg->reloadSignal); targetArg->reloadSignal = NULL; free(targetArg->snmpTarget); targetArg->snmpTarget = NULL; free(targetArg->snmpComName); targetArg->snmpComName = NULL; free(targetArg->logDir); targetArg->logDir = NULL; free(targetArg->archiveCommand); targetArg->archiveCommand = NULL; } /*! * \brief Inherited config from old config. * \param newArg [in,out] New argument settings. * \param oldArg [in,out] Old argument settings. */ void inheritedSetting(TArguments *newArg, TArguments *oldArg){ TArguments swapArg; memcpy(&swapArg, newArg, sizeof(TArguments)); /* Copy settings. */ newArg->triggerOnFullGC &= oldArg->triggerOnFullGC; newArg->triggerOnDump &= oldArg->triggerOnDump; newArg->triggerOnLogError &= oldArg->triggerOnLogError; newArg->triggerOnLogSignal = oldArg->triggerOnLogSignal; newArg->triggerOnLogLock = oldArg->triggerOnLogLock; newArg->TimerInterval = oldArg->TimerInterval; newArg->LogInterval = oldArg->LogInterval; newArg->logSignalNormal = oldArg->logSignalNormal; newArg->logSignalAll = oldArg->logSignalAll; newArg->reloadSignal = oldArg->reloadSignal; newArg->snmpSend &= oldArg->snmpSend; newArg->snmpTarget = oldArg->snmpTarget; newArg->snmpComName = oldArg->snmpComName; /* Swap setting. */ oldArg->logSignalNormal = swapArg.logSignalNormal; oldArg->logSignalAll = swapArg.logSignalAll; oldArg->reloadSignal = swapArg.reloadSignal; oldArg->snmpSend &= swapArg.snmpSend; oldArg->snmpTarget = swapArg.snmpTarget; oldArg->snmpComName = swapArg.snmpComName; if(newArg->attach && (!oldArg->attach)){ /* Reset collected flag. */ newArg->isFirstCollected = false; } else{ newArg->isFirstCollected = oldArg->isFirstCollected; } } /*! * \brief Read boolean value from configuration. * \param key [in] Key of this configuration. * \param value [in] Value of this configuration. * \param default_val [in] Defalt value of this configuration. * \return value which is represented by boolean. */ static bool ReadBooleanValue(const char *key, const char *value, const bool default_val){ if(strcmp(value, "true") == 0){ return true; } else if(strcmp(value, "false") == 0){ return false; } else{ PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; return default_val; } } /*! * \brief Read string value for filename from configuration. * \param key [in] Key of this configuration. * \param value [in] Value of this configuration. * \param dest [in] [out] Destination of this configuration. */ static void ReadFileNameValue(const char *key, const char *value, char **dest){ /* If file path is legal. */ std::ofstream testStream(value, std::ios::app); if(testStream){ if(*dest != NULL){ free(*dest); } *dest = strdup(value); } else{ PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; } } /*! * \brief Read long/int value from configuration. * \param key [in] Key of this configuration. * \param value [in] Value of this configuration. * \param default_val [in] Defalt value of this configuration. * \param max_val [in] Max value of this parameter. * \return value which is represented by long. */ static long ReadLongValue(const char *key, const char *value, const long default_val, const long max_val){ long ret = default_val; /* Convert to number from string. */ char *done = NULL; long temp = strtol(value, &done, 10); /* If all string is able to convert to number. */ if(*done == '\0' && errno != ERANGE && temp <= INT_MAX && temp >= 0){ ret = temp; } else{ PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; } return ret; } /*! * \brief Check that path is accessible. * \param key [in] Key of this configuration. * \param value [in] Value of this configuration. * \return Path is accessible. */ static bool IsValidPath(const char *key, const char *value){ bool ret = false; /* Check archive file path. */ char *dir = getParentDirectoryPath(value); if(dir != NULL && isAccessibleDirectory(dir, true, true)) { /* File is accessible. */ ret = true; } else{ PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; } if(likely(dir != NULL)) { free(dir); } return ret; } /*! * \brief Load configuration from file. * \param filename [in] Read configuration file path. */ void loadConfiguration(const char *filename) { /* Initialize config. */ initSetting(); /* Check filename. */ if (filename == NULL || strlen(filename) == 0) { return; } /* Open file. */ std::ifstream ifs(filename); if (!ifs) { PRINT_WARN_MSG("Couldn't open configuration file."); return; } /* Set locale. */ /* Please rewrite as you like if you routinely don't speak English. */ /* if (setlocale(LC_CTYPE, "") == NULL) { PRINT_WARN_MSG("Couldn't set locale information."); return; } */ /* Get language table. */ const unsigned char *tables = NULL; /* pcre_maketables(); */ /* Initialize PCRE. */ char pExpression[255] = "^\\s*(.+?)\\s*=\\s*(.+?)?\\s*$"; const char *errMsg; int errOffset; real_pcre *pPcre; /* Generated parse trigger. */ pPcre = pcre_compile(pExpression, PCRE_ANCHORED, &errMsg, &errOffset, tables); /* If failure initialize. */ if (pPcre == NULL) { PRINT_WARN_MSG("Couldn't initialize PCRE."); pcre_free((void*)tables); return; } /* Get string line from configure file. */ long lineCnt = 0; char pBuff[512] = {0}; while (!ifs.eof()) { /* Read line. */ ifs.getline(pBuff, 512); lineCnt++; /* If this line is comment. */ if (memcmp(pBuff, COMMENT_HEADER, COMMENT_HEADER_LEN) == 0 || strlen(pBuff) == 0) { /* skip this line. */ continue; } /* Search param in line. */ int matchCnt = 10; int matchArr[10]; try { /* Parse line. */ matchCnt = pcre_exec(pPcre, NULL, pBuff, strlen(pBuff), 0, 0, matchArr, matchCnt); /* Check matched pair. */ if (matchCnt > 0) { /* Key and value variables. */ const char *key = NULL; const char *value = NULL; /* Get matched key string. */ if (pcre_get_substring(pBuff, matchArr, matchCnt, 1, &key) <= 0) { continue; } /* Get matched value string. */ if (pcre_get_substring(pBuff, matchArr, matchCnt, 2, &value) <= 0) { /* Get "" to set default value or disable option */ value = (char *)calloc(1, sizeof(char)); } /* Check key name. */ if(strcmp(key, "attach") == 0){ arg.attach = ReadBooleanValue(key, value, arg.attach); } else if(strcmp(key, "file") == 0){ ReadFileNameValue(key, value, &arg.FileName); } else if (strcmp(key, "heaplogfile") == 0){ ReadFileNameValue(key, value, &arg.heapLogFile); } else if(strcmp(key, "archivefile") == 0){ if(IsValidPath(key, value)){ free(arg.archiveFile); arg.archiveFile = strdup(value); } } else if (strcmp(key, "rank_level") == 0){ arg.RankLevel = ReadLongValue(key, value, arg.RankLevel, INT_MAX); } else if (strcmp(key, "loglevel") == 0){ /* Check log level. */ if (strcmp(value, "CRIT") == 0) { arg.LogLevel = CRIT; } else if (strcmp(value, "WARN") == 0) { arg.LogLevel = WARN; } else if (strcmp(value, "INFO") == 0) { arg.LogLevel = INFO; } else if (strcmp(value, "DEBUG") == 0) { arg.LogLevel = DEBUG; } else { PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; } } else if (strcmp(key, "reduce_snapshot") == 0){ arg.reduceSnapShot = ReadBooleanValue(key, value, arg.reduceSnapShot); } else if(strcmp(key, "trigger_on_fullgc") == 0){ arg.triggerOnFullGC = ReadBooleanValue(key, value, arg.triggerOnFullGC); } else if(strcmp(key, "trigger_on_dump") == 0){ arg.triggerOnDump = ReadBooleanValue(key, value, arg.triggerOnDump); } else if(strcmp(key, "trigger_on_logerror") == 0){ arg.triggerOnLogError = ReadBooleanValue(key, value, arg.triggerOnLogError); } else if(strcmp(key, "trigger_on_logsignal") == 0){ arg.triggerOnLogSignal = ReadBooleanValue(key, value, arg.triggerOnLogSignal); } else if(strcmp(key, "trigger_on_loglock") == 0){ arg.triggerOnLogLock = ReadBooleanValue(key, value, arg.triggerOnLogLock); } else if(strcmp(key, "rank_order") == 0){ /* Check ranking order value. */ if (strcmp(value, "usage") == 0) { arg.order = USAGE; } else if (strcmp(value, "delta") == 0) { arg.order = DELTA; } else { PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; } } else if(strcmp(key, "alert_percentage") == 0){ arg.AlertPercentage = ReadLongValue(key, value, arg.AlertPercentage, INT_MAX); } else if (strcmp(key, "javaheap_alert_percentage") == 0) { arg.HeapAlertPercentage = ReadLongValue(key, value, arg.HeapAlertPercentage, INT_MAX); } else if(strcmp(key, "snapshot_interval") == 0){ arg.TimerInterval = ReadLongValue(key, value, arg.TimerInterval, LONG_MAX / 1000) * 1000; } else if(strcmp(key, "log_interval") == 0){ arg.LogInterval = ReadLongValue(key, value, arg.LogInterval, LONG_MAX / 1000) * 1000; } else if(strcmp(key, "first_collect") == 0){ arg.firstCollect = ReadBooleanValue(key, value, arg.firstCollect); } else if (strcmp(key, "logsignal_normal") == 0 || strcmp(key, "logsignal_all") == 0 || strcmp(key, "signal_reload") == 0) { /* If signal is supported by JVM. */ if (isSupportSignal(value) || value == NULL || value[0] == '\0') { /* Copy string exclude "SIG". */ if (strcmp(key, "logsignal_normal") == 0) { free(arg.logSignalNormal); if (value == NULL || value[0] == '\0') { arg.logSignalNormal = strdup(""); } else { arg.logSignalNormal = strdup(value + 3); } } else if (strcmp(key, "logsignal_all") == 0) { free(arg.logSignalAll); if (value == NULL || value[0] == '\0') { arg.logSignalAll = strdup(""); } else { arg.logSignalAll = strdup(value + 3); } } else { free(arg.reloadSignal); if (value == NULL || value[0] == '\0') { arg.reloadSignal = strdup(""); } else { arg.reloadSignal = strdup(value + 3); } } } else { PRINT_WARN_MSG_HEADER << "Ignore illegal configuration value." << " name:" << key << " value:" << value << NEWLINE; } } else if(strcmp(key, "snmp_send") == 0){ arg.snmpSend = ReadBooleanValue( key, value, arg.snmpSend); } else if(strcmp(key, "snmp_target") == 0){ /* Setting snmp target. */ free(arg.snmpTarget); arg.snmpTarget = strdup(value); } else if(strcmp(key, "snmp_comname") == 0){ /* Setting snmp community name. */ free(arg.snmpComName); /* If set empty value to community name. */ if (strcmp(value, "(NULL)") == 0) { arg.snmpComName = strdup(""); } else { arg.snmpComName = strdup(value); } } else if(strcmp(key, "logdir") == 0){ if(IsValidPath(key, value)){ free(arg.logDir); arg.logDir = strdup(value); } } else if(strcmp(key, "archive_command") == 0){ /* Setting log archive command. */ free(arg.archiveCommand); arg.archiveCommand = strdup(value); } else{ PRINT_WARN_MSG_HEADER << "Unknown configuration name. name:" << key << NEWLINE; } /* Cleanup after param setting. */ pcre_free_substring(key); pcre_free_substring(value); } } catch(...){ PRINT_WARN_MSG_HEADER << "Read configuration failed. line:" << lineCnt << NEWLINE; } } /* Cleanup after load file. */ pcre_free((void*)tables); pcre_free(pPcre); } /*! * \brief Print setting information. */ void printSetting(void) { const char *EMPTY_STR = ""; /* Agent attach state. */ if (arg.attach) { PRINT_INFO_MSG_HEADER_NOIF << "Agent Attach Enable = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Agent Attach Enable = false" << NEWLINE; } /* Output filenames. */ PRINT_INFO_MSG_HEADER_NOIF << "SnapShot FileName = " << ((arg.FileName != NULL) ? arg.FileName : EMPTY_STR) << NEWLINE; PRINT_INFO_MSG_HEADER_NOIF << "Heap Log FileName = " << ((arg.heapLogFile != NULL) ? arg.heapLogFile : EMPTY_STR) << NEWLINE; PRINT_INFO_MSG_HEADER_NOIF << "Archive FileName = " << ((arg.archiveFile != NULL) ? arg.archiveFile : EMPTY_STR) << NEWLINE; /* Output log-level. */ switch(arg.LogLevel) { case CRIT: PRINT_INFO_MSG_HEADER_NOIF << "LogLevel = Critical" << NEWLINE; break; case WARN: PRINT_INFO_MSG_HEADER_NOIF << "LogLevel = Warning" << NEWLINE; break; case INFO: PRINT_INFO_MSG_HEADER_NOIF << "LogLevel = Info" << NEWLINE; break; case DEBUG: PRINT_INFO_MSG_HEADER_NOIF << "LogLevel = Debug" << NEWLINE; break; default: /* Illegal log level. */ PRINT_INFO_MSG_HEADER_NOIF << "LogLevel = UNKNOWN" << NEWLINE; break; } /* Output about reduce SnapShot. */ if (arg.reduceSnapShot) { PRINT_INFO_MSG_HEADER_NOIF << "ReduceSnapShot = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "ReduceSnapShot = false" << NEWLINE; } /* Output status of snapshot triggers. */ if (arg.triggerOnFullGC) { PRINT_INFO_MSG_HEADER_NOIF << "Trigger on FullGC = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Trigger on FullGC = false" << NEWLINE; } if (arg.triggerOnDump) { PRINT_INFO_MSG_HEADER_NOIF << "Trigger on DumpRequest = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Trigger on DumpRequest = false" << NEWLINE; } /* Output status of logging triggers. */ if (arg.triggerOnLogError) { PRINT_INFO_MSG_HEADER_NOIF << "Log Trigger on Error = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Log Trigger on Error = false" << NEWLINE; } if (arg.triggerOnLogSignal) { PRINT_INFO_MSG_HEADER_NOIF << "Log Trigger on Signal = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Log Trigger on Signal = false" << NEWLINE; } if (arg.triggerOnLogLock) { PRINT_INFO_MSG_HEADER_NOIF << "Log Trigger on Deadlock (experimental feature) = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Log Trigger on Deadlock (experimental feature) = false" << NEWLINE; } /* Output about ranking. */ if (arg.order == DELTA) { PRINT_INFO_MSG_HEADER_NOIF << "RankingOrder = DELTA" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "RankingOrder = USAGE" << NEWLINE; } PRINT_INFO_MSG_HEADER_NOIF << "RankLevel = " << arg.RankLevel << NEWLINE; /* Output about heap alert. */ if (arg.AlertThreshold <= 0) { PRINT_INFO_MSG_HEADER_NOIF << "HeapAlert is DISABLED." << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "AlertPercentage = " << arg.AlertPercentage << " ( " << arg.AlertThreshold << " bytes )" << NEWLINE; } /* Output about heap alert. */ if (arg.HeapAlertThreshold <= 0) { PRINT_INFO_MSG_HEADER_NOIF << "Java heap usage alert is DISABLED." << NEWLINE; } else{ PRINT_INFO_MSG_HEADER_NOIF << "Java heap usage alert percentage = " << arg.HeapAlertPercentage << " ( " << arg.HeapAlertThreshold / 1024 / 1024 << " MB )" << NEWLINE; } /* Output about interval snapshot. */ if (arg.TimerInterval == 0) { PRINT_INFO_MSG_HEADER_NOIF << "Interval SnapShot is DISABLED." << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "SnapShot interval = " << arg.TimerInterval / 1000 << " sec" << NEWLINE; } /* Output about interval logging. */ if (arg.LogInterval == 0) { PRINT_INFO_MSG_HEADER_NOIF << "Interval Logging is DISABLED." << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Log interval = " << arg.LogInterval / 1000 << " sec" << NEWLINE; } if (arg.firstCollect) { PRINT_INFO_MSG_HEADER_NOIF << "First Collect log = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "First Collect log = false" << NEWLINE; } /* Output logging signal name. */ if (arg.logSignalNormal == NULL || strlen(arg.logSignalNormal) == 0) { PRINT_INFO_MSG_HEADER_NOIF << "Normal logging signal is DISABLED." << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Normal logging signal = " << arg.logSignalNormal << NEWLINE; } if (arg.logSignalAll == NULL || strlen(arg.logSignalAll) == 0) { PRINT_INFO_MSG_HEADER_NOIF << "All logging signal is DISABLED." << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "All logging signal = " << arg.logSignalAll << NEWLINE; } if (arg.reloadSignal == NULL || strlen(arg.reloadSignal) == 0) { PRINT_INFO_MSG_HEADER_NOIF << "Reload signal is DISABLED." << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "Reload signal = " << arg.reloadSignal << NEWLINE; } /* Output about SNMP trap. */ if (arg.snmpSend) { PRINT_INFO_MSG_HEADER_NOIF << "SNMP send = true" << NEWLINE; } else { PRINT_INFO_MSG_HEADER_NOIF << "SNMP send = false" << NEWLINE; } PRINT_INFO_MSG_HEADER_NOIF << "SNMP target = " << ((arg.snmpTarget != NULL) ? arg.snmpTarget : EMPTY_STR) << NEWLINE; PRINT_INFO_MSG_HEADER_NOIF << "SNMP community = " << ((arg.snmpComName != NULL) ? arg.snmpComName : EMPTY_STR) << NEWLINE; /* Output temporary log directory path. */ PRINT_INFO_MSG_HEADER_NOIF << "Log dirictory = " << ((arg.logDir != NULL) ? arg.logDir : EMPTY_STR) << NEWLINE; /* Output archive command. */ PRINT_INFO_MSG_HEADER_NOIF << "Archive command = \"" << ((arg.archiveCommand != NULL) ? arg.archiveCommand : EMPTY_STR) << "\"" << NEWLINE; } /*! * \brief Get system information. * \param env [in] JNI envrionment. * \param key [in] System property key. * \return String of system property. */ char *GetSystemProperty(JNIEnv *env, const char *key) { /* Convert string. */ jstring key_str = env->NewStringUTF(key); /* Search class. */ jclass sysClass = env->FindClass("Ljava/lang/System;"); /* if raised exception. */ if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); PRINT_WARN_MSG("Get system class failed !"); return NULL; } /* Search method. */ jmethodID System_getProperty = env->GetStaticMethodID(sysClass, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); /* If raised exception. */ if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); PRINT_WARN_MSG("Get system method failed !"); return NULL; } /* Call method in find class. */ jstring ret_str = (jstring)env->CallStaticObjectMethod(sysClass, System_getProperty, key_str); /* If got nothing or raised exception. */ if ((ret_str == NULL) || env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); PRINT_WARN_MSG("Get system properties failed !"); return NULL; } /* Get result and clean up. */ const char *ret_utf8 = env->GetStringUTFChars(ret_str, NULL); char *ret = NULL; /* If raised exception. */ if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); } else if(ret_utf8 != NULL) { /* Copy and free if got samething. */ ret = strdup(ret_utf8); env->ReleaseStringUTFChars(ret_str, ret_utf8); } /* Cleanup. */ env->DeleteLocalRef(key_str); return ret; } /*! * \brief Get ClassUnload event index. * \param jvmti [in] JVMTI envrionment object. * \return ClassUnload event index. * \sa hotspot/src/share/vm/prims/jvmtiExport.cpp<br> * hotspot/src/share/vm/prims/jvmtiEventController.cpp<br> * hotspot/src/share/vm/prims/jvmti.xml<br> * in JDK. */ jint GetClassUnloadingExtEventIndex(jvmtiEnv *jvmti) { jint count, ret = -1; jvmtiExtensionEventInfo *events; /* Get extension events. */ if (isError(jvmti, jvmti->GetExtensionEvents(&count, &events))) { /* Failure get extension events. */ PRINT_WARN_MSG("Get JVMTI Extension Event failed!"); return -1; } else if(count <= 0) { /* If extension event is none. */ PRINT_WARN_MSG("VM has no JVMTI Extension Event!"); if (events != NULL) { jvmti->Deallocate((unsigned char *)events); } return -1; } /* Check extension events. */ for (int Cnt = 0; Cnt < count; Cnt++) { if (strcmp(events[Cnt].id, "com.sun.hotspot.events.ClassUnload") == 0) { ret = events[Cnt].extension_event_index; break; } } /* Clean up. */ jvmti->Deallocate((unsigned char *)events); return ret; } /*! * \brief Verify CPU instruction set. */ void verifyInstructSet(void){ int cFlag = 0; int dFlag = 0; /* Call CPUID. */ asm volatile( /* ebx is address register by PIC in 32bit. */ #ifdef __i386__ "pushl %%ebx;\n\t" #endif "cpuid;\n\t" #ifdef __i386__ "popl %%ebx;\n\t" #endif : "=c"(cFlag), "=d"(dFlag) : "a"(1) : "cc" #ifndef __i386__ , "%ebx" #endif ); /* Set instruction usable flag. */ usableAVX = ((cFlag >> 28) & 1) != 0; usableSSE4_2 = ((cFlag >> 20) & 1) != 0; usableSSE4_1 = (((cFlag >> 19) & 1) != 0) || usableSSE4_2; usableSSSE3 = ((cFlag >> 9) & 1) != 0; usableSSE3 = (( cFlag & 1) != 0) || usableSSSE3; usableSSE2 = ((dFlag >> 26) & 1) != 0; /* If doesn't meet required performance conditions. */ if (!usableSSE2) { PRINT_WARN_MSG("Performance isn't enough." " please confirm this machine spec."); } } /*! * \brief Replace old string in new string on string. * \param str [in] Process target string. * \param oldStr [in] Targer of replacing. * \param newStr [in] A string will replace existing string. * \return String invoked replace.<br>Don't forget deallocate. */ char *strReplase(char const* str, char const* oldStr, char const* newStr) { char *strPos = NULL; char *chrPos = NULL; int oldStrCnt = 0; int oldStrLen = 0; char *newString = NULL; int newStrLen = 0; /* Sanity check. */ if (unlikely(str == NULL || oldStr == NULL || newStr == NULL || strlen(str) == 0 || strlen(oldStr) == 0)) { PRINT_WARN_MSG("Illegal string replacing paramters."); return NULL; } oldStrLen = strlen(oldStr); strPos = (char*)str; /* Counting oldStr */ while (true) { /* Find old string. */ chrPos = strstr(strPos, oldStr); if (chrPos == NULL) { /* All old string is already found. */ break; } /* Counting. */ oldStrCnt++; /* Move next. */ strPos = chrPos + oldStrLen; } newStrLen = strlen(newStr); /* Allocate result string memory. */ newString = (char*)calloc(1, strlen(str) + (newStrLen * oldStrCnt) - (oldStrLen * oldStrCnt) + 1); /* If failure allocate result string. */ if (unlikely(newString == NULL)) { PRINT_WARN_MSG("Failure allocate replaced string."); return NULL; } strPos = (char*)str; while (true) { /* Find old string. */ chrPos = strstr(strPos, oldStr); if (unlikely(chrPos == NULL)) { /* all old string is replaced. */ strcat(newString, strPos); break; } /* Copy from string exclude old string. */ strncat(newString, strPos, (chrPos - strPos)); /* Copy from new string. */ strcat(newString, newStr); /* Move next. */ strPos = chrPos + oldStrLen; } /* Succeed. */ return newString; } /*! * \brief Get now date and time. * \return Mili-second elapsed time from 1970/1/1 0:00:00. */ jlong getNowTimeSec(void) { struct timeval tv; gettimeofday(&tv, NULL); return (jlong)tv.tv_sec * 1000 + (jlong)tv.tv_usec / 1000; } /*! * \brief Check signal is supported by JVM. * \param sigName [in] Name of signal. * \return Signal is supported, if value is true. * Otherwise, value is false. * \sa Please see below JDK source about JVM supported signal.<br> * hotspot/src/os/linux/vm/jvm_linux.cpp */ bool isSupportSignal(char const* sigName) { /* JVM supported signals. */ const char *supportSignals[] = { /* POSIX signal. */ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", "SIGFPE", "SIGKILL", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGUSR1", "SIGUSR2", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGBUS", "SIGPOLL", "SIGTRAP", "SIGURG", "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGPROF", #ifdef SIGSYS "SIGSYS", #endif /* Non-POSIX signal. */ "SIGIOT", "SIGIO", "SIGCLD", "SIGPWR", "SIGWINCH", #ifdef SIGSTKFLT "SIGSTKFLT", #endif /* End flag. */ NULL /* JVM was not supported below signal. */ /* "SIGINFO", "SIGLOST", "SIGEMT", "SIGUNUSED" and etc.. */ }; /* Search signal name. */ for (int sigIdx = 0; supportSignals[sigIdx] != NULL; sigIdx++) { if (strcmp(sigName, supportSignals[sigIdx]) == 0) { /* Found. */ return true; } } return false; } /*! * \brief A little sleep. * \param sec [in] Second of sleep range. * \param nsec [in] Nano second of sleep range. */ void littleSleep(const long int sec, const long int nsec) { /* Timer setting variables. */ int result = 0; struct timespec buf1; struct timespec buf2 = {sec, nsec}; struct timespec *req = &buf1; struct timespec *rem = &buf2; /* Wait loop. */ do { /* Reset and exchange time. */ memset(req, 0, sizeof(timespec)); asm volatile( #ifdef __x86_64__ "xchgq %0, %1;" #else // __i386__ "xchgl %0, %1;" #endif : "=r" (req), "=r" (rem) : "0" (req), "1" (rem)); errno = 0; /* Sleep. */ result = nanosleep(req, rem); } while(result != 0 && errno == EINTR); } /*! * \brief Get thread information. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param thread [in] Thread object in java. * \param info [out] Record stored thread information. */ void getThreadDetailInfo(jvmtiEnv *jvmti, JNIEnv* env, jthread thread, TJavaThreadInfo *info) { /* Get thread basic information. */ jvmtiThreadInfo threadInfo = {0}; if (unlikely(isError(jvmti, jvmti->GetThreadInfo(thread, &threadInfo)))) { /* Setting dummy information. */ info->name = strdup("Unknown-Thread"); info->isDaemon = false; info->priority = 0; } else { /* Setting information. */ info->name = strdup(threadInfo.name); info->isDaemon = (threadInfo.is_daemon == JNI_TRUE); info->priority = threadInfo.priority; /* Cleanup. */ jvmti->Deallocate((unsigned char *)threadInfo.name); env->DeleteLocalRef(threadInfo.thread_group); env->DeleteLocalRef(threadInfo.context_class_loader); } /* Get thread state. */ jint state = 0; jvmti->GetThreadState(thread, &state); /* Set thread state. */ switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) { case JVMTI_JAVA_LANG_THREAD_STATE_NEW: info->state = strdup("NEW"); break; case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED: info->state = strdup("TERMINATED"); break; case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE: info->state = strdup("RUNNABLE"); break; case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED: info->state = strdup("BLOCKED"); break; case JVMTI_JAVA_LANG_THREAD_STATE_WAITING: info->state = strdup("WAITING"); break; case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING: info->state = strdup("TIMED_WAITING"); break; default: info->state = NULL; } } /*! * \brief Get method information in designed stack frame. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param frame [in] method stack frame. * \param info [out] Record stored method information in stack frame. */ void getMethodFrameInfo(jvmtiEnv *jvmti, JNIEnv* env, jvmtiFrameInfo frame, TJavaStackMethodInfo *info) { char *tempStr = NULL; jclass declareClass = NULL; /* Get method class. */ if (unlikely(isError(jvmti, jvmti->GetMethodDeclaringClass(frame.method, &declareClass)))) { info->className = NULL; info->sourceFile = NULL; } else { /* Get class signature. */ if (unlikely(isError(jvmti, jvmti->GetClassSignature(declareClass, &tempStr, NULL)))) { info->className = NULL; } else { info->className = strdup(tempStr); jvmti->Deallocate((unsigned char *)tempStr); } /* Get source filename. */ if (unlikely(isError(jvmti, jvmti->GetSourceFileName(declareClass, &tempStr)))) { info->sourceFile = NULL; } else { info->sourceFile = strdup(tempStr); jvmti->Deallocate((unsigned char *)tempStr); } env->DeleteLocalRef(declareClass); } /* Get method name. */ if (unlikely(isError(jvmti, jvmti->GetMethodName(frame.method, &tempStr, NULL, NULL)))) { info->methodName = NULL; } else { info->methodName = strdup(tempStr); jvmti->Deallocate((unsigned char *)tempStr); } /* Check method is native. */ jboolean isNativeMethod = JNI_TRUE; jvmti->IsMethodNative(frame.method, &isNativeMethod); if (unlikely(isNativeMethod == JNI_TRUE)) { /* method is native. */ info->isNative = true; info->lineNumber = -1; } else { /* method is java method. */ info->isNative = false; /* Get source code line number. */ jint entriyCount = 0; jvmtiLineNumberEntry *entries = NULL; if (unlikely(isError(jvmti, jvmti->GetLineNumberTable(frame.method, &entriyCount, &entries)))) { /* Method location is unknown. */ info->lineNumber = -1; } else { /* Search running line. */ jint lineIdx = 0; entriyCount--; for (; lineIdx < entriyCount; lineIdx++) { if (frame.location <= entries[lineIdx].start_location) { break; } } info->lineNumber = entries[lineIdx].line_number; jvmti->Deallocate((unsigned char *)entries); } } }