Home | History | Annotate | Download | only in ti-stress
      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 
     59 static void DeleteLocalRef(JNIEnv* env, jobject obj) {
     60   if (obj != nullptr) {
     61     env->DeleteLocalRef(obj);
     62   }
     63 }
     64 
     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:
    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 
    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 
    143   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:
    156   ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c)
    157       : jvmtienv_(jvmtienv),
    158         class_(c),
    159         name_(nullptr),
    160         file_(nullptr),
    161         debug_ext_(nullptr) {}
    162 
    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 
    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 
    186   jclass GetClass() const {
    187     return class_;
    188   }
    189   const char* GetName() const {
    190     return name_;
    191   }
    192   const char* GetSourceDebugExtension() const {
    193     if (debug_ext_ == nullptr) {
    194       return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
    195     } else {
    196       return debug_ext_;
    197     }
    198   }
    199   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:
    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 
    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 
    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 
    254   const ScopedClassInfo& GetDeclaringClassInfo() const {
    255     return *class_info_;
    256   }
    257 
    258   jclass GetDeclaringClass() const {
    259     return declaring_class_;
    260   }
    261 
    262   const char* GetName() const {
    263     return name_;
    264   }
    265 
    266   const char* GetSignature() const {
    267     return signature_;
    268   }
    269 
    270   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:
    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 
    297   ~ScopedFieldInfo() {
    298     jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
    299     jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(type_));
    300   }
    301 
    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 
    309   const ScopedClassInfo& GetDeclaringClassInfo() const {
    310     return *class_info_;
    311   }
    312 
    313   jclass GetDeclaringClass() const {
    314     return declaring_class_;
    315   }
    316 
    317   const char* GetName() const {
    318     return name_;
    319   }
    320 
    321   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 
    336 std::ostream& operator<<(std::ostream &os, const ScopedFieldInfo* m) {
    337   return os << *m;
    338 }
    339 
    340 std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m) {
    341   return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName()
    342             << ":" << m.GetType();
    343 }
    344 
    345 std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) {
    346   return os << *m;
    347 }
    348 
    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 
    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 
    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 
    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 
    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 
    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 
    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 }
    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 
    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 
    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 
    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.
    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 
    645 static std::string AdvanceOption(const std::string& ops) {
    646   return ops.substr(ops.find(',') + 1);
    647 }
    648 
    649 static bool HasNextOption(const std::string& ops) {
    650   return ops.find(',') != std::string::npos;
    651 }
    652 
    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]
    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.
    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 
    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 
    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 
    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