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