diff options
| -rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 13 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/jvmti_allocator.h | 170 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/object_tagging.cc | 139 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/object_tagging.h | 10 | ||||
| -rw-r--r-- | test/903-hello-tagging/expected.txt | 10 | ||||
| -rw-r--r-- | test/903-hello-tagging/src/Main.java | 97 | ||||
| -rw-r--r-- | test/903-hello-tagging/tagging.cc | 83 |
7 files changed, 520 insertions, 2 deletions
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 7292946999..b91acb691c 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -323,7 +323,18 @@ class JvmtiFunctions { jint* count_ptr, jobject** object_result_ptr, jlong** tag_result_ptr) { - return ERR(NOT_IMPLEMENTED); + JNIEnv* jni_env = GetJniEnv(env); + if (jni_env == nullptr) { + return ERR(INTERNAL); + } + + art::ScopedObjectAccess soa(jni_env); + return gObjectTagTable.GetTaggedObjects(env, + tag_count, + tags, + count_ptr, + object_result_ptr, + tag_result_ptr); } static jvmtiError ForceGarbageCollection(jvmtiEnv* env) { diff --git a/runtime/openjdkjvmti/jvmti_allocator.h b/runtime/openjdkjvmti/jvmti_allocator.h new file mode 100644 index 0000000000..1225c143f9 --- /dev/null +++ b/runtime/openjdkjvmti/jvmti_allocator.h @@ -0,0 +1,170 @@ +/* Copyright (C) 2016 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_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ +#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ + +#include "base/logging.h" +#include "base/macros.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +template <typename T> class JvmtiAllocator; + +template <> +class JvmtiAllocator<void> { + public: + typedef void value_type; + typedef void* pointer; + typedef const void* const_pointer; + + template <typename U> + struct rebind { + typedef JvmtiAllocator<U> other; + }; + + explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {} + + template <typename U> + JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit + : env_(other.env_) {} + + JvmtiAllocator(const JvmtiAllocator& other) = default; + JvmtiAllocator& operator=(const JvmtiAllocator& other) = default; + ~JvmtiAllocator() = default; + + private: + jvmtiEnv* env_; + + template <typename U> + friend class JvmtiAllocator; + + template <typename U> + friend bool operator==(const JvmtiAllocator<U>& lhs, const JvmtiAllocator<U>& rhs); +}; + +template <typename T> +class JvmtiAllocator { + public: + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef const T* const_pointer; + typedef const T& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + template <typename U> + struct rebind { + typedef JvmtiAllocator<U> other; + }; + + explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {} + + template <typename U> + JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit + : env_(other.env_) {} + + JvmtiAllocator(const JvmtiAllocator& other) = default; + JvmtiAllocator& operator=(const JvmtiAllocator& other) = default; + ~JvmtiAllocator() = default; + + size_type max_size() const { + return static_cast<size_type>(-1) / sizeof(T); + } + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pointer allocate(size_type n, JvmtiAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) { + DCHECK_LE(n, max_size()); + if (env_ == nullptr) { + T* result = reinterpret_cast<T*>(malloc(n * sizeof(T))); + CHECK(result != nullptr || n == 0u); // Abort if malloc() fails. + return result; + } else { + unsigned char* result; + jvmtiError alloc_error = env_->Allocate(n * sizeof(T), &result); + CHECK(alloc_error == JVMTI_ERROR_NONE); + return reinterpret_cast<T*>(result); + } + } + void deallocate(pointer p, size_type n ATTRIBUTE_UNUSED) { + if (env_ == nullptr) { + free(p); + } else { + jvmtiError dealloc_error = env_->Deallocate(reinterpret_cast<unsigned char*>(p)); + CHECK(dealloc_error == JVMTI_ERROR_NONE); + } + } + + void construct(pointer p, const_reference val) { + new (static_cast<void*>(p)) value_type(val); + } + template <class U, class... Args> + void construct(U* p, Args&&... args) { + ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); + } + void destroy(pointer p) { + p->~value_type(); + } + + inline bool operator==(JvmtiAllocator const& other) { + return env_ == other.env_; + } + inline bool operator!=(JvmtiAllocator const& other) { + return !operator==(other); + } + + private: + jvmtiEnv* env_; + + template <typename U> + friend class JvmtiAllocator; + + template <typename U> + friend bool operator==(const JvmtiAllocator<U>& lhs, const JvmtiAllocator<U>& rhs); +}; + +template <typename T> +inline bool operator==(const JvmtiAllocator<T>& lhs, const JvmtiAllocator<T>& rhs) { + return lhs.env_ == rhs.env_; +} + +template <typename T> +inline bool operator!=(const JvmtiAllocator<T>& lhs, const JvmtiAllocator<T>& rhs) { + return !(lhs == rhs); +} + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc index f16b023d1a..b359f36316 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -39,6 +39,7 @@ #include "gc/allocation_listener.h" #include "instrumentation.h" #include "jni_env_ext-inl.h" +#include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/object.h" #include "runtime.h" @@ -211,4 +212,142 @@ ALWAYS_INLINE inline void ObjectTagTable::UpdateTableWith(T& updater) { // TODO: consider rehash here. } +template <typename T, class Allocator = std::allocator<T>> +struct ReleasableContainer { + using allocator_type = Allocator; + + explicit ReleasableContainer(const allocator_type& alloc, size_t reserve = 10) + : allocator(alloc), + data(reserve > 0 ? allocator.allocate(reserve) : nullptr), + size(0), + capacity(reserve) { + } + + ~ReleasableContainer() { + if (data != nullptr) { + allocator.deallocate(data, capacity); + capacity = 0; + size = 0; + } + } + + T* Release() { + T* tmp = data; + + data = nullptr; + size = 0; + capacity = 0; + + return tmp; + } + + void Resize(size_t new_capacity) { + CHECK_GT(new_capacity, capacity); + + T* tmp = allocator.allocate(new_capacity); + DCHECK(tmp != nullptr); + if (data != nullptr) { + memcpy(tmp, data, sizeof(T) * size); + } + T* old = data; + data = tmp; + allocator.deallocate(old, capacity); + capacity = new_capacity; + } + + void Pushback(const T& elem) { + if (size == capacity) { + size_t new_capacity = 2 * capacity + 1; + Resize(new_capacity); + } + data[size++] = elem; + } + + Allocator allocator; + T* data; + size_t size; + size_t capacity; +}; + +jvmtiError ObjectTagTable::GetTaggedObjects(jvmtiEnv* jvmti_env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) { + if (tag_count < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + if (tag_count > 0) { + for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { + if (tags[i] == 0) { + return ERR(ILLEGAL_ARGUMENT); + } + } + } + if (tags == nullptr) { + return ERR(NULL_POINTER); + } + if (count_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + art::JNIEnvExt* jni_env = self->GetJniEnv(); + + constexpr size_t kDefaultSize = 10; + size_t initial_object_size; + size_t initial_tag_size; + if (tag_count == 0) { + initial_object_size = (object_result_ptr != nullptr) ? tagged_objects_.size() : 0; + initial_tag_size = (tag_result_ptr != nullptr) ? tagged_objects_.size() : 0; + } else { + initial_object_size = initial_tag_size = kDefaultSize; + } + JvmtiAllocator<void> allocator(jvmti_env); + ReleasableContainer<jobject, JvmtiAllocator<jobject>> selected_objects(allocator, initial_object_size); + ReleasableContainer<jlong, JvmtiAllocator<jlong>> selected_tags(allocator, initial_tag_size); + + size_t count = 0; + for (auto& pair : tagged_objects_) { + bool select; + if (tag_count > 0) { + select = false; + for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { + if (tags[i] == pair.second) { + select = true; + break; + } + } + } else { + select = true; + } + + if (select) { + art::mirror::Object* obj = pair.first.Read<art::kWithReadBarrier>(); + if (obj != nullptr) { + count++; + if (object_result_ptr != nullptr) { + selected_objects.Pushback(jni_env->AddLocalReference<jobject>(obj)); + } + if (tag_result_ptr != nullptr) { + selected_tags.Pushback(pair.second); + } + } + } + } + + if (object_result_ptr != nullptr) { + *object_result_ptr = selected_objects.Release(); + } + if (tag_result_ptr != nullptr) { + *tag_result_ptr = selected_tags.Release(); + } + *count_ptr = static_cast<jint>(count); + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h index 579dc22985..071d139f18 100644 --- a/runtime/openjdkjvmti/object_tagging.h +++ b/runtime/openjdkjvmti/object_tagging.h @@ -23,6 +23,7 @@ #include "gc/system_weak.h" #include "gc_root-inl.h" #include "globals.h" +#include "jvmti.h" #include "mirror/object.h" #include "thread-inl.h" @@ -64,6 +65,15 @@ class ObjectTagTable : public art::gc::SystemWeakHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); + jvmtiError GetTaggedObjects(jvmtiEnv* jvmti_env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + private: bool SetLocked(art::Thread* self, art::mirror::Object* obj, jlong tag) REQUIRES_SHARED(art::Locks::mutator_lock_) diff --git a/test/903-hello-tagging/expected.txt b/test/903-hello-tagging/expected.txt index e69de29bb2..872b79b518 100644 --- a/test/903-hello-tagging/expected.txt +++ b/test/903-hello-tagging/expected.txt @@ -0,0 +1,10 @@ +18 +<nothing> +18 +[<1;1>, <11;1>, <2;2>, <12;2>, <3;3>, <13;3>, <4;4>, <14;4>, <5;5>, <15;5>, <6;6>, <16;6>, <7;7>, <17;7>, <8;8>, <18;8>, <9;9>, <19;9>] +4 +[<2;2>, <12;2>, <5;5>, <15;5>] +18 +[<null;1>, <null;1>, <null;2>, <null;2>, <null;3>, <null;3>, <null;4>, <null;4>, <null;5>, <null;5>, <null;6>, <null;6>, <null;7>, <null;7>, <null;8>, <null;8>, <null;9>, <null;9>] +18 +[<1;0>, <2;0>, <3;0>, <4;0>, <5;0>, <6;0>, <7;0>, <8;0>, <9;0>, <11;0>, <12;0>, <13;0>, <14;0>, <15;0>, <16;0>, <17;0>, <18;0>, <19;0>] diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java index 2856a398c9..a8aedb410b 100644 --- a/test/903-hello-tagging/src/Main.java +++ b/test/903-hello-tagging/src/Main.java @@ -15,11 +15,14 @@ */ import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; public class Main { public static void main(String[] args) { System.loadLibrary(args[1]); doTest(); + testGetTaggedObjects(); } public static void doTest() { @@ -68,6 +71,100 @@ public class Main { } } + private static void testGetTaggedObjects() { + // Use an array list to ensure that the objects stay live for a bit. Also gives us a source + // to compare to. We use index % 10 as the tag. + ArrayList<Object> l = new ArrayList<>(); + + for (int i = 0; i < 20; i++) { + Integer o = new Integer(i); + l.add(o); + if (i % 10 != 0) { + setTag(o, i % 10); + } + } + + testGetTaggedObjectsRun(l, null, false, false); + testGetTaggedObjectsRun(l, null, true, true); + testGetTaggedObjectsRun(l, new long[] { 2, 5 }, true, true); + testGetTaggedObjectsRun(l, null, false, true); + testGetTaggedObjectsRun(l, null, true, false); + } + + private static void testGetTaggedObjectsRun(ArrayList<Object> l, long[] searchTags, + boolean returnObjects, boolean returnTags) { + Object[] result = getTaggedObjects(searchTags, returnObjects, returnTags); + + Object[] objects = (Object[])result[0]; + long[] tags = (long[])result[1]; + int count = (int)result[2]; + + System.out.println(count); + printArraysSorted(objects, tags); + } + + private static void printArraysSorted(Object[] objects, long[] tags) { + if (objects == null && tags == null) { + System.out.println("<nothing>"); + return; + } + + int l1 = objects == null ? 0 : objects.length; + int l2 = tags == null ? 0 : tags.length; + int l = Math.max(l1, l2); + Pair[] tmp = new Pair[l]; + for (int i = 0; i < l; i++) { + tmp[i] = new Pair(objects == null ? null : objects[i], tags == null ? 0 : tags[i]); + } + + Arrays.sort(tmp); + + System.out.println(Arrays.toString(tmp)); + } + + private static class Pair implements Comparable<Pair> { + Object obj; + long tag; + public Pair(Object o, long t) { + obj = o; + tag = t; + } + + public int compareTo(Pair p) { + if (tag != p.tag) { + return Long.compare(tag, p.tag); + } + + if ((obj instanceof Comparable) && (p.obj instanceof Comparable)) { + // It's not really correct, but w/e, best effort. + int result = ((Comparable<Object>)obj).compareTo(p.obj); + if (result != 0) { + return result; + } + } + + if (obj != null && p.obj != null) { + return obj.hashCode() - p.obj.hashCode(); + } + + if (obj != null) { + return 1; + } + + if (p.obj != null) { + return -1; + } + + return hashCode() - p.hashCode(); + } + + public String toString() { + return "<" + obj + ";" + tag + ">"; + } + } + private static native void setTag(Object o, long tag); private static native long getTag(Object o); + private static native Object[] getTaggedObjects(long[] searchTags, boolean returnObjects, + boolean returnTags); } diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc index 7d692fb5de..bed4e5d7ad 100644 --- a/test/903-hello-tagging/tagging.cc +++ b/test/903-hello-tagging/tagging.cc @@ -21,9 +21,12 @@ #include <stdio.h> #include <vector> +#include "jni.h" +#include "ScopedLocalRef.h" +#include "ScopedPrimitiveArray.h" + #include "art_method-inl.h" #include "base/logging.h" -#include "jni.h" #include "openjdkjvmti/jvmti.h" #include "ti-agent/common_load.h" #include "utils.h" @@ -56,6 +59,84 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env ATTRIBUTE_UNUSED return tag; } +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTaggedObjects(JNIEnv* env, + jclass, + jlongArray searchTags, + jboolean returnObjects, + jboolean returnTags) { + ScopedLongArrayRO scoped_array(env); + if (searchTags != nullptr) { + scoped_array.reset(searchTags); + } + const jlong* tag_ptr = scoped_array.get(); + if (tag_ptr == nullptr) { + // Can never pass null. + tag_ptr = reinterpret_cast<const jlong*>(1); + } + + jint result_count; + jobject* result_object_array; + jobject** result_object_array_ptr = returnObjects == JNI_TRUE ? &result_object_array : nullptr; + jlong* result_tag_array; + jlong** result_tag_array_ptr = returnTags == JNI_TRUE ? &result_tag_array : nullptr; + + jvmtiError ret = jvmti_env->GetObjectsWithTags(scoped_array.size(), + tag_ptr, + &result_count, + result_object_array_ptr, + result_tag_array_ptr); + if (ret != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(ret, &err); + printf("Failure running GetLoadedClasses: %s\n", err); + return nullptr; + } + + CHECK_GE(result_count, 0); + + ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/Object")); + if (obj_class.get() == nullptr) { + return nullptr; + } + + jobjectArray resultObjectArray = nullptr; + if (returnObjects == JNI_TRUE) { + resultObjectArray = env->NewObjectArray(result_count, obj_class.get(), nullptr); + if (resultObjectArray == nullptr) { + return nullptr; + } + for (jint i = 0; i < result_count; ++i) { + env->SetObjectArrayElement(resultObjectArray, i, result_object_array[i]); + } + } + + jlongArray resultTagArray = nullptr; + if (returnTags == JNI_TRUE) { + resultTagArray = env->NewLongArray(result_count); + env->SetLongArrayRegion(resultTagArray, 0, result_count, result_tag_array); + } + + jobject count_integer; + { + ScopedLocalRef<jclass> integer_class(env, env->FindClass("java/lang/Integer")); + jmethodID methodID = env->GetMethodID(integer_class.get(), "<init>", "(I)V"); + count_integer = env->NewObject(integer_class.get(), methodID, result_count); + if (count_integer == nullptr) { + return nullptr; + } + } + + jobjectArray resultArray = env->NewObjectArray(3, obj_class.get(), nullptr); + if (resultArray == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(resultArray, 0, resultObjectArray); + env->SetObjectArrayElement(resultArray, 1, resultTagArray); + env->SetObjectArrayElement(resultArray, 2, count_integer); + + return resultArray; +} + // Don't do anything jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, |