228 lines
8.6 KiB
C++
228 lines
8.6 KiB
C++
/* 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/globals.h"
|
|
#include "base/macros.h"
|
|
#include "base/mutex.h"
|
|
#include "gc/system_weak.h"
|
|
#include "gc_root-inl.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::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
|
|
REQUIRES_SHARED(art::Locks::mutator_lock_)
|
|
REQUIRES(!allow_disallow_lock_);
|
|
ALWAYS_INLINE bool RemoveLocked(art::ObjPtr<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::ObjPtr<art::mirror::Object> obj, T tag)
|
|
REQUIRES_SHARED(art::Locks::mutator_lock_)
|
|
REQUIRES(!allow_disallow_lock_);
|
|
ALWAYS_INLINE virtual bool SetLocked(art::ObjPtr<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::ObjPtr<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::ObjPtr<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::ObjPtr<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::ObjPtr<art::mirror::Object> obj, T tag)
|
|
REQUIRES_SHARED(art::Locks::mutator_lock_)
|
|
REQUIRES(allow_disallow_lock_);
|
|
|
|
ALWAYS_INLINE
|
|
bool RemoveLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
|
|
REQUIRES_SHARED(art::Locks::mutator_lock_)
|
|
REQUIRES(allow_disallow_lock_);
|
|
|
|
bool GetTagLocked(art::Thread* self, art::ObjPtr<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::ObjPtr<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_
|