From 423386885af50cf6f956d1b1ba159a994045a8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E6=98=82?= Date: Thu, 14 Nov 2019 17:46:01 +0800 Subject: [PATCH] [JFR] add support for opto object allocations sampling Summary: add support for opto object allocations sampling Test Plan: hotspot/test/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java Reviewers: zhengxiaolinX, luchsh, sanhong Issue: https://github.com/alibaba/dragonwell8/issues/64 --- src/share/classes/jdk/jfr/EventNames.java | 32 +++++++++++++++ src/share/classes/jdk/jfr/Recording.java | 3 ++ .../jdk/jfr/consumer/RecordedClass.java | 23 +++++++++++ src/share/classes/jdk/jfr/internal/JVM.java | 14 +++++++ .../classes/jdk/jfr/internal/Options.java | 31 ++++++++++++++ .../jdk/jfr/internal/PlatformRecorder.java | 5 +++ .../jdk/jfr/internal/PlatformRecording.java | 4 ++ .../jdk/jfr/internal/dcmd/DCmdConfigure.java | 41 ++++++++++++++++++- .../jdk/jfr/internal/dcmd/DCmdStart.java | 13 +++++- .../jdk/testlibrary/jfr/EventNames.java | 2 + 10 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 src/share/classes/jdk/jfr/EventNames.java diff --git a/src/share/classes/jdk/jfr/EventNames.java b/src/share/classes/jdk/jfr/EventNames.java new file mode 100644 index 000000000..c384bc904 --- /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 be8d8bc64..04f81e8b0 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 cd0556bc9..4ca121a34 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 d12d62f9a..6cdd2c5d8 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 ba8a01b0c..ed11a52b9 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 78c377708..d7e0de959 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 93f3c5db8..b6b23396d 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 638b63754..71d10f5b7 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 291390f04..21a4dcb02 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 a026ec54d..f6a9a53a7 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); -- GitLab