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