1 /*
2 * Copyright (C) 2013 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 <queue>
18 #include <vector>
19
20 #include "jvmti.h"
21
22 // Test infrastructure
23 #include "jvmti_helper.h"
24 #include "nativehelper/scoped_local_ref.h"
25 #include "nativehelper/scoped_primitive_array.h"
26 #include "test_env.h"
27
28 namespace art {
29 namespace Test1940DdmExt {
30
31 using DdmHandleChunk = jvmtiError(*)(jvmtiEnv* env,
32 jint type_in,
33 jint len_in,
34 const jbyte* data_in,
35 jint* type_out,
36 jint* len_data_out,
37 jbyte** data_out);
38
39 struct DdmCallbackData {
40 public:
DdmCallbackDataart::Test1940DdmExt::DdmCallbackData41 DdmCallbackData(jint type, jint size, jbyte* data) : type_(type), data_(data, data + size) {}
42 jint type_;
43 std::vector<jbyte> data_;
44 };
45 struct DdmsTrackingData {
46 DdmHandleChunk send_ddm_chunk;
47 jrawMonitorID callback_mon;
48 std::queue<DdmCallbackData> callbacks_received;
49 };
50
51 template <typename T>
Dealloc(T * t)52 static void Dealloc(T* t) {
53 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
54 }
55
56 template <typename T, typename ...Rest>
Dealloc(T * t,Rest...rs)57 static void Dealloc(T* t, Rest... rs) {
58 Dealloc(t);
59 Dealloc(rs...);
60 }
61
Java_art_Test1940_processChunk(JNIEnv * env,jclass,jobject chunk)62 extern "C" JNIEXPORT jobject JNICALL Java_art_Test1940_processChunk(JNIEnv* env,
63 jclass,
64 jobject chunk) {
65 DdmsTrackingData* data = nullptr;
66 if (JvmtiErrorToException(
67 env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
68 return nullptr;
69 }
70 CHECK(chunk != nullptr);
71 CHECK(data != nullptr);
72 CHECK(data->send_ddm_chunk != nullptr);
73 ScopedLocalRef<jclass> chunk_class(env, env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk"));
74 if (env->ExceptionCheck()) {
75 return nullptr;
76 }
77 jfieldID type_field_id = env->GetFieldID(chunk_class.get(), "type", "I");
78 jfieldID offset_field_id = env->GetFieldID(chunk_class.get(), "offset", "I");
79 jfieldID length_field_id = env->GetFieldID(chunk_class.get(), "length", "I");
80 jfieldID data_field_id = env->GetFieldID(chunk_class.get(), "data", "[B");
81 jint type = env->GetIntField(chunk, type_field_id);
82 jint off = env->GetIntField(chunk, offset_field_id);
83 jint len = env->GetIntField(chunk, length_field_id);
84 ScopedLocalRef<jbyteArray> chunk_buf(
85 env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk, data_field_id)));
86 if (env->ExceptionCheck()) {
87 return nullptr;
88 }
89 ScopedByteArrayRO byte_data(env, chunk_buf.get());
90 jint out_type;
91 jint out_size;
92 jbyte* out_data;
93 if (JvmtiErrorToException(env, jvmti_env, data->send_ddm_chunk(jvmti_env,
94 type,
95 len,
96 &byte_data[off],
97 /*out*/&out_type,
98 /*out*/&out_size,
99 /*out*/&out_data))) {
100 return nullptr;
101 } else {
102 ScopedLocalRef<jbyteArray> chunk_data(env, env->NewByteArray(out_size));
103 env->SetByteArrayRegion(chunk_data.get(), 0, out_size, out_data);
104 Dealloc(out_data);
105 ScopedLocalRef<jobject> res(env, env->NewObject(chunk_class.get(),
106 env->GetMethodID(chunk_class.get(),
107 "<init>",
108 "(I[BII)V"),
109 out_type,
110 chunk_data.get(),
111 0,
112 out_size));
113 return res.release();
114 }
115 }
116
DeallocParams(jvmtiParamInfo * params,jint n_params)117 static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
118 for (jint i = 0; i < n_params; i++) {
119 Dealloc(params[i].name);
120 }
121 }
122
Java_art_Test1940_publishListen(JNIEnv * env,jclass test_klass,jobject publish)123 extern "C" JNIEXPORT void JNICALL Java_art_Test1940_publishListen(JNIEnv* env,
124 jclass test_klass,
125 jobject publish) {
126 jmethodID publish_method = env->FromReflectedMethod(publish);
127 DdmsTrackingData* data = nullptr;
128 if (JvmtiErrorToException(
129 env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
130 return;
131 }
132 std::vector<DdmCallbackData> callbacks;
133 while (true) {
134 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(data->callback_mon))) {
135 return;
136 }
137 while (data->callbacks_received.empty()) {
138 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorWait(data->callback_mon, 0))) {
139 CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->RawMonitorExit(data->callback_mon));
140 return;
141 }
142 }
143 while (!data->callbacks_received.empty()) {
144 callbacks.emplace_back(std::move(data->callbacks_received.front()));
145 data->callbacks_received.pop();
146 }
147 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(data->callback_mon))) {
148 return;
149 }
150 for (auto cb : callbacks) {
151 ScopedLocalRef<jbyteArray> res(env, env->NewByteArray(cb.data_.size()));
152 env->SetByteArrayRegion(res.get(), 0, cb.data_.size(), cb.data_.data());
153 env->CallStaticVoidMethod(test_klass, publish_method, cb.type_, res.get());
154 }
155 callbacks.clear();
156 }
157 }
158
PublishCB(jvmtiEnv * jvmti,jint type,jint size,jbyte * bytes)159 static void JNICALL PublishCB(jvmtiEnv* jvmti, jint type, jint size, jbyte* bytes) {
160 DdmsTrackingData* data = nullptr;
161 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)));
162 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorEnter(data->callback_mon));
163 data->callbacks_received.emplace(type, size, bytes);
164 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorNotifyAll(data->callback_mon));
165 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorExit(data->callback_mon));
166 }
167
Java_art_Test1940_initializeTest(JNIEnv * env,jclass)168 extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, jclass) {
169 void* old_data = nullptr;
170 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
171 return;
172 } else if (old_data != nullptr) {
173 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
174 env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
175 return;
176 }
177 void* mem = nullptr;
178 if (JvmtiErrorToException(env,
179 jvmti_env,
180 jvmti_env->Allocate(sizeof(DdmsTrackingData),
181 reinterpret_cast<unsigned char**>(&mem)))) {
182 return;
183 }
184 DdmsTrackingData* data = new (mem) DdmsTrackingData{};
185 if (JvmtiErrorToException(
186 env, jvmti_env, jvmti_env->CreateRawMonitor("callback-mon", &data->callback_mon))) {
187 return;
188 }
189 // Get the extensions.
190 jint n_ext = 0;
191 jvmtiExtensionFunctionInfo* infos = nullptr;
192 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
193 return;
194 }
195 for (jint i = 0; i < n_ext; i++) {
196 jvmtiExtensionFunctionInfo* cur_info = &infos[i];
197 if (strcmp("com.android.art.internal.ddm.process_chunk", cur_info->id) == 0) {
198 data->send_ddm_chunk = reinterpret_cast<DdmHandleChunk>(cur_info->func);
199 }
200 // Cleanup the cur_info
201 DeallocParams(cur_info->params, cur_info->param_count);
202 Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
203 }
204 // Cleanup the array.
205 Dealloc(infos);
206 if (data->send_ddm_chunk == nullptr) {
207 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
208 env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions.");
209 return;
210 }
211 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
212 return;
213 }
214
215 jint event_index = -1;
216 bool found_event = false;
217 jvmtiExtensionEventInfo* events = nullptr;
218 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionEvents(&n_ext, &events))) {
219 return;
220 }
221 for (jint i = 0; i < n_ext; i++) {
222 jvmtiExtensionEventInfo* cur_info = &events[i];
223 if (strcmp("com.android.art.internal.ddm.publish_chunk_safe", cur_info->id) == 0) {
224 found_event = true;
225 event_index = cur_info->extension_event_index;
226 }
227 // Cleanup the cur_info
228 DeallocParams(cur_info->params, cur_info->param_count);
229 Dealloc(cur_info->id, cur_info->short_description, cur_info->params);
230 }
231 // Cleanup the array.
232 Dealloc(events);
233 if (!found_event) {
234 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
235 env->ThrowNew(rt_exception.get(), "Unable to find ddms extension event.");
236 return;
237 }
238 JvmtiErrorToException(env,
239 jvmti_env,
240 jvmti_env->SetExtensionEventCallback(
241 event_index, reinterpret_cast<jvmtiExtensionEvent>(PublishCB)));
242 return;
243 }
244
245 } // namespace Test1940DdmExt
246 } // namespace art
247