From c70258ca1cd074392b5bf844bf6f7b80601f45cc Mon Sep 17 00:00:00 2001 From: Johannes Bechberger Date: Tue, 15 Jul 2025 10:58:02 +0000 Subject: [PATCH] 8358619: Fix interval recomputation in CPU Time Profiler Reviewed-by: jbachorik, mgronlun --- src/hotspot/share/jfr/jni/jfrJniMethod.cpp | 10 +- src/hotspot/share/jfr/jni/jfrJniMethod.hpp | 4 +- .../jfr/jni/jfrJniMethodRegistration.cpp | 3 +- .../sampling/jfrCPUTimeThreadSampler.cpp | 127 +++++++++++------- .../sampling/jfrCPUTimeThreadSampler.hpp | 16 ++- .../share/classes/jdk/jfr/internal/JVM.java | 16 ++- .../jdk/jfr/internal/PlatformEventType.java | 14 +- .../internal/settings/CPUThrottleSetting.java | 12 +- .../jdk/jfr/internal/util/TimespanRate.java | 67 +++++++-- .../TestCPUTimeAndExecutionSample.java | 16 +-- 10 files changed, 198 insertions(+), 87 deletions(-) diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index bc2412a90c1..a09da529b1b 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -170,9 +170,15 @@ NO_TRANSITION(jboolean, jfr_set_throttle(JNIEnv* env, jclass jvm, jlong event_ty return JNI_TRUE; NO_TRANSITION_END -JVM_ENTRY_NO_ENV(void, jfr_set_cpu_throttle(JNIEnv* env, jclass jvm, jdouble rate, jboolean auto_adapt)) +JVM_ENTRY_NO_ENV(void, jfr_set_cpu_rate(JNIEnv* env, jclass jvm, jdouble rate)) JfrEventSetting::set_enabled(JfrCPUTimeSampleEvent, rate > 0); - JfrCPUTimeThreadSampling::set_rate(rate, auto_adapt == JNI_TRUE); + JfrCPUTimeThreadSampling::set_rate(rate); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_set_cpu_period(JNIEnv* env, jclass jvm, jlong period_nanos)) + assert(period_nanos >= 0, "invariant"); + JfrEventSetting::set_enabled(JfrCPUTimeSampleEvent, period_nanos > 0); + JfrCPUTimeThreadSampling::set_period(period_nanos); JVM_END NO_TRANSITION(void, jfr_set_miscellaneous(JNIEnv* env, jclass jvm, jlong event_type_id, jlong value)) diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index dbea7f0180d..9769df57bd3 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -129,7 +129,9 @@ jlong JNICALL jfr_get_unloaded_event_classes_count(JNIEnv* env, jclass jvm); jboolean JNICALL jfr_set_throttle(JNIEnv* env, jclass jvm, jlong event_type_id, jlong event_sample_size, jlong period_ms); -void JNICALL jfr_set_cpu_throttle(JNIEnv* env, jclass jvm, jdouble rate, jboolean auto_adapt); +void JNICALL jfr_set_cpu_rate(JNIEnv* env, jclass jvm, jdouble rate); + +void JNICALL jfr_set_cpu_period(JNIEnv* env, jclass jvm, jlong period_nanos); void JNICALL jfr_set_miscellaneous(JNIEnv* env, jclass jvm, jlong id, jlong value); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 82ef93d95b2..63efb7404e4 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -83,7 +83,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count, (char*)"setMiscellaneous", (char*)"(JJ)V", (void*)jfr_set_miscellaneous, (char*)"setThrottle", (char*)"(JJJ)Z", (void*)jfr_set_throttle, - (char*)"setCPUThrottle", (char*)"(DZ)V", (void*)jfr_set_cpu_throttle, + (char*)"setCPURate", (char*)"(D)V", (void*)jfr_set_cpu_rate, + (char*)"setCPUPeriod", (char*)"(J)V", (void*)jfr_set_cpu_period, (char*)"emitOldObjectSamples", (char*)"(JZZ)V", (void*)jfr_emit_old_object_samples, (char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk, (char*)"exclude", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_exclude_thread, diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp index 2f063123a3d..2793a1fb984 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp @@ -45,7 +45,7 @@ #include "signals_posix.hpp" -static const int64_t AUTOADAPT_INTERVAL_MS = 100; +static const int64_t RECOMPUTE_INTERVAL_MS = 100; static bool is_excluded(JavaThread* jt) { return jt->is_hidden_from_external_view() || @@ -163,20 +163,42 @@ void JfrCPUTimeTraceQueue::clear() { Atomic::release_store(&_head, (u4)0); } -static int64_t compute_sampling_period(double rate) { - if (rate == 0) { - return 0; +// A throttle is either a rate or a fixed period +class JfrCPUSamplerThrottle { + + union { + double _rate; + u8 _period_nanos; + }; + bool _is_rate; + +public: + + JfrCPUSamplerThrottle(double rate) : _rate(rate), _is_rate(true) { + assert(rate >= 0, "invariant"); } - return os::active_processor_count() * 1000000000.0 / rate; -} + + JfrCPUSamplerThrottle(u8 period_nanos) : _period_nanos(period_nanos), _is_rate(false) {} + + bool enabled() const { return _is_rate ? _rate > 0 : _period_nanos > 0; } + + int64_t compute_sampling_period() const { + if (_is_rate) { + if (_rate == 0) { + return 0; + } + return os::active_processor_count() * 1000000000.0 / _rate; + } + return _period_nanos; + } +}; class JfrCPUSamplerThread : public NonJavaThread { friend class JfrCPUTimeThreadSampling; private: Semaphore _sample; NonJavaThread* _sampler_thread; - double _rate; - bool _auto_adapt; + JfrCPUSamplerThrottle _throttle; volatile int64_t _current_sampling_period_ns; volatile bool _disenrolled; // top bit is used to indicate that no signal handler should proceed @@ -187,7 +209,7 @@ class JfrCPUSamplerThread : public NonJavaThread { static const u4 STOP_SIGNAL_BIT = 0x80000000; - JfrCPUSamplerThread(double rate, bool auto_adapt); + JfrCPUSamplerThread(JfrCPUSamplerThrottle& throttle); void start_thread(); @@ -195,9 +217,9 @@ class JfrCPUSamplerThread : public NonJavaThread { void disenroll(); void update_all_thread_timers(); - void auto_adapt_period_if_needed(); + void recompute_period_if_needed(); - void set_rate(double rate, bool auto_adapt); + void set_throttle(JfrCPUSamplerThrottle& throttle); int64_t get_sampling_period() const { return Atomic::load(&_current_sampling_period_ns); }; void sample_thread(JfrSampleRequest& request, void* ucontext, JavaThread* jt, JfrThreadLocal* tl, JfrTicks& now); @@ -231,18 +253,16 @@ public: void trigger_async_processing_of_cpu_time_jfr_requests(); }; -JfrCPUSamplerThread::JfrCPUSamplerThread(double rate, bool auto_adapt) : +JfrCPUSamplerThread::JfrCPUSamplerThread(JfrCPUSamplerThrottle& throttle) : _sample(), _sampler_thread(nullptr), - _rate(rate), - _auto_adapt(auto_adapt), - _current_sampling_period_ns(compute_sampling_period(rate)), + _throttle(throttle), + _current_sampling_period_ns(throttle.compute_sampling_period()), _disenrolled(true), _active_signal_handlers(STOP_SIGNAL_BIT), _is_async_processing_of_cpu_time_jfr_requests_triggered(false), _warned_about_timer_creation_failure(false), _signal_handler_installed(false) { - assert(rate >= 0, "invariant"); } void JfrCPUSamplerThread::trigger_async_processing_of_cpu_time_jfr_requests() { @@ -321,7 +341,7 @@ void JfrCPUSamplerThread::disenroll() { void JfrCPUSamplerThread::run() { assert(_sampler_thread == nullptr, "invariant"); _sampler_thread = this; - int64_t last_auto_adapt_check = os::javaTimeNanos(); + int64_t last_recompute_check = os::javaTimeNanos(); while (true) { if (!_sample.trywait()) { // disenrolled @@ -329,9 +349,9 @@ void JfrCPUSamplerThread::run() { } _sample.signal(); - if (os::javaTimeNanos() - last_auto_adapt_check > AUTOADAPT_INTERVAL_MS * 1000000) { - auto_adapt_period_if_needed(); - last_auto_adapt_check = os::javaTimeNanos(); + if (os::javaTimeNanos() - last_recompute_check > RECOMPUTE_INTERVAL_MS * 1000000) { + recompute_period_if_needed(); + last_recompute_check = os::javaTimeNanos(); } if (Atomic::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) { @@ -442,42 +462,50 @@ JfrCPUTimeThreadSampling::~JfrCPUTimeThreadSampling() { } } -void JfrCPUTimeThreadSampling::create_sampler(double rate, bool auto_adapt) { +void JfrCPUTimeThreadSampling::create_sampler(JfrCPUSamplerThrottle& throttle) { assert(_sampler == nullptr, "invariant"); - _sampler = new JfrCPUSamplerThread(rate, auto_adapt); + _sampler = new JfrCPUSamplerThread(throttle); _sampler->start_thread(); _sampler->enroll(); } -void JfrCPUTimeThreadSampling::update_run_state(double rate, bool auto_adapt) { - if (rate != 0) { +void JfrCPUTimeThreadSampling::update_run_state(JfrCPUSamplerThrottle& throttle) { + if (throttle.enabled()) { if (_sampler == nullptr) { - create_sampler(rate, auto_adapt); + create_sampler(throttle); } else { - _sampler->set_rate(rate, auto_adapt); + _sampler->set_throttle(throttle); _sampler->enroll(); } return; } if (_sampler != nullptr) { - _sampler->set_rate(rate /* 0 */, auto_adapt); + _sampler->set_throttle(throttle); _sampler->disenroll(); } } -void JfrCPUTimeThreadSampling::set_rate(double rate, bool auto_adapt) { - assert(rate >= 0, "invariant"); +void JfrCPUTimeThreadSampling::set_rate(double rate) { if (_instance == nullptr) { return; } - instance().set_rate_value(rate, auto_adapt); + JfrCPUSamplerThrottle throttle(rate); + instance().set_throttle_value(throttle); } -void JfrCPUTimeThreadSampling::set_rate_value(double rate, bool auto_adapt) { - if (_sampler != nullptr) { - _sampler->set_rate(rate, auto_adapt); +void JfrCPUTimeThreadSampling::set_period(u8 nanos) { + if (_instance == nullptr) { + return; } - update_run_state(rate, auto_adapt); + JfrCPUSamplerThrottle throttle(nanos); + instance().set_throttle_value(throttle); +} + +void JfrCPUTimeThreadSampling::set_throttle_value(JfrCPUSamplerThrottle& throttle) { + if (_sampler != nullptr) { + _sampler->set_throttle(throttle); + } + update_run_state(throttle); } void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread *thread) { @@ -704,24 +732,21 @@ void JfrCPUSamplerThread::stop_timer() { VMThread::execute(&op); } -void JfrCPUSamplerThread::auto_adapt_period_if_needed() { +void JfrCPUSamplerThread::recompute_period_if_needed() { int64_t current_period = get_sampling_period(); - if (_auto_adapt || current_period == -1) { - int64_t period = compute_sampling_period(_rate); - if (period != current_period) { - Atomic::store(&_current_sampling_period_ns, period); - update_all_thread_timers(); - } + int64_t period = _throttle.compute_sampling_period(); + if (period != current_period) { + Atomic::store(&_current_sampling_period_ns, period); + update_all_thread_timers(); } } -void JfrCPUSamplerThread::set_rate(double rate, bool auto_adapt) { - _rate = rate; - _auto_adapt = auto_adapt; - if (_rate > 0 && Atomic::load_acquire(&_disenrolled) == false) { - auto_adapt_period_if_needed(); +void JfrCPUSamplerThread::set_throttle(JfrCPUSamplerThrottle& throttle) { + _throttle = throttle; + if (_throttle.enabled() && Atomic::load_acquire(&_disenrolled) == false) { + recompute_period_if_needed(); } else { - Atomic::store(&_current_sampling_period_ns, compute_sampling_period(rate)); + Atomic::store(&_current_sampling_period_ns, _throttle.compute_sampling_period()); } } @@ -765,12 +790,18 @@ void JfrCPUTimeThreadSampling::destroy() { _instance = nullptr; } -void JfrCPUTimeThreadSampling::set_rate(double rate, bool auto_adapt) { +void JfrCPUTimeThreadSampling::set_rate(double rate) { if (rate != 0) { warn(); } } +void JfrCPUTimeThreadSampling::set_period(u8 period_nanos) { + if (period_nanos != 0) { + warn(); + } +} + void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread* thread) { } diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp index 7c0545f4772..dae0be5c3a7 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp @@ -95,14 +95,16 @@ public: class JfrCPUSamplerThread; +class JfrCPUSamplerThrottle; + class JfrCPUTimeThreadSampling : public JfrCHeapObj { friend class JfrRecorder; private: JfrCPUSamplerThread* _sampler; - void create_sampler(double rate, bool auto_adapt); - void set_rate_value(double rate, bool auto_adapt); + void create_sampler(JfrCPUSamplerThrottle& throttle); + void set_throttle_value(JfrCPUSamplerThrottle& throttle); JfrCPUTimeThreadSampling(); ~JfrCPUTimeThreadSampling(); @@ -111,10 +113,13 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj { static JfrCPUTimeThreadSampling* create(); static void destroy(); - void update_run_state(double rate, bool auto_adapt); + void update_run_state(JfrCPUSamplerThrottle& throttle); + + static void set_rate(JfrCPUSamplerThrottle& throttle); public: - static void set_rate(double rate, bool auto_adapt); + static void set_rate(double rate); + static void set_period(u8 nanos); static void on_javathread_create(JavaThread* thread); static void on_javathread_terminate(JavaThread* thread); @@ -140,7 +145,8 @@ private: static void destroy(); public: - static void set_rate(double rate, bool auto_adapt); + static void set_rate(double rate); + static void set_period(u8 nanos); static void on_javathread_create(JavaThread* thread); static void on_javathread_terminate(JavaThread* thread); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index b91f0c337b2..841221c57d9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -273,12 +273,24 @@ public final class JVM { /** * Set the maximum event emission rate for the CPU time sampler * + * Use {@link #setCPUPeriod(long)} if you want a fixed sampling period instead. + * * Setting rate to 0 turns off the CPU time sampler. * * @param rate the new rate in events per second - * @param autoAdapt true if the rate should be adapted automatically */ - public static native void setCPUThrottle(double rate, boolean autoAdapt); + public static native void setCPURate(double rate); + + /** + * Set the fixed CPU time sampler period. + * + * Use {@link #setCPURate(double)} if you want a fixed rate with an auto-adjusted period instead. + * + * Setting period to 0 turns off the CPU time sampler. + * + * @param periodNanos the new fixed period in nanoseconds + */ + public static native void setCPUPeriod(long periodNanos); /** * Sets the file where data should be written. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java index ff3e0238cf0..c820703b887 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java @@ -204,7 +204,11 @@ public final class PlatformEventType extends Type { if (isCPUTimeMethodSampling) { this.cpuRate = rate; if (isEnabled()) { - JVM.setCPUThrottle(rate.rate(), rate.autoAdapt()); + if (rate.isRate()) { + JVM.setCPURate(rate.rate()); + } else { + JVM.setCPUPeriod(rate.periodNanos()); + } } } } @@ -270,8 +274,12 @@ public final class PlatformEventType extends Type { long p = enabled ? period : 0; JVM.setMethodSamplingPeriod(getId(), p); } else if (isCPUTimeMethodSampling) { - TimespanRate r = enabled ? cpuRate : new TimespanRate(0, false); - JVM.setCPUThrottle(r.rate(), r.autoAdapt()); + TimespanRate r = enabled ? cpuRate : TimespanRate.OFF; + if (r.isRate()) { + JVM.setCPURate(r.rate()); + } else { + JVM.setCPUPeriod(r.periodNanos()); + } } else { JVM.setEnabled(getId(), enabled); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java index c18aeef2132..997f4a84949 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java @@ -58,18 +58,18 @@ public final class CPUThrottleSetting extends SettingControl { @Override public String combine(Set values) { - TimespanRate max = null; + TimespanRate highestRate = null; for (String value : values) { TimespanRate rate = TimespanRate.of(value); if (rate != null) { - if (max == null || rate.isHigher(max)) { - max = rate; + if (highestRate == null) { + highestRate = rate; + } else { + highestRate = TimespanRate.selectHigherResolution(highestRate, rate); } - max = new TimespanRate(max.rate(), max.autoAdapt() || rate.autoAdapt()); } } - // "off" is not supported - return Objects.requireNonNullElse(max.toString(), DEFAULT_VALUE); + return Objects.requireNonNullElse(highestRate.toString(), DEFAULT_VALUE); } @Override diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java index 5d671310e3c..c35432d5d6b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java @@ -30,11 +30,22 @@ import jdk.jfr.internal.settings.CPUThrottleSetting; /** * A rate or fixed period, see {@link jdk.jfr.internal.Rate} */ -public record TimespanRate(double rate, boolean autoAdapt) { +public record TimespanRate(double rate, long periodNanos, boolean isRate) { + public static final TimespanRate OFF = new TimespanRate(0, 0, false); + + /** + * Parses the rate string. Supports + * + * + */ public static TimespanRate of(String text) { if (text.equals("off")) { - text = CPUThrottleSetting.DEFAULT_VALUE; + return OFF; } boolean isPeriod = !text.contains("/"); if (isPeriod) { @@ -43,26 +54,62 @@ public record TimespanRate(double rate, boolean autoAdapt) { return null; } if (period == 0) { - return new TimespanRate(0, false); + return OFF; } - return new TimespanRate(Runtime.getRuntime().availableProcessors() / (period / 1_000_000_000.0), false); + return new TimespanRate(0, period, false); } Rate r = Rate.of(text); if (r == null) { return null; } - return new TimespanRate(r.perSecond(), true); + return new TimespanRate(r.perSecond(), 0, true); } - public boolean isHigher(TimespanRate that) { - return rate() > that.rate(); + public static TimespanRate selectHigherResolution(TimespanRate a, TimespanRate b) { + if (a.isRate && b.isRate) { + return a.rate() > b.rate() ? a : b; + } + if (!a.isRate && !b.isRate) { + return a.periodNanos() < b.periodNanos() ? a : b; + } + if (a.isRate) { + double bRate = Runtime.getRuntime().availableProcessors() * (1_000_000_000.0 / b.periodNanos()); + return new TimespanRate(Math.max(a.rate(), bRate), 0, true); + } + double aRate = Runtime.getRuntime().availableProcessors() * (1_000_000_000.0 / a.periodNanos()); + return new TimespanRate(Math.max(aRate, b.rate()), 0, true); } @Override public String toString() { - if (autoAdapt) { - return String.format("%d/ns", (long)(rate * 1_000_000_000L)); + if (isRate) { + return toRateString(); } - return String.format("%dns", (long)(Runtime.getRuntime().availableProcessors() / rate * 1_000_000_000L)); + return toPeriodString(); + } + + private String toRateString() { + // idea: try to use the smallest unit possible where the rate is still an integer + // start with seconds, then try minutes, hours, etc. + assert isRate; + if (rate == 0) { + return "0/s"; + } + for (TimespanUnit unit : TimespanUnit.values()) { + double value = rate / unit.nanos * 1_000_000_000.0; + if (value % 1 == 0) { + return String.format("%d/%s", (long)value, unit.text); + } + } + // fallback to days if no smaller unit is found + return String.format("%d/%s", (long)(rate / TimespanUnit.DAYS.nanos * 1_000_000_000.0), TimespanUnit.DAYS.text); + } + + private String toPeriodString() { + assert !isRate; + if (periodNanos == 0) { + return "0ms"; + } + return String.format("%dns", periodNanos); } } diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java index eb8d33832b5..d0765c00bd9 100644 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java +++ b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java @@ -38,23 +38,21 @@ import jdk.test.lib.jfr.RecurseThread; */ public class TestCPUTimeAndExecutionSample { - static String sampleEvent = EventNames.CPUTimeSample; - // The period is set to 1100 ms to provoke the 1000 ms // threshold in the JVM for os::naked_short_sleep(). public static void main(String[] args) throws Exception { - run(EventNames.ExecutionSample); - run(EventNames.CPUTimeSample); - run(EventNames.ExecutionSample); - run(EventNames.CPUTimeSample); + run(EventNames.CPUTimeSample, "throttle", "1000/s"); + run(EventNames.ExecutionSample, "period", "1100ms"); + run(EventNames.CPUTimeSample, "throttle", "1100ms"); + run(EventNames.ExecutionSample, "period", "1000ms"); } - private static void run(String eventType) { + private static void run(String eventType, String attribute, String value) { RecurseThread t = new RecurseThread(50); t.setDaemon(true); try (RecordingStream rs = new RecordingStream()) { - rs.enable(sampleEvent).with("throttle", "1000/s"); - rs.onEvent(sampleEvent, e -> { + rs.enable(eventType).with(attribute, value); + rs.onEvent(eventType, e -> { t.quit(); rs.close(); });