Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  stress.cc

  Sprache: C
 

/*
 * Copyright 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <cstdio>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <sstream>

#include <jni.h>

#include "base/utils.h"
#include "jvmti.h"

#pragma clang diagnostic push

// Slicer's headers have code that triggers these warnings. b/65298177
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wsign-compare"

#include "slicer/code_ir.h"
#include "slicer/control_flow_graph.h"
#include "slicer/dex_ir.h"
#include "slicer/dex_ir_builder.h"
#include "slicer/instrumentation.h"
#include "slicer/reader.h"
#include "slicer/writer.h"

#pragma clang diagnostic pop

namespace art {

// Should we do a 'full_rewrite' with this test?
static constexpr bool kDoFullRewrite = true;

struct StressData {
  bool vm_class_loader_initialized;
  bool trace_stress;
  bool redefine_stress;
  bool field_stress;
  bool step_stress;
};

static void DeleteLocalRef(JNIEnv* env, jobject obj) {
  if (obj != nullptr) {
    env->DeleteLocalRef(obj);
  }
}

static bool DoExtractClassFromData(jvmtiEnv* env,
                                   const std::string& descriptor,
                                   jint in_len,
                                   const unsigned char* in_data,
                                   /*out*/jint* out_len,
                                   /*out*/unsigned char** out_data) {
  dex::Reader reader(in_data, in_len);
  dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str());
  if (class_idx != dex::kNoIndex) {
    reader.CreateClassIr(class_idx);
  } else {
    LOG(ERROR) << "ERROR: Can't find class " << descriptor;
    return false;
  }
  auto dex_ir = reader.GetIr();

  if (kDoFullRewrite) {
    for (auto& ir_method : dex_ir->encoded_methods) {
      if (ir_method->code != nullptr) {
        lir::CodeIr code_ir(ir_method.get(), dex_ir);
        lir::ControlFlowGraph cfg_compact(&code_ir, false);
        lir::ControlFlowGraph cfg_verbose(&code_ir, true);
        code_ir.Assemble();
      }
    }
  }
  dex::Writer writer(dex_ir);

  struct Allocator : public dex::Writer::Allocator {
    explicit Allocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {}
    void* Allocate(size_t size) override {
      unsigned char* out = nullptr;
      if (JVMTI_ERROR_NONE != jvmti_env_->Allocate(size, &out)) {
        return nullptr;
      } else {
        return out;
      }
    }
    void Free(void* ptr) override {
      jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
    }
   private:
    jvmtiEnv* jvmti_env_;
  };
  Allocator alloc(env);
  size_t res_len;
  unsigned char* res = writer.CreateImage(&alloc, &res_len);
  if (res != nullptr) {
    *out_data = res;
    *out_len = res_len;
    return true;
  } else {
    return false;
  }
}

class ScopedThreadInfo {
 public:
  ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
      : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
    memset(&info_, 0sizeof(info_));
    if (thread == nullptr) {
      info_.name = const_cast<char*>("<NULLPTR>");
    } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
      info_.name = const_cast<char*>("<UNKNOWN THREAD>");
    } else {
      free_name_ = true;
    }
  }

  ~ScopedThreadInfo() {
    if (free_name_) {
      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
    }
    DeleteLocalRef(env_, info_.thread_group);
    DeleteLocalRef(env_, info_.context_class_loader);
  }

  const char* GetName() const {
    return info_.name;
  }

 private:
  jvmtiEnv* jvmtienv_;
  JNIEnv* env_;
  bool free_name_;
  jvmtiThreadInfo info_;
};

class ScopedClassInfo {
 public:
  ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c)
      : jvmtienv_(jvmtienv),
        class_(c),
        name_(nullptr),
        file_(nullptr),
        debug_ext_(nullptr) {}

  ~ScopedClassInfo() {
    if (class_ != nullptr) {
      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_));
      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_));
    }
  }

  bool Init() {
    if (class_ == nullptr) {
      name_ = const_cast<char*>("<NONE>");
      return true;
    } else {
      jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_);
      jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_);
      return jvmtienv_->GetClassSignature(class_, &name_, nullptr) == JVMTI_ERROR_NONE &&
          ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
          ret1 != JVMTI_ERROR_INVALID_CLASS &&
          ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
          ret2 != JVMTI_ERROR_INVALID_CLASS;
    }
  }

  jclass GetClass() const {
    return class_;
  }
  const char* GetName() const {
    return name_;
  }
  const char* GetSourceDebugExtension() const {
    if (debug_ext_ == nullptr) {
      return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
    } else {
      return debug_ext_;
    }
  }
  const char* GetSourceFileName() const {
    if (file_ == nullptr) {
      return "<UNKNOWN_FILE>";
    } else {
      return file_;
    }
  }

 private:
  jvmtiEnv* jvmtienv_;
  jclass class_;
  char* name_;
  char* file_;
  char* debug_ext_;
};

class ScopedMethodInfo {
 public:
  ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
      : jvmtienv_(jvmtienv),
        env_(env),
        method_(m),
        declaring_class_(nullptr),
        class_info_(nullptr),
        name_(nullptr),
        signature_(nullptr),
        first_line_(-1) {}

  ~ScopedMethodInfo() {
    DeleteLocalRef(env_, declaring_class_);
    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
  }

  bool Init() {
    if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
      return false;
    }
    class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
    jint nlines;
    jvmtiLineNumberEntry* lines;
    jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines);
    if (err == JVMTI_ERROR_NONE) {
      if (nlines > 0) {
        first_line_ = lines[0].line_number;
      }
      jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines));
    } else if (err != JVMTI_ERROR_ABSENT_INFORMATION &&
               err != JVMTI_ERROR_NATIVE_METHOD) {
      return false;
    }
    return class_info_->Init() &&
        (jvmtienv_->GetMethodName(method_, &name_, &signature_, nullptr) == JVMTI_ERROR_NONE);
  }

  const ScopedClassInfo& GetDeclaringClassInfo() const {
    return *class_info_;
  }

  jclass GetDeclaringClass() const {
    return declaring_class_;
  }

  const char* GetName() const {
    return name_;
  }

  const char* GetSignature() const {
    return signature_;
  }

  jint GetFirstLine() const {
    return first_line_;
  }

 private:
  jvmtiEnv* jvmtienv_;
  JNIEnv* env_;
  jmethodID method_;
  jclass declaring_class_;
  std::unique_ptr<ScopedClassInfo> class_info_;
  char* name_;
  char* signature_;
  jint first_line_;

  friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m);
};

class ScopedFieldInfo {
 public:
  ScopedFieldInfo(jvmtiEnv* jvmtienv, jclass field_klass, jfieldID field)
      : jvmtienv_(jvmtienv),
        declaring_class_(field_klass),
        field_(field),
        class_info_(nullptr),
        name_(nullptr),
        type_(nullptr) {}

  ~ScopedFieldInfo() {
    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
    jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(type_));
  }

  bool Init() {
    class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
    return class_info_->Init() &&
        (jvmtienv_->GetFieldName(
            declaring_class_, field_, &name_, &type_, nullptr) == JVMTI_ERROR_NONE);
  }

  const ScopedClassInfo& GetDeclaringClassInfo() const {
    return *class_info_;
  }

  jclass GetDeclaringClass() const {
    return declaring_class_;
  }

  const char* GetName() const {
    return name_;
  }

  const char* GetType() const {
    return type_;
  }

 private:
  jvmtiEnv* jvmtienv_;
  jclass declaring_class_;
  jfieldID field_;
  std::unique_ptr<ScopedClassInfo> class_info_;
  char* name_;
  char* type_;

  friend std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m);
};

std::ostream& operator<<(std::ostream &os, const ScopedFieldInfo* m) {
  return os << *m;
}

std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m) {
  return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName()
            << ":" << m.GetType();
}

std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) {
  return os << *m;
}

std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) {
  return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature()
            << " (source: " << m.GetDeclaringClassInfo().GetSourceFileName() << ":"
            << m.GetFirstLine() << ")";
}

static void doJvmtiMethodBind(jvmtiEnv* jvmtienv,
                              JNIEnv* env,
                              jthread thread,
                              jmethodID m,
                              void* address,
                              /*out*/void** out_address) {
  *out_address = address;
  ScopedThreadInfo thread_info(jvmtienv, env, thread);
  ScopedMethodInfo method_info(jvmtienv, env, m);
  if (!method_info.Init()) {
    LOG(ERROR) << "Unable to get method info!";
    return;
  }
  LOG(INFO) << "Loading native method \"" << method_info << "\". Thread is "
            << thread_info.GetName();
}

static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) {
  jclass klass = jnienv->GetObjectClass(obj);
  char *cname, *cgen;
  if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
    LOG(ERROR) << "Unable to get class name!";
    DeleteLocalRef(jnienv, klass);
    return "<UNKNOWN>";
  }
  std::string name(cname);
  if (name == "Ljava/lang/String;") {
    jstring str = reinterpret_cast<jstring>(obj);
    const char* val = jnienv->GetStringUTFChars(str, nullptr);
    if (val == nullptr) {
      name += " (unable to get value)";
    } else {
      std::ostringstream oss;
      oss << name << " (value: \"" << val << "\")";
      name = oss.str();
      jnienv->ReleaseStringUTFChars(str, val);
    }
  }
  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
  DeleteLocalRef(jnienv, klass);
  return name;
}

static std::string GetValOf(jvmtiEnv* env, JNIEnv* jnienv, std::string type, jvalue val) {
  std::ostringstream oss;
  switch (type[0]) {
    case '[':
    case 'L':
      return val.l != nullptr ? GetName(env, jnienv, val.l) : "null";
    case 'Z':
      return val.z == JNI_TRUE ? "true" : "false";
    case 'B':
      oss << val.b;
      return oss.str();
    case 'C':
      oss << val.c;
      return oss.str();
    case 'S':
      oss << val.s;
      return oss.str();
    case 'I':
      oss << val.i;
      return oss.str();
    case 'J':
      oss << val.j;
      return oss.str();
    case 'F':
      oss << val.f;
      return oss.str();
    case 'D':
      oss << val.d;
      return oss.str();
    case 'V':
      return "<void>";
    default:
      return "<ERROR Found type " + type + ">";
  }
}

void JNICALL FieldAccessHook(jvmtiEnv* jvmtienv,
                             JNIEnv* env,
                             jthread thread,
                             jmethodID m,
                             jlocation location,
                             jclass field_klass,
                             jobject object,
                             jfieldID field) {
  ScopedThreadInfo info(jvmtienv, env, thread);
  ScopedMethodInfo method_info(jvmtienv, env, m);
  ScopedFieldInfo field_info(jvmtienv, field_klass, field);
  jclass oklass = (object != nullptr) ? env->GetObjectClass(object) : nullptr;
  ScopedClassInfo obj_class_info(jvmtienv, oklass);
  if (!method_info.Init() || !field_info.Init() || !obj_class_info.Init()) {
    LOG(ERROR) << "Unable to get callback info!";
    return;
  }
  LOG(INFO) << "ACCESS field \"" << field_info << "\" on object of "
            << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info
            << "\" at location 0x" << std::hex << location << ". Thread is \""
            << info.GetName() << "\".";
  DeleteLocalRef(env, oklass);
}

static std::string PrintJValue(jvmtiEnv* jvmtienv, JNIEnv* env, char type, jvalue new_value) {
  std::ostringstream oss;
  switch (type) {
    case 'L': {
      jobject nv = new_value.l;
      if (nv == nullptr) {
        oss << "\"null\"";
      } else {
        jclass nv_klass = env->GetObjectClass(nv);
        ScopedClassInfo nv_class_info(jvmtienv, nv_klass);
        if (!nv_class_info.Init()) {
          oss << "with unknown type";
        } else {
          oss << "of type \"" << nv_class_info.GetName() << "\"";
        }
        DeleteLocalRef(env, nv_klass);
      }
      break;
    }
    case 'Z': {
      if (new_value.z) {
        oss << "true";
      } else {
        oss << "false";
      }
      break;
    }
#define SEND_VALUE(chr, sym, type) \
    case chr: { \
      oss << static_cast<type>(new_value.sym); \
      break; \
    }
    SEND_VALUE('B', b, int8_t);
    SEND_VALUE('C', c, uint16_t);
    SEND_VALUE('S', s, int16_t);
    SEND_VALUE('I', i, int32_t);
    SEND_VALUE('J', j, int64_t);
    SEND_VALUE('F', f, float);
    SEND_VALUE('D', d, double);
#undef SEND_VALUE
  }
  return oss.str();
}

void JNICALL FieldModificationHook(jvmtiEnv* jvmtienv,
                                   JNIEnv* env,
                                   jthread thread,
                                   jmethodID m,
                                   jlocation location,
                                   jclass field_klass,
                                   jobject object,
                                   jfieldID field,
                                   char type,
                                   jvalue new_value) {
  ScopedThreadInfo info(jvmtienv, env, thread);
  ScopedMethodInfo method_info(jvmtienv, env, m);
  ScopedFieldInfo field_info(jvmtienv, field_klass, field);
  jclass oklass = (object != nullptr) ? env->GetObjectClass(object) : nullptr;
  ScopedClassInfo obj_class_info(jvmtienv, oklass);
  if (!method_info.Init() || !field_info.Init() || !obj_class_info.Init()) {
    LOG(ERROR) << "Unable to get callback info!";
    return;
  }
  LOG(INFO) << "MODIFY field \"" << field_info << "\" on object of "
            << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info
            << "\" at location 0x" << std::hex << location << std::dec << "New value is "
            << PrintJValue(jvmtienv, env, type, new_value) << ". Thread is \""
            << info.GetName() << "\".";
  DeleteLocalRef(env, oklass);
}
void JNICALL MethodExitHook(jvmtiEnv* jvmtienv,
                            JNIEnv* env,
                            jthread thread,
                            jmethodID m,
                            jboolean was_popped_by_exception,
                            jvalue val) {
  ScopedThreadInfo info(jvmtienv, env, thread);
  ScopedMethodInfo method_info(jvmtienv, env, m);
  if (!method_info.Init()) {
    LOG(ERROR) << "Unable to get method info!";
    return;
  }
  std::string type(method_info.GetSignature());
  type = type.substr(type.find(')') + 1);
  std::string out_val(was_popped_by_exception ? "" : GetValOf(jvmtienv, env, type, val));
  LOG(INFO) << "Leaving method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"."
            << std::endl
            << "    Cause: " << (was_popped_by_exception ? "exception" : "return ")
            << out_val << ".";
}

void JNICALL MethodEntryHook(jvmtiEnv* jvmtienv,
                             JNIEnv* env,
                             jthread thread,
                             jmethodID m) {
  ScopedThreadInfo info(jvmtienv, env, thread);
  ScopedMethodInfo method_info(jvmtienv, env, m);
  if (!method_info.Init()) {
    LOG(ERROR) << "Unable to get method info!";
    return;
  }
  LOG(INFO) << "Entering method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"";
}

void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv,
                              JNIEnv* env,
                              jthread thread,
                              jclass klass) {
  StressData* data = nullptr;
  CHECK_EQ(jvmtienv->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
           JVMTI_ERROR_NONE);
  if (data->field_stress) {
    jint nfields;
    jfieldID* fields;
    if (jvmtienv->GetClassFields(klass, &nfields, &fields) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to get a classes fields!";
      return;
    }
    for (jint i = 0; i < nfields; i++) {
      jfieldID f = fields[i];
      // Ignore errors
      jvmtienv->SetFieldAccessWatch(klass, f);
      jvmtienv->SetFieldModificationWatch(klass, f);
    }
    jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fields));
  }
  if (data->trace_stress) {
    ScopedThreadInfo info(jvmtienv, env, thread);
    ScopedClassInfo class_info(jvmtienv, klass);
    if (!class_info.Init()) {
      LOG(ERROR) << "Unable to get class info!";
      return;
    }
    LOG(INFO) << "Prepared class \"" << class_info.GetName() << "\". Thread is \""
              << info.GetName() << "\"";
  }
}

void JNICALL SingleStepHook(jvmtiEnv* jvmtienv,
                            JNIEnv* env,
                            jthread thread,
                            jmethodID method,
                            jlocation location) {
  ScopedThreadInfo info(jvmtienv, env, thread);
  ScopedMethodInfo method_info(jvmtienv, env, method);
  if (!method_info.Init()) {
    LOG(ERROR) << "Unable to get method info!";
    return;
  }
  LOG(INFO) << "Single step at location: 0x" << std::setw(8) << std::setfill('0') << std::hex
            << location << " in method " << method_info << " thread: " << info.GetName();
}

// The hook we are using.
void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
                                         [[maybe_unused]] JNIEnv* jni_env,
                                         [[maybe_unused]] jclass class_being_redefined,
                                         [[maybe_unused]] jobject loader,
                                         const char* name,
                                         [[maybe_unused]] jobject protection_domain,
                                         jint class_data_len,
                                         const unsigned char* class_data,
                                         jint* new_class_data_len,
                                         unsigned char** new_class_data) {
  std::vector<unsigned char> out;
  // Make the jvmti semi-descriptor into the full descriptor.
  std::string name_str("L");
  name_str += name;
  name_str += ";";
  StressData* data = nullptr;
  CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
           JVMTI_ERROR_NONE);
  if (!data->vm_class_loader_initialized) {
    LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
                 << "initialized. Transforming this class could cause spurious test failures.";
    return;
  } else if (DoExtractClassFromData(jvmti, name_str, class_data_len, class_data,
                                    /*out*/ new_class_data_len, /*out*/ new_class_data)) {
    LOG(INFO) << "Extracted class: " << name;
  } else {
    std::cerr << "Unable to extract class " << name << std::endl;
    *new_class_data_len = 0;
    *new_class_data = nullptr;
  }
}

static std::string AdvanceOption(const std::string& ops) {
  return ops.substr(ops.find(',') + 1);
}

static bool HasNextOption(const std::string& ops) {
  return ops.find(',') != std::string::npos;
}

static std::string GetOption(const std::string& in) {
  return in.substr(0, in.find(','));
}

// Options are
// jvmti-stress,[redefine,][trace,][field]
static void ReadOptions(StressData* data, char* options) {
  std::string ops(options);
  CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress";
  do {
    ops = AdvanceOption(ops);
    std::string cur = GetOption(ops);
    if (cur == "trace") {
      data->trace_stress = true;
    } else if (cur == "step") {
      data->step_stress = true;
    } else if (cur == "field") {
      data->field_stress = true;
    } else if (cur == "redefine") {
      data->redefine_stress = true;
    } else {
      LOG(FATAL) << "Unknown option: " << GetOption(ops);
    }
  } while (HasNextOption(ops));
}

// Do final setup during the VMInit callback. By this time most things are all setup.
static void JNICALL PerformFinalSetupVMInit(jvmtiEnv *jvmti_env,
                                            JNIEnv* jni_env,
                                            [[maybe_unused]] jthread thread) {
  // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
  // visibility but the class will be loaded behind the scenes.
  LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
  jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
  StressData* data = nullptr;
  CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
           JVMTI_ERROR_NONE);
  // We need to make sure that VMClassLoader is initialized before we start redefining anything
  // since it can give (non-fatal) error messages if it's initialized after we've redefined BCP
  // classes. These error messages are expected and no problem but they will mess up our testing
  // infrastructure.
  if (klass == nullptr) {
    // Probably on RI. Clear the exception so we can continue but don't mark vmclassloader as
    // initialized.
    LOG(WARNING) << "Unable to find VMClassLoader class!";
    jni_env->ExceptionClear();
  } else {
    // GetMethodID is spec'd to cause the class to be initialized.
    jni_env->GetMethodID(klass, "hashCode""()I");
    DeleteLocalRef(jni_env, klass);
    data->vm_class_loader_initialized = true;
  }
}

static bool WatchAllFields(JavaVM* vm, jvmtiEnv* jvmti) {
  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                      JVMTI_EVENT_CLASS_PREPARE,
                                      nullptr) != JVMTI_ERROR_NONE) {
    LOG(ERROR) << "Couldn't set prepare event!";
    return false;
  }
  // TODO We really shouldn't need to do this step here.
  jint nklass;
  jclass* klasses;
  if (jvmti->GetLoadedClasses(&nklass, &klasses) != JVMTI_ERROR_NONE) {
    LOG(WARNING) << "Couldn't get loaded classes! Ignoring.";
    return true;
  }
  JNIEnv* jni = nullptr;
  if (vm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6)) {
    LOG(ERROR) << "Unable to get jni env. Ignoring and potentially leaking jobjects.";
    return false;
  }
  for (jint i = 0; i < nklass; i++) {
    jclass k = klasses[i];
    ScopedClassInfo sci(jvmti, k);
    if (sci.Init()) {
      LOG(INFO) << "NOTE: class " << sci.GetName() << " already loaded.";
    }
    jint nfields;
    jfieldID* fields;
    jvmtiError err = jvmti->GetClassFields(k, &nfields, &fields);
    if (err == JVMTI_ERROR_NONE) {
      for (jint j = 0; j < nfields; j++) {
        jfieldID f = fields[j];
        if (jvmti->SetFieldModificationWatch(k, f) != JVMTI_ERROR_NONE ||
            jvmti->SetFieldAccessWatch(k, f) != JVMTI_ERROR_NONE) {
          LOG(ERROR) << "Unable to set watches on a field.";
          return false;
        }
      }
    } else if (err != JVMTI_ERROR_CLASS_NOT_PREPARED) {
      LOG(ERROR) << "Unexpected error getting class fields!";
      return false;
    }
    jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
    DeleteLocalRef(jni, k);
  }
  jvmti->Deallocate(reinterpret_cast<unsigned char*>(klasses));
  return true;
}

extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
                                               char* options,
                                               [[maybe_unused]] void* reserved) {
  jvmtiEnv* jvmti = nullptr;
  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
    LOG(ERROR) << "Unable to get jvmti env.";
    return 1;
  }
  StressData* data = nullptr;
  if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
                                          reinterpret_cast<unsigned char**>(&data))) {
    LOG(ERROR) << "Unable to allocate data for stress test.";
    return 1;
  }
  memset(data, 0sizeof(StressData));
  // Read the options into the static variables that hold them.
  ReadOptions(data, options);
  // Save the data
  if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
    LOG(ERROR) << "Unable to save stress test data.";
    return 1;
  }

  // Just get all capabilities.
  jvmtiCapabilities caps = {
    .can_tag_objects                                 = 0,
    .can_generate_field_modification_events          = 1,
    .can_generate_field_access_events                = 1,
    .can_get_bytecodes                               = 0,
    .can_get_synthetic_attribute                     = 0,
    .can_get_owned_monitor_info                      = 0,
    .can_get_current_contended_monitor               = 0,
    .can_get_monitor_info                            = 0,
    .can_pop_frame                                   = 0,
    .can_redefine_classes                            = 1,
    .can_signal_thread                               = 0,
    .can_get_source_file_name                        = 1,
    .can_get_line_numbers                            = 1,
    .can_get_source_debug_extension                  = 1,
    .can_access_local_variables                      = 0,
    .can_maintain_original_method_order              = 0,
    .can_generate_single_step_events                 = 1,
    .can_generate_exception_events                   = 0,
    .can_generate_frame_pop_events                   = 0,
    .can_generate_breakpoint_events                  = 0,
    .can_suspend                                     = 0,
    .can_redefine_any_class                          = 0,
    .can_get_current_thread_cpu_time                 = 0,
    .can_get_thread_cpu_time                         = 0,
    .can_generate_method_entry_events                = 1,
    .can_generate_method_exit_events                 = 1,
    .can_generate_all_class_hook_events              = 0,
    .can_generate_compiled_method_load_events        = 0,
    .can_generate_monitor_events                     = 0,
    .can_generate_vm_object_alloc_events             = 0,
    .can_generate_native_method_bind_events          = 1,
    .can_generate_garbage_collection_events          = 0,
    .can_generate_object_free_events                 = 0,
    .can_force_early_return                          = 0,
    .can_get_owned_monitor_stack_depth_info          = 0,
    .can_get_constant_pool                           = 0,
    .can_set_native_method_prefix                    = 0,
    .can_retransform_classes                         = 1,
    .can_retransform_any_class                       = 0,
    .can_generate_resource_exhaustion_heap_events    = 0,
    .can_generate_resource_exhaustion_threads_events = 0,
  };
  jvmti->AddCapabilities(&caps);

  // Set callbacks.
  jvmtiEventCallbacks cb;
  memset(&cb, 0sizeof(cb));
  cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
  cb.NativeMethodBind = doJvmtiMethodBind;
  cb.VMInit = PerformFinalSetupVMInit;
  cb.MethodEntry = MethodEntryHook;
  cb.MethodExit = MethodExitHook;
  cb.FieldAccess = FieldAccessHook;
  cb.FieldModification = FieldModificationHook;
  cb.ClassPrepare = ClassPrepareHook;
  cb.SingleStep = SingleStepHook;
  if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
    LOG(ERROR) << "Unable to set class file load hook cb!";
    return 1;
  }
  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                      JVMTI_EVENT_VM_INIT,
                                      nullptr) != JVMTI_ERROR_NONE) {
    LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
    return 1;
  }
  if (data->redefine_stress) {
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
      return 1;
    }
  }
  if (data->trace_stress) {
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_CLASS_PREPARE,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable CLASS_PREPARE event!";
      return 1;
    }
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_NATIVE_METHOD_BIND,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
      return 1;
    }
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_METHOD_ENTRY,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_ENTRY event!";
      return 1;
    }
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_METHOD_EXIT,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_EXIT event!";
      return 1;
    }
  }
  if (data->field_stress) {
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_FIELD_MODIFICATION,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable FIELD_MODIFICATION event!";
      return 1;
    }
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_FIELD_ACCESS,
                                        nullptr) != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable FIELD_ACCESS event!";
      return 1;
    }
    if (!WatchAllFields(vm, jvmti)) {
      return 1;
    }
  }
  if (data->step_stress) {
    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                        JVMTI_EVENT_SINGLE_STEP,
                                        nullptr) != JVMTI_ERROR_NONE) {
      return 1;
    }
  }
  return 0;
}

extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
  return Agent_OnLoad(vm, options, reserved);
}

}  // namespace art

Messung V0.5 in Prozent
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik