summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2018-09-19 14:50:34 -0700
committer Alex Light <allight@google.com> 2018-09-19 16:35:43 -0700
commitcf9efd6a385f56921aa552cc1e04ba10aefdc766 (patch)
tree4d776e8bed16bd2b4effa1c09245f8de40cc4d5f
parente9c1634d987ebe39bb1cac6a01e89bacc3f75503 (diff)
Improved tifast.so agent
Made tifast.so=log actually print out the values of all the arguments passed to the events. This includes all integer values, thread names, class names and files and method names and signatures. With log the agent will now print lines like the following for each event: dalvikvm32 I 09-19 13:31:18 160583 160595 tifast.cc:579] Got event ExceptionCatch(jvmtiEnv*, JNIEnv*, jthread[FinalizerDaemon], jmethodID[Ljava/lang/Daemons$FinalizerDaemon;->runInternal()V (source: Daemons.java:217)], jlocation[88, hex: 0x58], jobject[type: Ljava/lang/InterruptedException; file: InterruptedException.java]) Gave ti-fast an 'all' option which will enable all events the runtime is capable of supporting. Test: ./test/run-test --host --with-agent libtifast.so=log,all 001-HelloWorld Change-Id: I672b91495e41eea5af9a7746f4149ebb0383131e
-rw-r--r--tools/ti-fast/README.md4
-rw-r--r--tools/ti-fast/tifast.cc575
2 files changed, 544 insertions, 35 deletions
diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md
index bc468826ec..a0a7dd7c35 100644
--- a/tools/ti-fast/README.md
+++ b/tools/ti-fast/README.md
@@ -21,6 +21,10 @@ following format:
called. This behavior is static. The no-log methods have no branches and just
immediately return.
+* If 'all' is one of the arguments all events the current runtime is capable of
+ providing will be listened for and all other arguments (excepting 'log') will
+ be ignored.
+
* The event-names are the same names as are used in the jvmtiEventCallbacks
struct.
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index b147addfd5..85c433b10f 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -36,6 +36,13 @@ namespace {
// env.
static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+template <typename ...Args> static void Unused(Args... args ATTRIBUTE_UNUSED) {}
+
+// 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) \
@@ -63,59 +70,520 @@ static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) {
}
// Setup for all supported events. Give a macro with fun(name, event_num, args)
+#define FOR_ALL_SUPPORTED_JNI_EVENTS(fun) \
+ fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth, jlocation loc), (jvmti, jni, jthreadContainer{.thread = thread}, meth, loc)) \
+ fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID meth), (jvmti, jni, jthreadContainer{.thread = thread}, meth)) \
+ 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)) \
+ 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)) \
+ 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)) \
+ 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)) \
+ fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
+ fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
+ fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass), (jvmti, jni, jthreadContainer{.thread = thread}, klass) ) \
+ fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass), (jvmti, jni, jthreadContainer{.thread = thread}, klass)) \
+ 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)) \
+ fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, obj)) \
+ fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj), (jvmti, jni, jthreadContainer{.thread = thread}, obj)) \
+ fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, jlongContainer{.val = l1})) \
+ fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jboolean b1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, b1)) \
+ fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv* jvmti, JNIEnv* jni, jint i1, const void* cv, const char* cc), (jvmti, jni, i1, cv, cc)) \
+ 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_NO_JNI_EVENTS(fun) \
+ 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)) \
+ fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv* jvmti, jmethodID meth, const void* cv1), (jvmti, meth, cv1)) \
+ fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv* jvmti, const char* cc, const void* cv, jint i1), (jvmti, cc, cv, i1)) \
+ fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv* jvmti), (jvmti)) \
+ fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv* jvmti), (jvmti)) \
+ fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv* jvmti), (jvmti))
+
#define FOR_ALL_SUPPORTED_EVENTS(fun) \
- fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \
- fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \
- fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \
- fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \
- fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \
- fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \
- fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv*, JNIEnv*, jthread)) \
- fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv*, JNIEnv*, jthread)) \
- fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \
- fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \
- fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv*, JNIEnv*, jclass, jobject, const char*, jobject, jint, const unsigned char*, jint*, unsigned char**)) \
- fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv*, jmethodID, jint, const void*, jint, const jvmtiAddrLocationMap*, const void*)) \
- fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv*, jmethodID, const void*)) \
- fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv*, const char*, const void*, jint)) \
- fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv*)) \
- fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \
- fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \
- fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv*, JNIEnv*, jthread, jobject, jlong)) \
- fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv*, JNIEnv*, jthread, jobject, jboolean)) \
- fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv*, JNIEnv*, jint, const void*, const char*)) \
- fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv*, JNIEnv*, jthread, jobject, jclass, jlong)) \
- fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv*)) \
- fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv*))
-
-#define GENERATE_EMPTY_FUNCTION(name, number, args) \
- static void JNICALL empty ## name args { }
+ FOR_ALL_SUPPORTED_JNI_EVENTS(fun) \
+ FOR_ALL_SUPPORTED_NO_JNI_EVENTS(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) \
+#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
};
-#define GENERATE_LOG_FUNCTION(name, number, args) \
- static void JNICALL log ## name args { \
- LOG(INFO) << "Got event " << #name ; \
+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_;
}
-FOR_ALL_SUPPORTED_EVENTS(GENERATE_LOG_FUNCTION)
+ }
+
+ 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(jvmtiEnv* jvmti ATTRIBUTE_UNUSED, 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 ATTRIBUTE_UNUSED, Args... args) {
+ stream << ", jvalue[<UNION>]";
+ 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_JNI_EVENTS(GENERATE_LOG_FUNCTION_JNI)
+FOR_ALL_SUPPORTED_NO_JNI_EVENTS(GENERATE_LOG_FUNCTION_NO_JNI)
#undef GENERATE_LOG_FUNCTION
static jvmtiEventCallbacks kLogCallbacks {
-#define CREATE_LOG_EVENT_CALLBACK(name, num, args) \
+#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) \
+#define CHECK_NAME(name, event, args, argnames) \
if (desired_name == #name) { \
return event; \
}
@@ -125,14 +593,46 @@ static jvmtiEvent NameToEvent(const std::string& desired_name) {
#undef CHECK_NAME
}
+#undef FOR_ALL_SUPPORTED_JNI_EVENTS
+#undef FOR_ALL_SUPPORTED_NO_JNI_EVENTS
#undef FOR_ALL_SUPPORTED_EVENTS
-static std::vector<jvmtiEvent> GetRequestedEventList(const std::string& args) {
+
+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& 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));
}
@@ -168,12 +668,17 @@ static jint AgentStart(JavaVM* vm,
args = args.substr(3);
}
- std::vector<jvmtiEvent> events = GetRequestedEventList(args);
+ 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";