/*! * \brief TClassContainer constructor. * \param base [in] Parent class container instance. * \param needToClr [in] Flag of deallocate all data on destructor. */ TClassContainer::TClassContainer(TClassContainer *base, bool needToClr) : localContainers() { /* Initialize each field. */ lockval = 0; queueLock = 0; needToClear = needToClr; classMap = NULL; pSender = NULL; unloadedList = NULL; if (likely(base != NULL)) { /* Get parent container's spin lock. */ spinLockWait(&base->lockval); } try { if (unlikely(base == NULL)) { classMap = new TClassMap(); } else { classMap = new TClassMap(*base->classMap); } } catch (...) { /* * This statement is for release lock. So allocate check is after. */ } if (likely(base != NULL)) { /* Release parent container's spin lock. */ spinLockRelease(&base->lockval); } try { /* Check classMap. */ if (unlikely(classMap == NULL)) { throw 1; } /* Create trap sender. */ if (conf->SnmpSend()->get()) { pSender = new TTrapSender(SNMP_VERSION_2c, conf->SnmpTarget()->get(), conf->SnmpComName()->get(), 162); } else { pSender = NULL; } /* Create unloaded class information queue. */ unloadedList = new TClassInfoQueue(); /* Create thread storage key. */ if (unlikely(pthread_key_create(&clsContainerKey, NULL) != 0)) { throw 1; } } catch (...) { delete classMap; delete pSender; delete unloadedList; throw "TClassContainer initialize failed!"; } }
/*! * \brief Remove all-class from container. */ void TClassContainer::allClear(void) { /* Get spin lock of containers queue. */ spinLockWait(&queueLock); { /* Broadcast to each local container. */ for (TLocalClassContainer::iterator it = localContainers.begin(); it != localContainers.end(); it++) { /* Get local container's spin lock. */ spinLockWait(&(*it)->lockval); { (*it)->classMap->clear(); } /* Release local container's spin lock. */ spinLockRelease(&(*it)->lockval); } } /* Release spin lock of containers queue. */ spinLockRelease(&queueLock); /* Get class container's spin lock. */ spinLockWait(&lockval); { /* Free allocated memory at class map. */ for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end(); ++cur) { TObjectData *pos = (*cur).second; if (likely(pos != NULL)) { free(pos->className); free(pos); } } /* Free allocated memory at unloaded list. */ while (!unloadedList->empty()) { TObjectData *pos = unloadedList->front(); unloadedList->pop(); free(pos->className); free(pos); } /* Clear all class. */ classMap->clear(); } /* Release class container's spin lock. */ spinLockRelease(&lockval); }
/*! * \brief Get class entries count. * \return Entries count of class information. */ inline size_t getContainerSize(void) { size_t result = 0; /* Get class container's spin lock. */ spinLockWait(&lockval); { result = this->classMap->size(); } /* Release class container's spin lock. */ spinLockRelease(&lockval); return result; }
/*! * \brief Remove class from container. * \param target [in] Remove class data. */ void TClassContainer::removeClass(TObjectData *target) { /* Remove item from map. Please callee has container's lock. */ classMap->erase(target->klassOop); /* Get spin lock of containers queue. */ spinLockWait(&queueLock); { /* Broadcast to each local container. */ for (TLocalClassContainer::iterator it = localContainers.begin(); it != localContainers.end(); it++) { /* Get local container's spin lock. */ spinLockWait(&(*it)->lockval); { (*it)->classMap->erase(target->klassOop); } /* Release local container's spin lock. */ spinLockRelease(&(*it)->lockval); } } /* Release spin lock of containers queue. */ spinLockRelease(&queueLock); }
/*! * \brief Update class oop. * \param oldKlassOop [in] Target old class oop. * \param newKlassOop [in] Target new class oop. * \return Class data of target class. */ inline void updateClass(void *oldKlassOop, void *newKlassOop) { /* Get class container's spin lock. */ spinLockWait(&lockval); { /* Search class data. */ TClassMap::iterator it = classMap->find(oldKlassOop); if (it != classMap->end()) { TObjectData *cur = (*it).second; /* Remove old klassOop. */ classMap->erase(it); try { /* Update class data. */ (*classMap)[newKlassOop] = cur; cur->klassOop = newKlassOop; } catch (...) { /* * Maybe failed to allocate memory * at "std::map::operator[]". */ } } } /* Release class container's spin lock. */ spinLockRelease(&lockval); /* Get spin lock of containers queue. */ spinLockWait(&queueLock); { TLocalClassContainer::iterator it = localContainers.begin(); /* Broadcast to each local container. */ for (; it != localContainers.end(); it++) { (*it)->updateClass(oldKlassOop, newKlassOop); } } /* Release spin lock of containers queue. */ spinLockRelease(&queueLock); }
/*! * \brief Enqueue new event. * * \param thread [in] jthread object which occurs new event. * \param event [in] New thread event. * \param additionalData [in] Additional data if exist. */ void TThreadRecorder::putEvent(jthread thread, TThreadEvent event, jlong additionalData) { struct timeval tv; gettimeofday(&tv, NULL); TEventRecord eventRecord __attribute__((aligned(32))); // for YMM vmovdqa eventRecord.time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); eventRecord.thread_id = TVMFunctions::getInstance()->GetThreadId(*(void **)thread); eventRecord.event = event; eventRecord.additionalData = additionalData; if (unlikely(top_of_buffer->event == ThreadEnd)) { spinLockWait(&idmapLockVal); { std::tr1::unordered_map<jlong, char *, TNumericalHasher<jlong> > ::iterator entry = threadIDMap.find(top_of_buffer->thread_id); if (likely(entry != threadIDMap.end())) { free(entry->second); threadIDMap.erase(entry); } } spinLockRelease(&idmapLockVal); } spinLockWait(&bufferLockVal); { memcpy32(top_of_buffer, &eventRecord); if (unlikely(++top_of_buffer == end_of_buffer)) { top_of_buffer = (TEventRecord *)record_buffer; } } spinLockRelease(&bufferLockVal); }
/*! * \brief Search class from container. * \param klassOop [in] Target class oop. * \return Class data of target class. */ inline TObjectData *findClass(void *klassOop) { /* Search class data. */ TObjectData *result = NULL; /* Get class container's spin lock. */ spinLockWait(&lockval); { /* Search class data. */ TClassMap::iterator it = classMap->find(klassOop); if (it != classMap->end()) { result = (*it).second; } } /* Release class container's spin lock. */ spinLockRelease(&lockval); return result; }
/*! * \brief Register new thread to thread id map. * * \param jvmti [in] JVMTI environment. * \param thread [in] jthread object of new thread. */ void TThreadRecorder::registerNewThread(jvmtiEnv *jvmti, jthread thread) { void *thread_oop = *(void **)thread; jlong id = TVMFunctions::getInstance()->GetThreadId(thread_oop); jvmtiThreadInfo threadInfo; jvmti->GetThreadInfo(thread, &threadInfo); spinLockWait(&idmapLockVal); { char *current_val = threadIDMap[id]; if (unlikely(current_val != NULL)) { free(current_val); } threadIDMap[id] = strdup(threadInfo.name); } spinLockRelease(&idmapLockVal); jvmti->Deallocate((unsigned char *)threadInfo.name); }
/*! * \brief Get local class container with each threads. * \return Local class container instance for this thread. */ inline TClassContainer *getLocalContainer(void) { /* Get container for this thread. */ TClassContainer *result = (TClassContainer *)pthread_getspecific(clsContainerKey); /* If container isn't exists yet. */ if (unlikely(result == NULL)) { try { result = new TClassContainer(this, false); } catch (...) { /* Maybe raised badalloc exception. */ return NULL; } pthread_setspecific(clsContainerKey, result); bool isFailure = false; /* Get spin lock of containers queue. */ spinLockWait(&queueLock); { try { localContainers.push_back(result); } catch (...) { /* Maybe failed to add queue. */ isFailure = true; } } /* Release spin lock of containers queue. */ spinLockRelease(&queueLock); if (unlikely(isFailure)) { delete result; result = NULL; } } return result; }
/*! * \brief Dump record data to file. * * \param fname [in] File name to dump record data. */ void TThreadRecorder::dump(const char *fname) { int fd = creat(fname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd == -1) { logger->printWarnMsgWithErrno("Thread Recorder dump failed."); throw errno; } /* Write byte order mark. */ char bom = BOM; write(fd, &bom, sizeof(char)); spinLockWait(&idmapLockVal); { /* Dump thread list. */ int threadIDMapSize = threadIDMap.size(); write(fd, &threadIDMapSize, sizeof(int)); for (std::tr1::unordered_map<jlong, char *, TNumericalHasher<jlong> >::iterator itr = threadIDMap.begin(); itr != threadIDMap.end(); itr++) { jlong id = itr->first; int classname_length = strlen(itr->second); write(fd, &id, sizeof(jlong)); write(fd, &classname_length, sizeof(int)); write(fd, itr->second, classname_length); } /* Dump thread event. */ write(fd, record_buffer, aligned_buffer_size); } spinLockRelease(&idmapLockVal); close(fd); }
/*! * \brief Commit class information changing in class container.<br> * This function is for avoiding trouble with class map.<br> * At "afterTakeSnapShot", map is copied as shadow copy.<br> * So crash JVM, * if we remove item and output map at the same times. */ void TClassContainer::commitClassChange(void) { TClassInfoQueue *list = NULL; /* Get class container's spin lock. */ spinLockWait(&lockval); { /* Remove unloaded class which detected at "pushNewClass". */ while (!unloadedList->empty()) { TObjectData *target = unloadedList->front(); unloadedList->pop(); /* Free allocated memory. */ free(target->className); free(target); } try { list = new TClassInfoQueue(); /* Search delete target. */ for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end(); ++cur) { TObjectData *objData = (*cur).second; /* If class is prepared remove from class container. */ if (unlikely(objData->oldTotalSize == 0 && objData->isRemoved)) { /* * If we do removing map item here, * iterator's relationship will be broken. * So we store to list. And we remove after iterator loop. */ list->push(objData); } } } catch (...) { /* * Maybe failed to allocate memory. * E.g. raise exception at "new", "std::queue<T>::push" or etc.. */ delete list; list = NULL; } if (likely(list != NULL)) { /* Remove delete target. */ while (!list->empty()) { TObjectData *target = list->front(); list->pop(); /* Remove from all containers. */ removeClass(target); /* Free allocated memory. */ free(target->className); free(target); } } } /* Release class container's spin lock. */ spinLockRelease(&lockval); /* Cleanup. */ delete list; }
/*! * \brief Output all-class information to file. * \param snapshot [in] Snapshot instance. * \param rank [out] Sorted-class information. * \return Value is zero, if process is succeed.<br /> * Value is error number a.k.a. "errno", if process is failure. */ int TClassContainer::afterTakeSnapShot(TSnapShotContainer *snapshot, TSorter<THeapDelta> **rank) { /* Sanity check. */ if (unlikely(snapshot == NULL || rank == NULL)) { return 0; } /* Copy header. */ TSnapShotFileHeader hdr; memcpy(&hdr, (const void *)snapshot->getHeader(), sizeof(TSnapShotFileHeader)); /* If java heap usage alert is enable. */ if (conf->getHeapAlertThreshold() > 0) { jlong usage = hdr.newAreaSize + hdr.oldAreaSize; if (usage > conf->getHeapAlertThreshold()) { /* Raise alert. */ logger->printWarnMsg( "ALERT: Java heap usage exceeded the threshold (%ld MB)", usage / 1024 / 1024); /* If need send trap. */ if (conf->SnmpSend()->get()) { if (unlikely(!sendMemoryUsageAlertTrap(pSender, ALERT_JAVA_HEAP, hdr.snapShotTime, usage, jvmInfo->getMaxMemory()))) { logger->printWarnMsg("SNMP trap send failed!"); } } } } /* If metaspace usage alert is enable. */ if ((conf->MetaspaceThreshold()->get() > 0) && ((conf->MetaspaceThreshold()->get() * 1024 * 1024) < hdr.metaspaceUsage)) { const char *label = jvmInfo->isAfterCR6964458() ? "Metaspace" : "PermGen"; /* Raise alert. */ logger->printWarnMsg("ALERT: %s usage exceeded the threshold (%ld MB)", label, hdr.metaspaceUsage / 1024 / 1024); /* If need send trap. */ if (conf->SnmpSend()->get()) { if (unlikely(!sendMemoryUsageAlertTrap( pSender, ALERT_METASPACE, hdr.snapShotTime, hdr.metaspaceUsage, hdr.metaspaceCapacity))) { logger->printWarnMsg("SNMP trap send failed!"); } } } /* Class map used snapshot output. */ TClassMap *workClsMap = NULL; /* Get class container's spin lock. */ spinLockWait(&lockval); { try { workClsMap = new TClassMap(*this->classMap); } catch (...) { workClsMap = NULL; } } /* Release class container's spin lock. */ spinLockRelease(&lockval); if (unlikely(workClsMap == NULL)) { int raisedErrNum = errno; logger->printWarnMsgWithErrno("Couldn't allocate working memory!"); return raisedErrNum; } /* Allocate return array. */ jlong rankCnt = workClsMap->size(); rankCnt = (rankCnt < conf->RankLevel()->get()) ? rankCnt : conf->RankLevel()->get(); /* Make controller to sort. */ register TRankOrder order = conf->Order()->get(); TSorter<THeapDelta> *sortArray; try { sortArray = new TSorter<THeapDelta>( rankCnt, (TComparator)((order == DELTA) ? &HeapDeltaCmp : &HeapUsageCmp)); } catch (...) { int raisedErrNum = errno; logger->printWarnMsgWithErrno("Couldn't allocate working memory!"); delete workClsMap; return raisedErrNum; } /* Open file and seek EOF. */ int fd = open(conf->FileName()->get(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); /* If failure open file. */ if (unlikely(fd < 0)) { int raisedErrNum = errno; logger->printWarnMsgWithErrno("Could not open %s", conf->FileName()->get()); delete sortArray; delete workClsMap; return raisedErrNum; } off_t oldFileOffset = -1; try { /* Move position to EOF. */ oldFileOffset = lseek(fd, 0, SEEK_END); /* If failure seek. */ if (unlikely(oldFileOffset < 0)) { throw 1; } /* Frist, Output each classes information. Secondly output header. */ if (unlikely(lseek(fd, sizeof(TSnapShotFileHeader) - sizeof(char[80]) + hdr.gcCauseLen, SEEK_CUR) < 0)) { throw 1; } } catch (...) { int raisedErrNum = errno; logger->printWarnMsg("Could not write snapshot"); close(fd); delete sortArray; delete workClsMap; return raisedErrNum; } /* Output class information. */ THeapDelta result; jlong numEntries = 0L; int raiseErrorCode = 0; register jlong AlertThreshold = conf->getAlertThreshold(); /* Loop each class. */ for (TClassMap::iterator it = workClsMap->begin(); it != workClsMap->end(); ++it) { TObjectData *objData = (*it).second; TClassCounter *cur = snapshot->findClass(objData); /* If don't registed class yet. */ if (unlikely(cur == NULL)) { cur = snapshot->pushNewClass(objData); if (unlikely(cur == NULL)) { raiseErrorCode = errno; logger->printWarnMsgWithErrno("Couldn't allocate working memory!"); delete sortArray; sortArray = NULL; break; } } /* Calculate uasge and delta size. */ result.usage = cur->counter->total_size; result.delta = cur->counter->total_size - objData->oldTotalSize; result.tag = objData->tag; objData->oldTotalSize = result.usage; /* If do output class. */ if (!conf->ReduceSnapShot()->get() || (result.usage > 0)) { /* Output class-information. */ if (likely(raiseErrorCode == 0)) { raiseErrorCode = writeClassData(fd, objData, cur); } numEntries++; } /* Ranking sort. */ sortArray->push(result); /* If alert is enable. */ if (AlertThreshold > 0) { /* Variable for send trap. */ int sendFlag = 0; /* If size is bigger more limit size. */ if ((order == DELTA) && (AlertThreshold <= result.delta)) { /* Raise alert. */ logger->printWarnMsg( "ALERT(DELTA): \"%s\" exceeded the threshold (%ld bytes)", objData->className, result.delta); /* Set need send trap flag. */ sendFlag = 1; } else if ((order == USAGE) && (AlertThreshold <= result.usage)) { /* Raise alert. */ logger->printWarnMsg( "ALERT(USAGE): \"%s\" exceeded the threshold (%ld bytes)", objData->className, result.usage); /* Set need send trap flag. */ sendFlag = 1; } /* If need send trap. */ if (conf->SnmpSend()->get() && sendFlag != 0) { if (unlikely(!sendHeapAlertTrap(pSender, result, objData->className, cur->counter->count))) { logger->printWarnMsg("Send SNMP trap failed!"); } } } } delete workClsMap; /* Set output entry count. */ hdr.size = numEntries; /* Stored error number to avoid overwriting by "truncate" and etc.. */ int raisedErrNum = 0; try { /* If already failed in processing to write snapshot. */ if (unlikely(raiseErrorCode != 0)) { errno = raiseErrorCode; raisedErrNum = raiseErrorCode; throw 1; } /* If fail seeking to header position. */ if (unlikely(lseek(fd, oldFileOffset, SEEK_SET) < 0)) { raisedErrNum = errno; throw 2; } raisedErrNum = writeHeader(fd, hdr); /* If failed to write a snapshot header. */ if (unlikely(raisedErrNum != 0)) { throw 3; } } catch (...) { ; /* Failed to write file. */ } /* Clean up. */ if (unlikely(close(fd) != 0 && raisedErrNum == 0)) { errno = raisedErrNum; logger->printWarnMsgWithErrno("Could not write snapshot"); } /* If need rollback snapshot. */ if (unlikely(raisedErrNum != 0)) { if (unlikely(truncate(conf->FileName()->get(), oldFileOffset) < 0)) { logger->printWarnMsgWithErrno("Could not rollback snapshot"); } } /* Cleanup. */ (*rank) = sortArray; return raisedErrNum; }
/*! * \brief Append new-class to container. * \param klassOop [in] New class oop. * \param objData [in] Add new class data. * \return New-class data.<br /> * This value isn't equal param "objData", * if already registered equivalence class. */ TObjectData *TClassContainer::pushNewClass(void *klassOop, TObjectData *objData) { TObjectData *existData = NULL; /* Get class container's spin lock. */ spinLockWait(&lockval); { /* * Jvmti extension event "classUnload" is loose once in a while. * The event forget callback occasionally when class unloading. * So we need to check klassOop that was doubling. */ /* Check klassOop doubling. */ TClassMap::iterator it = classMap->find(klassOop); if (likely(it != classMap->end())) { /* Store data to return value as result. */ TObjectData *expectData = (*it).second; if (likely(expectData != NULL)) { /* If adding class data is already exists. */ if (unlikely(expectData->className != NULL && strcmp(objData->className, expectData->className) == 0 && objData->clsLoaderId == expectData->clsLoaderId)) { /* Return existing data on map. */ existData = expectData; } else { /* klass oop is doubling for another class. */ removeClass(expectData); try { unloadedList->push(expectData); } catch (...) { /* * We try to continue running without crash * even if failed to allocate memory. */ } } } } if (likely(existData == NULL)) { try { /* Append class data. */ (*classMap)[klassOop] = objData; } catch (...) { /* * Maybe failed to allocate memory at "std::map::operator[]". */ } } } /* Release class container's spin lock. */ spinLockRelease(&lockval); /* If already exist class data. */ if (unlikely(existData != NULL)) { return existData; } /* Get spin lock of containers queue. */ spinLockWait(&queueLock); { /* Broadcast to each local container. */ for (TLocalClassContainer::iterator it = localContainers.begin(); it != localContainers.end(); it++) { (*it)->pushNewClass(klassOop, objData); } } /* Release spin lock of containers queue. */ spinLockRelease(&queueLock); return objData; }