Mercurial > hg > release > heapstats-1.1
view agent/src/snapShotMain.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 | affbc00b3f73 |
children | c5c05ec81fea |
line wrap: on
line source
/*! * \file snapShotMain.cpp * \brief This file is used to take snapshot. * 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 <string.h> #include <stdlib.h> #include <unistd.h> #include <execinfo.h> #include <pthread.h> #include "snapShotMain.hpp" #include "libmain.hpp" #include "classContainer.hpp" #include "snapShotContainer.hpp" #include "snapShotProcessor.hpp" #include "gcWatcher.hpp" #include "timer.hpp" #include "util.hpp" #include "oopUtil.hpp" #include "elapsedTimer.hpp" #include "jvmInfo.hpp" #include "bitMapMarker.hpp" /* Struct defines. */ /*! * \brief This structure is stored snapshot and class heap usage. */ typedef struct { TSnapShotContainer *snapshot; /*!< Container of taking snapshot. */ TClassCounter *counter; /*!< Counter of class heap usage. */ TClassContainer *clsContainer; /*!< Container of class data. */ } TCollectContainers; /* Variable defines. */ /*! * \brief ClassData Container. */ TClassContainer *clsContainer = NULL; /*! * \brief SnapShot Processor. */ TSnapShotProcessor *snapShotProcessor = NULL; /*! * \brief GC Watcher. */ TGCWatcher *gcWatcher = NULL; /*! * \brief Timer Thread. */ TTimer *timer = NULL; /*! * \brief Pthread mutex for user data dump request.<br> * E.g. continue pushing dump key.<br> * <br> * This mutex used in below process.<br> * - OnDataDumpRequest @ snapShotMain.cpp<br> * To avoid a lot of data dump request parallel processing.<br> */ pthread_mutex_t dumpMutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /*! * \brief Pthread mutex for change snapshot queue.<br> * <br> * This mutex used in below process.<br> * - TakeSnapShot @ snapShotMain.cpp<br> * To add snapshot by JVMTI to output wait queue.<br> * - popSnapShotQueue @ snapShotMain.cpp<br> * To get snapshot from output wait queue.<br> * - addSnapShotQueue @ snapShotMain.cpp<br> * To add snapshot by GC to output wait queue.<br> */ pthread_mutex_t queueMutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /*! * \brief Pthread mutex for JVMTI IterateOverHeap calling.<br> * <br> * This mutex used in below process.<br> * - TakeSnapShot @ snapShotMain.cpp<br> * To avoid taking double snapshot use JVMTI.<br> * Because agent cann't distinguish called from where in hook function.<br> */ pthread_mutex_t jvmtiMutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; /*! * \brief Snapshot container instance queue to waiting output. */ TSnapShotQueue snapStockQueue; /*! * \brief Container instance stored got snapshot by GC. */ TSnapShotContainer *snapshotByGC = NULL; /*! * \brief Container instance stored got snapshot by CMSGC. */ TSnapShotContainer *snapshotByCMS = NULL; /*! * \brief Container instance stored got snapshot by interval or dump request. */ TSnapShotContainer *snapshotByJvmti = NULL; /*! * \brief Index of JVM class unloading event. */ int classUnloadEventIdx = -1; /* Function defines. */ /*! * \brief Count object size in Heap. * \param clsTag [in] Tag of object's class. * \param size [in] Object size. * \param objTag [in,out] Object's tag. * \param userData [in,out] User's data. */ jvmtiIterationControl JNICALL HeapObjectCallBack(jlong clsTag, jlong size, jlong *objTag, void *userData) { /* This callback is dummy. */ return JVMTI_ITERATION_ABORT; } /*! * \brief New class loaded event. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param thread [in] Java thread object. * \param klass [in] Newly loaded class object. */ void JNICALL OnClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { /* Get klassOop. */ void *mirror = *(void**)klass; void *klassOop = asKlassOop(mirror); if (likely(klassOop != NULL)) { /* Push new loaded class. */ clsContainer->pushNewClass(klassOop); } } /*! * \brief Class unload event. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param thread [in] Java thread object. * \param klass [in] Unload class object. * \sa from: hotspot/src/share/vm/prims/jvmti.xml */ void JNICALL OnClassUnload(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { /* Get klassOop. */ void *mirror = *(void**)klass; void *klassOop = asKlassOop(mirror); if (likely(klassOop != NULL)) { /* Search class. */ TObjectData *counter = clsContainer->findClass(klassOop); if (likely(counter != NULL)) { /* Remove class data. */ clsContainer->popClass(counter); } } } /*! * \brief Setting JVM information to snapshot. * \param snapshot [in] Snapshot instance. * \param cause [in] Cause of taking a snapshot.<br> * e.g. GC, DumpRequest or Interval. */ inline void setSnapShotInfo(TInvokeCause cause, TSnapShotContainer *snapshot) { /* Get now date and time. */ struct timeval tv; gettimeofday(&tv, NULL); /* Set snapshot information. */ snapshot->setSnapShotTime( (jlong)tv.tv_sec * 1000 + (jlong)tv.tv_usec / 1000); snapshot->setSnapShotCause(cause); snapshot->setJvmInfo(jvmInfo); } /*! * \brief Add snapshot to outputing wait queue. * \param snapshot [in] Snapshot instance. * \return Process result.<br> * Maybe failed to add snapshot to queue, if value is false.<br> * So that, you should be additionally handling snapshot instance. */ inline bool addSnapShotQueue(TSnapShotContainer *snapshot) { bool isSucceed = false; /* Push output waiting queue. */ ENTER_PTHREAD_SECTION(&queueMutex) { try { snapStockQueue.push(snapshot); /* Reset exception flag. */ isSucceed = true; } catch(...) { /* * Maybe faield to allocate memory at "std:queue<T>::push()". * So we throw exception again at after release lock. */ } } EXIT_PTHREAD_SECTION(&queueMutex) return isSucceed; } /*! * \brief Pop snapshot from wait queue for output. * \return Snapshot instance.<br> * Maybe queue is empty, if value is NULL. */ inline TSnapShotContainer *popSnapShotQueue(void) { TSnapShotContainer *snapshot = NULL; /* Get snapshot data from waiting queue. */ ENTER_PTHREAD_SECTION(&queueMutex) { if (likely(!snapStockQueue.empty())) { snapshot = snapStockQueue.front(); snapStockQueue.pop(); } } EXIT_PTHREAD_SECTION(&queueMutex) return snapshot; } /*! * \brief Notify to processor for output snapshot to file. * \param snapshot [in] Snapshot instance. * \warning After this function has called, Don't use a param "snapshot" again. */ inline void notifySnapShot(TSnapShotContainer *snapshot) { try { /* Sending notification means able to output file. */ snapShotProcessor->notify(snapshot); } catch(...) { PRINT_WARN_MSG("Snapshot processeor notify failed!."); TSnapShotContainer::releaseInstance(snapshot); } } /*! * \brief Set information and push waiting queue. * \param snapshot [in] Snapshot instance. */ inline void outputSnapShotByGC(TSnapShotContainer *snapshot) { setSnapShotInfo(GC, snapshot); /* Standby for next GC. */ jvmInfo->resumeGCinfo(); /* If failed to push waiting queue. */ if (unlikely(!addSnapShotQueue(snapshot))) { TSnapShotContainer::releaseInstance(snapshot); } /* Send notification. */ gcWatcher->notify(); } /*! * \brief Interrupt inner garbage collection event. * Call this function when JVM invoke many times of GC process * during single JVMTI event of GC start and GC finish. */ void onInnerGarbageCollectionInterrupt(void) { /* Standby for next GC. */ jvmInfo->resumeGCinfo(); /* Clear unfinished snapshot data. */ snapshotByGC->clear(false); } /*! * \brief Before garbage collection event. * \param jvmti [in] JVMTI environment object. */ void JNICALL OnGarbageCollectionStart(jvmtiEnv *jvmti) { snapshotByGC = TSnapShotContainer::getInstance(); /* Enable inner GC event. */ setupHookForInnerGCEvent(true, &onInnerGarbageCollectionInterrupt); } /*! * \brief After garbage collection event. * \param jvmti [in] JVMTI environment object. */ void JNICALL OnGarbageCollectionFinish(jvmtiEnv *jvmti) { /* Disable inner GC event. */ setupHookForInnerGCEvent(false, NULL); /* If need getting snapshot. */ if(gcWatcher->needToStartGCTrigger()){ /* Set information and push waiting queue. */ outputSnapShotByGC(snapshotByGC); } else{ TSnapShotContainer::releaseInstance(snapshotByGC); } snapshotByGC = NULL; } /*! * \brief After G1 garbage collection event. */ void OnG1GarbageCollectionFinish(void) { //jvmInfo->loadGCCause(); jvmInfo->SetUnknownGCCause(); /* Set information and push waiting queue. */ outputSnapShotByGC(snapshotByGC); snapshotByGC = TSnapShotContainer::getInstance(); } /*! * \brief Get class information. * \param aClsContainer [in] Search target class container. * \param klassOop [in] Pointer of child java class object(KlassOopDesc). * \return Pointer of information of expceted class by klassOop. */ inline TObjectData *getObjectDataFromKlassOop(TClassContainer *aClsContainer, void *klassOop) { TObjectData *clsData = NULL; /* Search child class at local class container. */ clsData = aClsContainer->findClassWithoutLock(klassOop); if (unlikely(clsData == NULL)) { /* Search child class at root class container. */ clsData = clsContainer->findClass(klassOop); if (unlikely(clsData == NULL)) { /* Push new loaded class to root class container. */ clsData = clsContainer->pushNewClass(klassOop); } else { /* Push a loaded class to local class container. */ aClsContainer->pushNewClass(klassOop, clsData); } } return clsData; } /*! * \brief Iterate oop field object callback for GC and JVMTI snapshot. * \param oop [in] Java heap object(Inner class format). * \param data [in] User expected data. */ void iterateFieldObjectCallBack(void *oop, void* data) { TCollectContainers *containerInfo = (TCollectContainers*)data; void *klassOop = getKlassOopFromOop(oop); /* Sanity check. */ if (unlikely(klassOop == NULL || containerInfo == NULL)) { return; } TSnapShotContainer *localSnapshot = containerInfo->snapshot; TClassCounter *parentCounter = containerInfo->counter; TClassContainer *localClsContainer = containerInfo->clsContainer; TChildClassCounter *clsCounter = NULL; /* Search child class. */ clsCounter = localSnapshot->findChildClass(parentCounter, klassOop); if(unlikely(clsCounter == NULL)){ /* Get child class information. */ TObjectData *clsData = getObjectDataFromKlassOop( localClsContainer, klassOop); /* Push new child loaded class. */ if (!clsData->isRemoved) { clsCounter = localSnapshot->pushNewChildClass(parentCounter, clsData); } else { /* We should return if clsData has already been removed (unloaded). */ return; } } if(unlikely(clsCounter == NULL)){ PRINT_CRIT_MSG("Couldn't get class counter!"); return; } jlong size = 0; if(clsCounter->objData->oopType == otInstance){ if(likely(clsCounter->objData->instanceSize != 0)){ size = clsCounter->objData->instanceSize; } else{ getObjectSize(NULL, (jobject)&oop, &size); clsCounter->objData->instanceSize = size; } } else{ getObjectSize(NULL, (jobject)&oop, &size); } /* Count perent class size and instance count. */ localSnapshot->FastInc(clsCounter->counter, size); } /*! * \brief Calculate size of object and iterate child-class in heap. * \param snapshot [in] Snapshot instance. * \param oop [in] Java heap object(Inner class format). */ inline void calculateObjectUsage(TSnapShotContainer *snapshot, void *oop){ void *klassOop = getKlassOopFromOop(oop); TClassContainer *workClsContainer = clsContainer->getLocalContainer(); /* Sanity check. */ if (unlikely(snapshot == NULL || klassOop == NULL || workClsContainer == NULL)) { return; } TSnapShotContainer *localSnapshot = snapshot->getLocalContainer(); if(unlikely(localSnapshot == NULL)){ PRINT_CRIT_MSG("Couldn't get local snapshot container!"); return; } snapshot->setIsCleared(false); TClassCounter *clsCounter = NULL; TObjectData *clsData = NULL; /* Get class information. */ clsData = getObjectDataFromKlassOop(workClsContainer, klassOop); if(unlikely(clsData == NULL)){ PRINT_CRIT_MSG("Couldn't get ObjectData!"); return; } /* Search class. */ clsCounter = localSnapshot->findClass(clsData); if(unlikely(clsCounter == NULL)){ /* Push new loaded class. */ clsCounter = localSnapshot->pushNewClass(clsData); } if(unlikely(clsCounter == NULL)){ PRINT_CRIT_MSG("Couldn't get class counter!"); return; } TOopType oopType = clsData->oopType; jlong size = 0; if(oopType == otInstance){ if(likely(clsData->instanceSize != 0)){ size = clsData->instanceSize; } else{ getObjectSize(NULL, (jobject)&oop, &size); clsData->instanceSize = size; } } else{ getObjectSize(NULL, (jobject)&oop, &size); } /* Count perent class size and instance count. */ localSnapshot->FastInc(clsCounter->counter, size); /* If oop has no field. */ if(!hasOopField(oopType)){ return; } TCollectContainers containerInfo; containerInfo.snapshot = localSnapshot; containerInfo.counter = clsCounter; containerInfo.clsContainer = workClsContainer; TOopMapBlock *offsets = NULL; int offsetCount = 0; offsets = clsCounter->offsets; offsetCount = clsCounter->offsetCount; /* If offset list is unused yet. */ if(unlikely(offsets == NULL && offsetCount < 0)){ /* Generate offset list. */ generateIterateFieldOffsets(klassOop, oopType, &offsets, &offsetCount); clsCounter->offsets = offsets; clsCounter->offsetCount = offsetCount; } /* Iterate non-static field objects. */ iterateFieldObject(&iterateFieldObjectCallBack, oop, oopType, &offsets, &offsetCount, &containerInfo); } /*! * \brief Count object size in heap by GC. * \param oop [in] Java heap object(Inner class format). * \param data [in] User expected data. Always this value is NULL. */ void HeapObjectCallbackOnGC(void *oop, void* data){ /* Calculate and merge to GC snapshot. */ calculateObjectUsage(snapshotByGC, oop); } /*! * \brief Count object size in heap by CMSGC. * \param oop [in] Java heap object(Inner class format). * \param data [in] User expected data. Always this value is NULL. */ void HeapObjectCallbackOnCMS(void *oop, void* data){ /* Calculate and merge to CMSGC snapshot. */ calculateObjectUsage(snapshotByCMS, oop); } /*! * \brief Count object size in heap by JVMTI iterateOverHeap. * \param oop [in] Java heap object(Inner class format). * \param data [in] User expected data. Always this value is NULL. */ void HeapObjectCallbackOnJvmti(void *oop, void* data){ /* Calculate and merge to JVMTI snapshot. */ calculateObjectUsage(snapshotByJvmti, oop); } /*! * \brief This function is for class oop adjust callback by GC. * \param oldOop [in] Old pointer of java class object(KlassOopDesc). * \param newOop [in] New pointer of java class object(KlassOopDesc). */ void HeapKlassAdjustCallback(void *oldOop, void *newOop) { /* Class information update. */ clsContainer->updateClass(oldOop, newOop); } /*! * \brief Event of before garbage collection by CMS collector. * \param jvmti [in] JVMTI environment object. */ void JNICALL OnCMSGCStart(jvmtiEnv *jvmti) { /* Get CMS state. */ bool needShapShot = false; int cmsState = checkCMSState(gcStart, &needShapShot); /* If occurred snapshot target GC. */ if (needShapShot) { /* If need getting snapshot. */ if (gcWatcher->needToStartGCTrigger()) { /* Set information and push waiting queue. */ outputSnapShotByGC(snapshotByCMS); snapshotByCMS = NULL; } } if (likely(snapshotByGC == NULL)) { snapshotByGC = TSnapShotContainer::getInstance(); } else { snapshotByGC->clear(false); } if(likely(snapshotByCMS == NULL)){ snapshotByCMS = TSnapShotContainer::getInstance(); } else if(cmsState == CMS_FINALMARKING){ snapshotByCMS->clear(false); } /* Enable inner GC event. */ setupHookForInnerGCEvent(true, &onInnerGarbageCollectionInterrupt); } /*! * \brief Event of after garbage collection by CMS collector. * \param jvmti [in] JVMTI environment object. */ void JNICALL OnCMSGCFinish(jvmtiEnv *jvmti) { /* Disable inner GC event. */ setupHookForInnerGCEvent(false, NULL); /* Get CMS state. */ bool needShapShot = false; checkCMSState(gcFinish, &needShapShot); /* If occurred snapshot target GC. */ if (needShapShot) { /* If need getting snapshot. */ if (gcWatcher->needToStartGCTrigger()) { /* Set information and push waiting queue. */ outputSnapShotByGC(snapshotByGC); snapshotByGC = NULL; snapshotByCMS->clear(false); } } } /*! * \brief Data dump request event. * \param jvmti [in] JVMTI environment object. */ void JNICALL OnDataDumpRequest(jvmtiEnv *jvmti) { /* Avoid the plural simultaneous take snapshot by dump-request. */ /* E.g. keeping pushed dump key. */ /* Because classContainer register a redundancy class in TakeSnapShot. */ ENTER_PTHREAD_SECTION(&dumpMutex) { /* Make snapshot. */ TakeSnapShot(jvmti, NULL, DataDumpRequest); } EXIT_PTHREAD_SECTION(&dumpMutex) } /*! * \brief Take a heap information snapshot. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param cause [in] Cause of taking a snapshot.<br> * e.g. GC, DumpRequest or Interval. */ void TakeSnapShot(jvmtiEnv *jvmti, JNIEnv *env, TInvokeCause cause) { /* * Initial Mark is 1st phase in CMS. * So we can process the SnapShot in SnapShot queue. */ if(useCMS && (*CMS_collectorState > CMS_INITIALMARKING)){ PRINT_WARN_MSG("CMS GC is working. Skip to take a SnapShot."); TSnapShotContainer *snapshot = popSnapShotQueue(); if(likely(snapshot != NULL)){ TSnapShotContainer::releaseInstance(snapshot); } return; } /* Count working time. */ static const char *label = "Take SnapShot"; TElapsedTimer elapsedTime(label); /* Phase1: Heap Walking. */ jvmtiError error = JVMTI_ERROR_INTERNAL; if (cause != GC) { TSnapShotContainer *snapshot = TSnapShotContainer::getInstance(); if (likely(snapshot != NULL)) { /* Lock to avoid doubling call JVMTI. */ ENTER_PTHREAD_SECTION(&jvmtiMutex) { snapshotByJvmti = snapshot; /* Enable JVMTI hooking. */ if (likely(setJvmtiHookState(true))) { /* Count object size on heap. */ error = jvmti->IterateOverHeap(JVMTI_HEAP_OBJECT_EITHER, &HeapObjectCallBack, NULL); /* Disable JVMTI hooking. */ setJvmtiHookState(false); } snapshotByJvmti = NULL; } EXIT_PTHREAD_SECTION(&jvmtiMutex) } if (likely(error == JVMTI_ERROR_NONE)) { setSnapShotInfo(cause, snapshot); /* If failed to push waiting queue. */ if (unlikely(!addSnapShotQueue(snapshot))) { error = JVMTI_ERROR_OUT_OF_MEMORY; } } if (unlikely(error != JVMTI_ERROR_NONE)) { /* Process failure. So data is junk. */ TSnapShotContainer::releaseInstance(snapshot); } } else { /* * If cause is "GC", then we already collect heap object to snapshot * by override functions exist in "overrideBody.S". */ error = JVMTI_ERROR_NONE; } /* Phase2: Output snapshot. */ /* If failure count object size. */ if(unlikely(isError(jvmti, error))){ PRINT_WARN_MSG("Heap snapshot failed!"); } else{ /* Pop snapshot. */ TSnapShotContainer *snapshot = popSnapShotQueue(); if(likely(snapshot != NULL)){ /* Set total memory. */ snapshot->setTotalSize(jvmInfo->getTotalMemory()); /* Notify to processor. */ notifySnapShot(snapshot); } } /* Phase3: Reset Timer. */ if (arg.TimerInterval > 0) { /* Sending notification means reset timer. */ timer->notify(); } } /*! * \brief Setting JVMTI functional capabilities. * \param capabilities [out] Event capabilities structure. */ void setCapabilitiesForSnapShot(jvmtiCapabilities *capabilities) { /* Enable for take snapshop. */ capabilities->can_generate_garbage_collection_events = 1; capabilities->can_tag_objects = 1; } /*! * \brief Setting enable of JVMTI and extension events for snapshot function. * \param jvmti [in] JVMTI environment object. * \param enable [in] Event notification is enable. * \return Setting process result. */ jint setEventEnableForSnapShot(jvmtiEnv *jvmti, bool enable) { jvmtiEventMode mode = JVMTI_ENABLE; if (unlikely(!enable)) { mode = JVMTI_DISABLE; } /* Switch date dump event. */ if (arg.triggerOnDump) { SWITCH_EVENT_NOTIFICATION(jvmti, JVMTI_EVENT_DATA_DUMP_REQUEST, mode) } /* Switch gc events. */ if (arg.triggerOnFullGC) { /* Switch JVMTI GC events. */ SWITCH_EVENT_NOTIFICATION(jvmti, JVMTI_EVENT_GARBAGE_COLLECTION_START, mode) SWITCH_EVENT_NOTIFICATION(jvmti, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, mode) } return SUCCESS; } /*! * \brief Setting enable of agent each threads for snapshot function. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. * \param enable [in] Event notification is enable. */ void setThreadEnableForSnapShot(jvmtiEnv *jvmti, JNIEnv *env, bool enable) { /* Start or suspend HeapStats agent threads. */ try{ /* Switch GC watcher state. */ if (arg.triggerOnFullGC) { if (enable) { gcWatcher->start(jvmti, env); } else { gcWatcher->stop(); } /* Switch GC hooking state. */ setGCHookState(enable); } /* Switch interval snapshot timer state. */ if (arg.TimerInterval > 0) { if (enable) { timer->start(jvmti, env, arg.TimerInterval); } else { timer->stop(); } } /* Switch snapshot processor state. */ if (enable) { snapShotProcessor->start(jvmti, env); } else { snapShotProcessor->stop(); } } catch (const char *errMsg) { PRINT_WARN_MSG(errMsg); } } /*! * \brief JVM initialization event for snapshot function. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. */ void onVMInitForSnapShot(jvmtiEnv *jvmti, JNIEnv *env) { size_t maxMemSize = jvmInfo->getMaxMemory(); /* Setup for hooking. */ setupHook(&HeapObjectCallbackOnGC, &HeapObjectCallbackOnCMS, &HeapObjectCallbackOnJvmti, &HeapKlassAdjustCallback, &OnG1GarbageCollectionFinish, maxMemSize); /* JVMTI Extension Event Setup. */ int eventIdx = GetClassUnloadingExtEventIndex(jvmti); /* * JVMTI extension event is influenced by JVM's implementation. * Extension event may no exist , if JVM was a little updated. * This is JVMTI's Specifications. * So result is disregard, even if failure processed extension event. */ if (eventIdx < 0) { /* Miss get extension event list. */ PRINT_WARN_MSG("Couldn't get ClassUnload event."); } else { /* if failed to set onClassUnload event. */ if (isError(jvmti, jvmti->SetExtensionEventCallback(eventIdx, (jvmtiExtensionEvent)&OnClassUnload))) { /* Failure setting extension event. */ PRINT_WARN_MSG("Couldn't register ClassUnload event."); } else { classUnloadEventIdx = eventIdx; } } } /*! * \brief JVM finalization event for snapshot function. * \param jvmti [in] JVMTI environment object. * \param env [in] JNI environment object. */ void onVMDeathForSnapShot(jvmtiEnv *jvmti, JNIEnv *env) { if(useCMS){ /* Disable inner GC event. */ setupHookForInnerGCEvent(false, NULL); /* Get CMS state. */ bool needShapShot = false; checkCMSState(gcLast, &needShapShot); /* If occurred snapshot target GC. */ if (needShapShot && snapshotByCMS != NULL) { /* Output snapshot. */ outputSnapShotByGC(snapshotByCMS); snapshotByCMS = NULL; } } TSnapShotContainer *snapshot = popSnapShotQueue(); /* Output all waiting snapshot. */ while (snapshot != NULL) { notifySnapShot(snapshot); snapshot = popSnapShotQueue(); } /* Disable class prepare/unload events. */ jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CLASS_PREPARE, NULL); if (likely(classUnloadEventIdx >= 0)) { jvmti->SetExtensionEventCallback(classUnloadEventIdx, NULL); } } /*! * \brief Agent initialization for snapshot function. * \param jvmti [in] JVMTI environment object. * \return Initialize process result. */ jint onAgentInitForSnapShot(jvmtiEnv **jvmti) { /* Initialize oop util. */ if (unlikely(!oopUtilInitialize(*jvmti))) { PRINT_CRIT_MSG("Please check installation and version of java and debuginfo packages."); return GET_LOW_LEVEL_INFO_FAILED; } /* Initialize snapshot containers. */ if (unlikely(!TSnapShotContainer::globalInitialize())) { PRINT_CRIT_MSG("TSnapshotContainer initialize failed!"); return CLASSCONTAINER_INITIALIZE_FAILED; } /* Initialize TClassContainer. */ try { clsContainer = new TClassContainer(); } catch(...) { PRINT_CRIT_MSG("TClassContainer initialize failed!"); return CLASSCONTAINER_INITIALIZE_FAILED; } /* Create thread instances that controlled snapshot trigger. */ try { gcWatcher = new TGCWatcher(&TakeSnapShot, jvmInfo); snapShotProcessor = new TSnapShotProcessor(clsContainer, jvmInfo); timer = new TTimer(&TakeSnapShot, "HeapStats Snapshot Timer"); } catch(const char *errMsg) { PRINT_CRIT_MSG(errMsg); return AGENT_THREAD_INITIALIZE_FAILED; } catch(...) { PRINT_CRIT_MSG("AgentThread initialize failed!"); return AGENT_THREAD_INITIALIZE_FAILED; } return SUCCESS; } /*! * \brief Agent finalization for snapshot function. * \param env [in] JNI environment object. */ void onAgentFinalForSnapShot(JNIEnv *env) { /* Destroy object that is for snapshot. */ delete clsContainer; clsContainer = NULL; delete snapShotProcessor; snapShotProcessor = NULL; /* Finalize and deallocate old snapshot containers. */ TSnapShotContainer::globalFinalize(); /* Destroy object that is each snapshot trigger. */ delete gcWatcher; gcWatcher = NULL; delete timer; timer = NULL; /* Finalize oop util. */ oopUtilFinalize(); }