diff --git a/src/share/classes/jdk/jfr/EventNames.java b/src/share/classes/jdk/jfr/EventNames.java new file mode 100644 index 0000000000000000000000000000000000000000..c384bc904f14b31b4723f4ed0d7c6c169c362b64 --- /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 = "com.oracle.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 be8d8bc6433ccf5c9df1ff3e22d6cc7731fe606a..04f81e8b07ae7049e4523e1733508084cce9f379 100644 --- a/src/share/classes/jdk/jfr/Recording.java +++ b/src/share/classes/jdk/jfr/Recording.java @@ -668,4 +668,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/consumer/RecordedClass.java b/src/share/classes/jdk/jfr/consumer/RecordedClass.java index cd0556bc92ffdc869cc5b3c1042425220b2bcc7c..4ca121a34a60b0b4a0b22a2a7eb2cb885016fe17 100644 --- a/src/share/classes/jdk/jfr/consumer/RecordedClass.java +++ b/src/share/classes/jdk/jfr/consumer/RecordedClass.java @@ -100,4 +100,27 @@ public final class RecordedClass extends RecordedObject { public long getId() { return uniqueId; } + + /** + * Returns the object size for the class. + *

+ * 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 d12d62f9a4dfe0c78a84fe6f411c351deac26006..6cdd2c5d8947174ef28f53a6c942af295f1c0fe7 100644 --- a/src/share/classes/jdk/jfr/internal/JVM.java +++ b/src/share/classes/jdk/jfr/internal/JVM.java @@ -287,6 +287,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 ba8a01b0cb76d17d5ea51f062996d57ef3a57e04..ed11a52b981a0a04a6f95c7ed8cdfab2794884d8 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 78c377708da21deb32612cf14f1014b26c1fa6d7..d7e0de9591e9dc024b6e9270474c64ec016bef6a 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.TimerTask; import java.util.concurrent.CopyOnWriteArrayList; import jdk.jfr.Enabled; +import jdk.jfr.EventNames; import jdk.jfr.EventType; import jdk.jfr.FlightRecorder; import jdk.jfr.FlightRecorderListener; @@ -560,4 +561,8 @@ public final class PlatformRecorder { internal.setInternalDuration(Duration.between(startTime, endTime)); return snapshot; } + + 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 93f3c5db8b9a34ffee70be5eefd42bf8b3265356..b6b23396d7ddc65e54c246da3896fe3c9157ad26 100644 --- a/src/share/classes/jdk/jfr/internal/PlatformRecording.java +++ b/src/share/classes/jdk/jfr/internal/PlatformRecording.java @@ -693,4 +693,8 @@ public final class PlatformRecording implements AutoCloseable { boolean shouldWriteMetadataEvent() { return shuoldWriteActiveRecordingEvent; } + + 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 638b637548bf3185b92f467db055b42ed15b9ca1..71d10f5b7ee668a085cbd6361c50a860c1a21275 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,9 +71,18 @@ final class DCmdConfigure extends AbstractDCmd { Long threadBufferSize, Long memorySize, Long maxChunkSize, - Boolean sampleThreads - + Boolean sampleThreads, + Boolean sampleObjectAllocations, + Long objectAllocationsSamplingInterval ) throws DCmdException { + // 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) { try { @@ -139,6 +152,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(); @@ -150,6 +177,8 @@ final class DCmdConfigure extends AbstractDCmd { printMemorySize(); printMaxChunkSize(); printSampleThreads(); + printSampleObjectAllocations(); + printObjectAllocationsSamplingInterval(); } return getResult(); } @@ -201,4 +230,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 291390f043852f5b894654300c1786e299263251..21a4dcb0276486c6b29b39e67c032879e4330ef6 100644 --- a/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java +++ b/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java @@ -32,9 +32,11 @@ import java.time.Duration; 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.Options; import jdk.jfr.internal.SecuritySupport.SafePath; import jdk.jfr.internal.Type; import jdk.jfr.internal.Utils; @@ -86,6 +88,7 @@ final class DCmdStart extends AbstractDCmd { if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) { throw new DCmdException("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename."); } + if (dumpOnExit == null && path != null) { dumpOnExit = Boolean.TRUE; } @@ -100,7 +103,7 @@ final class DCmdStart extends AbstractDCmd { try { s.putAll(JFC.createKnown(configName).getSettings()); } catch (IOException | ParseException e) { - throw new DCmdException("Could not parse setting " + configurations[0], e); + throw new DCmdException("Could not parse setting " + configName, e); } } @@ -136,6 +139,14 @@ final class DCmdStart extends AbstractDCmd { } recording.setSettings(s); + 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 { recording.setDestination(Paths.get(path)); diff --git a/test/lib/testlibrary/jdk/testlibrary/jfr/EventNames.java b/test/lib/testlibrary/jdk/testlibrary/jfr/EventNames.java index a026ec54d0eab13a2183c52554da4809c68f52d1..f6a9a53a7650a70edfeb85bfa6e9e57da713dbbe 100644 --- a/test/lib/testlibrary/jdk/testlibrary/jfr/EventNames.java +++ b/test/lib/testlibrary/jdk/testlibrary/jfr/EventNames.java @@ -173,6 +173,8 @@ public class EventNames { public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";// "os.processor.cpu_tsc"; public final static String ActiveRecording = PREFIX + "ActiveRecording";//"com.oracle.jdk.ActiveRecording" public final static String ActiveSetting = PREFIX + "ActiveSetting";//"com.oracle.jdk.ActiveSetting" + public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation"; //"com.oracle.jdk.OptoInstanceObjectAllocation" + public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation"; //"com.oracle.jdk.OptoArrayObjectAllocation" public static boolean isGcEvent(EventType et) { return et.getCategoryNames().contains(GC_CATEGORY);