/* Copyright (C) 2017 The Android Open Source Project * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This file implements interfaces from the file jvmti.h. This implementation * is licensed under the same terms as the file jvmti.h. The * copyright and license information for the file jvmti.h follows. * * Copyright (c) 2003, 2011, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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. */ #ifndef ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_ #define ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_ #include <unordered_map> #include "base/macros.h" #include "base/mutex.h" #include "gc/system_weak.h" #include "gc_root-inl.h" #include "globals.h" #include "jvmti.h" #include "jvmti_allocator.h" #include "mirror/object.h" #include "thread-current-inl.h" namespace openjdkjvmti { class EventHandler; // A system-weak container mapping objects to elements of the template type. This corresponds // to a weak hash map. For historical reasons the stored value is called "tag." template <typename T> class JvmtiWeakTable : public art::gc::SystemWeakHolder { public: JvmtiWeakTable() : art::gc::SystemWeakHolder(art::kTaggingLockLevel), update_since_last_sweep_(false) { } // Remove the mapping for the given object, returning whether such a mapping existed (and the old // value). ALWAYS_INLINE bool Remove(art::mirror::Object* obj, /* out */ T* tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); ALWAYS_INLINE bool RemoveLocked(art::mirror::Object* obj, /* out */ T* tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); // Set the mapping for the given object. Returns true if this overwrites an already existing // mapping. ALWAYS_INLINE virtual bool Set(art::mirror::Object* obj, T tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); ALWAYS_INLINE virtual bool SetLocked(art::mirror::Object* obj, T tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); // Return the value associated with the given object. Returns true if the mapping exists, false // otherwise. bool GetTag(art::mirror::Object* obj, /* out */ T* result) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_) { art::Thread* self = art::Thread::Current(); art::MutexLock mu(self, allow_disallow_lock_); Wait(self); return GetTagLocked(self, obj, result); } bool GetTagLocked(art::mirror::Object* obj, /* out */ T* result) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_) { art::Thread* self = art::Thread::Current(); allow_disallow_lock_.AssertHeld(self); Wait(self); return GetTagLocked(self, obj, result); } // Sweep the container. DO NOT CALL MANUALLY. ALWAYS_INLINE void Sweep(art::IsMarkedVisitor* visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); // Return all objects that have a value mapping in tags. ALWAYS_INLINE jvmtiError GetTaggedObjects(jvmtiEnv* jvmti_env, jint tag_count, const T* tags, /* out */ jint* count_ptr, /* out */ jobject** object_result_ptr, /* out */ T** tag_result_ptr) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); // Locking functions, to allow coarse-grained locking and amortization. ALWAYS_INLINE void Lock() ACQUIRE(allow_disallow_lock_); ALWAYS_INLINE void Unlock() RELEASE(allow_disallow_lock_); ALWAYS_INLINE void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_); ALWAYS_INLINE art::mirror::Object* Find(T tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); protected: // Should HandleNullSweep be called when Sweep detects the release of an object? virtual bool DoesHandleNullOnSweep() { return false; } // If DoesHandleNullOnSweep returns true, this function will be called. virtual void HandleNullSweep(T tag ATTRIBUTE_UNUSED) {} private: ALWAYS_INLINE bool SetLocked(art::Thread* self, art::mirror::Object* obj, T tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); ALWAYS_INLINE bool RemoveLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* tag) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); bool GetTagLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* result) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_) { auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); if (it != tagged_objects_.end()) { *result = it->second; return true; } // Performance optimization: To avoid multiple table updates, ensure that during GC we // only update once. See the comment on the implementation of GetTagSlowPath. if (art::kUseReadBarrier && self != nullptr && self->GetIsGcMarking() && !update_since_last_sweep_) { return GetTagSlowPath(self, obj, result); } return false; } // Slow-path for GetTag. We didn't find the object, but we might be storing from-pointers and // are asked to retrieve with a to-pointer. ALWAYS_INLINE bool GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, /* out */ T* result) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); // Update the table by doing read barriers on each element, ensuring that to-space pointers // are stored. ALWAYS_INLINE void UpdateTableWithReadBarrier() REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); template <bool kHandleNull> void SweepImpl(art::IsMarkedVisitor* visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); enum TableUpdateNullTarget { kIgnoreNull, kRemoveNull, kCallHandleNull }; template <typename Updater, TableUpdateNullTarget kTargetNull> void UpdateTableWith(Updater& updater) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); template <typename Storage, class Allocator = JvmtiAllocator<T>> struct ReleasableContainer; struct HashGcRoot { size_t operator()(const art::GcRoot<art::mirror::Object>& r) const REQUIRES_SHARED(art::Locks::mutator_lock_) { return reinterpret_cast<uintptr_t>(r.Read<art::kWithoutReadBarrier>()); } }; struct EqGcRoot { bool operator()(const art::GcRoot<art::mirror::Object>& r1, const art::GcRoot<art::mirror::Object>& r2) const REQUIRES_SHARED(art::Locks::mutator_lock_) { return r1.Read<art::kWithoutReadBarrier>() == r2.Read<art::kWithoutReadBarrier>(); } }; using TagAllocator = JvmtiAllocator<std::pair<const art::GcRoot<art::mirror::Object>, T>>; std::unordered_map<art::GcRoot<art::mirror::Object>, T, HashGcRoot, EqGcRoot, TagAllocator> tagged_objects_ GUARDED_BY(allow_disallow_lock_) GUARDED_BY(art::Locks::mutator_lock_); // To avoid repeatedly scanning the whole table, remember if we did that since the last sweep. bool update_since_last_sweep_; }; } // namespace openjdkjvmti #endif // ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_