| /* | |
| * Copyright (c) 2020, 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 "logging/log.hpp" | |
| #include "runtime/atomic.hpp" | |
| #include "runtime/frame.inline.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/osThread.hpp" | |
| #include "runtime/safepoint.hpp" | |
| #include "runtime/stackFrameStream.inline.hpp" | |
| #include "runtime/stackWatermark.inline.hpp" | |
| #include "utilities/debug.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| #include "utilities/macros.hpp" | |
| #include "utilities/preserveException.hpp" | |
| class StackWatermarkFramesIterator : public CHeapObj<mtThread> { | |
| JavaThread* _jt; | |
| uintptr_t _caller; | |
| uintptr_t _callee; | |
| StackFrameStream _frame_stream; | |
| StackWatermark& _owner; | |
| bool _is_done; | |
| void set_watermark(uintptr_t sp); | |
| RegisterMap& register_map(); | |
| frame& current(); | |
| void next(); | |
| public: | |
| StackWatermarkFramesIterator(StackWatermark& owner); | |
| uintptr_t caller() const { return _caller; } | |
| uintptr_t callee() const { return _callee; } | |
| void process_one(void* context); | |
| void process_all(void* context); | |
| bool has_next() const; | |
| }; | |
| void StackWatermarkFramesIterator::set_watermark(uintptr_t sp) { | |
| assert(sp != 0, "Sanity check"); | |
| if (!has_next()) { | |
| return; | |
| } | |
| if (_callee == 0) { | |
| _callee = sp; | |
| } else if (_caller == 0) { | |
| _caller = sp; | |
| } else { | |
| _callee = _caller; | |
| _caller = sp; | |
| } | |
| } | |
| // This class encapsulates various marks we need to deal with calling the | |
| // frame processing code from arbitrary points in the runtime. It is mostly | |
| // due to problems that we might want to eventually clean up inside of the | |
| // frame processing code, such as creating random handles even though there | |
| // is no safepoint to protect against, and fiddling around with exceptions. | |
| class StackWatermarkProcessingMark { | |
| ResetNoHandleMark _rnhm; | |
| HandleMark _hm; | |
| PreserveExceptionMark _pem; | |
| ResourceMark _rm; | |
| public: | |
| StackWatermarkProcessingMark(Thread* thread) : | |
| _rnhm(), | |
| _hm(thread), | |
| _pem(thread), | |
| _rm(thread) { } | |
| }; | |
| void StackWatermarkFramesIterator::process_one(void* context) { | |
| StackWatermarkProcessingMark swpm(Thread::current()); | |
| while (has_next()) { | |
| frame f = current(); | |
| uintptr_t sp = reinterpret_cast<uintptr_t>(f.sp()); | |
| bool frame_has_barrier = StackWatermark::has_barrier(f); | |
| _owner.process(f, register_map(), context); | |
| next(); | |
| if (frame_has_barrier) { | |
| set_watermark(sp); | |
| break; | |
| } | |
| } | |
| } | |
| void StackWatermarkFramesIterator::process_all(void* context) { | |
| const uintptr_t frames_per_poll_gc = 5; | |
| ResourceMark rm; | |
| log_info(stackbarrier)("Processing whole stack for tid %d", | |
| _jt->osthread()->thread_id()); | |
| uint i = 0; | |
| while (has_next()) { | |
| frame f = current(); | |
| uintptr_t sp = reinterpret_cast<uintptr_t>(f.sp()); | |
| assert(sp >= _caller, "invariant"); | |
| bool frame_has_barrier = StackWatermark::has_barrier(f); | |
| _owner.process(f, register_map(), context); | |
| next(); | |
| if (frame_has_barrier) { | |
| set_watermark(sp); | |
| if (++i == frames_per_poll_gc) { | |
| // Yield every N frames so mutator can progress faster. | |
| i = 0; | |
| _owner.yield_processing(); | |
| } | |
| } | |
| } | |
| } | |
| StackWatermarkFramesIterator::StackWatermarkFramesIterator(StackWatermark& owner) : | |
| _jt(owner._jt), | |
| _caller(0), | |
| _callee(0), | |
| _frame_stream(owner._jt, true /* update_registers */, false /* process_frames */), | |
| _owner(owner), | |
| _is_done(_frame_stream.is_done()) { | |
| } | |
| frame& StackWatermarkFramesIterator::current() { | |
| return *_frame_stream.current(); | |
| } | |
| RegisterMap& StackWatermarkFramesIterator::register_map() { | |
| return *_frame_stream.register_map(); | |
| } | |
| bool StackWatermarkFramesIterator::has_next() const { | |
| return !_is_done; | |
| } | |
| void StackWatermarkFramesIterator::next() { | |
| _frame_stream.next(); | |
| _is_done = _frame_stream.is_done(); | |
| } | |
| StackWatermark::StackWatermark(JavaThread* jt, StackWatermarkKind kind, uint32_t epoch) : | |
| _state(StackWatermarkState::create(epoch, true /* is_done */)), | |
| _watermark(0), | |
| _next(nullptr), | |
| _jt(jt), | |
| _iterator(nullptr), | |
| _lock(Mutex::stackwatermark, "StackWatermark_lock"), | |
| _kind(kind), | |
| _linked_watermarks() { | |
| } | |
| StackWatermark::~StackWatermark() { | |
| delete _iterator; | |
| } | |
| #ifdef ASSERT | |
| void StackWatermark::assert_is_frame_safe(const frame& f) { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| assert(is_frame_safe(f), "Frame must be safe"); | |
| } | |
| #endif | |
| // A frame is "safe" if it *and* its caller have been processed. This is the invariant | |
| // that allows exposing a frame, and for that frame to directly access its caller frame | |
| // without going through any hooks. | |
| bool StackWatermark::is_frame_safe(const frame& f) { | |
| assert(_lock.owned_by_self(), "Must be locked"); | |
| uint32_t state = Atomic::load(&_state); | |
| if (!processing_started(state)) { | |
| return false; | |
| } | |
| if (processing_completed(state)) { | |
| return true; | |
| } | |
| return reinterpret_cast<uintptr_t>(f.sp()) < _iterator->caller(); | |
| } | |
| void StackWatermark::start_processing_impl(void* context) { | |
| log_info(stackbarrier)("Starting stack processing for tid %d", | |
| _jt->osthread()->thread_id()); | |
| delete _iterator; | |
| if (_jt->has_last_Java_frame()) { | |
| _iterator = new StackWatermarkFramesIterator(*this); | |
| // Always process three frames when starting an iteration. | |
| // | |
| // The three frames corresponds to: | |
| // 1) The callee frame | |
| // 2) The caller frame | |
| // This allows a callee to always be able to read state from its caller | |
| // without needing any special barriers. | |
| // | |
| // 3) An extra frame to deal with unwinding safepointing on the way out. | |
| // Sometimes, we also call into the runtime to on_unwind(), but then | |
| // hit a safepoint poll on the way out from the runtime. | |
| _iterator->process_one(context); | |
| _iterator->process_one(context); | |
| _iterator->process_one(context); | |
| } else { | |
| _iterator = nullptr; | |
| } | |
| update_watermark(); | |
| } | |
| void StackWatermark::yield_processing() { | |
| update_watermark(); | |
| MutexUnlocker mul(&_lock, Mutex::_no_safepoint_check_flag); | |
| } | |
| void StackWatermark::update_watermark() { | |
| assert(_lock.owned_by_self(), "invariant"); | |
| if (_iterator != nullptr && _iterator->has_next()) { | |
| assert(_iterator->callee() != 0, "sanity"); | |
| Atomic::release_store(&_watermark, _iterator->callee()); | |
| Atomic::release_store(&_state, StackWatermarkState::create(epoch_id(), false /* is_done */)); // release watermark w.r.t. epoch | |
| } else { | |
| Atomic::release_store(&_watermark, uintptr_t(0)); // Release stack data modifications w.r.t. watermark | |
| Atomic::release_store(&_state, StackWatermarkState::create(epoch_id(), true /* is_done */)); // release watermark w.r.t. epoch | |
| log_info(stackbarrier)("Finished stack processing iteration for tid %d", | |
| _jt->osthread()->thread_id()); | |
| } | |
| } | |
| void StackWatermark::process_one() { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| if (!processing_started()) { | |
| start_processing_impl(nullptr /* context */); | |
| } else if (!processing_completed()) { | |
| _iterator->process_one(nullptr /* context */); | |
| update_watermark(); | |
| } | |
| } | |
| void StackWatermark::push_linked_watermark(StackWatermark* watermark) { | |
| assert(JavaThread::current() == _jt, "This code is not thread safe"); | |
| _linked_watermarks.push(watermark); | |
| } | |
| void StackWatermark::pop_linked_watermark() { | |
| assert(JavaThread::current() == _jt, "This code is not thread safe"); | |
| assert(_linked_watermarks.length() > 0, "Mismatched push and pop?"); | |
| _linked_watermarks.pop(); | |
| } | |
| uintptr_t StackWatermark::watermark() { | |
| return Atomic::load_acquire(&_watermark); | |
| } | |
| uintptr_t StackWatermark::last_processed() { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| if (!processing_started()) { | |
| // Stale state; no last processed | |
| return 0; | |
| } | |
| if (processing_completed()) { | |
| // Already processed all; no last processed | |
| return 0; | |
| } | |
| return _iterator->caller(); | |
| } | |
| uintptr_t StackWatermark::last_processed_raw() { | |
| return _iterator->caller(); | |
| } | |
| bool StackWatermark::processing_started() const { | |
| return processing_started(Atomic::load(&_state)); | |
| } | |
| bool StackWatermark::processing_started_acquire() const { | |
| return processing_started(Atomic::load_acquire(&_state)); | |
| } | |
| bool StackWatermark::processing_completed() const { | |
| return processing_completed(Atomic::load(&_state)); | |
| } | |
| bool StackWatermark::processing_completed_acquire() const { | |
| return processing_completed(Atomic::load_acquire(&_state)); | |
| } | |
| void StackWatermark::process_linked_watermarks() { | |
| assert(JavaThread::current() == _jt, "This code is not thread safe"); | |
| // Finish processing all linked stack watermarks | |
| for (StackWatermark* watermark : _linked_watermarks) { | |
| watermark->finish_processing(nullptr /* context */); | |
| } | |
| } | |
| void StackWatermark::on_safepoint() { | |
| start_processing(); | |
| // If the thread waking up from a safepoint expected certain other | |
| // stack watermarks (potentially from different threads) are processed, | |
| // then we have to perform processing of said linked watermarks here. | |
| process_linked_watermarks(); | |
| } | |
| void StackWatermark::start_processing() { | |
| if (!processing_started_acquire()) { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| if (!processing_started()) { | |
| start_processing_impl(nullptr /* context */); | |
| } | |
| } | |
| } | |
| void StackWatermark::finish_processing(void* context) { | |
| MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); | |
| if (!processing_started()) { | |
| start_processing_impl(context); | |
| } | |
| if (!processing_completed()) { | |
| _iterator->process_all(context); | |
| update_watermark(); | |
| } | |
| } |