changeset 233:1ac3803a35d1

Bug 3403: HeapStats Agent might crash if application exits with System.exit() Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/100
author Yasumasa Suenaga <yasuenag@gmail.com>
date Wed, 14 Jun 2017 16:30:31 +0900
parents b862390b22bc
children 07b92bcfe98d
files ChangeLog agent/src/heapstats-engines/heapstatsMBean.cpp agent/src/heapstats-engines/heapstatsMBean.hpp agent/src/heapstats-engines/libmain.cpp agent/src/heapstats-engines/logMain.cpp agent/src/heapstats-engines/snapShotMain.cpp agent/src/heapstats-engines/threadRecorder.cpp agent/src/heapstats-engines/threadRecorder.hpp agent/src/heapstats-engines/util.hpp configure configure.ac
diffstat 11 files changed, 294 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Jun 08 23:06:50 2017 +0900
+++ b/ChangeLog	Wed Jun 14 16:30:31 2017 +0900
@@ -1,3 +1,7 @@
+2017-06-14 Yasumasa Suenaga <yasuenag@gmail.com>
+
+	* Bug 3403: HeapStats Agent might crash if application exits with System.exit()
+
 2017-06-08 KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
 
 	* Bug 3399: Fix potential error when conflict between class loading and GC
--- a/agent/src/heapstats-engines/heapstatsMBean.cpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/heapstatsMBean.cpp	Wed Jun 14 16:30:31 2017 +0900
@@ -1,7 +1,7 @@
 /*!
  * \file heapstatsMBean.cpp
  * \brief JNI implementation for HeapStatsMBean.
- * Copyright (C) 2014-2016 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -22,12 +22,20 @@
 #include <jni.h>
 
 #include <limits.h>
+#include <sched.h>
 
 #include <list>
 
+#ifdef HAVE_ATOMIC
+#include <atomic>
+#else
+#include <cstdatomic>
+#endif
+
 #include "globals.hpp"
 #include "configuration.hpp"
 #include "heapstatsMBean.hpp"
+#include "util.hpp"
 
 /* Variables */
 static jclass mapCls = NULL;
@@ -50,6 +58,11 @@
 static jmethodID longValue = NULL;
 static jmethodID longValueOf = NULL;
 
+static jclass linkedCls = NULL;
+static volatile bool isLoaded = false;
+static std::atomic_int processing(0);
+
+
 /*!
  * \brief Raise Java Exception.
  *
@@ -301,7 +314,11 @@
     return;
   }
 
+  TProcessMark mark(processing);
+
   /* Initialize variables. */
+  linkedCls = (jclass)env->NewGlobalRef(cls);
+  isLoaded = true;
 
   /* For Map object */
   if (!prepareForMapObject(env)) {
@@ -328,6 +345,52 @@
   }
 }
 
+static void *JNIDummy(void) {
+  return NULL;
+}
+
+/*!
+ * \brief Unregister JNI functions in libheapstats.
+ * \param env Pointer of JNI environment.
+ */
+void UnregisterHeapStatsNatives(JNIEnv *env) {
+  if (!isLoaded) {
+    return;
+  }
+
+  /* Regist JNI functions */
+  JNINativeMethod methods[] = {
+      {(char *)"getHeapStatsVersion0",
+       (char *)"()Ljava/lang/String;",
+       (void *)JNIDummy},
+      {(char *)"getConfiguration0",
+       (char *)"(Ljava/lang/String;)Ljava/lang/Object;",
+       (void *)JNIDummy},
+      {(char *)"getConfigurationList0",
+       (char *)"()Ljava/util/Map;",
+       (void *)JNIDummy},
+      {(char *)"changeConfiguration0",
+       (char *)"(Ljava/lang/String;Ljava/lang/Object;)Z",
+       (void *)JNIDummy},
+      {(char *)"invokeLogCollection0",
+       (char *)"()Z",
+       (void *)JNIDummy},
+      {(char *)"invokeAllLogCollection0",
+       (char *)"()Z",
+       (void *)JNIDummy}};
+
+  if (env->RegisterNatives(linkedCls, methods, 6) != 0) {
+    raiseException(env, "java/lang/UnsatisfiedLinkError",
+                   "Could not unregister HeapStats native functions.");
+  }
+
+  env->DeleteGlobalRef(linkedCls);
+
+  while (processing > 0) {
+    sched_yield();
+  }
+}
+
 /*!
  * \brief Get HeapStats version string from libheapstats.
  *
@@ -336,6 +399,7 @@
  * \return Version string which is attached.
  */
 JNIEXPORT jstring JNICALL GetHeapStatsVersion(JNIEnv *env, jobject obj) {
+  TProcessMark mark(processing);
   jstring versionStr = env->NewStringUTF(PACKAGE_STRING
                                          " ("
 #ifdef SSE2
@@ -432,6 +496,7 @@
  */
 JNIEXPORT jobject JNICALL
     GetConfiguration(JNIEnv *env, jobject obj, jstring key) {
+  TProcessMark mark(processing);
   const char *opt = env->GetStringUTFChars(key, NULL);
   if (opt == NULL) {
     raiseException(env, "java/lang/RuntimeException",
@@ -467,6 +532,7 @@
  * \return Current configuration list.
  */
 JNIEXPORT jobject JNICALL GetConfigurationList(JNIEnv *env, jobject obj) {
+  TProcessMark mark(processing);
   jobject result = env->NewObject(mapCls, map_ctor);
   if (result == NULL) {
     raiseException(env, "java/lang/RuntimeException",
@@ -509,6 +575,7 @@
  */
 JNIEXPORT jboolean JNICALL
     ChangeConfiguration(JNIEnv *env, jobject obj, jstring key, jobject value) {
+  TProcessMark mark(processing);
   jclass valueCls = env->GetObjectClass(value);
   char *opt = (char *)env->GetStringUTFChars(key, NULL);
   if (opt == NULL) {
@@ -631,6 +698,7 @@
  * \return Result of this call.
  */
 JNIEXPORT jboolean JNICALL InvokeLogCollection(JNIEnv *env, jobject obj) {
+  TProcessMark mark(processing);
   int ret = logManager->collectLog(NULL, env, Signal,
                                    (TMSecTime)getNowTimeSec(), "JMX event");
   return ret == 0 ? JNI_TRUE : JNI_FALSE;
@@ -644,6 +712,7 @@
  * \return Result of this call.
  */
 JNIEXPORT jboolean JNICALL InvokeAllLogCollection(JNIEnv *env, jobject obj) {
+  TProcessMark mark(processing);
   int ret = logManager->collectLog(NULL, env, AnotherSignal,
                                    (TMSecTime)getNowTimeSec(), "JMX event");
   return ret == 0 ? JNI_TRUE : JNI_FALSE;
--- a/agent/src/heapstats-engines/heapstatsMBean.hpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/heapstatsMBean.hpp	Wed Jun 14 16:30:31 2017 +0900
@@ -1,7 +1,7 @@
 /*!
  * \file heapstatsMBean.hpp
  * \brief JNI implementation for HeapStatsMBean.
- * Copyright (C) 2014 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -42,4 +42,6 @@
 }
 #endif
 
+void UnregisterHeapStatsNatives(JNIEnv *env);
+
 #endif  // HEAPSTATSMBEAN_HPP
--- a/agent/src/heapstats-engines/libmain.cpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/libmain.cpp	Wed Jun 14 16:30:31 2017 +0900
@@ -381,11 +381,6 @@
     reloadSigMngr = NULL;
   }
 
-  /* Invoke JVM finalize event of snapshot function. */
-  onVMDeathForSnapShot(jvmti, env);
-  /* Invoke JVM finalize event of log function. */
-  onVMDeathForLog(jvmti, env);
-
   /* If agent is attaching now. */
   if (likely(conf->Attach()->get())) {
     /* Stop and disable each thread. */
@@ -396,6 +391,13 @@
                                 conf->ThreadRecordFileName()->get());
     }
   }
+
+  /* Invoke JVM finalize event of snapshot function. */
+  onVMDeathForSnapShot(jvmti, env);
+  /* Invoke JVM finalize event of log function. */
+  onVMDeathForLog(jvmti, env);
+  /* Unregister native functions for HeapStats MBean */
+  UnregisterHeapStatsNatives(env);
 }
 
 /*!
--- a/agent/src/heapstats-engines/logMain.cpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/logMain.cpp	Wed Jun 14 16:30:31 2017 +0900
@@ -20,6 +20,13 @@
  */
 
 #include <signal.h>
+#include <sched.h>
+
+#ifdef HAVE_ATOMIC
+#include <atomic>
+#else
+#include <cstdatomic>
+#endif
 
 #include "globals.hpp"
 #include "elapsedTimer.hpp"
@@ -69,6 +76,12 @@
 bool abortionByDeadlock = false;
 
 /*!
+ * \brief processing flag
+ */
+static std::atomic_int processing(0);
+
+
+/*!
  * \brief Take log information.
  * \param jvmti   [in] JVMTI environment object.
  * \param env     [in] JNI environment object.
@@ -134,6 +147,8 @@
  * \param env   [in] JNI environment object.
  */
 void intervalSigProcForLog(jvmtiEnv *jvmti, JNIEnv *env) {
+  TProcessMark mark(processing);
+
   /* If catch normal log signal. */
   if (unlikely(flagLogSignal != 0)) {
     TMSecTime nowTime = (TMSecTime)getNowTimeSec();
@@ -163,6 +178,8 @@
  *                   This value is always OccurredDeadlock.
  */
 void onOccurredDeadLock(jvmtiEnv *jvmti, JNIEnv *env, TInvokeCause cause) {
+  TProcessMark mark(processing);
+
   /* Get now date and time. */
   TMSecTime occurTime = TDeadlockFinder::getInstance()->getDeadlockTime();
 
@@ -190,6 +207,8 @@
 void JNICALL OnResourceExhausted(jvmtiEnv *jvmti, JNIEnv *env, jint flags,
                                  const void *reserved,
                                  const char *description) {
+  TProcessMark mark(processing);
+
   /* Raise alert. */
   logger->printCritMsg("ALERT(RESOURCE): resource was exhausted. info:\"%s\"",
                        description);
@@ -409,6 +428,15 @@
     delete logAllSignalMngr;
     logAllSignalMngr = NULL;
   }
+
+  /*
+   * ResourceExhausted, MonitorContendedEnter for Deadlock JVMTI event,
+   * all callee of TakeLogInfo() will not be started at this point.
+   * So we wait to finish all existed tasks.
+   */
+  while (processing > 0) {
+    sched_yield();
+  }
 }
 
 /*!
--- a/agent/src/heapstats-engines/snapShotMain.cpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/snapShotMain.cpp	Wed Jun 14 16:30:31 2017 +0900
@@ -21,6 +21,12 @@
 
 #include <sched.h>
 
+#ifdef HAVE_ATOMIC
+#include <atomic>
+#else
+#include <cstdatomic>
+#endif
+
 #include "globals.hpp"
 #include "vmFunctions.hpp"
 #include "elapsedTimer.hpp"
@@ -112,6 +118,12 @@
  */
 int classUnloadEventIdx = -1;
 
+/*!
+ * \brief processing flag
+ */
+static std::atomic_int processing(0);
+
+
 /* Function defines. */
 
 /*!
@@ -604,6 +616,7 @@
  *                   e.g. GC, DumpRequest or Interval.
  */
 void TakeSnapShot(jvmtiEnv *jvmti, JNIEnv *env, TInvokeCause cause) {
+  TProcessMark mark(processing);
   TVMVariables *vmVal = TVMVariables::getInstance();
 
   /*
@@ -851,6 +864,18 @@
   if (likely(classUnloadEventIdx >= 0)) {
     jvmti->SetExtensionEventCallback(classUnloadEventIdx, NULL);
   }
+
+  /* Wait until all tasks are finished. */
+  while (processing > 0) {
+    sched_yield();
+  }
+
+  /* Destroy object that is each snapshot trigger. */
+  delete gcWatcher;
+  gcWatcher = NULL;
+
+  delete timer;
+  timer = NULL;
 }
 
 /*!
@@ -926,13 +951,6 @@
   delete clsContainer;
   clsContainer = NULL;
 
-  /* Destroy object that is each snapshot trigger. */
-  delete gcWatcher;
-  gcWatcher = NULL;
-
-  delete timer;
-  timer = NULL;
-
   /* Finalize oop util. */
   oopUtilFinalize();
 }
--- a/agent/src/heapstats-engines/threadRecorder.cpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/threadRecorder.cpp	Wed Jun 14 16:30:31 2017 +0900
@@ -31,6 +31,13 @@
 #include <pthread.h>
 #include <sys/time.h>
 #include <sys/stat.h>
+#include <sched.h>
+
+#ifdef HAVE_ATOMIC
+#include <atomic>
+#else
+#include <cstdatomic>
+#endif
 
 #include "globals.hpp"
 #include "util.hpp"
@@ -51,6 +58,7 @@
 /* variables */
 jclass threadClass;
 jmethodID currentThreadMethod;
+static std::atomic_int processing(0);
 
 /* JVMTI event handler */
 
@@ -62,6 +70,7 @@
  * \param thread [in] jthread object which is created.
  */
 void JNICALL OnThreadStart(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) {
+  TProcessMark mark(processing);
   TThreadRecorder *recorder = TThreadRecorder::getInstance();
   recorder->registerNewThread(jvmti, thread);
   recorder->putEvent(thread, ThreadStart, 0);
@@ -75,6 +84,7 @@
  * \param thread [in] jthread object which is created.
  */
 void JNICALL OnThreadEnd(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) {
+  TProcessMark mark(processing);
   TThreadRecorder::getInstance()->putEvent(thread, ThreadEnd, 0);
 }
 
@@ -89,6 +99,7 @@
 void JNICALL
     OnMonitorContendedEnterForThreadRecording(jvmtiEnv *jvmti, JNIEnv *env,
                                               jthread thread, jobject object) {
+  TProcessMark mark(processing);
   TThreadRecorder::getInstance()->putEvent(thread, MonitorContendedEnter, 0);
 }
 
@@ -104,6 +115,7 @@
                                                          JNIEnv *env,
                                                          jthread thread,
                                                          jobject object) {
+  TProcessMark mark(processing);
   TThreadRecorder::getInstance()->putEvent(thread, MonitorContendedEntered, 0);
 }
 
@@ -118,6 +130,7 @@
  */
 void JNICALL OnMonitorWait(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread,
                            jobject object, jlong timeout) {
+  TProcessMark mark(processing);
   TThreadRecorder::getInstance()->putEvent(thread, MonitorWait, timeout);
 }
 
@@ -132,6 +145,7 @@
  */
 void JNICALL OnMonitorWaited(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread,
                              jobject object, jboolean timeout) {
+  TProcessMark mark(processing);
   TThreadRecorder::getInstance()->putEvent(thread, MonitorWaited, timeout);
 }
 
@@ -141,6 +155,7 @@
  * \param jvmti [in] JVMTI environment.
  */
 void JNICALL OnDataDumpRequestForDumpThreadRecordData(jvmtiEnv *jvmti) {
+  TProcessMark mark(processing);
   TThreadRecorder::inst->dump(conf->ThreadRecordFileName()->get());
 }
 
@@ -168,6 +183,7 @@
  * \param millis [in] Time of sleeping.
  */
 void JNICALL JVM_SleepPrologue(JNIEnv *env, jclass threadClass, jlong millis) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            ThreadSleepStart, millis);
@@ -181,6 +197,7 @@
  * \param millis [in] Time of sleeping.
  */
 void JNICALL JVM_SleepEpilogue(JNIEnv *env, jclass threadClass, jlong millis) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            ThreadSleepEnd, millis);
@@ -196,6 +213,7 @@
  */
 void JNICALL UnsafeParkPrologue(JNIEnv *env, jobject unsafe,
                                 jboolean isAbsolute, jlong time) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread, Park, time);
 }
@@ -210,17 +228,24 @@
  */
 void JNICALL UnsafeParkEpilogue(JNIEnv *env, jobject unsafe,
                                 jboolean isAbsolute, jlong time) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread, Unpark, time);
 }
 
 /* I/O tracer */
 
+/* Dummy method */
+void * JNICALL IoTrace_dummy(void) {
+  return NULL;
+}
+
 /*
  * Method:    socketReadBegin
  * Signature: ()Ljava/lang/Object;
  */
 JNIEXPORT jobject JNICALL IoTrace_socketReadBegin(JNIEnv *env, jclass cls) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            SocketReadStart, 0);
@@ -235,6 +260,7 @@
                                              jobject context, jobject address,
                                              jint port, jint timeout,
                                              jlong bytesRead) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            SocketReadEnd, bytesRead);
@@ -245,6 +271,7 @@
  * Signature: ()Ljava/lang/Object;
  */
 JNIEXPORT jobject JNICALL IoTrace_socketWriteBegin(JNIEnv *env, jclass cls) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            SocketWriteStart, 0);
@@ -258,6 +285,7 @@
 JNIEXPORT void JNICALL IoTrace_socketWriteEnd(JNIEnv *env, jclass cls,
                                               jobject context, jobject address,
                                               jint port, jlong bytesWritten) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            SocketWriteEnd, bytesWritten);
@@ -269,6 +297,7 @@
  */
 JNIEXPORT jobject JNICALL
     IoTrace_fileReadBegin(JNIEnv *env, jclass cls, jstring path) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            FileReadStart, 0);
@@ -281,6 +310,7 @@
  */
 JNIEXPORT void JNICALL IoTrace_fileReadEnd(JNIEnv *env, jclass cls,
                                            jobject context, jlong bytesRead) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            FileReadEnd, bytesRead);
@@ -292,6 +322,7 @@
  */
 JNIEXPORT jobject JNICALL
     IoTrace_fileWriteBegin(JNIEnv *env, jclass cls, jstring path) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            FileWriteStart, 0);
@@ -305,6 +336,7 @@
 JNIEXPORT void JNICALL IoTrace_fileWriteEnd(JNIEnv *env, jclass cls,
                                             jobject context,
                                             jlong bytesWritten) {
+  TProcessMark mark(processing);
   void *javaThread = GetCurrentThread(env);
   TThreadRecorder::getInstance()->putEvent((jthread)&javaThread,
                                            FileWriteEnd, bytesWritten);
@@ -526,6 +558,55 @@
 }
 
 /*!
+ * \brief Unegister I/O tracer.
+ *
+ * \param env [in] JNI environment.
+ */
+void TThreadRecorder::UnregisterIOTracer(JNIEnv *env) {
+  if (conf->ThreadRecordIOTracer()->get() == NULL) {
+    return;
+  }
+
+  jclass iotraceClass = env->FindClass("sun/misc/IoTrace");
+
+  if (iotraceClass == NULL) {
+    env->ExceptionClear();  // It may occur NoClassDefFoumdError
+    return;
+  }
+
+  /* Register hook native methods. */
+  JNINativeMethod methods[] = {
+      {(char *)"socketReadBegin",
+       (char *)"()Ljava/lang/Object;",
+       (void *)&IoTrace_dummy},
+      {(char *)"socketReadEnd",
+       (char *)"(Ljava/lang/Object;Ljava/net/InetAddress;IIJ)V",
+       (void *)&IoTrace_dummy},
+      {(char *)"socketWriteBegin",
+       (char *)"()Ljava/lang/Object;",
+       (void *)&IoTrace_dummy},
+      {(char *)"socketWriteEnd",
+       (char *)"(Ljava/lang/Object;Ljava/net/InetAddress;IJ)V",
+       (void *)&IoTrace_dummy},
+      {(char *)"fileReadBegin",
+       (char *)"(Ljava/lang/String;)Ljava/lang/Object;",
+       (void *)&IoTrace_dummy},
+      {(char *)"fileReadEnd",
+       (char *)"(Ljava/lang/Object;J)V",
+       (void *)&IoTrace_dummy},
+      {(char *)"fileWriteBegin",
+       (char *)"(Ljava/lang/String;)Ljava/lang/Object;",
+       (void *)&IoTrace_dummy},
+      {(char *)"fileWriteEnd",
+       (char *)"(Ljava/lang/Object;J)V",
+       (void *)&IoTrace_dummy}};
+
+  env->RegisterNatives(iotraceClass, methods, 8);
+
+  return;
+}
+
+/*!
  * \brief Initialize HeapStats Thread Recorder.
  *
  * \param jvmti [in] JVMTI environment.
@@ -626,6 +707,14 @@
   TJVMSleepCallback::switchCallback(env, false);
   TUnsafeParkCallback::switchCallback(env, false);
 
+  /* Stop to hook IoTrace */
+  UnregisterIOTracer(env);
+
+  /* Wait until all tasks are finished. */
+  while (processing > 0) {
+    sched_yield();
+  }
+
   /* Stop HeapStats Thread Recorder */
   inst->dump(fname);
 
--- a/agent/src/heapstats-engines/threadRecorder.hpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/threadRecorder.hpp	Wed Jun 14 16:30:31 2017 +0900
@@ -1,7 +1,7 @@
 /*!
  * \file threadRecorder.hpp
  * \brief Recording thread status.
- * Copyright (C) 2015 Yasumasa Suenaga
+ * Copyright (C) 2015-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -177,6 +177,13 @@
    */
   static bool registerIOTracer(jvmtiEnv *jvmti, JNIEnv *env);
 
+  /*!
+   * \brief Unegister I/O tracer.
+   *
+   * \param env [in] JNI environment.
+   */
+  static void UnregisterIOTracer(JNIEnv *env);
+
  public:
   /*!
    * \brief JVMTI callback for ThreadStart event.
--- a/agent/src/heapstats-engines/util.hpp	Thu Jun 08 23:06:50 2017 +0900
+++ b/agent/src/heapstats-engines/util.hpp	Wed Jun 14 16:30:31 2017 +0900
@@ -25,6 +25,12 @@
 #include <jvmti.h>
 #include <jni.h>
 
+#ifdef HAVE_ATOMIC
+#include <atomic>
+#else
+#include <cstdatomic>
+#endif
+
 #include <stddef.h>
 #include <errno.h>
 #include <string.h>
@@ -367,6 +373,20 @@
   size_t operator()(const T &val) const { return (size_t)val; };
 };
 
+/*!
+ * \brief Utility class for flag of processing.
+ */
+class TProcessMark {
+
+  private:
+    std::atomic_int &flag;
+
+  public:
+    TProcessMark(std::atomic_int &flg) : flag(flg) { flag++; }
+    ~TProcessMark() { flag--; }
+
+};
+
 /* CPU Specific utilities. */
 #ifdef AVX
 #include "arch/x86/avx/util.hpp"
--- a/configure	Thu Jun 08 23:06:50 2017 +0900
+++ b/configure	Wed Jun 14 16:30:31 2017 +0900
@@ -6471,6 +6471,38 @@
   USE_PCRE_FALSE=
 fi
 
+
+# Check for atomic operation
+for ac_header in atomic
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "atomic" "ac_cv_header_atomic" "$ac_includes_default"
+if test "x$ac_cv_header_atomic" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_ATOMIC 1
+_ACEOF
+
+else
+
+  for ac_header in cstdatomic
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "cstdatomic" "ac_cv_header_cstdatomic" "$ac_includes_default"
+if test "x$ac_cv_header_cstdatomic" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_CSTDATOMIC 1
+_ACEOF
+
+else
+  as_fn_error $? "Compiler does not support C++ atomic." "$LINENO" 5
+fi
+
+done
+
+
+fi
+
+done
+
+
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
--- a/configure.ac	Thu Jun 08 23:06:50 2017 +0900
+++ b/configure.ac	Wed Jun 14 16:30:31 2017 +0900
@@ -99,6 +99,14 @@
   ]
 )
 AM_CONDITIONAL(USE_PCRE, test -n "${ac_cv_header_pcre_h}")
+
+# Check for atomic operation
+AC_CHECK_HEADERS([atomic], [],
+[
+  AC_CHECK_HEADERS([cstdatomic], [],
+                        [AC_MSG_ERROR([Compiler does not support C++ atomic.])])
+])
+
 AC_LANG_POP([C++])
 
 # Checks for library header files.