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
diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md
index bc46882..a0a7dd7 100644
--- a/tools/ti-fast/README.md
+++ b/tools/ti-fast/README.md
@@ -21,6 +21,10 @@
   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 b147add..85c433b 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -36,6 +36,13 @@
 // 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 @@
 }
 
 // Setup for all supported events. Give a macro with fun(name, event_num, args)
-#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 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 GENERATE_EMPTY_FUNCTION(name, number, args) \
-    static void JNICALL empty ## name  args { }
+#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) \
+    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;
     }
-FOR_ALL_SUPPORTED_EVENTS(GENERATE_LOG_FUNCTION)
+  }
+
+  ~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(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 @@
 #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 @@
     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";