Skip to content

Commit c70258c

Browse files
committed
8358619: Fix interval recomputation in CPU Time Profiler
Reviewed-by: jbachorik, mgronlun
1 parent 9697e5b commit c70258c

File tree

10 files changed

+197
-86
lines changed

10 files changed

+197
-86
lines changed

src/hotspot/share/jfr/jni/jfrJniMethod.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,15 @@ NO_TRANSITION(jboolean, jfr_set_throttle(JNIEnv* env, jclass jvm, jlong event_ty
170170
return JNI_TRUE;
171171
NO_TRANSITION_END
172172

173-
JVM_ENTRY_NO_ENV(void, jfr_set_cpu_throttle(JNIEnv* env, jclass jvm, jdouble rate, jboolean auto_adapt))
173+
JVM_ENTRY_NO_ENV(void, jfr_set_cpu_rate(JNIEnv* env, jclass jvm, jdouble rate))
174174
JfrEventSetting::set_enabled(JfrCPUTimeSampleEvent, rate > 0);
175-
JfrCPUTimeThreadSampling::set_rate(rate, auto_adapt == JNI_TRUE);
175+
JfrCPUTimeThreadSampling::set_rate(rate);
176+
JVM_END
177+
178+
JVM_ENTRY_NO_ENV(void, jfr_set_cpu_period(JNIEnv* env, jclass jvm, jlong period_nanos))
179+
assert(period_nanos >= 0, "invariant");
180+
JfrEventSetting::set_enabled(JfrCPUTimeSampleEvent, period_nanos > 0);
181+
JfrCPUTimeThreadSampling::set_period(period_nanos);
176182
JVM_END
177183

178184
NO_TRANSITION(void, jfr_set_miscellaneous(JNIEnv* env, jclass jvm, jlong event_type_id, jlong value))

src/hotspot/share/jfr/jni/jfrJniMethod.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ jlong JNICALL jfr_get_unloaded_event_classes_count(JNIEnv* env, jclass jvm);
129129

130130
jboolean JNICALL jfr_set_throttle(JNIEnv* env, jclass jvm, jlong event_type_id, jlong event_sample_size, jlong period_ms);
131131

132-
void JNICALL jfr_set_cpu_throttle(JNIEnv* env, jclass jvm, jdouble rate, jboolean auto_adapt);
132+
void JNICALL jfr_set_cpu_rate(JNIEnv* env, jclass jvm, jdouble rate);
133+
134+
void JNICALL jfr_set_cpu_period(JNIEnv* env, jclass jvm, jlong period_nanos);
133135

134136
void JNICALL jfr_set_miscellaneous(JNIEnv* env, jclass jvm, jlong id, jlong value);
135137

src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
8383
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
8484
(char*)"setMiscellaneous", (char*)"(JJ)V", (void*)jfr_set_miscellaneous,
8585
(char*)"setThrottle", (char*)"(JJJ)Z", (void*)jfr_set_throttle,
86-
(char*)"setCPUThrottle", (char*)"(DZ)V", (void*)jfr_set_cpu_throttle,
86+
(char*)"setCPURate", (char*)"(D)V", (void*)jfr_set_cpu_rate,
87+
(char*)"setCPUPeriod", (char*)"(J)V", (void*)jfr_set_cpu_period,
8788
(char*)"emitOldObjectSamples", (char*)"(JZZ)V", (void*)jfr_emit_old_object_samples,
8889
(char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk,
8990
(char*)"exclude", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_exclude_thread,

src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
#include "signals_posix.hpp"
4747

48-
static const int64_t AUTOADAPT_INTERVAL_MS = 100;
48+
static const int64_t RECOMPUTE_INTERVAL_MS = 100;
4949

5050
static bool is_excluded(JavaThread* jt) {
5151
return jt->is_hidden_from_external_view() ||
@@ -163,20 +163,42 @@ void JfrCPUTimeTraceQueue::clear() {
163163
Atomic::release_store(&_head, (u4)0);
164164
}
165165

166-
static int64_t compute_sampling_period(double rate) {
167-
if (rate == 0) {
168-
return 0;
166+
// A throttle is either a rate or a fixed period
167+
class JfrCPUSamplerThrottle {
168+
169+
union {
170+
double _rate;
171+
u8 _period_nanos;
172+
};
173+
bool _is_rate;
174+
175+
public:
176+
177+
JfrCPUSamplerThrottle(double rate) : _rate(rate), _is_rate(true) {
178+
assert(rate >= 0, "invariant");
169179
}
170-
return os::active_processor_count() * 1000000000.0 / rate;
171-
}
180+
181+
JfrCPUSamplerThrottle(u8 period_nanos) : _period_nanos(period_nanos), _is_rate(false) {}
182+
183+
bool enabled() const { return _is_rate ? _rate > 0 : _period_nanos > 0; }
184+
185+
int64_t compute_sampling_period() const {
186+
if (_is_rate) {
187+
if (_rate == 0) {
188+
return 0;
189+
}
190+
return os::active_processor_count() * 1000000000.0 / _rate;
191+
}
192+
return _period_nanos;
193+
}
194+
};
172195

173196
class JfrCPUSamplerThread : public NonJavaThread {
174197
friend class JfrCPUTimeThreadSampling;
175198
private:
176199
Semaphore _sample;
177200
NonJavaThread* _sampler_thread;
178-
double _rate;
179-
bool _auto_adapt;
201+
JfrCPUSamplerThrottle _throttle;
180202
volatile int64_t _current_sampling_period_ns;
181203
volatile bool _disenrolled;
182204
// top bit is used to indicate that no signal handler should proceed
@@ -187,17 +209,17 @@ class JfrCPUSamplerThread : public NonJavaThread {
187209

188210
static const u4 STOP_SIGNAL_BIT = 0x80000000;
189211

190-
JfrCPUSamplerThread(double rate, bool auto_adapt);
212+
JfrCPUSamplerThread(JfrCPUSamplerThrottle& throttle);
191213

192214
void start_thread();
193215

194216
void enroll();
195217
void disenroll();
196218
void update_all_thread_timers();
197219

198-
void auto_adapt_period_if_needed();
220+
void recompute_period_if_needed();
199221

200-
void set_rate(double rate, bool auto_adapt);
222+
void set_throttle(JfrCPUSamplerThrottle& throttle);
201223
int64_t get_sampling_period() const { return Atomic::load(&_current_sampling_period_ns); };
202224

203225
void sample_thread(JfrSampleRequest& request, void* ucontext, JavaThread* jt, JfrThreadLocal* tl, JfrTicks& now);
@@ -231,18 +253,16 @@ class JfrCPUSamplerThread : public NonJavaThread {
231253
void trigger_async_processing_of_cpu_time_jfr_requests();
232254
};
233255

234-
JfrCPUSamplerThread::JfrCPUSamplerThread(double rate, bool auto_adapt) :
256+
JfrCPUSamplerThread::JfrCPUSamplerThread(JfrCPUSamplerThrottle& throttle) :
235257
_sample(),
236258
_sampler_thread(nullptr),
237-
_rate(rate),
238-
_auto_adapt(auto_adapt),
239-
_current_sampling_period_ns(compute_sampling_period(rate)),
259+
_throttle(throttle),
260+
_current_sampling_period_ns(throttle.compute_sampling_period()),
240261
_disenrolled(true),
241262
_active_signal_handlers(STOP_SIGNAL_BIT),
242263
_is_async_processing_of_cpu_time_jfr_requests_triggered(false),
243264
_warned_about_timer_creation_failure(false),
244265
_signal_handler_installed(false) {
245-
assert(rate >= 0, "invariant");
246266
}
247267

248268
void JfrCPUSamplerThread::trigger_async_processing_of_cpu_time_jfr_requests() {
@@ -321,17 +341,17 @@ void JfrCPUSamplerThread::disenroll() {
321341
void JfrCPUSamplerThread::run() {
322342
assert(_sampler_thread == nullptr, "invariant");
323343
_sampler_thread = this;
324-
int64_t last_auto_adapt_check = os::javaTimeNanos();
344+
int64_t last_recompute_check = os::javaTimeNanos();
325345
while (true) {
326346
if (!_sample.trywait()) {
327347
// disenrolled
328348
_sample.wait();
329349
}
330350
_sample.signal();
331351

332-
if (os::javaTimeNanos() - last_auto_adapt_check > AUTOADAPT_INTERVAL_MS * 1000000) {
333-
auto_adapt_period_if_needed();
334-
last_auto_adapt_check = os::javaTimeNanos();
352+
if (os::javaTimeNanos() - last_recompute_check > RECOMPUTE_INTERVAL_MS * 1000000) {
353+
recompute_period_if_needed();
354+
last_recompute_check = os::javaTimeNanos();
335355
}
336356

337357
if (Atomic::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) {
@@ -442,42 +462,50 @@ JfrCPUTimeThreadSampling::~JfrCPUTimeThreadSampling() {
442462
}
443463
}
444464

445-
void JfrCPUTimeThreadSampling::create_sampler(double rate, bool auto_adapt) {
465+
void JfrCPUTimeThreadSampling::create_sampler(JfrCPUSamplerThrottle& throttle) {
446466
assert(_sampler == nullptr, "invariant");
447-
_sampler = new JfrCPUSamplerThread(rate, auto_adapt);
467+
_sampler = new JfrCPUSamplerThread(throttle);
448468
_sampler->start_thread();
449469
_sampler->enroll();
450470
}
451471

452-
void JfrCPUTimeThreadSampling::update_run_state(double rate, bool auto_adapt) {
453-
if (rate != 0) {
472+
void JfrCPUTimeThreadSampling::update_run_state(JfrCPUSamplerThrottle& throttle) {
473+
if (throttle.enabled()) {
454474
if (_sampler == nullptr) {
455-
create_sampler(rate, auto_adapt);
475+
create_sampler(throttle);
456476
} else {
457-
_sampler->set_rate(rate, auto_adapt);
477+
_sampler->set_throttle(throttle);
458478
_sampler->enroll();
459479
}
460480
return;
461481
}
462482
if (_sampler != nullptr) {
463-
_sampler->set_rate(rate /* 0 */, auto_adapt);
483+
_sampler->set_throttle(throttle);
464484
_sampler->disenroll();
465485
}
466486
}
467487

468-
void JfrCPUTimeThreadSampling::set_rate(double rate, bool auto_adapt) {
469-
assert(rate >= 0, "invariant");
488+
void JfrCPUTimeThreadSampling::set_rate(double rate) {
489+
if (_instance == nullptr) {
490+
return;
491+
}
492+
JfrCPUSamplerThrottle throttle(rate);
493+
instance().set_throttle_value(throttle);
494+
}
495+
496+
void JfrCPUTimeThreadSampling::set_period(u8 nanos) {
470497
if (_instance == nullptr) {
471498
return;
472499
}
473-
instance().set_rate_value(rate, auto_adapt);
500+
JfrCPUSamplerThrottle throttle(nanos);
501+
instance().set_throttle_value(throttle);
474502
}
475503

476-
void JfrCPUTimeThreadSampling::set_rate_value(double rate, bool auto_adapt) {
504+
void JfrCPUTimeThreadSampling::set_throttle_value(JfrCPUSamplerThrottle& throttle) {
477505
if (_sampler != nullptr) {
478-
_sampler->set_rate(rate, auto_adapt);
506+
_sampler->set_throttle(throttle);
479507
}
480-
update_run_state(rate, auto_adapt);
508+
update_run_state(throttle);
481509
}
482510

483511
void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread *thread) {
@@ -704,24 +732,21 @@ void JfrCPUSamplerThread::stop_timer() {
704732
VMThread::execute(&op);
705733
}
706734

707-
void JfrCPUSamplerThread::auto_adapt_period_if_needed() {
735+
void JfrCPUSamplerThread::recompute_period_if_needed() {
708736
int64_t current_period = get_sampling_period();
709-
if (_auto_adapt || current_period == -1) {
710-
int64_t period = compute_sampling_period(_rate);
711-
if (period != current_period) {
712-
Atomic::store(&_current_sampling_period_ns, period);
713-
update_all_thread_timers();
714-
}
737+
int64_t period = _throttle.compute_sampling_period();
738+
if (period != current_period) {
739+
Atomic::store(&_current_sampling_period_ns, period);
740+
update_all_thread_timers();
715741
}
716742
}
717743

718-
void JfrCPUSamplerThread::set_rate(double rate, bool auto_adapt) {
719-
_rate = rate;
720-
_auto_adapt = auto_adapt;
721-
if (_rate > 0 && Atomic::load_acquire(&_disenrolled) == false) {
722-
auto_adapt_period_if_needed();
744+
void JfrCPUSamplerThread::set_throttle(JfrCPUSamplerThrottle& throttle) {
745+
_throttle = throttle;
746+
if (_throttle.enabled() && Atomic::load_acquire(&_disenrolled) == false) {
747+
recompute_period_if_needed();
723748
} else {
724-
Atomic::store(&_current_sampling_period_ns, compute_sampling_period(rate));
749+
Atomic::store(&_current_sampling_period_ns, _throttle.compute_sampling_period());
725750
}
726751
}
727752

@@ -765,12 +790,18 @@ void JfrCPUTimeThreadSampling::destroy() {
765790
_instance = nullptr;
766791
}
767792

768-
void JfrCPUTimeThreadSampling::set_rate(double rate, bool auto_adapt) {
793+
void JfrCPUTimeThreadSampling::set_rate(double rate) {
769794
if (rate != 0) {
770795
warn();
771796
}
772797
}
773798

799+
void JfrCPUTimeThreadSampling::set_period(u8 period_nanos) {
800+
if (period_nanos != 0) {
801+
warn();
802+
}
803+
}
804+
774805
void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread* thread) {
775806
}
776807

src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,16 @@ class JfrCPUTimeTraceQueue {
9595

9696
class JfrCPUSamplerThread;
9797

98+
class JfrCPUSamplerThrottle;
99+
98100
class JfrCPUTimeThreadSampling : public JfrCHeapObj {
99101
friend class JfrRecorder;
100102
private:
101103

102104
JfrCPUSamplerThread* _sampler;
103105

104-
void create_sampler(double rate, bool auto_adapt);
105-
void set_rate_value(double rate, bool auto_adapt);
106+
void create_sampler(JfrCPUSamplerThrottle& throttle);
107+
void set_throttle_value(JfrCPUSamplerThrottle& throttle);
106108

107109
JfrCPUTimeThreadSampling();
108110
~JfrCPUTimeThreadSampling();
@@ -111,10 +113,13 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj {
111113
static JfrCPUTimeThreadSampling* create();
112114
static void destroy();
113115

114-
void update_run_state(double rate, bool auto_adapt);
116+
void update_run_state(JfrCPUSamplerThrottle& throttle);
117+
118+
static void set_rate(JfrCPUSamplerThrottle& throttle);
115119

116120
public:
117-
static void set_rate(double rate, bool auto_adapt);
121+
static void set_rate(double rate);
122+
static void set_period(u8 nanos);
118123

119124
static void on_javathread_create(JavaThread* thread);
120125
static void on_javathread_terminate(JavaThread* thread);
@@ -140,7 +145,8 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj {
140145
static void destroy();
141146

142147
public:
143-
static void set_rate(double rate, bool auto_adapt);
148+
static void set_rate(double rate);
149+
static void set_period(u8 nanos);
144150

145151
static void on_javathread_create(JavaThread* thread);
146152
static void on_javathread_terminate(JavaThread* thread);

src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,24 @@ public final class JVM {
273273
/**
274274
* Set the maximum event emission rate for the CPU time sampler
275275
*
276+
* Use {@link #setCPUPeriod(long)} if you want a fixed sampling period instead.
277+
*
276278
* Setting rate to 0 turns off the CPU time sampler.
277279
*
278280
* @param rate the new rate in events per second
279-
* @param autoAdapt true if the rate should be adapted automatically
280281
*/
281-
public static native void setCPUThrottle(double rate, boolean autoAdapt);
282+
public static native void setCPURate(double rate);
283+
284+
/**
285+
* Set the fixed CPU time sampler period.
286+
*
287+
* Use {@link #setCPURate(double)} if you want a fixed rate with an auto-adjusted period instead.
288+
*
289+
* Setting period to 0 turns off the CPU time sampler.
290+
*
291+
* @param periodNanos the new fixed period in nanoseconds
292+
*/
293+
public static native void setCPUPeriod(long periodNanos);
282294

283295
/**
284296
* Sets the file where data should be written.

src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,11 @@ public void setCPUThrottle(TimespanRate rate) {
204204
if (isCPUTimeMethodSampling) {
205205
this.cpuRate = rate;
206206
if (isEnabled()) {
207-
JVM.setCPUThrottle(rate.rate(), rate.autoAdapt());
207+
if (rate.isRate()) {
208+
JVM.setCPURate(rate.rate());
209+
} else {
210+
JVM.setCPUPeriod(rate.periodNanos());
211+
}
208212
}
209213
}
210214
}
@@ -270,8 +274,12 @@ public void setEnabled(boolean enabled) {
270274
long p = enabled ? period : 0;
271275
JVM.setMethodSamplingPeriod(getId(), p);
272276
} else if (isCPUTimeMethodSampling) {
273-
TimespanRate r = enabled ? cpuRate : new TimespanRate(0, false);
274-
JVM.setCPUThrottle(r.rate(), r.autoAdapt());
277+
TimespanRate r = enabled ? cpuRate : TimespanRate.OFF;
278+
if (r.isRate()) {
279+
JVM.setCPURate(r.rate());
280+
} else {
281+
JVM.setCPUPeriod(r.periodNanos());
282+
}
275283
} else {
276284
JVM.setEnabled(getId(), enabled);
277285
}

0 commit comments

Comments
 (0)