Mercurial > hg > release > heapstats-1.0
view agent/src/classContainer.cpp @ 54:296461a953ac
Bug 2378: JVM may crashe when class unload is occurred.
reviewed-by: ykubota
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Sun, 24 May 2015 18:31:15 +0900 |
parents | 6fec95209844 |
children |
line wrap: on
line source
/*! * \file classContainer.cpp * \brief This file is used to add up using size every class. * Copyright (C) 2011-2015 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 <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <jni.h> #include <time.h> #include <stddef.h> #include <pthread.h> #include "classContainer.hpp" #include "util.hpp" #include "oopUtil.hpp" #include "trapSender.hpp" #include "sorter.hpp" #include "libmain.hpp" /*! * \brief SNMP variable Identifier of raise heap-alert date. */ static oid OID_ALERT_DATE[] = {SNMP_OID_HEAPALERT, 1}; /*! * \brief SNMP variable Identifier of class-name is cause of heap-alert. */ static oid OID_ALERT_CLS_NAME[] = {SNMP_OID_HEAPALERT, 2}; /*! * \brief SNMP variable Identifier of kind of class-size. */ static oid OID_ALERT_SIZE_KIND[] = {SNMP_OID_HEAPALERT, 3}; /*! * \brief SNMP variable Identifier of class-size. */ static oid OID_ALERT_CLS_SIZE[] = {SNMP_OID_HEAPALERT, 4}; /*! * \brief SNMP variable Identifier of instance count. */ static oid OID_ALERT_CLS_COUNT[] = {SNMP_OID_HEAPALERT, 5}; /*! * \brief SNMP variable Identifier of raise Java heap alert date. */ static oid OID_JAVAHEAPALERT_DATE[] = {SNMP_OID_JAVAHEAPALERT, 1}; /*! * \brief SNMP variable Identifier of Java heap usage. */ static oid OID_JAVAHEAPALERT_USAGE[] = {SNMP_OID_JAVAHEAPALERT, 2}; /*! * \brief SNMP variable Identifier of Java heap max capacity. */ static oid OID_JAVAHEAPALERT_MAX_CAPACITY[] = {SNMP_OID_JAVAHEAPALERT, 3}; /*! * \brief Format string for jlong. */ const char JLONG_FORMAT_STR[5] = "%lld"; /*! * \brief String of USAGE order. */ static char ORDER_USAGE[6] = "USAGE"; /*! * \briefString of DELTA order. */ static char ORDER_DELTA[6] = "DELTA"; /*! * \brief TClassContainer constructor. */ TClassContainer::TClassContainer(TClassContainer *base, bool needToClr) : classContainerInTLS(), pendingQueue(){ try { if(base == NULL) classMap = new TClassMap(); else{ SpinLockWait(&base->lockval); { classMap = new TClassMap(*base->classMap); } SpinLockRelease(&base->lockval); } /* Create trap sender. */ pSender = new TTrapSender(SNMP_VERSION_2c, arg.snmpTarget, arg.snmpComName, 162); } catch(...){ throw "TClassContainer initialize failed!"; } pthread_key_create(&tlsKey, NULL); lockval = 0; tlsLock = 0; needToClear = needToClr; pendingQueueLock = 0; } /*! * \brief TClassContainer destructor. */ TClassContainer::~TClassContainer(void) { /* Cleanup class information. */ if(needToClear){ this->allClear(); } /* Cleanup ClassContainer in TLS */ for(TLocalClassContainer::iterator itr = classContainerInTLS.begin(); itr != classContainerInTLS.end(); itr++){ delete *itr; } /* Cleanup instances. */ delete classMap; delete pSender; pthread_key_delete(tlsKey); } /*! * \brief Append new-class to container. * \param klassOop [in] New class oop. * \return New-class data. */ TObjectData *TClassContainer::pushNewClass(void *klassOop, TObjectData *objData){ TObjectData *result = NULL; SpinLockWait(&lockval); { TClassMap::iterator itr = classMap->find(klassOop); if(itr != classMap->end()){ result = itr->second; /* * Existed class has been unloaded. * However ClassUnload event was not fired. * * In this case, we MUST USE existed TObjectData. * Because, TObjectData might be existed in TSnapShotContainer. * We can't remove TObjectData in TSnapShotContainer from here! */ if(unlikely(strcmp(objData->className, result->className) != 0)){ result->classNameLen = objData->classNameLen; result->className = strdup(objData->className); result->oldTotalSize = 0; } } else{ result = objData; (*classMap)[klassOop] = objData; } } SpinLockRelease(&lockval); if(unlikely(result != objData)){ /* entry has already existed */ return result; } /* Broadcast new entry to TLS Container */ SpinLockWait(&tlsLock); { for(TLocalClassContainer::iterator itr = classContainerInTLS.begin(); itr != classContainerInTLS.end(); itr++){ (*itr)->pushNewClass(klassOop, objData); } } SpinLockRelease(&tlsLock); return objData; } TObjectData *TClassContainer::pushNewClass(void *klassOop) { TObjectData *cur; /* Class info setting. */ cur = (TObjectData *)calloc(1, sizeof(TObjectData)); /* If failure allocate. */ if (unlikely(cur == NULL)) { /* Adding empty to list is deny. */ PRINT_WARN_MSG("Couldn't allocate counter memory!"); return NULL; } cur->tag = (jlong)cur; cur->className = getClassName(getKlassFromKlassOop(klassOop)); /* If failure getting class name. */ if (unlikely(cur->className == NULL)) { /* Adding empty to list is deny. */ PRINT_WARN_MSG("Couldn't allocate class name!"); free(cur); return NULL; } cur->classNameLen = strlen(cur->className); /* Chain setting. */ cur->klassOop = klassOop; /* push to classMap */ TObjectData *result = this->pushNewClass(klassOop, cur); if(result != cur){ free(cur); } return result; } /*! * \brief Remove class from container. * \param target [in] Remove class data. */ void TClassContainer::popClass(TObjectData *target) { SpinLockWait(&lockval); { classMap->erase(target->klassOop); } SpinLockRelease(&lockval); /* Broadcast to TLS Container */ for(TLocalClassContainer::iterator itr = classContainerInTLS.begin(); itr != classContainerInTLS.end(); itr++){ SpinLockWait(&(*itr)->lockval); { (*itr)->classMap->erase(target->klassOop); } SpinLockRelease(&(*itr)->lockval); } /* Free allocated memory. */ free(target->className); free(target); } /*! * \brief Remove all-class from container. */ void TClassContainer::allClear(void) { /* Broadcast to TLS Container */ SpinLockWait(&tlsLock); { for(TLocalClassContainer::iterator itr = classContainerInTLS.begin(); itr != classContainerInTLS.end(); itr++){ SpinLockWait(&(*itr)->lockval); { (*itr)->classMap->clear(); } SpinLockRelease(&(*itr)->lockval); } } SpinLockRelease(&tlsLock); SpinLockWait(&lockval); { /* Free allocated memory. */ for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end(); ++cur) { TObjectData *pos = (*cur).second; free(pos->className); free(pos); } /* Clear all class. */ classMap->clear(); } SpinLockRelease(&lockval); SpinLockWait(&pendingQueueLock); { pendingQueue.clear(); } SpinLockRelease(&pendingQueueLock); } /*! * \brief Remove all object data in pending queue. */ void TClassContainer::removePendingObjectData(void){ SpinLockWait(&pendingQueueLock); { while(!pendingQueue.empty()){ popClass(pendingQueue.front()); pendingQueue.pop_front(); } } SpinLockRelease(&pendingQueueLock); } /*! * \brief Comparator for sort by usage order. * \param *arg1 [in] Compare target A. * \param *arg2 [in] Compare target B. * \return Compare result. */ int HeapUsageCmp(const void *arg1, const void *arg2) { jlong cmp = ((THeapDelta*)arg2)->usage - ((THeapDelta*)arg1)->usage; if (cmp > 0) { /* arg2 is bigger than arg1. */ return -1; } else if (cmp < 0) { /* arg1 is bigger than arg2. */ return 1; } else { /* arg2 is equal arg1. */ return 0; } } /*! * \brief Comparator for sort by delta order. * \param *arg1 [in] Compare target A. * \param *arg2 [in] Compare target B. * \return Compare result. */ int HeapDeltaCmp(const void *arg1, const void *arg2) { jlong cmp = ((THeapDelta*)arg2)->delta - ((THeapDelta*)arg1)->delta; if (cmp > 0) { /* arg2 is bigger than arg1. */ return -1; } else if (cmp < 0) { /* arg1 is bigger than arg2. */ return 1; } else { /* arg2 is equal arg1. */ return 0; } } /*! * \brief Send memory usage information by SNMP trap. * \param pSender [in] SNMP trap sender. * \param type [in] This alert type. * \param occurred_time [in] Datetime of this alert is occurred. * \param usage [in] Java heap usage. * \param maxCapacity [in] Max capacity of Java heap. * \return Process result. */ inline bool sendMemoryUsageAlertTrap(TTrapSender *pSender, TMemoryUsageAlertType type, jlong occurred_time, jlong usage, jlong maxCapacity){ /* Setting trap information. */ char paramStr[256]; const char *trapOID; oid *alert_date; oid *alert_usage; oid *alert_capacity; int oid_len = 0; switch(type){ case ALERT_JAVA_HEAP: trapOID = OID_JAVAHEAPALERT; alert_date = OID_JAVAHEAPALERT_DATE; alert_usage = OID_JAVAHEAPALERT_USAGE; alert_capacity = OID_JAVAHEAPALERT_MAX_CAPACITY; oid_len = OID_LENGTH(OID_JAVAHEAPALERT_DATE); break; default: PRINT_CRIT_MSG("Unknown alert type!"); return false; } bool result = true; try{ /* Setting sysUpTime */ pSender->setSysUpTime(); /* Setting trapOID. */ pSender->setTrapOID(trapOID); /* Set time of occurring this alert. */ sprintf(paramStr, JLONG_FORMAT_STR, occurred_time); pSender->addValue(alert_date, oid_len, paramStr, SNMP_VAR_TYPE_COUNTER64); /* Set java heap usage. */ sprintf(paramStr, JLONG_FORMAT_STR, usage); pSender->addValue(alert_usage, oid_len, paramStr, SNMP_VAR_TYPE_COUNTER64); /* Set max capacity of java heap. */ sprintf(paramStr, JLONG_FORMAT_STR, maxCapacity); pSender->addValue(alert_capacity, oid_len, paramStr, SNMP_VAR_TYPE_COUNTER64); /* Send trap. */ if(unlikely(pSender->sendTrap() != SNMP_PROC_SUCCESS)){ /* Clean up. */ pSender->clearValues(); throw 1; } } catch(...){ result = false; } return result; } /*! * \brief Output all-class information to file. * \param snapshot [in] Snapshot instance. * \return Sorted-class size information. */ TSorter<THeapDelta> *TClassContainer::afterTakeSnapShot( TSnapShotContainer *snapshot) { /* Sanity check. */ if (unlikely(snapshot == NULL)) { return NULL; } /* Copy header. */ TSnapShotFileHeader hdr; memcpy(&hdr, (const void *)snapshot->getHeader(), sizeof(TSnapShotFileHeader)); /* If java heap usage alert is enable. */ if(arg.HeapAlertThreshold > 0){ jlong usage = hdr.newAreaSize + hdr.oldAreaSize; if(usage > arg.HeapAlertThreshold){ /* Raise alert. */ PRINT_WARN_MSG_HEADER << "ALERT: Java heap usage exceeded the threshold (" << usage / 1024 / 1024 << " MB)" << NEWLINE; /* If need send trap. */ if(arg.snmpSend){ if(unlikely(!sendMemoryUsageAlertTrap(pSender, ALERT_JAVA_HEAP, hdr.snapShotTime, usage, jvmInfo->getMaxMemory()))){ PRINT_WARN_MSG("SNMP trap send failed!"); } } } } /* Allocate return array. */ jlong rankCnt = this->classMap->size(); rankCnt = (rankCnt < arg.RankLevel) ? rankCnt : arg.RankLevel; /* Make controller to sort. */ register TRankOrder order = arg.order; TSorter<THeapDelta> *sortArray; try { sortArray = new TSorter<THeapDelta>(rankCnt, (TComparator)((order == DELTA) ? &HeapDeltaCmp : &HeapUsageCmp)); } catch (...) { PRINT_WARN_MSG("Couldn't allocate working memory!"); return NULL; } /* Open file and seek EOF. */ int fd = open(arg.FileName, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* If failure open file. */ if (unlikely(fd < 0)) { PRINT_WARN_MSG("Counldn't open snapshot file."); delete sortArray; return NULL; } /* Move position to EOF. */ off_t ofs = lseek(fd, 0, SEEK_END); /* If failure seek. */ if (unlikely(ofs < 0)) { PRINT_WARN_MSG("Counldn't open snapshot file."); close(fd); delete sortArray; return NULL; } /* Frist, Output each classes information. Secondly output header. */ lseek(fd, sizeof(TSnapShotFileHeader) - sizeof(char[80]) + hdr.gcCauseLen, SEEK_CUR); /* Output class information. */ THeapDelta result; jlong numEntries = 0L; jlong clsCount = 0L; register jlong AlertThreshold = arg.AlertThreshold; TClassMap *workClassMap = NULL; SpinLockWait(&lockval); { workClassMap = new TClassMap(*classMap); } SpinLockRelease(&lockval); /* Loop each class. */ for (TClassMap::iterator it = workClassMap->begin(); it != workClassMap->end(); ++it, clsCount++) { TObjectData *objData = (*it).second; TObjectCounter *cur = snapshot->findClass(objData); if (unlikely(cur == NULL)) { cur = snapshot->pushNewClass(objData); if (unlikely(cur == NULL)) { PRINT_WARN_MSG("Couldn't allocate working memory!"); close(fd); delete sortArray; return NULL; } } /* Calculate uasge and delta size. */ result.usage = cur->total_size; result.delta = cur->total_size - objData->oldTotalSize; result.tag = objData->tag; objData->oldTotalSize = result.usage; /* If do output class. */ if (!arg.reduceSnapShot || (result.usage > 0)) { /* Output class-information. */ write(fd, objData, sizeof(jlong) << 1); /* TObjectData.tag & TObjectData.classNameLen. */ write(fd, objData->className, objData->classNameLen); write(fd, cur, sizeof(TObjectCounter)); numEntries++; } /* Ranking sort. */ sortArray->push(result); /* If alert is enable. */ if (AlertThreshold > 0) { /* Variable for send trap. */ int sendFlag = 0; try { /* If size is bigger more limit size. */ if ((order == DELTA) && (AlertThreshold <= result.delta)) { /* Raise alert. */ PRINT_WARN_MSG_HEADER << "ALERT(DELTA): \"" << objData->className << "\" exceeded the threshold" << " (" << result.delta << " bytes)" << NEWLINE; /* Set need send trap flag. */ sendFlag = 1; } else if ((order == USAGE) && (AlertThreshold <= result.usage)) { /* Raise alert. */ PRINT_WARN_MSG_HEADER << "ALERT(USAGE): \"" << objData->className << "\" exceeded the threshold" << " (" << result.usage << " bytes)" << NEWLINE; /* Set need send trap flag. */ sendFlag = 1; } /* If need send trap. */ if (arg.snmpSend && sendFlag != 0) { /* Setting trap information. */ char paramStr[256]; /* Trap OID. */ char trapOID[50] = OID_HEAPALERT; /* Setting sysUpTime */ pSender->setSysUpTime(); /* Setting trapOID. */ pSender->setTrapOID(trapOID); if (order == USAGE) { /* Set size kind. */ pSender->addValue(OID_ALERT_SIZE_KIND, OID_LENGTH(OID_ALERT_SIZE_KIND), ORDER_USAGE, SNMP_VAR_TYPE_STRING); sprintf(paramStr, JLONG_FORMAT_STR, result.usage); } else { /* Set size kind. */ pSender->addValue(OID_ALERT_SIZE_KIND, OID_LENGTH(OID_ALERT_SIZE_KIND), ORDER_DELTA, SNMP_VAR_TYPE_STRING); sprintf(paramStr, JLONG_FORMAT_STR, result.delta); } /* Set alert size. */ pSender->addValue(OID_ALERT_CLS_SIZE, OID_LENGTH(OID_ALERT_CLS_SIZE), paramStr, SNMP_VAR_TYPE_COUNTER64); /* Set now time. */ sprintf(paramStr, JLONG_FORMAT_STR, getNowTimeSec()); pSender->addValue(OID_ALERT_DATE, OID_LENGTH(OID_ALERT_DATE), paramStr, SNMP_VAR_TYPE_COUNTER64); /* Set class-name. */ pSender->addValue(OID_ALERT_CLS_NAME, OID_LENGTH(OID_ALERT_CLS_NAME), objData->className, SNMP_VAR_TYPE_STRING); /* Set instance count. */ sprintf(paramStr, JLONG_FORMAT_STR, cur->count); pSender->addValue(OID_ALERT_CLS_COUNT, OID_LENGTH(OID_ALERT_CLS_COUNT), paramStr, SNMP_VAR_TYPE_COUNTER64); /* Send trap. */ if (unlikely(pSender->sendTrap() != SNMP_PROC_SUCCESS)) { /* Ouput message and clean up. */ PRINT_WARN_MSG("Send SNMP trap failed!"); pSender->clearValues(); } } } catch(...) { PRINT_WARN_MSG("Send SNMP trap failed!"); } } } delete workClassMap; /* Move header position. */ lseek(fd, ofs, SEEK_SET); /* Set output entry count. */ hdr.size = numEntries; /* Write header param before GC-cause. */ write(fd, &hdr, offsetof(TSnapShotFileHeader, gcCause)); /* Write GC-cause. */ write(fd, hdr.gcCause, hdr.gcCauseLen); /* Write header param after gccause. */ write(fd, &hdr.FGCCount, sizeof(TSnapShotFileHeader) - offsetof(TSnapShotFileHeader, FGCCount)); /* Clean up. */ close(fd); return sortArray; }