blob: 515a39126d9d780fabddfce6036bf6c8b9ea263b [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>
Alex Lightbad2f512017-06-14 11:33:55 -070021#include <memory>
Alex Light8f2c6d42017-04-10 16:27:35 -070022#include <stdio.h>
23#include <sstream>
Alex Lightb7edcda2017-04-27 13:20:31 -070024#include <strstream>
Alex Light8f2c6d42017-04-10 16:27:35 -070025
26#include "jvmti.h"
27#include "exec_utils.h"
28#include "utils.h"
29
30namespace art {
31
32// Should we do a 'full_rewrite' with this test?
33static constexpr bool kDoFullRewrite = true;
34
35struct StressData {
36 std::string dexter_cmd;
37 std::string out_temp_dex;
38 std::string in_temp_dex;
39 bool vm_class_loader_initialized;
Alex Lightb7edcda2017-04-27 13:20:31 -070040 bool trace_stress;
41 bool redefine_stress;
Alex Light8f2c6d42017-04-10 16:27:35 -070042};
43
44static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
45 std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
46 file.write(reinterpret_cast<const char*>(data), data_len);
47 file.flush();
48}
49
50static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
51 std::ifstream file(fname, std::ios::binary | std::ios::in);
52 file.seekg(0, std::ios::end);
53 size_t len = file.tellg();
54 data->resize(len);
55 file.seekg(0);
56 file.read(reinterpret_cast<char*>(data->data()), len);
57 return len != 0;
58}
59
60// TODO rewrite later.
61static bool DoExtractClassFromData(StressData* data,
62 const std::string& class_name,
63 jint in_len,
64 const unsigned char* in_data,
65 /*out*/std::vector<unsigned char>* dex) {
66 // Write the dex file into a temporary file.
67 WriteToFile(data->in_temp_dex, in_len, in_data);
68 // Clear out file so even if something suppresses the exit value we will still detect dexter
69 // failure.
70 WriteToFile(data->out_temp_dex, 0, nullptr);
71 // Have dexter do the extraction.
72 std::vector<std::string> args;
73 args.push_back(data->dexter_cmd);
74 if (kDoFullRewrite) {
75 args.push_back("-x");
76 args.push_back("full_rewrite");
77 }
78 args.push_back("-e");
79 args.push_back(class_name);
80 args.push_back("-o");
81 args.push_back(data->out_temp_dex);
82 args.push_back(data->in_temp_dex);
83 std::string error;
84 if (ExecAndReturnCode(args, &error) != 0) {
85 LOG(ERROR) << "unable to execute dexter: " << error;
86 return false;
87 }
88 return ReadIntoBuffer(data->out_temp_dex, dex);
89}
90
Alex Lightbad2f512017-06-14 11:33:55 -070091class ScopedThreadInfo {
92 public:
93 ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
94 : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
95 memset(&info_, 0, sizeof(info_));
96 if (thread == nullptr) {
97 info_.name = const_cast<char*>("<NULLPTR>");
98 } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
99 info_.name = const_cast<char*>("<UNKNOWN THREAD>");
100 } else {
101 free_name_ = true;
102 }
103 }
104
105 ~ScopedThreadInfo() {
106 if (free_name_) {
107 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
108 }
109 env_->DeleteLocalRef(info_.thread_group);
110 env_->DeleteLocalRef(info_.context_class_loader);
111 }
112
113 const char* GetName() const {
114 return info_.name;
115 }
116
117 private:
118 jvmtiEnv* jvmtienv_;
119 JNIEnv* env_;
120 bool free_name_;
121 jvmtiThreadInfo info_;
122};
123
124class ScopedClassInfo {
125 public:
126 ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c)
127 : jvmtienv_(jvmtienv),
128 class_(c),
129 name_(nullptr),
130 generic_(nullptr) {}
131
132 ~ScopedClassInfo() {
133 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
134 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
135 }
136
137 bool Init() {
138 return jvmtienv_->GetClassSignature(class_, &name_, &generic_) == JVMTI_ERROR_NONE;
139 }
140
141 jclass GetClass() const {
142 return class_;
143 }
144 const char* GetName() const {
145 return name_;
146 }
147 const char* GetGeneric() const {
148 return generic_;
149 }
150
151 private:
152 jvmtiEnv* jvmtienv_;
153 jclass class_;
154 char* name_;
155 char* generic_;
156};
157
158class ScopedMethodInfo {
159 public:
160 ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
161 : jvmtienv_(jvmtienv),
162 env_(env),
163 method_(m),
164 declaring_class_(nullptr),
165 class_info_(nullptr),
166 name_(nullptr),
167 signature_(nullptr),
168 generic_(nullptr) {}
169
170 ~ScopedMethodInfo() {
171 env_->DeleteLocalRef(declaring_class_);
172 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
173 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
174 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
175 }
176
177 bool Init() {
178 if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
179 return false;
180 }
181 class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
182 return class_info_->Init() &&
183 (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE);
184 }
185
186 const ScopedClassInfo& GetDeclaringClassInfo() const {
187 return *class_info_;
188 }
189
190 jclass GetDeclaringClass() const {
191 return declaring_class_;
192 }
193
194 const char* GetName() const {
195 return name_;
196 }
197
198 const char* GetSignature() const {
199 return signature_;
200 }
201
202 const char* GetGeneric() const {
203 return generic_;
204 }
205
206 private:
207 jvmtiEnv* jvmtienv_;
208 JNIEnv* env_;
209 jmethodID method_;
210 jclass declaring_class_;
211 std::unique_ptr<ScopedClassInfo> class_info_;
212 char* name_;
213 char* signature_;
214 char* generic_;
215
216 friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m);
217};
218
219std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) {
220 return os << *m;
221}
222
223std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) {
224 return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature();
225}
226
Alex Light0af8cde2017-04-20 13:35:05 -0700227static void doJvmtiMethodBind(jvmtiEnv* jvmtienv,
228 JNIEnv* env,
229 jthread thread,
230 jmethodID m,
231 void* address,
232 /*out*/void** out_address) {
233 *out_address = address;
Alex Lightbad2f512017-06-14 11:33:55 -0700234 ScopedThreadInfo thread_info(jvmtienv, env, thread);
235 ScopedMethodInfo method_info(jvmtienv, env, m);
236 if (!method_info.Init()) {
237 LOG(ERROR) << "Unable to get method info!";
Alex Light0af8cde2017-04-20 13:35:05 -0700238 return;
239 }
Alex Lightbad2f512017-06-14 11:33:55 -0700240 LOG(INFO) << "Loading native method \"" << method_info << "\". Thread is "
241 << thread_info.GetName();
Alex Light0af8cde2017-04-20 13:35:05 -0700242}
243
Alex Lightb7edcda2017-04-27 13:20:31 -0700244static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) {
245 jclass klass = jnienv->GetObjectClass(obj);
246 char *cname, *cgen;
247 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
248 LOG(ERROR) << "Unable to get class name!";
249 jnienv->DeleteLocalRef(klass);
250 return "<UNKNOWN>";
251 }
252 std::string name(cname);
253 if (name == "Ljava/lang/String;") {
254 jstring str = reinterpret_cast<jstring>(obj);
255 const char* val = jnienv->GetStringUTFChars(str, nullptr);
256 if (val == nullptr) {
257 name += " (unable to get value)";
258 } else {
259 std::ostringstream oss;
260 oss << name << " (value: \"" << val << "\")";
261 name = oss.str();
262 jnienv->ReleaseStringUTFChars(str, val);
263 }
264 }
265 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
266 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
267 jnienv->DeleteLocalRef(klass);
268 return name;
269}
270
271static std::string GetValOf(jvmtiEnv* env, JNIEnv* jnienv, std::string type, jvalue val) {
272 std::ostringstream oss;
273 switch (type[0]) {
274 case '[':
275 case 'L':
276 return val.l != nullptr ? GetName(env, jnienv, val.l) : "null";
277 case 'Z':
278 return val.z == JNI_TRUE ? "true" : "false";
279 case 'B':
280 oss << val.b;
281 return oss.str();
282 case 'C':
283 oss << val.c;
284 return oss.str();
285 case 'S':
286 oss << val.s;
287 return oss.str();
288 case 'I':
289 oss << val.i;
290 return oss.str();
291 case 'J':
292 oss << val.j;
293 return oss.str();
294 case 'F':
295 oss << val.f;
296 return oss.str();
297 case 'D':
298 oss << val.d;
299 return oss.str();
300 case 'V':
301 return "<void>";
302 default:
303 return "<ERROR Found type " + type + ">";
304 }
305}
306
307void JNICALL MethodExitHook(jvmtiEnv* jvmtienv,
308 JNIEnv* env,
309 jthread thread,
310 jmethodID m,
311 jboolean was_popped_by_exception,
312 jvalue val) {
Alex Lightbad2f512017-06-14 11:33:55 -0700313 ScopedThreadInfo info(jvmtienv, env, thread);
314 ScopedMethodInfo method_info(jvmtienv, env, m);
315 if (!method_info.Init()) {
316 LOG(ERROR) << "Unable to get method info!";
Alex Lightb7edcda2017-04-27 13:20:31 -0700317 return;
318 }
Alex Lightbad2f512017-06-14 11:33:55 -0700319 std::string type(method_info.GetSignature());
Alex Lightb7edcda2017-04-27 13:20:31 -0700320 type = type.substr(type.find(")") + 1);
321 std::string out_val(was_popped_by_exception ? "" : GetValOf(jvmtienv, env, type, val));
Alex Lightbad2f512017-06-14 11:33:55 -0700322 LOG(INFO) << "Leaving method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"."
323 << std::endl
Alex Lightb7edcda2017-04-27 13:20:31 -0700324 << " Cause: " << (was_popped_by_exception ? "exception" : "return ")
325 << out_val << ".";
Alex Lightb7edcda2017-04-27 13:20:31 -0700326}
327
328void JNICALL MethodEntryHook(jvmtiEnv* jvmtienv,
329 JNIEnv* env,
330 jthread thread,
331 jmethodID m) {
Alex Lightbad2f512017-06-14 11:33:55 -0700332 ScopedThreadInfo info(jvmtienv, env, thread);
333 ScopedMethodInfo method_info(jvmtienv, env, m);
334 if (!method_info.Init()) {
335 LOG(ERROR) << "Unable to get method info!";
Alex Lightb7edcda2017-04-27 13:20:31 -0700336 return;
337 }
Alex Lightbad2f512017-06-14 11:33:55 -0700338 LOG(INFO) << "Entering method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"";
Alex Lightb7edcda2017-04-27 13:20:31 -0700339}
340
Alex Light8f2c6d42017-04-10 16:27:35 -0700341// The hook we are using.
342void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
343 JNIEnv* jni_env ATTRIBUTE_UNUSED,
344 jclass class_being_redefined ATTRIBUTE_UNUSED,
345 jobject loader ATTRIBUTE_UNUSED,
346 const char* name,
347 jobject protection_domain ATTRIBUTE_UNUSED,
348 jint class_data_len,
349 const unsigned char* class_data,
350 jint* new_class_data_len,
351 unsigned char** new_class_data) {
352 std::vector<unsigned char> out;
353 std::string name_str(name);
354 // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
355 // classes).
356 std::replace(name_str.begin(), name_str.end(), '/', '.');
357 StressData* data = nullptr;
358 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
359 JVMTI_ERROR_NONE);
360 if (!data->vm_class_loader_initialized) {
361 LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
362 << "initialized. Transforming this class could cause spurious test failures.";
363 return;
364 } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
365 LOG(INFO) << "Extracted class: " << name;
366 unsigned char* new_data;
367 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
368 memcpy(new_data, out.data(), out.size());
369 *new_class_data_len = static_cast<jint>(out.size());
370 *new_class_data = new_data;
371 } else {
372 std::cerr << "Unable to extract class " << name_str << std::endl;
373 *new_class_data_len = 0;
374 *new_class_data = nullptr;
375 }
376}
377
Alex Lightb7edcda2017-04-27 13:20:31 -0700378static std::string AdvanceOption(const std::string& ops) {
379 return ops.substr(ops.find(',') + 1);
Alex Light8f2c6d42017-04-10 16:27:35 -0700380}
381
Alex Lightb7edcda2017-04-27 13:20:31 -0700382static bool HasNextOption(const std::string& ops) {
383 return ops.find(',') != std::string::npos;
384}
385
386static std::string GetOption(const std::string& in) {
387 return in.substr(0, in.find(','));
388}
389
390// Options are
391// jvmti-stress,[redefine,${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2},][trace]
392static void ReadOptions(StressData* data, char* options) {
393 std::string ops(options);
394 CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress";
395 do {
396 ops = AdvanceOption(ops);
397 std::string cur = GetOption(ops);
398 if (cur == "trace") {
399 data->trace_stress = true;
400 } else if (cur == "redefine") {
401 data->redefine_stress = true;
402 ops = AdvanceOption(ops);
403 data->dexter_cmd = GetOption(ops);
404 ops = AdvanceOption(ops);
405 data->in_temp_dex = GetOption(ops);
406 ops = AdvanceOption(ops);
407 data->out_temp_dex = GetOption(ops);
408 } else {
409 LOG(FATAL) << "Unknown option: " << GetOption(ops);
410 }
411 } while (HasNextOption(ops));
412}
413
414// Do final setup during the VMInit callback. By this time most things are all setup.
415static void JNICALL PerformFinalSetupVMInit(jvmtiEnv *jvmti_env,
416 JNIEnv* jni_env,
417 jthread thread ATTRIBUTE_UNUSED) {
Alex Light8f2c6d42017-04-10 16:27:35 -0700418 // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
419 // visibility but the class will be loaded behind the scenes.
420 LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
421 jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
Alex Lightb7edcda2017-04-27 13:20:31 -0700422 StressData* data = nullptr;
423 CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
424 JVMTI_ERROR_NONE);
425 // We need to make sure that VMClassLoader is initialized before we start redefining anything
426 // since it can give (non-fatal) error messages if it's initialized after we've redefined BCP
427 // classes. These error messages are expected and no problem but they will mess up our testing
428 // infrastructure.
Alex Light8f2c6d42017-04-10 16:27:35 -0700429 if (klass == nullptr) {
Alex Light42151c02017-04-20 15:54:25 -0700430 // Probably on RI. Clear the exception so we can continue but don't mark vmclassloader as
431 // initialized.
432 LOG(WARNING) << "Unable to find VMClassLoader class!";
433 jni_env->ExceptionClear();
Alex Light8f2c6d42017-04-10 16:27:35 -0700434 } else {
435 // GetMethodID is spec'd to cause the class to be initialized.
436 jni_env->GetMethodID(klass, "hashCode", "()I");
437 jni_env->DeleteLocalRef(klass);
Alex Light8f2c6d42017-04-10 16:27:35 -0700438 data->vm_class_loader_initialized = true;
439 }
Alex Lightb7edcda2017-04-27 13:20:31 -0700440 if (data->trace_stress) {
441 if (jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
442 JVMTI_EVENT_METHOD_ENTRY,
443 nullptr) != JVMTI_ERROR_NONE) {
444 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_ENTRY event!";
445 }
446 if (jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
447 JVMTI_EVENT_METHOD_EXIT,
448 nullptr) != JVMTI_ERROR_NONE) {
449 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_EXIT event!";
450 }
451 }
Alex Light8f2c6d42017-04-10 16:27:35 -0700452}
453
454extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
455 char* options,
456 void* reserved ATTRIBUTE_UNUSED) {
457 jvmtiEnv* jvmti = nullptr;
458 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
459 LOG(ERROR) << "Unable to get jvmti env.";
460 return 1;
461 }
462 StressData* data = nullptr;
463 if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
464 reinterpret_cast<unsigned char**>(&data))) {
465 LOG(ERROR) << "Unable to allocate data for stress test.";
466 return 1;
467 }
468 memset(data, 0, sizeof(StressData));
469 // Read the options into the static variables that hold them.
470 ReadOptions(data, options);
471 // Save the data
472 if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
473 LOG(ERROR) << "Unable to save stress test data.";
474 return 1;
475 }
476
477 // Just get all capabilities.
478 jvmtiCapabilities caps;
479 jvmti->GetPotentialCapabilities(&caps);
480 jvmti->AddCapabilities(&caps);
481
482 // Set callbacks.
483 jvmtiEventCallbacks cb;
484 memset(&cb, 0, sizeof(cb));
485 cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
Alex Light0af8cde2017-04-20 13:35:05 -0700486 cb.NativeMethodBind = doJvmtiMethodBind;
Alex Lightb7edcda2017-04-27 13:20:31 -0700487 cb.VMInit = PerformFinalSetupVMInit;
488 cb.MethodEntry = MethodEntryHook;
489 cb.MethodExit = MethodExitHook;
Alex Light8f2c6d42017-04-10 16:27:35 -0700490 if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
491 LOG(ERROR) << "Unable to set class file load hook cb!";
492 return 1;
493 }
494 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
Alex Light0af8cde2017-04-20 13:35:05 -0700495 JVMTI_EVENT_NATIVE_METHOD_BIND,
496 nullptr) != JVMTI_ERROR_NONE) {
497 LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
498 return 1;
499 }
500 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
Alex Light8f2c6d42017-04-10 16:27:35 -0700501 JVMTI_EVENT_VM_INIT,
502 nullptr) != JVMTI_ERROR_NONE) {
503 LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
504 return 1;
505 }
Alex Lightb7edcda2017-04-27 13:20:31 -0700506 if (data->redefine_stress) {
507 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
508 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
509 nullptr) != JVMTI_ERROR_NONE) {
510 LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
511 return 1;
512 }
Alex Light8f2c6d42017-04-10 16:27:35 -0700513 }
514 return 0;
515}
516
517} // namespace art