view agent/src/snapShotMain.cpp @ 66:bbf1ae6d2a2e

Bug 3331: Refactoring for memory management in HeapStats Agent Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/86
author Yasumasa Suenaga <yasuenag@gmail.com>
date Mon, 27 Feb 2017 19:25:26 +0900
parents ef928edf1d20
children 79f42583c31c
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 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) {
  /* Process unloaded classes. */
  OnGarbageCollectionFinishForUnload(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) {
  /* Process unloaded classes. */
  OnGarbageCollectionFinishForUnload(NULL);

  //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->findClass(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. */
      clsCounter = localSnapshot->pushNewChildClass(parentCounter, clsData);
    }
 
    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) {
    /* Process unloaded classes. */
    OnGarbageCollectionFinishForUnload(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) {
        snapshot->setTotalSize(jvmInfo->getTotalMemory());
        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 SnapShot Processor instance. */
    delete snapShotProcessor;
    snapShotProcessor = NULL;

    /*
     * Delete snapshot instances
     */
    if (snapshotByCMS != NULL) {
      TSnapShotContainer::releaseInstance(snapshotByCMS);
    }
    if (snapshotByGC != NULL) {
      TSnapShotContainer::releaseInstance(snapshotByGC);
    }

    /* Finalize and deallocate old snapshot containers. */
    TSnapShotContainer::globalFinalize();
    
    /* Destroy object that is for snapshot. */
    delete clsContainer;
    clsContainer = NULL;
    
    /* Destroy object that is each snapshot trigger. */
    delete gcWatcher;
    gcWatcher = NULL;
    
    delete timer;
    timer = NULL;
    
    /* Finalize oop util. */
    oopUtilFinalize();
}