| /* |
| * Copyright (c) 2014, 2019, 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 "aot/aotLoader.hpp" |
| #include "classfile/classLoaderDataGraph.hpp" |
| #include "classfile/stringTable.hpp" |
| #include "gc/shared/strongRootsScope.hpp" |
| #include "jfr/leakprofiler/utilities/unifiedOop.hpp" |
| #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" |
| #include "jfr/utilities/jfrThreadIterator.hpp" |
| #include "memory/iterator.hpp" |
| #include "memory/universe.hpp" |
| #include "oops/klass.hpp" |
| #include "oops/oop.hpp" |
| #include "prims/jvmtiThreadState.hpp" |
| #include "runtime/frame.inline.hpp" |
| #include "runtime/mutexLocker.hpp" |
| #include "runtime/vframe_hp.hpp" |
| #include "services/management.hpp" |
| #include "utilities/growableArray.hpp" |
| |
| class ReferenceLocateClosure : public OopClosure { |
| protected: |
| RootCallback& _callback; |
| RootCallbackInfo _info; |
| bool _complete; |
| |
| void do_oop_shared(const void* ref); |
| |
| public: |
| ReferenceLocateClosure(RootCallback& callback, |
| OldObjectRoot::System system, |
| OldObjectRoot::Type type, |
| const void* context) : _callback(callback), |
| _info(), |
| _complete(false) { |
| _info._high = NULL; |
| _info._low = NULL; |
| _info._system = system; |
| _info._type = type; |
| _info._context = context; |
| } |
| |
| virtual void do_oop(oop* ref); |
| virtual void do_oop(narrowOop* ref); |
| |
| bool complete() const { |
| return _complete; |
| } |
| }; |
| |
| void ReferenceLocateClosure::do_oop_shared(const void* ref) { |
| assert(ref != NULL, "invariant"); |
| if (!_complete) { |
| _info._high = ref; |
| _complete = _callback.process(_info); |
| } |
| } |
| |
| void ReferenceLocateClosure::do_oop(oop* ref) { |
| do_oop_shared(ref); |
| } |
| |
| void ReferenceLocateClosure::do_oop(narrowOop* ref) { |
| do_oop_shared(ref); |
| } |
| |
| class ReferenceToRootClosure : public StackObj { |
| private: |
| RootCallback& _callback; |
| RootCallbackInfo _info; |
| bool _complete; |
| |
| bool do_cldg_roots(); |
| bool do_object_synchronizer_roots(); |
| bool do_universe_roots(); |
| bool do_jni_handle_roots(); |
| bool do_jvmti_roots(); |
| bool do_system_dictionary_roots(); |
| bool do_management_roots(); |
| bool do_string_table_roots(); |
| bool do_aot_loader_roots(); |
| |
| bool do_roots(); |
| |
| public: |
| ReferenceToRootClosure(RootCallback& callback) : _callback(callback), |
| _info(), |
| _complete(false) { |
| _info._high = NULL; |
| _info._low = NULL; |
| _info._context = NULL; |
| _info._system = OldObjectRoot::_system_undetermined; |
| _info._type = OldObjectRoot::_type_undetermined; |
| |
| assert_locked_or_safepoint(Threads_lock); |
| do_roots(); |
| } |
| |
| bool complete() const { |
| return _complete; |
| } |
| }; |
| |
| bool ReferenceToRootClosure::do_cldg_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_class_loader_data, OldObjectRoot::_type_undetermined, NULL); |
| CLDToOopClosure cldt_closure(&rlc, ClassLoaderData::_claim_none); |
| ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_object_synchronizer_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_object_synchronizer, OldObjectRoot::_type_undetermined, NULL); |
| ObjectSynchronizer::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_universe_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_universe, OldObjectRoot::_type_undetermined, NULL); |
| Universe::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_jni_handle_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_global_jni_handles, OldObjectRoot::_global_jni_handle, NULL); |
| JNIHandles::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_jvmti_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_jvmti, OldObjectRoot::_global_jni_handle, NULL); |
| JvmtiExport::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_system_dictionary_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_system_dictionary, OldObjectRoot::_type_undetermined, NULL); |
| SystemDictionary::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_management_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_management, OldObjectRoot::_type_undetermined, NULL); |
| Management::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_string_table_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rlc(_callback, OldObjectRoot::_string_table, OldObjectRoot::_type_undetermined, NULL); |
| StringTable::oops_do(&rlc); |
| return rlc.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_aot_loader_roots() { |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rcl(_callback, OldObjectRoot::_aot, OldObjectRoot::_type_undetermined, NULL); |
| AOTLoader::oops_do(&rcl); |
| return rcl.complete(); |
| } |
| |
| bool ReferenceToRootClosure::do_roots() { |
| assert(!complete(), "invariant"); |
| assert(OldObjectRoot::_system_undetermined == _info._system, "invariant"); |
| assert(OldObjectRoot::_type_undetermined == _info._type, "invariant"); |
| |
| if (do_cldg_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_object_synchronizer_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_universe_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_jni_handle_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_jvmti_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_system_dictionary_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_management_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_string_table_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_aot_loader_roots()) { |
| _complete = true; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| class ReferenceToThreadRootClosure : public StackObj { |
| private: |
| RootCallback& _callback; |
| bool _complete; |
| |
| bool do_java_threads_oops(JavaThread* jt); |
| bool do_thread_roots(JavaThread* jt); |
| bool do_thread_stack_fast(JavaThread* jt); |
| bool do_thread_stack_detailed(JavaThread* jt); |
| bool do_thread_jni_handles(JavaThread* jt); |
| bool do_thread_handle_area(JavaThread* jt); |
| |
| public: |
| ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) { |
| assert_locked_or_safepoint(Threads_lock); |
| JfrJavaThreadIterator iter; |
| while (iter.has_next()) { |
| if (do_thread_roots(iter.next())) { |
| return; |
| } |
| } |
| } |
| |
| bool complete() const { |
| return _complete; |
| } |
| }; |
| |
| bool ReferenceToThreadRootClosure::do_thread_handle_area(JavaThread* jt) { |
| assert(jt != NULL, "invariant"); |
| assert(!complete(), "invariant"); |
| ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_handle_area, jt); |
| jt->handle_area()->oops_do(&rcl); |
| return rcl.complete(); |
| } |
| |
| bool ReferenceToThreadRootClosure::do_thread_jni_handles(JavaThread* jt) { |
| assert(jt != NULL, "invariant"); |
| assert(!complete(), "invariant"); |
| |
| ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_local_jni_handle, jt); |
| jt->active_handles()->oops_do(&rcl); |
| return rcl.complete(); |
| } |
| |
| bool ReferenceToThreadRootClosure::do_thread_stack_fast(JavaThread* jt) { |
| assert(jt != NULL, "invariant"); |
| assert(!complete(), "invariant"); |
| |
| if (_callback.entries() == 0) { |
| _complete = true; |
| return true; |
| } |
| |
| RootCallbackInfo info; |
| info._high = NULL; |
| info._low = NULL; |
| info._context = jt; |
| info._system = OldObjectRoot::_threads; |
| info._type = OldObjectRoot::_stack_variable; |
| |
| for (int i = 0; i < _callback.entries(); ++i) { |
| const address adr = (address)_callback.at(i); |
| if (jt->is_in_usable_stack(adr)) { |
| info._high = adr; |
| _complete = _callback.process(info); |
| if (_complete) { |
| return true; |
| } |
| } |
| } |
| assert(!complete(), "invariant"); |
| return false; |
| } |
| |
| bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { |
| assert(jt != NULL, "invariant"); |
| assert(!complete(), "invariant"); |
| |
| ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_stack_variable, jt); |
| |
| if (jt->has_last_Java_frame()) { |
| // traverse the registered growable array gc_array |
| // can't do this as it is not reachable from outside |
| |
| // Traverse the monitor chunks |
| MonitorChunk* chunk = jt->monitor_chunks(); |
| for (; chunk != NULL; chunk = chunk->next()) { |
| chunk->oops_do(&rcl); |
| } |
| |
| if (rcl.complete()) { |
| return true; |
| } |
| |
| // Traverse the execution stack |
| for (StackFrameStream fst(jt); !fst.is_done(); fst.next()) { |
| fst.current()->oops_do(&rcl, NULL, fst.register_map()); |
| } |
| |
| } // last java frame |
| |
| if (rcl.complete()) { |
| return true; |
| } |
| |
| GrowableArray<jvmtiDeferredLocalVariableSet*>* const list = jt->deferred_locals(); |
| if (list != NULL) { |
| for (int i = 0; i < list->length(); i++) { |
| list->at(i)->oops_do(&rcl); |
| } |
| } |
| |
| if (rcl.complete()) { |
| return true; |
| } |
| |
| // Traverse instance variables at the end since the GC may be moving things |
| // around using this function |
| /* |
| * // can't reach these oop* from the outside |
| f->do_oop((oop*) &_threadObj); |
| f->do_oop((oop*) &_vm_result); |
| f->do_oop((oop*) &_exception_oop); |
| f->do_oop((oop*) &_pending_async_exception); |
| */ |
| |
| JvmtiThreadState* const jvmti_thread_state = jt->jvmti_thread_state(); |
| if (jvmti_thread_state != NULL) { |
| jvmti_thread_state->oops_do(&rcl); |
| } |
| |
| return rcl.complete(); |
| } |
| |
| bool ReferenceToThreadRootClosure::do_java_threads_oops(JavaThread* jt) { |
| assert(jt != NULL, "invariant"); |
| assert(!complete(), "invariant"); |
| |
| ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_global_jni_handle, jt); |
| jt->oops_do(&rcl, NULL); |
| return rcl.complete(); |
| } |
| |
| bool ReferenceToThreadRootClosure::do_thread_roots(JavaThread* jt) { |
| assert(jt != NULL, "invariant"); |
| |
| if (do_thread_stack_fast(jt)) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_thread_jni_handles(jt)) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_thread_handle_area(jt)) { |
| _complete = true; |
| return true; |
| } |
| |
| if (do_thread_stack_detailed(jt)) { |
| _complete = true; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| class RootResolverMarkScope : public MarkScope { |
| }; |
| |
| void RootResolver::resolve(RootCallback& callback) { |
| RootResolverMarkScope mark_scope; |
| |
| // thread local roots |
| ReferenceToThreadRootClosure rtrc(callback); |
| if (rtrc.complete()) { |
| return; |
| } |
| // system global roots |
| ReferenceToRootClosure rrc(callback); |
| } |