diff --git a/src/share/classes/jdk/jfr/EventNames.java b/src/share/classes/jdk/jfr/EventNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f1ac349aa0219acc1edc08e94b3eff2922db8b9
--- /dev/null
+++ b/src/share/classes/jdk/jfr/EventNames.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Alibaba Group Holding Limited. 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. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ *
+ */
+package jdk.jfr;
+
+/**
+ * A constant class for JFR Event, contains the names of some events used by jdk.
+ */
+public class EventNames {
+ private final static String PREFIX = "jdk.";
+
+ public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation";
+ public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation";
+}
diff --git a/src/share/classes/jdk/jfr/Recording.java b/src/share/classes/jdk/jfr/Recording.java
index 43d54a39958d320ef0e8eeb56a79ab3f83ce4267..c36ccc055c44e20c4c252b84411f7349a1d492ba 100644
--- a/src/share/classes/jdk/jfr/Recording.java
+++ b/src/share/classes/jdk/jfr/Recording.java
@@ -673,4 +673,7 @@ public final class Recording implements Closeable {
internal.setSetting(id, value);
}
+ public boolean isRecorderEnabled(String eventName) {
+ return internal.isRecorderEnabled(eventName);
+ }
}
diff --git a/src/share/classes/jdk/jfr/conf/default.jfc b/src/share/classes/jdk/jfr/conf/default.jfc
index f759cd02d1e55e20e4ed5594686712afcaef759a..ce9050999ad9f575cb582f03d4b321859529cfbe 100644
--- a/src/share/classes/jdk/jfr/conf/default.jfc
+++ b/src/share/classes/jdk/jfr/conf/default.jfc
@@ -629,6 +629,16 @@
+ * The object size for instance class is accurate. But for the array class, it
+ * is a magic code 0x1111baba. The array object size can not be determined statically
+ * from the JVM klass information because array object size is affected by
+ * actual element length.
+ *
+ * @return the object size (instance object), or magic code 0x1111baba (array object)
+ */
+ public int getObjectSize() {
+ return getTyped("objectSize", Integer.class, -1);
+ }
+
+ /**
+ * Checks whether the class is for instance or array.
+ *
+ * @return true or false
+ */
+ public boolean isArray() {
+ return getName().startsWith("[");
+ }
}
diff --git a/src/share/classes/jdk/jfr/internal/JVM.java b/src/share/classes/jdk/jfr/internal/JVM.java
index bd7d086c47d0b1d805c30307638a165cd545fe5e..f8067f4e39edc9bcd8b9478268bca3ac7a6279b5 100644
--- a/src/share/classes/jdk/jfr/internal/JVM.java
+++ b/src/share/classes/jdk/jfr/internal/JVM.java
@@ -293,6 +293,20 @@ public final class JVM {
*/
public native void setSampleThreads(boolean sampleThreads) throws IllegalStateException;
+ /**
+ * Turn on/off objects allocations sampling.
+ *
+ * @param sampleAllocations true if object allocations should be sampled, false otherwise.
+ */
+ public native void setSampleObjectAllocations(boolean sampleAllocations);
+
+ /**
+ * Set object allocations sampling interval.
+ *
+ * @param interval
+ */
+ public native void setObjectAllocationsSamplingInterval(long interval) throws IllegalArgumentException;
+
/**
* Turn on/off compressed integers.
*
diff --git a/src/share/classes/jdk/jfr/internal/Options.java b/src/share/classes/jdk/jfr/internal/Options.java
index 23f3919bb3d14fd2b899cd5efaf0807e4421232b..ed6a15d49504c3b33b740838e548fe551421ca37 100644
--- a/src/share/classes/jdk/jfr/internal/Options.java
+++ b/src/share/classes/jdk/jfr/internal/Options.java
@@ -49,6 +49,8 @@ public final class Options {
private static final boolean DEFAULT_SAMPLE_THREADS = true;
private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024;
private static final SafePath DEFAULT_DUMP_PATH = SecuritySupport.USER_HOME;
+ private static final boolean DEFAULT_SAMPLE_OBJECT_ALLOCATIONS = false;
+ private static final long DEFAULT_OBJECT_ALLOCATIONS_SAMPLING_INTERVAL = 1024;
private static long memorySize;
private static long globalBufferSize;
@@ -58,6 +60,8 @@ public final class Options {
private static boolean sampleThreads;
private static long maxChunkSize;
private static SafePath dumpPath;
+ private static boolean sampleObjectAllocations;
+ private static long objectAllocationsSamplingInterval;
static {
final long pageSize = Unsafe.getUnsafe().pageSize();
@@ -139,6 +143,31 @@ public final class Options {
return sampleThreads;
}
+ public static synchronized void setSampleObjectAllocations(Boolean sample) {
+ jvm.setSampleObjectAllocations(sample);
+ sampleObjectAllocations = sample;
+ }
+
+ public static synchronized boolean getSampleObjectAllocations() {
+ return sampleObjectAllocations;
+ }
+
+ /**
+ * Set interval of sampling object allocation events
+ * @param interval the number of newly created objects between two sampling
+ */
+ public static synchronized void setObjectAllocationsSamplingInterval(Long interval) {
+ if (interval <= 0) {
+ throw new IllegalArgumentException("interval should be greater than 0");
+ }
+ jvm.setObjectAllocationsSamplingInterval(interval);
+ objectAllocationsSamplingInterval = interval;
+ }
+
+ public static synchronized long getObjectAllocationsSamplingInterval() {
+ return objectAllocationsSamplingInterval;
+ }
+
private static synchronized void reset() {
setMaxChunkSize(DEFAULT_MAX_CHUNK_SIZE);
setMemorySize(DEFAULT_MEMORY_SIZE);
@@ -148,6 +177,8 @@ public final class Options {
setSampleThreads(DEFAULT_SAMPLE_THREADS);
setStackDepth(DEFAULT_STACK_DEPTH);
setThreadBufferSize(DEFAULT_THREAD_BUFFER_SIZE);
+ setSampleObjectAllocations(DEFAULT_SAMPLE_OBJECT_ALLOCATIONS);
+ setObjectAllocationsSamplingInterval(DEFAULT_OBJECT_ALLOCATIONS_SAMPLING_INTERVAL);
}
static synchronized long getWaitInterval() {
diff --git a/src/share/classes/jdk/jfr/internal/PlatformRecorder.java b/src/share/classes/jdk/jfr/internal/PlatformRecorder.java
index cbea2e45aac08707726395ac15185b42a459ba39..7011541e6a812c629741d73a2e5913dc6a360a95 100644
--- a/src/share/classes/jdk/jfr/internal/PlatformRecorder.java
+++ b/src/share/classes/jdk/jfr/internal/PlatformRecorder.java
@@ -47,6 +47,7 @@ import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
+import jdk.jfr.EventNames;
import jdk.jfr.EventType;
import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener;
@@ -551,4 +552,8 @@ public final class PlatformRecorder {
target.setStopTime(endTime);
target.setInternalDuration(Duration.between(startTime, endTime));
}
+
+ public boolean isEnabled(String eventName) {
+ return MetadataRepository.getInstance().isEnabled(eventName);
+ }
}
diff --git a/src/share/classes/jdk/jfr/internal/PlatformRecording.java b/src/share/classes/jdk/jfr/internal/PlatformRecording.java
index 74707121d37ca09b691c2c5b742d236686eb4081..0e6e1c20f292d1959b641a5efe2371785ffeb9a4 100644
--- a/src/share/classes/jdk/jfr/internal/PlatformRecording.java
+++ b/src/share/classes/jdk/jfr/internal/PlatformRecording.java
@@ -775,4 +775,8 @@ public final class PlatformRecording implements AutoCloseable {
public SafePath getDumpOnExitDirectory() {
return this.dumpOnExitDirectory;
}
+
+ public boolean isRecorderEnabled(String eventName) {
+ return recorder.isEnabled(eventName);
+ }
}
diff --git a/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java b/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java
index f15053a7caafd02106fd65f288589c68d068da43..a7bd31df36be4e373426a308d8b0392ecd275425 100644
--- a/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java
+++ b/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java
@@ -43,6 +43,7 @@ final class DCmdConfigure extends AbstractDCmd {
/**
* Execute JFR.configure.
*
+ * @param onVMStart if cmd is invoked at the moment when vm is started
* @param repositoryPath the path
* @param dumpPath path to dump to on fatal error (oom)
* @param stackDepth depth of stack traces
@@ -51,6 +52,8 @@ final class DCmdConfigure extends AbstractDCmd {
* @param threadBufferSize size of thread buffer for events
* @param maxChunkSize threshold at which a new chunk is created in the disk repository
* @param sampleThreads if thread sampling should be enabled
+ * @param sampleObjectAllocations if object allocations sampling should be enbaled
+ * @param objectAllocationsSamplingInterval interval of object allocations samplings
*
* @return result
@@ -59,6 +62,7 @@ final class DCmdConfigure extends AbstractDCmd {
*/
public String execute
(
+ Boolean onVMStart,
String repositoryPath,
String dumpPath,
Integer stackDepth,
@@ -67,8 +71,9 @@ final class DCmdConfigure extends AbstractDCmd {
Long threadBufferSize,
Long memorySize,
Long maxChunkSize,
- Boolean sampleThreads
-
+ Boolean sampleThreads,
+ Boolean sampleObjectAllocations,
+ Long objectAllocationsSamplingInterval
) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdConfigure: repositorypath=" + repositoryPath +
@@ -82,6 +87,13 @@ final class DCmdConfigure extends AbstractDCmd {
", samplethreads" + sampleThreads);
}
+ // Check parameters correctness
+ if (!onVMStart) {
+ if (sampleObjectAllocations != null || objectAllocationsSamplingInterval != null) {
+ Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not change sampleObjectAllocations and objectAllocationsSamplingInterval during application's running");
+ return getResult();
+ }
+ }
boolean updated = false;
if (repositoryPath != null) {
@@ -152,6 +164,20 @@ final class DCmdConfigure extends AbstractDCmd {
updated = true;
}
+ if (objectAllocationsSamplingInterval != null) {
+ Options.setObjectAllocationsSamplingInterval(objectAllocationsSamplingInterval);
+ Logger.log(LogTag.JFR, LogLevel.INFO, "object allocations sampling interval set to " + objectAllocationsSamplingInterval);
+ printObjectAllocationsSamplingInterval();
+ updated = true;
+ }
+
+ if (sampleObjectAllocations != null) {
+ Options.setSampleObjectAllocations(sampleObjectAllocations);
+ Logger.log(LogTag.JFR, LogLevel.INFO, "Sample object allocations set to " + sampleObjectAllocations);
+ printSampleObjectAllocations();
+ updated = true;
+ }
+
if (!updated) {
println("Current configuration:");
println();
@@ -163,6 +189,8 @@ final class DCmdConfigure extends AbstractDCmd {
printMemorySize();
printMaxChunkSize();
printSampleThreads();
+ printSampleObjectAllocations();
+ printObjectAllocationsSamplingInterval();
}
return getResult();
}
@@ -214,4 +242,12 @@ final class DCmdConfigure extends AbstractDCmd {
printBytes(Options.getMaxChunkSize());
println();
}
+
+ private void printSampleObjectAllocations() {
+ println("Sample object allocations: " + Options.getSampleObjectAllocations());
+ }
+
+ private void printObjectAllocationsSamplingInterval() {
+ println("objects allocations sampling interval: " + Options.getObjectAllocationsSamplingInterval());
+ }
}
diff --git a/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java b/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
index c198e9b03eac1d9fc2e687be7d180557bc16c29d..09bd717c18710cf8a0d454b2236715c03d575c7a 100644
--- a/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
+++ b/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
@@ -36,12 +36,14 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import jdk.jfr.EventNames;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Recording;
import jdk.jfr.internal.JVM;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.Options;
import jdk.jfr.internal.OldObjectSample;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport.SafePath;
@@ -116,7 +118,7 @@ final class DCmdStart extends AbstractDCmd {
} catch(FileNotFoundException e) {
throw new DCmdException("Could not find settings file'" + configName + "'", e);
} catch (IOException | ParseException e) {
- throw new DCmdException("Could not parse settings file '" + settings[0] + "'", e);
+ throw new DCmdException("Could not parse settings file '" + configName + "'", e);
}
}
@@ -151,6 +153,14 @@ final class DCmdStart extends AbstractDCmd {
recording.setSettings(s);
SafePath safePath = null;
+ if (recording.isRecorderEnabled(EventNames.OptoInstanceObjectAllocation) ||
+ recording.isRecorderEnabled(EventNames.OptoArrayObjectAllocation)) {
+ if (!Options.getSampleObjectAllocations()) {
+ println("Please add -XX:FlightRecorderOptions=sampleobjectallocations=true in JVM options.");
+ return getResult();
+ }
+ }
+
if (path != null) {
try {
if (dumpOnExit == null) {
diff --git a/test/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java b/test/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dbee122d55ce62d018273bcb0c84977b495c3f2
--- /dev/null
+++ b/test/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2019 Alibaba Group Holding Limited. 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. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ *
+ */
+package jfr.event.objectsprofiling;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedClass;
+import jdk.jfr.consumer.RecordedStackTrace;
+import jdk.jfr.consumer.RecordedFrame;
+
+import sun.hotspot.WhiteBox;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.jfr.Events;
+import jdk.test.lib.Utils;
+
+/*
+ * @test TestOptoObjectAllocationsSampling
+ * @library /lib /
+ *
+ * @build sun.hotspot.WhiteBox
+ * @build ClassFileInstaller
+ *
+ * @compile -g TestOptoObjectAllocationsSampling.java
+ *
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ *
+ * @run main/othervm -Xbootclasspath/a:.
+ * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation
+ * -XX:+LogJFR -XX:FlightRecorderOptions=sampleobjectallocations=true,objectallocationssamplinginterval=1
+ * jfr.event.objectsprofiling.TestOptoObjectAllocationsSampling
+ */
+public class TestOptoObjectAllocationsSampling {
+ private static final WhiteBox WB;
+ private static final Method OPTO_METHOD;
+ private static final int OPTO_METHOD_INVOKE_COUNT = 10;
+ private static final String OPTO_METHOD_NAME = "fireOptoAllocations";
+ private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
+ private static final int RECORDED_ARRAY_CLASS_OBJECT_SIZE_MAGIC_CODE = 0x1111baba;
+
+ static {
+ try {
+ WB = WhiteBox.getWhiteBox();
+ Class> thisClass = TestOptoObjectAllocationsSampling.class;
+ OPTO_METHOD = thisClass.getMethod(OPTO_METHOD_NAME, Integer.TYPE, Byte.TYPE);
+ } catch (Exception e) {
+ Asserts.fail(e.getMessage());
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private static class InstanceObject {
+ private byte content;
+
+ InstanceObject(byte content) {
+ this.content = content;
+ }
+
+ public String toString() {
+ return "InstanceObject ( " + content + " )";
+ }
+ }
+
+ public static Object array;
+ public static Object instance;
+
+ public static void fireOptoAllocations(int arrayLength, byte b) {
+ array = new InstanceObject[arrayLength];
+ instance = new InstanceObject(b);
+ }
+
+ private static void ensureOptoMethod() throws Exception {
+ int initialCompLevel = WB.getMethodCompilationLevel(OPTO_METHOD);
+ if (initialCompLevel != COMP_LEVEL_FULL_OPTIMIZATION) {
+ WB.enqueueMethodForCompilation(OPTO_METHOD, COMP_LEVEL_FULL_OPTIMIZATION);
+ }
+ Utils.waitForCondition(() -> COMP_LEVEL_FULL_OPTIMIZATION == WB.getMethodCompilationLevel(OPTO_METHOD), 30_000L);
+ System.out.format("%s is already compiled at full optimization compile level\n", OPTO_METHOD_NAME);
+ }
+
+ private static void assertOptoMethod() throws Exception {
+ int compLevel = WB.getMethodCompilationLevel(OPTO_METHOD);
+ Asserts.assertTrue(compLevel == COMP_LEVEL_FULL_OPTIMIZATION);
+ }
+
+ private static void runForLong(int arg1, byte arg2) {
+ for (int i = 0; i < 1000_000; i++) {
+ try {
+ ensureOptoMethod();
+ fireOptoAllocations(arg1, arg2);
+ TimeUnit.SECONDS.sleep(1);
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ int arg1 = 3*1024;
+ byte arg2 = (byte)0x66;
+
+ // Run warm code to prevent deoptimizaiton.
+ // It will deoptimize if it runs without this warmup step.
+ fireOptoAllocations(arg1, arg2);
+ ensureOptoMethod();
+ assertOptoMethod();
+
+ // runForLong(arg1, arg2);
+
+ try (Recording recording = new Recording()) {
+ recording.enable(EventNames.OptoArrayObjectAllocation);
+ recording.enable(EventNames.OptoInstanceObjectAllocation);
+
+ recording.start();
+
+ // fireOptoAllocationsMethod would be deoptimized after jfr recording is started.
+ // The root cause is not clear.
+ // Invoke ensureOptoMethod blindly to enforce it in level 4 again.
+ ensureOptoMethod();
+
+ for (int i = 0; i < OPTO_METHOD_INVOKE_COUNT; i++) {
+ assertOptoMethod();
+ fireOptoAllocations(arg1, arg2);
+ WB.youngGC();
+ }
+
+ recording.stop();
+
+ List