diff options
| -rw-r--r-- | runtime/openjdkjvmti/Android.bp | 1 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 12 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_threadgroup.cc | 285 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_threadgroup.h | 60 | ||||
| -rwxr-xr-x | test/925-threadgroups/build | 17 | ||||
| -rw-r--r-- | test/925-threadgroups/expected.txt | 16 | ||||
| -rw-r--r-- | test/925-threadgroups/info.txt | 1 | ||||
| -rwxr-xr-x | test/925-threadgroups/run | 19 | ||||
| -rw-r--r-- | test/925-threadgroups/src/Main.java | 113 | ||||
| -rw-r--r-- | test/925-threadgroups/threadgroups.cc | 127 | ||||
| -rw-r--r-- | test/Android.bp | 1 | ||||
| -rw-r--r-- | test/Android.run-test.mk | 1 |
12 files changed, 650 insertions, 3 deletions
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 42fed50baf..af027f6ba6 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 4aedec9be0..d9aea01ef8 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 0000000000..35b1bfd920 --- /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<jthreadGroup*>(data); + *groups = + reinterpret_cast<JNIEnv*>(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<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(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<art::mirror::String> name_obj = + art::ObjPtr<art::mirror::String>::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<unsigned char**>(&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<art::mirror::Object> parent_group = parent_field->GetObject(obj); + info_ptr->parent = parent_group == nullptr + ? nullptr + : soa.AddLocalReference<jthreadGroup>(parent_group); + } + + // Max priority. + { + art::ArtField* prio_field = obj->GetClass()->FindDeclaredInstanceField("maxPriority", "I"); + CHECK(prio_field != nullptr); + info_ptr->max_priority = static_cast<jint>(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<art::mirror::Object> desired_thread_group, + art::ObjPtr<art::mirror::Object> 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<art::mirror::Object> group = thread_group_field->GetObject(peer); + return (group == desired_thread_group.Get()); +} + +static void GetThreads(art::Handle<art::mirror::Object> thread_group, + std::vector<art::ObjPtr<art::mirror::Object>>* 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<art::mirror::Object> peer = t->GetPeer(); + if (peer == nullptr) { + continue; + } + if (IsInDesiredThreadGroup(thread_group, peer)) { + thread_peers->push_back(peer); + } + } +} + +static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group, + std::vector<art::ObjPtr<art::mirror::Object>>* 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<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get()); + + if (groups_array == nullptr) { + return; + } + CHECK(groups_array->IsObjectArray()); + + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> groups_array_as_array = + groups_array->AsObjectArray<art::mirror::Object>(); + + // Copy all non-null elements. + for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) { + art::ObjPtr<art::mirror::Object> 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<art::mirror::Object> thread_group = hs.NewHandle( + soa.Decode<art::mirror::Object>(group)); + + art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group); + + std::vector<art::ObjPtr<art::mirror::Object>> thread_peers; + GetThreads(thread_group, &thread_peers); + + std::vector<art::ObjPtr<art::mirror::Object>> 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<jthread*>(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<jthreadGroup*>(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<jthread>(thread_peers[i]); + } + for (size_t i = 0; i != thread_groups.size(); ++i) { + group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]); + } + + *thread_count_ptr = static_cast<jint>(thread_peers.size()); + *threads_ptr = thread_data; + *group_count_ptr = static_cast<jint>(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 0000000000..c3a0ff5e15 --- /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 0000000000..898e2e54a2 --- /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 0000000000..7d1a259c89 --- /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 0000000000..875a5f6ec1 --- /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 0000000000..4379349cb2 --- /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 0000000000..c59efe2f7b --- /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> THREAD_COMP = new Comparator<Thread>() { + public int compare(Thread o1, Thread o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() { + 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 0000000000..6c6e835dd3 --- /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 <stdio.h> + +#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<unsigned char*>(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<unsigned char*>(threads)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups)); + + return ret; +} + +} // namespace Test925ThreadGroups +} // namespace art diff --git a/test/Android.bp b/test/Android.bp index 1ea1252893..c551b9de45 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 55cef974ab..11d069a395 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 \ |