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