ART: Support object tagging
Add support for tagging in the JVMTI plugin.
Bug: 31385027
Test: m test-art-host
Change-Id: I4d8fb12cd23ca60dc0b0ce9051d1c77e5eb18aa9
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index a1a2361..bebc2fc 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -38,7 +38,11 @@
#include "art_jvmti.h"
#include "jni_env_ext-inl.h"
+#include "object_tagging.h"
+#include "obj_ptr-inl.h"
#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread_list.h"
#include "transform.h"
// TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton
@@ -47,6 +51,8 @@
namespace openjdkjvmti {
+ObjectTagTable gObjectTagTable;
+
class JvmtiFunctions {
private:
static bool IsValidEnv(jvmtiEnv* env) {
@@ -270,11 +276,42 @@
}
static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ if (object == nullptr || tag_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ JNIEnv* jni_env = GetJniEnv(env);
+ if (jni_env == nullptr) {
+ return ERR(INTERNAL);
+ }
+
+ art::ScopedObjectAccess soa(jni_env);
+ art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object);
+ if (!gObjectTagTable.GetTag(obj.Ptr(), tag_ptr)) {
+ *tag_ptr = 0;
+ }
+
+ return ERR(NONE);
}
static jvmtiError SetTag(jvmtiEnv* env, jobject object, jlong tag) {
- return ERR(NOT_IMPLEMENTED);
+ if (object == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ JNIEnv* jni_env = GetJniEnv(env);
+ if (jni_env == nullptr) {
+ return ERR(INTERNAL);
+ }
+
+ art::ScopedObjectAccess soa(jni_env);
+ art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object);
+ gObjectTagTable.Remove(obj.Ptr(), /* tag* */ nullptr);
+ if (tag != 0) {
+ gObjectTagTable.Add(obj.Ptr(), tag);
+ }
+
+ return ERR(NONE);
}
static jvmtiError GetObjectsWithTags(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
new file mode 100644
index 0000000..e276c52
--- /dev/null
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
+#define ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
+
+#include "base/mutex.h"
+#include "gc/system_weak.h"
+#include "gc_root-inl.h"
+#include "mirror/object.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+class ObjectTagTable : public art::gc::SystemWeakHolder {
+ public:
+ ObjectTagTable() : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock) {
+ }
+
+ void Add(art::mirror::Object* obj, jlong tag)
+ 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);
+
+ if (first_free_ == tagged_objects_.size()) {
+ tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag));
+ first_free_++;
+ } else {
+ DCHECK_LT(first_free_, tagged_objects_.size());
+ DCHECK(tagged_objects_[first_free_].first.IsNull());
+ tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag);
+ // TODO: scan for free elements.
+ first_free_ = tagged_objects_.size();
+ }
+ }
+
+ bool GetTag(art::mirror::Object* obj, jlong* 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);
+
+ for (const auto& pair : tagged_objects_) {
+ if (pair.first.Read(nullptr) == obj) {
+ *result = pair.second;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool Remove(art::mirror::Object* obj, jlong* tag)
+ 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);
+
+ for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+ if (it->first.Read(nullptr) == obj) {
+ if (tag != nullptr) {
+ *tag = it->second;
+ }
+ it->first = art::GcRoot<art::mirror::Object>(nullptr);
+
+ size_t index = it - tagged_objects_.begin();
+ if (index < first_free_) {
+ first_free_ = index;
+ }
+
+ // TODO: compaction.
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void Sweep(art::IsMarkedVisitor* visitor)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(!allow_disallow_lock_) {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+
+ for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+ if (!it->first.IsNull()) {
+ art::mirror::Object* original_obj = it->first.Read();
+ art::mirror::Object* target_obj = visitor->IsMarked(original_obj);
+ if (original_obj != target_obj) {
+ it->first = art::GcRoot<art::mirror::Object>(target_obj);
+
+ if (target_obj == nullptr) {
+ HandleNullSweep(it->second);
+ }
+ }
+ } else {
+ size_t index = it - tagged_objects_.begin();
+ if (index < first_free_) {
+ first_free_ = index;
+ }
+ }
+ }
+ }
+
+ private:
+ using Entry = std::pair<art::GcRoot<art::mirror::Object>, jlong>;
+
+ void HandleNullSweep(jlong tag ATTRIBUTE_UNUSED) {
+ // TODO: Handle deallocation reporting here. We'll have to enqueue tags temporarily, as we
+ // probably shouldn't call the callbacks directly (to avoid any issues).
+ }
+
+ std::vector<Entry> tagged_objects_ GUARDED_BY(allow_disallow_lock_);
+ size_t first_free_ = 0;
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_