Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/art/art/tools/jvmti-agents/ti-fast/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 27 kB image not shown  

Quelle  tifast.cc

  Sprache: C
 

// Copyright (C) 2018 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 <android-base/logging.h>

#include <atomic>
#include <iostream>
#include <istream>
#include <iomanip>
#include <jni.h>
#include <jvmti.h>
#include <limits>
#include <memory>
#include <string>
#include <sstream>
#include <vector>

namespace tifast {

#define EVENT(x) JVMTI_EVENT_ ## x

namespace {

// Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI
// env.
static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;

template <typename... Args>
static void Unused([[maybe_unused]] Args... args) {}

// jthread is a typedef of jobject so we use this to allow the templates to distinguish them.
struct jthreadContainer { jthread thread; };
// jlocation is a typedef of jlong so use this to distinguish the less common jlong.
struct jlongContainer { jlong val; };

static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) {
  switch (event) {
#define DO_CASE(name, cap_name) \
    case EVENT(name):           \
      caps->cap_name = 1;       \
      break
    DO_CASE(SINGLE_STEP, can_generate_single_step_events);
    DO_CASE(METHOD_ENTRY, can_generate_method_entry_events);
    DO_CASE(METHOD_EXIT, can_generate_method_exit_events);
    DO_CASE(NATIVE_METHOD_BIND, can_generate_native_method_bind_events);
    DO_CASE(EXCEPTION, can_generate_exception_events);
    DO_CASE(EXCEPTION_CATCH, can_generate_exception_events);
    DO_CASE(COMPILED_METHOD_LOAD, can_generate_compiled_method_load_events);
    DO_CASE(COMPILED_METHOD_UNLOAD, can_generate_compiled_method_load_events);
    DO_CASE(MONITOR_CONTENDED_ENTER, can_generate_monitor_events);
    DO_CASE(MONITOR_CONTENDED_ENTERED, can_generate_monitor_events);
    DO_CASE(MONITOR_WAIT, can_generate_monitor_events);
    DO_CASE(MONITOR_WAITED, can_generate_monitor_events);
    DO_CASE(VM_OBJECT_ALLOC, can_generate_vm_object_alloc_events);
    DO_CASE(GARBAGE_COLLECTION_START, can_generate_garbage_collection_events);
    DO_CASE(GARBAGE_COLLECTION_FINISH, can_generate_garbage_collection_events);
#undef DO_CASE
    defaultbreak;
  }
}

// Setup for all supported events. Give a macro with {non_}jni_fun(name, event_num, args)
#define FOR_ALL_SUPPORTED_EVENTS_DIFFERENT(jni_fun, non_jni_fun)          \
    jni_fun(VMInit, EVENT(VM_INIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
    jni_fun(VMDeath, EVENT(VM_DEATH), (jvmtiEnv* jvmti, JNIEnv* jni), (jvmti, jni)) \
    jni_fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
    jni_fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
    jni_fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, jobject obj1, const char* c1, jobject obj2, jint i1, const unsigned char* c2, jint* ip1, unsigned char** cp1), (jvmti, jni, klass, obj1, c1, obj2, i1, c2, ip1, cp1)) \
    jni_fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass), (jvmti, jni, jthreadContainer{.thread = thread}, klass) ) \
    jni_fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass), (jvmti, jni, jthreadContainer{.thread = thread}, klass)) \
    jni_fun(VMStart, EVENT(VM_START), (jvmtiEnv* jvmti, JNIEnv* jni), (jvmti, jni)) \
    jni_fun(Exception, EVENT(EXCEPTION), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth1, jlocation loc1, jobject obj, jmethodID meth2, jlocation loc2), (jvmti, jni, jthreadContainer{.thread = thread}, meth1, loc1, obj, meth2, loc2)) \
    jni_fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jlocation loc, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, meth, loc, obj)) \
    jni_fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jlocation loc), (jvmti, jni, jthreadContainer{.thread = thread}, meth, loc)) \
    jni_fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth), (jvmti, jni, jthreadContainer{.thread = thread}, meth)) \
    jni_fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jboolean jb, jvalue jv), (jvmti, jni, jthreadContainer{.thread = thread}, meth, jb, jv)) \
    jni_fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, void* v1, void** v2), (jvmti, jni, jthreadContainer{.thread = thread}, meth, v1, v2)) \
    non_jni_fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv* jvmti, jmethodID meth, jint i1, const void* cv1, jint i2, const jvmtiAddrLocationMap* alm, const void* cv2), (jvmti, meth, i1, cv1, i2, alm, cv2)) \
    non_jni_fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv* jvmti, jmethodID meth, const void* cv1), (jvmti, meth, cv1)) \
    non_jni_fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv* jvmti, const char* cc, const void* cv, jint i1), (jvmti, cc, cv, i1)) \
    non_jni_fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv* jvmti), (jvmti)) \
    jni_fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, jlongContainer{.val = l1})) \
    jni_fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jboolean b1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, b1)) \
    jni_fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, obj)) \
    jni_fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, obj)) \
    jni_fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv* jvmti, JNIEnv* jni, jint i1, const void* cv, const char* cc), (jvmti, jni, i1, cv, cc)) \
    non_jni_fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv* jvmti), (jvmti)) \
    non_jni_fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv* jvmti), (jvmti)) \
    jni_fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jclass klass, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, klass, jlongContainer{.val = l1})) \

#define FOR_ALL_SUPPORTED_EVENTS(fun) \
    FOR_ALL_SUPPORTED_EVENTS_DIFFERENT(fun, fun)

static const jvmtiEvent kAllEvents[] = {
#define GET_EVENT(a, event, b, c) event,
FOR_ALL_SUPPORTED_EVENTS(GET_EVENT)
#undef GET_EVENT
};

#define GENERATE_EMPTY_FUNCTION(name, number, args, argnames) \
    static void JNICALL empty ## name  args { Unused argnames ; }
FOR_ALL_SUPPORTED_EVENTS(GENERATE_EMPTY_FUNCTION)
#undef GENERATE_EMPTY_FUNCTION

static jvmtiEventCallbacks kEmptyCallbacks {
#define CREATE_EMPTY_EVENT_CALLBACKS(name, num, args, argnames) \
    .name = empty ## name,
  FOR_ALL_SUPPORTED_EVENTS(CREATE_EMPTY_EVENT_CALLBACKS)
#undef CREATE_EMPTY_EVENT_CALLBACKS
};

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

class ScopedThreadInfo {
 public:
  ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
      : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
    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) {}

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

  bool Init(bool get_generic = true) {
    if (class_ == nullptr) {
      name_ = const_cast<char*>("<NONE>");
      generic_ = const_cast<char*>("<NONE>");
      return true;
    } else {
      jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_);
      jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_);
      char** gen_ptr = &generic_;
      if (!get_generic) {
        generic_ = nullptr;
        gen_ptr = nullptr;
      }
      return jvmtienv_->GetClassSignature(class_, &name_, gen_ptr) == 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* GetGeneric() const {
    return generic_;
  }

  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_ = nullptr;
  char* generic_ = nullptr;
  char* file_ = nullptr;
  char* debug_ext_ = nullptr;

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

class ScopedMethodInfo {
 public:
  ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
      : jvmtienv_(jvmtienv), env_(env), method_(m) {}

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

  bool Init(bool get_generic = true) {
    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(get_generic) &&
        (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == 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_;
  }

  const char* GetGeneric() const {
    return generic_;
  }

  jint GetFirstLine() const {
    return first_line_;
  }

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

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

std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& c) {
  const char* generic = c.GetGeneric();
  if (generic != nullptr) {
    return os << c.GetName() << "<" << generic << ">" << " file: " << c.GetSourceFileName();
  } else {
    return os << c.GetName() << " file: " << c.GetSourceFileName();
  }
}

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


class LogPrinter {
 public:
  explicit LogPrinter(jvmtiEvent event) : event_(event) {}

  template <typename ...Args> void PrintRestNoJNI(jvmtiEnv* jvmti, Args... args) {
    PrintRest(jvmti, static_cast<JNIEnv*>(nullptr), args...);
  }

  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti, JNIEnv* env, Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jlongContainer l,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jthreadContainer thr,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jboolean i,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jint i,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jclass klass,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jmethodID meth,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jlocation loc,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jint* ip,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             const void* loc,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             void* loc,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             void** loc,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             unsigned char** v,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             const unsigned char* v,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             const char* v,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             const jvmtiAddrLocationMap* v,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jvalue v,
                                             Args... args);
  template <typename ...Args> void PrintRest(jvmtiEnv* jvmti,
                                             JNIEnv* env,
                                             jobject v,
                                             Args... args);

  std::string GetResult() {
    std::string out_str = stream.str();
    return start_args + out_str;
  }

 private:
  jvmtiEvent event_;
  std::string start_args;
  std::ostringstream stream;
};

// Base case
template <>
void LogPrinter::PrintRest([[maybe_unused]] jvmtiEnv* jvmti, JNIEnv* jni) {
  if (jni == nullptr) {
    start_args = "jvmtiEnv*";
  } else {
    start_args = "jvmtiEnv*, JNIEnv*";
  }
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti,
                           JNIEnv* jni,
                           const jvmtiAddrLocationMap* v,
                           Args... args) {
  if (v != nullptr) {
    stream << ", const jvmtiAddrLocationMap*[start_address: "
           << v->start_address << ", location: " << v->location << "]";
  } else {
    stream << ", const jvmtiAddrLocationMap*[nullptr]";
  }
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jint* v, Args... args) {
  stream << ", jint*[" << static_cast<const void*>(v) << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, const void* v, Args... args) {
  stream << ", const void*[" << v << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, unsigned char** v, Args... args) {
  stream << ", unsigned char**[" << static_cast<const void*>(v) << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, const unsigned char* v, Args... args) {
  stream << ", const unsigned char*[" << static_cast<const void*>(v) << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, const char* v, Args... args) {
  stream << ", const char*[" << v << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename... Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jvalue v, Args... args) {
  std::ostringstream hex;
  hex << std::hex << v.j;
  std::ostringstream char_val;
  if (std::isprint(v.c) && v.c < std::numeric_limits<unsigned char>::max()) {
    char_val << "'" << static_cast<unsigned char>(v.c) << "'";
  } else {
    char_val << "0x" << std::hex << reinterpret_cast<uint16_t>(v.c);
  }
  stream << ", jvalue[{<hex: 0x" << hex.str() << ">"
         << ", .z=" << (v.z ? "true" : "false")
         << ", .b=" << static_cast<int32_t>(v.b)
         << ", .c=" << char_val.str()
         << ", .s=" << static_cast<int32_t>(v.s)
         << ", .i=" << v.i
         << ", .j=" << v.j
         << ", .f=" << v.f
         << ", .d=" << v.d
         << ", .l=" << v.l << "}]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, void** v, Args... args) {
  stream << ", void**[" << v << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, void* v, Args... args) {
  stream << ", void*[" << v << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jlongContainer l, Args... args) {
  stream << ", jlong[" << l.val << ", hex: 0x" << std::hex << l.val << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jlocation l, Args... args) {
  stream << ", jlocation[" << l << ", hex: 0x" << std::hex << l << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jboolean b, Args... args) {
  stream << ", jboolean[" << (b ? "true" : "false") << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jint i, Args... args) {
  stream << ", jint[" << i << ", hex: 0x" << std::hex << i << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jobject obj, Args... args) {
  if (obj == nullptr) {
    stream << ", jobject[nullptr]";
  } else {
    jni->PushLocalFrame(1);
    jclass klass = jni->GetObjectClass(obj);
    ScopedClassInfo sci(jvmti, klass);
    if (sci.Init(event_ != JVMTI_EVENT_VM_OBJECT_ALLOC)) {
      stream << ", jobject[type: " << sci << "]";
    } else {
      stream << ", jobject[type: TYPE UNKNOWN]";
    }
    jni->PopLocalFrame(nullptr);
  }
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jthreadContainer thr, Args... args) {
  ScopedThreadInfo sti(jvmti, jni, thr.thread);
  stream << ", jthread[" << sti.GetName() << "]";
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, Args... args) {
  ScopedClassInfo sci(jvmti, klass);
  if (sci.Init(/*get_generic=*/event_ != JVMTI_EVENT_VM_OBJECT_ALLOC)) {
    stream << ", jclass[" << sci << "]";
  } else {
    stream << ", jclass[TYPE UNKNOWN]";
  }
  PrintRest(jvmti, jni, args...);
}

template<typename ...Args>
void LogPrinter::PrintRest(jvmtiEnv* jvmti, JNIEnv* jni, jmethodID meth, Args... args) {
  ScopedMethodInfo smi(jvmti, jni, meth);
  if (smi.Init()) {
    stream << ", jmethodID[" << smi << "]";
  } else {
    stream << ", jmethodID[METHOD UNKNOWN]";
  }
  PrintRest(jvmti, jni, args...);
}

#define GENERATE_LOG_FUNCTION_JNI(name, event, args, argnames) \
    static void JNICALL log ## name  args { \
      LogPrinter printer(event); \
      printer.PrintRest argnames; \
      LOG(INFO) << "Got event " << #name << "(" << printer.GetResult() << ")"; \
    } \

#define GENERATE_LOG_FUNCTION_NO_JNI(name, event, args, argnames) \
    static void JNICALL log ## name  args { \
      LogPrinter printer(event); \
      printer.PrintRestNoJNI argnames; \
      LOG(INFO) << "Got event " << #name << "(" << printer.GetResult() << ")"; \
    } \

FOR_ALL_SUPPORTED_EVENTS_DIFFERENT(GENERATE_LOG_FUNCTION_JNI, GENERATE_LOG_FUNCTION_NO_JNI)
#undef GENERATE_LOG_FUNCTION

static jvmtiEventCallbacks kLogCallbacks {
#define CREATE_LOG_EVENT_CALLBACK(name, num, args, argnames) \
    .name = log ## name,
  FOR_ALL_SUPPORTED_EVENTS(CREATE_LOG_EVENT_CALLBACK)
#undef CREATE_LOG_EVENT_CALLBACK
};

static std::string EventToName(jvmtiEvent desired_event) {
#define CHECK_NAME(name, event, args, argnames) \
  if (desired_event == (event)) { \
    return #name; \
  }
  FOR_ALL_SUPPORTED_EVENTS(CHECK_NAME);
  LOG(FATAL) << "Unknown event " << desired_event;
  __builtin_unreachable();
#undef CHECK_NAME
}
static jvmtiEvent NameToEvent(const std::string& desired_name) {
#define CHECK_NAME(name, event, args, argnames) \
  if (desired_name == #name) { \
    return event; \
  }
  FOR_ALL_SUPPORTED_EVENTS(CHECK_NAME);
  LOG(FATAL) << "Unknown event " << desired_name;
  __builtin_unreachable();
#undef CHECK_NAME
}

#undef FOR_ALL_SUPPORTED_EVENTS
#undef FOR_ALL_SUPPORTED_EVENTS_DIFFERENT

static std::vector<jvmtiEvent> GetAllAvailableEvents(jvmtiEnv* jvmti) {
  std::vector<jvmtiEvent> out;
  jvmtiCapabilities caps{};
  jvmti->GetPotentialCapabilities(&caps);
  uint8_t caps_bytes[sizeof(caps)];
  memcpy(caps_bytes, &caps, sizeof(caps));
  for (jvmtiEvent e : kAllEvents) {
    jvmtiCapabilities req{};
    AddCapsForEvent(e, &req);
    uint8_t req_bytes[sizeof(req)];
    memcpy(req_bytes, &req, sizeof(req));
    bool good = true;
    for (size_t i = 0; i < sizeof(caps); i++) {
      if ((req_bytes[i] & caps_bytes[i]) != req_bytes[i]) {
        good = false;
        break;
      }
    }
    if (good) {
      out.push_back(e);
    } else {
      LOG(WARNING) << "Unable to get capabilities for event " << EventToName(e);
    }
  }
  return out;
}

static std::vector<jvmtiEvent> GetRequestedEventList(jvmtiEnv* jvmti, const std::string&&nbsp;args) {
  std::vector<jvmtiEvent> res;
  std::stringstream args_stream(args);
  std::string item;
  while (std::getline(args_stream, item, ',')) {
    if (item == "") {
      continue;
    } else if (item == "all") {
      return GetAllAvailableEvents(jvmti);
    }
    res.push_back(NameToEvent(item));
  }
  return res;
}

static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) {
  jint res = 0;
  res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1);

  if (res != JNI_OK || *jvmti == nullptr) {
    LOG(ERROR) << "Unable to access JVMTI, error code " << res;
    return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion);
  }
  return res;
}

}  // namespace

static jint AgentStart(JavaVM* vm, char* options, [[maybe_unused]] void* reserved) {
  jvmtiEnv* jvmti = nullptr;
  jvmtiError error = JVMTI_ERROR_NONE;
  if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) {
    LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!";
    return JNI_ERR;
  }
  std::string args(options);
  bool is_log = false;
  if (args.compare(03"log") == 0) {
    is_log = true;
    args = args.substr(3);
  }

  std::vector<jvmtiEvent> events = GetRequestedEventList(jvmti, args);

  jvmtiCapabilities caps{};
  for (jvmtiEvent e : events) {
    AddCapsForEvent(e, &caps);
  }
  if (is_log) {
    caps.can_get_line_numbers = 1;
    caps.can_get_source_file_name = 1;
    caps.can_get_source_debug_extension = 1;
  }
  error = jvmti->AddCapabilities(&caps);
  if (error != JVMTI_ERROR_NONE) {
    LOG(ERROR) << "Unable to set caps";
    return JNI_ERR;
  }

  if (is_log) {
    error = jvmti->SetEventCallbacks(&kLogCallbacks, static_cast<jint>(sizeof(kLogCallbacks)));
  } else {
    error = jvmti->SetEventCallbacks(&kEmptyCallbacks, static_cast<jint>(sizeof(kEmptyCallbacks)));
  }
  if (error != JVMTI_ERROR_NONE) {
    LOG(ERROR) << "Unable to set event callbacks.";
    return JNI_ERR;
  }
  for (jvmtiEvent e : events) {
    error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                            e,
                                            nullptr /* all threads */);
    if (error != JVMTI_ERROR_NONE) {
      LOG(ERROR) << "Unable to enable event " << e;
      return JNI_ERR;
    }
  }
  return JNI_OK;
}

// Late attachment (e.g. 'am attach-agent').
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
  return AgentStart(vm, options, reserved);
}

// Early attachment
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
  return AgentStart(jvm, options, reserved);
}

}  // namespace tifast


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

¤ Dauer der Verarbeitung: 0.3 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.