view agent/src/heapstats-engines/snapShotContainer.cpp @ 261:3215a746cc9d

Bug 3515: Cannot build HeapStats Agent on CentOS 6 after merging TBB Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/131
author Yasumasa Suenaga <yasuenag@gmail.com>
date Mon, 05 Feb 2018 16:03:30 +0900
parents 9a13d070bb77
children
line wrap: on
line source

/*!
 * \file snapshotContainer.cpp
 * \brief This file is used to add up using size every class.
 * 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 <utility>
#include <algorithm>

#include "globals.hpp"
#include "snapShotContainer.hpp"
#include "classContainer.hpp"

/*!
 * \brief Snapshot container instance stock queue.
 */
tbb::concurrent_queue<TSnapShotContainer *>
                                 *TSnapShotContainer::stockQueue = NULL;

/*!
 * \brief Set of active TSnapShotContainer set
 */
TActiveSnapShots TSnapShotContainer::activeSnapShots;

/*!
 * \brief Initialize snapshot caontainer class.
 * \return Is process succeed.
 * \warning Please call only once from main thread.
 */
bool TSnapShotContainer::globalInitialize(void) {
  try {
    /* Create snapshot container storage. */
    stockQueue = new TSnapShotQueue();
  } catch (...) {
    logger->printWarnMsg("Failure initialize snapshot container.");
    return false;
  }

  return true;
}

/*!
 * \brief Finalize snapshot caontainer class.
 * \warning Please call only once from main thread.
 */
void TSnapShotContainer::globalFinalize(void) {
  if (likely(stockQueue != NULL)) {
    TSnapShotContainer *item;

    /* Clear snapshots in queue. */
    while (stockQueue->try_pop(item)) {
      /* Deallocate snapshot instance. */
      delete item;
    }

    /* 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;
  if (!stockQueue->try_pop(result)) {
    /* Create new snapshot container instance. */
    result = new TSnapShotContainer();
    TActiveSnapShots::accessor acc;
    activeSnapShots.insert(acc, result);
  }

  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;
  }

  /*
   * unsafe_size() might return actual size if it is accessed concurrently.
   * However we use this function because we can use to decide cache count.
   * SnapShot cache means best effort.
   */
  if (likely(stockQueue->unsafe_size() < MAX_STOCK_COUNT)) {
    /* Clear data. */
    instance->clear(false);
    /* Store instance. */
    stockQueue->push(instance);
  } else {
    /* Deallocate instance. */
    activeSnapShots.erase(instance);
    delete instance;
  }
}

/*!
 * \brief TSnapshotContainer constructor.
 */
TSnapShotContainer::TSnapShotContainer(void) : counterMap(), childrenMap() {
  /* Header setting. */
  this->_header.magicNumber = conf->CollectRefTree()->get()
                                ? EXTENDED_REFTREE_SNAPSHOT
                                : EXTENDED_SNAPSHOT;
  this->_header.byteOrderMark = BOM;
  this->_header.snapShotTime = 0;
  this->_header.size = 0;
  memset((void *)&this->_header.gcCause[0], 0, 80);

  this->isCleared = true;
}

/*!
 * \brief TSnapshotContainer destructor.
 */
TSnapShotContainer::~TSnapShotContainer(void) {
  /* Cleanup elements on counter map. */
  for (auto itr = counterMap.begin(); itr != counterMap.end(); itr++) {
    TClassCounter *clsCounter = itr->second;
    free(clsCounter->offsets);
    free(clsCounter->counter);
    free(clsCounter);
  }

  /* Cleanup elements on children map. */
  for (auto itr = childrenMap.begin(); itr != childrenMap.end(); itr++) {
    TChildClassCounter *childCounter = itr->second;
    free(childCounter->counter);
    free(childCounter);
  }
}

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

  cur = (TClassCounter *)calloc(1, sizeof(TClassCounter));
  /* If failure allocate counter data. */
  if (unlikely(cur == NULL)) {
    /* Adding empty to list is deny. */
    logger->printWarnMsg("Couldn't allocate counter memory!");
    return NULL;
  }
  cur->offsetCount = -1;

  int ret = posix_memalign(
      (void **)&cur->counter, 16,
      sizeof(TObjectCounter) /* sizeof(TObjectCounter) == 16. */);
  /* If failure allocate counter. */
  if (unlikely(ret != 0)) {
    /* Adding empty to list is deny. */
    logger->printWarnMsg("Couldn't allocate counter memory!");
    free(cur);
    return NULL;
  }

  this->clearObjectCounter(cur->counter);

  /* Set to counter map. */
  TSizeMap::accessor acc;
  if (!counterMap.insert(acc, std::make_pair(objData, cur))) {
    free(cur->counter);
    free(cur);
    cur = NULL;
  }

  return acc->second;
}

/*!
 * \brief Append new-child-class to container.
 * \param clsCounter [in] Parent class counter object.
 * \param objData    [in] New-child-class key object.
 * \return New-class data.
 */
TChildClassCounter *TSnapShotContainer::pushNewChildClass(
    TClassCounter *clsCounter, TObjectData *objData) {
  TChildClassCounter *newCounter =
      (TChildClassCounter *)calloc(1, sizeof(TChildClassCounter));
  /* If failure allocate child class counter data. */
  if (unlikely(newCounter == NULL)) {
    return NULL;
  }

  int ret = posix_memalign(
      (void **)&newCounter->counter, 16,
      sizeof(TObjectCounter) /* sizeof(TObjectCounter) == 16. */);
  /* If failure allocate child class counter. */
  if (unlikely(ret != 0)) {
    free(newCounter);
    return NULL;
  }

  this->clearObjectCounter(newCounter->counter);
  newCounter->objData = objData;

  /* Set to children map. */
  TChildrenMapKey key = std::make_pair(clsCounter, objData->klassOop);
  TChildrenMap::accessor acc;
  TChildrenMap::value_type value = std::make_pair(key, newCounter);
  childrenMap.insert(acc, value);
  acc.release();

  /* Add new counter to children list */
  spinLockWait(&clsCounter->spinlock);
  {
    TChildClassCounter *counter = clsCounter->child;
    if (unlikely(counter == NULL)) {
      clsCounter->child = newCounter;
    } else {
      /* Get last counter. */
      while (counter->next != NULL) {
        counter = counter->next;
      }
      counter->next = newCounter;
    }
  }
  spinLockRelease(&clsCounter->spinlock);

  return newCounter;
}

/*!
 * \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)) {
    logger->printWarnMsg("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();
  this->_header.metaspaceUsage = info->getMetaspaceUsage();
  this->_header.metaspaceCapacity = info->getMetaspaceCapacity();
}

/*!
 * \brief Clear snapshot data.
 */
void TSnapShotContainer::clear(bool isForce) {
  if (!isForce && this->isCleared) {
    return;
  }

  /* Cleanup elements on counter map. */
  for (auto itr = counterMap.begin(); itr != counterMap.end(); itr++) {
    TClassCounter *clsCounter = itr->second;
    free(clsCounter->offsets);
    clsCounter->offsets = NULL;
    clsCounter->offsetCount = -1;

    /* Reset counters. */
    clearChildClassCounters(clsCounter);
  }
}

/*!
 * \brief Output GC statistics information.
 */
void TSnapShotContainer::printGCInfo(void) {
  logger->printInfoMsg("GC Statistics Information:");

  /* GC cause and GC worktime show only raised GC. */
  if (this->_header.cause == GC) {
    logger->printInfoMsg(
        "GC Cause: %s,  GC Worktime: " JLONG_FORMAT_STR " msec",
        (char *)this->_header.gcCause, this->_header.gcWorktime);
  }

  /* Output GC count. */
  logger->printInfoMsg("GC Count:  FullGC: " JLONG_FORMAT_STR
                       " / Young GC: " JLONG_FORMAT_STR,
                       this->_header.FGCCount, this->_header.YGCCount);

  /* Output heap size status. */
  logger->printInfoMsg("Area using size:  New: " JLONG_FORMAT_STR
                       " bytes"
                       " / Old: " JLONG_FORMAT_STR
                       " bytes"
                       " / Total: " JLONG_FORMAT_STR " bytes",
                       this->_header.newAreaSize, this->_header.oldAreaSize,
                       this->_header.totalHeapSize);

  /* Output metaspace size status. */
  const char *label =
      jvmInfo->isAfterCR6964458() ? "Metaspace usage: " : "PermGen usage: ";
  logger->printInfoMsg("%s " JLONG_FORMAT_STR
                       " bytes"
                       ", capacity: " JLONG_FORMAT_STR "  bytes",
                       label, this->_header.metaspaceUsage,
                       this->_header.metaspaceCapacity);
}

/*!
 * \brief Remove unloaded TObjectData in this snapshot container.
 *        This function should be called at safepoint.
 * \param unloadedList Set of unloaded TObjectData.
 */
void TSnapShotContainer::removeObjectData(TClassInfoSet &unloadedList) {
  /*
   * This function is called at safepoint.
   *   (from OnGarbageCollectionFinishForUnload() in classContainer.cpp)
   * So we can use *unsafe* iterator access in tbb::concurrent_queue.
   */
  for (auto itr = unloadedList.unsafe_begin();
       itr != unloadedList.unsafe_end(); itr++) {
    TSizeMap::const_accessor acc;
    if (counterMap.find(acc, *itr)) {
      TClassCounter *clsCounter = acc->second;
      counterMap.erase(acc);
      acc.release();

      TChildClassCounter *child = clsCounter->child;
      while (child != NULL) {
        TChildClassCounter *next = child->next;
        childrenMap.erase(std::make_pair(clsCounter, child->objData->klassOop));
        free(child->counter);
        free(child);
        child = next;
      }
    }
  }
}

/*!
 * \brief Remove unloaded TObjectData all active snapshot container.
 * \param unloadedList Set of unloaded TObjectData.
 */
void TSnapShotContainer::removeObjectDataFromAllSnapShots(
                                                 TClassInfoSet &unloadedList) {
  for (auto itr = activeSnapShots.begin();
       itr != activeSnapShots.end(); itr++) {
    itr->first->removeObjectData(unloadedList);
  }
}