changeset 51213:1596f418ffe4

8205096: Add missing files for 8203394 Summary: Add files missed in the push for Low-Overhead Heap Profiling Reviewed-by: dholmes, tschatzl, rehn, simonis, ehelin
author jcbeyler
date Fri, 15 Jun 2018 02:31:37 -0700
parents e2a7f431f65c
children eb0287b637bd
files src/hotspot/share/runtime/threadHeapSampler.cpp src/hotspot/share/runtime/threadHeapSampler.hpp test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorRecursiveTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
diffstat 28 files changed, 3163 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/threadHeapSampler.cpp	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/orderAccess.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/threadHeapSampler.hpp"
+
+// Cheap random number generator
+uint64_t ThreadHeapSampler::_rnd;
+// Default is 512kb.
+int ThreadHeapSampler::_sampling_rate = 512 * 1024;
+int ThreadHeapSampler::_enabled;
+
+// Statics for the fast log
+static const int FastLogNumBits = 10;
+static const int FastLogMask = (1 << FastLogNumBits) - 1;
+static double log_table[1<<FastLogNumBits];  // Constant
+static bool log_table_initialized;
+
+// Returns the next prng value.
+// pRNG is: aX+b mod c with a = 0x5DEECE66D, b =  0xB, c = 1<<48
+// This is the lrand64 generator.
+static uint64_t next_random(uint64_t rnd) {
+  const uint64_t PrngMult = 0x5DEECE66DLL;
+  const uint64_t PrngAdd = 0xB;
+  const uint64_t PrngModPower = 48;
+  const uint64_t PrngModMask = right_n_bits(PrngModPower);
+  //assert(IS_SAFE_SIZE_MUL(PrngMult, rnd), "Overflow on multiplication.");
+  //assert(IS_SAFE_SIZE_ADD(PrngMult * rnd, PrngAdd), "Overflow on addition.");
+  return (PrngMult * rnd + PrngAdd) & PrngModMask;
+}
+
+static double fast_log2(const double & d) {
+  assert(d>0, "bad value passed to assert");
+  uint64_t x = 0;
+  assert(sizeof(d) == sizeof(x),
+         "double and uint64_t do not have the same size");
+  x = *reinterpret_cast<const uint64_t*>(&d);
+  const uint32_t x_high = x >> 32;
+  assert(FastLogNumBits <= 20, "FastLogNumBits should be less than 20.");
+  const uint32_t y = x_high >> (20 - FastLogNumBits) & FastLogMask;
+  const int32_t exponent = ((x_high >> 20) & 0x7FF) - 1023;
+  return exponent + log_table[y];
+}
+
+// Generates a geometric variable with the specified mean (512K by default).
+// This is done by generating a random number between 0 and 1 and applying
+// the inverse cumulative distribution function for an exponential.
+// Specifically: Let m be the inverse of the sample rate, then
+// the probability distribution function is m*exp(-mx) so the CDF is
+// p = 1 - exp(-mx), so
+// q = 1 - p = exp(-mx)
+// log_e(q) = -mx
+// -log_e(q)/m = x
+// log_2(q) * (-log_e(2) * 1/m) = x
+// In the code, q is actually in the range 1 to 2**26, hence the -26 below
+void ThreadHeapSampler::pick_next_geometric_sample() {
+  _rnd = next_random(_rnd);
+  // Take the top 26 bits as the random number
+  // (This plus a 1<<58 sampling bound gives a max possible step of
+  // 5194297183973780480 bytes.  In this case,
+  // for sample_parameter = 1<<19, max possible step is
+  // 9448372 bytes (24 bits).
+  const uint64_t PrngModPower = 48;  // Number of bits in prng
+  // The uint32_t cast is to prevent a (hard-to-reproduce) NAN
+  // under piii debug for some binaries.
+  double q = static_cast<uint32_t>(_rnd >> (PrngModPower - 26)) + 1.0;
+  // Put the computed p-value through the CDF of a geometric.
+  // For faster performance (save ~1/20th exec time), replace
+  // min(0.0, FastLog2(q) - 26)  by  (Fastlog2(q) - 26.000705)
+  // The value 26.000705 is used rather than 26 to compensate
+  // for inaccuracies in FastLog2 which otherwise result in a
+  // negative answer.
+  double log_val = (fast_log2(q) - 26);
+  double result =
+      (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_sampling_rate())) + 1;
+  assert(result > 0 && result < SIZE_MAX, "Result is not in an acceptable range.");
+  size_t rate = static_cast<size_t>(result);
+  _bytes_until_sample = rate;
+}
+
+void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) {
+  if (get_sampling_rate() == 1) {
+    _bytes_until_sample = 1;
+    return;
+  }
+
+  pick_next_geometric_sample();
+
+  // Try to correct sample size by removing extra space from last allocation.
+  if (overflowed_bytes > 0 && _bytes_until_sample > overflowed_bytes) {
+    _bytes_until_sample -= overflowed_bytes;
+  }
+}
+
+void ThreadHeapSampler::check_for_sampling(HeapWord* ptr, size_t allocation_size, size_t bytes_since_allocation) {
+  oopDesc* oop = reinterpret_cast<oopDesc*>(ptr);
+  size_t total_allocated_bytes = bytes_since_allocation + allocation_size;
+
+  // If not yet time for a sample, skip it.
+  if (total_allocated_bytes < _bytes_until_sample) {
+    _bytes_until_sample -= total_allocated_bytes;
+    return;
+  }
+
+  JvmtiExport::sampled_object_alloc_event_collector(oop);
+
+  size_t overflow_bytes = total_allocated_bytes - _bytes_until_sample;
+  pick_next_sample(overflow_bytes);
+}
+
+void ThreadHeapSampler::init_log_table() {
+  MutexLockerEx mu(ThreadHeapSampler_lock, Mutex::_no_safepoint_check_flag);
+
+  if (log_table_initialized) {
+    return;
+  }
+
+  for (int i = 0; i < (1 << FastLogNumBits); i++) {
+    log_table[i] = (log(1.0 + static_cast<double>(i+0.5) / (1 << FastLogNumBits))
+                    / log(2.0));
+  }
+
+  log_table_initialized = true;
+}
+
+void ThreadHeapSampler::enable() {
+  // Done here to be done when things have settled. This adds a mutex lock but
+  // presumably, users won't be enabling and disabling all the time.
+  init_log_table();
+  OrderAccess::release_store(&_enabled, 1);
+}
+
+int ThreadHeapSampler::enabled() {
+  return OrderAccess::load_acquire(&_enabled);
+}
+
+void ThreadHeapSampler::disable() {
+  OrderAccess::release_store(&_enabled, 0);
+}
+
+int ThreadHeapSampler::get_sampling_rate() {
+  return OrderAccess::load_acquire(&_sampling_rate);
+}
+
+void ThreadHeapSampler::set_sampling_rate(int sampling_rate) {
+  OrderAccess::release_store(&_sampling_rate, sampling_rate);
+}
+
+// Methods used in assertion mode to check if a collector is present or not at
+// the moment of TLAB sampling, ie a slow allocation path.
+bool ThreadHeapSampler::sampling_collector_present() const {
+  return _collectors_present > 0;
+}
+
+bool ThreadHeapSampler::remove_sampling_collector() {
+  assert(_collectors_present > 0, "Problem with collector counter.");
+  _collectors_present--;
+  return true;
+}
+
+bool ThreadHeapSampler::add_sampling_collector() {
+  _collectors_present++;
+  return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/threadHeapSampler.hpp	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef RUNTIME_THREADHEAPSAMPLER_HPP
+#define RUNTIME_THREADHEAPSAMPLER_HPP
+
+#include "memory/allocation.hpp"
+
+class ThreadHeapSampler {
+ private:
+  size_t _bytes_until_sample;
+  // Cheap random number generator
+  static uint64_t _rnd;
+
+  void pick_next_geometric_sample();
+  void pick_next_sample(size_t overflowed_bytes = 0);
+  static int _enabled;
+  static int _sampling_rate;
+
+  // Used for assertion mode to determine if there is a path to a TLAB slow path
+  // without a collector present.
+  size_t _collectors_present;
+
+  static void init_log_table();
+
+ public:
+  ThreadHeapSampler() : _bytes_until_sample(0) {
+    _rnd = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this));
+    if (_rnd == 0) {
+      _rnd = 1;
+    }
+
+    _collectors_present = 0;
+  }
+
+  size_t bytes_until_sample()                    { return _bytes_until_sample;   }
+  void set_bytes_until_sample(size_t bytes)      { _bytes_until_sample = bytes;  }
+
+  void check_for_sampling(HeapWord* obj, size_t size_in_bytes, size_t bytes_allocated_before = 0);
+
+  static int enabled();
+  static void enable();
+  static void disable();
+
+  static void set_sampling_rate(int sampling_rate);
+  static int get_sampling_rate();
+
+  bool sampling_collector_present() const;
+  bool remove_sampling_collector();
+  bool add_sampling_collector();
+};
+
+#endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+class Frame {
+  Frame(String method, String signature, String fileName, int lineNumber) {
+    this.method = method;
+    this.signature = signature;
+    this.fileName = fileName;
+    this.lineNumber = lineNumber;
+  }
+
+  public String method;
+  public String signature;
+  public String fileName;
+  public int lineNumber;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** API for handling the underlying heap sampling monitoring system. */
+public class HeapMonitor {
+  private static int[][] arrays;
+  private static int allocationIterations = 1000;
+
+  static {
+    try {
+      System.loadLibrary("HeapMonitorTest");
+    } catch (UnsatisfiedLinkError ule) {
+      System.err.println("Could not load HeapMonitor library");
+      System.err.println("java.library.path: " + System.getProperty("java.library.path"));
+      throw ule;
+    }
+  }
+
+  /** Set a specific sampling rate, 0 samples every allocation. */
+  public native static void setSamplingRate(int rate);
+  public native static void enableSamplingEvents();
+  public native static boolean enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread);
+  public native static void disableSamplingEvents();
+
+  /**
+   * Allocate memory but first create a stack trace.
+   *
+   * @return list of frames for the allocation.
+   */
+  public static List<Frame> allocate() {
+    int sum = 0;
+    List<Frame> frames = new ArrayList<Frame>();
+    allocate(frames);
+    frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 58));
+    return frames;
+  }
+
+  private static void allocate(List<Frame> frames) {
+    int sum = 0;
+    for (int j = 0; j < allocationIterations; j++) {
+      sum += actuallyAllocate();
+    }
+    frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 93));
+    frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 66));
+  }
+
+  public static List<Frame> repeatAllocate(int max) {
+    List<Frame> frames = null;
+    for (int i = 0; i < max; i++) {
+      frames = allocate();
+    }
+    frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 75));
+    return frames;
+  }
+
+  private static int actuallyAllocate() {
+    int sum = 0;
+
+    // Let us assume that a 1-element array is 24 bytes of memory and we want
+    // 2MB allocated.
+    int iterations = (1 << 19) / 6;
+
+    if (arrays == null) {
+      arrays = new int[iterations][];
+    }
+
+    for (int i = 0; i < iterations; i++) {
+      int tmp[] = new int[1];
+      // Force it to be kept and, at the same time, wipe out any previous data.
+      arrays[i] = tmp;
+      sum += arrays[0][0];
+    }
+    return sum;
+  }
+
+  public static int allocateSize(int totalSize) {
+    int sum = 0;
+
+    // Let us assume that a 1-element array is 24 bytes.
+    int iterations = totalSize / 24;
+
+    if (arrays == null) {
+      arrays = new int[iterations][];
+    }
+
+    System.out.println("Allocating for " + iterations);
+    for (int i = 0; i < iterations; i++) {
+      int tmp[] = new int[1];
+
+      // Force it to be kept and, at the same time, wipe out any previous data.
+      arrays[i] = tmp;
+      sum += arrays[0][0];
+    }
+
+    return sum;
+  }
+
+  /** Remove the reference to the global array to free data at the next GC. */
+  public static void freeStorage() {
+    arrays = null;
+  }
+
+  public static int[][][] sampleEverything() {
+    enableSamplingEvents();
+    setSamplingRate(0);
+
+    // Loop around an allocation loop and wait until the tlabs have settled.
+    final int maxTries = 10;
+    int[][][] result = new int[maxTries][][];
+    for (int i = 0; i < maxTries; i++) {
+      final int maxInternalTries = 400;
+      result[i] = new int[maxInternalTries][];
+
+      resetEventStorage();
+      for (int j = 0; j < maxInternalTries; j++) {
+        final int size = 1000;
+        result[i][j] = new int[size];
+      }
+
+      int sampledEvents = sampledEvents();
+      if (sampledEvents == maxInternalTries) {
+        return result;
+      }
+    }
+
+    throw new RuntimeException("Could not set the sampler");
+  }
+
+  public native static int sampledEvents();
+  public native static boolean obtainedEvents(Frame[] frames);
+  public native static boolean garbageContains(Frame[] frames);
+  public native static boolean eventStorageIsEmpty();
+  public native static void resetEventStorage();
+  public native static int getEventStorageElementCount();
+  public native static void forceGarbageCollection();
+  public native static boolean enableVMEvents();
+
+  public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) {
+    double actual = getEventStorageElementCount();
+    double diffPercentage = Math.abs(actual - expected) / expected;
+    return diffPercentage < acceptedErrorPercentage;
+  }
+
+  public static void setAllocationIterations(int iterations) {
+    allocationIterations = iterations;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
+ * @compile HeapMonitorArrayAllSampledTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorArrayAllSampledTest
+ */
+
+public class HeapMonitorArrayAllSampledTest {
+
+  // Do 1000 iterations and expect maxIteration samples.
+  private static final int maxIteration = 1000;
+  private static int array[];
+
+  private static void allocate(int size) {
+    for (int j = 0; j < maxIteration; j++) {
+      array = new int[size];
+    }
+  }
+
+  public static void main(String[] args) {
+    int sizes[] = {1000, 10000, 100000, 1000000};
+
+    HeapMonitor.setSamplingRate(0);
+    HeapMonitor.enableSamplingEvents();
+
+    for (int currentSize : sizes) {
+      System.out.println("Testing size " + currentSize);
+
+      HeapMonitor.resetEventStorage();
+      allocate(currentSize);
+
+      // 10% error ensures a sanity test without becoming flaky.
+      // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+      // statistical geometric variable around the sampling rate. This means that the test could be
+      // unlucky and not achieve the mean average fast enough for the test case.
+      if (!HeapMonitor.statsHaveExpectedNumberSamples(maxIteration, 10)) {
+        throw new RuntimeException("Statistics should show about " + maxIteration + " samples.");
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies if turning off the event notification stops events.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorEventOnOffTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventOnOffTest
+ */
+public class HeapMonitorEventOnOffTest {
+  private static void checkNoEventsAreBeingSent() {
+    HeapMonitor.resetEventStorage();
+    HeapMonitor.repeatAllocate(5);
+
+    // Check that the data is not available while heap sampling is disabled.
+    boolean status = HeapMonitor.eventStorageIsEmpty();
+    if (!status) {
+      throw new RuntimeException("Storage is not empty after allocating with disabled events.");
+    }
+  }
+
+  private static void checkEventsAreBeingSent() {
+    List<Frame> frameList = HeapMonitor.repeatAllocate(5);
+
+    frameList.add(new Frame("checkEventsAreBeingSent", "()V", "HeapMonitorEventOnOffTest.java", 48));
+    Frame[] frames = frameList.toArray(new Frame[0]);
+
+    // Check that the data is available while heap sampling is enabled.
+    boolean status = HeapMonitor.obtainedEvents(frames);
+    if (!status) {
+      throw new RuntimeException("Failed to find the traces after allocating with enabled events.");
+    }
+  }
+
+  public static void main(String[] args) {
+    HeapMonitor.enableSamplingEvents();
+    checkEventsAreBeingSent();
+
+    // Disabling the notification system should stop events.
+    HeapMonitor.disableSamplingEvents();
+    checkNoEventsAreBeingSent();
+
+    // Enabling the notification system should start events again.
+    HeapMonitor.enableSamplingEvents();
+    checkEventsAreBeingSent();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor ThreadInformation
+ * @summary Ensures the JVMTI Heap Monitor is not thread enable (test to change when it becomes so)
+ * @compile HeapMonitorEventsForTwoThreadsTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest
+ */
+
+import java.util.List;
+
+public class HeapMonitorEventsForTwoThreadsTest {
+  public native static boolean checkSamples();
+
+  public static void main(String[] args) {
+    final int numThreads = 24;
+    List<ThreadInformation> threadList = ThreadInformation.createThreadList(numThreads);
+
+    Thread firstThread = threadList.get(0).getThread();
+    Thread secondThread = threadList.get(1).getThread();
+    if (HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread)) {
+      throw new RuntimeException("Sampling event is thread enabled, that is unexpected.");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor Statistics using CMS GC
+ * @build Frame HeapMonitor
+ * @requires vm.gc == "ConcMarkSweep" | vm.gc == "null"
+ * @compile HeapMonitorGCCMSTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseConcMarkSweepGC MyPackage.HeapMonitorGCTest
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor Statistics using ParallelGc
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorGCTest.java
+ * @requires vm.gc == "Parallel" | vm.gc == "null"
+ * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseParallelGC MyPackage.HeapMonitorGCTest
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor Statistics using SerialGC
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorGCTest.java
+ * @requires vm.gc == "Serial" | vm.gc == "null"
+ * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseSerialGC MyPackage.HeapMonitorGCTest
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the default GC with the Heap Monitor event system.
+ * @compile HeapMonitorGCTest.java
+ * @requires vm.gc == "G1" | vm.gc == "null"
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorGCTest
+ */
+
+/**
+ * This test is checking that various GCs work as intended: events are sent, forcing GC works, etc.
+ */
+public class HeapMonitorGCTest {
+  public static void main(String[] args) {
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Statistics should be null to begin with.");
+    }
+
+    HeapMonitor.enableSamplingEvents();
+
+    List<Frame> frameList = HeapMonitor.allocate();
+    frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorGCTest.java", 48));
+    Frame[] frames = frameList.toArray(new Frame[0]);
+
+    if (!HeapMonitor.obtainedEvents(frames)) {
+      throw new RuntimeException("No expected events were found.");
+    }
+
+    HeapMonitor.forceGarbageCollection();
+
+    if (!HeapMonitor.garbageContains(frames)) {
+      throw new RuntimeException("Forcing GC did not work, not a single object was collected.");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI SetHeapSamplingRate returns an illegal argument for negative ints.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorIllegalArgumentTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorIllegalArgumentTest
+ */
+
+public class HeapMonitorIllegalArgumentTest {
+  private native static int testIllegalArgument();
+
+  public static void main(String[] args) {
+    int result = testIllegalArgument();
+
+    if (result == 0) {
+      throw new RuntimeException("Test illegal argument failed.");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor using the interpreter.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest -Xint MyPackage.HeapMonitorTest 10
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor using the interpreter.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorStatObjectCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor API when allocating a multi-array.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorMultiArrayTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorMultiArrayTest
+ */
+public class HeapMonitorMultiArrayTest {
+
+  private static int[][] tab;
+  public static void main(String[] args) throws Exception {
+    // Set ourselves to sample everything.
+    HeapMonitor.sampleEverything();
+
+    // Do a few allocations now and see if the callback happens and the program finishes:
+    HeapMonitor.resetEventStorage();
+    int iterations = 1000;
+    int allocationsPerIteration = 6;
+    for (int i = 0; i < iterations; i++) {
+      tab = new int[5][5];
+    }
+
+    int sampledEvents = HeapMonitor.sampledEvents();
+    int expectedNumber = iterations * allocationsPerIteration;
+
+    if (sampledEvents != expectedNumber) {
+      throw new RuntimeException("Number of samples (" + sampledEvents + ") not the expected ("
+          + expectedNumber + ")");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor does not work without the required capability.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorNoCapabilityTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorNoCapabilityTest
+ */
+
+public class HeapMonitorNoCapabilityTest {
+  private native static int allSamplingMethodsFail();
+
+  public static void main(String[] args) {
+    int result = allSamplingMethodsFail();
+
+    if (result == 0) {
+      throw new RuntimeException("Some methods could be called without a capability.");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorRecursiveTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor API does not do infinite recursion.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorRecursiveTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorRecursiveTest
+ */
+public class HeapMonitorRecursiveTest {
+  private native static void setCallbackToCallAllocateSomeMore();
+  private native static boolean didCallback();
+  private static int[] tab;
+
+  public static void main(String[] args) throws Exception {
+    // Set ourselves to sample everything.
+    HeapMonitor.sampleEverything();
+
+    // Set a callback that does allocate more data. If a callback's allocations get sampled, this
+    // would do an infinite recursion.
+    setCallbackToCallAllocateSomeMore();
+
+    // Do a few allocations now and see if the callback happens and the program finishes:
+    for (int i = 0; i < 1000; i++) {
+      tab = new int[1024];
+    }
+
+    if (!didCallback()) {
+      throw new RuntimeException("Did not get a callback...");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
+ * @compile HeapMonitorStatArrayCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatArrayCorrectnessTest
+ */
+
+public class HeapMonitorStatArrayCorrectnessTest {
+
+  // Do 100000 iterations and expect maxIteration / multiplier samples.
+  private static final int maxIteration = 100000;
+  private static int array[];
+
+  private static void allocate(int size) {
+    for (int j = 0; j < maxIteration; j++) {
+      array = new int[size];
+    }
+  }
+
+  public static void main(String[] args) {
+    int sizes[] = {1000, 10000, 100000};
+
+    HeapMonitor.enableSamplingEvents();
+
+    for (int currentSize : sizes) {
+      System.out.println("Testing size " + currentSize);
+
+      HeapMonitor.resetEventStorage();
+      if (!HeapMonitor.eventStorageIsEmpty()) {
+        throw new RuntimeException("Should not have any events stored yet.");
+      }
+
+      // 111 is as good a number as any.
+      final int samplingMultiplier = 111;
+      HeapMonitor.setSamplingRate(samplingMultiplier * currentSize);
+
+      allocate(currentSize);
+
+      // For simplifications, we ignore the array memory usage for array internals (with the array
+      // sizes requested, it should be a negligible oversight).
+      //
+      // That means that with maxIterations, the loop in the method allocate requests:
+      //    maxIterations * currentSize * 4 bytes (4 for integers)
+      //
+      // Via the enable sampling, the code requests a sample every samplingMultiplier * currentSize bytes.
+      //
+      // Therefore, the expected sample number is:
+      //   (maxIterations * currentSize * 4) / (samplingMultiplier * currentSize);
+      double expected = maxIteration;
+      expected *= 4;
+      expected /= samplingMultiplier;
+
+      // 10% error ensures a sanity test without becoming flaky.
+      // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+      // statistical geometric variable around the sampling rate. This means that the test could be
+      // unlucky and not achieve the mean average fast enough for the test case.
+      if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) {
+        throw new RuntimeException("Statistics should show about " + expected + " samples.");
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor sampling via object allocation.
+ * @compile HeapMonitorStatObjectCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatObjectCorrectnessTest
+ */
+
+/** This test is checking the object allocation path works with heap sampling. */
+public class HeapMonitorStatObjectCorrectnessTest {
+
+  // Do 200000 iterations and expect maxIteration / multiplier samples.
+  private static final int maxIteration = 200000;
+  private static BigObject obj;
+
+  private native static boolean statsHaveExpectedNumberSamples(int expected, int percentError);
+
+  private static void allocate() {
+    for (int j = 0; j < maxIteration; j++) {
+      obj = new BigObject();
+    }
+  }
+
+  private static void testBigAllocationRate() {
+    final int sizeObject = 1400;
+
+    // 111 is as good a number as any.
+    final int samplingMultiplier = 111;
+    HeapMonitor.setSamplingRate(samplingMultiplier * sizeObject);
+
+    emptyStorage();
+    allocate();
+
+    // For simplifications, the code is allocating:
+    //   (BigObject size) * maxIteration.
+    //
+    // We ignore the class memory usage apart from field memory usage for BigObject. BigObject
+    // allocates 250 long, so 2000 bytes, so whatever is used for the class is negligible.
+    //
+    // That means that with maxIterations, the loop in the method allocate requests:
+    //    maxIterations * 2000 bytes.
+    //
+    // Via the enable sampling, the code requests a sample every samplingMultiplier * sizeObject bytes.
+    //
+    // Therefore, the expected sample number is:
+    //   (maxIterations * sizeObject) / (samplingMultiplier * sizeObject);
+    //
+    // Which becomes:
+    //   maxIterations / samplingMultiplier
+    double expected = maxIteration;
+    expected /= samplingMultiplier;
+
+    // 10% error ensures a sanity test without becoming flaky.
+    // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+    // statistical geometric variable around the sampling rate. This means that the test could be
+    // unlucky and not achieve the mean average fast enough for the test case.
+    if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) {
+      throw new RuntimeException("Statistics should show about " + expected + " samples.");
+    }
+  }
+
+  private static void emptyStorage() {
+    HeapMonitor.resetEventStorage();
+
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Statistics should be null to begin with.");
+    }
+  }
+
+  private static void testEveryAllocationSampled() {
+    // 0 means sample every allocation.
+    HeapMonitor.setSamplingRate(0);
+
+    emptyStorage();
+    allocate();
+
+    double expected = maxIteration;
+
+    // 10% error ensures a sanity test without becoming flaky.
+    // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+    // statistical geometric variable around the sampling rate. This means that the test could be
+    // unlucky and not achieve the mean average fast enough for the test case.
+    if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) {
+      throw new RuntimeException("Statistics should show about " + expected + " samples.");
+    }
+  }
+
+  public static void main(String[] args) {
+    HeapMonitor.enableSamplingEvents();
+
+    testBigAllocationRate();
+    testEveryAllocationSampled();
+  }
+
+  /**
+   * Big class on purpose to just be able to ignore the class memory space overhead.
+   *
+   * Class contains 175 long fields, so 175 * 8 = 1400 bytes.
+   */
+  private static class BigObject {
+    private long a0_0, a0_1, a0_2, a0_3, a0_4, a0_5, a0_6, a0_7, a0_8, a0_9;
+    private long a1_0, a1_1, a1_2, a1_3, a1_4, a1_5, a1_6, a1_7, a1_8, a1_9;
+    private long a2_0, a2_1, a2_2, a2_3, a2_4, a2_5, a2_6, a2_7, a2_8, a2_9;
+    private long a3_0, a3_1, a3_2, a3_3, a3_4, a3_5, a3_6, a3_7, a3_8, a3_9;
+    private long a4_0, a4_1, a4_2, a4_3, a4_4, a4_5, a4_6, a4_7, a4_8, a4_9;
+    private long a5_0, a5_1, a5_2, a5_3, a5_4, a5_5, a5_6, a5_7, a5_8, a5_9;
+    private long a6_0, a6_1, a6_2, a6_3, a6_4, a6_5, a6_6, a6_7, a6_8, a6_9;
+    private long a7_0, a7_1, a7_2, a7_3, a7_4, a7_5, a7_6, a7_7, a7_8, a7_9;
+    private long a8_0, a8_1, a8_2, a8_3, a8_4, a8_5, a8_6, a8_7, a8_8, a8_9;
+    private long a9_0, a9_1, a9_2, a9_3, a9_4, a9_5, a9_6, a9_7, a9_8, a9_9;
+    private long a10_0, a10_1, a10_2, a10_3, a10_4, a10_5, a10_6, a10_7, a10_8, a10_9;
+    private long a11_0, a11_1, a11_2, a11_3, a11_4, a11_5, a11_6, a11_7, a11_8, a11_9;
+    private long a12_0, a12_1, a12_2, a12_3, a12_4, a12_5, a12_6, a12_7, a12_8, a12_9;
+    private long a13_0, a13_1, a13_2, a13_3, a13_4, a13_5, a13_6, a13_7, a13_8, a13_9;
+    private long a14_0, a14_1, a14_2, a14_3, a14_4, a14_5, a14_6, a14_7, a14_8, a14_9;
+    private long a15_0, a15_1, a15_2, a15_3, a15_4, a15_5, a15_6, a15_7, a15_8, a15_9;
+    private long a16_0, a16_1, a16_2, a16_3, a16_4, a16_5, a16_6, a16_7, a16_8, a16_9;
+    private long a17_0, a17_1, a17_2, a17_3, a17_4;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor sampling rate average.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorStatRateTest.java
+ * @requires vm.compMode != "Xcomp"
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatRateTest
+ */
+
+public class HeapMonitorStatRateTest {
+
+  private native static double getAverageRate();
+
+  private static boolean testRateOnce(int rate, boolean throwIfFailure) {
+    HeapMonitor.resetEventStorage();
+    HeapMonitor.setSamplingRate(rate);
+
+    HeapMonitor.enableSamplingEvents();
+
+    int allocationTotal = 10 * 1024 * 1024;
+    HeapMonitor.allocateSize(allocationTotal);
+
+    HeapMonitor.disableSamplingEvents();
+
+    double actualCount = HeapMonitor.getEventStorageElementCount();
+    double expectedCount = allocationTotal / rate;
+
+    double error = Math.abs(actualCount - expectedCount);
+    double errorPercentage = error / expectedCount * 100;
+
+    boolean failure = (errorPercentage > 10.0);
+
+    if (failure && throwIfFailure) {
+      throw new RuntimeException("Rate average over 10% for rate " + rate + " -> " + actualCount
+          + ", " + expectedCount);
+    }
+
+    return failure;
+  }
+
+
+  private static void testRate(int rate) {
+    // Test the rate twice, it can happen that the test is "unlucky" and the rate just goes above
+    // the 10% mark. So try again to squash flakiness.
+    // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+    // statistical geometric variable around the sampling rate. This means that the test could be
+    // unlucky and not achieve the mean average fast enough for the test case.
+    if (!testRateOnce(rate, false)) {
+      testRateOnce(rate, true);
+    }
+  }
+
+  public static void main(String[] args) {
+    int[] tab = {1024, 8192};
+
+    for (int rateIdx = 0; rateIdx < tab.length; rateIdx++) {
+      testRate(tab[rateIdx]);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor events are only sent after enabling.
+ * @compile HeapMonitorStatSimpleTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatSimpleTest
+ */
+
+public class HeapMonitorStatSimpleTest {
+  private native static int areSamplingStatisticsZero();
+
+  public static void main(String[] args) {
+    HeapMonitor.allocate();
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Statistics should be null to begin with.");
+    }
+
+    HeapMonitor.enableSamplingEvents();
+    HeapMonitor.allocate();
+
+    if (HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Statistics should not be null now.");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor API
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorTest
+ */
+
+public class HeapMonitorTest {
+
+  private static native boolean framesAreNotLive(Frame[] frames);
+
+  public static void main(String[] args) {
+    if (args.length > 0) {
+      // For interpreter mode, have a means to reduce the default iteration count.
+      HeapMonitor.setAllocationIterations(Integer.parseInt(args[0]));
+    }
+
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Storage is not empty at test start...");
+    }
+
+    HeapMonitor.enableSamplingEvents();
+    List<Frame> frameList = HeapMonitor.allocate();
+    frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 51));
+
+    Frame[] frames = frameList.toArray(new Frame[0]);
+    if (!HeapMonitor.obtainedEvents(frames)) {
+      throw new RuntimeException("Events not found with the right frames.");
+    }
+
+    HeapMonitor.disableSamplingEvents();
+    HeapMonitor.resetEventStorage();
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Storage is not empty after reset.");
+    }
+
+    HeapMonitor.allocate();
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Storage is not empty after allocation while disabled.");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor Thread sanity.
+ * @compile HeapMonitorThreadOnOffTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadOnOffTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HeapMonitorThreadOnOffTest {
+  public static void main(String[] args) {
+    final int numThreads = 24;
+    ArrayList<Thread> list = new ArrayList<>();
+
+    // Add one thread that consistently turns on/off the sampler to ensure correctness with
+    // potential resets.
+    Switch switchPlayer = new Switch();
+    Thread switchThread = new Thread(switchPlayer, "Switch Player");
+    switchThread.start();
+
+    for (int i = 0 ; i < numThreads; i++) {
+      Thread thread = new Thread(new Allocator(i), "Allocator" + i);
+      thread.start();
+      list.add(thread);
+    }
+
+    for (Thread elem : list) {
+      try {
+        elem.join();
+      } catch(InterruptedException e) {
+        throw new RuntimeException("Thread got interrupted...");
+      }
+    }
+
+    switchPlayer.stop();
+    try {
+      switchThread.join();
+    } catch(InterruptedException e) {
+      throw new RuntimeException("Thread got interrupted while waiting for the switch player...");
+    }
+
+    // We don't check here for correctness of data. If we made it here, the test succeeded:
+    //  Threads can allocate like crazy
+    //  Other threads can turn on/off the system
+  }
+}
+
+class Allocator implements Runnable {
+  private int depth;
+  private volatile int tmp[];
+
+  public Allocator(int depth) {
+    this.depth = depth;
+  }
+
+  private int helper() {
+    int sum = 0;
+    // Let us assume that the array is 24 bytes of memory.
+    for (int i = 0; i < 127000 / 6; i++) {
+      int newTmp[] = new int[1];
+      // Force it to be kept.
+      tmp = newTmp;
+      sum += tmp[0];
+    }
+    return sum;
+  }
+
+  private int recursiveWrapper(int depth) {
+    if (depth > 0) {
+      return recursiveWrapper(depth - 1);
+    }
+    return helper();
+  }
+
+  public void run() {
+    int sum = 0;
+    for (int j = 0; j < 100; j++) {
+      sum += recursiveWrapper(depth);
+    }
+  }
+}
+
+class Switch implements Runnable {
+  private volatile boolean keepGoing;
+
+  public Switch() {
+    keepGoing = true;
+  }
+
+  public void stop() {
+    keepGoing = false;
+  }
+
+  public void run() {
+    while (keepGoing) {
+      HeapMonitor.disableSamplingEvents();
+      HeapMonitor.resetEventStorage();
+      HeapMonitor.enableSamplingEvents();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor ThreadInformation
+ * @summary Verifies the JVMTI Heap Monitor Thread information sanity.
+ * @compile HeapMonitorThreadTest.java
+ * @run main/othervm/native -Xmx512m -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadTest
+ * @requires !vm.gc.Z
+ */
+
+import java.util.List;
+
+public class HeapMonitorThreadTest {
+  private native static boolean checkSamples(int numThreads);
+
+  public static void main(String[] args) {
+    final int numThreads = 5;
+    List<ThreadInformation> threadList = ThreadInformation.createThreadList(numThreads);
+
+    // Sample at a rate of 8k.
+    HeapMonitor.setSamplingRate(1 << 13);
+    HeapMonitor.enableSamplingEvents();
+
+    System.err.println("Starting threads");
+    ThreadInformation.startThreads(threadList);
+    ThreadInformation.waitForThreads(threadList);
+    System.err.println("Waited for threads");
+
+    if (!checkSamples(numThreads)) {
+      throw new RuntimeException("Problem with checkSamples...");
+    }
+
+    // Now inform each thread we are done and wait for them to be done.
+    ThreadInformation.stopThreads(threadList);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor ThreadInformation
+ * @summary Verifies the JVMTI Heap Monitor does not work with two agents.
+ * @compile HeapMonitorTwoAgentsTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorTwoAgentsTest
+ */
+
+import java.util.List;
+
+public class HeapMonitorTwoAgentsTest {
+  private native static boolean enablingSamplingInSecondaryAgent();
+  private native static boolean obtainedEventsForBothAgents(Frame[] frames);
+
+  public static void main(String[] args) {
+    HeapMonitor.enableSamplingEvents();
+
+    if (enablingSamplingInSecondaryAgent()) {
+      throw new RuntimeException("Enabling sampling in second agent succeeded...");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies that when the VM event is sent, sampled events are also collected.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorVMEventsTest.java
+ * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions
+ *                          -XX:DisableIntrinsic=_clone
+ *                          -agentlib:HeapMonitorTest MyPackage.HeapMonitorVMEventsTest
+ */
+
+public class HeapMonitorVMEventsTest implements Cloneable {
+  private static native int vmEvents();
+  private static final int ITERATIONS = 1 << 15;
+  private static final Object[] array = new Object[ITERATIONS];
+
+  private static void cloneObjects(int iterations) {
+    HeapMonitorVMEventsTest object = new HeapMonitorVMEventsTest();
+    for (int i = 0; i < iterations; i++) {
+      try {
+        array[i] = object.clone();
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  public Object clone() throws CloneNotSupportedException {
+    return super.clone();
+  }
+
+  private static void checkDifference(int first, int second) {
+    double diff = Math.abs(first - second) * 100;
+    diff /= first;
+
+    // Accept a 10% error rate: with objects being allocated: this allows a bit of room in
+    // case other items are getting allocated during the test.
+    if (diff > 10) {
+      throw new RuntimeException("Error rate is over the accepted rate: " + diff
+          + ": " + first + " , " + second);
+    }
+  }
+
+  private static void compareSampledAndVM() {
+    HeapMonitor.resetEventStorage();
+    cloneObjects(ITERATIONS);
+
+    int onlySampleCount = HeapMonitor.sampledEvents();
+
+    HeapMonitor.enableVMEvents();
+    HeapMonitor.resetEventStorage();
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Storage is not empty after reset.");
+    }
+
+    cloneObjects(ITERATIONS);
+
+    int sampleCount = HeapMonitor.sampledEvents();
+    int vmCount = vmEvents();
+
+    System.err.println("Obtained: " + onlySampleCount + " - " + sampleCount + " - "  + vmCount);
+    checkDifference(onlySampleCount, sampleCount);
+    checkDifference(onlySampleCount, vmCount);
+  }
+
+  public static void main(String[] args) {
+    if (!HeapMonitor.eventStorageIsEmpty()) {
+      throw new RuntimeException("Storage is not empty at test start...");
+    }
+
+    HeapMonitor.sampleEverything();
+    compareSampledAndVM();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** API for handling heap allocating threads. */
+class ThreadInformation {
+  private Thread thread;
+  private Allocator allocator;
+
+  public ThreadInformation(Thread thread, Allocator allocator) {
+    this.thread = thread;
+    this.allocator = allocator;
+  }
+
+  public void waitForJobDone() {
+    allocator.waitForJobDone();
+  }
+
+  public void stop() {
+    try {
+      allocator.stopRun();
+      thread.join();
+
+      if (!allocator.endedNormally()) {
+        throw new RuntimeException("Thread did not end normally...");
+      }
+
+    } catch(InterruptedException e) {
+      throw new RuntimeException("Thread got interrupted...");
+    }
+  }
+
+  private void start() {
+    allocator.start();
+  }
+
+  public static void startThreads(List<ThreadInformation> threadList) {
+    for (ThreadInformation info : threadList) {
+      info.start();
+    }
+  }
+
+  public static void stopThreads(List<ThreadInformation> threadList) {
+    for (ThreadInformation info : threadList) {
+      info.stop();
+    }
+  }
+
+  public Thread getThread() {
+    return thread;
+  }
+
+  public static void waitForThreads(List<ThreadInformation> threadList) {
+    System.err.println("Waiting for threads to be done");
+    // Wait until all threads have put an object in the queue.
+    for (ThreadInformation info : threadList) {
+      info.waitForJobDone();
+    }
+  }
+
+  public static List<ThreadInformation> createThreadList(int numThreads) {
+    List<ThreadInformation> threadList = new ArrayList<>();
+    for (int i = 0 ; i < numThreads; i++) {
+      Allocator allocator = new Allocator(i);
+      Thread thread = new Thread(allocator, "Allocator" + i);
+
+      ThreadInformation info = new ThreadInformation(thread, allocator);
+      threadList.add(info);
+      thread.start();
+    }
+    return threadList;
+  }
+}
+
+class Allocator implements Runnable {
+  private int depth;
+  private List<int[]> currentList;
+  private BlockingQueue<Object> jobCanStart;
+  private BlockingQueue<Object> jobDone;
+  private BlockingQueue<Object> jobCanStop;
+  private boolean failed;
+
+  public Allocator(int depth) {
+    this.jobCanStart = new LinkedBlockingQueue<>();
+    this.jobDone = new LinkedBlockingQueue<>();
+    this.jobCanStop = new LinkedBlockingQueue<>();
+    this.depth = depth;
+  }
+
+  public boolean endedNormally() {
+    return !failed;
+  }
+
+  private void helper() {
+    List<int[]> newList = new ArrayList<>();
+    // Let us assume that the array is 40 bytes of memory, keep in
+    // memory at least 1.7MB without counting the link-list itself, which adds to this.
+    int iterations = (1 << 20) / 24;
+    for (int i = 0; i < iterations; i++) {
+      int newTmp[] = new int[5];
+      // Force it to be kept.
+      newList.add(newTmp);
+    }
+
+    // Replace old list with new list, which provokes two things:
+    //  Old list will get GC'd at some point.
+    //  New list forces that this thread has some allocations still sampled.
+    currentList = newList;
+  }
+
+  private void recursiveWrapper(int depth) {
+    if (depth > 0) {
+      recursiveWrapper(depth - 1);
+      return;
+    }
+    helper();
+  }
+
+  public void stopRun() throws InterruptedException {
+    jobCanStop.put(new Object());
+  }
+
+  public void run() {
+    String name = Thread.currentThread().getName();
+    System.err.println("Going to run: " + name);
+    // Wait till we are told to really start.
+    waitForStart();
+
+    System.err.println("Running: " + name);
+    for (int j = 0; j < 100; j++) {
+      recursiveWrapper(depth);
+    }
+
+    try {
+      // Tell the main thread we are done.
+      jobDone.put(new Object());
+
+      System.err.println("Waiting for main: " + name);
+      // Wait until the main thread says we can stop.
+      jobCanStop.take();
+      System.err.println("Waited for main: " + name);
+    } catch (InterruptedException e) {
+      failed = true;
+    }
+  }
+
+  public void waitForJobDone() {
+    try {
+      jobDone.take();
+    } catch(InterruptedException e) {
+      throw new RuntimeException("Thread got interrupted...");
+    }
+  }
+
+  public void waitForStart() {
+    try {
+      jobCanStart.take();
+    } catch(InterruptedException e) {
+      throw new RuntimeException("Thread got interrupted...");
+    }
+  }
+
+  public void start() {
+    try {
+      jobCanStart.put(new Object());
+    } catch(InterruptedException e) {
+      throw new RuntimeException("Thread got interrupted...");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c	Fri Jun 15 02:31:37 2018 -0700
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "jvmti.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef JNI_ENV_ARG
+
+#ifdef __cplusplus
+#define JNI_ENV_ARG(x, y) y
+#define JNI_ENV_PTR(x) x
+#else
+#define JNI_ENV_ARG(x,y) x, y
+#define JNI_ENV_PTR(x) (*x)
+#endif
+
+#endif
+
+#define TRUE 1
+#define FALSE 0
+#define PRINT_OUT 0
+
+static jvmtiEnv *jvmti = NULL;
+static jvmtiEnv *second_jvmti = NULL;
+
+typedef struct _ObjectTrace{
+  jweak object;
+  size_t size;
+  jvmtiFrameInfo* frames;
+  size_t frame_count;
+  jthread thread;
+} ObjectTrace;
+
+typedef struct _EventStorage {
+  int live_object_additions;
+  int live_object_size;
+  int live_object_count;
+  ObjectTrace** live_objects;
+
+  int garbage_history_size;
+  int garbage_history_index;
+  ObjectTrace** garbage_collected_objects;
+
+  // Two separate monitors to separate storage data race and the compaction field
+  // data race.
+  jrawMonitorID storage_monitor;
+
+  int compaction_required;
+  jrawMonitorID compaction_monitor;
+} EventStorage;
+
+typedef struct _ExpectedContentFrame {
+  const char *name;
+  const char *signature;
+  const char *file_name;
+  int line_number;
+} ExpectedContentFrame;
+
+static
+void event_storage_lock(EventStorage* storage) {
+  (*jvmti)->RawMonitorEnter(jvmti, storage->storage_monitor);
+}
+
+static
+void event_storage_unlock(EventStorage* storage) {
+  (*jvmti)->RawMonitorExit(jvmti, storage->storage_monitor);
+}
+
+static
+void event_storage_lock_compaction(EventStorage* storage) {
+  (*jvmti)->RawMonitorEnter(jvmti, storage->compaction_monitor);
+}
+
+static
+void event_storage_unlock_compaction(EventStorage* storage) {
+  (*jvmti)->RawMonitorExit(jvmti, storage->compaction_monitor);
+}
+
+// Given a method and a location, this method gets the line number.
+static
+jint get_line_number(jvmtiEnv* jvmti, jmethodID method,
+                     jlocation location) {
+  // Read the line number table.
+  jvmtiLineNumberEntry *table_ptr = 0;
+  jint line_number_table_entries;
+  int l;
+  jlocation last_location;
+  int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method,
+                                                 &line_number_table_entries,
+                                                 &table_ptr);
+
+  if (JVMTI_ERROR_NONE != jvmti_error) {
+    return -1;
+  }
+  if (line_number_table_entries <= 0) {
+    return -1;
+  }
+  if (line_number_table_entries == 1) {
+    return table_ptr[0].line_number;
+  }
+
+  // Go through all the line numbers...
+  last_location = table_ptr[0].start_location;
+  for (l = 1; l < line_number_table_entries; l++) {
+    // ... and if you see one that is in the right place for your
+    // location, you've found the line number!
+    if ((location < table_ptr[l].start_location) &&
+        (location >= last_location)) {
+      return table_ptr[l - 1].line_number;
+    }
+    last_location = table_ptr[l].start_location;
+  }
+
+  if (location >= last_location) {
+    return table_ptr[line_number_table_entries - 1].line_number;
+  } else {
+    return -1;
+  }
+}
+
+static void print_out_frames(JNIEnv* env, ObjectTrace* trace) {
+  jvmtiFrameInfo* frames = trace->frames;
+  size_t i;
+  for (i = 0; i < trace->frame_count; i++) {
+    // Get basic information out of the trace.
+    jlocation bci = frames[i].location;
+    jmethodID methodid = frames[i].method;
+    char *name = NULL, *signature = NULL, *file_name = NULL;
+    jclass declaring_class;
+    int line_number;
+    jvmtiError err;
+
+    if (bci < 0) {
+      fprintf(stderr, "\tNative frame\n");
+      continue;
+    }
+
+    // Transform into usable information.
+    line_number = get_line_number(jvmti, methodid, bci);
+    if (JVMTI_ERROR_NONE !=
+        (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0)) {
+      fprintf(stderr, "\tUnknown method name\n");
+      continue;
+    }
+
+    if (JVMTI_ERROR_NONE !=
+        (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) {
+      fprintf(stderr, "\tUnknown class\n");
+      continue;
+    }
+
+    err = (*jvmti)->GetSourceFileName(jvmti, declaring_class,
+                                      &file_name);
+    if (err != JVMTI_ERROR_NONE) {
+      fprintf(stderr, "\tUnknown file\n");
+      continue;
+    }
+
+    // Compare now, none should be NULL.
+    if (name == NULL) {
+      fprintf(stderr, "\tUnknown name\n");
+      continue;
+    }
+
+    if (file_name == NULL) {
+      fprintf(stderr, "\tUnknown file\n");
+      continue;
+    }
+
+    if (signature == NULL) {
+      fprintf(stderr, "\tUnknown signature\n");
+      continue;
+    }
+
+    fprintf(stderr, "\t%s%s (%s: %d)\n",
+            name, signature, file_name, line_number);
+  }
+}
+
+static jboolean check_sample_content(JNIEnv* env,
+                                     ObjectTrace* trace,
+                                     ExpectedContentFrame *expected,
+                                     size_t expected_count,
+                                     int print_out_comparisons) {
+  jvmtiFrameInfo* frames;
+  size_t i;
+
+  if (expected_count > trace->frame_count) {
+    return FALSE;
+  }
+
+  frames = trace->frames;
+  for (i = 0; i < expected_count; i++) {
+    // Get basic information out of the trace.
+    jlocation bci = frames[i].location;
+    jmethodID methodid = frames[i].method;
+    char *name = NULL, *signature = NULL, *file_name = NULL;
+    jclass declaring_class;
+    int line_number;
+    jvmtiError err;
+
+    if (bci < 0 && expected[i].line_number != -1) {
+      return FALSE;
+    }
+
+    // Transform into usable information.
+    line_number = get_line_number(jvmti, methodid, bci);
+    (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0);
+
+    if (JVMTI_ERROR_NONE !=
+        (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) {
+      return FALSE;
+    }
+
+    err = (*jvmti)->GetSourceFileName(jvmti, declaring_class,
+                                      &file_name);
+    if (err != JVMTI_ERROR_NONE) {
+      return FALSE;
+    }
+
+    // Compare now, none should be NULL.
+    if (name == NULL) {
+      return FALSE;
+    }
+
+    if (file_name == NULL) {
+      return FALSE;
+    }
+
+    if (signature == NULL) {
+      return FALSE;
+    }
+
+    if (print_out_comparisons) {
+      fprintf(stderr, "\tComparing:\n");
+      fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name);
+      fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature);
+      fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name);
+      fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number);
+      fprintf(stderr, "\t\tResult is %d\n",
+              (strcmp(name, expected[i].name) ||
+               strcmp(signature, expected[i].signature) ||
+               strcmp(file_name, expected[i].file_name) ||
+               line_number != expected[i].line_number));
+    }
+
+    if (strcmp(name, expected[i].name) ||
+        strcmp(signature, expected[i].signature) ||
+        strcmp(file_name, expected[i].file_name) ||
+        line_number != expected[i].line_number) {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+// Static native API for various tests.
+static void fill_native_frames(JNIEnv* env, jobjectArray frames,
+                               ExpectedContentFrame* native_frames, size_t size) {
+  size_t i;
+  for (i = 0; i < size; i++) {
+    jobject obj = (*env)->GetObjectArrayElement(env, frames, (jsize) i);
+    jclass frame_class = (*env)->GetObjectClass(env, obj);
+    jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class,
+                                                       "lineNumber", "I");
+    int line_number = (*env)->GetIntField(env, obj, line_number_field_id);
+
+    jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method",
+                                            "Ljava/lang/String;");
+    jstring string_object = (jstring) (*env)->GetObjectField(env, obj,
+                                                             string_id);
+    const char* method = (*env)->GetStringUTFChars(env, string_object, 0);
+    const char* file_name;
+    const char* signature;
+
+    string_id = (*env)->GetFieldID(env, frame_class, "fileName",
+                                   "Ljava/lang/String;");
+    string_object = (jstring) (*env)->GetObjectField(env, obj, string_id);
+    file_name = (*env)->GetStringUTFChars(env, string_object, 0);
+
+    string_id = (*env)->GetFieldID(env, frame_class, "signature",
+                                   "Ljava/lang/String;");
+    string_object = (jstring) (*env)->GetObjectField(env, obj, string_id);
+    signature= (*env)->GetStringUTFChars(env, string_object, 0);
+
+    native_frames[i].name = method;
+    native_frames[i].file_name = file_name;
+    native_frames[i].signature = signature;
+    native_frames[i].line_number = line_number;
+  }
+}
+
+// Internal storage system implementation.
+static EventStorage global_event_storage;
+static EventStorage second_global_event_storage;
+
+static void event_storage_set_compaction_required(EventStorage* storage) {
+  event_storage_lock_compaction(storage);
+  storage->compaction_required = 1;
+  event_storage_unlock_compaction(storage);
+}
+
+static int event_storage_get_compaction_required(EventStorage* storage) {
+  int result;
+  event_storage_lock_compaction(storage);
+  result = storage->compaction_required;
+  event_storage_unlock_compaction(storage);
+  return result;
+}
+
+static void event_storage_set_garbage_history(EventStorage* storage, int value) {
+  size_t size;
+  event_storage_lock(storage);
+  global_event_storage.garbage_history_size = value;
+  free(global_event_storage.garbage_collected_objects);
+  size = sizeof(*global_event_storage.garbage_collected_objects) * value;
+  global_event_storage.garbage_collected_objects = malloc(size);
+  memset(global_event_storage.garbage_collected_objects, 0, size);
+  event_storage_unlock(storage);
+}
+
+// No mutex here, it is handled by the caller.
+static void event_storage_add_garbage_collected_object(EventStorage* storage,
+                                                       ObjectTrace* object) {
+  int idx = storage->garbage_history_index;
+  ObjectTrace* old_object = storage->garbage_collected_objects[idx];
+  if (old_object != NULL) {
+    free(old_object->frames);
+    free(storage->garbage_collected_objects[idx]);
+  }
+
+  storage->garbage_collected_objects[idx] = object;
+  storage->garbage_history_index = (idx + 1) % storage->garbage_history_size;
+}
+
+static int event_storage_get_count(EventStorage* storage) {
+  int result;
+  event_storage_lock(storage);
+  result = storage->live_object_count;
+  event_storage_unlock(storage);
+  return result;
+}
+
+static double event_storage_get_average_rate(EventStorage* storage) {
+  double accumulation = 0;
+  int max_size;
+  int i;
+
+  event_storage_lock(storage);
+  max_size = storage->live_object_count;
+
+  for (i = 0; i < max_size; i++) {
+    accumulation += storage->live_objects[i]->size;
+  }
+
+  event_storage_unlock(storage);
+  return accumulation / max_size;
+}
+
+static jboolean event_storage_contains(JNIEnv* env,
+                                       EventStorage* storage,
+                                       ExpectedContentFrame* frames,
+                                       size_t size) {
+  int i;
+  event_storage_lock(storage);
+  fprintf(stderr, "Checking storage count %d\n", storage->live_object_count);
+  for (i = 0; i < storage->live_object_count; i++) {
+    ObjectTrace* trace = storage->live_objects[i];
+
+    if (check_sample_content(env, trace, frames, size, PRINT_OUT)) {
+      event_storage_unlock(storage);
+      return TRUE;
+    }
+  }
+  event_storage_unlock(storage);
+  return FALSE;
+}
+
+static jboolean event_storage_garbage_contains(JNIEnv* env,
+                                               EventStorage* storage,
+                                               ExpectedContentFrame* frames,
+                                               size_t size) {
+  int i;
+  event_storage_lock(storage);
+  fprintf(stderr, "Checking garbage storage count %d\n",
+          storage->garbage_history_size);
+  for (i = 0; i < storage->garbage_history_size; i++) {
+    ObjectTrace* trace = storage->garbage_collected_objects[i];
+
+    if (trace == NULL) {
+      continue;
+    }
+
+    if (check_sample_content(env, trace, frames, size, PRINT_OUT)) {
+      event_storage_unlock(storage);
+      return TRUE;
+    }
+  }
+  event_storage_unlock(storage);
+  return FALSE;
+}
+
+// No mutex here, handled by the caller.
+static void event_storage_augment_storage(EventStorage* storage) {
+  int new_max = (storage->live_object_size * 2) + 1;
+  ObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects));
+
+  int current_count = storage->live_object_count;
+  memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects));
+  free(storage->live_objects);
+  storage->live_objects = new_objects;
+  storage->live_object_size = new_max;
+}
+
+static void event_storage_add(EventStorage* storage,
+                              JNIEnv* jni,
+                              jthread thread,
+                              jobject object,
+                              jclass klass,
+                              jlong size) {
+  jvmtiFrameInfo frames[64];
+  jint count;
+  jvmtiError err;
+
+  err = (*jvmti)->GetStackTrace(jvmti, thread, 0, 64, frames, &count);
+  if (err == JVMTI_ERROR_NONE && count >= 1) {
+    ObjectTrace* live_object;
+    jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames));
+    memcpy(allocated_frames, frames, count * sizeof(*allocated_frames));
+
+    live_object = (ObjectTrace*) malloc(sizeof(*live_object));
+    live_object->frames = allocated_frames;
+    live_object->frame_count = count;
+    live_object->size = size;
+    live_object->thread = thread;
+    live_object->object = (*jni)->NewWeakGlobalRef(jni, object);
+
+    // Only now lock and get things done quickly.
+    event_storage_lock(storage);
+
+    storage->live_object_additions++;
+
+    if (storage->live_object_count >= storage->live_object_size) {
+      event_storage_augment_storage(storage);
+    }
+    assert(storage->live_object_count < storage->live_object_size);
+
+    if (PRINT_OUT) {
+      fprintf(stderr, "Adding trace for thread %p, frame_count %d, storage %p\n",
+              thread, count, storage);
+      print_out_frames(jni, live_object);
+    }
+    storage->live_objects[storage->live_object_count] = live_object;
+    storage->live_object_count++;
+
+    event_storage_unlock(storage);
+  }
+}
+
+static void event_storage_compact(EventStorage* storage, JNIEnv* jni) {
+  int max, i, dest;
+  ObjectTrace** live_objects;
+
+  event_storage_lock_compaction(storage);
+  storage->compaction_required = 0;
+  event_storage_unlock_compaction(storage);
+
+  event_storage_lock(storage);
+
+  max = storage->live_object_count;
+  live_objects = storage->live_objects;
+
+  for (i = 0, dest = 0; i < max; i++) {
+    ObjectTrace* live_object = live_objects[i];
+    jweak object = live_object->object;
+
+    if (!(*jni)->IsSameObject(jni, object, NULL)) {
+      if (dest != i) {
+        live_objects[dest] = live_object;
+        dest++;
+      }
+    } else {
+      (*jni)->DeleteWeakGlobalRef(jni, object);
+      live_object->object = NULL;
+
+      event_storage_add_garbage_collected_object(storage, live_object);
+    }
+  }
+
+  storage->live_object_count = dest;
+  event_storage_unlock(storage);
+}
+
+static void event_storage_free_objects(ObjectTrace** array, int max) {
+  int i;
+  for (i = 0; i < max; i++) {
+    free(array[i]), array[i] = NULL;
+  }
+}
+
+static void event_storage_reset(EventStorage* storage) {
+  event_storage_lock(storage);
+
+  // Reset everything except the mutex and the garbage collection.
+  event_storage_free_objects(storage->live_objects,
+                             storage->live_object_count);
+  storage->live_object_additions = 0;
+  storage->live_object_size = 0;
+  storage->live_object_count = 0;
+  free(storage->live_objects), storage->live_objects = NULL;
+
+  event_storage_free_objects(storage->garbage_collected_objects,
+                             storage->garbage_history_size);
+
+  storage->compaction_required = 0;
+  storage->garbage_history_index = 0;
+
+  event_storage_unlock(storage);
+}
+
+static int event_storage_number_additions(EventStorage* storage) {
+  int result;
+  event_storage_lock(storage);
+  result = storage->live_object_additions;
+  event_storage_unlock(storage);
+  return result;
+}
+
+// Start of the JVMTI agent code.
+static const char *EXC_CNAME = "java/lang/Exception";
+
+static int check_error(jvmtiError err, const char *s) {
+  if (err != JVMTI_ERROR_NONE) {
+    printf("  ## %s error: %d\n", s, err);
+    return 1;
+  }
+  return 0;
+}
+
+static int check_capability_error(jvmtiError err, const char *s) {
+  if (err != JVMTI_ERROR_NONE) {
+    if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) {
+      return 0;
+    }
+    fprintf(stderr, "  ## %s error: %d\n", s, err);
+  }
+  return 1;
+}
+
+static jint throw_exception(JNIEnv *env, char *msg) {
+  jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME));
+
+  if (exc_class == NULL) {
+    fprintf(stderr, "throw_exception: Error in FindClass(env, %s)\n",
+            EXC_CNAME);
+    return -1;
+  }
+  return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
+}
+
+static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
+
+JNIEXPORT
+jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
+  return Agent_Initialize(jvm, options, reserved);
+}
+
+JNIEXPORT
+jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
+  return Agent_Initialize(jvm, options, reserved);
+}
+
+JNIEXPORT
+jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
+  return JNI_VERSION_1_8;
+}
+
+#define MAX_THREADS 500
+
+typedef struct ThreadStats {
+  int number_threads;
+  int counts[MAX_THREADS];
+  int not_helper_counts[MAX_THREADS];
+  int index[MAX_THREADS];
+  jthread threads[MAX_THREADS];
+
+  int method_resolution_problem;
+} ThreadStats;
+
+static ThreadStats thread_stats;
+
+static void add_thread_count(jthread thread, int lock, int helper) {
+  int i;
+  jvmtiThreadInfo info;
+  const char* name;
+  char* end;
+  int idx;
+  int err;
+
+  if (lock) {
+    event_storage_lock(&global_event_storage);
+  }
+
+  for (i = 0; i < thread_stats.number_threads; i++) {
+    if (thread_stats.threads[i] == thread) {
+      if (helper) {
+        thread_stats.counts[i]++;
+      } else {
+        thread_stats.not_helper_counts[i]++;
+      }
+
+      if (lock) {
+        event_storage_unlock(&global_event_storage);
+      }
+      return;
+    }
+  }
+
+  thread_stats.threads[thread_stats.number_threads] = thread;
+
+  err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
+  if (err != JVMTI_ERROR_NONE) {
+    if (lock) {
+      event_storage_unlock(&global_event_storage);
+    }
+
+    // Just to have it accounted as an error...
+    info.name = "Allocator99";
+  }
+
+  if (!strstr(info.name, "Allocator")) {
+    if (lock) {
+      event_storage_unlock(&global_event_storage);
+    }
+
+    // Just to have it accounted as an error...
+    info.name = "Allocator98";
+  }
+
+  name = info.name + 9;
+  end = NULL;
+  idx = strtol(name, &end, 0);
+
+  if (*end == '\0') {
+    if (helper) {
+      thread_stats.counts[thread_stats.number_threads]++;
+    } else {
+      thread_stats.not_helper_counts[thread_stats.number_threads]++;
+    }
+
+    thread_stats.index[thread_stats.number_threads] = idx;
+    thread_stats.number_threads++;
+  } else {
+    fprintf(stderr, "Problem with thread name...: %p %s\n", thread, name);
+  }
+
+  if (PRINT_OUT) {
+    fprintf(stderr, "Added %s - %p - %d - lock: %d\n", info.name, thread, idx, lock);
+  }
+
+  if (lock) {
+    event_storage_unlock(&global_event_storage);
+  }
+}
+
+static void print_thread_stats() {
+  int i;
+  event_storage_lock(&global_event_storage);
+  fprintf(stderr, "Method resolution problem: %d\n", thread_stats.method_resolution_problem);
+  fprintf(stderr, "Thread count:\n");
+  for (i = 0; i < thread_stats.number_threads; i++) {
+    fprintf(stderr, "\t%p: %d: %d - %d\n", thread_stats.threads[i],
+            thread_stats.index[i],
+            thread_stats.counts[i],
+            thread_stats.not_helper_counts[i]);
+  }
+  event_storage_unlock(&global_event_storage);
+}
+
+JNIEXPORT
+void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env,
+                                JNIEnv* jni_env,
+                                jthread thread,
+                                jobject object,
+                                jclass object_klass,
+                                jlong size) {
+  add_thread_count(thread, 1, 1);
+
+  if (event_storage_get_compaction_required(&global_event_storage)) {
+    event_storage_compact(&global_event_storage, jni_env);
+  }
+
+  event_storage_add(&global_event_storage, jni_env, thread, object,
+                    object_klass, size);
+}
+
+JNIEXPORT
+void JNICALL VMObjectAlloc(jvmtiEnv *jvmti_env,
+                           JNIEnv* jni_env,
+                           jthread thread,
+                           jobject object,
+                           jclass object_klass,
+                           jlong size) {
+  event_storage_add(&second_global_event_storage, jni_env, thread, object,
+                    object_klass, size);
+}
+
+JNIEXPORT
+void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) {
+  event_storage_set_compaction_required(&global_event_storage);
+}
+
+static int enable_notifications() {
+  if (check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
+                     "Set event notifications")) {
+    return 1;
+  }
+
+  return check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL),
+                     "Set event notifications");
+}
+
+static int enable_notifications_for_two_threads(jthread first, jthread second) {
+  if (check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
+                           "Set event notifications")) {
+    return 0;
+  }
+
+  if (check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first),
+                  "Set event notifications")) {
+    return 0;
+  }
+
+  // Second thread should fail.
+  if (check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second),
+                  "Set event notifications")) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static
+jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
+  jint res;
+  jvmtiEventCallbacks callbacks;
+  jvmtiCapabilities caps;
+
+  res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
+                                 JVMTI_VERSION_9);
+  if (res != JNI_OK || jvmti == NULL) {
+    fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
+    return JNI_ERR;
+  }
+
+  // Get second jvmti environment.
+  res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &second_jvmti),
+                                 JVMTI_VERSION_9);
+  if (res != JNI_OK || second_jvmti == NULL) {
+    fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n");
+    return JNI_ERR;
+  }
+
+  if (PRINT_OUT) {
+    fprintf(stderr, "Storage is at %p, secondary is at %p\n",
+            &global_event_storage, &second_global_event_storage);
+  }
+
+  (*jvmti)->CreateRawMonitor(jvmti, "storage_monitor",
+                             &global_event_storage.storage_monitor);
+  (*jvmti)->CreateRawMonitor(jvmti, "second_storage_monitor",
+                             &second_global_event_storage.storage_monitor);
+
+  (*jvmti)->CreateRawMonitor(jvmti, "compaction_monitor",
+                             &global_event_storage.compaction_monitor);
+  (*jvmti)->CreateRawMonitor(jvmti, "second_compaction_monitor",
+                             &second_global_event_storage.compaction_monitor);
+
+  event_storage_set_garbage_history(&global_event_storage, 200);
+  event_storage_set_garbage_history(&second_global_event_storage, 200);
+
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks.SampledObjectAlloc = &SampledObjectAlloc;
+  callbacks.VMObjectAlloc = &VMObjectAlloc;
+  callbacks.GarbageCollectionFinish = &GarbageCollectionFinish;
+
+  memset(&caps, 0, sizeof(caps));
+  // Get line numbers, sample events, filename, and gc events for the tests.
+  caps.can_get_line_numbers = 1;
+  caps.can_get_source_file_name = 1;
+  caps.can_generate_garbage_collection_events = 1;
+  caps.can_generate_sampled_object_alloc_events = 1;
+  caps.can_generate_vm_object_alloc_events = 1;
+  if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")) {
+    return JNI_ERR;
+  }
+
+  if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks,
+                                              sizeof(jvmtiEventCallbacks)),
+                  " Set Event Callbacks")) {
+    return JNI_ERR;
+  }
+  return JNI_OK;
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_setSamplingRate(JNIEnv* env, jclass cls, jint value) {
+  (*jvmti)->SetHeapSamplingRate(jvmti, value);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) {
+  return event_storage_get_count(&global_event_storage) == 0;
+}
+
+JNIEXPORT jint JNICALL
+Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) {
+  return event_storage_get_count(&global_event_storage);
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) {
+  enable_notifications();
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env,
+                                                             jclass cls,
+                                                             jthread first,
+                                                             jthread second) {
+  return enable_notifications_for_two_threads(first, second);
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
+  check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL),
+              "Set event notifications");
+
+  check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
+              "Garbage Collection Finish");
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls, jobjectArray frames) {
+  jboolean result;
+  jsize size = (*env)->GetArrayLength(env, frames);
+  ExpectedContentFrame *native_frames = malloc(size * sizeof(*native_frames));
+
+  if (native_frames == NULL) {
+    return 0;
+  }
+
+  fill_native_frames(env, frames, native_frames, size);
+  result = event_storage_contains(env, &global_event_storage, native_frames, size);
+
+  free(native_frames), native_frames = NULL;
+  return result;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls, jobjectArray frames) {
+  jboolean result;
+  jsize size = (*env)->GetArrayLength(env, frames);
+  ExpectedContentFrame *native_frames = malloc(size * sizeof(*native_frames));
+
+  if (native_frames == NULL) {
+    return 0;
+  }
+
+  fill_native_frames(env, frames, native_frames, size);
+  result = event_storage_garbage_contains(env, &global_event_storage, native_frames, size);
+
+  free(native_frames), native_frames = NULL;
+  return result;
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) {
+  check_error((*jvmti)->ForceGarbageCollection(jvmti),
+              "Forced Garbage Collection");
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) {
+  event_storage_reset(&global_event_storage);
+  event_storage_reset(&second_global_event_storage);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env,
+                                                                  jclass cls) {
+  jvmtiCapabilities caps;
+  memset(&caps, 0, sizeof(caps));
+  caps.can_generate_sampled_object_alloc_events = 1;
+  if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps),
+                  "Add capabilities\n")){
+    return FALSE;
+  }
+
+  if (check_capability_error((*jvmti)->SetHeapSamplingRate(jvmti, 1<<19),
+                             "Set Heap Sampling Rate")) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env,
+                                                                  jclass cls) {
+  if (check_error((*jvmti)->SetHeapSamplingRate(jvmti, 0),
+                  "Sampling rate 0 failed\n")){
+    return FALSE;
+  }
+
+  if (check_error((*jvmti)->SetHeapSamplingRate(jvmti, 1024),
+                  "Sampling rate 1024 failed\n")){
+    return FALSE;
+  }
+
+  if (!check_error((*jvmti)->SetHeapSamplingRate(jvmti, -1),
+                   "Sampling rate -1 passed\n")){
+    return FALSE;
+  }
+
+  if (!check_error((*jvmti)->SetHeapSamplingRate(jvmti, -1024),
+                   "Sampling rate -1024 passed\n")){
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+JNIEXPORT jdouble JNICALL
+Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) {
+  return event_storage_get_average_rate(&global_event_storage);
+}
+
+typedef struct sThreadsFound {
+  jthread* threads;
+  int num_threads;
+} ThreadsFound;
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
+                                                  jint num_threads) {
+
+  print_thread_stats();
+  // Ensure we got stacks from at least num_threads.
+  return thread_stats.number_threads >= num_threads;
+}
+
+JNIEXPORT
+void JNICALL SampledObjectAlloc2(jvmtiEnv *jvmti_env,
+                                 JNIEnv* jni_env,
+                                 jthread thread,
+                                 jobject object,
+                                 jclass object_klass,
+                                 jlong size) {
+  // Nop for now, two agents are not yet implemented.
+  assert(0);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorTwoAgentsTest_enablingSamplingInSecondaryAgent(
+    JNIEnv* env, jclass cls) {
+  // Currently this method should be failing directly at the AddCapability step
+  // but the implementation is correct for when multi-agent support is enabled.
+  jvmtiCapabilities caps;
+  jvmtiEventCallbacks callbacks;
+
+  memset(&caps, 0, sizeof(caps));
+  caps.can_generate_sampled_object_alloc_events = 1;
+  if (check_error((*second_jvmti)->AddCapabilities(second_jvmti, &caps),
+                  "Set the capability for second agent")) {
+    return FALSE;
+  }
+
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks.SampledObjectAlloc = &SampledObjectAlloc2;
+
+  if (check_error((*second_jvmti)->SetEventCallbacks(second_jvmti, &callbacks,
+                                                     sizeof(jvmtiEventCallbacks)),
+                  " Set Event Callbacks for second agent")) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_enableVMEvents(JNIEnv* env, jclass cls) {
+  check_error((*jvmti)->SetEventNotificationMode(
+      jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL),
+              "Set vm event notifications");
+}
+
+JNIEXPORT jint JNICALL
+Java_MyPackage_HeapMonitorVMEventsTest_vmEvents(JNIEnv* env, jclass cls) {
+  return event_storage_number_additions(&second_global_event_storage);
+}
+
+JNIEXPORT jint JNICALL
+Java_MyPackage_HeapMonitor_sampledEvents(JNIEnv* env, jclass cls) {
+  return event_storage_number_additions(&global_event_storage);
+}
+
+static void allocate_object(JNIEnv* env) {
+  // Construct an Object.
+  jclass cls = (*env)->FindClass(env, "java/lang/Object");
+  jmethodID constructor;
+
+  if (cls == NULL) {
+    throw_exception(env, "Cannot find Object class.");
+    return;
+  }
+
+  constructor = (*env)->GetMethodID(env, cls, "<init>", "()V");
+
+  if (constructor == NULL) {
+    throw_exception(env, "Cannot find Object class constructor.");
+    return;
+  }
+
+  // Call back constructor to allocate a new instance, with an int argument
+  (*env)->NewObject(env, cls, constructor);
+}
+
+// Ensure we got a callback for the test.
+static int did_recursive_callback_test;
+
+JNIEXPORT
+void JNICALL RecursiveSampledObjectAlloc(jvmtiEnv *jvmti_env,
+                                         JNIEnv* jni_env,
+                                         jthread thread,
+                                         jobject object,
+                                         jclass object_klass,
+                                         jlong size) {
+  // Basically ensure that if we were to allocate objects, we would not have an
+  // infinite recursion here.
+  int i;
+  for (i = 0; i < 1000; i++) {
+    allocate_object(jni_env);
+  }
+
+  did_recursive_callback_test = 1;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorRecursiveTest_didCallback(JNIEnv* env, jclass cls) {
+  return did_recursive_callback_test != 0;
+}
+
+JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitorRecursiveTest_setCallbackToCallAllocateSomeMore(JNIEnv* env, jclass cls) {
+  jvmtiEventCallbacks callbacks;
+
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks.SampledObjectAlloc = &RecursiveSampledObjectAlloc;
+
+  if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks,
+                                              sizeof(jvmtiEventCallbacks)),
+                  " Set Event Callbacks")) {
+    throw_exception(env, "Cannot reset the callback.");
+    return;
+  }
+}
+
+#ifdef __cplusplus
+}
+#endif