| /* | |
| * Copyright (c) 2019, 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 "cds/cdsConfig.hpp" | |
| #include "cds/serializeClosure.hpp" | |
| #include "classfile/vmClasses.hpp" | |
| #include "compiler/oopMap.inline.hpp" | |
| #include "gc/shared/gc_globals.hpp" | |
| #include "memory/oopFactory.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "oops/instanceStackChunkKlass.inline.hpp" | |
| #include "oops/stackChunkOop.inline.hpp" | |
| #include "runtime/continuation.hpp" | |
| #include "runtime/continuationJavaClasses.inline.hpp" | |
| #include "runtime/frame.hpp" | |
| #include "runtime/handles.hpp" | |
| #include "runtime/registerMap.hpp" | |
| #include "runtime/smallRegisterMap.inline.hpp" | |
| #include "runtime/stackChunkFrameStream.inline.hpp" | |
| #include "utilities/devirtualizer.inline.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| #include "utilities/macros.hpp" | |
| #include "utilities/ostream.hpp" | |
| int InstanceStackChunkKlass::_offset_of_stack = 0; | |
| #if INCLUDE_CDS | |
| void InstanceStackChunkKlass::serialize_offsets(SerializeClosure* f) { | |
| f->do_int(&_offset_of_stack); | |
| } | |
| #endif | |
| InstanceStackChunkKlass::InstanceStackChunkKlass() { | |
| assert(CDSConfig::is_dumping_static_archive() || CDSConfig::is_using_archive(), "only for CDS"); | |
| } | |
| InstanceStackChunkKlass::InstanceStackChunkKlass(const ClassFileParser& parser) | |
| : InstanceKlass(parser, Kind) { | |
| // Change the layout_helper to use the slow path because StackChunkOops are | |
| // variable sized InstanceOops. | |
| const jint lh = Klass::instance_layout_helper(size_helper(), true); | |
| set_layout_helper(lh); | |
| } | |
| size_t InstanceStackChunkKlass::oop_size(oop obj) const { | |
| return instance_size(jdk_internal_vm_StackChunk::size(obj)); | |
| } | |
| #ifndef PRODUCT | |
| void InstanceStackChunkKlass::oop_print_on(oop obj, outputStream* st) { | |
| print_chunk(stackChunkOopDesc::cast(obj), false, st); | |
| } | |
| #endif | |
| template<typename OopClosureType> | |
| class StackChunkOopIterateFilterClosure: public OopClosure { | |
| private: | |
| OopClosureType* const _closure; | |
| MemRegion _bound; | |
| public: | |
| StackChunkOopIterateFilterClosure(OopClosureType* closure, MemRegion bound) | |
| : _closure(closure), | |
| _bound(bound) {} | |
| virtual void do_oop(oop* p) override { do_oop_work(p); } | |
| virtual void do_oop(narrowOop* p) override { do_oop_work(p); } | |
| template <typename T> | |
| void do_oop_work(T* p) { | |
| if (_bound.contains(p)) { | |
| Devirtualizer::do_oop(_closure, p); | |
| } | |
| } | |
| }; | |
| class DoMethodsStackChunkFrameClosure { | |
| OopIterateClosure* _closure; | |
| public: | |
| DoMethodsStackChunkFrameClosure(OopIterateClosure* cl) : _closure(cl) {} | |
| template <ChunkFrames frame_kind, typename RegisterMapT> | |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { | |
| if (f.is_interpreted()) { | |
| Method* m = f.to_frame().interpreter_frame_method(); | |
| _closure->do_method(m); | |
| } else if (f.is_compiled()) { | |
| nmethod* nm = f.cb()->as_nmethod(); | |
| // The do_nmethod function takes care of having the right synchronization | |
| // when keeping the nmethod alive during concurrent execution. | |
| _closure->do_nmethod(nm); | |
| // There is no need to mark the Method, as class redefinition will walk the | |
| // CodeCache, noting their Methods | |
| } | |
| return true; | |
| } | |
| }; | |
| void InstanceStackChunkKlass::do_methods(stackChunkOop chunk, OopIterateClosure* cl) { | |
| DoMethodsStackChunkFrameClosure closure(cl); | |
| chunk->iterate_stack(&closure); | |
| } | |
| class OopIterateStackChunkFrameClosure { | |
| OopIterateClosure* const _closure; | |
| MemRegion _bound; | |
| const bool _do_metadata; | |
| public: | |
| OopIterateStackChunkFrameClosure(OopIterateClosure* closure, MemRegion mr) | |
| : _closure(closure), | |
| _bound(mr), | |
| _do_metadata(_closure->do_metadata()) { | |
| assert(_closure != nullptr, "must be set"); | |
| } | |
| template <ChunkFrames frame_kind, typename RegisterMapT> | |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { | |
| if (_do_metadata) { | |
| DoMethodsStackChunkFrameClosure(_closure).do_frame(f, map); | |
| } | |
| StackChunkOopIterateFilterClosure<OopIterateClosure> cl(_closure, _bound); | |
| f.iterate_oops(&cl, map); | |
| return true; | |
| } | |
| }; | |
| void InstanceStackChunkKlass::oop_oop_iterate_stack_slow(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr) { | |
| if (UseZGC || UseShenandoahGC) { | |
| // An OopClosure could apply barriers to a stack chunk. The side effects | |
| // of the load barriers could destroy derived pointers, which must be | |
| // processed before their base oop is processed. So we force processing | |
| // of derived pointers before applying the closures. | |
| chunk->relativize_derived_pointers_concurrently(); | |
| } | |
| OopIterateStackChunkFrameClosure frame_closure(closure, mr); | |
| chunk->iterate_stack(&frame_closure); | |
| } | |
| template <typename OopT> | |
| void InstanceStackChunkKlass::oop_oop_iterate_lockstack(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr) { | |
| if (LockingMode != LM_LIGHTWEIGHT) { | |
| return; | |
| } | |
| StackChunkOopIterateFilterClosure<OopIterateClosure> cl(closure, mr); | |
| if (chunk->has_bitmap()) { | |
| chunk->iterate_lockstack<OopT>(&cl); | |
| } else { | |
| chunk->iterate_lockstack<oop>(&cl); | |
| } | |
| } | |
| template void InstanceStackChunkKlass::oop_oop_iterate_lockstack<oop>(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr); | |
| template void InstanceStackChunkKlass::oop_oop_iterate_lockstack<narrowOop>(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr); | |
| #ifdef ASSERT | |
| class DescribeStackChunkClosure { | |
| stackChunkOop _chunk; | |
| FrameValues _values; | |
| RegisterMap _map; | |
| int _frame_no; | |
| public: | |
| DescribeStackChunkClosure(stackChunkOop chunk) | |
| : _chunk(chunk), | |
| _map(nullptr, | |
| RegisterMap::UpdateMap::include, | |
| RegisterMap::ProcessFrames::skip, | |
| RegisterMap::WalkContinuation::include), | |
| _frame_no(0) { | |
| _map.set_include_argument_oops(false); | |
| } | |
| const RegisterMap* get_map(const RegisterMap* map, intptr_t* sp) { return map; } | |
| const RegisterMap* get_map(const SmallRegisterMap* map, intptr_t* sp) { return map->copy_to_RegisterMap(&_map, sp); } | |
| template <ChunkFrames frame_kind, typename RegisterMapT> | |
| bool do_frame(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { | |
| ResetNoHandleMark rnhm; | |
| HandleMark hm(Thread::current()); | |
| frame fr = f.to_frame(); | |
| fr.describe(_values, _frame_no++, get_map(map, f.sp())); | |
| return true; | |
| } | |
| void describe_chunk() { | |
| // _values.describe(-1, _chunk->start_address(), "CHUNK START"); | |
| _values.describe(-1, _chunk->sp_address(), "CHUNK SP"); | |
| _values.describe(-1, _chunk->bottom_address() - 1, "CHUNK ARGS"); | |
| _values.describe(-1, _chunk->end_address() - 1, "CHUNK END"); | |
| } | |
| void print_on(outputStream* out) { | |
| if (_frame_no > 0) { | |
| describe_chunk(); | |
| _values.print_on(_chunk, out); | |
| } else { | |
| out->print_cr(" EMPTY"); | |
| } | |
| } | |
| }; | |
| #endif | |
| class PrintStackChunkClosure { | |
| outputStream* _st; | |
| public: | |
| PrintStackChunkClosure(outputStream* st) : _st(st) {} | |
| template <ChunkFrames frame_kind, typename RegisterMapT> | |
| bool do_frame(const StackChunkFrameStream<frame_kind>& fs, const RegisterMapT* map) { | |
| frame f = fs.to_frame(); | |
| _st->print_cr("-- frame sp: " PTR_FORMAT " interpreted: %d size: %d argsize: %d", | |
| p2i(fs.sp()), fs.is_interpreted(), f.frame_size(), | |
| fs.is_interpreted() || fs.is_stub() ? 0 : f.compiled_frame_stack_argsize()); | |
| #ifdef ASSERT | |
| f.print_value_on(_st); | |
| #else | |
| f.print_on(_st); | |
| #endif | |
| const ImmutableOopMap* oopmap = fs.oopmap(); | |
| if (oopmap != nullptr) { | |
| oopmap->print_on(_st); | |
| _st->cr(); | |
| } | |
| return true; | |
| } | |
| }; | |
| void InstanceStackChunkKlass::print_chunk(const stackChunkOop c, bool verbose, outputStream* st) { | |
| if (c == nullptr) { | |
| st->print_cr("CHUNK null"); | |
| return; | |
| } | |
| st->print_cr("CHUNK " PTR_FORMAT " " PTR_FORMAT " - " PTR_FORMAT " :: " INTPTR_FORMAT, | |
| p2i(c), p2i(c->start_address()), p2i(c->end_address()), c->identity_hash()); | |
| st->print_cr(" barriers: %d gc_mode: %d bitmap: %d parent: " PTR_FORMAT, | |
| c->requires_barriers(), c->is_gc_mode(), c->has_bitmap(), p2i(c->parent())); | |
| st->print_cr(" flags mixed: %d", c->has_mixed_frames()); | |
| st->print_cr(" size: %d bottom: %d max_size: %d sp: %d pc: " PTR_FORMAT, | |
| c->stack_size(), c->bottom(), c->max_thawing_size(), c->sp(), p2i(c->pc())); | |
| if (verbose) { | |
| st->cr(); | |
| st->print_cr("------ chunk frames end: " PTR_FORMAT, p2i(c->bottom_address())); | |
| PrintStackChunkClosure closure(st); | |
| c->iterate_stack(&closure); | |
| st->print_cr("------"); | |
| #ifdef ASSERT | |
| ResourceMark rm; | |
| DescribeStackChunkClosure describe(c); | |
| c->iterate_stack(&describe); | |
| describe.print_on(st); | |
| st->print_cr("======"); | |
| #endif | |
| } | |
| } | |
| void InstanceStackChunkKlass::init_offset_of_stack() { | |
| // Cache the offset of the static fields in the Class instance | |
| assert(_offset_of_stack == 0, "once"); | |
| _offset_of_stack = cast(vmClasses::StackChunk_klass())->size_helper() << LogHeapWordSize; | |
| } |