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) {
--- /dev/null
+/* 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_
#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"
// 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
#include "gc/system_weak.h"
#include "gc_root-inl.h"
#include "globals.h"
+#include "jvmti.h"
#include "mirror/object.h"
#include "thread-inl.h"
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_)
+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>]
*/
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() {
}
}
+ 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);
}
#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"
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,