Mercurial > hg > release > heapstats-1.1
view agent/src/snapShotContainer.cpp @ 60:eafa619db958
Bug 3284: HeapStats Agent might crash when object children are scanned
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/74
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Tue, 17 Jan 2017 12:08:51 +0900 |
parents | 2b0645fd51d3 |
children | 2836f23c1fbb |
line wrap: on
line source
/*! * \file snapshotContainer.cpp * \brief This file is used to add up using size every class. * 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 <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <jni.h> #include <time.h> #include <pthread.h> #include "snapShotContainer.hpp" #include "util.hpp" #include "trapSender.hpp" #include "sorter.hpp" /*! * \brief Pthread mutex for instance control.<br> * <br> * This mutex used in below process.<br> * - TSnapShotContainer::getInstance @ snapShotContainer.cpp<br> * To get older snapShotContainer instance from stockQueue.<br> * - TSnapShotContainer::releaseInstance @ snapShotContainer.cpp<br> * To add used snapShotContainer instance to stockQueue.<br> */ pthread_mutex_t TSnapShotContainer::instanceLocker = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /*! * \brief Snapshot container instance stock queue. */ TSnapShotQueue *TSnapShotContainer::stockQueue = NULL; /*! * \brief Initialize snapshot caontainer class. * \return Is process succeed. * \warning Please call only once from main thread. */ bool TSnapShotContainer::globalInitialize(void) { try { /* Create snapshot container storage. */ stockQueue = new TSnapShotQueue(); } catch(...) { PRINT_WARN_MSG("Failure initialize snapshot container."); return false; } return true; } /*! * \brief Finalize snapshot caontainer class. * \warning Please call only once from main thread. */ void TSnapShotContainer::globalFinalize(void) { if (likely(stockQueue != NULL)) { /* Clear snapshot in queue. */ while (!stockQueue->empty()) { TSnapShotContainer *item = stockQueue->front(); /* Deallocate snapshot instance. */ delete item; stockQueue->pop(); } /* Deallocate stock queue. */ delete stockQueue; stockQueue = NULL; } } /*! * \brief Get snapshot container instance. * \return Snapshot container instance. * \warning Don't deallocate instance getting by this function.<br> * Please call "releaseInstance" method. */ TSnapShotContainer *TSnapShotContainer::getInstance(void) { TSnapShotContainer *result = NULL; ENTER_PTHREAD_SECTION(&instanceLocker) { if (!stockQueue->empty()) { /* Reuse snapshot container instance. */ result = stockQueue->front(); stockQueue->pop(); } } EXIT_PTHREAD_SECTION(&instanceLocker) /* If need create new instance. */ if (result == NULL) { /* Create new snapshot container instance. */ try { result = new TSnapShotContainer(); } catch(...) { result = NULL; } } return result; } /*! * \brief Release snapshot container instance.. * \param instance [in] Snapshot container instance. * \warning Don't access instance after called this function. */ void TSnapShotContainer::releaseInstance(TSnapShotContainer *instance) { /* Sanity check. */ if (unlikely(instance == NULL)) { return; } bool existStockSpace = false; ENTER_PTHREAD_SECTION(&instanceLocker) { existStockSpace = (stockQueue->size() < MAX_STOCK_COUNT); } EXIT_PTHREAD_SECTION(&instanceLocker) if (likely(existStockSpace)) { /* * We reset this flag. * Because we need deallocating if failed to store to stock. * E.g. Failed to get mutex at "pthread_mutex_lock/unlock" * or no more memory at "std::queue<T>::push()". */ existStockSpace = false; /* Clear data. */ instance->clear(false); ENTER_PTHREAD_SECTION(&instanceLocker) { try { /* Store instance. */ stockQueue->push(instance); existStockSpace = true; } catch(...) { /* Maybe faield to allocate memory. So we release instance. */ } } EXIT_PTHREAD_SECTION(&instanceLocker) } if (unlikely(!existStockSpace)) { /* Deallocate instance. */ delete instance; } } /*! * \brief TSnapshotContainer constructor. */ TSnapShotContainer::TSnapShotContainer(bool isParent) : counterMap(), containerMap() { /* Header setting. */ this->_header.magicNumber = MAGIC_NUM; this->_header.byteOrderMark = BOM; this->_header.snapShotTime = 0; this->_header.size = 0; memset((void*)&this->_header.gcCause[0], 0, 80); /* Initialize each field. */ lockval = 0; isParentContainer = isParent; /* Create thread storage key. */ if (unlikely(isParent && pthread_key_create(&snapShotContainerKey, NULL) != 0)) { throw "Failed to create pthread key"; } this->isCleared = true; } /*! * \brief TSnapshotContainer destructor. */ TSnapShotContainer::~TSnapShotContainer(void) { /* Cleanup elements on counter map. */ for (TSizeMap::iterator it = counterMap.begin(); it != counterMap.end(); ++it) { TClassCounter *clsCounter = (*it).second; if (unlikely(clsCounter == NULL)) { continue; } /* Deallocate field block cache. */ free(clsCounter->offsets); /* Deallocate children class list. */ TChildClassCounter *counter = clsCounter->child; while (counter != NULL) { TChildClassCounter *aCounter = counter; counter = counter->next; /* Deallocate TChildClassCounter. */ atomic_inc(&aCounter->objData->numRefsFromChildren, -1); free(aCounter->counter); free(aCounter); } /* Deallocate TClassCounter. */ free(clsCounter->counter); free(clsCounter); } /* Cleanup elements on snapshot container map. */ for (TLocalSnapShotContainer::iterator it = containerMap.begin(); it != containerMap.end(); ++it) { delete (*it).second; } /* Clean maps. */ counterMap.clear(); containerMap.clear(); if (isParentContainer) { /* Clean thread storage key. */ pthread_key_delete(snapShotContainerKey); } } /*! * \brief Append new-class to container. * \param objData [in] New-class key object. * \return New-class data. */ TClassCounter *TSnapShotContainer::pushNewClass(TObjectData *objData) { TClassCounter *cur = NULL; cur = (TClassCounter *)calloc(1, sizeof(TClassCounter)); /* If failure allocate counter data. */ if (unlikely(cur == NULL)) { /* Adding empty to list is deny. */ PRINT_WARN_MSG("Couldn't allocate counter memory!"); return NULL; } cur->offsetCount = -1; int ret = posix_memalign((void **)&cur->counter, 16, sizeof(TObjectCounter) /* sizeof(TObjectCounter) == 16. */); /* If failure allocate counter. */ if (unlikely(ret != 0)) { /* Adding empty to list is deny. */ PRINT_WARN_MSG("Couldn't allocate counter memory!"); free(cur); return NULL; } #ifdef AVX if(likely(usableAVX)){ asm volatile("vpxor %%xmm0, %%xmm0, %%xmm0;" "vmovdqa %%xmm0, (%0);" : : "r" (cur->counter) : "%xmm0"); } else #endif #ifndef __amd64__ if(unlikely(!usableSSE2)){ cur->counter->count = 0; cur->counter->total_size = 0; } else #endif { asm volatile("pxor %%xmm0, %%xmm0;" "movdqa %%xmm0, (%0);" : : "r" (cur->counter) : "%xmm0"); } try { /* Set counter map. */ counterMap[objData] = cur; } catch(...) { /* * Maybe failed to allocate memory at "std::map::operator[]". */ free(cur->counter); free(cur); cur = NULL; } return cur; } /*! * \brief Append new-child-class to container. * \param clsCounter [in] Parent class counter object. * \param objData [in] New-child-class key object. * \return New-class data. */ TChildClassCounter *TSnapShotContainer::pushNewChildClass( TClassCounter *clsCounter, TObjectData *objData) { TChildClassCounter *newCounter = (TChildClassCounter*)calloc( 1, sizeof(TChildClassCounter)); /* If failure allocate child class counter data. */ if (unlikely(newCounter == NULL)) { return NULL; } int ret = posix_memalign((void **)&newCounter->counter, 16, sizeof(TObjectCounter) /* sizeof(TObjectCounter) == 16. */); /* If failure allocate child class counter. */ if (unlikely(ret != 0)) { free(newCounter); return NULL; } #ifndef __amd64__ if(unlikely(!usableSSE2)){ memset(newCounter->counter, 0, sizeof(TObjectCounter)); } else #endif { asm volatile( "pxor %%xmm0, %%xmm0;" "movdqa %%xmm0, (%0);" : : "r" (newCounter->counter) : "cc", "memory", "%xmm0"); } atomic_inc(&objData->numRefsFromChildren, 1); newCounter->objData = objData; /* Chain children list. */ TChildClassCounter *counter = clsCounter->child; if (unlikely(counter == NULL)) { clsCounter->child = newCounter; } else { /* Get last counter. */ while (counter->next != NULL) { counter = counter->next; } counter->next = newCounter; } return newCounter; } /*! * \brief Set JVM performance info to header. * \param info [in] JVM running performance information. */ void TSnapShotContainer::setJvmInfo(TJvmInfo *info) { /* Sanity check. */ if (unlikely(info == NULL)) { PRINT_WARN_MSG("Couldn't get GC Information!"); return; } /* If GC cause is need. */ if (this->_header.cause == GC) { /* Copy GC cause. */ strcpy((char*)this->_header.gcCause, info->getGCCause()); this->_header.gcCauseLen = strlen((char*)this->_header.gcCause); /* Setting GC work time. */ this->_header.gcWorktime = info->getGCWorktime(); } else { /* Clear GC cause. */ this->_header.gcCauseLen = 1; this->_header.gcCause[0] = '\0'; /* GC no work. */ this->_header.gcWorktime = 0; } /* Setting header info from TJvmInfo. * Total memory (JVM_TotalMemory) should be called from outside of GC. * Comment of VM_ENTRY_BASE macro says as following: * ENTRY routines may lock, GC and throw exceptions * So we set TSnapShotFileHeader.totalHeapSize in TakeSnapShot() . */ this->_header.FGCCount = info->getFGCCount(); this->_header.YGCCount = info->getYGCCount(); this->_header.newAreaSize = info->getNewAreaSize(); this->_header.oldAreaSize = info->getOldAreaSize(); this->_header.metaspaceUsage = info->getMetaspaceUsage(); this->_header.metaspaceCapacity = info->getMetaspaceCapacity(); } #ifdef __amd64__ #define PTR_SIZE "8" #define TMP_REG1 "rax" #define TMP_REG2 "rbx" #define OP_MOV "movq" #define OP_TEST "testq" #else #define PTR_SIZE "4" #define TMP_REG1 "eax" #define TMP_REG2 "ecx" #define OP_MOV "movl" #define OP_TEST "testl" #endif /*! * \brief Clear snapshot data. */ void TSnapShotContainer::clear(bool isForce) { if(!isForce && this->isCleared){ return; } /* Get snapshot container's spin lock. */ spinLockWait(&lockval); { /* Clean heap usage information. */ for (TSizeMap::iterator it = counterMap.begin(); it != counterMap.end(); ++it) { TClassCounter *clsCounter = (*it).second; if (unlikely(clsCounter == NULL)) { continue; } /* Deallocate field block cache. */ free(clsCounter->offsets); clsCounter->offsets = NULL; clsCounter->offsetCount = -1; /* Reset counters. */ #ifdef AVX if(usableAVX){ asm volatile("vpxor %%xmm0, %%xmm0, %%xmm0;" OP_MOV " " PTR_SIZE "(%0), %%" TMP_REG1 ";" /* clsCounter->child */ ".align 16;" "1:" OP_TEST " %%" TMP_REG1 ", %%" TMP_REG1 ";" "jz 2f;" OP_MOV " (%%" TMP_REG1 "), %%" TMP_REG2 ";" /* child->counter */ "vmovdqa %%xmm0, (%%" TMP_REG2 ");" OP_MOV " 2*" PTR_SIZE "(%%" TMP_REG1 "), %%" TMP_REG1 ";" /* child->next */ "jmp 1b;" "2:" OP_MOV " (%0), %%" TMP_REG2 ";" /* clsCounter->counter */ "vmovdqa %%xmm0, (%%" TMP_REG2 ");" : : "r" (clsCounter) : "%xmm0", TMP_REG1, TMP_REG2, "cc"); } else #endif #ifndef __amd64__ if(!unlikely(!usableSSE2)){ /* Reset counter of children class. */ TChildClassCounter *counter = clsCounter->child; while(counter != NULL){ counter->counter->count = 0; counter->counter->total_size = 0; //counter->callCount >>= 1; counter = counter->next; } /* Reset counter of all class. */ memset(clsCounter->counter, 0, sizeof(TObjectCounter)); } else #endif { asm volatile("pxor %%xmm0, %%xmm0;" OP_MOV " " PTR_SIZE "(%0), %%" TMP_REG1 ";" /* clsCounter->child */ ".align 16;" "1:" OP_TEST " %%" TMP_REG1 ", %%" TMP_REG1 ";" "jz 2f;" OP_MOV " (%%" TMP_REG1 "), %%" TMP_REG2 ";" /* child->counter */ "movdqa %%xmm0, (%%" TMP_REG2 ");" OP_MOV " 2*" PTR_SIZE "(%%" TMP_REG1 "), %%" TMP_REG1 ";" /* child->next */ "jmp 1b;" "2:" OP_MOV " (%0), %%" TMP_REG2 ";" /* clsCounter->counter */ "movdqa %%xmm0, (%%" TMP_REG2 ");" : : "r" (clsCounter) : "%xmm0", TMP_REG1, TMP_REG2, "cc"); } } /* Clean local snapshots. */ for (TLocalSnapShotContainer::iterator it = containerMap.begin(); it != containerMap.end(); ++it) { (*it).second->clear(true); } this->isCleared = true; } /* Release snapshot container's spin lock. */ spinLockRelease(&lockval); } /*! * \brief Output GC statistics information. */ void TSnapShotContainer::printGCInfo(void) { /* Check Loglevel. */ if (arg.LogLevel < INFO) { return; } PRINT_INFO_MSG_HEADER_NOIF << "GC Statistics Information " << NEWLINE; /* GC cause and GC worktime show only raised GC. */ if (this->_header.cause == GC) { PRINT_INFO_MSG_HEADER_NOIF << "GC Cause : '" << (char*)this->_header.gcCause << "' , " << "GC Worktime : " << this->_header.gcWorktime << "msec " << NEWLINE; } /* Output GC count. */ PRINT_INFO_MSG_HEADER_NOIF << "GC Count FullGC:" << this->_header.FGCCount << " / YoungGC:" << this->_header.YGCCount << NEWLINE; /* Output heap size status. */ PRINT_INFO_MSG_HEADER_NOIF << "Area using size New: " << this->_header.newAreaSize << " bytes" << " / Old: " << this->_header.oldAreaSize << " bytes" << " / Total: " << this->_header.totalHeapSize << " bytes" << NEWLINE; /* Output metaspace size status. */ const char *label = enableCR6964458 ? "Metaspace usage: " : "PermGen usage: "; PRINT_INFO_MSG_HEADER_NOIF << label << this->_header.metaspaceUsage << " bytes" << ", capacity: " << this->_header.metaspaceCapacity << " bytes" << NEWLINE; } /*! * \brief Merge children data. */ void TSnapShotContainer::mergeChildren(void) { /* Get snapshot container's spin lock. */ spinLockWait(&lockval); { /* Loop each local snapshot container. */ for (TLocalSnapShotContainer::iterator it = this->containerMap.begin(); it != this->containerMap.end(); it++) { /* Loop each class in snapshot container. */ TSizeMap *srcCounterMap = &(*it).second->counterMap; for (TSizeMap::iterator it2 = srcCounterMap->begin(); it2 != srcCounterMap->end(); it2++) { TClassCounter *srcClsCounter = (*it2).second; /* Search or register class. */ TClassCounter *clsCounter = this->findClass((*it2).first); if (unlikely(clsCounter == NULL)) { clsCounter = this->pushNewClass((*it2).first); /* If failed to search and register class. */ if (unlikely(clsCounter == NULL)) { continue; /* Skip merge this class. */ } } /* Marge class heap usage. */ this->addInc(clsCounter->counter, srcClsCounter->counter); /* Loop each children class. */ TChildClassCounter *counter = srcClsCounter->child; TChildClassCounter *prevCounter = NULL; while (counter != NULL){ TObjectData *objData = counter->objData; /* * If the class of objData is already unloaded, we should * free and shold decrement reference count. * TChildClassCounter reference count will be checked at * TClassContainer::commitClassChange(). */ if (objData->isRemoved) { atomic_inc(&objData->numRefsFromChildren, -1); TChildClassCounter *nextCounter = counter->next; if (prevCounter == NULL) { srcClsCounter->child = nextCounter; } else { prevCounter->next = nextCounter; } /* Deallocate TChildClassCounter. */ free(counter->counter); free(counter); /* Deallocate TChildClassCounter in parent container. */ TChildClassCounter *childClsData, *parentPrevData, *parentMorePrevData; this->findChildCounters(clsCounter, objData->klassOop, &childClsData, &parentPrevData, &parentMorePrevData); if (likely(childClsData != NULL)) { atomic_inc(&objData->numRefsFromChildren, -1); if (parentPrevData == NULL) { clsCounter->child = childClsData->next; } else { parentPrevData->next = childClsData->next; } free(childClsData->counter); free(childClsData); } counter = nextCounter; } else { /* Search child class. */ TChildClassCounter *childClsData = this->findChildClass(clsCounter, objData->klassOop); /* Register class as child class. */ if (unlikely(childClsData == NULL)) { childClsData = this->pushNewChildClass(clsCounter, objData); } if (likely(childClsData != NULL)) { /* Marge children class heap usage. */ this->addInc(childClsData->counter, counter->counter); } prevCounter = counter; counter = counter->next; } } } } } /* Release snapshot container's spin lock. */ spinLockRelease(&lockval); }