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
--- /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