Mercurial > hg > release > heapstats-1.0
view agent/src/logManager.cpp @ 41:db8eea95ac52
Bug 1849: HeapStats agent should collect systemd-journald information
reviewed-by: yasuenag
author | KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp> |
---|---|
date | Fri, 20 Jun 2014 12:10:55 +0900 |
parents | 48dc7c3ceee5 |
children | c80ffa6f3759 |
line wrap: on
line source
/*! * \file logManager.cpp * \brief This file is used collect log information. * Copyright (C) 2011-2013 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 <pthread.h> #include <unistd.h> #include <queue> #include <sys/utsname.h> #include <gnu/libc-version.h> #include <fstream> #include <fcntl.h> #include <stdlib.h> #include <limits.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include "logManager.hpp" #include "util.hpp" #include "trapSender.hpp" #include "cmdArchiver.hpp" #include "jniZipArchiver.hpp" #include "jvmSockCmd.hpp" #include "jvmInfo.hpp" #include "fsUtil.hpp" /* Static variables. */ /*! * \brief Mutex of collect normal log. */ pthread_mutex_t TLogManager::logMutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /*! * \brief Mutex of archive file. */ pthread_mutex_t TLogManager::archiveMutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /* Util method for log manager. */ /*! * \brief Convert log cause to integer for file output. * \param cause [in] Log collect cause. * \return Number of log cause. */ inline int logCauseToInt(TInvokeCause cause) { /* Convert invoke function cause. */ int logCause; switch (cause) { case ResourceExhausted: case ThreadExhausted: logCause = 1; break; case Signal: case AnotherSignal: logCause = 2; break; case Interval: logCause = 3; break; case OccurredDeadlock: logCause = 4; break; default: /* Illegal. */ logCause = 0; } return logCause; } /* Class method. */ /*! * \brief TLogManager constructor. * \param env [in] JNI environment object. * \param info [in] JVM running performance information. */ TLogManager::TLogManager(JNIEnv* env, TJvmInfo *info) { /* Sanity check. */ if (unlikely(info == NULL)) { throw "TJvmInfo is NULL."; } jvmInfo = info; char *tempdirPath = NULL; /* Get temporary path of java */ tempdirPath = GetSystemProperty(env, "java.io.tmpdir"); try { /* Create JVM socket commander. */ jvmCmd = new TJVMSockCmd(tempdirPath); /* Archive file maker. */ arcMaker = new TCmdArchiver(); /* Archive file maker to use zip library in java. */ jniArchiver = new TJniZipArchiver(); } catch(...) { free(tempdirPath); throw "TLogManager initialize failed!"; } free(tempdirPath); } /*! * \brief TLogManager destructor. */ TLogManager::~TLogManager(void) { /* Destroy instance. */ delete jvmCmd; delete arcMaker; delete jniArchiver; } /*! * \brief Collect log. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param cause [in] Invoke function cause.<br> * E.g. ResourceExhausted, Signal, Interval. * \param nowTime [in] Log collect time. * \return Value is true, if process is succeed. */ bool TLogManager::collectLog(jvmtiEnv *jvmti, JNIEnv* env, TInvokeCause cause, TMSecTime nowTime) { bool result = true; /* Variable store archive file path. */ char arcPath[PATH_MAX] = {0}; /* Switch collecting log type by invoking cause. */ switch (cause) { case ResourceExhausted: case ThreadExhausted: case AnotherSignal: case OccurredDeadlock: /* Collect log about java running environment and etc.. */ result &= collectAllLog(jvmti, env, cause, nowTime, (char*)arcPath, PATH_MAX); default: /* Collect log about java and machine now status. */ result &= collectNormalLog(cause, nowTime, arcPath); } return result; } /*! * \brief Collect normal log. * \param cause [in] Invoke function cause.<br> * E.g. ResourceExhausted, Signal, Interval. * \param nowTime [in] Log collect time. * \param archivePath [in] Archive file path. * \return Value is true, if process is succeed. */ bool TLogManager::collectNormalLog(TInvokeCause cause, TMSecTime nowTime, char *archivePath) { bool result = true; TLargeUInt systime = 0; TLargeUInt usrtime = 0; TLargeUInt vmsize = 0; TLargeUInt rssize = 0; TMachineTimes cpuTimes = {0}; /* Get java process information. */ if (unlikely(!getProcInfo(&systime, &usrtime, &vmsize, &rssize))) { PRINT_WARN_MSG("Failure getting java process information."); } /* Get machine cpu times. */ if (unlikely(!getSysTimes(&cpuTimes))) { PRINT_WARN_MSG("Failure getting machine cpu times."); } /* Make write log line. */ char logData[4097] = {0}; snprintf(logData, 4096, "%lld,%d" /* Format : Logging information. */ ",%llu,%llu,%llu,%llu" /* Format : Java process information. */ ",%llu,%llu,%llu" /* Format : Machine CPU times. */ ",%llu,%llu,%llu" ",%llu,%llu,%llu" ",%lld,%lld,%lld,%lld" /* Format : JVM running information. */ ",%s\n", /* Format : Archive file name. */ /* Params : Logging information. */ nowTime, logCauseToInt(cause), /* Params : Java process information. */ usrtime, systime, vmsize, rssize, /* Params : Machine CPU times. */ cpuTimes.usrTime, cpuTimes.lowUsrTime, cpuTimes.sysTime, cpuTimes.idleTime, cpuTimes.iowaitTime, cpuTimes.irqTime, cpuTimes.sortIrqTime, cpuTimes.stealTime, cpuTimes.guestTime, /* Params : JVM running information. */ (long long int)jvmInfo->getSyncPark(), (long long int)jvmInfo->getSafepointTime(), (long long int)jvmInfo->getSafepoints(), (long long int)jvmInfo->getThreadLive(), /* Params : Archive file name. */ archivePath); /* Get mutex. */ ENTER_PTHREAD_SECTION(&logMutex) /* Open log file. */ int fd = open(arg.heapLogFile, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* If failure open file. */ if (unlikely(fd < 0)) { PRINT_WARN_MSG("Couldn't open log file."); result = false; } else { /* Write line to log file. */ if (unlikely(write(fd, logData, strlen(logData)) < 0)) { PRINT_WARN_MSG("Couldn't append log file."); result = false; } /* Cleanup. */ close(fd); } /* Release mutex. */ EXIT_PTHREAD_SECTION(&logMutex) return result; } /*! * \brief Collect all log. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param cause [in] Invoke function cause.<br> * E.g. ResourceExhausted, Signal, Interval. * \param nowTime [in] Log collect time. * \param archivePath [out] Archive file path. * \param pathLen [in] Max size of paramter"archivePath". * \return Value is true, if process is succeed. */ bool TLogManager::collectAllLog(jvmtiEnv *jvmti, JNIEnv* env, TInvokeCause cause, TMSecTime nowTime, char *archivePath, size_t pathLen) { /* Working directory path. */ char *basePath = NULL; /* Archive file path. */ char *uniqArcName = NULL; /* Make directory. */ createTempDir(&basePath, arg.logDir); if (unlikely(basePath == NULL)) { PRINT_WARN_MSG("Failure create working directory."); return false; } /* Create enviroment report file. */ if (unlikely(!makeEnvironFile(basePath, cause, nowTime))) { PRINT_WARN_MSG("Failure create enviroment report file."); } /* Copy many files. */ copyInfoFiles(basePath); /* Dump thread information. */ char *dumpName = NULL; /* Create dump filename. */ dumpName = createFilename((char*)basePath, "threaddump.txt"); if (unlikely(dumpName == NULL)) { PRINT_WARN_MSG("Couldn't allocate thread dump file path."); } else { /* Flag of thread dump is failed. */ bool flagFailDump = false; if (unlikely(cause == ThreadExhausted && !jvmCmd->isConnectable())) { /* * JVM is aborted when reserve signal SIGQUIT, * if JVM can't make new thread (e.g. RLIMIT_NPROC). * So we need avoid to send SIGQUIT signal. */ flagFailDump = true; } else { /* Create thread dump file. */ if (unlikely(jvmCmd->exec("threaddump", dumpName) != 0)) { flagFailDump = true; } } /* If need original thread dump. */ if (unlikely(flagFailDump)) { flagFailDump = !makeThreadDump(jvmti, env, dumpName, nowTime); } /* If failure thread dump. */ if (unlikely(flagFailDump)) { PRINT_WARN_MSG("Failure thread dumping."); } free(dumpName); } /* Copy gc log file. */ if (unlikely(!copyOptionFile("-Xloggc:", (char*)basePath))) { PRINT_WARN_MSG("Failure copy gc log file."); } /* Variable for archive process result. */ int result = 1; /* Get mutex. */ ENTER_PTHREAD_SECTION(&archiveMutex) /* Create archive file name. */ uniqArcName = createArchiveName(nowTime); if (unlikely(uniqArcName == NULL)) { /* Failure make archive uniq name. */ PRINT_WARN_MSG("Failure create archive name."); } else { /* Execute archive. */ arcMaker->setTarget(basePath); result = arcMaker->doArchive(env, uniqArcName); struct stat st = {0}; /* If process is succeed but archive isn't exists. */ if (unlikely(result == 0 && stat(uniqArcName, &st) != 0)) { /* Accept as failure. */ result = -1; } /* If failure create archive file. */ if (unlikely(result != 0)) { /* Execute archive to use jniArchiver. */ jniArchiver->setTarget(basePath); result = jniArchiver->doArchive(env, uniqArcName); } } /* Release mutex. */ EXIT_PTHREAD_SECTION(&archiveMutex) char *sendPath = uniqArcName; bool flagDirectory = false; /* If archive process is succeed. */ if (likely(result == 0)) { /* Search last path separator. */ char *filePos = strrchr(uniqArcName, '/'); if (filePos != NULL) { filePos++; } else { filePos = uniqArcName; } /* Copy archive file name without directory path. */ strncpy(archivePath, filePos, pathLen); } else { /* If failure execute command. */ PRINT_WARN_MSG("Failure create archive file."); /* Send working directory path to make up for archive file. */ sendPath = basePath; flagDirectory = true; } /* Send log archive trap. */ if (unlikely(!sendLogArchiveTrap(cause, nowTime, sendPath, flagDirectory))) { PRINT_WARN_MSG("Send SNMP log archive trap failed!"); } /* If execute command is succeed. */ if (likely(result == 0)) { /* Remove needless working directory. */ removeTempDir(basePath); } /* If allocated archive file path. */ if (likely(uniqArcName != NULL)) { free(uniqArcName); } /* Cleanup. */ free(basePath); return (result == 0); } /*! * \brief Create file about JVM running environment. * \param basePath [in] Path of directory put report file. * \param cause [in] Invoke function cause.<br> * E.g. Signal, ResourceExhausted, Interval. * \param nowTime [in] Log collect time. * \return Prosess is succeed or failure. */ bool TLogManager::makeEnvironFile(char *basePath, TInvokeCause cause, TMSecTime nowTime) { /* Invoke OS version function. */ struct utsname uInfo; memset(&uInfo, 0, sizeof(struct utsname)); if (unlikely(uname(&uInfo) != 0)) { PRINT_WARN_MSG("Failure call uname."); } /* Invoke glibc information function. */ const char *glibcVersion = NULL; const char *glibcRelease = NULL; glibcVersion = gnu_get_libc_version(); glibcRelease = gnu_get_libc_release(); if (unlikely(glibcVersion == NULL || glibcRelease == NULL)) { PRINT_WARN_MSG("Failure call glibc function."); } /* Create filename. */ char *envInfoName = NULL; envInfoName = createFilename(basePath, "envInfo.txt"); /* If failure create file name. */ if (unlikely(envInfoName == NULL)) { PRINT_WARN_MSG("Couldn't allocate filename."); return false; } /* Create envInfo.txt. */ std::ofstream ofs(envInfoName, std::ios::out); if (unlikely(!ofs)) { PRINT_WARN_MSG("Couldn't open enviroment report."); free(envInfoName); return false; } /* Separator of between name and value. */ const char SepChar = '='; /* Output enviroment information. */ ofs << "CollectionDate" << SepChar << nowTime << std::endl << "LogTrigger" << SepChar << logCauseToInt(cause) << std::endl; ofs << "VmVersion" << SepChar; if (likely(jvmInfo->getVmVersion() != NULL)) { ofs << jvmInfo->getVmVersion(); } ofs << std::endl; ofs << "OsRelease" << SepChar << (char*)uInfo.release << std::endl; ofs << "LibCVersion" << SepChar; if (likely(glibcVersion != NULL)) { ofs << glibcVersion; } ofs << std::endl; ofs << "LibCRelease" << SepChar; if (likely(glibcRelease != NULL)) { ofs << glibcRelease; } ofs << std::endl; ofs << "VmName" << SepChar; if (likely(jvmInfo->getVmName() != NULL)) { ofs << jvmInfo->getVmName(); } ofs << std::endl; ofs << "ClassPath" << SepChar; if (likely(jvmInfo->getClassPath() != NULL)) { ofs << jvmInfo->getClassPath(); } ofs << std::endl; ofs << "EndorsedPath" << SepChar; if (likely(jvmInfo->getEndorsedPath() != NULL)) { ofs << jvmInfo->getEndorsedPath(); } ofs << std::endl; ofs << "JavaVersion" << SepChar; if (likely(jvmInfo->getJavaVersion() != NULL)) { ofs << jvmInfo->getJavaVersion(); } ofs << std::endl; ofs << "JavaHome" << SepChar; if (likely(jvmInfo->getJavaHome() != NULL)) { ofs << jvmInfo->getJavaHome(); } ofs << std::endl; ofs << "BootClassPath" << SepChar; if (likely(jvmInfo->getBootClassPath() != NULL)) { ofs << jvmInfo->getBootClassPath(); } ofs << std::endl; ofs << "VmArgs" << SepChar; if (likely(jvmInfo->getVmArgs() != NULL)) { ofs << jvmInfo->getVmArgs(); } ofs << std::endl; ofs << "VmFlags" << SepChar; if (likely(jvmInfo->getVmFlags() != NULL)) { ofs << jvmInfo->getVmFlags(); } ofs << std::endl; ofs << "JavaCmd" << SepChar; if (likely(jvmInfo->getJavaCommand() != NULL)) { ofs << jvmInfo->getJavaCommand(); } ofs << std::endl; ofs << "VmTime" << SepChar << jvmInfo->getTickTime() << std::endl; /* Cleanup. */ ofs.close(); free(envInfoName); return true; } /*! * \brief Dump thread and stack information to stream. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param ofs [in] Output stream object. * \param stackInfo [in] Stack frame of java thread. */ void TLogManager::dumpThreadInformation(jvmtiEnv *jvmti, JNIEnv* env, std::ofstream &ofs, jvmtiStackInfo stackInfo) { /* * This function use below JVMTI function. * - GetOwnedMonitorStackDepthInfo, GetCurrentContendedMonitor * But the JVMTI function is need capabilities. * The capability is cann't get if agent is attached by on-demand-attach. * So check attach type before call the JVMTI function. */ bool hasCapability = !isOnDemandAttached; /* Get thread information. */ TJavaThreadInfo threadInfo = {0}; getThreadDetailInfo(jvmti, env, stackInfo.thread, &threadInfo); /* Print thread information. */ if (likely(threadInfo.name != NULL)) { ofs << "\"" << threadInfo.name << "\""; } else { ofs << "UnknownThread"; } if (unlikely(threadInfo.isDaemon)) { ofs << " daemon"; } ofs << " prio=" << threadInfo.priority; ofs << std::endl; ofs << " java.lang.Thread.State: "; if (likely(threadInfo.state != NULL)) { ofs << threadInfo.state; } else { ofs << "Unknown"; } ofs << std::endl; /* Cleanup. */ free(threadInfo.name); free(threadInfo.state); /* Get thread monitor stack information. */ jint monitorCount = 0; jvmtiMonitorStackDepthInfo *monitorInfo = NULL; if (unlikely(hasCapability && isError(jvmti, jvmti->GetOwnedMonitorStackDepthInfo(stackInfo.thread, &monitorCount, &monitorInfo)))) { /* Agnet is failed getting monitor info. */ monitorInfo = NULL; } /* Output thread flame trace. */ for (jint flameIdx = 0, flameSize = stackInfo.frame_count; flameIdx < flameSize; flameIdx++) { jvmtiFrameInfo frameInfo = stackInfo.frame_buffer[flameIdx]; TJavaStackMethodInfo methodInfo = {0}; /* Get method information. */ getMethodFrameInfo(jvmti, env, frameInfo, &methodInfo); /* Output method class, name and source file location. */ ofs << "\tat "; if (likely(methodInfo.className != NULL)) { ofs << methodInfo.className; } else { ofs << "UnknwonClass"; } ofs << "."; if (likely(methodInfo.methodName != NULL)) { ofs << methodInfo.methodName; } else { ofs << "UnknwonMethod"; } ofs << "("; if (unlikely(methodInfo.isNative)) { ofs << "Native method"; } else { if (likely(methodInfo.sourceFile != NULL)) { ofs << methodInfo.sourceFile; } else { ofs << "UnknwonFile"; } ofs << ":"; if (likely(methodInfo.lineNumber > 0)) { ofs << methodInfo.lineNumber; } else { ofs << "UnknwonLine"; } } ofs << ")" << std::endl; /* Cleanup. */ free(methodInfo.className); free(methodInfo.methodName); free(methodInfo.sourceFile); /* If current stack frame. */ if (unlikely(hasCapability && flameIdx == 0)) { jobject jMonitor = NULL; jvmti->GetCurrentContendedMonitor(stackInfo.thread, &jMonitor); /* If contented monitor is existing now. */ if (likely(jMonitor != NULL)) { jclass monitorClass = NULL; char *tempStr = NULL; char ownerThreadName[1024] = "UNKNOWN"; char monitorClsName[1024] = "UNKNOWN"; /* Get monitor class. */ monitorClass = env->GetObjectClass(jMonitor); if (likely(monitorClass != NULL)) { /* Get class signature. */ jvmti->GetClassSignature(monitorClass, &tempStr, NULL); if (likely(tempStr != NULL)) { snprintf(monitorClsName, 1024, "%s", tempStr); jvmti->Deallocate((unsigned char *)tempStr); } /* Cleanup. */ env->DeleteLocalRef(monitorClass); } /* Get monitor owner. */ jvmtiMonitorUsage monitorInfo = {0}; if (unlikely(!isError(jvmti, jvmti->GetObjectMonitorUsage(jMonitor, &monitorInfo)))) { /* Get owner thread information. */ getThreadDetailInfo(jvmti, env, monitorInfo.owner, &threadInfo); if (likely(threadInfo.name != NULL)) { strncpy(ownerThreadName, threadInfo.name, 1024); } /* Cleanup. */ free(threadInfo.name); free(threadInfo.state); } /* Print contented monitor information. */ ofs << "\t- waiting to lock " << "<owner:" << ownerThreadName << ">" << " (a " << monitorClsName << ")" << std::endl; env->DeleteLocalRef(jMonitor); } } if (likely(monitorInfo != NULL)) { /* Search monitor. */ jint monitorIdx = 0; for (; monitorIdx < monitorCount; monitorIdx++) { if (monitorInfo[monitorIdx].stack_depth == flameIdx) { break; } } /* If locked monitor is found. */ if (likely(monitorIdx < monitorCount)) { jobject jMonitor = monitorInfo[monitorIdx].monitor; jclass monitorClass = NULL; char *tempStr = NULL; char monitorClsName[1024] = "UNKNOWN"; /* Get object class. */ monitorClass = env->GetObjectClass(jMonitor); if (likely(monitorClass != NULL)) { /* Get class signature. */ jvmti->GetClassSignature(monitorClass, &tempStr, NULL); if (likely(tempStr != NULL)) { snprintf(monitorClsName, 1024, "%s", tempStr); jvmti->Deallocate((unsigned char *)tempStr); } env->DeleteLocalRef(monitorClass); } /* Print owned monitor information. */ ofs << "\t- locked" << " (a " << monitorClsName << ")" << std::endl; } } } ofs << std::endl; /* Cleanup. */ if (likely(monitorInfo != NULL)) { for (jint monitorIdx = 0; monitorIdx < monitorCount; monitorIdx++) { env->DeleteLocalRef(monitorInfo[monitorIdx].monitor); } jvmti->Deallocate((unsigned char *)monitorInfo); } } /*! * \brief Create thread dump file. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param filename [in] Path of thread dump file. * \param nowTime [in] Log collect time. * \return Prosess is succeed or failure. */ bool TLogManager::makeThreadDump(jvmtiEnv *jvmti, JNIEnv* env, char *filename, TMSecTime nowTime) { /* Open thread dump file. */ std::ofstream ofs(filename, std::ios::out); if (unlikely(!ofs)) { PRINT_WARN_MSG("Couldn't open response file."); return false; } const jint MAX_STACK_COUNT = 100; jvmtiStackInfo *stackList = NULL; jint threadCount = 0; /* If failure get all stack traces. */ if(unlikely(isError(jvmti, jvmti->GetAllStackTraces(MAX_STACK_COUNT, &stackList, &threadCount)))) { PRINT_WARN_MSG("Couldn't get thread stack trace."); return false; } /* Output stack trace. */ for (jint i = 0; i < threadCount; i++) { jvmtiStackInfo stackInfo = stackList[i]; /* Output thread information. */ dumpThreadInformation(jvmti, env, ofs, stackInfo); } /* Cleanup. */ jvmti->Deallocate((unsigned char *)stackList); return true; } /*! * \brief Getting java process information. * \param systime [out] System used cpu time in java process. * \param usrtime [out] User and java used cpu time in java process. * \param vmsize [out] Virtual memory size of java process. * \param rssize [out] Memory size of java process on main memory. * \return Prosess is succeed or failure. */ bool TLogManager::getProcInfo(TLargeUInt *systime, TLargeUInt *usrtime, TLargeUInt *vmsize, TLargeUInt *rssize) { /* Get page size. */ long pageSize = 0; /* If failure get page size from sysconf. */ if (unlikely(systemPageSize <= 0)) { /* Assume page size is 4 KiByte. */ systemPageSize = 1 << 12; PRINT_WARN_MSG("Not found system page size." " Assume that page size is equal 4KiByte."); } /* Use sysconf return value. */ pageSize = systemPageSize; /* Initialize. */ *usrtime = -1; *systime = -1; *vmsize = -1; *rssize = -1; /* Make path of process status file. */ char path[256] = {0}; snprintf(path, 255, "/proc/%d/stat", getpid()); /* Open process status file. */ FILE *proc = fopen(path, "r"); /* If failure open process status file. */ if (unlikely(proc == NULL)) { PRINT_WARN_MSG("Couldn't open process status file."); return false; } char *line = NULL; size_t len = 0; /* Get line from process status file. */ if (likely(getline(&line, &len, proc) > 0)) { /* Parse line. */ if (likely(sscanf(line, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u" " %*u %*u %*u %llu %llu %*d %*d %*d %*d %*d %*d %*u %llu %llu", usrtime, systime, vmsize, rssize) == 4)) { /* Convert real page count to real page size. */ *rssize = *rssize * pageSize; } else { /* Kernel may be old or customized. */ PRINT_WARN_MSG("Process data has shortage."); } } else { PRINT_WARN_MSG("Couldn't read process status."); } /* Cleanup. */ if (likely(line != NULL)) { free(line); } fclose(proc); return true; } /*! * \brief Getting machine cpu times. * \param times [out] Machine cpu times information. * \return Prosess is succeed or failure. */ bool TLogManager::getSysTimes(TMachineTimes *times) { /* Initialize. */ memset(times, 0, sizeof(TMachineTimes)); /* Open machine status file. */ FILE *proc = fopen("/proc/stat", "r"); /* If failure open machine status file. */ if (unlikely(proc == NULL)) { PRINT_WARN_MSG("Couldn't open machine status file."); return false; } char *line = NULL; size_t len = 0; bool flagFound = false; /* Loop for find "cpu" line. */ while (likely(getline(&line, &len, proc) > 0)) { /* Check line. */ if (unlikely(strncmp(line, "cpu ", 4) != 0)) { /* This line is non-target. */ continue; } /* Parse cpu time information. */ if (unlikely(sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu", ×->usrTime, ×->lowUsrTime, ×->sysTime, ×->idleTime, ×->iowaitTime, ×->sortIrqTime, ×->irqTime, ×->stealTime, ×->guestTime) != 9)) { /* Maybe the kernel is old version. */ PRINT_WARN_MSG("CPU status data has shortage."); } flagFound = true; break; } /* If not found cpu information. */ if (unlikely(!flagFound)) { /* Maybe the kernel is modified. */ PRINT_WARN_MSG("Not found cpu status data."); } /* Cleanup. */ if (likely(line != NULL)) { free(line); } fclose(proc); return flagFound; } /*! * \brief Send log archive trap. * \param cause [in] Invoke function cause.<br> * E.g. Signal, ResourceExhausted, Interval. * \param nowTime [in] Log collect time. * \param path [in] Archive file or directory path. * \param isDirectory [in] Param "path" is directory. * \return Value is true, if process is succeed. */ bool TLogManager::sendLogArchiveTrap(TInvokeCause cause, TMSecTime nowTime, char *path, bool isDirectory) { /* Return value. */ bool result = true; /* Get full path. */ char realSendPath[PATH_MAX] = {0}; if (unlikely(realpath(path, realSendPath) == NULL)) { PRINT_WARN_MSG("Failure get real path of archive file."); return false; } /* If path is directory, then append "/" to path. */ if (isDirectory) { size_t pathSize = strlen(realSendPath); pathSize = (pathSize >= PATH_MAX) ? PATH_MAX - 1 : pathSize; realSendPath[pathSize] = '/'; } /* Output archive file path. */ PRINT_INFO_MSG_HEADER << "Completed collect log." << " file:" << realSendPath << NEWLINE; if (arg.snmpSend) { /* Trap OID. */ char trapOID[50] = OID_LOGARCHIVE; oid OID_ALERT_DATE[] = {SNMP_OID_HEAPALERT, 1}; oid OID_LOG_PATH[] = {SNMP_OID_LOGARCHIVE, 1}; oid OID_TROUBLE_DATE[] = {SNMP_OID_LOGARCHIVE, 2}; /* Temporary buffer. */ char buff[256] = {0}; /* Send resource trap. */ TTrapSender sender(SNMP_VERSION_2c, arg.snmpTarget, arg.snmpComName, 162); /* Setting sysUpTime */ sender.setSysUpTime(); /* Setting trapOID. */ sender.setTrapOID(trapOID); /* Set alert date. */ snprintf(buff, 255, "%llu", (TMSecTime)getNowTimeSec()); sender.addValue(OID_ALERT_DATE, OID_LENGTH(OID_ALERT_DATE), buff, SNMP_VAR_TYPE_COUNTER64); /* Set log archive path. */ sender.addValue(OID_LOG_PATH, OID_LENGTH(OID_LOG_PATH), realSendPath, SNMP_VAR_TYPE_STRING); /* Set date which JVM resource exhausted. */ switch (cause) { case ResourceExhausted: case ThreadExhausted: case OccurredDeadlock: /* Collect archive by resource or deadlock. */ snprintf(buff, 255, "%llu", nowTime); break; default: /* Collect archive by signal. */ snprintf(buff, 255, "%llu", (TMSecTime)0LL); } sender.addValue(OID_TROUBLE_DATE, OID_LENGTH(OID_TROUBLE_DATE), buff, SNMP_VAR_TYPE_COUNTER64); /* Send trap. */ result = (sender.sendTrap() == SNMP_PROC_SUCCESS); if (unlikely(!result)) { /* Clean up. */ sender.clearValues(); } } return result; } /*! * \brief Collect files about JVM enviroment. * \param basePath [in] Temporary working directory. */ void TLogManager::copyInfoFiles(char const* basePath){ /* Copy distribution file list. */ const char distFileList[][255] = { /* Distribution release. */ "/etc/redhat-release", /* For other distribution. */ "/etc/sun-release", "/etc/mandrake-release", "/etc/SuSE-release", "/etc/turbolinux-release", "/etc/gentoo-release", "/etc/debian_version", "/etc/ltib-release", "/etc/angstrom-version", "/etc/fedora-release", "/etc/vine-release", "/etc/issue", /* End flag. */ {0} }; bool flagCopyedDistFile = false; /* Copy distribution file. */ for (int i = 0; strlen(distFileList[i]) > 0; i++) { if (likely(copyFile(distFileList[i], basePath))) { flagCopyedDistFile = true; break; } } /* If failure copy distribution file. */ if (unlikely(!flagCopyedDistFile)) { PRINT_WARN_MSG("Failure copy file. path:\"distribution file\""); } /* Copy file list. */ const char copyFileList[][255] = { /* Process information. */ "/proc/self/smaps", "/proc/self/limits", "/proc/self/cmdline", "/proc/self/status", /* Netstat infomation. */ "/proc/net/tcp", "/proc/net/tcp6", "/proc/net/udp", "/proc/net/udp6", /* End flag. */ {0} }; /* Copy files in list. */ for (int i = 0; strlen(copyFileList[i]) > 0; i++) { /* Copy file. */ if (unlikely(!copyFile(copyFileList[i], basePath))) { PRINT_WARN_MSG_HEADER << "Failure copy file." << " path:\"" << copyFileList[i] << "\"" << NEWLINE; } } /* Collect Syslog or Systemd-Journald */ /* * Try to copy syslog at first, because journal daemon will forward all received * log messages to a traditional syslog by default. */ bool hasSyslog = copyFile("/var/log/messages", basePath); if (unlikely(!hasSyslog)) { PRINT_WARN_MSG("Failure copy file. path:\"/var/log/messages\""); } /* Collect systemd-journald instead of syslog when failed to copy syslog, */ if (!hasSyslog) { /* * Select vfork() to run journalctl in a separate process. Unlikely system(), * this function does not block SIGINT, et al. Unlikely fork(), this function * does not copy but shares all memory with its parent, including the stack. */ pid_t child = vfork(); if ( child == 0 ) { /* Child process */ /* logfile name shows what command is used to output it.*/ char logfile[PATH_MAX]; sprintf(logfile, "%s%s", basePath, "/journalctl_-q_--all_--this-boot_--no-pager_-o_verbose.log"); /* Redirect child process' stdout/stderr to logfile */ int fd = open(logfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (dup2(fd, 1) < 0) { close(fd); _exit(EXIT_FAILURE); } if (dup2(fd, 2) < 0) { close(fd); _exit(EXIT_FAILURE); } close(fd); /* Use execve() for journalctl to prevent command injection */ const char * argv[] = {"journalctl", "-q", "--all", "--this-boot", "--no-pager", "-o", "verbose", NULL}; extern char **environ; execve("/bin/journalctl", (char* const*)argv, environ); /* if execve returns, it has failed */ _exit(EXIT_FAILURE); } else if(child < 0) { /* vfork failed */ PRINT_WARN_MSG("Failure collect systemd-journald log by vfork()."); } else { /* Parent process */ int status; /* Wait for the child process to exit. If the child has already exited, * this returns immediately. */ if (waitpid(child, &status, 0) < 0) { PRINT_WARN_MSG("Failure collect systemd-journald log by process error."); } if (WIFEXITED(status)) { /* The child exited normally, get the status as result. */ if (WEXITSTATUS(status) != 0) { PRINT_WARN_MSG("Failure collect systemd-journald log."); } } else { /* The child exited with a signal or unknown exit code. */ PRINT_WARN_MSG("Failure collect systemd-journald log by signal or unknown exit code."); } } } /* Copy stdout. */ if (unlikely(!copyFile("/proc/self/fd/1", basePath, "fd1"))) { PRINT_WARN_MSG("Failure copy standard output."); } /* Copy stderr. */ if (unlikely(!copyFile("/proc/self/fd/2", basePath, "fd2"))) { PRINT_WARN_MSG("Failure copy standard error."); } } /*! * \brief Copy option target file. * \param option [in] Path of file designate by JVM options. * \param basePath [in] Path of temporary directory. * \return Process is succeed or skiped, if value is true.<br> * Process is failure, if value is false. */ bool TLogManager::copyOptionFile(char const* option, char const* basePath) { char optPath[PATH_MAX + 1] = {0}; char *pathPos = NULL; char *endPos = NULL; /* Get JVM arguments string. */ if (unlikely((pathPos = jvmInfo->getVmArgs()) == NULL)) { /* JVM argument isn't exists in JVM performance data. */ PRINT_WARN_MSG("Couldn't get JVM arguments."); return false; } /* Find option string in JVM arguments. */ if (unlikely((pathPos = strstr(pathPos, option)) == NULL)) { /* Not found option. */ return true; } /* Move head of path. */ pathPos += strlen(option); /* Get option separatar charactor. */ endPos = strchr(pathPos, ' '); /* Calculate and adjust copy string length. */ int optStrLen = (endPos == NULL) ? strlen(pathPos) : endPos - pathPos; optStrLen = (optStrLen > PATH_MAX) ? PATH_MAX : optStrLen; /* Copy string between option string and separator or '\0'. */ strncpy(optPath, pathPos, optStrLen); /* Copy file. */ return copyFile(optPath, basePath); } /*! * \brief Create archive file path. * \param nowTime [in] Log collect time. * \return Return archive file name, if process is succeed.<br> * Don't forget deallocate memory.<br> * Process is failure, if value is null. */ char *TLogManager::createArchiveName(TMSecTime nowTime) { time_t nowTimeSec = 0; struct tm time_struct = {0}; char time_str[20] = {0}; char arcName[PATH_MAX + 1] = {0}; char extPart[PATH_MAX + 1] = {0}; char namePart[PATH_MAX + 1] = {0}; /* Search extension. */ char *extPos = strrchr(arg.archiveFile, '.'); if (likely(extPos != NULL)) { /* Path and extension store each other. */ strncpy(extPart, extPos, PATH_MAX); strncpy(namePart, arg.archiveFile, (extPos - arg.archiveFile)); } else { /* Not found extension in path. */ strncpy(namePart, arg.archiveFile, PATH_MAX); } /* Get now datetime and convert to string. */ nowTimeSec = (time_t)(nowTime / 1000); localtime_r((const time_t*)&nowTimeSec, &time_struct); strftime(time_str, 20, "%y%m%d%H%M%S", &time_struct); /* Create file name. */ snprintf(arcName, PATH_MAX, "%s%s%s", namePart, time_str, extPart); /* Create unique file name. */ return createUniquePath(arcName, false); }