8351594: JFR: Rate-limited sampling of Java events

Reviewed-by: mgronlun, alanb
This commit is contained in:
Erik Gahlin 2025-06-05 11:36:08 +00:00
parent c5daf89053
commit eb770a060a
36 changed files with 1120 additions and 238 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2025, Oracle 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
@ -205,22 +205,17 @@ public class FileInputStream extends InputStream
private int traceRead0() throws IOException {
int result = 0;
boolean endOfFile = false;
long bytesRead = 0;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
result = read0();
if (result < 0) {
endOfFile = true;
bytesRead = -1;
} else {
bytesRead = 1;
}
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
FileReadEvent.commit(start, duration, path, bytesRead, endOfFile);
}
FileReadEvent.offer(start, path, bytesRead);
}
return result;
}
@ -236,19 +231,11 @@ public class FileInputStream extends InputStream
private int traceReadBytes(byte b[], int off, int len) throws IOException {
int bytesRead = 0;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
bytesRead = readBytes(b, off, len);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
if (bytesRead < 0) {
FileReadEvent.commit(start, duration, path, 0L, true);
} else {
FileReadEvent.commit(start, duration, path, bytesRead, false);
}
}
FileReadEvent.offer(start, path, bytesRead);
}
return bytesRead;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2025, Oracle 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
@ -266,16 +266,12 @@ public class FileOutputStream extends OutputStream
private void traceWrite(int b, boolean append) throws IOException {
long bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
write(b, append);
bytesWritten = 1;
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
FileWriteEvent.commit(start, duration, path, bytesWritten);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
}
@ -310,16 +306,12 @@ public class FileOutputStream extends OutputStream
private void traceWriteBytes(byte b[], int off, int len, boolean append) throws IOException {
long bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
writeBytes(b, off, len, append);
bytesWritten = len;
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
FileWriteEvent.commit(start, duration, path, bytesWritten);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2025, Oracle 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
@ -367,21 +367,16 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private int traceRead0() throws IOException {
int result = 0;
long bytesRead = 0;
boolean endOfFile = false;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
result = read0();
if (result < 0) {
endOfFile = true;
bytesRead = -1;
} else {
bytesRead = 1;
}
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
FileReadEvent.commit(start, duration, path, bytesRead, endOfFile);
}
FileReadEvent.offer(start, path, bytesRead);
}
return result;
}
@ -404,19 +399,11 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private int traceReadBytes0(byte b[], int off, int len) throws IOException {
int bytesRead = 0;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
bytesRead = readBytes0(b, off, len);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
if (bytesRead < 0) {
FileReadEvent.commit(start, duration, path, 0L, true);
} else {
FileReadEvent.commit(start, duration, path, bytesRead, false);
}
}
FileReadEvent.offer(start, path, bytesRead);
}
return bytesRead;
}
@ -582,16 +569,12 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private void traceImplWrite(int b) throws IOException {
long bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
implWrite(b);
bytesWritten = 1;
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
FileWriteEvent.commit(start, duration, path, bytesWritten);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
}
@ -624,16 +607,12 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private void traceImplWriteBytes(byte b[], int off, int len) throws IOException {
long bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
implWriteBytes(b, off, len);
bytesWritten = len;
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
FileWriteEvent.commit(start, duration, path, bytesWritten);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2025, Oracle 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
@ -121,7 +121,7 @@ public class Throwable implements Serializable {
* Flag set by jdk.internal.event.JFRTracing to indicate if
* exceptions should be traced by JFR.
*/
static volatile boolean jfrTracing;
static boolean jfrTracing;
/**
* The JVM saves some indication of the stack backtrace in this slot.

View File

@ -965,10 +965,7 @@ public class Socket implements java.io.Closeable {
}
long start = SocketReadEvent.timestamp();
int nbytes = implRead(b, off, len);
long duration = SocketReadEvent.timestamp() - start;
if (SocketReadEvent.shouldCommit(duration)) {
SocketReadEvent.emit(start, duration, nbytes, parent.getRemoteSocketAddress(), getSoTimeout());
}
SocketReadEvent.offer(start, nbytes, parent.getRemoteSocketAddress(), getSoTimeout());
return nbytes;
}
@ -1081,10 +1078,7 @@ public class Socket implements java.io.Closeable {
}
long start = SocketWriteEvent.timestamp();
implWrite(b, off, len);
long duration = SocketWriteEvent.timestamp() - start;
if (SocketWriteEvent.shouldCommit(duration)) {
SocketWriteEvent.emit(start, duration, len, parent.getRemoteSocketAddress());
}
SocketWriteEvent.offer(start, len, parent.getRemoteSocketAddress());
}
private void implWrite(byte[] b, int off, int len) throws IOException {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle 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
@ -31,6 +31,11 @@ public final class ExceptionThrownEvent extends Event {
public String message;
public Class<?> thrownClass;
public static boolean shouldThrottleCommit(long timestamp) {
// Generated by JFR
return false;
}
public static void commit(long start, String message, Class<?> thrownClass) {
// Generated by JFR
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, Oracle 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
@ -45,11 +45,33 @@ public final class FileReadEvent extends Event {
return 0L;
}
public static boolean shouldCommit(long duration) {
public static boolean shouldThrottleCommit(long duration, long end) {
// Generated by JFR
return false;
}
/**
* Helper method to offer the data needed to potentially commit an event.
* The duration of the operation is computed using the current
* timestamp and the given start time. If the duration meets
* or exceeds the configured value and is not throttled (determined by calling the
* generated method {@link #shouldThrottleCommit(long, long)}), an event will be
* emitted by calling {@link #commit(long, long, String, long, boolean)}
*
* @param start the start time
* @param path the path
* @param bytesRead the number of bytes that were read, or -1 if the end of the file was reached
*/
public static void offer(long start, String path, long bytesRead) {
long end = timestamp();
long duration = end - start;
if (shouldThrottleCommit(duration, end)) {
boolean endOfFile = bytesRead < 0;
long bytes = endOfFile ? 0 : bytesRead;
commit(start, duration, path, bytes, endOfFile);
}
}
public static void commit(long start, long duration, String path, long bytesRead, boolean endOfFile) {
// Generated by JFR
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle 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
@ -44,11 +44,32 @@ public final class FileWriteEvent extends Event {
return 0L;
}
public static boolean shouldCommit(long duration) {
public static boolean shouldThrottleCommit(long duration, long end) {
// Generated by JFR
return false;
}
/**
* Helper method to offer the data needed to potentially commit an event.
* The duration of the operation is computed using the current
* timestamp and the given start time. If the duration meets
* or exceeds the configured value and is not throttled (determined by calling the
* generated method {@link #shouldThrottleCommit(long, long)}), an event will be
* emitted by calling {@link #commit(long, long, String, long)}
*
* @param start the start time
* @param path the path
* @param bytesRead the number of bytes that were written, or -1 if the end of the file was reached
*/
public static void offer(long start, String path, long bytesWritten) {
long end = timestamp();
long duration = end - start;
if (shouldThrottleCommit(duration, end)) {
long bytes = bytesWritten > 0 ? bytesWritten : 0;
commit(start, duration, path, bytes);
}
}
public static void commit(long start, long duration, String path, long bytesWritten) {
// Generated by JFR
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle 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
@ -74,9 +74,10 @@ public class SocketReadEvent extends Event {
* of this method is generated automatically if jfr is enabled.
*
* @param duration time in nanoseconds to complete the operation
* @param end timestamp at the end of the operation
* @return true if the event should be commited
*/
public static boolean shouldCommit(long duration) {
public static boolean shouldThrottleCommit(long duration, long end) {
// Generated by JFR
return false;
}
@ -118,8 +119,9 @@ public class SocketReadEvent extends Event {
* @param timeout maximum time to wait
*/
public static void offer(long start, long nbytes, SocketAddress remote, long timeout) {
long duration = timestamp() - start;
if (shouldCommit(duration)) {
long end = timestamp();
long duration = end - start;
if (shouldThrottleCommit(duration, end)) {
emit(start, duration, nbytes, remote, timeout);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle 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
@ -68,10 +68,11 @@ public class SocketWriteEvent extends Event {
* must exceed some threshold in order to commit the event. The implementation
* of this method is generated automatically if jfr is enabled.
*
* @param duration time in nanoseconds to complete the operation
* @param duration time to complete the operation
* @param end timestamp at the end of the operation
* @return true if the event should be commited
*/
public static boolean shouldCommit(long duration) {
public static boolean shouldThrottleCommit(long duration, long end) {
// Generated by JFR
return false;
}
@ -104,7 +105,7 @@ public class SocketWriteEvent extends Event {
* The duration of the operation is computed using the current
* timestamp and the given start time. If the duration is meets
* or exceeds the configured value (determined by calling the generated method
* {@link #shouldCommit(long)}), an event will be emitted by calling
* {@link #shouldThrottleCommit(long)}), an event will be emitted by calling
* {@link #emit(long, long, long, SocketAddress)}.
*
* @param start the start time
@ -112,8 +113,9 @@ public class SocketWriteEvent extends Event {
* @param remote the address of the remote socket being written to
*/
public static void offer(long start, long bytesWritten, SocketAddress remote) {
long duration = timestamp() - start;
if (shouldCommit(duration)) {
long end = timestamp();
long duration = end - start;
if (shouldThrottleCommit(duration, end)) {
emit(start, duration, bytesWritten, remote);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle 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
@ -37,22 +37,24 @@ public final class ThrowableTracer {
if (OutOfMemoryError.class.isAssignableFrom(clazz)) {
return;
}
if (ErrorThrownEvent.enabled()) {
if (ErrorThrownEvent.enabled() || ExceptionThrownEvent.enabled()) {
long timestamp = ErrorThrownEvent.timestamp();
ErrorThrownEvent.commit(timestamp, message, clazz);
}
if (ExceptionThrownEvent.enabled()) {
long timestamp = ExceptionThrownEvent.timestamp();
ExceptionThrownEvent.commit(timestamp, message, clazz);
if (ErrorThrownEvent.enabled()) {
ErrorThrownEvent.commit(timestamp, message, clazz);
}
if (ExceptionThrownEvent.shouldThrottleCommit(timestamp)) {
ExceptionThrownEvent.commit(timestamp, message, clazz);
}
}
numThrowables.incrementAndGet();
}
public static void traceThrowable(Class<?> clazz, String message) {
if (ExceptionThrownEvent.enabled()) {
long timestamp = ExceptionThrownEvent.timestamp();
ExceptionThrownEvent.commit(timestamp, message, clazz);
long timestamp = ErrorThrownEvent.timestamp();
if (ExceptionThrownEvent.shouldThrottleCommit(timestamp)) {
ExceptionThrownEvent.commit(timestamp, message, clazz);
}
}
numThrowables.incrementAndGet();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2025, Oracle 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
@ -264,19 +264,11 @@ public class FileChannelImpl
private int traceImplRead(ByteBuffer dst) throws IOException {
int bytesRead = 0;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
bytesRead = implRead(dst);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
if (bytesRead < 0) {
FileReadEvent.commit(start, duration, path, 0L, true);
} else {
FileReadEvent.commit(start, duration, path, bytesRead, false);
}
}
FileReadEvent.offer(start, path, bytesRead);
}
return bytesRead;
}
@ -326,19 +318,11 @@ public class FileChannelImpl
private long traceImplRead(ByteBuffer[] dsts, int offset, int length) throws IOException {
long bytesRead = 0;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
bytesRead = implRead(dsts, offset, length);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
if (bytesRead < 0) {
FileReadEvent.commit(start, duration, path, 0L, true);
} else {
FileReadEvent.commit(start, duration, path, bytesRead, false);
}
}
FileReadEvent.offer(start, path, bytesRead);
}
return bytesRead;
}
@ -385,16 +369,11 @@ public class FileChannelImpl
private int traceImplWrite(ByteBuffer src) throws IOException {
int bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
bytesWritten = implWrite(src);
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
long bytes = bytesWritten > 0 ? bytesWritten : 0;
FileWriteEvent.commit(start, duration, path, bytes);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
return bytesWritten;
}
@ -441,16 +420,11 @@ public class FileChannelImpl
private long traceImplWrite(ByteBuffer[] srcs, int offset, int length) throws IOException {
long bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
bytesWritten = implWrite(srcs, offset, length);
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
long bytes = bytesWritten > 0 ? bytesWritten : 0;
FileWriteEvent.commit(start, duration, path, bytes);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
return bytesWritten;
}
@ -1199,19 +1173,11 @@ public class FileChannelImpl
private int traceImplRead(ByteBuffer dst, long position) throws IOException {
int bytesRead = 0;
long start = 0;
long start = FileReadEvent.timestamp();
try {
start = FileReadEvent.timestamp();
bytesRead = implRead(dst, position);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
if (bytesRead < 0) {
FileReadEvent.commit(start, duration, path, 0L, true);
} else {
FileReadEvent.commit(start, duration, path, bytesRead, false);
}
}
FileReadEvent.offer(start, path, bytesRead);
}
return bytesRead;
}
@ -1271,16 +1237,11 @@ public class FileChannelImpl
private int traceImplWrite(ByteBuffer src, long position) throws IOException {
int bytesWritten = 0;
long start = 0;
long start = FileWriteEvent.timestamp();
try {
start = FileWriteEvent.timestamp();
bytesWritten = implWrite(src, position);
} finally {
long duration = FileWriteEvent.timestamp() - start;
if (FileWriteEvent.shouldCommit(duration)) {
long bytes = bytesWritten > 0 ? bytesWritten : 0;
FileWriteEvent.commit(start, duration, path, bytes);
}
FileWriteEvent.offer(start, path, bytesWritten);
}
return bytesWritten;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, Datadog, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -24,7 +24,7 @@
* questions.
*/
package jdk.jfr.internal;
package jdk.jfr;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
@ -32,14 +32,15 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jdk.jfr.MetadataDefinition;
/**
* Event annotation, determines the event emission rate in events per time unit.
* Event annotation, specifies the maximum rate of events per time unit, (for
* example, {@code "100/s"}).
* <p>
* If the event class annotated with {@code Throttle} is filtered by other
* settings, such as a {@link jdk.jfr.Threshold} or a user-defined setting, the
* throttling will happen after those settings have been applied.
*
* This setting is only supported for JVM events.
*
* @since 16
* @since 25
*/
@MetadataDefinition
@Target({ ElementType.TYPE })
@ -47,30 +48,33 @@ import jdk.jfr.MetadataDefinition;
@Retention(RetentionPolicy.RUNTIME)
public @interface Throttle {
/**
* Settings name {@code "throttle"} for configuring an event emission rate in events per time unit.
* Setting name {@code "throttle"} for configuring throttled events.
*/
public static final String NAME = "throttle";
public static final String DEFAULT = "off";
/**
* Throttle, for example {@code "100/s"}.
* The throttle rate, for example {@code "100/s"}.
* <p>
* String representation of a non-negative {@code Long} value followed by a slash ("/")
* and one of the following units<br>
* {@code "ns"} (nanoseconds)<br>
* {@code "us"} (microseconds)<br>
* {@code "ms"} (milliseconds)<br>
* {@code "s"} (seconds)<br>
* {@code "m"} (minutes)<br>
* {@code "h"} (hours)<br>
* {@code "d"} (days)<br>
* String representation of a non-negative {@code long} value followed by a
* forward slash ("/") and one of the following units: <br>
* <ul style="list-style-type:none">
* <li>{@code "ns"} (nanoseconds)</li>
* <li>{@code "us"} (microseconds)</li>
* <li>{@code "ms"} (milliseconds)</li>
* <li>{@code "s"} (seconds)</li>
* <li>{@code "m"} (minutes)</li>
* <li>{@code "h"} (hours)</li>
* <li>{@code "d"} (days)</li>
* </ul>
* <p>
* Example values, {@code "6000/m"}, {@code "10/ms"} and {@code "200/s"}.
* When zero is specified, for example {@code "0/s"}, no events are emitted.
* When {@code "off"} is specified, all events are emitted.
* <p>
* Specifying zero, for example {@code "0/s"}, results in no events being
* emitted.
* <p>
* Specifying {@code "off"} (case-sensitive) results in all events being emitted.
*
* @return the throttle value, default {@code "off"} not {@code null}
*
*/
String value() default DEFAULT;
String value() default "off";
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle 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
@ -29,6 +29,7 @@ import jdk.jfr.Category;
import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.Name;
import jdk.jfr.Throttle;
import jdk.jfr.internal.MirrorEvent;
import jdk.jfr.internal.RemoveFields;
import jdk.jfr.internal.Type;
@ -38,6 +39,7 @@ import jdk.jfr.internal.Type;
@Category("Java Application")
@Description("An object derived from java.lang.Exception has been created")
@RemoveFields("duration")
@Throttle
public final class ExceptionThrownEvent extends MirrorEvent {
@Label("Message")

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle 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
@ -30,6 +30,7 @@ import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.DataAmount;
import jdk.jfr.Name;
import jdk.jfr.Throttle;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.MirrorEvent;
@ -38,6 +39,7 @@ import jdk.jfr.internal.MirrorEvent;
@Category("Java Application")
@Description("Reading data from a file")
@StackFilter({"java.io.FileInputStream", "java.io.RandomAccessFile", "sun.nio.ch.FileChannelImpl"})
@Throttle
public final class FileReadEvent extends MirrorEvent {
@Label("Path")

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle 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
@ -30,6 +30,7 @@ import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.DataAmount;
import jdk.jfr.Name;
import jdk.jfr.Throttle;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.MirrorEvent;
@ -38,6 +39,7 @@ import jdk.jfr.internal.MirrorEvent;
@Category("Java Application")
@Description("Writing data to a file")
@StackFilter({"java.io.FileOutputStream", "java.io.RandomAccessFile", "sun.nio.ch.FileChannelImpl"})
@Throttle
public final class FileWriteEvent extends MirrorEvent {
@Label("Path")

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle 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
@ -31,6 +31,7 @@ import jdk.jfr.Label;
import jdk.jfr.DataAmount;
import jdk.jfr.Name;
import jdk.jfr.Timespan;
import jdk.jfr.Throttle;
import jdk.jfr.internal.MirrorEvent;
import jdk.jfr.internal.Type;
@ -38,6 +39,7 @@ import jdk.jfr.internal.Type;
@Label("Socket Read")
@Category("Java Application")
@Description("Reading data from a socket")
@Throttle
public final class SocketReadEvent extends MirrorEvent {
@Label("Remote Host")

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle 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
@ -30,6 +30,7 @@ import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.DataAmount;
import jdk.jfr.Name;
import jdk.jfr.Throttle;
import jdk.jfr.internal.MirrorEvent;
import jdk.jfr.internal.Type;
@ -37,6 +38,7 @@ import jdk.jfr.internal.Type;
@Label("Socket Write")
@Category("Java Application")
@Description("Writing data to a socket")
@Throttle
public final class SocketWriteEvent extends MirrorEvent {
@Label("Remote Host")

View File

@ -51,6 +51,7 @@ import jdk.jfr.Name;
import jdk.jfr.Registered;
import jdk.jfr.SettingControl;
import jdk.jfr.SettingDefinition;
import jdk.jfr.Throttle;
import jdk.jfr.internal.util.Bytecode;
import jdk.jfr.internal.util.ImplicitFields;
import jdk.jfr.internal.util.Bytecode.FieldDesc;
@ -64,6 +65,7 @@ final class ClassInspector {
private static final ClassDesc ANNOTATION_NAME = classDesc(Name.class);
private static final ClassDesc ANNOTATION_ENABLED = classDesc(Enabled.class);
private static final ClassDesc ANNOTATION_REMOVE_FIELDS = classDesc(RemoveFields.class);
private static final ClassDesc ANNOTATION_THROTTLE = classDesc(Throttle.class);
private static final String[] EMPTY_STRING_ARRAY = {};
private final ClassModel classModel;
@ -138,6 +140,20 @@ final class ClassInspector {
return true;
}
boolean isThrottled() {
String result = annotationValue(ANNOTATION_THROTTLE, String.class, "off");
if (result != null) {
return true;
}
if (superClass != null) {
Throttle t = superClass.getAnnotation(Throttle.class);
if (t != null) {
return true;
}
}
return false;
}
boolean hasStaticMethod(MethodDesc method) {
for (MethodModel m : classModel.methods()) {
if (Modifier.isStatic(m.flags().flagsMask())) {

View File

@ -44,6 +44,7 @@ import jdk.jfr.SettingControl;
import jdk.jfr.SettingDefinition;
import jdk.jfr.StackTrace;
import jdk.jfr.Threshold;
import jdk.jfr.Throttle;
import jdk.jfr.events.ActiveSettingEvent;
import jdk.jfr.events.StackFilter;
import jdk.jfr.internal.settings.CutoffSetting;
@ -55,6 +56,7 @@ import jdk.jfr.internal.settings.CPUThrottleSetting;
import jdk.jfr.internal.settings.StackTraceSetting;
import jdk.jfr.internal.settings.ThresholdSetting;
import jdk.jfr.internal.settings.ThrottleSetting;
import jdk.jfr.internal.settings.Throttler;
import jdk.jfr.internal.tracing.Modification;
import jdk.jfr.internal.util.Utils;
@ -95,6 +97,7 @@ public final class EventControl {
}
if (eventType.hasThrottle()) {
addControl(Throttle.NAME, defineThrottle(eventType));
eventType.setThrottler(new Throttler(eventType));
}
if (eventType.hasLevel()) {
addControl(Level.NAME, defineLevel(eventType));

View File

@ -60,7 +60,14 @@ import jdk.jfr.internal.util.ImplicitFields;
* Class responsible for adding instrumentation to a subclass of {@link Event}.
*
*/
final class EventInstrumentation {
public final class EventInstrumentation {
public static final long MASK_THROTTLE = 1 << 62;
public static final long MASK_THROTTLE_CHECK = 1 << 63;
public static final long MASK_THROTTLE_BITS = MASK_THROTTLE | MASK_THROTTLE_CHECK;
public static final long MASK_THROTTLE_CHECK_SUCCESS = MASK_THROTTLE_CHECK | MASK_THROTTLE;
public static final long MASK_THROTTLE_CHECK_FAIL = MASK_THROTTLE_CHECK | 0;
public static final long MASK_NON_THROTTLE_BITS = ~MASK_THROTTLE_BITS;
private static final FieldDesc FIELD_EVENT_CONFIGURATION = FieldDesc.of(Object.class, "eventConfiguration");
private static final ClassDesc TYPE_EVENT_CONFIGURATION = classDesc(EventConfiguration.class);
@ -71,11 +78,17 @@ final class EventInstrumentation {
private static final MethodDesc METHOD_BEGIN = MethodDesc.of("begin", "()V");
private static final MethodDesc METHOD_COMMIT = MethodDesc.of("commit", "()V");
private static final MethodDesc METHOD_DURATION = MethodDesc.of("duration", "(J)J");
private static final MethodDesc METHOD_THROTTLE = MethodDesc.of("throttle", "(JJ)J");
private static final MethodDesc METHOD_ENABLED = MethodDesc.of("enabled", "()Z");
private static final MethodDesc METHOD_END = MethodDesc.of("end", "()V");
private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "(J)Z");
private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT_LONG = MethodDesc.of("shouldCommit", "(J)Z");
private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_THROTTLE_COMMIT_LONG_LONG = MethodDesc.of("shouldThrottleCommit", "(JJ)Z");
private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_THROTTLE_COMMIT_LONG = MethodDesc.of("shouldThrottleCommit", "(J)Z");
private static final MethodDesc METHOD_EVENT_CONFIGURATION_GET_SETTING = MethodDesc.of("getSetting", SettingControl.class, int.class);
private static final MethodDesc METHOD_EVENT_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "()Z");
private static final MethodDesc METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG_LONG = MethodDesc.of("shouldThrottleCommit", "(JJ)Z");
private static final MethodDesc METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG = MethodDesc.of("shouldThrottleCommit", "(J)Z");
private static final MethodDesc METHOD_GET_EVENT_WRITER = MethodDesc.of("getEventWriter", "()" + TYPE_EVENT_WRITER.descriptorString());
private static final MethodDesc METHOD_IS_ENABLED = MethodDesc.of("isEnabled", "()Z");
private static final MethodDesc METHOD_RESET = MethodDesc.of("reset", "()V");
@ -88,6 +101,7 @@ final class EventInstrumentation {
private final MethodDesc staticCommitMethod;
private final boolean untypedEventConfiguration;
private final boolean guardEventConfiguration;
private final boolean throttled;
/**
* Creates an EventInstrumentation object.
@ -110,6 +124,11 @@ final class EventInstrumentation {
this.eventClassDesc = inspector.getClassDesc();
this.staticCommitMethod = inspector.findStaticCommitMethod();
this.untypedEventConfiguration = hasUntypedConfiguration();
if (inspector.isJDK()) {
this.throttled = inspector.hasStaticMethod(METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG_LONG);
} else {
this.throttled = inspector.isThrottled();
}
}
byte[] buildInstrumented() {
@ -147,6 +166,12 @@ final class EventInstrumentation {
if (isMethod(method, METHOD_SHOULD_COMMIT_LONG)) {
return this::methodShouldCommitStatic;
}
if (isMethod(method, METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG_LONG)) {
return this::methodShouldCommitThrottleStaticLongLong;
}
if (isMethod(method, METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG)) {
return this::methodShouldCommitThrottleStaticLong;
}
if (isMethod(method, METHOD_TIME_STAMP)) {
return this::methodTimestamp;
}
@ -188,11 +213,11 @@ final class EventInstrumentation {
if (!inspector.hasDuration()) {
throwMissingDuration(codeBuilder, "end");
} else {
codeBuilder.aload(0);
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_START_TIME);
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_DURATION);
putfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
setDuration(codeBuilder, cb -> {
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_START_TIME);
invokestatic(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_DURATION);
});
codeBuilder.return_();
}
}
@ -205,9 +230,8 @@ final class EventInstrumentation {
}
// if (!eventConfiguration.shouldCommit(duration) goto fail;
getEventConfiguration(codeBuilder);
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT);
getDuration(codeBuilder);
invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT_LONG);
codeBuilder.ifeq(fail);
List<SettingDesc> settingDescs = inspector.getSettings();
for (int index = 0; index < settingDescs.size(); index++) {
@ -222,6 +246,30 @@ final class EventInstrumentation {
codeBuilder.invokevirtual(eventClassDesc, sd.methodName(), mdesc);
codeBuilder.ifeq(fail);
}
if (throttled) {
// long d = eventConfiguration.throttle(this.duration);
// this.duration = d;
// if (d & MASK_THROTTLE_BIT == 0) {
// goto fail;
// }
getEventConfiguration(codeBuilder);
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_START_TIME);
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
Bytecode.invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_THROTTLE);
int result = codeBuilder.allocateLocal(TypeKind.LONG);
codeBuilder.lstore(result);
codeBuilder.aload(0);
codeBuilder.lload(result);
putfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
codeBuilder.lload(result);
codeBuilder.ldc(MASK_THROTTLE);
codeBuilder.land();
codeBuilder.lconst_0();
codeBuilder.lcmp();
codeBuilder.ifeq(fail);
}
// return true
codeBuilder.iconst_1();
codeBuilder.ireturn();
@ -294,6 +342,17 @@ final class EventInstrumentation {
}
private void methodShouldCommitStatic(CodeBuilder codeBuilder) {
methodShouldCommitStatic(codeBuilder, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT_LONG);
}
private void methodShouldCommitThrottleStaticLongLong(CodeBuilder codeBuilder) {
methodShouldCommitStatic(codeBuilder, METHOD_EVENT_CONFIGURATION_SHOULD_THROTTLE_COMMIT_LONG_LONG);
}
private void methodShouldCommitThrottleStaticLong(CodeBuilder codeBuilder) {
methodShouldCommitStatic(codeBuilder, METHOD_EVENT_CONFIGURATION_SHOULD_THROTTLE_COMMIT_LONG);
}
private void methodShouldCommitStatic(CodeBuilder codeBuilder, MethodDesc method) {
Label fail = codeBuilder.newLabel();
if (guardEventConfiguration) {
// if (eventConfiguration == null) goto fail;
@ -302,8 +361,10 @@ final class EventInstrumentation {
}
// return eventConfiguration.shouldCommit(duration);
getEventConfiguration(codeBuilder);
codeBuilder.lload(0);
codeBuilder.invokevirtual(TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.name(), METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT.descriptor());
for (int i = 0 ; i < method.descriptor().parameterCount(); i++) {
codeBuilder.lload(2 * i);
}
codeBuilder.invokevirtual(TYPE_EVENT_CONFIGURATION, method.name(), method.descriptor());
codeBuilder.ireturn();
// fail:
codeBuilder.labelBinding(fail);
@ -515,6 +576,28 @@ final class EventInstrumentation {
return desc.matches(m);
}
private void getDuration(CodeBuilder codeBuilder) {
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
if (throttled) {
codeBuilder.loadConstant(MASK_NON_THROTTLE_BITS);
codeBuilder.land();
}
}
private void setDuration(CodeBuilder codeBuilder, Consumer<CodeBuilder> expression) {
codeBuilder.aload(0);
expression.accept(codeBuilder);
if (throttled) {
codeBuilder.aload(0);
getfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
codeBuilder.loadConstant(MASK_THROTTLE_BITS);
codeBuilder.land();
codeBuilder.lor();
}
putfield(codeBuilder, eventClassDesc, ImplicitFields.FIELD_DURATION);
}
private static void getEventWriter(CodeBuilder codeBuilder) {
invokestatic(codeBuilder, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
}

View File

@ -98,7 +98,7 @@ public final class JVMSupport {
public static void tryToInitializeJVM() {
}
static long nanosToTicks(long nanos) {
public static long nanosToTicks(long nanos) {
return (long) (nanos * JVM.getTimeConversionFactor());
}

View File

@ -45,11 +45,12 @@ import jdk.jfr.Period;
import jdk.jfr.Relational;
import jdk.jfr.StackTrace;
import jdk.jfr.Threshold;
import jdk.jfr.Throttle;
import jdk.jfr.TransitionFrom;
import jdk.jfr.TransitionTo;
import jdk.jfr.Unsigned;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.settings.ThrottleSetting;
public final class MetadataLoader {
// Caching to reduce allocation pressure and heap usage
@ -320,7 +321,7 @@ public final class MetadataLoader {
aes.add(new AnnotationElement(Cutoff.class, Cutoff.INFINITY));
}
if (t.throttle) {
aes.add(new AnnotationElement(Throttle.class, Throttle.DEFAULT));
aes.add(new AnnotationElement(Throttle.class, ThrottleSetting.DEFAULT_VALUE));
}
}
if (t.experimental) {

View File

@ -45,11 +45,13 @@ import jdk.jfr.EventType;
import jdk.jfr.Name;
import jdk.jfr.Period;
import jdk.jfr.SettingControl;
import jdk.jfr.Throttle;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.consumer.RepositoryFiles;
import jdk.jfr.internal.event.EventConfiguration;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.periodic.PeriodicEvents;
import jdk.jfr.internal.settings.Throttler;
import jdk.jfr.internal.util.Utils;
public final class MetadataRepository {
@ -214,9 +216,11 @@ public final class MetadataRepository {
}
}
EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
pEventType.setHasThrottle(pEventType.getAnnotation(Throttle.class) != null);
EventControl ec = new EventControl(pEventType, eventClass);
SettingControl[] settings = ec.getSettingControls().toArray(new SettingControl[0]);
EventConfiguration configuration = new EventConfiguration(pEventType, eventType, ec, settings, eventType.getId());
Throttler throttler = pEventType.getThrottler();
EventConfiguration configuration = new EventConfiguration(pEventType, eventType, ec, settings, throttler, eventType.getId());
pEventType.setRegistered(true);
// If class is instrumented or should not be instrumented, mark as instrumented.
if (JVM.isInstrumented(eventClass) || !JVMSupport.shouldInstrument(pEventType.isJDK(), pEventType.getName())) {

View File

@ -35,6 +35,7 @@ import jdk.jfr.internal.periodic.PeriodicEvents;
import jdk.jfr.internal.util.ImplicitFields;
import jdk.jfr.internal.util.TimespanRate;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.settings.Throttler;
import jdk.jfr.internal.tracing.Modification;
/**
@ -72,6 +73,7 @@ public final class PlatformEventType extends Type {
private boolean registered = true;
private boolean committable = enabled && registered;
private boolean hasLevel = false;
private Throttler throttler;
// package private
PlatformEventType(String name, long id, boolean isJDK, boolean dynamicSettings) {
@ -190,9 +192,11 @@ public final class PlatformEventType extends Type {
}
}
public void setThrottle(long eventSampleSize, long period_ms) {
public void setThrottle(long eventSampleSize, long periodInMillis) {
if (isJVM) {
JVM.setThrottle(getId(), eventSampleSize, period_ms);
JVM.setThrottle(getId(), eventSampleSize, periodInMillis);
} else {
throttler.configure(eventSampleSize, periodInMillis);
}
}
@ -420,4 +424,12 @@ public final class PlatformEventType extends Type {
public long getStackFilterId() {
return startFilterId;
}
public Throttler getThrottler() {
return throttler;
}
public void setThrottler(Throttler throttler) {
this.throttler = throttler;
}
}

View File

@ -28,14 +28,17 @@ package jdk.jfr.internal.event;
import jdk.jfr.EventType;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.EventControl;
import jdk.jfr.internal.EventInstrumentation;
import jdk.jfr.internal.JVM;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.settings.Throttler;
public record EventConfiguration(
PlatformEventType platformEventType,
EventType eventType,
EventControl eventControl,
SettingControl[] settings,
Throttler throttler,
long id) {
// Accessed by generated code in event class
@ -43,6 +46,19 @@ public record EventConfiguration(
return isEnabled() && duration >= platformEventType.getThresholdTicks();
}
// Accessed by generated code in event class. Used by:
// static boolean shouldThrottleCommit(long duration, long timestamp)
public boolean shouldThrottleCommit(long duration, long timestamp) {
return isEnabled() && duration >= platformEventType.getThresholdTicks() && throttler.sample(timestamp);
}
// Caller must of Event::shouldThrottleCommit must check enablement.
// Accessed by generated code in event class. Used by:
// static boolean shouldThrottleCommit(long timestamp)
public boolean shouldThrottleCommit(long timestamp) {
return throttler.sample(timestamp);
}
// Accessed by generated code in event class
public SettingControl getSetting(int index) {
return settings[index];
@ -53,6 +69,19 @@ public record EventConfiguration(
return platformEventType.isCommittable();
}
public long throttle(long startTime, long rawDuration) {
// We have already tried to throttle, return as is
if ((rawDuration & EventInstrumentation.MASK_THROTTLE_BITS) != 0) {
return rawDuration;
}
long endTime = startTime + rawDuration;
if (throttler.sample(endTime)) {
return rawDuration | EventInstrumentation.MASK_THROTTLE_CHECK_SUCCESS;
} else {
return rawDuration | EventInstrumentation.MASK_THROTTLE_CHECK_FAIL;
}
}
// Not really part of the configuration, but the method
// needs to be somewhere the event class can access,
// but not part of the public API.

View File

@ -38,7 +38,6 @@ import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.Throttle;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.util.Rate;
import jdk.jfr.internal.util.TimespanUnit;
@ -49,7 +48,7 @@ import jdk.jfr.internal.util.Utils;
@Description("Throttles the emission rate for an event")
@Name(Type.SETTINGS_PREFIX + "Throttle")
public final class ThrottleSetting extends SettingControl {
public static final String DEFAULT_VALUE = Throttle.DEFAULT;
public static final String DEFAULT_VALUE = "off";
private final PlatformEventType eventType;
private final String defaultValue;
private String value;

View File

@ -0,0 +1,233 @@
/*
* Copyright (c) 2025, Oracle 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. Oracle 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.
*
* 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 jdk.jfr.internal.settings;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
import jdk.jfr.internal.PlatformEventType;
public final class Throttler {
private static final ThrottlerParameters DISABLED_PARAMETERS = new ThrottlerParameters(0, 0, 0);
private static final long MILLIUNITS = 1000;
private static final long MINUTE = 60 * MILLIUNITS;
private static final long TEN_PER_1000_MS_IN_MINUTES = 600;
private static final long HOUR = 60 * MINUTE;
private static final long TEN_PER_1000_MS_IN_HOURS = 36000;
private static final long TEN_PER_1000_MS_IN_DAYS = 864000;
private static final long EVENT_THROTTLER_OFF = -2;
private final ReentrantLock lock = new ReentrantLock();
private final Random randomGenerator = new Random();
private final ThrottlerWindow window0 = new ThrottlerWindow();
private final ThrottlerWindow window1 = new ThrottlerWindow();
private volatile ThrottlerWindow activeWindow = window0;
// Guarded by lock
private double averagePopulationSize;
private double ewmaPopulationSize;
private long accumulatedDebtCarryLimit;
private long accumulatedDebtCarryCount;
private ThrottlerParameters lastParameters = new ThrottlerParameters(0, 0, 0);
private long sampleSize;
private long periodMillis;
private boolean disabled;
private boolean update = true;
public Throttler(PlatformEventType t) {
}
// Not synchronized in fast path, but uses volatile reads.
public boolean sample(long ticks) {
if (disabled) {
return true;
}
ThrottlerWindow current = activeWindow;
if (current.isExpired(ticks)) {
if (lock.tryLock()) {
try {
rotateWindow(ticks);
} finally {
lock.unlock();
}
}
return activeWindow.sample();
}
return current.sample();
}
public void configure(long sampleSize, long periodMillis) {
lock.lock();
try {
this.sampleSize = sampleSize;
this.periodMillis = periodMillis;
this.update = true;
this.activeWindow = configure(nextWindowParameters(), activeWindow);
} finally {
lock.unlock();
}
}
private ThrottlerWindow configure(ThrottlerParameters parameters, ThrottlerWindow expired) {
if (parameters.reconfigure) {
// Store updated params once to both windows
expired.parameters = parameters;
nextWindow(expired).parameters = parameters;
configure(parameters);
}
ThrottlerWindow next = setRate(parameters, expired);
next.initialize(parameters);
return next;
}
private void configure(ThrottlerParameters parameters) {
averagePopulationSize = 0;
ewmaPopulationSize = computeEwmaAlphaCoefficient(parameters.windowLookBackCount);
accumulatedDebtCarryLimit = computeAccumulatedDebtCarryLimit(parameters);
accumulatedDebtCarryCount = accumulatedDebtCarryLimit;
parameters.reconfigure = false;
}
private void rotateWindow(long ticks) {
ThrottlerWindow current = activeWindow;
if (current.isExpired(ticks)) {
activeWindow = configure(current.parameters.copy(), current);
}
}
private ThrottlerWindow setRate(ThrottlerParameters parameters, ThrottlerWindow expired) {
ThrottlerWindow next = nextWindow(expired);
long projectedSampleSize = parameters.samplePointsPerWindow + amortizeDebt(expired);
if (projectedSampleSize == 0) {
next.projectedPopulationSize = 0;
return next;
}
next.samplingInterval = deriveSamplingInterval(projectedSampleSize, expired);
next.projectedPopulationSize = projectedSampleSize * next.samplingInterval;
return next;
}
private long amortizeDebt(ThrottlerWindow expired) {
long accumulatedDebt = expired.accumulatedDebt();
if (accumulatedDebtCarryCount == accumulatedDebtCarryLimit) {
accumulatedDebtCarryCount = 1;
return 0;
}
accumulatedDebtCarryCount++;
return -accumulatedDebt;
}
private long deriveSamplingInterval(double sampleSize, ThrottlerWindow expired) {
double populationSize = projectPopulationSize(expired);
if (populationSize <= sampleSize) {
return 1;
}
double projectProbability = sampleSize / populationSize;
return nextGeometric(projectProbability, randomGenerator.nextDouble());
}
private double projectPopulationSize(ThrottlerWindow expired) {
averagePopulationSize = exponentiallyWeightedMovingAverage(expired.populationSize(), ewmaPopulationSize, averagePopulationSize);
return averagePopulationSize;
}
private static long nextGeometric(double p, double u) {
return (long) Math.ceil(Math.log(1.0 - adjustBoundary(u)) / Math.log(1.0 - p));
}
private static double adjustBoundary(double u) {
if (u == 0.0) {
return 0.01;
}
if (u == 1.0) {
return 0.99;
}
return u;
}
private void normalize() {
if (periodMillis == MILLIUNITS) {
return;
}
if (periodMillis == MINUTE) {
if (sampleSize >= TEN_PER_1000_MS_IN_MINUTES) {
sampleSize /= 60;
periodMillis /= 60;
}
return;
}
if (periodMillis == HOUR) {
if (sampleSize >= TEN_PER_1000_MS_IN_HOURS) {
sampleSize /= 3600;
periodMillis /= 3600;
}
return;
}
if (sampleSize >= TEN_PER_1000_MS_IN_DAYS) {
sampleSize /= 86400;
periodMillis /= 86400;
}
}
private ThrottlerParameters nextWindowParameters() {
if (update) {
return updateParameters();
}
return disabled ? DISABLED_PARAMETERS : lastParameters;
}
private ThrottlerParameters updateParameters() {
disabled = is_disabled(sampleSize);
if (disabled) {
return DISABLED_PARAMETERS;
}
normalize();
lastParameters.setSamplePointsAndWindowDuration(sampleSize, periodMillis);
lastParameters.reconfigure = true;
update = false;
return lastParameters;
}
private boolean is_disabled(long eventSampleSize) {
return eventSampleSize == EVENT_THROTTLER_OFF;
}
private double exponentiallyWeightedMovingAverage(double y, double alpha, double s) {
return alpha * y + (1 - alpha) * s;
}
private double computeEwmaAlphaCoefficient(long lookBackCount) {
return lookBackCount <= 1 ? 1.0 : 1.0 / lookBackCount;
}
private long computeAccumulatedDebtCarryLimit(ThrottlerParameters parameters) {
if (parameters.windowDurationMillis == 0 || parameters.windowDurationMillis >= 1000) {
return 1;
}
return 1000 / parameters.windowDurationMillis;
}
private ThrottlerWindow nextWindow(ThrottlerWindow expired) {
return expired == window0 ? window1 : window0;
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2025, Oracle 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. Oracle 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.
*
* 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 jdk.jfr.internal.settings;
final class ThrottlerParameters {
private static final long LOW_RATE_UPPER_BOUND = 9;
private static final long WINDOW_DIVISOR = 5;
private static final long MILLIUNITS = 1000;
private static final long MINUTE = 60 * MILLIUNITS;
private static final long TEN_PER_1000_MS_IN_MINUTES = 600;
private static final long HOUR = 60 * MINUTE;
private static final long TEN_PER_1000_MS_IN_HOURS = 36000;
private static final long DAY = 24 * HOUR;
private static final long TEN_PER_1000_MS_IN_DAYS = 864000;
private static final long DEFAULT_WINDOWS_LOOKBACK_COUNT = 25; // 25 windows == 5 seconds (for default window duration of 200 ms)
long samplePointsPerWindow;
long windowDurationMillis;
long windowLookBackCount;
boolean reconfigure;
ThrottlerParameters(long samplePointsPerWindow, long windowDuration, long windowLockBackCount) {
this.samplePointsPerWindow = samplePointsPerWindow;
this.windowDurationMillis = windowDuration;
this.windowLookBackCount = windowLockBackCount;
}
public ThrottlerParameters copy() {
return new ThrottlerParameters(samplePointsPerWindow, windowDurationMillis, windowLookBackCount);
}
void setSamplePointsAndWindowDuration(long sampleSize, long periodMillis) {
try {
if (sampleSize <= LOW_RATE_UPPER_BOUND) {
samplePointsPerWindow = sampleSize;
windowDurationMillis = periodMillis;
return;
}
if (periodMillis == MINUTE && sampleSize < TEN_PER_1000_MS_IN_MINUTES) {
samplePointsPerWindow = sampleSize;
windowDurationMillis = periodMillis;
return;
}
if (periodMillis == HOUR && sampleSize < TEN_PER_1000_MS_IN_HOURS) {
samplePointsPerWindow = sampleSize;
windowDurationMillis = periodMillis;
return;
}
if (periodMillis == DAY && sampleSize < TEN_PER_1000_MS_IN_DAYS) {
samplePointsPerWindow = sampleSize;
windowDurationMillis = periodMillis;
return;
}
samplePointsPerWindow = sampleSize / WINDOW_DIVISOR;
windowDurationMillis = periodMillis / WINDOW_DIVISOR;
} finally {
updateWindowLookback();
}
}
private void updateWindowLookback() {
if (windowDurationMillis <= MILLIUNITS) {
windowLookBackCount = DEFAULT_WINDOWS_LOOKBACK_COUNT; // 5 seconds
return;
}
if (windowDurationMillis == MINUTE) {
windowLookBackCount = 5; // 5 windows == 5 minutes
return;
}
windowLookBackCount = 1; // 1 window == 1 hour or 1 day
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2025, Oracle 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. Oracle 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.
*
* 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 jdk.jfr.internal.settings;
import jdk.jfr.internal.JVMSupport;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicLong;
import jdk.jfr.internal.JVM;
final class ThrottlerWindow {
private final AtomicLong measuredPopulationSize = new AtomicLong();
// Guarded by Throttler.lock.
ThrottlerParameters parameters = new ThrottlerParameters(0, 0, 0);
long samplingInterval = 1;
long projectedPopulationSize;
private volatile long endTicks;
void initialize(ThrottlerParameters parameters) {
if (parameters.windowDurationMillis == 0) {
endTicks = 0;
return;
}
measuredPopulationSize.set(0);
endTicks = JVM.counterTime() + JVMSupport.nanosToTicks(1_000_000L * parameters.windowDurationMillis);
}
boolean isExpired(long timestamp) {
long endTicks = this.endTicks;
if (timestamp == 0) {
return JVM.counterTime() >= endTicks;
} else {
return timestamp >= endTicks;
}
}
boolean sample() {
long ordinal = measuredPopulationSize.incrementAndGet();
return ordinal <= projectedPopulationSize && ordinal % samplingInterval == 0;
}
long maxSampleSize() {
return samplingInterval == 0 ? 0 : projectedPopulationSize / samplingInterval;
}
long sampleSize() {
long size = populationSize();
return size > projectedPopulationSize ? maxSampleSize() : size / samplingInterval;
}
long populationSize() {
return measuredPopulationSize.get();
}
long accumulatedDebt() {
if (projectedPopulationSize == 0) {
return 0;
}
return parameters.samplePointsPerWindow - maxSampleSize() + debt();
}
long debt() {
if (projectedPopulationSize == 0) {
return 0;
}
return sampleSize() - parameters.samplePointsPerWindow;
}
public String toString() {
StringJoiner sb = new StringJoiner(", ");
sb.add("measuredPopulationSize=" + measuredPopulationSize);
sb.add("samplingInterval=" + samplingInterval);
sb.add("projectedPopulationSize=" + projectedPopulationSize);
return sb.toString();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, Oracle 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
@ -177,6 +177,28 @@
* {@code "false"}</td>
* </tr>
* <tr>
* <th scope="row">{@code throttle}</th>
* <td>Specifies the maximum rate of events per time unit.</td>
* <td>{@code "off"} (no throttling)</td>
* <td>
* "off", if events should not be throttled, otherwise a string representation of a positive {@code Long} value followed by forward slash ("/") and one of the following units:
* <ul style="list-style-type:none">
* <li>{@code "ns"} (nanoseconds)
* <li>{@code "us"} (microseconds)
* <li>{@code "ms"} (milliseconds)</li>
* <li>{@code "s"} (seconds)</li>
* <li>{@code "m"} (minutes)</li>
* <li>{@code "h"} (hours)</li>
* <li>{@code "d"} (days)</li>
* </ul>
* </td>
* <td>
* {@code "off"}<br>
* {@code "100/s"}<br>
* {@code "1000/m"}
* </td>
* </tr>
* <tr>
* <th scope="row">{@code filter}</th>
* <td>Specifies the filter for the event</td>
* <td>{@code ""} (empty string)</td>

View File

@ -769,31 +769,35 @@
<event name="jdk.FileForce">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-threshold">20 ms</setting>
<setting name="threshold">20 ms</setting>
</event>
<event name="jdk.FileRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-threshold">20 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">100/s</setting>
</event>
<event name="jdk.FileWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-threshold">20 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">100/s</setting>
</event>
<event name="jdk.SocketRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-threshold">20 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">100/s</setting>
</event>
<event name="jdk.SocketWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-threshold">20 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">100/s</setting>
</event>
<event name="jdk.Deserialization">
@ -836,8 +840,9 @@
</event>
<event name="jdk.JavaExceptionThrow">
<setting name="enabled" control="enable-exceptions">false</setting>
<setting name="enabled" control="enable-exceptions">true</setting>
<setting name="stackTrace">true</setting>
<setting name="throttle" control="exceptions-throttle-rate">100/s</setting>
</event>
<event name="jdk.JavaErrorThrow">
@ -1136,20 +1141,27 @@
<test name="thread-dump" operator="equal" value="999 d"/>
</condition>
<selection name="exceptions" default="errors" label="Exceptions">
<selection name="exceptions" default="throttled" label="Exceptions">
<option label="Off" name="off">off</option>
<option label="Errors Only" name="errors">errors</option>
<option label="All Exceptions, including Errors" name="all">all</option>
<option label="Errors and 100 Exceptions Per Second" name="throttled">throttled</option>
<option label="Errors and All Exceptions" name="all">all</option>
</selection>
<condition name="enable-errors" true="true" false="false">
<or>
<test name="exceptions" operator="equal" value="errors"/>
<test name="exceptions" operator="equal" value="throttled"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>
<condition name="enable-exceptions" true="true" false="false">
<or>
<test name="exceptions" operator="equal" value="throttled"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>
<condition name="exceptions-throttle-rate" true="off" false="100/s">
<test name="exceptions" operator="equal" value="all"/>
</condition>
@ -1177,10 +1189,6 @@
<text name="locking-threshold" label="Locking Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="file-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="socket-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="method-timing" label="Method Timing" contentType="text"></text>
<text name="method-trace" label="Method Trace" contentType="text"></text>

View File

@ -769,31 +769,35 @@
<event name="jdk.FileForce">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-threshold">10 ms</setting>
<setting name="threshold">10 ms</setting>
</event>
<event name="jdk.FileRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-threshold">10 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">300/s</setting>
</event>
<event name="jdk.FileWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-threshold">10 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">300/s</setting>
</event>
<event name="jdk.SocketRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-threshold">10 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">300/s</setting>
</event>
<event name="jdk.SocketWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-threshold">10 ms</setting>
<setting name="threshold">1 ms</setting>
<setting name="throttle">300/s</setting>
</event>
<event name="jdk.Deserialization">
@ -836,8 +840,9 @@
</event>
<event name="jdk.JavaExceptionThrow">
<setting name="enabled" control="enable-exceptions">false</setting>
<setting name="enabled" control="enable-exceptions">true</setting>
<setting name="stackTrace">true</setting>
<setting name="throttle" control="exceptions-throttle-rate">300/s</setting>
</event>
<event name="jdk.JavaErrorThrow">
@ -1135,21 +1140,28 @@
<test name="thread-dump" operator="equal" value="999 d"/>
</condition>
<selection name="exceptions" default="errors" label="Exceptions">
<selection name="exceptions" default="throttled" label="Exceptions">
<option label="Off" name="off">off</option>
<option label="Errors Only" name="errors">errors</option>
<option label="All Exceptions, including Errors" name="all">all</option>
<option label="Errors and 300 Exceptions Per Second" name="throttled">throttled</option>
<option label="Errors and All Exceptions" name="all">all</option>
</selection>
<condition name="enable-errors" true="true" false="false">
<or>
<test name="exceptions" operator="equal" value="errors"/>
<test name="exceptions" operator="equal" value="throttled"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>
<condition name="enable-exceptions" true="true" false="false">
<test name="exceptions" operator="equal" value="all"/>
<or>
<test name="exceptions" operator="equal" value="throttled"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>
<condition name="exceptions-throttle-rate" true="off" false="300/s">
<test name="exceptions" operator="equal" value="all"/>
</condition>
<selection name="memory-leaks" default="stack-traces" label="Memory Leak Detection">
@ -1176,10 +1188,6 @@
<text name="locking-threshold" label="Locking Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="file-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="socket-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="method-timing" label="Method Timing" contentType="text"></text>
<text name="method-trace" label="Method Trace" contentType="text"></text>

View File

@ -0,0 +1,290 @@
/*
* Copyright (c) 2025, Oracle 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 jdk.jfr.api.metadata.annotations;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import jdk.jfr.AnnotationElement;
import jdk.jfr.Event;
import jdk.jfr.EventType;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.Threshold;
import jdk.jfr.Enabled;
import jdk.jfr.Recording;
import jdk.jfr.SettingDefinition;
import jdk.jfr.SettingDescriptor;
import jdk.jfr.Throttle;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.Events;
import jdk.jfr.SettingControl;
/**
* @test
* @requires vm.flagless
* @requires vm.hasJFR
* @library /test/lib
* @run main/othervm jdk.jfr.api.metadata.annotations.TestThrottle
*/
public class TestThrottle {
@Throttle("off")
@Enabled(false)
public static class ThrottledDisabledEvent extends Event {
}
@Throttle("off")
public static class ThrottledOffEvent extends Event {
}
@Throttle("0/s")
public static class ThrottledZeroRateEvent extends Event {
}
@Throttle("10000000/s")
public static class ThrottledHighRateEvent extends Event {
}
@Throttle("off")
@Threshold("5 h")
public static class ThrottledThresholdedEvent extends Event {
}
@Throttle("50/s")
public static class ThrottledNormalRateEvent extends Event {
public int index;
}
static class TestSetting extends SettingControl {
private boolean value;
@Override
public String combine(Set<String> values) {
if (values.contains("true")) {
return "true";
}
if (values.contains("false")) {
return "false";
}
return "true";
}
@Override
public void setValue(String text) {
value = Boolean.parseBoolean(text);
}
@Override
public String getValue() {
return "" + value;
}
}
@Throttle("10000000/s")
public static class ThrottledUserdefinedEvent extends Event {
@SettingDefinition
public boolean test(TestSetting control) {
return control.value;
}
}
@Throttle("50/s")
public static class ThrottledReuseEvent extends Event {
public int index;
}
public static void main(String[] args) throws Exception {
testThrottleDisabled();
testThrottledOff();
testThottleZeroRate();
testThrottleHighRate();
testThrottleThresholded();
testThrottleNormalRate();
testThrottleUserdefined();
}
private static void testThrottleDisabled() throws Exception {
testEvent(ThrottledDisabledEvent.class, false);
}
private static void testThrottledOff() throws Exception {
testEvent(ThrottledOffEvent.class, true);
}
private static void testThottleZeroRate() throws Exception {
testEvent(ThrottledZeroRateEvent.class, false);
}
private static void testThrottleHighRate() throws Exception {
testEvent(ThrottledHighRateEvent.class, true);
}
private static void testThrottleThresholded() throws Exception {
testEvent(ThrottledThresholdedEvent.class, false);
}
private static void testThrottleNormalRate() throws Exception {
try (RecordingStream rs = new RecordingStream()) {
AtomicInteger lastIndex = new AtomicInteger();
AtomicInteger throttled = new AtomicInteger();
rs.onEvent(ThrottledNormalRateEvent.class.getName(), e -> {
int index = e.getInt("index");
if (lastIndex.get() + 1 != index) {
throttled.incrementAndGet();
}
lastIndex.set(index);
});
rs.startAsync();
int index = 1;
while (throttled.get() < 30) {
ThrottledNormalRateEvent e = new ThrottledNormalRateEvent();
e.index = index;
e.commit();
index++;
Thread.sleep(3);
}
}
}
private static void testThrottleUserdefined() throws Exception {
testThrottleUserdefined("false", "1000000/s", false);
testThrottleUserdefined("true", "10000000/s", true);
testThrottleUserdefined("true", "0/s", false);
testThrottleUserdefined("true", "off", true);
testThrottleUserdefined("false", "off", false);
}
private static void testThrottleUserdefined(String test, String throttle, boolean emit) throws Exception {
String eventName = ThrottledUserdefinedEvent.class.getName();
try (Recording r = new Recording()) {
r.enable(eventName).with("test", test).with("throttle", throttle);
r.start();
ThrottledUserdefinedEvent e1 = new ThrottledUserdefinedEvent();
e1.commit();
ThrottledUserdefinedEvent e2 = new ThrottledUserdefinedEvent();
e2.begin();
e2.commit();
ThrottledUserdefinedEvent e3 = new ThrottledUserdefinedEvent();
e3.begin();
e3.end();
e3.commit();
ThrottledUserdefinedEvent e4 = new ThrottledUserdefinedEvent();
if (e4.shouldCommit()) {
e4.commit();
}
assertShouldCommit(e4, emit);
ThrottledUserdefinedEvent e5 = new ThrottledUserdefinedEvent();
assertShouldCommit(e5, emit);
if (e5.shouldCommit()) {
e5.commit();
}
r.stop();
assertEvents(r, eventName, emit ? 5 : 0);
}
}
@SuppressWarnings("unchecked")
private static void testEvent(Class<? extends Event> eventClass, boolean shouldCommit) throws Exception {
try (Recording r = new Recording()) {
r.start();
Constructor<Event> c = (Constructor<Event>) eventClass.getConstructor();
for (int i = 0; i < 17; i++) {
Event e = c.newInstance();
if (i % 5 == 0) {
assertShouldCommit(e, shouldCommit);
}
e.commit();
if (i % 3 == 0) {
assertShouldCommit(e, shouldCommit);
}
}
for (int i = 0; i < 50; i++) {
Event e = c.newInstance();
e.begin();
if (i % 5 == 0) {
assertShouldCommit(e, shouldCommit);
}
e.end();
if (i % 3 == 0) {
assertShouldCommit(e, shouldCommit);
}
e.commit();
if (i % 7 == 0) {
assertShouldCommit(e, shouldCommit);
}
}
for (int i = 0; i < 11; i++) {
Event e = c.newInstance();
e.begin();
e.commit();
if (i % 7 == 0) {
assertShouldCommit(e, shouldCommit);
}
}
if (shouldCommit) {
assertEvents(r, eventClass.getName(), 17 + 50 + 11);
}
}
}
private static void assertEvents(Recording r, String name, int expected) throws Exception {
int count = 0;
for (RecordedEvent event : Events.fromRecording(r)) {
if (event.getEventType().getName().equals(name)) {
count++;
}
}
if (count != expected) {
throw new Exception("Expected " + expected + " " + name + " events, but found " + count);
}
}
private static void assertShouldCommit(Event e, boolean expected) throws Exception {
if (e.shouldCommit() != expected) {
throw new Exception("Expected " + e.getClass() + "::shouldCommit() to return " + expected);
}
}
}

View File

@ -66,7 +66,7 @@ public class TestSettingsAvailability {
for (EventType parsedType : rf.readEventTypes()) {
EventType inMem = inMemoryTypes.get(parsedType.getName());
if (inMem == null) {
throw new Exception("Superflous event type " + parsedType.getName() + " in recording");
throw new Exception("Superfluous event type " + parsedType.getName() + " in recording");
}
Set<String> inMemsettings = new HashSet<>();
for (SettingDescriptor sd : inMem.getSettingDescriptors()) {
@ -75,7 +75,7 @@ public class TestSettingsAvailability {
for (SettingDescriptor parsedSetting : parsedType.getSettingDescriptors()) {
if (!inMemsettings.contains(parsedSetting.getName())) {
throw new Exception("Superflous setting " + parsedSetting.getName() + " in " + parsedType.getName());
throw new Exception("Superfluous setting " + parsedSetting.getName() + " in " + parsedType.getName());
}
inMemsettings.remove(parsedSetting.getName());
}
@ -89,14 +89,14 @@ public class TestSettingsAvailability {
private static void testKnownSettings() throws Exception {
testSetting(EventNames.JVMInformation, "enabled", "period");
testSetting(EventNames.FileRead, "enabled", "threshold", "stackTrace");
testSetting(EventNames.FileWrite, "enabled", "threshold","stackTrace");
testSetting(EventNames.FileRead, "enabled", "threshold", "stackTrace", "throttle");
testSetting(EventNames.FileWrite, "enabled", "threshold", "stackTrace", "throttle");
testSetting(EventNames.ExceptionStatistics, "enabled", "period");
testSetting(EventNames.SocketRead, "enabled", "threshold", "stackTrace");
testSetting(EventNames.SocketWrite, "enabled", "threshold", "stackTrace");
testSetting(EventNames.SocketRead, "enabled", "threshold", "stackTrace", "throttle");
testSetting(EventNames.SocketWrite, "enabled", "threshold", "stackTrace", "throttle");
testSetting(EventNames.ActiveRecording, "enabled");
testSetting(EventNames.ActiveSetting, "enabled");
testSetting(EventNames.JavaExceptionThrow, "enabled", "stackTrace");
testSetting(EventNames.JavaExceptionThrow, "enabled", "stackTrace", "throttle");
}
private static void testSetting(String eventName, String... settingNames) throws Exception {

View File

@ -51,7 +51,7 @@ import jdk.jfr.Recording;
* jdk.jfr.startupargs.TestEventSettings multipleSettings
*
* @run main/othervm
* -XX:StartFlightRecording:class-loading=true,socket-threshold=100ms
* -XX:StartFlightRecording:class-loading=true,locking-threshold=100ms
* jdk.jfr.startupargs.TestEventSettings jfcOptions
*/
public class TestEventSettings {
@ -70,7 +70,7 @@ public class TestEventSettings {
}
case "jfcOptions" -> {
assertSetting("jdk.ClassDefine#enabled","true");
assertSetting("jdk.SocketRead#threshold", "100 ms");
assertSetting("jdk.JavaMonitorEnter#threshold", "100 ms");
}
default -> throw new Exception("Uknown tes " + subTest);
}