提交 44c71bc8 编写于 作者: D Denghui Dong 提交者: D-D-H

Revert "Revert "[JFR] add support for opto object allocations sampling""

This reverts commit 98c962d6.
上级 e31837fc
/*
* 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";
}
......@@ -673,4 +673,7 @@ public final class Recording implements Closeable {
internal.setSetting(id, value);
}
public boolean isRecorderEnabled(String eventName) {
return internal.isRecorderEnabled(eventName);
}
}
......@@ -629,6 +629,16 @@
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.OptoInstanceObjectAllocation">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.OptoArrayObjectAllocation">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
......
......@@ -629,6 +629,16 @@
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.OptoInstanceObjectAllocation">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.OptoArrayObjectAllocation">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
......
......@@ -100,4 +100,27 @@ public final class RecordedClass extends RecordedObject {
public long getId() {
return uniqueId;
}
/**
* Returns the object size for the class.
* <p>
* 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("[");
}
}
......@@ -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.
*
......
......@@ -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() {
......
......@@ -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);
}
}
......@@ -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);
}
}
......@@ -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());
}
}
......@@ -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) {
......
/*
* 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<RecordedEvent> events = Events.fromRecording(recording);
int countOfInstanceObject = 0;
int countOfArrayObject = 0;
final String instanceObjectClassName = InstanceObject.class.getName();
final String arrayObjectClassName = InstanceObject[].class.getName();
for (RecordedEvent event : events) {
RecordedStackTrace stackTrace = event.getStackTrace();
Asserts.assertTrue(stackTrace != null);
List<RecordedFrame> frames = stackTrace.getFrames();
Asserts.assertTrue(frames != null && frames.size() > 0);
RecordedFrame topFrame = frames.get(0);
Asserts.assertTrue(event.hasField("objectClass"));
RecordedClass clazz = event.getValue("objectClass");
String className = clazz.getName();
Asserts.assertTrue(event.getStackTrace().getFrames().size() > 0);
int objectSize = clazz.getObjectSize();
System.out.format("Allocation Object Class Name: %s, Object Size: %x, topFrame: %s\n", className, objectSize, topFrame.getMethod());
if (className.equals(instanceObjectClassName)) {
Asserts.assertTrue(!clazz.isArray());
Asserts.assertTrue(objectSize > 0);
Asserts.assertTrue(topFrame.getLineNumber() > 0);
Asserts.assertTrue(topFrame.getBytecodeIndex() > 0);
countOfInstanceObject++;
} else if (className.equals(arrayObjectClassName)) {
Asserts.assertTrue(clazz.isArray());
Asserts.assertTrue(objectSize == RECORDED_ARRAY_CLASS_OBJECT_SIZE_MAGIC_CODE);
countOfArrayObject++;
Asserts.assertTrue(topFrame.getLineNumber() > 0);
Asserts.assertTrue(topFrame.getBytecodeIndex() > 0);
}
}
System.out.format("Total Event Count: %d, EventOptoInstanceObjectAllocaiton Count: %d, EventOptoArrayObjectAllocation Count: %d\n", events.size(), countOfInstanceObject, countOfArrayObject);
Asserts.assertTrue(countOfArrayObject == countOfInstanceObject);
Asserts.assertTrue(countOfArrayObject == OPTO_METHOD_INVOKE_COUNT);
Asserts.assertTrue(events.size() >= (countOfInstanceObject + countOfArrayObject));
}
}
}
......@@ -176,6 +176,9 @@ public class EventNames {
public final static String ActiveRecording = PREFIX + "ActiveRecording";
public final static String ActiveSetting = PREFIX + "ActiveSetting";
public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation";
public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation";
public static boolean isGcEvent(EventType et) {
return et.getCategoryNames().contains(GC_CATEGORY);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册