blob: 2e6bd46f4e342059999f57d680a003193d7b5d31 [file] [log] [blame]
Andreas Gampe732b0ac2017-01-18 15:23:39 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <inttypes.h>
Andreas Gampee0220152017-02-07 14:59:08 -080018#include <sched.h>
Andreas Gampe732b0ac2017-01-18 15:23:39 -080019
20#include "barrier.h"
21#include "base/logging.h"
22#include "base/macros.h"
23#include "jni.h"
Andreas Gampe5e03a302017-03-13 13:10:00 -070024#include "jvmti.h"
Andreas Gampe732b0ac2017-01-18 15:23:39 -080025#include "runtime.h"
26#include "ScopedLocalRef.h"
27#include "thread-inl.h"
28#include "well_known_classes.h"
29
30#include "ti-agent/common_helper.h"
31#include "ti-agent/common_load.h"
32
33namespace art {
34namespace Test930AgentThread {
35
36struct AgentData {
37 AgentData() : main_thread(nullptr),
38 jvmti_env(nullptr),
Andreas Gamped9911ee2017-03-27 13:27:24 -070039 b(2),
40 priority(0) {
Andreas Gampe732b0ac2017-01-18 15:23:39 -080041 }
42
43 jthread main_thread;
44 jvmtiEnv* jvmti_env;
45 Barrier b;
46 jint priority;
47};
48
49static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) {
50 AgentData* data = reinterpret_cast<AgentData*>(arg);
51
52 // Check some basics.
53 // This thread is not the main thread.
54 jthread this_thread;
55 jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread);
56 CHECK(!JvmtiErrorToException(env, this_thread_result));
57 CHECK(!env->IsSameObject(this_thread, data->main_thread));
58
59 // The thread is a daemon.
60 jvmtiThreadInfo info;
61 jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info);
62 CHECK(!JvmtiErrorToException(env, info_result));
63 CHECK(info.is_daemon);
64
65 // The thread has the requested priority.
66 // TODO: Our thread priorities do not work on the host.
67 // CHECK_EQ(info.priority, data->priority);
68
69 // Check further parts of the thread:
70 jint thread_count;
71 jthread* threads;
72 jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads);
73 CHECK(!JvmtiErrorToException(env, threads_result));
74 bool found = false;
75 for (jint i = 0; i != thread_count; ++i) {
76 if (env->IsSameObject(threads[i], this_thread)) {
77 found = true;
78 break;
79 }
80 }
81 CHECK(found);
82
83 // Done, let the main thread progress.
84 data->b.Pass(Thread::Current());
85}
86
87extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
88 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
89 // Create a Thread object.
90 ScopedLocalRef<jobject> thread_name(env,
91 env->NewStringUTF("Agent Thread"));
92 if (thread_name.get() == nullptr) {
93 return;
94 }
95
96 ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
97 if (thread.get() == nullptr) {
98 return;
99 }
100
101 env->CallNonvirtualVoidMethod(thread.get(),
102 WellKnownClasses::java_lang_Thread,
103 WellKnownClasses::java_lang_Thread_init,
104 Runtime::Current()->GetMainThreadGroup(),
105 thread_name.get(),
106 kMinThreadPriority,
107 JNI_FALSE);
108 if (env->ExceptionCheck()) {
109 return;
110 }
111
112 jthread main_thread;
113 jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread);
114 if (JvmtiErrorToException(env, main_thread_result)) {
115 return;
116 }
117
118 AgentData data;
119 data.main_thread = env->NewGlobalRef(main_thread);
120 data.jvmti_env = jvmti_env;
121 data.priority = JVMTI_THREAD_MIN_PRIORITY;
122
123 jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority);
124 if (JvmtiErrorToException(env, result)) {
125 return;
126 }
127
128 data.b.Wait(Thread::Current());
129
Andreas Gampee0220152017-02-07 14:59:08 -0800130 // Scheduling may mean that the agent thread is put to sleep. Wait until it's dead in an effort
131 // to not unload the plugin and crash.
132 for (;;) {
133 NanoSleep(1000 * 1000);
134 jint thread_state;
135 jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state);
136 if (JvmtiErrorToException(env, state_result)) {
137 return;
138 }
139 if (thread_state == 0 || // Was never alive.
140 (thread_state & JVMTI_THREAD_STATE_TERMINATED) != 0) { // Was alive and died.
141 break;
142 }
143 }
144 // Yield and sleep a bit more, to give the plugin time to tear down the native thread structure.
145 sched_yield();
146 NanoSleep(100 * 1000 * 1000);
147
Andreas Gampe732b0ac2017-01-18 15:23:39 -0800148 env->DeleteGlobalRef(data.main_thread);
149}
150
151} // namespace Test930AgentThread
152} // namespace art