• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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