view agent/src/classContainer.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 6fec95209844
children
line wrap: on
line source

/*!
 * \file classContainer.cpp
 * \brief This file is used to add up using size every class.
 * 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 <fcntl.h>
#include <unistd.h>
#include <jni.h>
#include <time.h>
#include <stddef.h>
#include <pthread.h>

#include "classContainer.hpp"
#include "util.hpp"
#include "oopUtil.hpp"
#include "trapSender.hpp"
#include "sorter.hpp"
#include "libmain.hpp"

/*!
 * \brief SNMP variable Identifier of raise heap-alert date.
 */
static oid OID_ALERT_DATE[]      = {SNMP_OID_HEAPALERT, 1};
/*!
 * \brief SNMP variable Identifier of class-name is cause of heap-alert.
 */
static oid OID_ALERT_CLS_NAME[]  = {SNMP_OID_HEAPALERT, 2};
/*!
 * \brief SNMP variable Identifier of kind of class-size.
 */
static oid OID_ALERT_SIZE_KIND[] = {SNMP_OID_HEAPALERT, 3};
/*!
 * \brief SNMP variable Identifier of class-size.
 */
static oid OID_ALERT_CLS_SIZE[]  = {SNMP_OID_HEAPALERT, 4};
/*!
 * \brief SNMP variable Identifier of instance count.
 */
static oid OID_ALERT_CLS_COUNT[] = {SNMP_OID_HEAPALERT, 5};

/*!
 * \brief SNMP variable Identifier of raise Java heap alert date.
 */
static oid OID_JAVAHEAPALERT_DATE[]         = {SNMP_OID_JAVAHEAPALERT, 1};
/*!
 * \brief SNMP variable Identifier of Java heap usage.
 */
static oid OID_JAVAHEAPALERT_USAGE[]        = {SNMP_OID_JAVAHEAPALERT, 2};
/*!
 * \brief SNMP variable Identifier of Java heap max capacity.
 */
static oid OID_JAVAHEAPALERT_MAX_CAPACITY[] = {SNMP_OID_JAVAHEAPALERT, 3};

/*!
 * \brief Format string for jlong.
 */
const char JLONG_FORMAT_STR[5] = "%lld";

/*!
 * \brief String of USAGE order.
 */
static char ORDER_USAGE[6] = "USAGE";

/*!
 * \briefString of DELTA order.
 */
static char ORDER_DELTA[6] = "DELTA";

/*!
 * \brief TClassContainer constructor.
 */
TClassContainer::TClassContainer(TClassContainer *base, bool needToClr)
                                        : classContainerInTLS(), pendingQueue(){
  
  try {

    if(base == NULL)
      classMap = new TClassMap();
    else{
      SpinLockWait(&base->lockval);
      {
        classMap = new TClassMap(*base->classMap);
      }
      SpinLockRelease(&base->lockval);
    }
    
    /* Create trap sender. */
    pSender = new TTrapSender(SNMP_VERSION_2c, arg.snmpTarget,
      arg.snmpComName, 162);
  } catch(...){ 
    throw "TClassContainer initialize failed!";
  }

  pthread_key_create(&tlsKey, NULL);
  lockval = 0;
  tlsLock = 0;
  needToClear = needToClr;
  pendingQueueLock = 0;
}

/*!
 * \brief TClassContainer destructor.
 */
TClassContainer::~TClassContainer(void) {

  /* Cleanup class information. */
  if(needToClear){
    this->allClear();
  }
  
  /* Cleanup ClassContainer in TLS */
  for(TLocalClassContainer::iterator itr = classContainerInTLS.begin();
    itr != classContainerInTLS.end(); itr++){
    delete *itr;
  }

  /* Cleanup instances. */
  delete classMap;
  delete pSender;

  pthread_key_delete(tlsKey);
}

/*!
 * \brief Append new-class to container.
 * \param klassOop [in] New class oop.
 * \return New-class data.
 */
TObjectData *TClassContainer::pushNewClass(void *klassOop, TObjectData *objData){
  TObjectData *result = NULL;

  SpinLockWait(&lockval);
  {
    TClassMap::iterator itr = classMap->find(klassOop);

    if(itr != classMap->end()){
      result = itr->second;

      /*
       * Existed class has been unloaded.
       * However ClassUnload event was not fired.
       *
       * In this case, we MUST USE existed TObjectData.
       * Because, TObjectData might be existed in TSnapShotContainer.
       * We can't remove TObjectData in TSnapShotContainer from here!
       */
      if(unlikely(strcmp(objData->className, result->className) != 0)){
        result->classNameLen = objData->classNameLen;
        result->className = strdup(objData->className);
        result->oldTotalSize = 0;
      }

    }
    else{
      result = objData;
      (*classMap)[klassOop] = objData;
    }

  }
  SpinLockRelease(&lockval);

  if(unlikely(result != objData)){ /* entry has already existed */
    return result;
  }

  /* Broadcast new entry to TLS Container */
  SpinLockWait(&tlsLock);
  {
    for(TLocalClassContainer::iterator itr = classContainerInTLS.begin();
                                      itr != classContainerInTLS.end(); itr++){
      (*itr)->pushNewClass(klassOop, objData);
    }
  }
  SpinLockRelease(&tlsLock);

  return objData;
}

TObjectData *TClassContainer::pushNewClass(void *klassOop) {
  TObjectData *cur;

  /* Class info setting. */
  cur = (TObjectData *)calloc(1, sizeof(TObjectData));

  /* If failure allocate. */
  if (unlikely(cur == NULL)) {
    /* Adding empty to list is deny. */
    PRINT_WARN_MSG("Couldn't allocate counter memory!");
    return NULL;
  }
  
  cur->tag = (jlong)cur;
  cur->className = getClassName(getKlassFromKlassOop(klassOop));
  /* If failure getting class name. */
  if (unlikely(cur->className == NULL)) {
    /* Adding empty to list is deny. */
    PRINT_WARN_MSG("Couldn't allocate class name!");
    free(cur);
    return NULL;
  }
  cur->classNameLen = strlen(cur->className);
  
  /* Chain setting. */
  cur->klassOop = klassOop;

  /* push to classMap */
  TObjectData *result = this->pushNewClass(klassOop, cur);
  if(result != cur){
    free(cur);
  }

  return result;
}

/*!
 * \brief Remove class from container.
 * \param target [in] Remove class data.
 */
void TClassContainer::popClass(TObjectData *target) {
  
  SpinLockWait(&lockval);
  {
    classMap->erase(target->klassOop);
  }
  SpinLockRelease(&lockval);

  /* Broadcast to TLS Container */
  for(TLocalClassContainer::iterator itr = classContainerInTLS.begin();
    itr != classContainerInTLS.end(); itr++){
    SpinLockWait(&(*itr)->lockval);
    {
      (*itr)->classMap->erase(target->klassOop);
    }
    SpinLockRelease(&(*itr)->lockval);
  }
  
  /* Free allocated memory. */
  free(target->className);
  free(target);
}

/*!
 * \brief Remove all-class from container.
 */
void TClassContainer::allClear(void) {

  /* Broadcast to TLS Container */
  SpinLockWait(&tlsLock);
  {
    for(TLocalClassContainer::iterator itr = classContainerInTLS.begin();
      itr != classContainerInTLS.end(); itr++){
      SpinLockWait(&(*itr)->lockval);
      {
        (*itr)->classMap->clear();
      }
      SpinLockRelease(&(*itr)->lockval);
    }
  }
  SpinLockRelease(&tlsLock);

  SpinLockWait(&lockval);
  {
    /* Free allocated memory. */
    for (TClassMap::iterator cur = classMap->begin();
      cur != classMap->end(); ++cur) {
      TObjectData *pos = (*cur).second;
    
      free(pos->className);
      free(pos);
    }
  
    /* Clear all class. */
    classMap->clear();
  }
  SpinLockRelease(&lockval);

  SpinLockWait(&pendingQueueLock);
  {
    pendingQueue.clear();
  }
  SpinLockRelease(&pendingQueueLock);

}

/*!
 * \brief Remove all object data in pending queue.
 */
void TClassContainer::removePendingObjectData(void){
  SpinLockWait(&pendingQueueLock);
  {
    while(!pendingQueue.empty()){
      popClass(pendingQueue.front());
      pendingQueue.pop_front();
    }
  }
  SpinLockRelease(&pendingQueueLock);
}

/*!
 * \brief Comparator for sort by usage order.
 * \param *arg1 [in] Compare target A.
 * \param *arg2 [in] Compare target B.
 * \return Compare result.
 */
int HeapUsageCmp(const void *arg1, const void *arg2) {
  jlong cmp = ((THeapDelta*)arg2)->usage - ((THeapDelta*)arg1)->usage;
  if (cmp > 0) {
    /* arg2 is bigger than arg1. */
    return -1;
  } else if (cmp < 0) {
    /* arg1 is bigger than arg2. */
    return  1;
  } else {
    /* arg2 is equal arg1. */
    return  0;
  }
}

/*!
 * \brief Comparator for sort by delta order.
 * \param *arg1 [in] Compare target A.
 * \param *arg2 [in] Compare target B.
 * \return Compare result.
 */
int HeapDeltaCmp(const void *arg1, const void *arg2) {
  jlong cmp = ((THeapDelta*)arg2)->delta - ((THeapDelta*)arg1)->delta;
  if (cmp > 0) {
    /* arg2 is bigger than arg1. */
    return -1;
  } else if (cmp < 0) {
    /* arg1 is bigger than arg2. */
    return  1;
  } else {
    /* arg2 is equal arg1. */
    return  0;
  }
}

/*!
 * \brief Send memory usage information by SNMP trap.
 * \param pSender       [in] SNMP trap sender.
 * \param type          [in] This alert type.
 * \param occurred_time [in] Datetime of this alert is occurred.
 * \param usage         [in] Java heap usage.
 * \param maxCapacity   [in] Max capacity of Java heap.
 * \return Process result.
 */
inline bool sendMemoryUsageAlertTrap(TTrapSender *pSender,
                         TMemoryUsageAlertType type, jlong occurred_time,
                                                jlong usage, jlong maxCapacity){
  /* Setting trap information. */
  char paramStr[256];
  const char *trapOID;
  oid *alert_date;
  oid *alert_usage;
  oid *alert_capacity;
  int oid_len = 0;

  switch(type){

    case ALERT_JAVA_HEAP:
      trapOID        = OID_JAVAHEAPALERT;
      alert_date     = OID_JAVAHEAPALERT_DATE;
      alert_usage    = OID_JAVAHEAPALERT_USAGE;
      alert_capacity = OID_JAVAHEAPALERT_MAX_CAPACITY;
      oid_len = OID_LENGTH(OID_JAVAHEAPALERT_DATE);
      break;

    default:
      PRINT_CRIT_MSG("Unknown alert type!");
      return false;

  }

  bool result = true;

  try{
    /* Setting sysUpTime */
    pSender->setSysUpTime();

    /* Setting trapOID. */
    pSender->setTrapOID(trapOID);

    /* Set time of occurring this alert. */
    sprintf(paramStr, JLONG_FORMAT_STR, occurred_time);
    pSender->addValue(alert_date, oid_len, paramStr, SNMP_VAR_TYPE_COUNTER64);

    /* Set java heap usage. */
    sprintf(paramStr, JLONG_FORMAT_STR, usage);
    pSender->addValue(alert_usage, oid_len, paramStr, SNMP_VAR_TYPE_COUNTER64);

    /* Set max capacity of java heap. */
    sprintf(paramStr, JLONG_FORMAT_STR, maxCapacity);
    pSender->addValue(alert_capacity, oid_len, paramStr, SNMP_VAR_TYPE_COUNTER64);

    /* Send trap. */
    if(unlikely(pSender->sendTrap() != SNMP_PROC_SUCCESS)){
      /* Clean up. */
      pSender->clearValues();
      throw 1;
    }

  }
  catch(...){
    result = false;
  }

  return result;
}

/*!
 * \brief Output all-class information to file.
 * \param snapshot [in] Snapshot instance.
 * \return Sorted-class size information.
 */
TSorter<THeapDelta> *TClassContainer::afterTakeSnapShot(
  TSnapShotContainer *snapshot) {
  
  /* Sanity check. */
  if (unlikely(snapshot == NULL)) {
    return NULL;
  }
  
  /* Copy header. */
  TSnapShotFileHeader hdr;
  memcpy(&hdr, (const void *)snapshot->getHeader(), sizeof(TSnapShotFileHeader));

  /* If java heap usage alert is enable. */
  if(arg.HeapAlertThreshold > 0){
    jlong usage = hdr.newAreaSize + hdr.oldAreaSize;

    if(usage > arg.HeapAlertThreshold){
      /* Raise alert. */
      PRINT_WARN_MSG_HEADER
                << "ALERT: Java heap usage exceeded the threshold ("
                              << usage / 1024 / 1024 << " MB)" << NEWLINE;

      /* If need send trap. */
      if(arg.snmpSend){
        if(unlikely(!sendMemoryUsageAlertTrap(pSender, ALERT_JAVA_HEAP,
                            hdr.snapShotTime, usage, jvmInfo->getMaxMemory()))){
          PRINT_WARN_MSG("SNMP trap send failed!");
        }
      }

    }

  }
  
  /* Allocate return array. */
  jlong rankCnt = this->classMap->size();
  rankCnt = (rankCnt < arg.RankLevel) ? rankCnt : arg.RankLevel;
  
  /* Make controller to sort. */
  register TRankOrder order = arg.order;
  TSorter<THeapDelta> *sortArray;
  try {
    sortArray = new TSorter<THeapDelta>(rankCnt,
      (TComparator)((order == DELTA) ? &HeapDeltaCmp : &HeapUsageCmp));
  } catch (...) {
    PRINT_WARN_MSG("Couldn't allocate working memory!");
    return NULL;
  }
  
  /* Open file and seek EOF. */
  
  int fd = open(arg.FileName, O_CREAT | O_WRONLY,
                                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  /* If failure open file. */
  if (unlikely(fd < 0)) {
    PRINT_WARN_MSG("Counldn't open snapshot file.");
    delete sortArray;
    return NULL;
  }
  
  /* Move position to EOF. */
  off_t ofs = lseek(fd, 0, SEEK_END);
  /* If failure seek. */
  if (unlikely(ofs < 0)) {
    PRINT_WARN_MSG("Counldn't open snapshot file.");
    close(fd);
    delete sortArray;
    return NULL;
  }
  
  /* Frist, Output each classes information. Secondly output header. */
  lseek(fd,
      sizeof(TSnapShotFileHeader) - sizeof(char[80]) + hdr.gcCauseLen, SEEK_CUR);
  
  /* Output class information. */
  
  THeapDelta result;
  jlong numEntries = 0L;
  jlong clsCount = 0L;
  register jlong AlertThreshold = arg.AlertThreshold;

  TClassMap *workClassMap = NULL;
  SpinLockWait(&lockval);
  {
    workClassMap = new TClassMap(*classMap);
  }
  SpinLockRelease(&lockval);
  
  /* Loop each class. */
  for (TClassMap::iterator it = workClassMap->begin();
    it != workClassMap->end(); ++it, clsCount++) {
    
    TObjectData *objData = (*it).second;
    TObjectCounter *cur = snapshot->findClass(objData);
    if (unlikely(cur == NULL)) {
      cur = snapshot->pushNewClass(objData);
      
      if (unlikely(cur == NULL)) {
        PRINT_WARN_MSG("Couldn't allocate working memory!");
        close(fd);
        delete sortArray;
        return NULL;
      }
    }
    
    /* Calculate uasge and delta size. */
    result.usage = cur->total_size;
    result.delta = cur->total_size - objData->oldTotalSize;
    result.tag   = objData->tag;
    objData->oldTotalSize = result.usage;
    
    /* If do output class. */
    if (!arg.reduceSnapShot || (result.usage > 0)) {
      
      /* Output class-information. */
      
      write(fd, objData, sizeof(jlong) << 1);
      /* TObjectData.tag & TObjectData.classNameLen. */
      
      write(fd, objData->className, objData->classNameLen);
      write(fd, cur, sizeof(TObjectCounter));
      
      numEntries++;
    }
    
    /* Ranking sort. */
    sortArray->push(result);
    
    /* If alert is enable. */
    if (AlertThreshold > 0) {
      /* Variable for send trap. */
      int sendFlag = 0;
      
      try {
        
        /* If size is bigger more limit size. */
        if ((order == DELTA) && (AlertThreshold <= result.delta)) {
          
          /* Raise alert. */
          PRINT_WARN_MSG_HEADER
            << "ALERT(DELTA): \"" << objData->className
            << "\" exceeded the threshold"
            << " (" << result.delta << " bytes)"
            << NEWLINE;
          
          /* Set need send trap flag. */
          sendFlag = 1;
        }
        else if ((order == USAGE) && (AlertThreshold <= result.usage)) {
          
          /* Raise alert. */
          PRINT_WARN_MSG_HEADER
            << "ALERT(USAGE): \"" << objData->className
            << "\" exceeded the threshold"
            << " (" << result.usage << " bytes)"
            << NEWLINE;
          
          /* Set need send trap flag. */
          sendFlag = 1;
        }
        
        /* If need send trap. */
        if (arg.snmpSend && sendFlag != 0) {
          
          /* Setting trap information. */
          char paramStr[256];
          /* Trap OID. */
          char trapOID[50] = OID_HEAPALERT;
          
          /* Setting sysUpTime */
          pSender->setSysUpTime();
          
          /* Setting trapOID. */
          pSender->setTrapOID(trapOID);
          
          if (order == USAGE) {
            /* Set size kind. */
            pSender->addValue(OID_ALERT_SIZE_KIND,
                                   OID_LENGTH(OID_ALERT_SIZE_KIND),
                                           ORDER_USAGE, SNMP_VAR_TYPE_STRING);
            
            sprintf(paramStr, JLONG_FORMAT_STR, result.usage);
          } else {
            /* Set size kind. */
            pSender->addValue(OID_ALERT_SIZE_KIND,
                                   OID_LENGTH(OID_ALERT_SIZE_KIND),
                                           ORDER_DELTA, SNMP_VAR_TYPE_STRING);
            
            sprintf(paramStr, JLONG_FORMAT_STR, result.delta);
          }
          
          /* Set alert size. */
          pSender->addValue(OID_ALERT_CLS_SIZE,
                                   OID_LENGTH(OID_ALERT_CLS_SIZE),
                                           paramStr, SNMP_VAR_TYPE_COUNTER64);
          
          /* Set now time. */
          sprintf(paramStr, JLONG_FORMAT_STR, getNowTimeSec());
          pSender->addValue(OID_ALERT_DATE,
                                   OID_LENGTH(OID_ALERT_DATE),
                                           paramStr, SNMP_VAR_TYPE_COUNTER64);
          
          /* Set class-name. */
          pSender->addValue(OID_ALERT_CLS_NAME,
                                   OID_LENGTH(OID_ALERT_CLS_NAME),
                                       objData->className, SNMP_VAR_TYPE_STRING);
          
          /* Set instance count. */
          sprintf(paramStr, JLONG_FORMAT_STR, cur->count);
          pSender->addValue(OID_ALERT_CLS_COUNT,
                                   OID_LENGTH(OID_ALERT_CLS_COUNT),
                                           paramStr, SNMP_VAR_TYPE_COUNTER64);
          
          /* Send trap. */
          if (unlikely(pSender->sendTrap() != SNMP_PROC_SUCCESS)) {
            /* Ouput message and clean up. */
            PRINT_WARN_MSG("Send SNMP trap failed!");
            pSender->clearValues();
          }
        }
      } catch(...) {
        PRINT_WARN_MSG("Send SNMP trap failed!");
      }
    }
  }

  delete workClassMap;
  
  /* Move header position. */
  lseek(fd, ofs, SEEK_SET);
  
  /* Set output entry count. */
  hdr.size = numEntries;
  
  /* Write header param before GC-cause. */
  write(fd, &hdr, offsetof(TSnapShotFileHeader, gcCause));
  
  /* Write GC-cause. */
  write(fd, hdr.gcCause, hdr.gcCauseLen);
  
  /* Write header param after gccause. */
  write(fd, &hdr.FGCCount,
    sizeof(TSnapShotFileHeader) - offsetof(TSnapShotFileHeader, FGCCount));
  
  /* Clean up. */
  
  close(fd);
  
  return sortArray;
}