1 /*
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 <cstdio>
18 #include <fstream>
19 #include <iomanip>
20 #include <iostream>
21 #include <memory>
22 #include <sstream>
23 #include <strstream>
24
25 #include <jni.h>
26
27 #include "base/utils.h"
28 #include "jvmti.h"
29
30 #pragma clang diagnostic push
31
32 // Slicer's headers have code that triggers these warnings. b/65298177
33 #pragma clang diagnostic ignored "-Wunused-parameter"
34 #pragma clang diagnostic ignored "-Wsign-compare"
35
36 #include "slicer/code_ir.h"
37 #include "slicer/control_flow_graph.h"
38 #include "slicer/dex_ir.h"
39 #include "slicer/dex_ir_builder.h"
40 #include "slicer/instrumentation.h"
41 #include "slicer/reader.h"
42 #include "slicer/writer.h"
43
44 #pragma clang diagnostic pop
45
46 namespace art {
47
48 // Should we do a 'full_rewrite' with this test?
49 static constexpr bool kDoFullRewrite = true;
50
51 struct StressData {
52 bool vm_class_loader_initialized;
53 bool trace_stress;
54 bool redefine_stress;
55 bool field_stress;
56 bool step_stress;
57 };
58
DeleteLocalRef(JNIEnv * env,jobject obj)59 static void DeleteLocalRef(JNIEnv* env, jobject obj) {
60 if (obj != nullptr) {
61 env->DeleteLocalRef(obj);
62 }
63 }
64
DoExtractClassFromData(jvmtiEnv * env,const std::string & descriptor,jint in_len,const unsigned char * in_data,jint * out_len,unsigned char ** out_data)65 static bool DoExtractClassFromData(jvmtiEnv* env,
66 const std::string& descriptor,
67 jint in_len,
68 const unsigned char* in_data,
69 /*out*/jint* out_len,
70 /*out*/unsigned char** out_data) {
71 dex::Reader reader(in_data, in_len);
72 dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str());
73 if (class_idx != dex::kNoIndex) {
74 reader.CreateClassIr(class_idx);
75 } else {
76 LOG(ERROR) << "ERROR: Can't find class " << descriptor;
77 return false;
78 }
79 auto dex_ir = reader.GetIr();
80
81 if (kDoFullRewrite) {
82 for (auto& ir_method : dex_ir->encoded_methods) {
83 if (ir_method->code != nullptr) {
84 lir::CodeIr code_ir(ir_method.get(), dex_ir);
85 lir::ControlFlowGraph cfg_compact(&code_ir, false);
86 lir::ControlFlowGraph cfg_verbose(&code_ir, true);
87 code_ir.Assemble();
88 }
89 }
90 }
91 dex::Writer writer(dex_ir);
92
93 struct Allocator : public dex::Writer::Allocator {
94 explicit Allocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {}
95 void* Allocate(size_t size) override {
96 unsigned char* out = nullptr;
97 if (JVMTI_ERROR_NONE != jvmti_env_->Allocate(size, &out)) {
98 return nullptr;
99 } else {
100 return out;
101 }
102 }
103 void Free(void* ptr) override {
104 jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
105 }
106 private:
107 jvmtiEnv* jvmti_env_;
108 };
109 Allocator alloc(env);
110 size_t res_len;
111 unsigned char* res = writer.CreateImage(&alloc, &res_len);
112 if (res != nullptr) {
113 *out_data = res;
114 *out_len = res_len;
115 return true;
116 } else {
117 return false;
118 }
119 }
120
121 class ScopedThreadInfo {
122 public:
ScopedThreadInfo(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread)123 ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
124 : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
125 memset(&info_, 0, sizeof(info_));
126 if (thread == nullptr) {
127 info_.name = const_cast<char*>("<NULLPTR>");
128 } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
129 info_.name = const_cast<char*>("<UNKNOWN THREAD>");
130 } else {
131 free_name_ = true;
132 }
133 }
134
~ScopedThreadInfo()135 ~ScopedThreadInfo() {
136 if (free_name_) {
137 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
138 }
139 DeleteLocalRef(env_, info_.thread_group);
140 DeleteLocalRef(env_, info_.context_class_loader);
141 }
142
GetName() const143 const char* GetName() const {
144 return info_.name;
145 }
146
147 private:
148 jvmtiEnv* jvmtienv_;
149 JNIEnv* env_;
150 bool free_name_;
151 jvmtiThreadInfo info_;
152 };
153
154 class ScopedClassInfo {
155 public:
ScopedClassInfo(jvmtiEnv * jvmtienv,jclass c)156 ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c)
157 : jvmtienv_(jvmtienv),
158 class_(c),
159 name_(nullptr),
160 file_(nullptr),
161 debug_ext_(nullptr) {}
162
~ScopedClassInfo()163 ~ScopedClassInfo() {
164 if (class_ != nullptr) {
165 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
166 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_));
167 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_));
168 }
169 }
170
Init()171 bool Init() {
172 if (class_ == nullptr) {
173 name_ = const_cast<char*>("<NONE>");
174 return true;
175 } else {
176 jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_);
177 jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_);
178 return jvmtienv_->GetClassSignature(class_, &name_, nullptr) == JVMTI_ERROR_NONE &&
179 ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
180 ret1 != JVMTI_ERROR_INVALID_CLASS &&
181 ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
182 ret2 != JVMTI_ERROR_INVALID_CLASS;
183 }
184 }
185
GetClass() const186 jclass GetClass() const {
187 return class_;
188 }
GetName() const189 const char* GetName() const {
190 return name_;
191 }
GetSourceDebugExtension() const192 const char* GetSourceDebugExtension() const {
193 if (debug_ext_ == nullptr) {
194 return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
195 } else {
196 return debug_ext_;
197 }
198 }
GetSourceFileName() const199 const char* GetSourceFileName() const {
200 if (file_ == nullptr) {
201 return "<UNKNOWN_FILE>";
202 } else {
203 return file_;
204 }
205 }
206
207 private:
208 jvmtiEnv* jvmtienv_;
209 jclass class_;
210 char* name_;
211 char* file_;
212 char* debug_ext_;
213 };
214
215 class ScopedMethodInfo {
216 public:
ScopedMethodInfo(jvmtiEnv * jvmtienv,JNIEnv * env,jmethodID m)217 ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
218 : jvmtienv_(jvmtienv),
219 env_(env),
220 method_(m),
221 declaring_class_(nullptr),
222 class_info_(nullptr),
223 name_(nullptr),
224 signature_(nullptr),
225 first_line_(-1) {}
226
~ScopedMethodInfo()227 ~ScopedMethodInfo() {
228 DeleteLocalRef(env_, declaring_class_);
229 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
230 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
231 }
232
Init()233 bool Init() {
234 if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
235 return false;
236 }
237 class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
238 jint nlines;
239 jvmtiLineNumberEntry* lines;
240 jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines);
241 if (err == JVMTI_ERROR_NONE) {
242 if (nlines > 0) {
243 first_line_ = lines[0].line_number;
244 }
245 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines));
246 } else if (err != JVMTI_ERROR_ABSENT_INFORMATION &&
247 err != JVMTI_ERROR_NATIVE_METHOD) {
248 return false;
249 }
250 return class_info_->Init() &&
251 (jvmtienv_->GetMethodName(method_, &name_, &signature_, nullptr) == JVMTI_ERROR_NONE);
252 }
253
GetDeclaringClassInfo() const254 const ScopedClassInfo& GetDeclaringClassInfo() const {
255 return *class_info_;
256 }
257
GetDeclaringClass() const258 jclass GetDeclaringClass() const {
259 return declaring_class_;
260 }
261
GetName() const262 const char* GetName() const {
263 return name_;
264 }
265
GetSignature() const266 const char* GetSignature() const {
267 return signature_;
268 }
269
GetFirstLine() const270 jint GetFirstLine() const {
271 return first_line_;
272 }
273
274 private:
275 jvmtiEnv* jvmtienv_;
276 JNIEnv* env_;
277 jmethodID method_;
278 jclass declaring_class_;
279 std::unique_ptr<ScopedClassInfo> class_info_;
280 char* name_;
281 char* signature_;
282 jint first_line_;
283
284 friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m);
285 };
286
287 class ScopedFieldInfo {
288 public:
ScopedFieldInfo(jvmtiEnv * jvmtienv,jclass field_klass,jfieldID field)289 ScopedFieldInfo(jvmtiEnv* jvmtienv, jclass field_klass, jfieldID field)
290 : jvmtienv_(jvmtienv),
291 declaring_class_(field_klass),
292 field_(field),
293 class_info_(nullptr),
294 name_(nullptr),
295 type_(nullptr) {}
296
~ScopedFieldInfo()297 ~ScopedFieldInfo() {
298 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
299 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(type_));
300 }
301
Init()302 bool Init() {
303 class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
304 return class_info_->Init() &&
305 (jvmtienv_->GetFieldName(
306 declaring_class_, field_, &name_, &type_, nullptr) == JVMTI_ERROR_NONE);
307 }
308
GetDeclaringClassInfo() const309 const ScopedClassInfo& GetDeclaringClassInfo() const {
310 return *class_info_;
311 }
312
GetDeclaringClass() const313 jclass GetDeclaringClass() const {
314 return declaring_class_;
315 }
316
GetName() const317 const char* GetName() const {
318 return name_;
319 }
320
GetType() const321 const char* GetType() const {
322 return type_;
323 }
324
325 private:
326 jvmtiEnv* jvmtienv_;
327 jclass declaring_class_;
328 jfieldID field_;
329 std::unique_ptr<ScopedClassInfo> class_info_;
330 char* name_;
331 char* type_;
332
333 friend std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m);
334 };
335
operator <<(std::ostream & os,const ScopedFieldInfo * m)336 std::ostream& operator<<(std::ostream &os, const ScopedFieldInfo* m) {
337 return os << *m;
338 }
339
operator <<(std::ostream & os,ScopedFieldInfo const & m)340 std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m) {
341 return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName()
342 << ":" << m.GetType();
343 }
344
operator <<(std::ostream & os,const ScopedMethodInfo * m)345 std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) {
346 return os << *m;
347 }
348
operator <<(std::ostream & os,ScopedMethodInfo const & m)349 std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) {
350 return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature()
351 << " (source: " << m.GetDeclaringClassInfo().GetSourceFileName() << ":"
352 << m.GetFirstLine() << ")";
353 }
354
doJvmtiMethodBind(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID m,void * address,void ** out_address)355 static void doJvmtiMethodBind(jvmtiEnv* jvmtienv,
356 JNIEnv* env,
357 jthread thread,
358 jmethodID m,
359 void* address,
360 /*out*/void** out_address) {
361 *out_address = address;
362 ScopedThreadInfo thread_info(jvmtienv, env, thread);
363 ScopedMethodInfo method_info(jvmtienv, env, m);
364 if (!method_info.Init()) {
365 LOG(ERROR) << "Unable to get method info!";
366 return;
367 }
368 LOG(INFO) << "Loading native method \"" << method_info << "\". Thread is "
369 << thread_info.GetName();
370 }
371
GetName(jvmtiEnv * jvmtienv,JNIEnv * jnienv,jobject obj)372 static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) {
373 jclass klass = jnienv->GetObjectClass(obj);
374 char *cname, *cgen;
375 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
376 LOG(ERROR) << "Unable to get class name!";
377 DeleteLocalRef(jnienv, klass);
378 return "<UNKNOWN>";
379 }
380 std::string name(cname);
381 if (name == "Ljava/lang/String;") {
382 jstring str = reinterpret_cast<jstring>(obj);
383 const char* val = jnienv->GetStringUTFChars(str, nullptr);
384 if (val == nullptr) {
385 name += " (unable to get value)";
386 } else {
387 std::ostringstream oss;
388 oss << name << " (value: \"" << val << "\")";
389 name = oss.str();
390 jnienv->ReleaseStringUTFChars(str, val);
391 }
392 }
393 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
394 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
395 DeleteLocalRef(jnienv, klass);
396 return name;
397 }
398
GetValOf(jvmtiEnv * env,JNIEnv * jnienv,std::string type,jvalue val)399 static std::string GetValOf(jvmtiEnv* env, JNIEnv* jnienv, std::string type, jvalue val) {
400 std::ostringstream oss;
401 switch (type[0]) {
402 case '[':
403 case 'L':
404 return val.l != nullptr ? GetName(env, jnienv, val.l) : "null";
405 case 'Z':
406 return val.z == JNI_TRUE ? "true" : "false";
407 case 'B':
408 oss << val.b;
409 return oss.str();
410 case 'C':
411 oss << val.c;
412 return oss.str();
413 case 'S':
414 oss << val.s;
415 return oss.str();
416 case 'I':
417 oss << val.i;
418 return oss.str();
419 case 'J':
420 oss << val.j;
421 return oss.str();
422 case 'F':
423 oss << val.f;
424 return oss.str();
425 case 'D':
426 oss << val.d;
427 return oss.str();
428 case 'V':
429 return "<void>";
430 default:
431 return "<ERROR Found type " + type + ">";
432 }
433 }
434
FieldAccessHook(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID m,jlocation location,jclass field_klass,jobject object,jfieldID field)435 void JNICALL FieldAccessHook(jvmtiEnv* jvmtienv,
436 JNIEnv* env,
437 jthread thread,
438 jmethodID m,
439 jlocation location,
440 jclass field_klass,
441 jobject object,
442 jfieldID field) {
443 ScopedThreadInfo info(jvmtienv, env, thread);
444 ScopedMethodInfo method_info(jvmtienv, env, m);
445 ScopedFieldInfo field_info(jvmtienv, field_klass, field);
446 jclass oklass = (object != nullptr) ? env->GetObjectClass(object) : nullptr;
447 ScopedClassInfo obj_class_info(jvmtienv, oklass);
448 if (!method_info.Init() || !field_info.Init() || !obj_class_info.Init()) {
449 LOG(ERROR) << "Unable to get callback info!";
450 return;
451 }
452 LOG(INFO) << "ACCESS field \"" << field_info << "\" on object of "
453 << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info
454 << "\" at location 0x" << std::hex << location << ". Thread is \""
455 << info.GetName() << "\".";
456 DeleteLocalRef(env, oklass);
457 }
458
PrintJValue(jvmtiEnv * jvmtienv,JNIEnv * env,char type,jvalue new_value)459 static std::string PrintJValue(jvmtiEnv* jvmtienv, JNIEnv* env, char type, jvalue new_value) {
460 std::ostringstream oss;
461 switch (type) {
462 case 'L': {
463 jobject nv = new_value.l;
464 if (nv == nullptr) {
465 oss << "\"null\"";
466 } else {
467 jclass nv_klass = env->GetObjectClass(nv);
468 ScopedClassInfo nv_class_info(jvmtienv, nv_klass);
469 if (!nv_class_info.Init()) {
470 oss << "with unknown type";
471 } else {
472 oss << "of type \"" << nv_class_info.GetName() << "\"";
473 }
474 DeleteLocalRef(env, nv_klass);
475 }
476 break;
477 }
478 case 'Z': {
479 if (new_value.z) {
480 oss << "true";
481 } else {
482 oss << "false";
483 }
484 break;
485 }
486 #define SEND_VALUE(chr, sym, type) \
487 case chr: { \
488 oss << static_cast<type>(new_value.sym); \
489 break; \
490 }
491 SEND_VALUE('B', b, int8_t);
492 SEND_VALUE('C', c, uint16_t);
493 SEND_VALUE('S', s, int16_t);
494 SEND_VALUE('I', i, int32_t);
495 SEND_VALUE('J', j, int64_t);
496 SEND_VALUE('F', f, float);
497 SEND_VALUE('D', d, double);
498 #undef SEND_VALUE
499 }
500 return oss.str();
501 }
502
FieldModificationHook(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID m,jlocation location,jclass field_klass,jobject object,jfieldID field,char type,jvalue new_value)503 void JNICALL FieldModificationHook(jvmtiEnv* jvmtienv,
504 JNIEnv* env,
505 jthread thread,
506 jmethodID m,
507 jlocation location,
508 jclass field_klass,
509 jobject object,
510 jfieldID field,
511 char type,
512 jvalue new_value) {
513 ScopedThreadInfo info(jvmtienv, env, thread);
514 ScopedMethodInfo method_info(jvmtienv, env, m);
515 ScopedFieldInfo field_info(jvmtienv, field_klass, field);
516 jclass oklass = (object != nullptr) ? env->GetObjectClass(object) : nullptr;
517 ScopedClassInfo obj_class_info(jvmtienv, oklass);
518 if (!method_info.Init() || !field_info.Init() || !obj_class_info.Init()) {
519 LOG(ERROR) << "Unable to get callback info!";
520 return;
521 }
522 LOG(INFO) << "MODIFY field \"" << field_info << "\" on object of "
523 << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info
524 << "\" at location 0x" << std::hex << location << std::dec << ". New value is "
525 << PrintJValue(jvmtienv, env, type, new_value) << ". Thread is \""
526 << info.GetName() << "\".";
527 DeleteLocalRef(env, oklass);
528 }
MethodExitHook(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID m,jboolean was_popped_by_exception,jvalue val)529 void JNICALL MethodExitHook(jvmtiEnv* jvmtienv,
530 JNIEnv* env,
531 jthread thread,
532 jmethodID m,
533 jboolean was_popped_by_exception,
534 jvalue val) {
535 ScopedThreadInfo info(jvmtienv, env, thread);
536 ScopedMethodInfo method_info(jvmtienv, env, m);
537 if (!method_info.Init()) {
538 LOG(ERROR) << "Unable to get method info!";
539 return;
540 }
541 std::string type(method_info.GetSignature());
542 type = type.substr(type.find(')') + 1);
543 std::string out_val(was_popped_by_exception ? "" : GetValOf(jvmtienv, env, type, val));
544 LOG(INFO) << "Leaving method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"."
545 << std::endl
546 << " Cause: " << (was_popped_by_exception ? "exception" : "return ")
547 << out_val << ".";
548 }
549
MethodEntryHook(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID m)550 void JNICALL MethodEntryHook(jvmtiEnv* jvmtienv,
551 JNIEnv* env,
552 jthread thread,
553 jmethodID m) {
554 ScopedThreadInfo info(jvmtienv, env, thread);
555 ScopedMethodInfo method_info(jvmtienv, env, m);
556 if (!method_info.Init()) {
557 LOG(ERROR) << "Unable to get method info!";
558 return;
559 }
560 LOG(INFO) << "Entering method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"";
561 }
562
ClassPrepareHook(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jclass klass)563 void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv,
564 JNIEnv* env,
565 jthread thread,
566 jclass klass) {
567 StressData* data = nullptr;
568 CHECK_EQ(jvmtienv->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
569 JVMTI_ERROR_NONE);
570 if (data->field_stress) {
571 jint nfields;
572 jfieldID* fields;
573 if (jvmtienv->GetClassFields(klass, &nfields, &fields) != JVMTI_ERROR_NONE) {
574 LOG(ERROR) << "Unable to get a classes fields!";
575 return;
576 }
577 for (jint i = 0; i < nfields; i++) {
578 jfieldID f = fields[i];
579 // Ignore errors
580 jvmtienv->SetFieldAccessWatch(klass, f);
581 jvmtienv->SetFieldModificationWatch(klass, f);
582 }
583 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fields));
584 }
585 if (data->trace_stress) {
586 ScopedThreadInfo info(jvmtienv, env, thread);
587 ScopedClassInfo class_info(jvmtienv, klass);
588 if (!class_info.Init()) {
589 LOG(ERROR) << "Unable to get class info!";
590 return;
591 }
592 LOG(INFO) << "Prepared class \"" << class_info.GetName() << "\". Thread is \""
593 << info.GetName() << "\"";
594 }
595 }
596
SingleStepHook(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID method,jlocation location)597 void JNICALL SingleStepHook(jvmtiEnv* jvmtienv,
598 JNIEnv* env,
599 jthread thread,
600 jmethodID method,
601 jlocation location) {
602 ScopedThreadInfo info(jvmtienv, env, thread);
603 ScopedMethodInfo method_info(jvmtienv, env, method);
604 if (!method_info.Init()) {
605 LOG(ERROR) << "Unable to get method info!";
606 return;
607 }
608 LOG(INFO) << "Single step at location: 0x" << std::setw(8) << std::setfill('0') << std::hex
609 << location << " in method " << method_info << " thread: " << info.GetName();
610 }
611
612 // The hook we are using.
ClassFileLoadHookSecretNoOp(jvmtiEnv * jvmti,JNIEnv * jni_env ATTRIBUTE_UNUSED,jclass class_being_redefined ATTRIBUTE_UNUSED,jobject loader ATTRIBUTE_UNUSED,const char * name,jobject protection_domain ATTRIBUTE_UNUSED,jint class_data_len,const unsigned char * class_data,jint * new_class_data_len,unsigned char ** new_class_data)613 void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
614 JNIEnv* jni_env ATTRIBUTE_UNUSED,
615 jclass class_being_redefined ATTRIBUTE_UNUSED,
616 jobject loader ATTRIBUTE_UNUSED,
617 const char* name,
618 jobject protection_domain ATTRIBUTE_UNUSED,
619 jint class_data_len,
620 const unsigned char* class_data,
621 jint* new_class_data_len,
622 unsigned char** new_class_data) {
623 std::vector<unsigned char> out;
624 // Make the jvmti semi-descriptor into the full descriptor.
625 std::string name_str("L");
626 name_str += name;
627 name_str += ";";
628 StressData* data = nullptr;
629 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
630 JVMTI_ERROR_NONE);
631 if (!data->vm_class_loader_initialized) {
632 LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
633 << "initialized. Transforming this class could cause spurious test failures.";
634 return;
635 } else if (DoExtractClassFromData(jvmti, name_str, class_data_len, class_data,
636 /*out*/ new_class_data_len, /*out*/ new_class_data)) {
637 LOG(INFO) << "Extracted class: " << name;
638 } else {
639 std::cerr << "Unable to extract class " << name << std::endl;
640 *new_class_data_len = 0;
641 *new_class_data = nullptr;
642 }
643 }
644
AdvanceOption(const std::string & ops)645 static std::string AdvanceOption(const std::string& ops) {
646 return ops.substr(ops.find(',') + 1);
647 }
648
HasNextOption(const std::string & ops)649 static bool HasNextOption(const std::string& ops) {
650 return ops.find(',') != std::string::npos;
651 }
652
GetOption(const std::string & in)653 static std::string GetOption(const std::string& in) {
654 return in.substr(0, in.find(','));
655 }
656
657 // Options are
658 // jvmti-stress,[redefine,][trace,][field]
ReadOptions(StressData * data,char * options)659 static void ReadOptions(StressData* data, char* options) {
660 std::string ops(options);
661 CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress";
662 do {
663 ops = AdvanceOption(ops);
664 std::string cur = GetOption(ops);
665 if (cur == "trace") {
666 data->trace_stress = true;
667 } else if (cur == "step") {
668 data->step_stress = true;
669 } else if (cur == "field") {
670 data->field_stress = true;
671 } else if (cur == "redefine") {
672 data->redefine_stress = true;
673 } else {
674 LOG(FATAL) << "Unknown option: " << GetOption(ops);
675 }
676 } while (HasNextOption(ops));
677 }
678
679 // Do final setup during the VMInit callback. By this time most things are all setup.
PerformFinalSetupVMInit(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread ATTRIBUTE_UNUSED)680 static void JNICALL PerformFinalSetupVMInit(jvmtiEnv *jvmti_env,
681 JNIEnv* jni_env,
682 jthread thread ATTRIBUTE_UNUSED) {
683 // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
684 // visibility but the class will be loaded behind the scenes.
685 LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
686 jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
687 StressData* data = nullptr;
688 CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
689 JVMTI_ERROR_NONE);
690 // We need to make sure that VMClassLoader is initialized before we start redefining anything
691 // since it can give (non-fatal) error messages if it's initialized after we've redefined BCP
692 // classes. These error messages are expected and no problem but they will mess up our testing
693 // infrastructure.
694 if (klass == nullptr) {
695 // Probably on RI. Clear the exception so we can continue but don't mark vmclassloader as
696 // initialized.
697 LOG(WARNING) << "Unable to find VMClassLoader class!";
698 jni_env->ExceptionClear();
699 } else {
700 // GetMethodID is spec'd to cause the class to be initialized.
701 jni_env->GetMethodID(klass, "hashCode", "()I");
702 DeleteLocalRef(jni_env, klass);
703 data->vm_class_loader_initialized = true;
704 }
705 }
706
WatchAllFields(JavaVM * vm,jvmtiEnv * jvmti)707 static bool WatchAllFields(JavaVM* vm, jvmtiEnv* jvmti) {
708 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
709 JVMTI_EVENT_CLASS_PREPARE,
710 nullptr) != JVMTI_ERROR_NONE) {
711 LOG(ERROR) << "Couldn't set prepare event!";
712 return false;
713 }
714 // TODO We really shouldn't need to do this step here.
715 jint nklass;
716 jclass* klasses;
717 if (jvmti->GetLoadedClasses(&nklass, &klasses) != JVMTI_ERROR_NONE) {
718 LOG(WARNING) << "Couldn't get loaded classes! Ignoring.";
719 return true;
720 }
721 JNIEnv* jni = nullptr;
722 if (vm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6)) {
723 LOG(ERROR) << "Unable to get jni env. Ignoring and potentially leaking jobjects.";
724 return false;
725 }
726 for (jint i = 0; i < nklass; i++) {
727 jclass k = klasses[i];
728 ScopedClassInfo sci(jvmti, k);
729 if (sci.Init()) {
730 LOG(INFO) << "NOTE: class " << sci.GetName() << " already loaded.";
731 }
732 jint nfields;
733 jfieldID* fields;
734 jvmtiError err = jvmti->GetClassFields(k, &nfields, &fields);
735 if (err == JVMTI_ERROR_NONE) {
736 for (jint j = 0; j < nfields; j++) {
737 jfieldID f = fields[j];
738 if (jvmti->SetFieldModificationWatch(k, f) != JVMTI_ERROR_NONE ||
739 jvmti->SetFieldAccessWatch(k, f) != JVMTI_ERROR_NONE) {
740 LOG(ERROR) << "Unable to set watches on a field.";
741 return false;
742 }
743 }
744 } else if (err != JVMTI_ERROR_CLASS_NOT_PREPARED) {
745 LOG(ERROR) << "Unexpected error getting class fields!";
746 return false;
747 }
748 jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
749 DeleteLocalRef(jni, k);
750 }
751 jvmti->Deallocate(reinterpret_cast<unsigned char*>(klasses));
752 return true;
753 }
754
Agent_OnLoad(JavaVM * vm,char * options,void * reserved ATTRIBUTE_UNUSED)755 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
756 char* options,
757 void* reserved ATTRIBUTE_UNUSED) {
758 jvmtiEnv* jvmti = nullptr;
759 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
760 LOG(ERROR) << "Unable to get jvmti env.";
761 return 1;
762 }
763 StressData* data = nullptr;
764 if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
765 reinterpret_cast<unsigned char**>(&data))) {
766 LOG(ERROR) << "Unable to allocate data for stress test.";
767 return 1;
768 }
769 memset(data, 0, sizeof(StressData));
770 // Read the options into the static variables that hold them.
771 ReadOptions(data, options);
772 // Save the data
773 if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
774 LOG(ERROR) << "Unable to save stress test data.";
775 return 1;
776 }
777
778 // Just get all capabilities.
779 jvmtiCapabilities caps = {
780 .can_tag_objects = 0,
781 .can_generate_field_modification_events = 1,
782 .can_generate_field_access_events = 1,
783 .can_get_bytecodes = 0,
784 .can_get_synthetic_attribute = 0,
785 .can_get_owned_monitor_info = 0,
786 .can_get_current_contended_monitor = 0,
787 .can_get_monitor_info = 0,
788 .can_pop_frame = 0,
789 .can_redefine_classes = 1,
790 .can_signal_thread = 0,
791 .can_get_source_file_name = 1,
792 .can_get_line_numbers = 1,
793 .can_get_source_debug_extension = 1,
794 .can_access_local_variables = 0,
795 .can_maintain_original_method_order = 0,
796 .can_generate_single_step_events = 1,
797 .can_generate_exception_events = 0,
798 .can_generate_frame_pop_events = 0,
799 .can_generate_breakpoint_events = 0,
800 .can_suspend = 0,
801 .can_redefine_any_class = 0,
802 .can_get_current_thread_cpu_time = 0,
803 .can_get_thread_cpu_time = 0,
804 .can_generate_method_entry_events = 1,
805 .can_generate_method_exit_events = 1,
806 .can_generate_all_class_hook_events = 0,
807 .can_generate_compiled_method_load_events = 0,
808 .can_generate_monitor_events = 0,
809 .can_generate_vm_object_alloc_events = 0,
810 .can_generate_native_method_bind_events = 1,
811 .can_generate_garbage_collection_events = 0,
812 .can_generate_object_free_events = 0,
813 .can_force_early_return = 0,
814 .can_get_owned_monitor_stack_depth_info = 0,
815 .can_get_constant_pool = 0,
816 .can_set_native_method_prefix = 0,
817 .can_retransform_classes = 1,
818 .can_retransform_any_class = 0,
819 .can_generate_resource_exhaustion_heap_events = 0,
820 .can_generate_resource_exhaustion_threads_events = 0,
821 };
822 jvmti->AddCapabilities(&caps);
823
824 // Set callbacks.
825 jvmtiEventCallbacks cb;
826 memset(&cb, 0, sizeof(cb));
827 cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
828 cb.NativeMethodBind = doJvmtiMethodBind;
829 cb.VMInit = PerformFinalSetupVMInit;
830 cb.MethodEntry = MethodEntryHook;
831 cb.MethodExit = MethodExitHook;
832 cb.FieldAccess = FieldAccessHook;
833 cb.FieldModification = FieldModificationHook;
834 cb.ClassPrepare = ClassPrepareHook;
835 cb.SingleStep = SingleStepHook;
836 if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
837 LOG(ERROR) << "Unable to set class file load hook cb!";
838 return 1;
839 }
840 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
841 JVMTI_EVENT_VM_INIT,
842 nullptr) != JVMTI_ERROR_NONE) {
843 LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
844 return 1;
845 }
846 if (data->redefine_stress) {
847 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
848 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
849 nullptr) != JVMTI_ERROR_NONE) {
850 LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
851 return 1;
852 }
853 }
854 if (data->trace_stress) {
855 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
856 JVMTI_EVENT_CLASS_PREPARE,
857 nullptr) != JVMTI_ERROR_NONE) {
858 LOG(ERROR) << "Unable to enable CLASS_PREPARE event!";
859 return 1;
860 }
861 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
862 JVMTI_EVENT_NATIVE_METHOD_BIND,
863 nullptr) != JVMTI_ERROR_NONE) {
864 LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
865 return 1;
866 }
867 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
868 JVMTI_EVENT_METHOD_ENTRY,
869 nullptr) != JVMTI_ERROR_NONE) {
870 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_ENTRY event!";
871 return 1;
872 }
873 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
874 JVMTI_EVENT_METHOD_EXIT,
875 nullptr) != JVMTI_ERROR_NONE) {
876 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_EXIT event!";
877 return 1;
878 }
879 }
880 if (data->field_stress) {
881 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
882 JVMTI_EVENT_FIELD_MODIFICATION,
883 nullptr) != JVMTI_ERROR_NONE) {
884 LOG(ERROR) << "Unable to enable FIELD_MODIFICATION event!";
885 return 1;
886 }
887 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
888 JVMTI_EVENT_FIELD_ACCESS,
889 nullptr) != JVMTI_ERROR_NONE) {
890 LOG(ERROR) << "Unable to enable FIELD_ACCESS event!";
891 return 1;
892 }
893 if (!WatchAllFields(vm, jvmti)) {
894 return 1;
895 }
896 }
897 if (data->step_stress) {
898 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
899 JVMTI_EVENT_SINGLE_STEP,
900 nullptr) != JVMTI_ERROR_NONE) {
901 return 1;
902 }
903 }
904 return 0;
905 }
906
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)907 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
908 return Agent_OnLoad(vm, options, reserved);
909 }
910
911 } // namespace art
912