view agent/src/snapShotContainer.cpp @ 28:0a4a349799b0

Bug 1593: HeapStats agent handles concurrent mode failure incorrectly. reviewed-by: ykubota
author Yasumasa Suenaga <suenaga.yasumasa@lab.ntt.co.jp>
date Thu, 21 Nov 2013 15:21:34 +0900
parents 5a930f8c514b
children
line wrap: on
line source

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

#include "snapShotContainer.hpp"
#include "util.hpp"
#include "trapSender.hpp"
#include "sorter.hpp"

/*!
 * \brief Pthread mutex for instance control.<br>
 * <br>
 * This mutex used in below process.<br>
 *   - TSnapShotContainer::getInstance @ snapShotContainer.cpp<br>
 *     To get older snapShotContainer instance from stockQueue.<br>
 *   - TSnapShotContainer::releaseInstance @ snapShotContainer.cpp<br>
 *     To add used snapShotContainer instance to stockQueue.<br>
 */
pthread_mutex_t TSnapShotContainer::instanceLocker =
                                       PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;

/*!
 * \brief Snapshot container instance stock queue.
 */
TSnapShotQueue *TSnapShotContainer::stockQueue = NULL;

/*!
 * \brief Initialize snapshot caontainer class.
 * \return Is process succeed.
 */
bool TSnapShotContainer::globalInitialize(void) {
  try {
    stockQueue = new TSnapShotQueue();
  } catch(...) {
    PRINT_WARN_MSG("Failure initialize snapshot container.");
    return false;
  }
  
  return true;
}

/*!
 * \brief Finalize snapshot caontainer class.
 */
void TSnapShotContainer::globalFinalize(void) {
  if (likely(stockQueue != NULL)) {
    
    /* Clear snapshot in queue. */
    while (!stockQueue->empty()) {
      TSnapShotContainer *item = stockQueue->front();
      
      /* Deallocate snapshot instance. */
      delete item;
      stockQueue->pop();
    }
    
    /* Deallocate stock queue. */
    delete stockQueue;
    stockQueue = NULL;
  }
}

/*!
 * \brief Get snapshot container instance.
 * \return Snapshot container instance.
 * \warning Don't deallocate instance getting by this function.<br>
 *          Please call "releaseInstance" method.
 */
TSnapShotContainer *TSnapShotContainer::getInstance(void) {
  
  TSnapShotContainer *result = NULL;
  bool isEmptyStock = true;
  
  ENTER_PTHREAD_SECTION(&instanceLocker) {
    isEmptyStock = stockQueue->empty();
  } EXIT_PTHREAD_SECTION(&instanceLocker)

  /* If need create new instance. */
  if (isEmptyStock) {
    
    /* Create new snapshot container instance. */
    try {
      result = new TSnapShotContainer();
    } catch(...) {
      result = NULL;
    }
  } else {
    ENTER_PTHREAD_SECTION(&instanceLocker) {
      
      /* Reuse snapshot container instance. */
      result = stockQueue->front();
      stockQueue->pop();
      
    } EXIT_PTHREAD_SECTION(&instanceLocker)
  }

  return result;
}

/*!
 * \brief Release snapshot container instance..
 * \param instance [in] Snapshot container instance.
 * \warning Don't access instance after called this function.
 */
void TSnapShotContainer::releaseInstance(TSnapShotContainer *instance) {
  
  /* Sanity check. */
  if (unlikely(instance == NULL)) {
    return;
  }

  unsigned int stockCount = MAX_STOCK_COUNT;
  ENTER_PTHREAD_SECTION(&instanceLocker) {
    stockCount = stockQueue->size();
  } EXIT_PTHREAD_SECTION(&instanceLocker)
  
  if (unlikely(stockCount >= MAX_STOCK_COUNT)) {
    
    /* Deallocate instance. */
    delete instance;
  } else {
    
    /* Clear data. */
    instance->clear(false);
    
    ENTER_PTHREAD_SECTION(&instanceLocker) {
      /* Keep instance. */
      stockQueue->push(instance);
    } EXIT_PTHREAD_SECTION(&instanceLocker)
  }

}

/*!
 * \brief TSnapshotContainer constructor.
 */
TSnapShotContainer::TSnapShotContainer() : counterMap(), sshotContainerMap() {
  
  /* Header setting. */
  this->_header.magicNumber   = MAGIC_NUM;
  this->_header.byteOrderMark = BOM;
  this->_header.snapShotTime  = 0;
  this->_header.size          = 0;
  memset((void*)&this->_header.gcCause[0], 0, 80);
  int ret = posix_memalign((void **)&this->counterBuf, 16, sizeof(TObjectCounter));

  if(ret != 0){
    throw ret;
  }

  this->isCleared = true;

  this->counterBuf->count = 1;
  tlsLock = 0;
  pthread_key_create(&tlsKey, NULL);
  pthread_key_create(&snapShotKey, NULL);
}

/*!
 * \brief TSnapshotContainer destructor.
 */
TSnapShotContainer::~TSnapShotContainer(void) {
  
  /* Cleanup elements on counter map. */
  for(TSizeMap::iterator it = counterMap.begin(); it != counterMap.end(); ++it){
    
    free((*it).second);
  }
  
  /* Clean counter map. */
  counterMap.clear();

  /* Cleanup elements on sshotContainerMap */
  for(TLocalSShotContainer::iterator itr = sshotContainerMap.begin();
                                      itr != sshotContainerMap.end(); itr++){
    delete itr->second;
  }
  sshotContainerMap.clear();

  free(this->counterBuf);

  pthread_key_delete(snapShotKey);
  pthread_key_delete(tlsKey);
}

/*!
 * \brief Append new-class to container.
 * \param objData [in] New-class key object.
 * \return New-class data.
 */
TObjectCounter *TSnapShotContainer::pushNewClass(TObjectData *objData) {
  TObjectCounter *cur = NULL;
  int ret;

  ret = posix_memalign((void **)&cur, 16, 16 /* = sizeof(TObjectCounter) : 8x2 */);

  /* If failure allocate. */
  if(unlikely(ret != 0)){
    /* Adding empty to list is deny. */
    PRINT_WARN_MSG("Couldn't allocate counter memory!");
    return NULL;
  }

  /* zero clear */
#ifdef AVX
  if(likely(usableAVX)){
    asm volatile("vpxor %%xmm0, %%xmm0, %%xmm0;"
                 "vmovdqa %%xmm0, (%0);"
                 : : "r" (cur) : "%xmm0");
  }
  else
#endif
#ifndef __amd64__
#ifdef SSE2
  if(unlikely(!usableSSE2))
#endif // SSE2
  {
    cur->count = 0;
    cur->total_size = 0;
  }
#ifdef SSE2
  else
#endif // SSE2
#endif // __amd64__
#ifdef SSE2
  {
    asm volatile("pxor %%xmm0, %%xmm0;"
                 "movdqa %%xmm0, (%0);"
                 : : "r" (cur) : "%xmm0");
  }
#endif // SSE2

  /* Set counter map. */
  counterMap[objData] = cur;

  return cur;
}

/*!
 * \brief Set JVM performance info to header.
 * \param info [in] JVM running performance information.
 */
void TSnapShotContainer::setJvmInfo(TJvmInfo *info) {
  /* Sanity check. */
  if (unlikely(info == NULL)) {
    PRINT_WARN_MSG("Couldn't get GC Information!");
    return;
  }
  
  /* If GC cause is need. */
  if (this->_header.cause == GC) {
    /* Copy GC cause. */
    strcpy((char*)this->_header.gcCause, info->getGCCause());
    this->_header.gcCauseLen = strlen((char*)this->_header.gcCause);
    
    /* Setting GC work time. */
    this->_header.gcWorktime = info->getGCWorktime();
  } else {
    /* Clear GC cause. */
    this->_header.gcCauseLen = 1;
    this->_header.gcCause[0] = '\0';
    
    /* GC no work. */
    this->_header.gcWorktime = 0;
  }
  
  /* Setting header info from TJvmInfo.
   * Total memory (JVM_TotalMemory) should be called from outside of GC.
   * Comment of VM_ENTRY_BASE macro says as following:
   *    ENTRY routines may lock, GC and throw exceptions
   * So we set TSnapShotFileHeader.totalHeapSize in TakeSnapShot() .
   */
  this->_header.FGCCount      = info->getFGCCount();
  this->_header.YGCCount      = info->getYGCCount();
  this->_header.newAreaSize   = info->getNewAreaSize();
  this->_header.oldAreaSize   = info->getOldAreaSize();
}

/*!
 * \brief Clear snapshot data.
 */
void TSnapShotContainer::clear(bool isForce){

  if(!isForce && this->isCleared){
    return;
  }

  /* Phase1: clear parent counter */

  for(TSizeMap::iterator it = counterMap.begin(); it != counterMap.end(); ++it){
    /* Reset counter of all class. */
#ifdef AVX
    if(likely(usableAVX)){
      asm volatile("vpxor %%xmm0, %%xmm0, %%xmm0;"
                   "vmovdqa %%xmm0, (%0);"
                   : : "r" (it->second) : "%xmm0");
    }
    else
#endif
#ifndef __amd64__
#ifdef SSE2
    if(unlikely(!usableSSE2))
#endif // SSE2
    {
      it->second->count = 0;
      it->second->total_size = 0;
    }
#ifdef SSE2
    else
#endif // SSE2
#endif // __amd64__
#ifdef SSE2
    {
      asm volatile("pxor %%xmm0, %%xmm0;"
                   "movdqa %%xmm0, (%0);"
                   : : "r" (it->second) : "%xmm0");
    }
#endif // SSE2

  }

  /* Phase2: clear counters in TLS */
  for(TLocalSShotContainer::iterator itr = this->sshotContainerMap.begin();
                                  itr != this->sshotContainerMap.end(); itr++){
    itr->second->clear(true);
  }

  this->isCleared = true;
}

/*!
 * \brief Output GC statistics information.
 */
void TSnapShotContainer::printGCInfo(void) {
  /* Check Loglevel. */
  if (arg.LogLevel < INFO) {
    return;
  }
  
  PRINT_INFO_MSG_HEADER_NOIF << "GC Statistics Information " << NEWLINE;
  
  /* GC cause and GC worktime show only raised GC. */
  if (this->_header.cause == GC) {
    PRINT_INFO_MSG_HEADER_NOIF
      << "GC Cause : '" << (char*)this->_header.gcCause << "' , "
      << "GC Worktime : " << this->_header.gcWorktime << "msec "
      << NEWLINE;
  }
  
  /* Output GC count. */
  PRINT_INFO_MSG_HEADER_NOIF
    << "GC Count FullGC:" << this->_header.FGCCount
    << " / YoungGC:" << this->_header.YGCCount << NEWLINE;
  
  /* Output heap size status. */
  PRINT_INFO_MSG_HEADER_NOIF
    << "Area using size New:" << this->_header.newAreaSize << "byte"
    << " / Old:" << this->_header.oldAreaSize << "byte"
    << " / Total:" << this->_header.totalHeapSize << "byte" << NEWLINE;
}

/*!
 * \brief Merge all sshotContainerMap data to this counterMap
 */
void TSnapShotContainer::mergeChildren(void){
  SpinLockWait(&tlsLock);
  {
    for(TLocalSShotContainer::iterator itr = sshotContainerMap.begin();
                                         itr != sshotContainerMap.end(); itr++){

      for(TSizeMap::iterator itr2 = itr->second->counterMap.begin();
                                 itr2 != itr->second->counterMap.end(); itr2++){
        TObjectCounter *objCounter = counterMap[itr2->first];

        if(objCounter == NULL){
          objCounter = pushNewClass(itr2->first);
        }

        FastInc(objCounter, itr2->second);
      }

    }

  }
  SpinLockRelease(&tlsLock);
}