| /* |
| * Copyright (c) 2021, 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. |
| * |
| */ |
| |
| #include "precompiled.hpp" |
| #include "code/compiledMethod.hpp" |
| #include "code/scopeDesc.hpp" |
| #include "gc/shared/barrierSet.hpp" |
| #include "gc/shared/barrierSetStackChunk.hpp" |
| #include "logging/log.hpp" |
| #include "logging/logStream.hpp" |
| #include "memory/memRegion.hpp" |
| #include "oops/instanceStackChunkKlass.inline.hpp" |
| #include "oops/oop.inline.hpp" |
| #include "oops/stackChunkOop.inline.hpp" |
| #include "runtime/frame.hpp" |
| #include "runtime/registerMap.hpp" |
| #include "runtime/smallRegisterMap.inline.hpp" |
| #include "runtime/stackChunkFrameStream.inline.hpp" |
| |
| // Note: Some functions in this file work with stale object pointers, e.g. |
| // DerivedPointerSupport. Be extra careful to not put those pointers into |
| // variables of the 'oop' type. There's extra GC verification around oops |
| // that may fail when stale oops are being used. |
| |
| template <typename RegisterMapT> |
| class FrameOopIterator : public OopIterator { |
| private: |
| const frame& _f; |
| const RegisterMapT* _map; |
| |
| public: |
| FrameOopIterator(const frame& f, const RegisterMapT* map) |
| : _f(f), |
| _map(map) { |
| } |
| |
| virtual void oops_do(OopClosure* cl) override { |
| if (_f.is_interpreted_frame()) { |
| _f.oops_interpreted_do(cl, nullptr); |
| } else { |
| OopMapDo<OopClosure, DerivedOopClosure, IncludeAllValues> visitor(cl, nullptr); |
| visitor.oops_do(&_f, _map, _f.oop_map()); |
| } |
| } |
| }; |
| |
| frame stackChunkOopDesc::top_frame(RegisterMap* map) { |
| assert(!is_empty(), ""); |
| StackChunkFrameStream<ChunkFrames::Mixed> fs(this); |
| |
| map->set_stack_chunk(this); |
| fs.initialize_register_map(map); |
| |
| frame f = fs.to_frame(); |
| |
| assert(to_offset(f.sp()) == sp(), "f.offset_sp(): %d sp(): %d async: %d", f.offset_sp(), sp(), map->is_async()); |
| relativize_frame(f); |
| f.set_frame_index(0); |
| return f; |
| } |
| |
| frame stackChunkOopDesc::sender(const frame& f, RegisterMap* map) { |
| assert(map->in_cont(), ""); |
| assert(!map->include_argument_oops(), ""); |
| assert(!f.is_empty(), ""); |
| assert(map->stack_chunk() == this, ""); |
| assert(!is_empty(), ""); |
| |
| int index = f.frame_index(); // we need to capture the index before calling derelativize, which destroys it |
| StackChunkFrameStream<ChunkFrames::Mixed> fs(this, derelativize(f)); |
| fs.next(map); |
| |
| if (!fs.is_done()) { |
| frame sender = fs.to_frame(); |
| assert(is_usable_in_chunk(sender.unextended_sp()), ""); |
| relativize_frame(sender); |
| |
| sender.set_frame_index(index+1); |
| return sender; |
| } |
| |
| if (parent() != nullptr) { |
| assert(!parent()->is_empty(), ""); |
| return parent()->top_frame(map); |
| } |
| |
| return Continuation::continuation_parent_frame(map); |
| } |
| |
| static int num_java_frames(CompiledMethod* cm, address pc) { |
| int count = 0; |
| for (ScopeDesc* scope = cm->scope_desc_at(pc); scope != nullptr; scope = scope->sender()) { |
| count++; |
| } |
| return count; |
| } |
| |
| static int num_java_frames(const StackChunkFrameStream<ChunkFrames::Mixed>& f) { |
| assert(f.is_interpreted() |
| || (f.cb() != nullptr && f.cb()->is_compiled() && f.cb()->as_compiled_method()->is_java_method()), ""); |
| return f.is_interpreted() ? 1 : num_java_frames(f.cb()->as_compiled_method(), f.orig_pc()); |
| } |
| |
| int stackChunkOopDesc::num_java_frames() const { |
| int n = 0; |
| for (StackChunkFrameStream<ChunkFrames::Mixed> f(const_cast<stackChunkOopDesc*>(this)); !f.is_done(); |
| f.next(SmallRegisterMap::instance)) { |
| if (!f.is_stub()) { |
| n += ::num_java_frames(f); |
| } |
| } |
| return n; |
| } |
| |
| template <stackChunkOopDesc::BarrierType barrier> |
| class DoBarriersStackClosure { |
| const stackChunkOop _chunk; |
| |
| public: |
| DoBarriersStackClosure(stackChunkOop chunk) : _chunk(chunk) {} |
| |
| template <ChunkFrames frame_kind, typename RegisterMapT> |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { |
| _chunk->do_barriers0<barrier>(f, map); |
| return true; |
| } |
| }; |
| |
| template <stackChunkOopDesc::BarrierType barrier> |
| void stackChunkOopDesc::do_barriers() { |
| DoBarriersStackClosure<barrier> closure(this); |
| iterate_stack(&closure); |
| } |
| |
| template void stackChunkOopDesc::do_barriers<stackChunkOopDesc::BarrierType::Load> (); |
| template void stackChunkOopDesc::do_barriers<stackChunkOopDesc::BarrierType::Store>(); |
| |
| class DerivedPointersSupport { |
| public: |
| static void relativize(derived_base* base_loc, derived_pointer* derived_loc) { |
| // The base oop could be stale from the GC's point-of-view. Treat it as an |
| // uintptr_t to stay clear of the oop verification code in oopsHierarcy.hpp. |
| uintptr_t base = *(uintptr_t*)base_loc; |
| if (base == 0) { |
| return; |
| } |
| assert(!UseCompressedOops || !CompressedOops::is_base((void*)base), ""); |
| |
| // This is always a full derived pointer |
| uintptr_t derived_int_val = *(uintptr_t*)derived_loc; |
| |
| // Make the pointer an offset (relativize) and store it at the same location |
| uintptr_t offset = derived_int_val - base; |
| *(uintptr_t*)derived_loc = offset; |
| } |
| |
| static void derelativize(derived_base* base_loc, derived_pointer* derived_loc) { |
| uintptr_t base = *(uintptr_t*)base_loc; |
| if (base == 0) { |
| return; |
| } |
| assert(!UseCompressedOops || !CompressedOops::is_base((void*)base), ""); |
| |
| // All derived pointers should have been relativized into offsets |
| uintptr_t offset = *(uintptr_t*)derived_loc; |
| |
| // Restore the original derived pointer |
| *(uintptr_t*)derived_loc = base + offset; |
| } |
| |
| struct RelativizeClosure : public DerivedOopClosure { |
| virtual void do_derived_oop(derived_base* base_loc, derived_pointer* derived_loc) override { |
| DerivedPointersSupport::relativize(base_loc, derived_loc); |
| } |
| }; |
| |
| struct DerelativizeClosure : public DerivedOopClosure { |
| virtual void do_derived_oop(derived_base* base_loc, derived_pointer* derived_loc) override { |
| DerivedPointersSupport::derelativize(base_loc, derived_loc); |
| } |
| }; |
| }; |
| |
| template <typename DerivedPointerClosureType> |
| class EncodeGCModeConcurrentFrameClosure { |
| stackChunkOop _chunk; |
| DerivedPointerClosureType* _cl; |
| |
| public: |
| EncodeGCModeConcurrentFrameClosure(stackChunkOop chunk, DerivedPointerClosureType* cl) |
| : _chunk(chunk), |
| _cl(cl) { |
| } |
| |
| template <ChunkFrames frame_kind, typename RegisterMapT> |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { |
| f.iterate_derived_pointers(_cl, map); |
| |
| BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); |
| frame fr = f.to_frame(); |
| FrameOopIterator<RegisterMapT> iterator(fr, map); |
| bs_chunk->encode_gc_mode(_chunk, &iterator); |
| |
| return true; |
| } |
| }; |
| |
| bool stackChunkOopDesc::try_acquire_relativization() { |
| for (;;) { |
| // We use an acquiring load when reading the flags to ensure that if we leave this |
| // function thinking that relativization is finished, we know that if another thread |
| // did the relativization, we will still be able to observe the relativized derived |
| // pointers, which is important as subsequent modifications of derived pointers must |
| // happen after relativization. |
| uint8_t flags_before = flags_acquire(); |
| if ((flags_before & FLAG_GC_MODE) != 0) { |
| // Terminal state - relativization is ensured |
| return false; |
| } |
| |
| if ((flags_before & FLAG_CLAIM_RELATIVIZE) != 0) { |
| // Someone else has claimed relativization - wait for completion |
| MonitorLocker ml(ContinuationRelativize_lock, Mutex::_no_safepoint_check_flag); |
| uint8_t flags_under_lock = flags_acquire(); |
| if ((flags_under_lock & FLAG_GC_MODE) != 0) { |
| // Terminal state - relativization is ensured |
| return false; |
| } |
| |
| if ((flags_under_lock & FLAG_NOTIFY_RELATIVIZE) != 0) { |
| // Relativization is claimed by another thread, and it knows it needs to notify |
| ml.wait(); |
| } else if (try_set_flags(flags_under_lock, flags_under_lock | FLAG_NOTIFY_RELATIVIZE)) { |
| // Relativization is claimed by another thread, and it knows it needs to notify |
| ml.wait(); |
| } |
| // Retry - rerun the loop |
| continue; |
| } |
| |
| if (try_set_flags(flags_before, flags_before | FLAG_CLAIM_RELATIVIZE)) { |
| // Claimed relativization - let's do it |
| return true; |
| } |
| } |
| } |
| |
| void stackChunkOopDesc::release_relativization() { |
| for (;;) { |
| uint8_t flags_before = flags(); |
| if ((flags_before & FLAG_NOTIFY_RELATIVIZE) != 0) { |
| MonitorLocker ml(ContinuationRelativize_lock, Mutex::_no_safepoint_check_flag); |
| // No need to CAS the terminal state; nobody else can be racingly mutating here |
| // as both claim and notify flags are already set (and monotonic) |
| // We do however need to use a releasing store on the flags, to ensure that |
| // the reader of that value (using load_acquire) will be able to observe |
| // the relativization of the derived pointers |
| uint8_t flags_under_lock = flags(); |
| release_set_flags(flags_under_lock | FLAG_GC_MODE); |
| ml.notify_all(); |
| return; |
| } |
| |
| if (try_set_flags(flags_before, flags_before | FLAG_GC_MODE)) { |
| // Successfully set the terminal state; we are done |
| return; |
| } |
| } |
| } |
| |
| void stackChunkOopDesc::relativize_derived_pointers_concurrently() { |
| if (!try_acquire_relativization()) { |
| // Already relativized |
| return; |
| } |
| |
| DerivedPointersSupport::RelativizeClosure derived_cl; |
| EncodeGCModeConcurrentFrameClosure<decltype(derived_cl)> frame_cl(this, &derived_cl); |
| iterate_stack(&frame_cl); |
| |
| release_relativization(); |
| } |
| |
| class TransformStackChunkClosure { |
| stackChunkOop _chunk; |
| |
| public: |
| TransformStackChunkClosure(stackChunkOop chunk) : _chunk(chunk) { } |
| |
| template <ChunkFrames frame_kind, typename RegisterMapT> |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { |
| DerivedPointersSupport::RelativizeClosure derived_cl; |
| f.iterate_derived_pointers(&derived_cl, map); |
| |
| BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); |
| frame fr = f.to_frame(); |
| FrameOopIterator<RegisterMapT> iterator(fr, map); |
| bs_chunk->encode_gc_mode(_chunk, &iterator); |
| |
| return true; |
| } |
| }; |
| |
| void stackChunkOopDesc::transform() { |
| assert(!is_gc_mode(), "Should only be called once per chunk"); |
| set_gc_mode(true); |
| |
| assert(!has_bitmap(), "Should only be set once"); |
| set_has_bitmap(true); |
| bitmap().clear(); |
| |
| TransformStackChunkClosure closure(this); |
| iterate_stack(&closure); |
| } |
| |
| template <stackChunkOopDesc::BarrierType barrier, bool compressedOopsWithBitmap> |
| class BarrierClosure: public OopClosure { |
| NOT_PRODUCT(intptr_t* _sp;) |
| |
| public: |
| BarrierClosure(intptr_t* sp) NOT_PRODUCT(: _sp(sp)) {} |
| |
| virtual void do_oop(oop* p) override { compressedOopsWithBitmap ? do_oop_work((narrowOop*)p) : do_oop_work(p); } |
| virtual void do_oop(narrowOop* p) override { do_oop_work(p); } |
| |
| template <class T> inline void do_oop_work(T* p) { |
| oop value = (oop)HeapAccess<>::oop_load(p); |
| if (barrier == stackChunkOopDesc::BarrierType::Store) { |
| HeapAccess<>::oop_store(p, value); |
| } |
| } |
| }; |
| |
| template <stackChunkOopDesc::BarrierType barrier, ChunkFrames frame_kind, typename RegisterMapT> |
| void stackChunkOopDesc::do_barriers0(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { |
| // We need to invoke the write barriers so as not to miss oops in old chunks that haven't yet been concurrently scanned |
| assert (!f.is_done(), ""); |
| |
| if (f.is_interpreted()) { |
| Method* m = f.to_frame().interpreter_frame_method(); |
| // Class redefinition support |
| m->record_gc_epoch(); |
| } else if (f.is_compiled()) { |
| nmethod* nm = f.cb()->as_nmethod(); |
| // The entry barrier takes care of having the right synchronization |
| // when keeping the nmethod alive during concurrent execution. |
| nm->run_nmethod_entry_barrier(); |
| // There is no need to mark the Method, as class redefinition will walk the |
| // CodeCache, noting their Methods |
| } |
| |
| if (has_bitmap() && UseCompressedOops) { |
| BarrierClosure<barrier, true> oops_closure(f.sp()); |
| f.iterate_oops(&oops_closure, map); |
| } else { |
| BarrierClosure<barrier, false> oops_closure(f.sp()); |
| f.iterate_oops(&oops_closure, map); |
| } |
| } |
| |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::Mixed>& f, const RegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::Mixed>& f, const RegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const RegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const RegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::Mixed>& f, const SmallRegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::Mixed>& f, const SmallRegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Load> (const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const SmallRegisterMap* map); |
| template void stackChunkOopDesc::do_barriers0<stackChunkOopDesc::BarrierType::Store>(const StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, const SmallRegisterMap* map); |
| |
| template <typename RegisterMapT> |
| void stackChunkOopDesc::fix_thawed_frame(const frame& f, const RegisterMapT* map) { |
| if (!(is_gc_mode() || requires_barriers())) { |
| return; |
| } |
| |
| BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); |
| FrameOopIterator<RegisterMapT> iterator(f, map); |
| bs_chunk->decode_gc_mode(this, &iterator); |
| |
| if (f.is_compiled_frame() && f.oop_map()->has_derived_oops()) { |
| DerivedPointersSupport::DerelativizeClosure derived_closure; |
| OopMapDo<OopClosure, DerivedPointersSupport::DerelativizeClosure, SkipNullValue> visitor(nullptr, &derived_closure); |
| visitor.oops_do(&f, map, f.oop_map()); |
| } |
| } |
| |
| template void stackChunkOopDesc::fix_thawed_frame(const frame& f, const RegisterMap* map); |
| template void stackChunkOopDesc::fix_thawed_frame(const frame& f, const SmallRegisterMap* map); |
| |
| void stackChunkOopDesc::print_on(bool verbose, outputStream* st) const { |
| if (*((juint*)this) == badHeapWordVal) { |
| st->print_cr("BAD WORD"); |
| } else if (*((juint*)this) == badMetaWordVal) { |
| st->print_cr("BAD META WORD"); |
| } else { |
| InstanceStackChunkKlass::print_chunk(const_cast<stackChunkOopDesc*>(this), verbose, st); |
| } |
| } |
| |
| #ifdef ASSERT |
| |
| class StackChunkVerifyOopsClosure : public OopClosure { |
| stackChunkOop _chunk; |
| int _count; |
| |
| public: |
| StackChunkVerifyOopsClosure(stackChunkOop chunk) |
| : _chunk(chunk), _count(0) {} |
| |
| void do_oop(oop* p) override { (_chunk->has_bitmap() && UseCompressedOops) ? do_oop_work((narrowOop*)p) : do_oop_work(p); } |
| void do_oop(narrowOop* p) override { do_oop_work(p); } |
| |
| template <typename T> inline void do_oop_work(T* p) { |
| _count++; |
| oop obj = _chunk->load_oop(p); |
| assert(obj == nullptr || dbg_is_good_oop(obj), "p: " PTR_FORMAT " obj: " PTR_FORMAT, p2i(p), p2i(obj)); |
| if (_chunk->has_bitmap()) { |
| BitMap::idx_t index = _chunk->bit_index_for(p); |
| assert(_chunk->bitmap().at(index), "Bit not set at index " SIZE_FORMAT " corresponding to " PTR_FORMAT, index, p2i(p)); |
| } |
| } |
| |
| int count() const { return _count; } |
| }; |
| |
| class VerifyStackChunkFrameClosure { |
| stackChunkOop _chunk; |
| |
| public: |
| intptr_t* _sp; |
| CodeBlob* _cb; |
| bool _callee_interpreted; |
| int _size; |
| int _argsize; |
| int _num_oops; |
| int _num_frames; |
| int _num_interpreted_frames; |
| int _num_i2c; |
| |
| VerifyStackChunkFrameClosure(stackChunkOop chunk, int num_frames, int size) |
| : _chunk(chunk), _sp(nullptr), _cb(nullptr), _callee_interpreted(false), |
| _size(size), _argsize(0), _num_oops(0), _num_frames(num_frames), _num_interpreted_frames(0), _num_i2c(0) {} |
| |
| template <ChunkFrames frame_kind, typename RegisterMapT> |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { |
| _sp = f.sp(); |
| _cb = f.cb(); |
| |
| int fsize = f.frame_size() - ((f.is_interpreted() == _callee_interpreted) ? _argsize : 0); |
| int num_oops = f.num_oops(); |
| assert(num_oops >= 0, ""); |
| |
| _argsize = f.stack_argsize() + frame::metadata_words_at_top; |
| _size += fsize; |
| _num_oops += num_oops; |
| if (f.is_interpreted()) { |
| _num_interpreted_frames++; |
| } |
| |
| log_develop_trace(continuations)("debug_verify_stack_chunk frame: %d sp: " INTPTR_FORMAT " pc: " PTR_FORMAT " interpreted: %d size: %d argsize: %d oops: %d", _num_frames, f.sp() - _chunk->start_address(), p2i(f.pc()), f.is_interpreted(), fsize, _argsize, num_oops); |
| LogTarget(Trace, continuations) lt; |
| if (lt.develop_is_enabled()) { |
| LogStream ls(lt); |
| f.print_on(&ls); |
| } |
| assert(f.pc() != nullptr, |
| "young: %d num_frames: %d sp: " PTR_FORMAT " start: " PTR_FORMAT " end: " PTR_FORMAT, |
| !_chunk->requires_barriers(), _num_frames, p2i(f.sp()), p2i(_chunk->start_address()), p2i(_chunk->bottom_address())); |
| |
| if (_num_frames == 0) { |
| assert(f.pc() == _chunk->pc(), ""); |
| } |
| |
| if (_num_frames > 0 && !_callee_interpreted && f.is_interpreted()) { |
| log_develop_trace(continuations)("debug_verify_stack_chunk i2c"); |
| _num_i2c++; |
| } |
| |
| StackChunkVerifyOopsClosure oops_closure(_chunk); |
| f.iterate_oops(&oops_closure, map); |
| assert(oops_closure.count() == num_oops, "oops: %d oopmap->num_oops(): %d", oops_closure.count(), num_oops); |
| |
| _callee_interpreted = f.is_interpreted(); |
| _num_frames++; |
| return true; |
| } |
| }; |
| |
| template <typename T> |
| class StackChunkVerifyBitmapClosure : public BitMapClosure { |
| stackChunkOop _chunk; |
| |
| public: |
| int _count; |
| |
| StackChunkVerifyBitmapClosure(stackChunkOop chunk) : _chunk(chunk), _count(0) {} |
| |
| bool do_bit(BitMap::idx_t index) override { |
| T* p = _chunk->address_for_bit<T>(index); |
| _count++; |
| |
| oop obj = _chunk->load_oop(p); |
| assert(obj == nullptr || dbg_is_good_oop(obj), |
| "p: " PTR_FORMAT " obj: " PTR_FORMAT " index: " SIZE_FORMAT, |
| p2i(p), p2i((oopDesc*)obj), index); |
| |
| return true; // continue processing |
| } |
| }; |
| |
| bool stackChunkOopDesc::verify(size_t* out_size, int* out_oops, int* out_frames, int* out_interpreted_frames) { |
| DEBUG_ONLY(if (!VerifyContinuations) return true;) |
| |
| assert(oopDesc::is_oop(this), ""); |
| |
| assert(stack_size() >= 0, ""); |
| assert(argsize() >= 0, ""); |
| assert(!has_bitmap() || is_gc_mode(), ""); |
| |
| if (is_empty()) { |
| assert(argsize() == 0, ""); |
| assert(max_thawing_size() == 0, ""); |
| } |
| |
| assert(oopDesc::is_oop_or_null(parent()), ""); |
| |
| const bool concurrent = !Thread::current()->is_Java_thread(); |
| |
| // If argsize == 0 and the chunk isn't mixed, the chunk contains the metadata (pc, fp -- frame::sender_sp_offset) |
| // for the top frame (below sp), and *not* for the bottom frame. |
| int size = stack_size() - argsize() - sp(); |
| assert(size >= 0, ""); |
| assert((size == 0) == is_empty(), ""); |
| |
| const StackChunkFrameStream<ChunkFrames::Mixed> first(this); |
| const bool has_safepoint_stub_frame = first.is_stub(); |
| |
| VerifyStackChunkFrameClosure closure(this, |
| has_safepoint_stub_frame ? 1 : 0, // Iterate_stack skips the safepoint stub |
| has_safepoint_stub_frame ? first.frame_size() : 0); |
| iterate_stack(&closure); |
| |
| assert(!is_empty() || closure._cb == nullptr, ""); |
| if (closure._cb != nullptr && closure._cb->is_compiled()) { |
| assert(argsize() == |
| (closure._cb->as_compiled_method()->method()->num_stack_arg_slots()*VMRegImpl::stack_slot_size) >>LogBytesPerWord, |
| "chunk argsize: %d bottom frame argsize: %d", argsize(), |
| (closure._cb->as_compiled_method()->method()->num_stack_arg_slots()*VMRegImpl::stack_slot_size) >>LogBytesPerWord); |
| } |
| |
| assert(closure._num_interpreted_frames == 0 || has_mixed_frames(), ""); |
| |
| if (!concurrent) { |
| assert(closure._size <= size + argsize() + frame::metadata_words, |
| "size: %d argsize: %d closure.size: %d end sp: " PTR_FORMAT " start sp: %d chunk size: %d", |
| size, argsize(), closure._size, closure._sp - start_address(), sp(), stack_size()); |
| assert(argsize() == closure._argsize - (closure._num_frames > 0 ? frame::metadata_words_at_top : 0), |
| "argsize(): %d closure.argsize: %d closure.callee_interpreted: %d", |
| argsize(), closure._argsize, closure._callee_interpreted); |
| |
| int calculated_max_size = closure._size |
| + closure._num_i2c * frame::align_wiggle |
| + closure._num_interpreted_frames * frame::align_wiggle; |
| assert(max_thawing_size() == calculated_max_size, |
| "max_size(): %d calculated_max_size: %d argsize: %d num_i2c: %d", |
| max_thawing_size(), calculated_max_size, closure._argsize, closure._num_i2c); |
| |
| if (out_size != nullptr) *out_size += size; |
| if (out_oops != nullptr) *out_oops += closure._num_oops; |
| if (out_frames != nullptr) *out_frames += closure._num_frames; |
| if (out_interpreted_frames != nullptr) *out_interpreted_frames += closure._num_interpreted_frames; |
| } else { |
| assert(out_size == nullptr, ""); |
| assert(out_oops == nullptr, ""); |
| assert(out_frames == nullptr, ""); |
| assert(out_interpreted_frames == nullptr, ""); |
| } |
| |
| if (has_bitmap()) { |
| assert(bitmap().size() == InstanceStackChunkKlass::bitmap_size_in_bits(stack_size()), |
| "bitmap().size(): %zu stack_size: %d", |
| bitmap().size(), stack_size()); |
| |
| int oop_count; |
| if (UseCompressedOops) { |
| StackChunkVerifyBitmapClosure<narrowOop> bitmap_closure(this); |
| bitmap().iterate(&bitmap_closure, |
| bit_index_for((narrowOop*)(sp_address() - frame::metadata_words_at_bottom)), |
| bit_index_for((narrowOop*)end_address())); |
| oop_count = bitmap_closure._count; |
| } else { |
| StackChunkVerifyBitmapClosure<oop> bitmap_closure(this); |
| bitmap().iterate(&bitmap_closure, |
| bit_index_for((oop*)(sp_address() - frame::metadata_words_at_bottom)), |
| bit_index_for((oop*)end_address())); |
| oop_count = bitmap_closure._count; |
| } |
| assert(oop_count == closure._num_oops, |
| "bitmap_closure._count: %d closure._num_oops: %d", oop_count, closure._num_oops); |
| } |
| |
| return true; |
| } |
| #endif |