view agent/src/snapShotProcessor.cpp @ 54:296461a953ac

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:31:15 +0900
parents 8be12a9f1298
children
line wrap: on
line source

/*!
 * \file snapShotProcessor.cpp
 * \brief This file is used to output ranking and call snapshot function.
 * Copyright (C) 2011-2015 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 "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 mutex        [in] Mutex-object for class container.
 * \param info         [in] JVM running performance information.
 */
TSnapShotProcessor::TSnapShotProcessor(
              TClassContainer *clsContainer, TJvmInfo *info)
                               : TAgentThread("HeapStats SnapShot Processor") {
  
  /* 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;
  
  /* Loop for agent run. */
  while (!controller->_terminateRequest) {
    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;
        snapshot = controller->snapQueue.front();
        controller->snapQueue.pop();
      }
      
    } EXIT_PTHREAD_SECTION(&controller->mutex)
    
    /* If waiting is finished by notification. */
    if (needProcess && (snapshot != NULL)) {
      
      /* Count working time. */
      static const char *label = "Write SnapShot and calculation";
      TElapsedTimer elapsedTime(label);
        
      /* Output class-data. */
      ranking = controller->_container->afterTakeSnapShot(snapshot);
      
      /* Output snapshot infomartion. */
      snapshot->printGCInfo();
      
      /* If output failure. */
      if (unlikely(ranking == NULL)) {
        controller->_container->removePendingObjectData();
        TSnapShotContainer::releaseInstance(snapshot);
        continue;
      }
      
      /* Show class ranking. */
      if (arg.LogLevel >= INFO && arg.RankLevel > 0) {
        controller->showRanking(snapshot->getHeader(), ranking);
      }
      
      /* Clean up. */
      controller->_container->removePendingObjectData();
      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;
  }

  snapshot->mergeChildren();
  
  /* Send notification and count notify. */
  ENTER_PTHREAD_SECTION(&this->mutex) {
    
    /* Count and store data. */
    this->_numRequests++;
    snapQueue.push(snapshot);
    
    /* Notify occurred deadlock. */
    pthread_cond_signal(&this->mutexCond);
    
  } EXIT_PTHREAD_SECTION(&this->mutex)
}

/*!
 * \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;
}