From d18d9e2a94445d4b42e4bc6f0e642e6f76b4706d Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 16 Jan 2017 16:08:45 +0000 Subject: [PATCH] Revert "Revert "ART: Add ThreadGroup API support"" This reverts commit 87071bfb6c1b708bdfa2a5f91d4744667b3a0443. Add an ObjectLock, which corresponds to the synchronized(this) implementation on the Java side. Wait for the expected five child threads in the root group before running the actual child test. Bug: 31455788 Change-Id: Ib7a065d6a11f06f0325e3a8db040629f3ca69407 Test: m test-art-host-run-test-925-threadgroups --- runtime/openjdkjvmti/Android.bp | 1 + runtime/openjdkjvmti/OpenjdkJvmTi.cc | 12 +- runtime/openjdkjvmti/ti_threadgroup.cc | 285 +++++++++++++++++++++++++++++++++ runtime/openjdkjvmti/ti_threadgroup.h | 60 +++++++ test/925-threadgroups/build | 17 ++ test/925-threadgroups/expected.txt | 16 ++ test/925-threadgroups/info.txt | 1 + test/925-threadgroups/run | 19 +++ test/925-threadgroups/src/Main.java | 113 +++++++++++++ test/925-threadgroups/threadgroups.cc | 127 +++++++++++++++ test/Android.bp | 1 + test/Android.run-test.mk | 1 + 12 files changed, 650 insertions(+), 3 deletions(-) create mode 100644 runtime/openjdkjvmti/ti_threadgroup.cc create mode 100644 runtime/openjdkjvmti/ti_threadgroup.h create mode 100755 test/925-threadgroups/build create mode 100644 test/925-threadgroups/expected.txt create mode 100644 test/925-threadgroups/info.txt create mode 100755 test/925-threadgroups/run create mode 100644 test/925-threadgroups/src/Main.java create mode 100644 test/925-threadgroups/threadgroups.cc diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 42fed50ba..af027f6ba 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -30,6 +30,7 @@ cc_defaults { "ti_stack.cc", "ti_redefine.cc", "ti_thread.cc", + "ti_threadgroup.cc", "ti_timers.cc", "transform.cc"], include_dirs: ["art/runtime"], diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 4aedec9be..d9aea01ef 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -58,6 +58,7 @@ #include "ti_redefine.h" #include "ti_stack.h" #include "ti_thread.h" +#include "ti_threadgroup.h" #include "ti_timers.h" #include "transform.h" @@ -205,13 +206,13 @@ class JvmtiFunctions { static jvmtiError GetTopThreadGroups(jvmtiEnv* env, jint* group_count_ptr, jthreadGroup** groups_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr); } static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr); } static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, @@ -220,7 +221,12 @@ class JvmtiFunctions { jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetThreadGroupChildren(env, + group, + thread_count_ptr, + threads_ptr, + group_count_ptr, + groups_ptr); } static jvmtiError GetStackTrace(jvmtiEnv* env, diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc new file mode 100644 index 000000000..35b1bfd92 --- /dev/null +++ b/runtime/openjdkjvmti/ti_threadgroup.cc @@ -0,0 +1,285 @@ +/* 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. + */ + +#include "ti_threadgroup.h" + +#include "art_field.h" +#include "art_jvmti.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "handle_scope-inl.h" +#include "jni_internal.h" +#include "mirror/class.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "obj_ptr.h" +#include "object_lock.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" +#include "well_known_classes.h" + +namespace openjdkjvmti { + + +jvmtiError ThreadGroupUtil::GetTopThreadGroups(jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + // We only have a single top group. So we can take the current thread and move upwards. + if (group_count_ptr == nullptr || groups_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Runtime* runtime = art::Runtime::Current(); + if (runtime == nullptr) { + // Must be starting the runtime, or dying. + return ERR(WRONG_PHASE); + } + + jobject sys_thread_group = runtime->GetSystemThreadGroup(); + if (sys_thread_group == nullptr) { + // Seems we're still starting up. + return ERR(WRONG_PHASE); + } + + unsigned char* data; + jvmtiError result = env->Allocate(sizeof(jthreadGroup), &data); + if (result != ERR(NONE)) { + return result; + } + + jthreadGroup* groups = reinterpret_cast(data); + *groups = + reinterpret_cast(art::Thread::Current()->GetJniEnv())->NewLocalRef(sys_thread_group); + *groups_ptr = groups; + *group_count_ptr = 1; + + return ERR(NONE); +} + +jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + if (group == nullptr) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ObjPtr obj = soa.Decode(group); + + // Do the name first. It's the only thing that can fail. + { + art::ArtField* name_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name); + CHECK(name_field != nullptr); + art::ObjPtr name_obj = + art::ObjPtr::DownCast(name_field->GetObject(obj)); + std::string tmp_str; + const char* tmp_cstr; + if (name_obj == nullptr) { + tmp_cstr = ""; + } else { + tmp_str = name_obj->ToModifiedUtf8(); + tmp_cstr = tmp_str.c_str(); + } + jvmtiError result = + CopyString(env, tmp_cstr, reinterpret_cast(&info_ptr->name)); + if (result != ERR(NONE)) { + return result; + } + } + + // Parent. + { + art::ArtField* parent_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent); + CHECK(parent_field != nullptr); + art::ObjPtr parent_group = parent_field->GetObject(obj); + info_ptr->parent = parent_group == nullptr + ? nullptr + : soa.AddLocalReference(parent_group); + } + + // Max priority. + { + art::ArtField* prio_field = obj->GetClass()->FindDeclaredInstanceField("maxPriority", "I"); + CHECK(prio_field != nullptr); + info_ptr->max_priority = static_cast(prio_field->GetInt(obj)); + } + + // Daemon. + { + art::ArtField* daemon_field = obj->GetClass()->FindDeclaredInstanceField("daemon", "Z"); + CHECK(daemon_field != nullptr); + info_ptr->is_daemon = daemon_field->GetBoolean(obj) == 0 ? JNI_FALSE : JNI_TRUE; + } + + return ERR(NONE); +} + + +static bool IsInDesiredThreadGroup(art::Handle desired_thread_group, + art::ObjPtr peer) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + CHECK(desired_thread_group.Get() != nullptr); + + art::ArtField* thread_group_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + DCHECK(thread_group_field != nullptr); + art::ObjPtr group = thread_group_field->GetObject(peer); + return (group == desired_thread_group.Get()); +} + +static void GetThreads(art::Handle thread_group, + std::vector>* thread_peers) + REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) { + CHECK(thread_group.Get() != nullptr); + + art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_); + for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) { + if (t->IsStillStarting()) { + continue; + } + art::ObjPtr peer = t->GetPeer(); + if (peer == nullptr) { + continue; + } + if (IsInDesiredThreadGroup(thread_group, peer)) { + thread_peers->push_back(peer); + } + } +} + +static void GetChildThreadGroups(art::Handle thread_group, + std::vector>* thread_groups) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + CHECK(thread_group.Get() != nullptr); + + // Get the ThreadGroup[] "groups" out of this thread group... + art::ArtField* groups_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups); + art::ObjPtr groups_array = groups_field->GetObject(thread_group.Get()); + + if (groups_array == nullptr) { + return; + } + CHECK(groups_array->IsObjectArray()); + + art::ObjPtr> groups_array_as_array = + groups_array->AsObjectArray(); + + // Copy all non-null elements. + for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) { + art::ObjPtr entry = groups_array_as_array->Get(i); + if (entry != nullptr) { + thread_groups->push_back(entry); + } + } +} + +jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + if (group == nullptr) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + + if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) { + return ERR(INVALID_THREAD_GROUP); + } + + art::StackHandleScope<1> hs(soa.Self()); + art::Handle thread_group = hs.NewHandle( + soa.Decode(group)); + + art::ObjectLock thread_group_lock(soa.Self(), thread_group); + + std::vector> thread_peers; + GetThreads(thread_group, &thread_peers); + + std::vector> thread_groups; + GetChildThreadGroups(thread_group, &thread_groups); + + jthread* thread_data = nullptr; + JvmtiUniquePtr peers_uptr; + if (!thread_peers.empty()) { + unsigned char* data; + jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data); + if (res != ERR(NONE)) { + return res; + } + thread_data = reinterpret_cast(data); + peers_uptr = MakeJvmtiUniquePtr(env, data); + } + + jthreadGroup* group_data = nullptr; + if (!thread_groups.empty()) { + unsigned char* data; + jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data); + if (res != ERR(NONE)) { + return res; + } + group_data = reinterpret_cast(data); + } + + // Can't fail anymore from here on. + + // Copy data into out buffers. + for (size_t i = 0; i != thread_peers.size(); ++i) { + thread_data[i] = soa.AddLocalReference(thread_peers[i]); + } + for (size_t i = 0; i != thread_groups.size(); ++i) { + group_data[i] = soa.AddLocalReference(thread_groups[i]); + } + + *thread_count_ptr = static_cast(thread_peers.size()); + *threads_ptr = thread_data; + *group_count_ptr = static_cast(thread_groups.size()); + *groups_ptr = group_data; + + // Everything's fine. + peers_uptr.release(); + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_threadgroup.h b/runtime/openjdkjvmti/ti_threadgroup.h new file mode 100644 index 000000000..c3a0ff5e1 --- /dev/null +++ b/runtime/openjdkjvmti/ti_threadgroup.h @@ -0,0 +1,60 @@ +/* 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_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class ThreadGroupUtil { + public: + static jvmtiError GetTopThreadGroups(jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ diff --git a/test/925-threadgroups/build b/test/925-threadgroups/build new file mode 100755 index 000000000..898e2e54a --- /dev/null +++ b/test/925-threadgroups/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 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. + +./default-build "$@" --experimental agents diff --git a/test/925-threadgroups/expected.txt b/test/925-threadgroups/expected.txt new file mode 100644 index 000000000..7d1a259c8 --- /dev/null +++ b/test/925-threadgroups/expected.txt @@ -0,0 +1,16 @@ +java.lang.ThreadGroup[name=main,maxpri=10] + java.lang.ThreadGroup[name=system,maxpri=10] + main + 10 + false +java.lang.ThreadGroup[name=system,maxpri=10] + null + system + 10 + false +main: + [Thread[main,5,main]] + [] +system: + [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system]] + [java.lang.ThreadGroup[name=main,maxpri=10]] diff --git a/test/925-threadgroups/info.txt b/test/925-threadgroups/info.txt new file mode 100644 index 000000000..875a5f6ec --- /dev/null +++ b/test/925-threadgroups/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run new file mode 100755 index 000000000..4379349cb --- /dev/null +++ b/test/925-threadgroups/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright 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. + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java new file mode 100644 index 000000000..c59efe2f7 --- /dev/null +++ b/test/925-threadgroups/src/Main.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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. + */ + +import java.util.Arrays; +import java.util.Comparator; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + Thread t1 = Thread.currentThread(); + ThreadGroup curGroup = t1.getThreadGroup(); + + ThreadGroup rootGroup = curGroup; + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + + ThreadGroup topGroups[] = getTopThreadGroups(); + if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) { + System.out.println(Arrays.toString(topGroups)); + throw new RuntimeException("Unexpected topGroups"); + } + + printThreadGroupInfo(curGroup); + printThreadGroupInfo(rootGroup); + + waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */); + + checkChildren(curGroup); + } + + private static void printThreadGroupInfo(ThreadGroup tg) { + Object[] threadGroupInfo = getThreadGroupInfo(tg); + if (threadGroupInfo == null || threadGroupInfo.length != 4) { + System.out.println(Arrays.toString(threadGroupInfo)); + throw new RuntimeException("threadGroupInfo length wrong"); + } + + System.out.println(tg); + System.out.println(" " + threadGroupInfo[0]); // Parent + System.out.println(" " + threadGroupInfo[1]); // Name + System.out.println(" " + threadGroupInfo[2]); // Priority + System.out.println(" " + threadGroupInfo[3]); // Daemon + } + + private static void checkChildren(ThreadGroup tg) { + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + ThreadGroup[] groups = (ThreadGroup[])data[1]; + + Arrays.sort(threads, THREAD_COMP); + Arrays.sort(groups, THREADGROUP_COMP); + System.out.println(tg.getName() + ":"); + System.out.println(" " + Arrays.toString(threads)); + System.out.println(" " + Arrays.toString(groups)); + + if (tg.getParent() != null) { + checkChildren(tg.getParent()); + } + } + + private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS) + throws Exception { + for (int i = 0; i < timeoutS; i++) { + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + if (threads.length == expectedChildCount) { + return; + } + Thread.sleep(1000); + } + + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + System.out.println(Arrays.toString(threads)); + throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children."); + } + + private final static Comparator THREAD_COMP = new Comparator() { + public int compare(Thread o1, Thread o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private final static Comparator THREADGROUP_COMP = new Comparator() { + public int compare(ThreadGroup o1, ThreadGroup o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private static native ThreadGroup[] getTopThreadGroups(); + private static native Object[] getThreadGroupInfo(ThreadGroup tg); + // Returns an array where element 0 is an array of threads and element 1 is an array of groups. + private static native Object[] getThreadGroupChildren(ThreadGroup tg); +} diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc new file mode 100644 index 000000000..6c6e835dd --- /dev/null +++ b/test/925-threadgroups/threadgroups.cc @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 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. + */ + +#include + +#include "android-base/stringprintf.h" +#include "base/macros.h" +#include "base/logging.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedLocalRef.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test925ThreadGroups { + +// private static native Object[] getThreadGroupInfo(); +// // Returns an array where element 0 is an array of threads and element 1 is an array of groups. +// private static native Object[] getThreadGroupChildren(); + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTopThreadGroups( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jthreadGroup* groups; + jint group_count; + jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + return groups[index]; + }; + jobjectArray ret = CreateObjectArray(env, group_count, "java/lang/ThreadGroup", callback); + + jvmti_env->Deallocate(reinterpret_cast(groups)); + + return ret; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupInfo( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) { + jvmtiThreadGroupInfo info; + jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + switch (index) { + // The parent. + case 0: + return info.parent; + + // The name. + case 1: + return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name); + + // The priority. Use a string for simplicity of construction. + case 2: + return env->NewStringUTF(android::base::StringPrintf("%d", info.max_priority).c_str()); + + // Whether it's a daemon. Use a string for simplicity of construction. + case 3: + return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false"); + } + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + }; + return CreateObjectArray(env, 4, "java/lang/Object", callback); +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupChildren( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) { + jint thread_count; + jthread* threads; + jint threadgroup_count; + jthreadGroup* groups; + + jvmtiError result = jvmti_env->GetThreadGroupChildren(group, + &thread_count, + &threads, + &threadgroup_count, + &groups); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint component_index) -> jobject { + if (component_index == 0) { + // Threads. + auto inner_callback = [&](jint index) { + return threads[index]; + }; + return CreateObjectArray(env, thread_count, "java/lang/Thread", inner_callback); + } else { + // Groups. + auto inner_callback = [&](jint index) { + return groups[index]; + }; + return CreateObjectArray(env, threadgroup_count, "java/lang/ThreadGroup", inner_callback); + } + }; + jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback); + + jvmti_env->Deallocate(reinterpret_cast(threads)); + jvmti_env->Deallocate(reinterpret_cast(groups)); + + return ret; +} + +} // namespace Test925ThreadGroups +} // namespace art diff --git a/test/Android.bp b/test/Android.bp index 1ea125289..c551b9de4 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -265,6 +265,7 @@ art_cc_defaults { "922-properties/properties.cc", "923-monitors/monitors.cc", "924-threads/threads.cc", + "925-threadgroups/threadgroups.cc", "927-timers/timers.cc", ], shared_libs: [ diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 55cef974a..11d069a39 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -303,6 +303,7 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 922-properties \ 923-monitors \ 924-threads \ + 925-threadgroups \ 926-multi-obsolescence \ 927-timers \ -- 2.11.0