view agent/src/snapShotProcessor.cpp @ 42:6dd54555d3bb

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:32:48 +0900
parents b21d5eef58f0
children bbf1ae6d2a2e
line wrap: on
line source

/*!
 * \file snapShotProcessor.cpp
 * \brief This file is used to output ranking and call snapshot function.
 * Copyright (C) 2011-2013 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 <time.h>
#include <jni.h>
#include <jvmti.h>

#include "util.hpp"
#include "fsUtil.hpp"
#include "classContainer.hpp"
#include "agentThread.hpp"
#include "snapShotProcessor.hpp"
#include "elapsedTimer.hpp"
#include "jvmInfo.hpp"
#include "sorter.hpp"

/*!
 * \brief TSnapShotProcessor constructor.
 * \param clsContainer [in] Class container object.
 * \param info         [in] JVM running performance information.
 */
TSnapShotProcessor::TSnapShotProcessor(TClassContainer *clsContainer,
    TJvmInfo *info)
    : TAgentThread("HeapStats SnapShot Processor"), snapQueue() {
    
    /* Sanity check. */
    if (clsContainer == NULL) {
        throw "TClassContainer is NULL.";
    }
    
    if (info == NULL) {
        throw "TJvmInfo is NULL.";
    }
    
    /* Setting parameter. */
    this->_container = clsContainer;
    this->jvmInfo    = info;
}

/*!
 * \brief TSnapShotProcessor destructor.
 */
TSnapShotProcessor::~TSnapShotProcessor(void) {
    /* Do Nothing. */
}

/*!
 * \brief Parallel work function by JThread.
 * \param jvmti [in] JVMTI environment information.
 * \param jni   [in] JNI environment information.
 * \param data  [in] Monitor-object for class-container.
 */
void JNICALL TSnapShotProcessor::entryPoint(jvmtiEnv *jvmti, JNIEnv *jni,
    void *data) {
    
    /* Ranking pointer. */
    TSorter<THeapDelta> *ranking = NULL;
    
    /* Get self. */
    TSnapShotProcessor *controller = (TSnapShotProcessor *)data;
    /* Change running state. */
    controller->_isRunning = true;
    
    bool existRemainder = false;
    /* Loop for agent run or remaining work exist. */
    while (!controller->_terminateRequest || existRemainder) {
        TSnapShotContainer *snapshot = NULL;
        /* Is notify flag. */
        bool needProcess = false;
        
        ENTER_PTHREAD_SECTION(&controller->mutex) {
            
            if (likely(controller->_numRequests == 0)) {
                
                /* Wait for notification or termination. */
                pthread_cond_wait(&controller->mutexCond, &controller->mutex);
            }
            
            /* If get notification means output. */
            if (likely(controller->_numRequests > 0)) {
                controller->_numRequests--;
                needProcess = true;
                if (likely(!controller->snapQueue.empty())) {
                    snapshot = controller->snapQueue.front();
                    controller->snapQueue.pop();
                }
            }
            
            /* Check remaining work. */
            existRemainder = (controller->_numRequests > 0);
            
        } EXIT_PTHREAD_SECTION(&controller->mutex)
        
        /* If waiting is finished by notification. */
        if (needProcess && (snapshot != NULL)) {
            
            int result = 0;
            {
                /* Count working time. */
                static const char *label = "Write SnapShot and calculation";
                TElapsedTimer elapsedTime(label);
                
                /* Marge children snapshot's data to parent snapshot. */
                snapshot->mergeChildren();
                
                /* Output class-data. */
                result = controller->_container->afterTakeSnapShot(
                    snapshot, &ranking);
            }
            
            /* If raise disk full error. */
            if (unlikely(isRaisedDiskFull(result))) {
                checkDiskFull(result, "snapshot");
            }
            
            /* Output snapshot infomartion. */
            snapshot->printGCInfo();
            
            /* If output failure. */
            if (likely(ranking != NULL)) {
                
                /* Show class ranking. */
                if (arg.LogLevel >= INFO && arg.RankLevel > 0) {
                    controller->showRanking(snapshot->getHeader(), ranking);
                }
            }
            
            /* Clean up. */
            controller->_container->commitClassChange();
            TSnapShotContainer::releaseInstance(snapshot);
            delete ranking;
            ranking = NULL;
        }
    }
    
    /* Change running state. */
    controller->_isRunning = false;
}

/*!
 * \brief Start parallel work by JThread.
 * \param jvmti [in] JVMTI environment information.
 * \param env   [in] JNI environment information.
 */
void TSnapShotProcessor::start(jvmtiEnv *jvmti, JNIEnv *env) {
    /* Start JThread. */
    TAgentThread::start(jvmti, env, TSnapShotProcessor::entryPoint,
        this, JVMTI_THREAD_MIN_PRIORITY);
}

/*!
 * \brief Notify output snapshot to this thread from other thread.
 * \param snapshot [in] Output snapshot instance.
 */
void TSnapShotProcessor::notify(TSnapShotContainer *snapshot) {
    
    /* Sanity check. */
    if (unlikely(snapshot == NULL)) {
        return;
    }
    
    bool raiseException = true;
    /* Send notification and count notify. */
    ENTER_PTHREAD_SECTION(&this->mutex) {
        
        try {
            /* Store and count data. */
            snapQueue.push(snapshot);
            this->_numRequests++;
            
            /* Notify occurred deadlock. */
            pthread_cond_signal(&this->mutexCond);
            
            /* Reset exception flag. */
            raiseException = false;
        } catch(...) {
            /*
             * Maybe faield to allocate memory at "std:queue<T>::push()".
             * So we throw exception again at after release lock.
             */
        }
    } EXIT_PTHREAD_SECTION(&this->mutex)
    
    if (unlikely(raiseException)) {
        throw "Failed to TSnapShotProcessor notify";
    }
}

/*!
 * \brief Show ranking.
 * \param hdr  [in] Snapshot file information.
 * \param data [in] All class-data.
 */
void TSnapShotProcessor::showRanking(const TSnapShotFileHeader *hdr,
    TSorter<THeapDelta> *data) {
    
    /* Get now datetime. */
    char time_str[20];
    struct tm time_struct;
    
    /* snapshot time is "msec" */
    jlong snapDate = hdr->snapShotTime / 1000;
    /* "time_t" is defined as type has 8byte size in 32bit. */
    /* But jlong is defined as type has 16byte size in 32bit and 64bit. */
    /* So this part need cast-code for 32bit. */
    localtime_r((const time_t*)&snapDate, &time_struct);
    strftime(time_str, 20, "%F %T", &time_struct);
    
    /* Output ranking header. */
    
    PRINT_INFO_MSG_HEADER_NOIF << "Heap Ranking at " << time_str;
    
    switch (hdr->cause) {
        case GC:
            STDOUT << " (caused by GC , GCCause:'" << hdr->gcCause
                << "')" << NEWLINE;
            break;
            
        case DataDumpRequest:
            STDOUT << " (caused by DataDumpRequest)" << NEWLINE;
            break;
            
        case Interval:
            STDOUT << " (caused by Interval)" << NEWLINE;
            break;
            
        default:
            /* Illegal value. */
            STDOUT << " (caused by UNKNOWN)" << NEWLINE;
            PRINT_WARN_MSG("Illegal snapshot cause!");
            return;
    }
    
    /* Output ranking column. */
    
    PRINT_INFO_MSG_HEADER_NOIF
        << "Rank    usage(byte)    increment(byte)  Class name" << NEWLINE;
    
    PRINT_INFO_MSG_HEADER_NOIF
        << "----  ---------------  ---------------  ----------" << NEWLINE;
    
    /* Output high-rank class information. */
    
    int rankCnt = data->getCount();
    Node<THeapDelta> *aNode = data->lastNode();
    for (int Cnt = 0; Cnt < rankCnt && aNode != NULL;
        Cnt++, aNode = aNode->prev) {
        
        /* Output header. */
        PRINT_INFO_MSG_HEADER_NOIF;
        
        STDOUT_WIDTH_ADJ(4);
        STDOUT << Cnt + 1 << "  ";
        
        STDOUT_WIDTH_ADJ(15);
        STDOUT << aNode->value.usage << "  ";
        
        STDOUT_WIDTH_ADJ(15);
        STDOUT << aNode->value.delta << "  ";
        
        STDOUT << ((TObjectData *)aNode->value.tag)->className << NEWLINE;
    }
    
    /* Clean up after ranking output. */
    
    FLUSH_STDOUT;
}