view agent/src/heapstats-engines/snapShotProcessor.cpp @ 236:84f67233da38

Bug 3406: [TEST]Add race condition tests for core components Reviewed-by: yasuenag https://github.com/HeapStats/heapstats/pull/103
author KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
date Thu, 22 Jun 2017 09:55:19 +0900
parents aba6d9899517
children 1885f234b75b
line wrap: on
line source

/*!
 * \file snapShotProcessor.cpp
 * \brief This file is used to output ranking and call snapshot function.
 * 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 "globals.hpp"
#include "elapsedTimer.hpp"
#include "fsUtil.hpp"
#include "snapShotProcessor.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);
      }

RACE_COND_DEBUG_POINT:
      /* 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 (conf->RankLevel()->get() > 0) {
          controller->showRanking(snapshot->getHeader(), ranking);
        }
      }

      /* Clean up. */
      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. */
  switch (hdr->cause) {
    case GC:
      logger->printInfoMsg("Heap Ranking at %s (caused by GC, GCCause: %s)",
                           time_str, hdr->gcCause);
      break;

    case DataDumpRequest:
      logger->printInfoMsg("Heap Ranking at %s (caused by DataDumpRequest)",
                           time_str);
      break;

    case Interval:
      logger->printInfoMsg("Heap Ranking at %s (caused by Interval)");
      break;

    default:
      /* Illegal value. */
      logger->printInfoMsg("Heap Ranking at %s (caused by UNKNOWN)");
      logger->printWarnMsg("Illegal snapshot cause!");
      return;
  }

  /* Output ranking column. */
  logger->printInfoMsg("Rank    usage(byte)    increment(byte)  Class name");
  logger->printInfoMsg("----  ---------------  ---------------  ----------");

  /* 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. */
#ifdef LP64
    logger->printInfoMsg("%4d  %15ld  %15ld  %s",
#else
    logger->printInfoMsg("%4d  %15lld  %15lld  %s",
#endif
                         Cnt + 1, aNode->value.usage, aNode->value.delta,
                         ((TObjectData *)aNode->value.tag)->className);
  }

  /* Clean up after ranking output. */
  logger->flush();
}