blob: 497db1cd3e99262f65b0fadd1db8a793db73588f [file] [log] [blame]
Alex Light8f2c6d42017-04-10 16:27:35 -07001/*
2 * Copyright 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 <jni.h>
18#include <stdio.h>
19#include <iostream>
20#include <fstream>
21#include <stdio.h>
22#include <sstream>
Alex Lightb7edcda2017-04-27 13:20:31 -070023#include <strstream>
Alex Light8f2c6d42017-04-10 16:27:35 -070024
25#include "jvmti.h"
26#include "exec_utils.h"
27#include "utils.h"
28
29namespace art {
30
31// Should we do a 'full_rewrite' with this test?
32static constexpr bool kDoFullRewrite = true;
33
34struct StressData {
35 std::string dexter_cmd;
36 std::string out_temp_dex;
37 std::string in_temp_dex;
38 bool vm_class_loader_initialized;
Alex Lightb7edcda2017-04-27 13:20:31 -070039 bool trace_stress;
40 bool redefine_stress;
Alex Light8f2c6d42017-04-10 16:27:35 -070041};
42
43static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
44 std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
45 file.write(reinterpret_cast<const char*>(data), data_len);
46 file.flush();
47}
48
49static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
50 std::ifstream file(fname, std::ios::binary | std::ios::in);
51 file.seekg(0, std::ios::end);
52 size_t len = file.tellg();
53 data->resize(len);
54 file.seekg(0);
55 file.read(reinterpret_cast<char*>(data->data()), len);
56 return len != 0;
57}
58
59// TODO rewrite later.
60static bool DoExtractClassFromData(StressData* data,
61 const std::string& class_name,
62 jint in_len,
63 const unsigned char* in_data,
64 /*out*/std::vector<unsigned char>* dex) {
65 // Write the dex file into a temporary file.
66 WriteToFile(data->in_temp_dex, in_len, in_data);
67 // Clear out file so even if something suppresses the exit value we will still detect dexter
68 // failure.
69 WriteToFile(data->out_temp_dex, 0, nullptr);
70 // Have dexter do the extraction.
71 std::vector<std::string> args;
72 args.push_back(data->dexter_cmd);
73 if (kDoFullRewrite) {
74 args.push_back("-x");
75 args.push_back("full_rewrite");
76 }
77 args.push_back("-e");
78 args.push_back(class_name);
79 args.push_back("-o");
80 args.push_back(data->out_temp_dex);
81 args.push_back(data->in_temp_dex);
82 std::string error;
83 if (ExecAndReturnCode(args, &error) != 0) {
84 LOG(ERROR) << "unable to execute dexter: " << error;
85 return false;
86 }
87 return ReadIntoBuffer(data->out_temp_dex, dex);
88}
89
Alex Light0af8cde2017-04-20 13:35:05 -070090static void doJvmtiMethodBind(jvmtiEnv* jvmtienv,
91 JNIEnv* env,
92 jthread thread,
93 jmethodID m,
94 void* address,
95 /*out*/void** out_address) {
96 *out_address = address;
97 jvmtiThreadInfo info;
98 if (thread == nullptr) {
99 info.name = const_cast<char*>("<NULLPTR>");
100 } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) {
Alex Light42151c02017-04-20 15:54:25 -0700101 info.name = const_cast<char*>("<UNKNOWN THREAD>");
Alex Light0af8cde2017-04-20 13:35:05 -0700102 }
103 char *fname, *fsig, *fgen;
104 char *cname, *cgen;
105 jclass klass = nullptr;
106 if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) {
107 LOG(ERROR) << "Unable to get method declaring class!";
108 return;
109 }
110 if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) {
111 LOG(ERROR) << "Unable to get method name!";
112 env->DeleteLocalRef(klass);
113 return;
114 }
115 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
116 LOG(ERROR) << "Unable to get class name!";
117 env->DeleteLocalRef(klass);
118 return;
119 }
Alex Lightb7edcda2017-04-27 13:20:31 -0700120 LOG(INFO) << "Loading native method \"" << cname << "->" << fname << fsig << "\". Thread is \""
121 << info.name << "\"";
Alex Light0af8cde2017-04-20 13:35:05 -0700122 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
123 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
124 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
125 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
126 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
127 env->DeleteLocalRef(klass);
128 return;
129}
130
Alex Lightb7edcda2017-04-27 13:20:31 -0700131static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) {
132 jclass klass = jnienv->GetObjectClass(obj);
133 char *cname, *cgen;
134 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
135 LOG(ERROR) << "Unable to get class name!";
136 jnienv->DeleteLocalRef(klass);
137 return "<UNKNOWN>";
138 }
139 std::string name(cname);
140 if (name == "Ljava/lang/String;") {
141 jstring str = reinterpret_cast<jstring>(obj);
142 const char* val = jnienv->GetStringUTFChars(str, nullptr);
143 if (val == nullptr) {
144 name += " (unable to get value)";
145 } else {
146 std::ostringstream oss;
147 oss << name << " (value: \"" << val << "\")";
148 name = oss.str();
149 jnienv->ReleaseStringUTFChars(str, val);
150 }
151 }
152 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
153 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
154 jnienv->DeleteLocalRef(klass);
155 return name;
156}
157
158static std::string GetValOf(jvmtiEnv* env, JNIEnv* jnienv, std::string type, jvalue val) {
159 std::ostringstream oss;
160 switch (type[0]) {
161 case '[':
162 case 'L':
163 return val.l != nullptr ? GetName(env, jnienv, val.l) : "null";
164 case 'Z':
165 return val.z == JNI_TRUE ? "true" : "false";
166 case 'B':
167 oss << val.b;
168 return oss.str();
169 case 'C':
170 oss << val.c;
171 return oss.str();
172 case 'S':
173 oss << val.s;
174 return oss.str();
175 case 'I':
176 oss << val.i;
177 return oss.str();
178 case 'J':
179 oss << val.j;
180 return oss.str();
181 case 'F':
182 oss << val.f;
183 return oss.str();
184 case 'D':
185 oss << val.d;
186 return oss.str();
187 case 'V':
188 return "<void>";
189 default:
190 return "<ERROR Found type " + type + ">";
191 }
192}
193
194void JNICALL MethodExitHook(jvmtiEnv* jvmtienv,
195 JNIEnv* env,
196 jthread thread,
197 jmethodID m,
198 jboolean was_popped_by_exception,
199 jvalue val) {
200 jvmtiThreadInfo info;
201 if (thread == nullptr) {
202 info.name = const_cast<char*>("<NULLPTR>");
203 } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) {
204 // LOG(WARNING) << "Unable to get thread info!";
205 info.name = const_cast<char*>("<UNKNOWN THREAD>");
206 }
207 char *fname, *fsig, *fgen;
208 char *cname, *cgen;
209 jclass klass = nullptr;
210 if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) {
211 LOG(ERROR) << "Unable to get method declaring class!";
212 return;
213 }
214 if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) {
215 LOG(ERROR) << "Unable to get method name!";
216 env->DeleteLocalRef(klass);
217 return;
218 }
219 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
220 LOG(ERROR) << "Unable to get class name!";
221 env->DeleteLocalRef(klass);
222 return;
223 }
224 std::string type(fsig);
225 type = type.substr(type.find(")") + 1);
226 std::string out_val(was_popped_by_exception ? "" : GetValOf(jvmtienv, env, type, val));
227 LOG(INFO) << "Leaving method \"" << cname << "->" << fname << fsig << "\". Thread is \""
228 << info.name << "\"." << std::endl
229 << " Cause: " << (was_popped_by_exception ? "exception" : "return ")
230 << out_val << ".";
231 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
232 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
233 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
234 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
235 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
236 env->DeleteLocalRef(klass);
237}
238
239void JNICALL MethodEntryHook(jvmtiEnv* jvmtienv,
240 JNIEnv* env,
241 jthread thread,
242 jmethodID m) {
243 jvmtiThreadInfo info;
244 if (thread == nullptr) {
245 info.name = const_cast<char*>("<NULLPTR>");
246 } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) {
247 info.name = const_cast<char*>("<UNKNOWN THREAD>");
248 }
249 char *fname, *fsig, *fgen;
250 char *cname, *cgen;
251 jclass klass = nullptr;
252 if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) {
253 LOG(ERROR) << "Unable to get method declaring class!";
254 return;
255 }
256 if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) {
257 LOG(ERROR) << "Unable to get method name!";
258 env->DeleteLocalRef(klass);
259 return;
260 }
261 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
262 LOG(ERROR) << "Unable to get class name!";
263 env->DeleteLocalRef(klass);
264 return;
265 }
266 LOG(INFO) << "Entering method \"" << cname << "->" << fname << fsig << "\". Thread is \""
267 << info.name << "\"";
268 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
269 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
270 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
271 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
272 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
273 env->DeleteLocalRef(klass);
274}
275
Alex Light8f2c6d42017-04-10 16:27:35 -0700276// The hook we are using.
277void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
278 JNIEnv* jni_env ATTRIBUTE_UNUSED,
279 jclass class_being_redefined ATTRIBUTE_UNUSED,
280 jobject loader ATTRIBUTE_UNUSED,
281 const char* name,
282 jobject protection_domain ATTRIBUTE_UNUSED,
283 jint class_data_len,
284 const unsigned char* class_data,
285 jint* new_class_data_len,
286 unsigned char** new_class_data) {
287 std::vector<unsigned char> out;
288 std::string name_str(name);
289 // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
290 // classes).
291 std::replace(name_str.begin(), name_str.end(), '/', '.');
292 StressData* data = nullptr;
293 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
294 JVMTI_ERROR_NONE);
295 if (!data->vm_class_loader_initialized) {
296 LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
297 << "initialized. Transforming this class could cause spurious test failures.";
298 return;
299 } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
300 LOG(INFO) << "Extracted class: " << name;
301 unsigned char* new_data;
302 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
303 memcpy(new_data, out.data(), out.size());
304 *new_class_data_len = static_cast<jint>(out.size());
305 *new_class_data = new_data;
306 } else {
307 std::cerr << "Unable to extract class " << name_str << std::endl;
308 *new_class_data_len = 0;
309 *new_class_data = nullptr;
310 }
311}
312
Alex Lightb7edcda2017-04-27 13:20:31 -0700313static std::string AdvanceOption(const std::string& ops) {
314 return ops.substr(ops.find(',') + 1);
Alex Light8f2c6d42017-04-10 16:27:35 -0700315}
316
Alex Lightb7edcda2017-04-27 13:20:31 -0700317static bool HasNextOption(const std::string& ops) {
318 return ops.find(',') != std::string::npos;
319}
320
321static std::string GetOption(const std::string& in) {
322 return in.substr(0, in.find(','));
323}
324
325// Options are
326// jvmti-stress,[redefine,${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2},][trace]
327static void ReadOptions(StressData* data, char* options) {
328 std::string ops(options);
329 CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress";
330 do {
331 ops = AdvanceOption(ops);
332 std::string cur = GetOption(ops);
333 if (cur == "trace") {
334 data->trace_stress = true;
335 } else if (cur == "redefine") {
336 data->redefine_stress = true;
337 ops = AdvanceOption(ops);
338 data->dexter_cmd = GetOption(ops);
339 ops = AdvanceOption(ops);
340 data->in_temp_dex = GetOption(ops);
341 ops = AdvanceOption(ops);
342 data->out_temp_dex = GetOption(ops);
343 } else {
344 LOG(FATAL) << "Unknown option: " << GetOption(ops);
345 }
346 } while (HasNextOption(ops));
347}
348
349// Do final setup during the VMInit callback. By this time most things are all setup.
350static void JNICALL PerformFinalSetupVMInit(jvmtiEnv *jvmti_env,
351 JNIEnv* jni_env,
352 jthread thread ATTRIBUTE_UNUSED) {
Alex Light8f2c6d42017-04-10 16:27:35 -0700353 // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
354 // visibility but the class will be loaded behind the scenes.
355 LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
356 jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
Alex Lightb7edcda2017-04-27 13:20:31 -0700357 StressData* data = nullptr;
358 CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
359 JVMTI_ERROR_NONE);
360 // We need to make sure that VMClassLoader is initialized before we start redefining anything
361 // since it can give (non-fatal) error messages if it's initialized after we've redefined BCP
362 // classes. These error messages are expected and no problem but they will mess up our testing
363 // infrastructure.
Alex Light8f2c6d42017-04-10 16:27:35 -0700364 if (klass == nullptr) {
Alex Light42151c02017-04-20 15:54:25 -0700365 // Probably on RI. Clear the exception so we can continue but don't mark vmclassloader as
366 // initialized.
367 LOG(WARNING) << "Unable to find VMClassLoader class!";
368 jni_env->ExceptionClear();
Alex Light8f2c6d42017-04-10 16:27:35 -0700369 } else {
370 // GetMethodID is spec'd to cause the class to be initialized.
371 jni_env->GetMethodID(klass, "hashCode", "()I");
372 jni_env->DeleteLocalRef(klass);
Alex Light8f2c6d42017-04-10 16:27:35 -0700373 data->vm_class_loader_initialized = true;
374 }
Alex Lightb7edcda2017-04-27 13:20:31 -0700375 if (data->trace_stress) {
376 if (jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
377 JVMTI_EVENT_METHOD_ENTRY,
378 nullptr) != JVMTI_ERROR_NONE) {
379 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_ENTRY event!";
380 }
381 if (jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
382 JVMTI_EVENT_METHOD_EXIT,
383 nullptr) != JVMTI_ERROR_NONE) {
384 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_EXIT event!";
385 }
386 }
Alex Light8f2c6d42017-04-10 16:27:35 -0700387}
388
389extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
390 char* options,
391 void* reserved ATTRIBUTE_UNUSED) {
392 jvmtiEnv* jvmti = nullptr;
393 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
394 LOG(ERROR) << "Unable to get jvmti env.";
395 return 1;
396 }
397 StressData* data = nullptr;
398 if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
399 reinterpret_cast<unsigned char**>(&data))) {
400 LOG(ERROR) << "Unable to allocate data for stress test.";
401 return 1;
402 }
403 memset(data, 0, sizeof(StressData));
404 // Read the options into the static variables that hold them.
405 ReadOptions(data, options);
406 // Save the data
407 if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
408 LOG(ERROR) << "Unable to save stress test data.";
409 return 1;
410 }
411
412 // Just get all capabilities.
413 jvmtiCapabilities caps;
414 jvmti->GetPotentialCapabilities(&caps);
415 jvmti->AddCapabilities(&caps);
416
417 // Set callbacks.
418 jvmtiEventCallbacks cb;
419 memset(&cb, 0, sizeof(cb));
420 cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
Alex Light0af8cde2017-04-20 13:35:05 -0700421 cb.NativeMethodBind = doJvmtiMethodBind;
Alex Lightb7edcda2017-04-27 13:20:31 -0700422 cb.VMInit = PerformFinalSetupVMInit;
423 cb.MethodEntry = MethodEntryHook;
424 cb.MethodExit = MethodExitHook;
Alex Light8f2c6d42017-04-10 16:27:35 -0700425 if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
426 LOG(ERROR) << "Unable to set class file load hook cb!";
427 return 1;
428 }
429 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
Alex Light0af8cde2017-04-20 13:35:05 -0700430 JVMTI_EVENT_NATIVE_METHOD_BIND,
431 nullptr) != JVMTI_ERROR_NONE) {
432 LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
433 return 1;
434 }
435 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
Alex Light8f2c6d42017-04-10 16:27:35 -0700436 JVMTI_EVENT_VM_INIT,
437 nullptr) != JVMTI_ERROR_NONE) {
438 LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
439 return 1;
440 }
Alex Lightb7edcda2017-04-27 13:20:31 -0700441 if (data->redefine_stress) {
442 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
443 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
444 nullptr) != JVMTI_ERROR_NONE) {
445 LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
446 return 1;
447 }
Alex Light8f2c6d42017-04-10 16:27:35 -0700448 }
449 return 0;
450}
451
452} // namespace art