From 9186cc7310c0cca2fca776031280f08d84e43b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Wed, 4 Jun 2025 23:55:18 +0000 Subject: [PATCH] 8358628: [BACKOUT] 8342818: Implement JEP 509: JFR CPU-Time Profiling Reviewed-by: pchilanomate, dholmes --- src/hotspot/os/posix/signals_posix.cpp | 8 - src/hotspot/os/posix/signals_posix.hpp | 2 - src/hotspot/share/jfr/jfr.inline.hpp | 3 +- src/hotspot/share/jfr/jni/jfrJniMethod.cpp | 6 - src/hotspot/share/jfr/jni/jfrJniMethod.hpp | 2 - .../jfr/jni/jfrJniMethodRegistration.cpp | 1 - src/hotspot/share/jfr/metadata/metadata.xml | 16 - .../sampling/jfrCPUTimeThreadSampler.cpp | 769 ------------------ .../sampling/jfrCPUTimeThreadSampler.hpp | 152 ---- .../periodic/sampling/jfrSampleRequest.cpp | 33 +- .../periodic/sampling/jfrSampleRequest.hpp | 5 - .../periodic/sampling/jfrThreadSampling.cpp | 92 +-- .../periodic/sampling/jfrThreadSampling.hpp | 2 - .../share/jfr/recorder/jfrRecorder.cpp | 15 - .../share/jfr/recorder/jfrRecorder.hpp | 1 - .../recorder/service/jfrEventThrottler.cpp | 12 +- .../share/jfr/support/jfrThreadLocal.cpp | 98 +-- .../share/jfr/support/jfrThreadLocal.hpp | 53 -- src/hotspot/share/runtime/thread.hpp | 1 - src/hotspot/share/runtime/vmOperation.hpp | 2 - src/hotspot/share/utilities/ticks.hpp | 1 - .../jdk/jfr/internal/EventControl.java | 4 - .../share/classes/jdk/jfr/internal/JVM.java | 10 - .../jdk/jfr/internal/PlatformEventType.java | 19 - .../classes/jdk/jfr/internal/query/view.ini | 25 +- .../internal/settings/CPUThrottleSetting.java | 89 -- .../classes/jdk/jfr/internal/util/Rate.java | 4 - .../jdk/jfr/internal/util/TimespanRate.java | 68 -- src/jdk.jfr/share/conf/jfr/default.jfc | 10 - src/jdk.jfr/share/conf/jfr/profile.jfc | 10 - .../metadata/TestLookForUntestedEvents.java | 9 +- .../profiling/BaseTestFullStackTrace.java | 176 ---- .../TestCPUTimeAndExecutionSample.java | 65 -- .../TestCPUTimeSampleFullStackTrace.java | 41 - .../TestCPUTimeSampleMultipleRecordings.java | 70 -- .../profiling/TestCPUTimeSampleNative.java | 66 -- .../TestCPUTimeSampleThrottling.java | 111 --- .../TestCPUTimeSamplingLongPeriod.java | 58 -- .../event/profiling/TestFullStackTrace.java | 131 ++- .../classes/test/RecursiveMethods.java | 76 -- test/lib/jdk/test/lib/jfr/EventNames.java | 2 - 41 files changed, 140 insertions(+), 2178 deletions(-) delete mode 100644 src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp delete mode 100644 src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp delete mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java delete mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleMultipleRecordings.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleNative.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingLongPeriod.java delete mode 100644 test/jdk/jdk/jfr/event/profiling/classes/test/RecursiveMethods.java diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 0157d354f40..e900d5695ae 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -1505,14 +1505,6 @@ bool PosixSignals::is_sig_ignored(int sig) { } } -void* PosixSignals::get_signal_handler_for_signal(int sig) { - struct sigaction oact; - if (sigaction(sig, (struct sigaction*)nullptr, &oact) == -1) { - return nullptr; - } - return get_signal_handler(&oact); -} - static void signal_sets_init() { sigemptyset(&preinstalled_sigs); diff --git a/src/hotspot/os/posix/signals_posix.hpp b/src/hotspot/os/posix/signals_posix.hpp index c1cb70df153..9deade4db18 100644 --- a/src/hotspot/os/posix/signals_posix.hpp +++ b/src/hotspot/os/posix/signals_posix.hpp @@ -52,8 +52,6 @@ public: static bool is_sig_ignored(int sig); - static void* get_signal_handler_for_signal(int sig); - static void hotspot_sigmask(Thread* thread); static void print_signal_handler(outputStream* st, int sig, char* buf, size_t buflen); diff --git a/src/hotspot/share/jfr/jfr.inline.hpp b/src/hotspot/share/jfr/jfr.inline.hpp index 5b6fc62d0ea..bdb47f600e6 100644 --- a/src/hotspot/share/jfr/jfr.inline.hpp +++ b/src/hotspot/share/jfr/jfr.inline.hpp @@ -32,8 +32,7 @@ inline bool Jfr::has_sample_request(JavaThread* jt) { assert(jt != nullptr, "invariant"); - JfrThreadLocal* tl = jt->jfr_thread_local(); - return tl->has_sample_request() || tl->has_cpu_time_jfr_requests(); + return jt->jfr_thread_local()->has_sample_request(); } inline void Jfr::check_and_process_sample_request(JavaThread* jt) { diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index bc2412a90c1..6f1c1936574 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -24,7 +24,6 @@ #include "jfr/jfr.hpp" #include "jfr/jfrEvents.hpp" -#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" #include "jfr/periodic/sampling/jfrThreadSampler.hpp" #include "jfr/recorder/jfrEventSetting.hpp" #include "jfr/recorder/jfrRecorder.hpp" @@ -170,11 +169,6 @@ 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)) - JfrEventSetting::set_enabled(JfrCPUTimeSampleEvent, rate > 0); - JfrCPUTimeThreadSampling::set_rate(rate, auto_adapt == JNI_TRUE); -JVM_END - NO_TRANSITION(void, jfr_set_miscellaneous(JNIEnv* env, jclass jvm, jlong event_type_id, jlong value)) JfrEventSetting::set_miscellaneous(event_type_id, value); const JfrEventId typed_event_id = (JfrEventId)event_type_id; diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index dbea7f0180d..9c78c6239d4 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -129,8 +129,6 @@ 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_miscellaneous(JNIEnv* env, jclass jvm, jlong id, jlong value); void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean, jboolean); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 82ef93d95b2..33a564dee2f 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -83,7 +83,6 @@ 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*)"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/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 03daca946f6..9c04ec3dca1 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -962,22 +962,6 @@ - - - - - - - - - - - - - diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp deleted file mode 100644 index ae8877ee3f2..00000000000 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ /dev/null @@ -1,769 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. 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. - * - */ - -#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" -#include "logging/log.hpp" - - -#if defined(LINUX) -#include "jfr/periodic/sampling/jfrThreadSampling.hpp" -#include "jfr/support/jfrThreadLocal.hpp" -#include "jfr/utilities/jfrTime.hpp" -#include "jfr/utilities/jfrThreadIterator.hpp" -#include "jfr/utilities/jfrTypes.hpp" -#include "jfrfiles/jfrEventClasses.hpp" -#include "memory/resourceArea.hpp" -#include "runtime/atomic.hpp" -#include "runtime/javaThread.hpp" -#include "runtime/osThread.hpp" -#include "runtime/safepointMechanism.inline.hpp" -#include "runtime/threadSMR.hpp" -#include "runtime/vmOperation.hpp" -#include "runtime/vmThread.hpp" -#include "utilities/ticks.hpp" - -#include "signals_posix.hpp" - -static const int64_t AUTOADAPT_INTERVAL_MS = 100; - -static bool is_excluded(JavaThread* jt) { - return jt->is_hidden_from_external_view() || - jt->jfr_thread_local()->is_excluded() || - jt->is_JfrRecorder_thread(); -} - -static JavaThread* get_java_thread_if_valid() { - Thread* raw_thread = Thread::current_or_null_safe(); - if (raw_thread == nullptr) { - // probably while shutting down - return nullptr; - } - assert(raw_thread->is_Java_thread(), "invariant"); - JavaThread* jt = JavaThread::cast(raw_thread); - if (is_excluded(jt) || jt->is_exiting()) { - return nullptr; - } - return jt; -} - -JfrCPUTimeTraceQueue::JfrCPUTimeTraceQueue(u4 capacity) : - _capacity(capacity), _head(0), _lost_samples(0) { - _data = JfrCHeapObj::new_array(capacity); -} - -JfrCPUTimeTraceQueue::~JfrCPUTimeTraceQueue() { - JfrCHeapObj::free(_data, _capacity * sizeof(JfrCPUTimeSampleRequest)); -} - -bool JfrCPUTimeTraceQueue::enqueue(JfrCPUTimeSampleRequest& request) { - assert(JavaThread::current()->jfr_thread_local()->is_cpu_time_jfr_enqueue_locked(), "invariant"); - assert(&JavaThread::current()->jfr_thread_local()->cpu_time_jfr_queue() == this, "invariant"); - u4 elementIndex; - do { - elementIndex = Atomic::load_acquire(&_head); - if (elementIndex >= _capacity) { - return false; - } - } while (Atomic::cmpxchg(&_head, elementIndex, elementIndex + 1) != elementIndex); - _data[elementIndex] = request; - return true; -} - -JfrCPUTimeSampleRequest& JfrCPUTimeTraceQueue::at(u4 index) { - assert(index < _head, "invariant"); - return _data[index]; -} - -static volatile u4 _lost_samples_sum = 0; - -u4 JfrCPUTimeTraceQueue::size() const { - return Atomic::load_acquire(&_head); -} - -void JfrCPUTimeTraceQueue::set_size(u4 size) { - Atomic::release_store(&_head, size); -} - -u4 JfrCPUTimeTraceQueue::capacity() const { - return _capacity; -} - -void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { - _head = 0; - JfrCHeapObj::free(_data, _capacity * sizeof(JfrCPUTimeSampleRequest)); - _data = JfrCHeapObj::new_array(capacity); - _capacity = capacity; -} - -bool JfrCPUTimeTraceQueue::is_empty() const { - return Atomic::load_acquire(&_head) == 0; -} - -u4 JfrCPUTimeTraceQueue::lost_samples() const { - return Atomic::load(&_lost_samples); -} - -void JfrCPUTimeTraceQueue::increment_lost_samples() { - Atomic::inc(&_lost_samples_sum); - Atomic::inc(&_lost_samples); -} - -u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples() { - return Atomic::xchg(&_lost_samples, (u4)0); -} - -void JfrCPUTimeTraceQueue::resize(u4 capacity) { - if (capacity != _capacity) { - set_capacity(capacity); - } -} - -void JfrCPUTimeTraceQueue::resize_for_period(u4 period_millis) { - u4 capacity = CPU_TIME_QUEUE_CAPACITY; - if (period_millis > 0 && period_millis < 10) { - capacity = (u4) ((double) capacity * 10 / period_millis); - } - resize(capacity); -} - -void JfrCPUTimeTraceQueue::clear() { - Atomic::release_store(&_head, (u4)0); -} - -static int64_t compute_sampling_period(double rate) { - if (rate == 0) { - return 0; - } - return os::active_processor_count() * 1000000000.0 / rate; -} - -class JfrCPUSamplerThread : public NonJavaThread { - friend class JfrCPUTimeThreadSampling; - private: - Semaphore _sample; - NonJavaThread* _sampler_thread; - double _rate; - bool _auto_adapt; - volatile int64_t _current_sampling_period_ns; - volatile bool _disenrolled; - // top bit is used to indicate that no signal handler should proceed - volatile u4 _active_signal_handlers; - volatile bool _is_async_processing_of_cpu_time_jfr_requests_triggered; - volatile bool _warned_about_timer_creation_failure; - volatile bool _signal_handler_installed; - - static const u4 STOP_SIGNAL_BIT = 0x80000000; - - JfrCPUSamplerThread(double rate, bool auto_adapt); - - void start_thread(); - - void enroll(); - void disenroll(); - void update_all_thread_timers(); - - void auto_adapt_period_if_needed(); - - void set_rate(double rate, bool auto_adapt); - 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); - - // process the queues for all threads that are in native state (and requested to be processed) - void stackwalk_threads_in_native(); - bool create_timer_for_thread(JavaThread* thread, timer_t &timerid); - - void stop_signal_handlers(); - - // returns false if the stop signal bit was set, true otherwise - bool increment_signal_handler_count(); - - void decrement_signal_handler_count(); - - void initialize_active_signal_handler_counter(); - -protected: - virtual void post_run(); -public: - virtual const char* name() const { return "JFR CPU Sampler Thread"; } - virtual const char* type_name() const { return "JfrCPUTimeSampler"; } - void run(); - void on_javathread_create(JavaThread* thread); - void on_javathread_terminate(JavaThread* thread); - - void handle_timer_signal(siginfo_t* info, void* context); - bool init_timers(); - void stop_timer(); - - void trigger_async_processing_of_cpu_time_jfr_requests(); -}; - -JfrCPUSamplerThread::JfrCPUSamplerThread(double rate, bool auto_adapt) : - _sample(), - _sampler_thread(nullptr), - _rate(rate), - _auto_adapt(auto_adapt), - _current_sampling_period_ns(compute_sampling_period(rate)), - _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() { - Atomic::release_store(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true); -} - -void JfrCPUSamplerThread::on_javathread_create(JavaThread* thread) { - if (thread->is_hidden_from_external_view() || thread->is_JfrRecorder_thread() || - !Atomic::load_acquire(&_signal_handler_installed)) { - return; - } - JfrThreadLocal* tl = thread->jfr_thread_local(); - assert(tl != nullptr, "invariant"); - tl->cpu_time_jfr_queue().resize_for_period(_current_sampling_period_ns / 1000000); - timer_t timerid; - if (create_timer_for_thread(thread, timerid)) { - tl->set_cpu_timer(&timerid); - } else { - if (!Atomic::or_then_fetch(&_warned_about_timer_creation_failure, true)) { - log_warning(jfr)("Failed to create timer for a thread"); - } - tl->deallocate_cpu_time_jfr_queue(); - } -} - -void JfrCPUSamplerThread::on_javathread_terminate(JavaThread* thread) { - JfrThreadLocal* tl = thread->jfr_thread_local(); - assert(tl != nullptr, "invariant"); - timer_t* timer = tl->cpu_timer(); - if (timer == nullptr) { - return; // no timer was created for this thread - } - tl->unset_cpu_timer(); - tl->deallocate_cpu_time_jfr_queue(); - s4 lost_samples = tl->cpu_time_jfr_queue().lost_samples(); - if (lost_samples > 0) { - JfrCPUTimeThreadSampling::send_lost_event(JfrTicks::now(), JfrThreadLocal::thread_id(thread), lost_samples); - } -} - -void JfrCPUSamplerThread::start_thread() { - if (os::create_thread(this, os::os_thread)) { - os::start_thread(this); - } else { - log_error(jfr)("Failed to create thread for thread sampling"); - } -} - -void JfrCPUSamplerThread::enroll() { - if (Atomic::cmpxchg(&_disenrolled, true, false)) { - Atomic::store(&_warned_about_timer_creation_failure, false); - initialize_active_signal_handler_counter(); - log_trace(jfr)("Enrolling CPU thread sampler"); - _sample.signal(); - if (!init_timers()) { - log_error(jfr)("Failed to initialize timers for CPU thread sampler"); - disenroll(); - return; - } - log_trace(jfr)("Enrolled CPU thread sampler"); - } -} - -void JfrCPUSamplerThread::disenroll() { - if (!Atomic::cmpxchg(&_disenrolled, false, true)) { - log_trace(jfr)("Disenrolling CPU thread sampler"); - if (Atomic::load_acquire(&_signal_handler_installed)) { - stop_timer(); - stop_signal_handlers(); - } - _sample.wait(); - log_trace(jfr)("Disenrolled CPU thread sampler"); - } -} - -void JfrCPUSamplerThread::run() { - assert(_sampler_thread == nullptr, "invariant"); - _sampler_thread = this; - int64_t last_auto_adapt_check = os::javaTimeNanos(); - while (true) { - if (!_sample.trywait()) { - // disenrolled - _sample.wait(); - } - _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 (Atomic::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) { - stackwalk_threads_in_native(); - } - os::naked_sleep(100); - } -} - -void JfrCPUSamplerThread::stackwalk_threads_in_native() { - ResourceMark rm; - // Required to prevent JFR from sampling through an ongoing safepoint - MutexLocker tlock(Threads_lock); - ThreadsListHandle tlh; - Thread* current = Thread::current(); - for (size_t i = 0; i < tlh.list()->length(); i++) { - JavaThread* jt = tlh.list()->thread_at(i); - JfrThreadLocal* tl = jt->jfr_thread_local(); - if (tl->wants_async_processing_of_cpu_time_jfr_requests()) { - if (jt->thread_state() != _thread_in_native || !tl->try_acquire_cpu_time_jfr_dequeue_lock()) { - tl->set_do_async_processing_of_cpu_time_jfr_requests(false); - continue; - } - if (jt->has_last_Java_frame()) { - JfrThreadSampling::process_cpu_time_request(jt, tl, current, false); - } else { - tl->set_do_async_processing_of_cpu_time_jfr_requests(false); - } - tl->release_cpu_time_jfr_queue_lock(); - } - } -} - -static volatile size_t count = 0; - -void JfrCPUTimeThreadSampling::send_empty_event(const JfrTicks &start_time, traceid tid, Tickspan cpu_time_period) { - EventCPUTimeSample event(UNTIMED); - event.set_failed(true); - event.set_starttime(start_time); - event.set_eventThread(tid); - event.set_stackTrace(0); - event.set_samplingPeriod(cpu_time_period); - event.set_biased(false); - event.commit(); -} - - -static volatile size_t biased_count = 0; - -void JfrCPUTimeThreadSampling::send_event(const JfrTicks &start_time, traceid sid, traceid tid, Tickspan cpu_time_period, bool biased) { - EventCPUTimeSample event(UNTIMED); - event.set_failed(false); - event.set_starttime(start_time); - event.set_eventThread(tid); - event.set_stackTrace(sid); - event.set_samplingPeriod(cpu_time_period); - event.set_biased(biased); - event.commit(); - Atomic::inc(&count); - if (biased) { - Atomic::inc(&biased_count); - } - if (Atomic::load(&count) % 1000 == 0) { - log_debug(jfr)("CPU thread sampler sent %zu events, lost %d, biased %zu\n", Atomic::load(&count), Atomic::load(&_lost_samples_sum), Atomic::load(&biased_count)); - } -} - -void JfrCPUTimeThreadSampling::send_lost_event(const JfrTicks &time, traceid tid, s4 lost_samples) { - if (!EventCPUTimeSamplesLost::is_enabled()) { - return; - } - EventCPUTimeSamplesLost event(UNTIMED); - event.set_starttime(time); - event.set_lostSamples(lost_samples); - event.set_eventThread(tid); - event.commit(); -} - -void JfrCPUSamplerThread::post_run() { - this->NonJavaThread::post_run(); - delete this; -} - -static JfrCPUTimeThreadSampling* _instance = nullptr; - -JfrCPUTimeThreadSampling& JfrCPUTimeThreadSampling::instance() { - return *_instance; -} - -JfrCPUTimeThreadSampling* JfrCPUTimeThreadSampling::create() { - assert(_instance == nullptr, "invariant"); - _instance = new JfrCPUTimeThreadSampling(); - return _instance; -} - -void JfrCPUTimeThreadSampling::destroy() { - if (_instance != nullptr) { - delete _instance; - _instance = nullptr; - } -} - -JfrCPUTimeThreadSampling::JfrCPUTimeThreadSampling() : _sampler(nullptr) {} - -JfrCPUTimeThreadSampling::~JfrCPUTimeThreadSampling() { - if (_sampler != nullptr) { - _sampler->disenroll(); - } -} - -void JfrCPUTimeThreadSampling::create_sampler(double rate, bool auto_adapt) { - assert(_sampler == nullptr, "invariant"); - _sampler = new JfrCPUSamplerThread(rate, auto_adapt); - _sampler->start_thread(); - _sampler->enroll(); -} - -void JfrCPUTimeThreadSampling::update_run_state(double rate, bool auto_adapt) { - if (rate != 0) { - if (_sampler == nullptr) { - create_sampler(rate, auto_adapt); - } else { - _sampler->set_rate(rate, auto_adapt); - _sampler->enroll(); - } - return; - } - if (_sampler != nullptr) { - _sampler->set_rate(rate /* 0 */, auto_adapt); - _sampler->disenroll(); - } -} - -void JfrCPUTimeThreadSampling::set_rate(double rate, bool auto_adapt) { - assert(rate >= 0, "invariant"); - if (_instance == nullptr) { - return; - } - instance().set_rate_value(rate, auto_adapt); -} - -void JfrCPUTimeThreadSampling::set_rate_value(double rate, bool auto_adapt) { - if (_sampler != nullptr) { - _sampler->set_rate(rate, auto_adapt); - } - update_run_state(rate, auto_adapt); -} - -void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread *thread) { - if (_instance != nullptr && _instance->_sampler != nullptr) { - _instance->_sampler->on_javathread_create(thread); - } -} - -void JfrCPUTimeThreadSampling::on_javathread_terminate(JavaThread *thread) { - if (_instance != nullptr && _instance->_sampler != nullptr) { - _instance->_sampler->on_javathread_terminate(thread); - } -} - -void JfrCPUTimeThreadSampling::trigger_async_processing_of_cpu_time_jfr_requests() { - if (_instance != nullptr && _instance->_sampler != nullptr) { - _instance->_sampler->trigger_async_processing_of_cpu_time_jfr_requests(); - } -} - -void handle_timer_signal(int signo, siginfo_t* info, void* context) { - assert(_instance != nullptr, "invariant"); - _instance->handle_timer_signal(info, context); -} - - -void JfrCPUTimeThreadSampling::handle_timer_signal(siginfo_t* info, void* context) { - if (info->si_code != SI_TIMER) { - // not the signal we are interested in - return; - } - assert(_sampler != nullptr, "invariant"); - - if (!_sampler->increment_signal_handler_count()) { - return; - } - _sampler->handle_timer_signal(info, context); - _sampler->decrement_signal_handler_count(); -} - -void JfrCPUSamplerThread::sample_thread(JfrSampleRequest& request, void* ucontext, JavaThread* jt, JfrThreadLocal* tl, JfrTicks& now) { - JfrSampleRequestBuilder::build_cpu_time_sample_request(request, ucontext, jt, jt->jfr_thread_local(), now); -} - -static bool check_state(JavaThread* thread) { - switch (thread->thread_state()) { - case _thread_in_Java: - case _thread_in_native: - return true; - default: - return false; - } -} - -void JfrCPUSamplerThread::handle_timer_signal(siginfo_t* info, void* context) { - JfrTicks now = JfrTicks::now(); - JavaThread* jt = get_java_thread_if_valid(); - if (jt == nullptr) { - return; - } - JfrThreadLocal* tl = jt->jfr_thread_local(); - JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); - if (!check_state(jt)) { - queue.increment_lost_samples(); - return; - } - if (!tl->try_acquire_cpu_time_jfr_enqueue_lock()) { - queue.increment_lost_samples(); - return; - } - - JfrCPUTimeSampleRequest request; - // the sampling period might be too low for the current Linux configuration - // so samples might be skipped and we have to compute the actual period - int64_t period = get_sampling_period() * (info->si_overrun + 1); - request._cpu_time_period = Ticks(period / 1000000000.0 * JfrTime::frequency()) - Ticks(0); - sample_thread(request._request, context, jt, tl, now); - - if (queue.enqueue(request)) { - if (queue.size() == 1) { - tl->set_has_cpu_time_jfr_requests(true); - SafepointMechanism::arm_local_poll_release(jt); - } - } else { - queue.increment_lost_samples(); - } - - if (jt->thread_state() == _thread_in_native) { - if (!tl->wants_async_processing_of_cpu_time_jfr_requests()) { - tl->set_do_async_processing_of_cpu_time_jfr_requests(true); - JfrCPUTimeThreadSampling::trigger_async_processing_of_cpu_time_jfr_requests(); - } - } else { - tl->set_do_async_processing_of_cpu_time_jfr_requests(false); - } - - tl->release_cpu_time_jfr_queue_lock(); -} - -static const int SIG = SIGPROF; - -static void set_timer_time(timer_t timerid, int64_t period_nanos) { - struct itimerspec its; - if (period_nanos == 0) { - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - } else { - its.it_interval.tv_sec = period_nanos / NANOSECS_PER_SEC; - its.it_interval.tv_nsec = period_nanos % NANOSECS_PER_SEC; - } - its.it_value = its.it_interval; - if (timer_settime(timerid, 0, &its, nullptr) == -1) { - warning("Failed to set timer for thread sampling: %s", os::strerror(os::get_last_error())); - } -} - -bool JfrCPUSamplerThread::create_timer_for_thread(JavaThread* thread, timer_t& timerid) { - struct sigevent sev; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = SIG; - sev.sigev_value.sival_ptr = nullptr; - ((int*)&sev.sigev_notify)[1] = thread->osthread()->thread_id(); - clockid_t clock; - int err = pthread_getcpuclockid(thread->osthread()->pthread_id(), &clock); - if (err != 0) { - log_error(jfr)("Failed to get clock for thread sampling: %s", os::strerror(err)); - return false; - } - if (timer_create(clock, &sev, &timerid) < 0) { - return false; - } - int64_t period = get_sampling_period(); - if (period != 0) { - set_timer_time(timerid, period); - } - return true; -} - - -void JfrCPUSamplerThread::stop_signal_handlers() { - // set the stop signal bit - Atomic::or_then_fetch(&_active_signal_handlers, STOP_SIGNAL_BIT, memory_order_acq_rel); - while (Atomic::load_acquire(&_active_signal_handlers) > STOP_SIGNAL_BIT) { - // wait for all signal handlers to finish - os::naked_short_nanosleep(1000); - } -} - -// returns false if the stop signal bit was set, true otherwise -bool JfrCPUSamplerThread::increment_signal_handler_count() { - // increment the count of active signal handlers - u4 old_value = Atomic::fetch_then_add(&_active_signal_handlers, (u4)1, memory_order_acq_rel); - if ((old_value & STOP_SIGNAL_BIT) != 0) { - // if the stop signal bit was set, we are not allowed to increment - Atomic::dec(&_active_signal_handlers, memory_order_acq_rel); - return false; - } - return true; -} - -void JfrCPUSamplerThread::decrement_signal_handler_count() { - Atomic::dec(&_active_signal_handlers, memory_order_acq_rel); -} - -void JfrCPUSamplerThread::initialize_active_signal_handler_counter() { - Atomic::release_store(&_active_signal_handlers, (u4)0); -} - -class VM_JFRInitializeCPUTimeSampler : public VM_Operation { - private: - JfrCPUSamplerThread* const _sampler; - - public: - VM_JFRInitializeCPUTimeSampler(JfrCPUSamplerThread* sampler) : _sampler(sampler) {} - - VMOp_Type type() const { return VMOp_JFRInitializeCPUTimeSampler; } - void doit() { - JfrJavaThreadIterator iter; - while (iter.has_next()) { - _sampler->on_javathread_create(iter.next()); - } - }; -}; - -bool JfrCPUSamplerThread::init_timers() { - // install sig handler for sig - void* prev_handler = PosixSignals::get_signal_handler_for_signal(SIG); - if ((prev_handler != SIG_DFL && prev_handler != SIG_IGN && prev_handler != (void*)::handle_timer_signal) || - PosixSignals::install_generic_signal_handler(SIG, (void*)::handle_timer_signal) == (void*)-1) { - log_error(jfr)("Conflicting SIGPROF handler found: %p. CPUTimeSample events will not be recorded", prev_handler); - return false; - } - Atomic::release_store(&_signal_handler_installed, true); - VM_JFRInitializeCPUTimeSampler op(this); - VMThread::execute(&op); - return true; -} - -class VM_JFRTerminateCPUTimeSampler : public VM_Operation { - private: - JfrCPUSamplerThread* const _sampler; - - public: - VM_JFRTerminateCPUTimeSampler(JfrCPUSamplerThread* sampler) : _sampler(sampler) {} - - VMOp_Type type() const { return VMOp_JFRTerminateCPUTimeSampler; } - void doit() { - JfrJavaThreadIterator iter; - while (iter.has_next()) { - JavaThread *thread = iter.next(); - JfrThreadLocal* tl = thread->jfr_thread_local(); - timer_t* timer = tl->cpu_timer(); - if (timer == nullptr) { - continue; - } - timer_delete(*timer); - tl->deallocate_cpu_time_jfr_queue(); - tl->unset_cpu_timer(); - } - }; -}; - -void JfrCPUSamplerThread::stop_timer() { - VM_JFRTerminateCPUTimeSampler op(this); - VMThread::execute(&op); -} - -void JfrCPUSamplerThread::auto_adapt_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(); - } - } -} - -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(); - } else { - Atomic::store(&_current_sampling_period_ns, compute_sampling_period(rate)); - } -} - -void JfrCPUSamplerThread::update_all_thread_timers() { - int64_t period_millis = get_sampling_period(); - ThreadsListHandle tlh; - for (size_t i = 0; i < tlh.length(); i++) { - JavaThread* thread = tlh.thread_at(i); - JfrThreadLocal* tl = thread->jfr_thread_local(); - assert(tl != nullptr, "invariant"); - timer_t* timer = tl->cpu_timer(); - if (timer != nullptr) { - set_timer_time(*timer, period_millis); - } - } -} - -#else - -static void warn() { - static bool displayed_warning = false; - if (!displayed_warning) { - warning("CPU time method sampling not supported in JFR on your platform"); - displayed_warning = true; - } -} - -static JfrCPUTimeThreadSampling* _instance = nullptr; - -JfrCPUTimeThreadSampling& JfrCPUTimeThreadSampling::instance() { - return *_instance; -} - -JfrCPUTimeThreadSampling* JfrCPUTimeThreadSampling::create() { - _instance = new JfrCPUTimeThreadSampling(); - return _instance; -} - -void JfrCPUTimeThreadSampling::destroy() { - delete _instance; - _instance = nullptr; -} - -void JfrCPUTimeThreadSampling::set_rate(double rate, bool auto_adapt) { - if (rate != 0) { - warn(); - } -} - -void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread* thread) { -} - -void JfrCPUTimeThreadSampling::on_javathread_terminate(JavaThread* thread) { -} - -#endif // defined(LINUX) && defined(INCLUDE_JFR) diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp deleted file mode 100644 index 7c0545f4772..00000000000 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. 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. - * - */ - -#ifndef SHARE_JFR_PERIODIC_SAMPLING_JFRCPUTIMETHREADSAMPLER_HPP -#define SHARE_JFR_PERIODIC_SAMPLING_JFRCPUTIMETHREADSAMPLER_HPP - -#include "jfr/utilities/jfrAllocation.hpp" - -class JavaThread; - -#if defined(LINUX) - -#include "jfr/periodic/sampling/jfrSampleRequest.hpp" -#include "jfr/utilities/jfrTypes.hpp" - -struct JfrCPUTimeSampleRequest { - JfrSampleRequest _request; - Tickspan _cpu_time_period; - - JfrCPUTimeSampleRequest() {} -}; - -// Fixed size async-signal-safe SPSC linear queue backed by an array. -// Designed to be only used under lock and read linearly -class JfrCPUTimeTraceQueue { - - // the default queue capacity, scaled if the sampling period is smaller than 10ms - // when the thread is started - static const u4 CPU_TIME_QUEUE_CAPACITY = 500; - - JfrCPUTimeSampleRequest* _data; - u4 _capacity; - // next unfilled index - volatile u4 _head; - - volatile u4 _lost_samples; - -public: - JfrCPUTimeTraceQueue(u4 capacity); - - ~JfrCPUTimeTraceQueue(); - - // signal safe, but can't be interleaved with dequeue - bool enqueue(JfrCPUTimeSampleRequest& trace); - - JfrCPUTimeSampleRequest& at(u4 index); - - u4 size() const; - - void set_size(u4 size); - - u4 capacity() const; - - // deletes all samples in the queue - void set_capacity(u4 capacity); - - bool is_empty() const; - - u4 lost_samples() const; - - void increment_lost_samples(); - - // returns the previous lost samples count - u4 get_and_reset_lost_samples(); - - void resize(u4 capacity); - - void resize_for_period(u4 period_millis); - - void clear(); - -}; - - -class JfrCPUSamplerThread; - -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); - - JfrCPUTimeThreadSampling(); - ~JfrCPUTimeThreadSampling(); - - static JfrCPUTimeThreadSampling& instance(); - static JfrCPUTimeThreadSampling* create(); - static void destroy(); - - void update_run_state(double rate, bool auto_adapt); - - public: - static void set_rate(double rate, bool auto_adapt); - - static void on_javathread_create(JavaThread* thread); - static void on_javathread_terminate(JavaThread* thread); - void handle_timer_signal(siginfo_t* info, void* context); - - static void send_empty_event(const JfrTicks& start_time, traceid tid, Tickspan cpu_time_period); - static void send_event(const JfrTicks& start_time, traceid sid, traceid tid, Tickspan cpu_time_period, bool biased); - static void send_lost_event(const JfrTicks& time, traceid tid, s4 lost_samples); - - static void trigger_async_processing_of_cpu_time_jfr_requests(); -}; - -#else - -// a basic implementation on other platforms that -// emits warnings - -class JfrCPUTimeThreadSampling : public JfrCHeapObj { - friend class JfrRecorder; -private: - static JfrCPUTimeThreadSampling& instance(); - static JfrCPUTimeThreadSampling* create(); - static void destroy(); - - public: - static void set_rate(double rate, bool auto_adapt); - - static void on_javathread_create(JavaThread* thread); - static void on_javathread_terminate(JavaThread* thread); -}; - -#endif // defined(LINUX) - - -#endif // SHARE_JFR_PERIODIC_SAMPLING_JFRCPUTIMETHREADSAMPLER_HPP diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.cpp index 7049df0198b..f8e63e2e344 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.cpp @@ -24,7 +24,6 @@ #include "asm/codeBuffer.hpp" #include "interpreter/interpreter.hpp" #include "jfr/periodic/sampling/jfrSampleRequest.hpp" -#include "jfr/utilities/jfrTime.hpp" #include "runtime/continuationEntry.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.inline.hpp" @@ -172,7 +171,7 @@ static bool build(JfrSampleRequest& request, intptr_t* fp, JavaThread* jt) { assert(request._sample_sp != nullptr, "invariant"); assert(request._sample_pc != nullptr, "invariant"); assert(jt != nullptr, "invariant"); - assert(jt->thread_state() == _thread_in_Java || jt->thread_state() == _thread_in_native, "invariant"); + assert(jt->thread_state() == _thread_in_Java, "invariant"); // 1. Interpreter frame? if (is_interpreter(request)) { @@ -304,33 +303,3 @@ JfrSampleResult JfrSampleRequestBuilder::build_java_sample_request(const void* u } return set_biased_java_sample(request, tl, jt); } - - -// A biased sample request is denoted by an empty bcp and an empty pc. -static inline void set_cpu_time_biased_sample(JfrSampleRequest& request, JavaThread* jt) { - if (request._sample_bcp != nullptr) { - request._sample_bcp = nullptr; - } - assert(request._sample_bcp == nullptr, "invariant"); - request._sample_pc = nullptr; -} - -void JfrSampleRequestBuilder::build_cpu_time_sample_request(JfrSampleRequest& request, - void* ucontext, - JavaThread* jt, - JfrThreadLocal* tl, - JfrTicks& now) { - assert(jt != nullptr, "invariant"); - request._sample_ticks = now; - - // Prioritize the ljf, if one exists. - request._sample_sp = jt->last_Java_sp(); - if (request._sample_sp == nullptr || !build_from_ljf(request, tl, jt)) { - intptr_t* fp; - request._sample_pc = os::fetch_frame_from_context(ucontext, reinterpret_cast(&request._sample_sp), &fp); - assert(sp_in_stack(request, jt), "invariant"); - if (!build(request, fp, jt)) { - set_cpu_time_biased_sample(request, jt); - } - } -} diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.hpp index 20e737e0cbf..6567e7f8bff 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrSampleRequest.hpp @@ -81,11 +81,6 @@ class JfrSampleRequestBuilder : AllStatic { static JfrSampleResult build_java_sample_request(const void* ucontext, JfrThreadLocal* tl, JavaThread* jt); - static void build_cpu_time_sample_request(JfrSampleRequest &request, - void* ucontext, - JavaThread* jt, - JfrThreadLocal* tl, - JfrTicks& now); }; #endif // SHARE_JFR_PERIODIC_SAMPLING_JFRSAMPLEREQUEST_HPP diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp index ddc9d59b295..aa72c29cf50 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp @@ -28,7 +28,6 @@ #include "code/nmethod.hpp" #include "interpreter/interpreter.hpp" #include "jfr/jfrEvents.hpp" -#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" #include "jfr/periodic/sampling/jfrSampleMonitor.hpp" #include "jfr/periodic/sampling/jfrSampleRequest.hpp" #include "jfr/periodic/sampling/jfrThreadSampling.hpp" @@ -162,7 +161,7 @@ static inline bool is_valid(const PcDesc* pc_desc) { return pc_desc != nullptr && pc_desc->scope_decode_offset() != DebugInformationRecorder::serialized_null; } -static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, bool& in_continuation, JavaThread* jt, bool& biased) { +static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, bool& in_continuation, JavaThread* jt) { assert(jt != nullptr, "invariant"); if (!jt->has_last_Java_frame()) { @@ -179,7 +178,6 @@ static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, // A biased sample is requested or no code blob. top_frame = jt->last_frame(); in_continuation = is_in_continuation(top_frame, jt); - biased = true; return true; } @@ -229,8 +227,6 @@ static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, assert(!stream.current()->is_safepoint_blob_frame(), "invariant"); - biased = true; - // Search the first frame that is above the sampled sp. for (; !stream.is_done(); stream.next()) { frame* const current = stream.current(); @@ -254,7 +250,6 @@ static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, const PcDesc* const pc_desc = get_pc_desc(sampled_nm, sampled_pc); if (is_valid(pc_desc)) { current->adjust_pc(pc_desc->real_pc(sampled_nm)); - biased = false; } } } @@ -275,9 +270,8 @@ static void record_thread_in_java(const JfrSampleRequest& request, const JfrTick assert(current != nullptr, "invariant"); frame top_frame; - bool biased = false; bool in_continuation; - if (!compute_top_frame(request, top_frame, in_continuation, jt, biased)) { + if (!compute_top_frame(request, top_frame, in_continuation, jt)) { return; } @@ -299,42 +293,6 @@ static void record_thread_in_java(const JfrSampleRequest& request, const JfrTick } } -#ifdef LINUX -static void record_cpu_time_thread(const JfrCPUTimeSampleRequest& request, const JfrTicks& now, const JfrThreadLocal* tl, JavaThread* jt, Thread* current) { - assert(jt != nullptr, "invariant"); - assert(tl != nullptr, "invariant"); - assert(current != nullptr, "invariant"); - frame top_frame; - bool biased = false; - bool in_continuation = false; - bool could_compute_top_frame = compute_top_frame(request._request, top_frame, in_continuation, jt, biased); - const traceid tid = in_continuation ? tl->vthread_id_with_epoch_update(jt) : JfrThreadLocal::jvm_thread_id(jt); - - if (!could_compute_top_frame) { - JfrCPUTimeThreadSampling::send_empty_event(request._request._sample_ticks, tid, request._cpu_time_period); - return; - } - traceid sid; - { - ResourceMark rm(current); - JfrStackTrace stacktrace; - if (!stacktrace.record(jt, top_frame, in_continuation, request._request)) { - // Unable to record stacktrace. Fail. - JfrCPUTimeThreadSampling::send_empty_event(request._request._sample_ticks, tid, request._cpu_time_period); - return; - } - sid = JfrStackTraceRepository::add(stacktrace); - } - assert(sid != 0, "invariant"); - - - JfrCPUTimeThreadSampling::send_event(request._request._sample_ticks, sid, tid, request._cpu_time_period, biased); - if (current == jt) { - send_safepoint_latency_event(request._request, now, sid, jt); - } -} -#endif - static void drain_enqueued_requests(const JfrTicks& now, JfrThreadLocal* tl, JavaThread* jt, Thread* current) { assert(tl != nullptr, "invariant"); assert(jt != nullptr, "invariant"); @@ -350,49 +308,6 @@ static void drain_enqueued_requests(const JfrTicks& now, JfrThreadLocal* tl, Jav assert(!tl->has_enqueued_requests(), "invariant"); } -static void drain_enqueued_cpu_time_requests(const JfrTicks& now, JfrThreadLocal* tl, JavaThread* jt, Thread* current, bool lock) { - assert(tl != nullptr, "invariant"); - assert(jt != nullptr, "invariant"); - assert(current != nullptr, "invariant"); -#ifdef LINUX - tl->set_do_async_processing_of_cpu_time_jfr_requests(false); - if (lock) { - tl->acquire_cpu_time_jfr_dequeue_lock(); - } - JfrCPUTimeTraceQueue& queue = tl->cpu_time_jfr_queue(); - for (u4 i = 0; i < queue.size(); i++) { - record_cpu_time_thread(queue.at(i), now, tl, jt, current); - } - queue.clear(); - assert(queue.is_empty(), "invariant"); - tl->set_has_cpu_time_jfr_requests(false); - if (queue.lost_samples() > 0) { - JfrCPUTimeThreadSampling::send_lost_event( now, JfrThreadLocal::thread_id(jt), queue.get_and_reset_lost_samples()); - } - if (lock) { - tl->release_cpu_time_jfr_queue_lock(); - } -#endif -} - -// Entry point for a thread that has been sampled in native code and has a pending JFR CPU time request. -void JfrThreadSampling::process_cpu_time_request(JavaThread* jt, JfrThreadLocal* tl, Thread* current, bool lock) { - assert(jt != nullptr, "invariant"); - - const JfrTicks now = JfrTicks::now(); - drain_enqueued_cpu_time_requests(now, tl, jt, current, lock); -} - -static void drain_all_enqueued_requests(const JfrTicks& now, JfrThreadLocal* tl, JavaThread* jt, Thread* current) { - assert(tl != nullptr, "invariant"); - assert(jt != nullptr, "invariant"); - assert(current != nullptr, "invariant"); - drain_enqueued_requests(now, tl, jt, current); - if (tl->has_cpu_time_jfr_requests()) { - drain_enqueued_cpu_time_requests(now, tl, jt, current, true); - } -} - // Only entered by the JfrSampler thread. bool JfrThreadSampling::process_native_sample_request(JfrThreadLocal* tl, JavaThread* jt, Thread* sampler_thread) { assert(tl != nullptr, "invairant"); @@ -467,6 +382,5 @@ void JfrThreadSampling::process_sample_request(JavaThread* jt) { break; } } - drain_all_enqueued_requests(now, tl, jt, jt); + drain_enqueued_requests(now, tl, jt, jt); } - diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.hpp index 3c4d1a126b0..d4c4bc0d873 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.hpp @@ -33,10 +33,8 @@ class Thread; class JfrThreadSampling : AllStatic { friend class JfrSamplerThread; - friend class JfrCPUSamplerThread; private: static bool process_native_sample_request(JfrThreadLocal* tl, JavaThread* jt, Thread* sampler_thread); - static void process_cpu_time_request(JavaThread* jt, JfrThreadLocal* tl, Thread* current, bool lock); public: static void process_sample_request(JavaThread* jt); }; diff --git a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp index dd75cb2929f..384305862ca 100644 --- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp +++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp @@ -29,7 +29,6 @@ #include "jfr/jni/jfrJavaSupport.hpp" #include "jfr/leakprofiler/sampling/objectSampler.hpp" #include "jfr/periodic/jfrOSInterface.hpp" -#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" #include "jfr/periodic/sampling/jfrThreadSampler.hpp" #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" @@ -305,9 +304,6 @@ bool JfrRecorder::create_components() { if (!create_thread_sampler()) { return false; } - if (!create_cpu_time_thread_sampling()) { - return false; - } if (!create_event_throttler()) { return false; } @@ -322,7 +318,6 @@ static JfrStackTraceRepository* _stack_trace_repository; static JfrStringPool* _stringpool = nullptr; static JfrOSInterface* _os_interface = nullptr; static JfrThreadSampler* _thread_sampler = nullptr; -static JfrCPUTimeThreadSampling* _cpu_time_thread_sampling = nullptr; static JfrCheckpointManager* _checkpoint_manager = nullptr; bool JfrRecorder::create_java_event_writer() { @@ -395,12 +390,6 @@ bool JfrRecorder::create_thread_sampler() { return _thread_sampler != nullptr; } -bool JfrRecorder::create_cpu_time_thread_sampling() { - assert(_cpu_time_thread_sampling == nullptr, "invariant"); - _cpu_time_thread_sampling = JfrCPUTimeThreadSampling::create(); - return _cpu_time_thread_sampling != nullptr; -} - bool JfrRecorder::create_event_throttler() { return JfrEventThrottler::create(); } @@ -439,10 +428,6 @@ void JfrRecorder::destroy_components() { JfrThreadSampler::destroy(); _thread_sampler = nullptr; } - if (_cpu_time_thread_sampling != nullptr) { - JfrCPUTimeThreadSampling::destroy(); - _cpu_time_thread_sampling = nullptr; - } JfrEventThrottler::destroy(); } diff --git a/src/hotspot/share/jfr/recorder/jfrRecorder.hpp b/src/hotspot/share/jfr/recorder/jfrRecorder.hpp index 3099c8ad344..b917904c5fb 100644 --- a/src/hotspot/share/jfr/recorder/jfrRecorder.hpp +++ b/src/hotspot/share/jfr/recorder/jfrRecorder.hpp @@ -54,7 +54,6 @@ class JfrRecorder : public JfrCHeapObj { static bool create_storage(); static bool create_stringpool(); static bool create_thread_sampler(); - static bool create_cpu_time_thread_sampling(); static bool create_event_throttler(); static bool create_components(); static void destroy_components(); diff --git a/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp b/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp index f660a01c04c..0befaae7751 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp @@ -34,7 +34,6 @@ constexpr static const JfrSamplerParams _disabled_params = { false // reconfigure }; -static JfrEventThrottler* _disabled_cpu_time_sample_throttler = nullptr; static JfrEventThrottler* _object_allocation_throttler = nullptr; static JfrEventThrottler* _safepoint_latency_throttler = nullptr; @@ -49,9 +48,6 @@ JfrEventThrottler::JfrEventThrottler(JfrEventId event_id) : _update(false) {} bool JfrEventThrottler::create() { - assert(_disabled_cpu_time_sample_throttler == nullptr, "invariant"); - _disabled_cpu_time_sample_throttler = new JfrEventThrottler(JfrCPUTimeSampleEvent); - _disabled_cpu_time_sample_throttler->_disabled = true; assert(_object_allocation_throttler == nullptr, "invariant"); _object_allocation_throttler = new JfrEventThrottler(JfrObjectAllocationSampleEvent); if (_object_allocation_throttler == nullptr || !_object_allocation_throttler->initialize()) { @@ -63,8 +59,6 @@ bool JfrEventThrottler::create() { } void JfrEventThrottler::destroy() { - delete _disabled_cpu_time_sample_throttler; - _disabled_cpu_time_sample_throttler = nullptr; delete _object_allocation_throttler; _object_allocation_throttler = nullptr; delete _safepoint_latency_throttler; @@ -75,19 +69,15 @@ void JfrEventThrottler::destroy() { // and another for the SamplingLatency event. // When introducing many more throttlers, consider adding a lookup map keyed by event id. JfrEventThrottler* JfrEventThrottler::for_event(JfrEventId event_id) { - assert(_disabled_cpu_time_sample_throttler != nullptr, "Disabled CPU time throttler has not been properly initialized"); assert(_object_allocation_throttler != nullptr, "ObjectAllocation throttler has not been properly initialized"); assert(_safepoint_latency_throttler != nullptr, "SafepointLatency throttler has not been properly initialized"); - assert(event_id == JfrObjectAllocationSampleEvent || event_id == JfrSafepointLatencyEvent || event_id == JfrCPUTimeSampleEvent, "Event type has an unconfigured throttler"); + assert(event_id == JfrObjectAllocationSampleEvent || event_id == JfrSafepointLatencyEvent, "Event type has an unconfigured throttler"); if (event_id == JfrObjectAllocationSampleEvent) { return _object_allocation_throttler; } if (event_id == JfrSafepointLatencyEvent) { return _safepoint_latency_throttler; } - if (event_id == JfrCPUTimeSampleEvent) { - return _disabled_cpu_time_sample_throttler; - } return nullptr; } diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 4b805a98a32..503aa85e02f 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -26,7 +26,6 @@ #include "jfr/jni/jfrJavaSupport.hpp" #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" #include "jfr/periodic/jfrThreadCPULoadEvent.hpp" -#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrOopTraceId.inline.hpp" #include "jfr/recorder/jfrRecorder.hpp" @@ -79,15 +78,7 @@ JfrThreadLocal::JfrThreadLocal() : _enqueued_requests(false), _vthread(false), _notified(false), - _dead(false) -#ifdef LINUX - ,_cpu_timer(nullptr), - _cpu_time_jfr_locked(UNLOCKED), - _has_cpu_time_jfr_requests(false), - _cpu_time_jfr_queue(0), - _do_async_processing_of_cpu_time_jfr_requests(false) -#endif - { + _dead(false) { Thread* thread = Thread::current_or_null(); _parent_trace_id = thread != nullptr ? jvm_thread_id(thread) : (traceid)0; } @@ -138,9 +129,7 @@ void JfrThreadLocal::on_start(Thread* t) { if (JfrRecorder::is_recording()) { JfrCheckpointManager::write_checkpoint(t); if (t->is_Java_thread()) { - JavaThread *const jt = JavaThread::cast(t); - JfrCPUTimeThreadSampling::on_javathread_create(jt); - send_java_thread_start_event(jt); + send_java_thread_start_event(JavaThread::cast(t)); } } if (t->jfr_thread_local()->has_cached_stack_trace()) { @@ -232,7 +221,6 @@ void JfrThreadLocal::on_exit(Thread* t) { if (t->is_Java_thread()) { JavaThread* const jt = JavaThread::cast(t); send_java_thread_end_event(jt, JfrThreadLocal::jvm_thread_id(jt)); - JfrCPUTimeThreadSampling::on_javathread_terminate(jt); JfrThreadCPULoadEvent::send_event_for_thread(jt); } release(tl, Thread::current()); // because it could be that Thread::current() != t @@ -549,85 +537,3 @@ Arena* JfrThreadLocal::dcmd_arena(JavaThread* jt) { tl->_dcmd_arena = arena; return arena; } - - -#ifdef LINUX - -void JfrThreadLocal::set_cpu_timer(timer_t* timer) { - if (_cpu_timer == nullptr) { - _cpu_timer = JfrCHeapObj::new_array(1); - } - *_cpu_timer = *timer; -} - -void JfrThreadLocal::unset_cpu_timer() { - if (_cpu_timer != nullptr) { - timer_delete(*_cpu_timer); - JfrCHeapObj::free(_cpu_timer, sizeof(timer_t)); - _cpu_timer = nullptr; - } -} - -timer_t* JfrThreadLocal::cpu_timer() const { - return _cpu_timer; -} - -bool JfrThreadLocal::is_cpu_time_jfr_enqueue_locked() { - return Atomic::load_acquire(&_cpu_time_jfr_locked) == ENQUEUE; -} - -bool JfrThreadLocal::is_cpu_time_jfr_dequeue_locked() { - return Atomic::load_acquire(&_cpu_time_jfr_locked) == DEQUEUE; -} - -bool JfrThreadLocal::try_acquire_cpu_time_jfr_enqueue_lock() { - return Atomic::cmpxchg(&_cpu_time_jfr_locked, UNLOCKED, ENQUEUE) == UNLOCKED; -} - -bool JfrThreadLocal::try_acquire_cpu_time_jfr_dequeue_lock() { - CPUTimeLockState got; - while (true) { - CPUTimeLockState got = Atomic::cmpxchg(&_cpu_time_jfr_locked, UNLOCKED, DEQUEUE); - if (got == UNLOCKED) { - return true; // successfully locked for dequeue - } - if (got == DEQUEUE) { - return false; // already locked for dequeue - } - // else wait for the lock to be released from a signal handler - } -} - -void JfrThreadLocal::acquire_cpu_time_jfr_dequeue_lock() { - while (Atomic::cmpxchg(&_cpu_time_jfr_locked, UNLOCKED, DEQUEUE) != UNLOCKED); -} - -void JfrThreadLocal::release_cpu_time_jfr_queue_lock() { - Atomic::release_store(&_cpu_time_jfr_locked, UNLOCKED); -} - -void JfrThreadLocal::set_has_cpu_time_jfr_requests(bool has_requests) { - Atomic::release_store(&_has_cpu_time_jfr_requests, has_requests); -} - -bool JfrThreadLocal::has_cpu_time_jfr_requests() { - return Atomic::load_acquire(&_has_cpu_time_jfr_requests); -} - -JfrCPUTimeTraceQueue& JfrThreadLocal::cpu_time_jfr_queue() { - return _cpu_time_jfr_queue; -} - -void JfrThreadLocal::deallocate_cpu_time_jfr_queue() { - cpu_time_jfr_queue().resize(0); -} - -void JfrThreadLocal::set_do_async_processing_of_cpu_time_jfr_requests(bool wants) { - Atomic::release_store(&_do_async_processing_of_cpu_time_jfr_requests, wants); -} - -bool JfrThreadLocal::wants_async_processing_of_cpu_time_jfr_requests() { - return Atomic::load_acquire(&_do_async_processing_of_cpu_time_jfr_requests); -} - -#endif diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp index 715a2c44f93..8e545d9c429 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp @@ -33,10 +33,6 @@ #include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" -#ifdef LINUX -#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" -#endif - class Arena; class JavaThread; class JfrBuffer; @@ -83,22 +79,6 @@ class JfrThreadLocal { bool _dead; bool _sampling_critical_section; -#ifdef LINUX - timer_t* _cpu_timer; - - enum CPUTimeLockState { - UNLOCKED, - // locked for enqueuing - ENQUEUE, - // locked for dequeuing - DEQUEUE - }; - volatile CPUTimeLockState _cpu_time_jfr_locked; - volatile bool _has_cpu_time_jfr_requests; - JfrCPUTimeTraceQueue _cpu_time_jfr_queue; - volatile bool _do_async_processing_of_cpu_time_jfr_requests; -#endif - JfrBuffer* install_native_buffer() const; JfrBuffer* install_java_buffer() const; void release(Thread* t); @@ -362,39 +342,6 @@ class JfrThreadLocal { void set_thread_blob(const JfrBlobHandle& handle); const JfrBlobHandle& thread_blob() const; - // CPU time sampling -#ifdef LINUX - void set_cpu_timer(timer_t* timer); - void unset_cpu_timer(); - timer_t* cpu_timer() const; - - // The CPU time JFR lock has three different states: - // - ENQUEUE: lock for enqueuing CPU time requests - // - DEQUEUE: lock for dequeuing CPU time requests - // - UNLOCKED: no lock held - // This ensures that we can safely enqueue and dequeue CPU time requests, - // without interleaving - - bool is_cpu_time_jfr_enqueue_locked(); - bool is_cpu_time_jfr_dequeue_locked(); - - bool try_acquire_cpu_time_jfr_enqueue_lock(); - bool try_acquire_cpu_time_jfr_dequeue_lock(); - void acquire_cpu_time_jfr_dequeue_lock(); - void release_cpu_time_jfr_queue_lock(); - - void set_has_cpu_time_jfr_requests(bool has_events); - bool has_cpu_time_jfr_requests(); - - JfrCPUTimeTraceQueue& cpu_time_jfr_queue(); - void deallocate_cpu_time_jfr_queue(); - - void set_do_async_processing_of_cpu_time_jfr_requests(bool wants); - bool wants_async_processing_of_cpu_time_jfr_requests(); -#else - bool has_cpu_time_jfr_requests() { return false; } -#endif - // Hooks static void on_start(Thread* t); static void on_exit(Thread* t); diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 772ef7bbe82..f3a06d5efd2 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -78,7 +78,6 @@ class JavaThread; // - WorkerThread // - WatcherThread // - JfrThreadSampler -// - JfrCPUSamplerThread // - LogAsyncWriter // // All Thread subclasses must be either JavaThread or NonJavaThread. diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index ac5d37381f9..50d85944485 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -115,8 +115,6 @@ template(JFROldObject) \ template(JvmtiPostObjectFree) \ template(RendezvousGCThreads) \ - template(JFRInitializeCPUTimeSampler) \ - template(JFRTerminateCPUTimeSampler) \ template(ReinitializeMDO) class Thread; diff --git a/src/hotspot/share/utilities/ticks.hpp b/src/hotspot/share/utilities/ticks.hpp index 88dc9a787f9..8d2bbc7ce1f 100644 --- a/src/hotspot/share/utilities/ticks.hpp +++ b/src/hotspot/share/utilities/ticks.hpp @@ -236,7 +236,6 @@ class TimeInstant : public Rep { friend class TimePartitionsTest; friend class GCTimerTest; friend class CompilerEvent; - friend class JfrCPUSamplerThread; }; #if INCLUDE_JFR diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java index 2ea4725abc8..f43e45b724e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -51,7 +51,6 @@ import jdk.jfr.internal.settings.EnabledSetting; import jdk.jfr.internal.settings.LevelSetting; import jdk.jfr.internal.settings.MethodSetting; import jdk.jfr.internal.settings.PeriodSetting; -import jdk.jfr.internal.settings.CPUThrottleSetting; import jdk.jfr.internal.settings.StackTraceSetting; import jdk.jfr.internal.settings.ThresholdSetting; import jdk.jfr.internal.settings.ThrottleSetting; @@ -327,9 +326,6 @@ public final class EventControl { private static Control defineThrottle(PlatformEventType type) { String def = type.getAnnotationValue(Throttle.class, ThrottleSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THROTTLE, Throttle.NAME, def, Collections.emptyList())); - if (type.getName().equals("jdk.CPUTimeSample")) { - return new Control(new CPUThrottleSetting(type), def); - } return new Control(new ThrottleSetting(type, def), def); } 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..e0eaef74b4c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -270,16 +270,6 @@ public final class JVM { */ public static native void setMethodSamplingPeriod(long type, long periodMillis); - /** - * Set the maximum event emission rate for the CPU time sampler - * - * 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); - /** * 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 769e7055305..32b59bca4c0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java @@ -30,10 +30,8 @@ import java.util.List; import java.util.Objects; import jdk.jfr.SettingDescriptor; -import jdk.jfr.events.ActiveSettingEvent; 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.tracing.Modification; @@ -47,7 +45,6 @@ public final class PlatformEventType extends Type { private final boolean isJVM; private final boolean isJDK; private final boolean isMethodSampling; - private final boolean isCPUTimeMethodSampling; private final List settings = new ArrayList<>(5); private final boolean dynamicSettings; private final int stackTraceOffset; @@ -59,7 +56,6 @@ public final class PlatformEventType extends Type { private boolean stackTraceEnabled = true; private long thresholdTicks = 0; private long period = 0; - private TimespanRate cpuRate; private boolean hasHook; private boolean beginChunk; @@ -79,7 +75,6 @@ public final class PlatformEventType extends Type { this.dynamicSettings = dynamicSettings; this.isJVM = Type.isDefinedByJVM(id); this.isMethodSampling = determineMethodSampling(); - this.isCPUTimeMethodSampling = isJVM && name.equals(Type.EVENT_NAME_PREFIX + "CPUTimeSample"); this.isJDK = isJDK; this.stackTraceOffset = determineStackTraceOffset(); } @@ -196,13 +191,6 @@ public final class PlatformEventType extends Type { } } - public void setCPUThrottle(TimespanRate rate) { - if (isCPUTimeMethodSampling) { - this.cpuRate = rate; - JVM.setCPUThrottle(rate.rate(), rate.autoAdapt()); - } - } - public void setHasPeriod(boolean hasPeriod) { this.hasPeriod = hasPeriod; } @@ -263,9 +251,6 @@ public final class PlatformEventType extends Type { if (isMethodSampling) { 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()); } else { JVM.setEnabled(getId(), enabled); } @@ -403,10 +388,6 @@ public final class PlatformEventType extends Type { return isMethodSampling; } - public boolean isCPUTimeMethodSampling() { - return isCPUTimeMethodSampling; - } - public void setStackFilterId(long id) { startFilterId = id; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini index a2ac74142f4..a6192db1ab0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini @@ -39,8 +39,7 @@ label = "Active Settings" table = "COLUMN 'Event Type', 'Enabled', 'Threshold', 'Stack Trace','Period','Cutoff', 'Throttle' FORMAT none, missing:whitespace, missing:whitespace, missing:whitespace, - missing:whitespace, missing:whitespace, missing:whitespace, - missing:whitespace + missing:whitespace, missing:whitespace, missing:whitespace SELECT E.id, LAST_BATCH(E.value), LAST_BATCH(T.value), LAST_BATCH(S.value), LAST_BATCH(P.value), LAST_BATCH(C.value), LAST_BATCH(U.value) @@ -401,28 +400,6 @@ table = "COLUMN 'Method', 'Samples', 'Percent' SELECT stackTrace.topFrame AS T, COUNT(*), COUNT(*) FROM ExecutionSample GROUP BY T LIMIT 25" -[application.cpu-time-hot-methods] -label = "Java Methods that Execute the Most from CPU Time Sampler" -table = "COLUMN 'Method', 'Samples', 'Percent' - FORMAT none, none, normalized - SELECT stackTrace.topFrame AS T, COUNT(*), COUNT(*) - FROM CPUTimeSample GROUP BY T LIMIT 25" - -[application.cpu-time-statistics] -label = "CPU Time Sample Statistics" -form = "COLUMN 'Successful Samples', 'Failed Samples', 'Biased Samples', 'Total Samples', 'Lost Samples' - SELECT COUNT(S.startTime), COUNT(F.startTime), COUNT(B.startTime), Count(A.startTime), SUM(L.lostSamples) - FROM - CPUTimeSample AS S, - CPUTimeSample AS F, - CPUTimeSample AS A, - CPUTimeSample AS B, - CPUTimeSamplesLost AS L - WHERE - S.failed = 'false' AND - F.failed = 'true' AND - B.biased = 'true'" - [jvm.jdk-agents] label = "JDK Agents" table = "COLUMN 'Time', 'Initialization', 'Name', 'Options' 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 deleted file mode 100644 index c18aeef2132..00000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Datadog, Inc. All rights reserved. - * Copyright (c) 2025 SAP SE. 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 static jdk.jfr.internal.util.TimespanUnit.SECONDS; -import static jdk.jfr.internal.util.TimespanUnit.MILLISECONDS; - -import java.util.Objects; -import java.util.Set; - -import jdk.jfr.Description; -import jdk.jfr.SettingControl; -import jdk.jfr.Label; -import jdk.jfr.MetadataDefinition; -import jdk.jfr.Name; -import jdk.jfr.internal.PlatformEventType; -import jdk.jfr.internal.Type; -import jdk.jfr.internal.util.TimespanRate; -import jdk.jfr.internal.util.Utils; - -@MetadataDefinition -@Label("CPUThrottleSetting") -@Description("Upper bounds the emission rate for CPU time samples") -@Name(Type.SETTINGS_PREFIX + "Rate") -public final class CPUThrottleSetting extends SettingControl { - public static final String DEFAULT_VALUE = "0/s"; - private final PlatformEventType eventType; - private String value = DEFAULT_VALUE; - - public CPUThrottleSetting(PlatformEventType eventType) { - this.eventType = Objects.requireNonNull(eventType); - } - - @Override - public String combine(Set values) { - TimespanRate max = null; - for (String value : values) { - TimespanRate rate = TimespanRate.of(value); - if (rate != null) { - if (max == null || rate.isHigher(max)) { - max = rate; - } - max = new TimespanRate(max.rate(), max.autoAdapt() || rate.autoAdapt()); - } - } - // "off" is not supported - return Objects.requireNonNullElse(max.toString(), DEFAULT_VALUE); - } - - @Override - public void setValue(String value) { - TimespanRate rate = TimespanRate.of(value); - if (rate != null) { - eventType.setCPUThrottle(rate); - this.value = value; - } - } - - @Override - public String getValue() { - return value; - } -} - diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java index 2632cd63848..f32436a5e0f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java @@ -55,8 +55,4 @@ public record Rate(long amount, TimespanUnit unit) { private double inNanos() { return (double) amount / unit.nanos; } - - public double perSecond() { - return inNanos() * 1_000_000_000.0; - } } 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 deleted file mode 100644 index 5d671310e3c..00000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. All rights reserved. - * Copyright (c) 2024, 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.util; - -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 static TimespanRate of(String text) { - if (text.equals("off")) { - text = CPUThrottleSetting.DEFAULT_VALUE; - } - boolean isPeriod = !text.contains("/"); - if (isPeriod) { - var period = ValueParser.parseTimespanWithInfinity(text, Long.MAX_VALUE); - if (period == Long.MAX_VALUE) { - return null; - } - if (period == 0) { - return new TimespanRate(0, false); - } - return new TimespanRate(Runtime.getRuntime().availableProcessors() / (period / 1_000_000_000.0), false); - } - Rate r = Rate.of(text); - if (r == null) { - return null; - } - return new TimespanRate(r.perSecond(), true); - } - - public boolean isHigher(TimespanRate that) { - return rate() > that.rate(); - } - - @Override - public String toString() { - if (autoAdapt) { - return String.format("%d/ns", (long)(rate * 1_000_000_000L)); - } - return String.format("%dns", (long)(Runtime.getRuntime().availableProcessors() / rate * 1_000_000_000L)); - } -} diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 541d1d3aa2f..293af26746f 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -226,16 +226,6 @@ off - - false - 500/s - true - - - - true - - true 10 ms diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 9cec2d9a70f..89a9022d11e 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -206,16 +206,6 @@ 20 ms - - false - 10ms - true - - - - true - - true diff --git a/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java index d6e126a493f..5b8aacfb1d2 100644 --- a/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java +++ b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java @@ -89,10 +89,7 @@ public class TestLookForUntestedEvents { // Experimental events private static final Set experimentalEvents = Set.of( - "Flush", "SyncOnValueBasedClass", "CPUTimeSample", "CPUTimeSamplesLost"); - - // Subset of the experimental events that should have tests - private static final Set experimentalButTestedEvents = Set.of("CPUTimeSample"); + "Flush", "SyncOnValueBasedClass"); public static void main(String[] args) throws Exception { for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { @@ -113,9 +110,7 @@ public class TestLookForUntestedEvents { .collect(Collectors.toList()); Set eventsNotCoveredByTest = new HashSet<>(jfrEventTypes); - Set checkedEvents = new HashSet<>(jfrEventTypes); - checkedEvents.addAll(experimentalButTestedEvents); - for (String event : checkedEvents) { + for (String event : jfrEventTypes) { for (Path p : paths) { if (findStringInFile(p, event)) { eventsNotCoveredByTest.remove(event); diff --git a/test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java b/test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java deleted file mode 100644 index de211b8c454..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2013, 2023, 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.event.profiling; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; - -import jdk.jfr.Recording; -import jdk.jfr.consumer.RecordedEvent; -import jdk.jfr.consumer.RecordedFrame; -import jdk.jfr.consumer.RecordedStackTrace; -import jdk.test.lib.Asserts; -import jdk.test.lib.jfr.EventNames; -import jdk.test.lib.jfr.Events; -import jdk.test.lib.jfr.RecurseThread; - -public class BaseTestFullStackTrace { - private final static int MAX_DEPTH = 64; // currently hardcoded in jvm - - private final String eventName; - private final String threadFieldName; - - public BaseTestFullStackTrace(String eventName, String threadFieldName) { - this.eventName = eventName; - this.threadFieldName = threadFieldName; - } - - public void run() throws Throwable { - RecurseThread[] threads = new RecurseThread[3]; - for (int i = 0; i < threads.length; ++i) { - int depth = MAX_DEPTH - 1 + i; - threads[i] = new RecurseThread(depth); - threads[i].setName("recursethread-" + depth); - threads[i].start(); - } - - for (RecurseThread thread : threads) { - while (!thread.isInRunLoop()) { - Thread.sleep(20); - } - } - - assertStackTraces(threads); - - for (RecurseThread thread : threads) { - thread.quit(); - thread.join(); - } - } - - private void assertStackTraces(RecurseThread[] threads) throws Throwable { - while (true) { - try (Recording recording = new Recording()) { - if (eventName.equals(EventNames.CPUTimeSample)) { - recording.enable(eventName).with("throttle", "50ms"); - } else { - recording.enable(eventName).withPeriod(Duration.ofMillis(50)); - } - recording.start(); - Thread.sleep(500); - recording.stop(); - if (hasValidStackTraces(recording, threads)) { - break; - } - } - }; - } - - private boolean hasValidStackTraces(Recording recording, RecurseThread[] threads) throws Throwable { - boolean[] isEventFound = new boolean[threads.length]; - - for (RecordedEvent event : Events.fromRecording(recording)) { - System.out.println("Event: " + event); - String threadName = Events.assertField(event, threadFieldName + ".javaName").getValue(); - long threadId = Events.assertField(event, threadFieldName + ".javaThreadId").getValue(); - - for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { - RecurseThread currThread = threads[threadIndex]; - if (threadId == currThread.getId()) { - System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); - Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); - if ("recurseEnd".equals(getTopMethodName(event))) { - isEventFound[threadIndex] = true; - checkEvent(event, currThread.totalDepth); - break; - } - } - } - } - - for (int i = 0; i < threads.length; ++i) { - String msg = "threadIndex=%d, recurseDepth=%d, isEventFound=%b%n"; - System.out.printf(msg, i, threads[i].totalDepth, isEventFound[i]); - } - for (int i = 0; i < threads.length; ++i) { - if(!isEventFound[i]) { - // no assertion, let's retry. - // Could be race condition, i.e safe point during Thread.sleep - System.out.println("Failed to validate all threads, will retry."); - return false; - } - } - return true; - } - - public String getTopMethodName(RecordedEvent event) { - List frames = event.getStackTrace().getFrames(); - Asserts.assertFalse(frames.isEmpty(), "JavaFrames was empty"); - return frames.getFirst().getMethod().getName(); - } - - private void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable { - RecordedStackTrace stacktrace = null; - try { - stacktrace = event.getStackTrace(); - List frames = stacktrace.getFrames(); - Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); - List expectedMethods = getExpectedMethods(expectedDepth); - Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); - - for (int i = 0; i < frames.size(); ++i) { - String name = frames.get(i).getMethod().getName(); - String expectedName = expectedMethods.get(i); - System.out.printf("method[%d]=%s, expected=%s%n", i, name, expectedName); - Asserts.assertEquals(name, expectedName, "Wrong method name"); - } - - boolean isTruncated = stacktrace.isTruncated(); - boolean isTruncateExpected = expectedDepth > MAX_DEPTH; - Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); - - String firstMethod = frames.getLast().getMethod().getName(); - boolean isFullTrace = "run".equals(firstMethod); - String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); - Asserts.assertTrue(isTruncated != isFullTrace, msg); - } catch (Throwable t) { - System.out.println(String.format("stacktrace:%n%s", stacktrace)); - throw t; - } - } - - private List getExpectedMethods(int depth) { - List methods = new ArrayList<>(); - methods.add("recurseEnd"); - for (int i = 0; i < depth - 2; ++i) { - methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); - } - methods.add("run"); - if (depth > MAX_DEPTH) { - methods = methods.subList(0, MAX_DEPTH); - } - return methods; - } -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java deleted file mode 100644 index eb8d33832b5..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeAndExecutionSample.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. 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.event.profiling; - -import java.time.Duration; - -import jdk.jfr.consumer.RecordingStream; -import jdk.test.lib.jfr.EventNames; -import jdk.test.lib.jfr.RecurseThread; - -/* - * @test - * @requires vm.hasJFR & os.family == "linux" - * @library /test/lib - * @modules jdk.jfr/jdk.jfr.internal - * @run main/timeout=30 jdk.jfr.event.profiling.TestCPUTimeAndExecutionSample - */ -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); - } - - private static void run(String eventType) { - RecurseThread t = new RecurseThread(50); - t.setDaemon(true); - try (RecordingStream rs = new RecordingStream()) { - rs.enable(sampleEvent).with("throttle", "1000/s"); - rs.onEvent(sampleEvent, e -> { - t.quit(); - rs.close(); - }); - t.start(); - rs.start(); - } - } -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java deleted file mode 100644 index 0d1108346ab..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.event.profiling; - -import jdk.test.lib.jfr.EventNames; - -/** - * @test - * @requires vm.hasJFR & os.family == "linux" - * @library /test/lib - * @build jdk.jfr.event.profiling.BaseTestFullStackTrace - * @run main/othervm jdk.jfr.event.profiling.TestCPUTimeSampleFullStackTrace - */ -public class TestCPUTimeSampleFullStackTrace { - - public static void main(String[] args) throws Throwable { - new BaseTestFullStackTrace(EventNames.CPUTimeSample, "eventThread").run(); - } - -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleMultipleRecordings.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleMultipleRecordings.java deleted file mode 100644 index 133df36684c..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleMultipleRecordings.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. 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.event.profiling; - -import java.time.Duration; - -import jdk.jfr.Recording; -import jdk.jfr.consumer.RecordingStream; -import jdk.jfr.internal.JVM; -import jdk.test.lib.jfr.EventNames; - -/* - * Tests that creating multiple recordings after another is possible. - * @test - * @requires vm.hasJFR & os.family == "linux" - * @library /test/lib - * @modules jdk.jfr/jdk.jfr.internal - * @run main jdk.jfr.event.profiling.TestCPUTimeSampleMultipleRecordings - */ -public class TestCPUTimeSampleMultipleRecordings { - - static String nativeEvent = EventNames.CPUTimeSample; - - static volatile boolean alive = true; - - public static void main(String[] args) throws Exception { - Thread t = new Thread(TestCPUTimeSampleMultipleRecordings::nativeMethod); - t.setDaemon(true); - t.start(); - for (int i = 0; i < 2; i++) { - try (RecordingStream rs = new RecordingStream()) { - rs.enable(nativeEvent).with("throttle", "1ms"); - rs.onEvent(nativeEvent, e -> { - alive = false; - rs.close(); - }); - - rs.start(); - } - } - alive = false; - } - - public static void nativeMethod() { - while (alive) { - JVM.getPid(); - } - } -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleNative.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleNative.java deleted file mode 100644 index 1617bce4ba3..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleNative.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2017, 2023, 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.event.profiling; - -import java.time.Duration; - -import jdk.jfr.Recording; -import jdk.jfr.consumer.RecordingStream; -import jdk.jfr.internal.JVM; -import jdk.test.lib.jfr.EventNames; - -/* - * @test - * @requires vm.hasJFR & os.family == "linux" - * @library /test/lib - * @modules jdk.jfr/jdk.jfr.internal - * @run main jdk.jfr.event.profiling.TestCPUTimeSampleNative - */ -public class TestCPUTimeSampleNative { - - static String nativeEvent = EventNames.CPUTimeSample; - - static volatile boolean alive = true; - - public static void main(String[] args) throws Exception { - try (RecordingStream rs = new RecordingStream()) { - rs.enable(nativeEvent).with("throttle", "1ms"); - rs.onEvent(nativeEvent, e -> { - alive = false; - rs.close(); - }); - Thread t = new Thread(TestCPUTimeSampleNative::nativeMethod); - t.setDaemon(true); - t.start(); - rs.start(); - } - - } - - public static void nativeMethod() { - while (alive) { - JVM.getPid(); - } - } -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java deleted file mode 100644 index 55b350ad096..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. 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.event.profiling; -import java.lang.management.ManagementFactory; -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Comparator; - -import jdk.jfr.Recording; -import jdk.jfr.consumer.RecordedEvent; -import jdk.test.lib.Asserts; -import jdk.test.lib.jfr.EventNames; -import jdk.test.lib.jfr.Events; - -/** - * @test - * @requires vm.hasJFR & os.family == "linux" - * @library /test/lib - * @run main/othervm jdk.jfr.event.profiling.TestCPUTimeSampleThrottling - */ -public class TestCPUTimeSampleThrottling { - - public static void main(String[] args) throws Exception { - testZeroPerSecond(); - testThrottleSettings(); - testThrottleSettingsPeriod(); - } - - private static void testZeroPerSecond() throws Exception { - Asserts.assertTrue(0L == countEvents(1000, "0/s").count()); - } - - private static void testThrottleSettings() throws Exception { - long count = countEvents(1000, - Runtime.getRuntime().availableProcessors() * 2 + "/s").count(); - Asserts.assertTrue(count > 0 && count < 3, - "Expected between 0 and 3 events, got " + count); - } - - private static void testThrottleSettingsPeriod() throws Exception { - float rate = countEvents(1000, "10ms").rate(); - Asserts.assertTrue(rate > 90 && rate < 110, "Expected around 100 events per second, got " + rate); - } - - private record EventCount(long count, float time) { - float rate() { - return count / time; - } - } - - private static EventCount countEvents(int timeMs, String rate) throws Exception { - try(Recording recording = new Recording()) { - recording.enable(EventNames.CPUTimeSample) - .with("throttle", rate); - - var bean = ManagementFactory.getThreadMXBean(); - - recording.start(); - - long startThreadCpuTime = bean.getCurrentThreadCpuTime(); - - wasteCPU(timeMs); - - long spendCPUTime = bean.getCurrentThreadCpuTime() - startThreadCpuTime; - - recording.stop(); - - long eventCount = Events.fromRecording(recording).stream() - .filter(e -> e.getThread().getJavaName() - .equals(Thread.currentThread().getName())) - .count(); - - System.out.println("Event count: " + eventCount + ", CPU time: " + spendCPUTime / 1_000_000_000f + "s"); - - return new EventCount(eventCount, spendCPUTime / 1_000_000_000f); - } - } - - private static void wasteCPU(int durationMs) { - long start = System.currentTimeMillis(); - double i = 0; - while (System.currentTimeMillis() - start < durationMs) { - for (int j = 0; j < 100000; j++) { - i = Math.sqrt(i * Math.pow(Math.sqrt(Math.random()), Math.random())); - } - } - } - -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingLongPeriod.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingLongPeriod.java deleted file mode 100644 index 69d32d48282..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSamplingLongPeriod.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2017, 2022, 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.event.profiling; - -import java.time.Duration; - -import jdk.jfr.consumer.RecordingStream; -import jdk.test.lib.jfr.EventNames; -import jdk.test.lib.jfr.RecurseThread; - -/* - * @test - * @requires vm.hasJFR & os.family == "linux" - * @library /test/lib - * @modules jdk.jfr/jdk.jfr.internal - * @run main jdk.jfr.event.profiling.TestCPUTimeSamplingLongPeriod - */ -public class TestCPUTimeSamplingLongPeriod { - - 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 { - RecurseThread t = new RecurseThread(50); - t.setDaemon(true); - try (RecordingStream rs = new RecordingStream()) { - rs.enable(sampleEvent).with("throttle", "1100ms"); - rs.onEvent(sampleEvent, e -> { - t.quit(); - rs.close(); - }); - t.start(); - rs.start(); - } - } -} diff --git a/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java b/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java index c337a8128ab..b06f9eed03d 100644 --- a/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java +++ b/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java @@ -23,20 +23,147 @@ package jdk.jfr.event.profiling; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.Asserts; import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.RecurseThread; /** * @test * @requires vm.hasJFR * @requires vm.opt.DeoptimizeALot != true * @library /test/lib - * @build jdk.jfr.event.profiling.BaseTestFullStackTrace * @run main/othervm jdk.jfr.event.profiling.TestFullStackTrace */ public class TestFullStackTrace { + private final static String EVENT_NAME = EventNames.ExecutionSample; + private final static int MAX_DEPTH = 64; // currently hardcoded in jvm public static void main(String[] args) throws Throwable { - new BaseTestFullStackTrace(EventNames.ExecutionSample, "sampledThread").run(); + RecurseThread[] threads = new RecurseThread[3]; + for (int i = 0; i < threads.length; ++i) { + int depth = MAX_DEPTH - 1 + i; + threads[i] = new RecurseThread(depth); + threads[i].setName("recursethread-" + depth); + threads[i].start(); + } + + for (RecurseThread thread : threads) { + while (!thread.isInRunLoop()) { + Thread.sleep(20); + } + } + + assertStackTraces(threads); + + for (RecurseThread thread : threads) { + thread.quit(); + thread.join(); + } } + private static void assertStackTraces( RecurseThread[] threads) throws Throwable { + Recording recording= null; + do { + recording = new Recording(); + recording.enable(EVENT_NAME).withPeriod(Duration.ofMillis(50)); + recording.start(); + Thread.sleep(500); + recording.stop(); + } while (!hasValidStackTraces(recording, threads)); + } + + private static boolean hasValidStackTraces(Recording recording, RecurseThread[] threads) throws Throwable { + boolean[] isEventFound = new boolean[threads.length]; + + for (RecordedEvent event : Events.fromRecording(recording)) { + //System.out.println("Event: " + event); + String threadName = Events.assertField(event, "sampledThread.javaName").getValue(); + long threadId = Events.assertField(event, "sampledThread.javaThreadId").getValue(); + + for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { + RecurseThread currThread = threads[threadIndex]; + if (threadId == currThread.getId()) { + System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); + Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); + if ("recurseEnd".equals(getTopMethodName(event))) { + isEventFound[threadIndex] = true; + checkEvent(event, currThread.totalDepth); + break; + } + } + } + } + + for (int i = 0; i < threads.length; ++i) { + String msg = "threadIndex=%d, recurseDepth=%d, isEventFound=%b%n"; + System.out.printf(msg, i, threads[i].totalDepth, isEventFound[i]); + } + for (int i = 0; i < threads.length; ++i) { + if(!isEventFound[i]) { + // no assertion, let's retry. + // Could be race condition, i.e safe point during Thread.sleep + System.out.println("Failed to validate all threads, will retry."); + return false; + } + } + return true; + } + + public static String getTopMethodName(RecordedEvent event) { + List frames = event.getStackTrace().getFrames(); + Asserts.assertFalse(frames.isEmpty(), "JavaFrames was empty"); + return frames.getFirst().getMethod().getName(); + } + + private static void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable { + RecordedStackTrace stacktrace = null; + try { + stacktrace = event.getStackTrace(); + List frames = stacktrace.getFrames(); + Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); + List expectedMethods = getExpectedMethods(expectedDepth); + Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); + + for (int i = 0; i < frames.size(); ++i) { + String name = frames.get(i).getMethod().getName(); + String expectedName = expectedMethods.get(i); + System.out.printf("method[%d]=%s, expected=%s%n", i, name, expectedName); + Asserts.assertEquals(name, expectedName, "Wrong method name"); + } + + boolean isTruncated = stacktrace.isTruncated(); + boolean isTruncateExpected = expectedDepth > MAX_DEPTH; + Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); + + String firstMethod = frames.getLast().getMethod().getName(); + boolean isFullTrace = "run".equals(firstMethod); + String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); + Asserts.assertTrue(isTruncated != isFullTrace, msg); + } catch (Throwable t) { + System.out.println(String.format("stacktrace:%n%s", stacktrace)); + throw t; + } + } + + private static List getExpectedMethods(int depth) { + List methods = new ArrayList<>(); + methods.add("recurseEnd"); + for (int i = 0; i < depth - 2; ++i) { + methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); + } + methods.add("run"); + if (depth > MAX_DEPTH) { + methods = methods.subList(0, MAX_DEPTH); + } + return methods; + } } diff --git a/test/jdk/jdk/jfr/event/profiling/classes/test/RecursiveMethods.java b/test/jdk/jdk/jfr/event/profiling/classes/test/RecursiveMethods.java deleted file mode 100644 index 8dc77fd38da..00000000000 --- a/test/jdk/jdk/jfr/event/profiling/classes/test/RecursiveMethods.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2025 SAP SE. 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 test; - - import java.time.Duration; - - /* - * A class used to create a simple deep call stack for testing purposes - */ - public class RecursiveMethods { - - /** Method that uses recursion to produce a call stack of at least {@code depth} depth */ - public static int entry(int depth) { - return method2(--depth); - } - - private static int method2(int depth) { - return method3(--depth); - } - - private static int method3(int depth) { - return method4(--depth); - } - - private static int method4(int depth) { - return method5(--depth); - } - - private static int method5(int depth) { - return method6(--depth); - } - - private static int method6(int depth) { - return method7(--depth); - } - - private static int method7(int depth) { - return method8(--depth); - } - - private static int method8(int depth) { - return method9(--depth); - } - - private static int method9(int depth) { - return method10(--depth); - } - - private static int method10(int depth) { - if (depth > 0) { - return entry(--depth); - } - return depth; - } -} diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index a00898358a8..904abe8e3e2 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -77,8 +77,6 @@ public class EventNames { public static final String ThreadAllocationStatistics = PREFIX + "ThreadAllocationStatistics"; public static final String ExecutionSample = PREFIX + "ExecutionSample"; public static final String NativeMethodSample = PREFIX + "NativeMethodSample"; - public static final String CPUTimeSample = PREFIX + "CPUTimeSample"; - public static final String CPUTimeSamplesLost = PREFIX + "CPUTimeSamplesLost"; public static final String ThreadDump = PREFIX + "ThreadDump"; public static final String OldObjectSample = PREFIX + "OldObjectSample"; public static final String SymbolTableStatistics = PREFIX + "SymbolTableStatistics";