blob: 7049df0198bb6e6c0d650ed3e52e3679bce61bef [file] [edit]
/*
* 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.
*
*/
#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"
#include "runtime/os.hpp"
#include "runtime/safepointMechanism.inline.hpp"
#include "runtime/stubRoutines.hpp"
static inline bool is_entry_frame(address pc) {
return StubRoutines::returns_to_call_stub(pc);
}
static inline bool is_entry_frame(const JfrSampleRequest& request) {
return is_entry_frame(static_cast<address>(request._sample_pc));
}
static inline bool is_interpreter(address pc) {
return Interpreter::contains(pc);
}
static inline bool is_interpreter(const JfrSampleRequest& request) {
return is_interpreter(static_cast<address>(request._sample_pc));
}
static inline address interpreter_frame_bcp(const JfrSampleRequest& request) {
assert(is_interpreter(request), "invariant");
return frame::interpreter_bcp(static_cast<intptr_t*>(request._sample_bcp));
}
static inline bool in_stack(intptr_t* ptr, JavaThread* jt) {
assert(jt != nullptr, "invariant");
return jt->is_in_full_stack_checked(reinterpret_cast<address>(ptr));
}
static inline bool sp_in_stack(const JfrSampleRequest& request, JavaThread* jt) {
return in_stack(static_cast<intptr_t*>(request._sample_sp), jt);
}
static inline bool fp_in_stack(const JfrSampleRequest& request, JavaThread* jt) {
return in_stack(static_cast<intptr_t*>(request._sample_bcp), jt);
}
static inline void update_interpreter_frame_sender_pc(JfrSampleRequest& request, intptr_t* fp) {
request._sample_pc = frame::interpreter_return_address(fp);
}
static inline void update_interpreter_frame_pc(JfrSampleRequest& request, JavaThread* jt) {
assert(fp_in_stack(request, jt), "invariant");
assert(is_interpreter(request), "invariant");
request._sample_pc = frame::interpreter_return_address(static_cast<intptr_t*>(request._sample_bcp));
}
static inline address interpreter_frame_return_address(const JfrSampleRequest& request) {
assert(is_interpreter(request), "invariant");
return frame::interpreter_return_address(static_cast<intptr_t*>(request._sample_bcp));
}
static inline intptr_t* frame_sender_sp(const JfrSampleRequest& request, JavaThread* jt) {
assert(fp_in_stack(request, jt), "invariant");
return frame::sender_sp(static_cast<intptr_t*>(request._sample_bcp));
}
static inline void update_frame_sender_sp(JfrSampleRequest& request, JavaThread* jt) {
request._sample_sp = frame_sender_sp(request, jt);
}
static inline void update_frame_sender_sp(JfrSampleRequest& request, intptr_t* fp) {
request._sample_sp = frame::sender_sp(fp);
}
static inline intptr_t* frame_link(const JfrSampleRequest& request) {
return frame::link(static_cast<intptr_t*>(request._sample_bcp));
}
static inline void update_sp(JfrSampleRequest& request, int frame_size) {
assert(frame_size >= 0, "invariant");
request._sample_sp = static_cast<intptr_t*>(request._sample_sp) + frame_size;
}
static inline void update_pc(JfrSampleRequest& request) {
assert(request._sample_sp != nullptr, "invariant");
request._sample_pc = frame::return_address(static_cast<intptr_t*>(request._sample_sp));
}
static inline void update_fp(JfrSampleRequest& request) {
assert(request._sample_sp != nullptr, "invariant");
request._sample_bcp = is_interpreter(request) ? frame::fp(static_cast<intptr_t*>(request._sample_sp)) : nullptr;
}
// Less extensive sanity checks for an interpreter frame.
static bool is_valid_interpreter_frame(const JfrSampleRequest& request, JavaThread* jt) {
assert(sp_in_stack(request, jt), "invariant");
assert(fp_in_stack(request, jt), "invariant");
return frame::is_interpreter_frame_setup_at(static_cast<intptr_t*>(request._sample_bcp), request._sample_sp);
}
static inline bool is_continuation_frame(address pc) {
return ContinuationEntry::return_pc() == pc;
}
static inline bool is_continuation_frame(const JfrSampleRequest& request) {
return is_continuation_frame(static_cast<address>(request._sample_pc));
}
static intptr_t* sender_for_interpreter_frame(JfrSampleRequest& request, JavaThread* jt) {
update_interpreter_frame_pc(request, jt); // pick up return address
if (is_continuation_frame(request) || is_entry_frame(request)) {
request._sample_pc = nullptr;
return nullptr;
}
update_frame_sender_sp(request, jt);
intptr_t* fp = nullptr;
if (is_interpreter(request)) {
fp = frame_link(request);
}
request._sample_bcp = nullptr;
return fp;
}
static bool build(JfrSampleRequest& request, intptr_t* fp, JavaThread* jt);
static bool build_for_interpreter(JfrSampleRequest& request, JavaThread* jt) {
assert(is_interpreter(request), "invariant");
assert(jt != nullptr, "invariant");
if (!fp_in_stack(request, jt)) {
return false;
}
if (is_valid_interpreter_frame(request, jt)) {
// Set fp as sp for interpreter frames.
request._sample_sp = request._sample_bcp;
// Get real bcp.
void* const bcp = interpreter_frame_bcp(request);
// Setting bcp = 1 marks the sample request to represent a native method.
request._sample_bcp = bcp != nullptr ? bcp : reinterpret_cast<address>(1);
return true;
}
intptr_t* fp = sender_for_interpreter_frame(request, jt);
if (request._sample_pc == nullptr || request._sample_sp == nullptr) {
return false;
}
return build(request, fp, jt);
}
// Attempt to build a Jfr sample request.
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");
// 1. Interpreter frame?
if (is_interpreter(request)) {
request._sample_bcp = fp;
return build_for_interpreter(request, jt);
}
const CodeBlob* const cb = CodeCache::find_blob(request._sample_pc);
if (cb != nullptr) {
// 2. Is nmethod?
return cb->is_nmethod();
// 3. What kind of CodeBlob or Stub?
// Longer plan is to make stubs and blobs parsable,
// and we will have a list of cases here for each blob type
// describing how to locate the sender. We can't get to the
// sender of a blob or stub until they have a standardized
// layout and proper metadata descriptions.
}
return false;
}
static bool build_from_ljf(JfrSampleRequest& request,
const JfrThreadLocal* tl,
JavaThread* jt) {
assert(tl != nullptr, "invariant");
assert(jt != nullptr, "invariant");
assert(jt->jfr_thread_local() == tl, "invariant");
assert(sp_in_stack(request, jt), "invariant");
// Last Java frame is available, but might not be walkable, fix it.
address last_pc = jt->last_Java_pc();
if (last_pc == nullptr) {
last_pc = frame::return_address(static_cast<intptr_t*>(request._sample_sp));
if (last_pc == nullptr) {
return false;
}
}
assert(last_pc != nullptr, "invariant");
if (is_interpreter(last_pc)) {
if (tl->in_sampling_critical_section()) {
return false;
}
request._sample_pc = last_pc;
request._sample_bcp = jt->frame_anchor()->last_Java_fp();
return build_for_interpreter(request, jt);
}
request._sample_pc = last_pc;
return build(request, nullptr, jt);
}
static bool build_from_context(JfrSampleRequest& request,
const void* ucontext,
const JfrThreadLocal* tl,
JavaThread* jt) {
assert(ucontext != nullptr, "invariant");
assert(tl != nullptr, "invariant");
assert(jt != nullptr, "invariant");
assert(jt->jfr_thread_local() == tl, "invariant");
assert(!jt->has_last_Java_frame(), "invariant");
intptr_t* fp;
request._sample_pc = os::fetch_frame_from_context(ucontext, reinterpret_cast<intptr_t**>(&request._sample_sp), &fp);
assert(sp_in_stack(request, jt), "invariant");
if (is_interpreter(request)) {
if (tl->in_sampling_critical_section() || !in_stack(fp, jt)) {
return false;
}
if (frame::is_interpreter_frame_setup_at(fp, request._sample_sp)) {
// Set fp as sp for interpreter frames.
request._sample_sp = fp;
void* bcp = os::fetch_bcp_from_context(ucontext);
// Setting bcp = 1 marks the sample request to represent a native method.
request._sample_bcp = bcp != nullptr ? bcp : reinterpret_cast<void*>(1);
return true;
}
request._sample_bcp = fp;
fp = sender_for_interpreter_frame(request, jt);
if (request._sample_pc == nullptr || request._sample_sp == nullptr) {
return false;
}
}
return build(request, fp, jt);
}
static inline JfrSampleResult set_request_and_arm_local_poll(JfrSampleRequest& request, JfrThreadLocal* tl, JavaThread* jt) {
assert(tl != nullptr, "invariant");
assert(jt->jfr_thread_local() == tl, "invariant");
tl->set_sample_state(JAVA_SAMPLE);
SafepointMechanism::arm_local_poll_release(jt);
// For a Java sample, request._sample_ticks is also the start time for the SafepointLatency event.
request._sample_ticks = JfrTicks::now();
tl->set_sample_request(request);
return SAMPLE_JAVA;
}
// A biased sample request is denoted by an empty bcp and an empty pc.
static inline JfrSampleResult set_biased_java_sample(JfrSampleRequest& request, JfrThreadLocal* tl, JavaThread* jt) {
if (request._sample_bcp != nullptr) {
request._sample_bcp = nullptr;
}
assert(request._sample_bcp == nullptr, "invariant");
request._sample_pc = nullptr;
return set_request_and_arm_local_poll(request, tl, jt);
}
static inline JfrSampleResult set_unbiased_java_sample(JfrSampleRequest& request, JfrThreadLocal* tl, JavaThread* jt) {
assert(request._sample_sp != nullptr, "invariant");
assert(sp_in_stack(request, jt), "invariant");
assert(request._sample_bcp != nullptr || !is_interpreter(request), "invariant");
return set_request_and_arm_local_poll(request, tl, jt);
}
JfrSampleResult JfrSampleRequestBuilder::build_java_sample_request(const void* ucontext,
JfrThreadLocal* tl,
JavaThread* jt) {
assert(ucontext != nullptr, "invariant");
assert(tl != nullptr, "invariant");
assert(tl->sample_state() == NO_SAMPLE, "invariant");
assert(jt != nullptr, "invariant");
assert(jt->thread_state() == _thread_in_Java, "invariant");
JfrSampleRequest request;
// Prioritize the ljf, if one exists.
request._sample_sp = jt->last_Java_sp();
if (request._sample_sp != nullptr) {
if (build_from_ljf(request, tl, jt)) {
return set_unbiased_java_sample(request, tl, jt);
}
} else if (build_from_context(request, ucontext, tl, jt)) {
return set_unbiased_java_sample(request, tl, jt);
}
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<intptr_t**>(&request._sample_sp), &fp);
assert(sp_in_stack(request, jt), "invariant");
if (!build(request, fp, jt)) {
set_cpu_time_biased_sample(request, jt);
}
}
}