blob: 8c82dfad8af8fe3600a4c3278675517ab21e624e [file] [edit]
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* 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_SUPPORT_JFRTHREADLOCAL_HPP
#define SHARE_JFR_SUPPORT_JFRTHREADLOCAL_HPP
#include "jfr/periodic/sampling/jfrSampleRequest.hpp"
#include "jfr/utilities/jfrAllocation.hpp"
#include "jfr/utilities/jfrBlob.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/atomic.hpp"
#include "runtime/mutexLocker.hpp"
#ifdef LINUX
#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp"
#endif
class Arena;
class JavaThread;
class JfrBuffer;
class Thread;
class JfrThreadLocal {
friend class Jfr;
friend class JfrIntrinsicSupport;
friend class JfrJavaSupport;
friend class JVMCIVMStructs;
private:
mutable JfrSampleRequest _sample_request;
JfrSampleRequestQueue _sample_request_queue;
Monitor _sample_monitor;
jobject _java_event_writer;
mutable JfrBuffer* _java_buffer;
mutable JfrBuffer* _native_buffer;
JfrBuffer* _shelved_buffer;
JfrBuffer* _load_barrier_buffer_epoch_0;
JfrBuffer* _load_barrier_buffer_epoch_1;
JfrBuffer* _checkpoint_buffer_epoch_0;
JfrBuffer* _checkpoint_buffer_epoch_1;
volatile int _sample_state;
Arena* _dcmd_arena;
JfrBlobHandle _thread;
mutable traceid _vthread_id;
mutable traceid _jvm_thread_id;
mutable traceid _thread_id_alias;
u8 _data_lost;
traceid _stack_trace_id;
traceid _stack_trace_hash;
traceid _parent_trace_id;
int64_t _last_allocated_bytes;
jlong _user_time;
jlong _cpu_time;
jlong _wallclock_time;
int32_t _non_reentrant_nesting;
u2 _vthread_epoch;
mutable u2 _generation;
bool _vthread_excluded;
bool _jvm_thread_excluded;
volatile bool _enqueued_requests;
bool _vthread;
bool _notified;
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);
static void release(JfrThreadLocal* tl, Thread* t);
static void initialize_main_thread(JavaThread* jt);
static void set(bool* excluded_field, bool state);
static traceid assign_thread_id(const Thread* t, JfrThreadLocal* tl);
static traceid vthread_id(const Thread* t);
static void set_vthread_epoch(const JavaThread* jt, traceid tid, u2 epoch);
static void set_vthread_epoch_checked(const JavaThread* jt, traceid tid, u2 epoch);
static traceid jvm_thread_id(const JfrThreadLocal* tl);
bool is_vthread_excluded() const;
static void exclude_vthread(const JavaThread* jt);
static void include_vthread(const JavaThread* jt);
static bool is_jvm_thread_excluded(const Thread* t);
static void exclude_jvm_thread(const Thread* t);
static void include_jvm_thread(const Thread* t);
static bool is_non_reentrant();
public:
JfrThreadLocal();
JfrBuffer* native_buffer() const {
return _native_buffer != nullptr ? _native_buffer : install_native_buffer();
}
bool has_native_buffer() const {
return _native_buffer != nullptr;
}
void set_native_buffer(JfrBuffer* buffer) {
_native_buffer = buffer;
}
JfrBuffer* java_buffer() const {
return _java_buffer != nullptr ? _java_buffer : install_java_buffer();
}
bool has_java_buffer() const {
return _java_buffer != nullptr;
}
void set_java_buffer(JfrBuffer* buffer) {
_java_buffer = buffer;
}
JfrBuffer* shelved_buffer() const {
return _shelved_buffer;
}
void shelve_buffer(JfrBuffer* buffer) {
_shelved_buffer = buffer;
}
bool has_java_event_writer() const {
return _java_event_writer != nullptr;
}
jobject java_event_writer() {
return _java_event_writer;
}
void set_java_event_writer(jobject java_event_writer) {
_java_event_writer = java_event_writer;
}
int sample_state() const {
return Atomic::load_acquire(&_sample_state);
}
void set_sample_state(int state) {
Atomic::release_store(&_sample_state, state);
}
Monitor* sample_monitor() {
return &_sample_monitor;
}
JfrSampleRequestQueue* sample_requests() {
return &_sample_request_queue;
}
JfrSampleRequest sample_request() const {
return _sample_request;
}
void set_sample_request(JfrSampleRequest request) {
_sample_request = request;
}
void set_sample_ticks() {
_sample_request._sample_ticks = JfrTicks::now();
}
void set_sample_ticks(const JfrTicks& ticks) {
_sample_request._sample_ticks = ticks;
}
bool has_sample_ticks() const {
return _sample_request._sample_ticks.value() != 0;
}
const JfrTicks& sample_ticks() const {
return _sample_request._sample_ticks;
}
bool has_enqueued_requests() const {
return Atomic::load_acquire(&_enqueued_requests);
}
void enqueue_request() {
assert_lock_strong(sample_monitor());
assert(sample_state() == JAVA_SAMPLE, "invariant");
if (_sample_request_queue.append(_sample_request) == 0) {
Atomic::release_store(&_enqueued_requests, true);
}
set_sample_state(NO_SAMPLE);
}
void clear_enqueued_requests() {
assert_lock_strong(sample_monitor());
assert(has_enqueued_requests(), "invariant");
assert(_sample_request_queue.is_nonempty(), "invariant");
_sample_request_queue.clear();
Atomic::release_store(&_enqueued_requests, false);
}
bool has_native_sample_request() const {
return sample_state() == NATIVE_SAMPLE;
}
bool has_java_sample_request() const {
return sample_state() == JAVA_SAMPLE || has_enqueued_requests();
}
bool has_sample_request() const {
return sample_state() != NO_SAMPLE || has_enqueued_requests();
}
int64_t last_allocated_bytes() const {
return _last_allocated_bytes;
}
void set_last_allocated_bytes(int64_t allocated_bytes) {
_last_allocated_bytes = allocated_bytes;
}
void clear_last_allocated_bytes() {
set_last_allocated_bytes(0);
}
// Contextually defined thread id that is volatile,
// a function of Java carrier thread mounts / unmounts.
static traceid thread_id(const Thread* t);
static bool is_vthread(const JavaThread* jt);
static u2 vthread_epoch(const JavaThread* jt);
traceid vthread_id_with_epoch_update(const JavaThread* jt) const;
// Exposed to external code that use a thread id unconditionally.
// Jfr might not even be running.
static traceid external_thread_id(const Thread* t);
// Non-volatile thread id, for Java carrier threads and non-java threads.
static traceid jvm_thread_id(const Thread* t);
// To impersonate is to temporarily masquerade as another thread.
// For example, when writing an event that should be attributed to some other thread.
static void impersonate(const Thread* t, traceid other_thread_id);
static void stop_impersonating(const Thread* t);
static bool is_impersonating(const Thread* t);
traceid parent_thread_id() const {
return _parent_trace_id;
}
void set_cached_stack_trace_id(traceid id, traceid hash = 0) {
_stack_trace_id = id;
_stack_trace_hash = hash;
}
bool has_cached_stack_trace() const {
return _stack_trace_id != max_julong;
}
void clear_cached_stack_trace() {
_stack_trace_id = max_julong;
_stack_trace_hash = 0;
}
traceid cached_stack_trace_id() const {
return _stack_trace_id;
}
traceid cached_stack_trace_hash() const {
return _stack_trace_hash;
}
u8 data_lost() const {
return _data_lost;
}
u8 add_data_lost(u8 value);
jlong get_user_time() const {
return _user_time;
}
void set_user_time(jlong user_time) {
_user_time = user_time;
}
jlong get_cpu_time() const {
return _cpu_time;
}
void set_cpu_time(jlong cpu_time) {
_cpu_time = cpu_time;
}
jlong get_wallclock_time() const {
return _wallclock_time;
}
void set_wallclock_time(jlong wallclock_time) {
_wallclock_time = wallclock_time;
}
bool is_notified() {
return _notified;
}
void notify() {
_notified = true;
}
void clear_notification() {
_notified = false;
}
bool is_dead() const {
return _dead;
}
bool in_sampling_critical_section() const {
return _sampling_critical_section;
}
// Serialization state.
bool should_write() const;
static int32_t make_non_reentrant(Thread* thread);
static void make_reentrant(Thread* thread, int32_t previous_nesting);
bool is_excluded() const;
bool is_included() const;
static bool is_excluded(const Thread* thread);
static bool is_included(const Thread* thread);
static Arena* dcmd_arena(JavaThread* jt);
bool has_thread_blob() const;
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);
static void on_set_current_thread(JavaThread* jt, oop thread);
static void on_java_thread_start(JavaThread* starter, JavaThread* startee);
// Code generation
static ByteSize java_event_writer_offset();
static ByteSize java_buffer_offset();
static ByteSize vthread_id_offset();
static ByteSize vthread_offset();
static ByteSize vthread_epoch_offset();
static ByteSize vthread_excluded_offset();
static ByteSize notified_offset();
static ByteSize sample_state_offset();
static ByteSize sampling_critical_section_offset();
friend class JfrJavaThread;
friend class JfrCheckpointManager;
template <typename>
friend class JfrEpochQueueKlassPolicy;
};
#endif // SHARE_JFR_SUPPORT_JFRTHREADLOCAL_HPP