Add back the dalvik-style JNI logging and tracing.

Change-Id: I3f8f50c9f9f2cb36f1668150c5e19f6225d15bbb
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 88f3fec..3037eea 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -28,17 +28,21 @@
 namespace art {
 
 void JniAbort(const char* jni_function_name) {
-  std::stringstream os;
+  Thread* self = Thread::Current();
+  const Method* current_method = self->GetCurrentMethod();
 
-  // dvmDumpThread(dvmThreadSelf(), false);
+  std::stringstream os;
   os << "JNI app bug detected";
 
   if (jni_function_name != NULL) {
     os << "\n             in call to " << jni_function_name;
   }
-  // TODO: say what native method we're in...
-  //const Method* method = dvmGetCurrentJNIMethod();
-  //os << "\n             in " << PrettyMethod(method);
+  // TODO: is this useful given that we're about to dump the calling thread's stack?
+  if (current_method != NULL) {
+    os << "\n             from " << PrettyMethod(current_method);
+  }
+  os << "\n";
+  self->Dump(os);
 
   JavaVMExt* vm = Runtime::Current()->GetJavaVM();
   if (vm->check_jni_abort_hook != NULL) {
@@ -267,17 +271,53 @@
 
 #define kFlag_Invocation    0x8000      /* Part of the invocation interface (JavaVM*) */
 
+static const char* gBuiltInPrefixes[] = {
+  "Landroid/",
+  "Lcom/android/",
+  "Lcom/google/android/",
+  "Ldalvik/",
+  "Ljava/",
+  "Ljavax/",
+  "Llibcore/",
+  "Lorg/apache/harmony/",
+  NULL
+};
+
+bool ShouldTrace(JavaVMExt* vm, const Method* method) {
+  // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
+  // when a native method that matches the -Xjnitrace argument calls a JNI function
+  // such as NewByteArray.
+  // If -verbose:third-party-jni is on, we want to log any JNI function calls
+  // made by a third-party native method.
+  std::string classNameStr(method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8());
+  if (!vm->trace.empty() && classNameStr.find(vm->trace) != std::string::npos) {
+    return true;
+  }
+  if (vm->log_third_party_jni) {
+    // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
+    // like part of Android.
+    StringPiece className(classNameStr);
+    for (size_t i = 0; gBuiltInPrefixes[i] != NULL; ++i) {
+      if (className.starts_with(gBuiltInPrefixes[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
 class ScopedCheck {
 public:
   // For JNIEnv* functions.
   explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName) {
-    init(env, flags, functionName, true);
+    init(env, reinterpret_cast<JNIEnvExt*>(env)->vm, flags, functionName, true);
     checkThread(flags);
   }
 
   // For JavaVM* functions.
-  explicit ScopedCheck(bool hasMethod, const char* functionName) {
-    init(NULL, kFlag_Invocation, functionName, hasMethod);
+  explicit ScopedCheck(JavaVM* vm, bool hasMethod, const char* functionName) {
+    init(NULL, vm, kFlag_Invocation, functionName, hasMethod);
   }
 
   bool forceCopy() {
@@ -501,31 +541,17 @@
   void check(bool entry, const char* fmt0, ...) {
     va_list ap;
 
-    bool shouldTrace = false;
-    const Method* method = NULL;
-#if 0
-    if ((gDvm.jniTrace || gDvmJni.logThirdPartyJni) && mHasMethod) {
+    const Method* traceMethod = NULL;
+    if ((!mVm->trace.empty() || mVm->log_third_party_jni) && mHasMethod) {
       // We need to guard some of the invocation interface's calls: a bad caller might
       // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
-      if ((mFlags & kFlag_Invocation) == 0 || dvmThreadSelf() != NULL) {
-        method = dvmGetCurrentJNIMethod();
+      Thread* self = Thread::Current();
+      if ((mFlags & kFlag_Invocation) == 0 || self != NULL) {
+        traceMethod = self->GetCurrentMethod();
       }
     }
-    if (method != NULL) {
-      // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
-      // when a native method that matches the Xjnitrace argument calls a JNI function
-      // such as NewByteArray.
-      if (gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace) != NULL) {
-        shouldTrace = true;
-      }
-      // If -Xjniopts:logThirdPartyJni is on, we want to log any JNI function calls
-      // made by a third-party native method.
-      if (gDvmJni.logThirdPartyJni) {
-        shouldTrace |= method->shouldTrace;
-      }
-    }
-#endif
-    if (shouldTrace) {
+
+    if (traceMethod != NULL && ShouldTrace(mVm, traceMethod)) {
       va_start(ap, fmt0);
       std::string msg;
       for (const char* fmt = fmt0; *fmt;) {
@@ -644,7 +670,7 @@
 
       if (entry) {
         if (mHasMethod) {
-          std::string methodName(PrettyMethod(method, false));
+          std::string methodName(PrettyMethod(traceMethod, false));
           LOG(INFO) << "JNI: " << methodName << " -> " << mFunctionName << "(" << msg << ")";
           mIndent = methodName.size() + 1;
         } else {
@@ -704,8 +730,9 @@
     return Decode<Method*>(ts, reinterpret_cast<jweak>(mid));
   }
 
-  void init(JNIEnv* env, int flags, const char* functionName, bool hasMethod) {
+  void init(JNIEnv* env, JavaVM* vm, int flags, const char* functionName, bool hasMethod) {
     mEnv = reinterpret_cast<JNIEnvExt*>(env);
+    mVm = reinterpret_cast<JavaVMExt*>(vm);
     mFlags = flags;
     mFunctionName = functionName;
 
@@ -989,6 +1016,7 @@
   }
 
   JNIEnvExt* mEnv;
+  JavaVMExt* mVm;
   const char* mFunctionName;
   int mFlags;
   bool mHasMethod;
@@ -2140,31 +2168,31 @@
 class CheckJII {
 public:
   static jint DestroyJavaVM(JavaVM* vm) {
-    ScopedCheck sc(false, __FUNCTION__);
+    ScopedCheck sc(vm, false, __FUNCTION__);
     sc.check(true, "v", vm);
     return CHECK_JNI_EXIT("I", baseVm(vm)->DestroyJavaVM(vm));
   }
 
   static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
-    ScopedCheck sc(false, __FUNCTION__);
+    ScopedCheck sc(vm, false, __FUNCTION__);
     sc.check(true, "vpp", vm, p_env, thr_args);
     return CHECK_JNI_EXIT("I", baseVm(vm)->AttachCurrentThread(vm, p_env, thr_args));
   }
 
   static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
-    ScopedCheck sc(false, __FUNCTION__);
+    ScopedCheck sc(vm, false, __FUNCTION__);
     sc.check(true, "vpp", vm, p_env, thr_args);
     return CHECK_JNI_EXIT("I", baseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args));
   }
 
   static jint DetachCurrentThread(JavaVM* vm) {
-    ScopedCheck sc(true, __FUNCTION__);
+    ScopedCheck sc(vm, true, __FUNCTION__);
     sc.check(true, "v", vm);
     return CHECK_JNI_EXIT("I", baseVm(vm)->DetachCurrentThread(vm));
   }
 
   static jint GetEnv(JavaVM* vm, void** env, jint version) {
-    ScopedCheck sc(true, __FUNCTION__);
+    ScopedCheck sc(vm, true, __FUNCTION__);
     sc.check(true, "v", vm);
     return CHECK_JNI_EXIT("I", baseVm(vm)->GetEnv(vm, env, version));
   }
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index c9e4839..36787d8 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2666,12 +2666,14 @@
 static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
 static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check.
 
-JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni)
+JavaVMExt::JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options)
     : runtime(runtime),
       check_jni_abort_hook(NULL),
-      check_jni(check_jni),
-      verbose_jni(verbose_jni),
+      check_jni(options->check_jni_),
       force_copy(false), // TODO: add a way to enable this
+      verbose_jni(options->IsVerbose("jni")),
+      log_third_party_jni(options->IsVerbose("third-party-jni")),
+      trace(options->jni_trace_),
       work_around_app_jni_bugs(false), // TODO: add a way to enable this
       pins_lock(Mutex::Create("JNI pin table lock")),
       pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 11f7750d..ed66e64 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -9,6 +9,7 @@
 #include "indirect_reference_table.h"
 #include "macros.h"
 #include "reference_table.h"
+#include "runtime.h"
 
 #include <string>
 
@@ -18,7 +19,6 @@
 class Libraries;
 class Method;
 class Mutex;
-class Runtime;
 class Thread;
 
 void JniAbort(const char* jni_function_name);
@@ -27,7 +27,7 @@
 template<typename T> T AddLocalReference(JNIEnv*, const Object*);
 
 struct JavaVMExt : public JavaVM {
-  JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni);
+  JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options);
   ~JavaVMExt();
 
   /**
@@ -51,10 +51,15 @@
   // Used for testing. By default, we'll LOG(FATAL) the reason.
   void (*check_jni_abort_hook)(const std::string& reason);
 
+  // Extra checking.
   bool check_jni;
-  bool verbose_jni;
   bool force_copy;
 
+  // Extra diagnostics.
+  bool verbose_jni;
+  bool log_third_party_jni;
+  std::string trace;
+
   // Used to provide compatibility for apps that assumed direct references.
   bool work_around_app_jni_bugs;
 
diff --git a/src/runtime.cc b/src/runtime.cc
index 0e5da23..3934571 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -250,6 +250,8 @@
       parsed->stack_size_ = size;
     } else if (option.starts_with("-D")) {
       parsed->properties_.push_back(option.substr(strlen("-D")).data());
+    } else if (option.starts_with("-Xjnitrace:")) {
+      parsed->jni_trace_ = option.substr(strlen("-Xjnitrace:")).data();
     } else if (option.starts_with("-verbose:")) {
       std::vector<std::string> verbose_options;
       Split(option.substr(strlen("-verbose:")).data(), ',', verbose_options);
@@ -328,8 +330,7 @@
 
   BlockSignals();
 
-  bool verbose_jni = options->verbose_.find("jni") != options->verbose_.end();
-  java_vm_ = new JavaVMExt(this, options->check_jni_, verbose_jni);
+  java_vm_ = new JavaVMExt(this, options.get());
 
   if (!Thread::Startup()) {
     return false;
diff --git a/src/runtime.h b/src/runtime.h
index 6a5d755..9bb60d9 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -42,6 +42,7 @@
     std::vector<const DexFile*> boot_class_path_;
     const char* boot_image_;
     bool check_jni_;
+    std::string jni_trace_;
     size_t heap_initial_size_;
     size_t heap_maximum_size_;
     size_t stack_size_;
@@ -51,6 +52,10 @@
     std::tr1::unordered_set<std::string> verbose_;
     std::vector<std::string> properties_;
 
+    bool IsVerbose(const std::string& key) const {
+      return verbose_.find(key) != verbose_.end();
+    }
+
    private:
     ParsedOptions() {};
   };
diff --git a/src/runtime_linux.cc b/src/runtime_linux.cc
index c87825c..1461528 100644
--- a/src/runtime_linux.cc
+++ b/src/runtime_linux.cc
@@ -44,23 +44,27 @@
 
   // backtrace_symbols(3) gives us lines like this:
   // "/usr/local/google/home/enh/a1/out/host/linux-x86/bin/../lib/libartd.so(_ZN3art7Runtime13PlatformAbortEPKci+0x15b) [0xf76c5af3]"
+  // "[0xf7b62057]"
 
   // We extract the pieces and demangle, so we can produce output like this:
   // libartd.so:-1] 	#00 art::Runtime::PlatformAbort(char const*, int) +0x15b [0xf770dd51]
 
   for (size_t i = 0; i < frame_count; ++i) {
     std::string text(symbols[i]);
+    std::string filename("??");
+    std::string function_name;
 
     size_t index = text.find('(');
-    std::string filename(text.substr(0, index));
-    text.erase(0, index + 1);
+    if (index != std::string::npos) {
+      filename = text.substr(0, index);
+      text.erase(0, index + 1);
 
-    index = text.find_first_of("+)");
-    std::string function_name(Demangle(text.substr(0, index)));
-    text.erase(0, index);
-    index = text.find(')');
-    text.erase(index, 1);
-
+      index = text.find_first_of("+)");
+      function_name = Demangle(text.substr(0, index));
+      text.erase(0, index);
+      index = text.find(')');
+      text.erase(index, 1);
+    }
     std::string log_line(StringPrintf("\t#%02d ", i) + function_name + text);
     LogMessage(filename.c_str(), -1, ERROR, -1).stream() << log_line;
   }
diff --git a/src/thread.cc b/src/thread.cc
index 5a612dd..f5c1343 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -4,8 +4,10 @@
 
 #include <pthread.h>
 #include <sys/mman.h>
+
 #include <algorithm>
 #include <cerrno>
+#include <iostream>
 #include <list>
 
 #include "class_linker.h"
@@ -13,8 +15,8 @@
 #include "jni_internal.h"
 #include "object.h"
 #include "runtime.h"
-#include "utils.h"
 #include "runtime_support.h"
+#include "utils.h"
 
 namespace art {
 
@@ -217,6 +219,10 @@
   return thread;
 }
 
+void Thread::Dump(std::ostream& os) const {
+  os << "UNIMPLEMENTED: Thread::Dump\n";
+}
+
 pid_t Thread::GetTid() const {
   return gettid();
 }
diff --git a/src/thread.h b/src/thread.h
index ae07fa6..6933962 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -4,6 +4,8 @@
 #define ART_SRC_THREAD_H_
 
 #include <pthread.h>
+
+#include <iosfwd>
 #include <list>
 
 #include "dex_file.h"
@@ -126,7 +128,7 @@
   Frame() : sp_(NULL) {}
 
   const Method* GetMethod() const {
-    return *sp_;
+    return (sp_ != NULL) ? *sp_ : NULL;
   }
 
   bool HasNext() const {
@@ -234,6 +236,8 @@
     return reinterpret_cast<Thread*>(thread);
   }
 
+  void Dump(std::ostream& os) const;
+
   uint32_t GetId() const {
     return id_;
   }
@@ -252,6 +256,12 @@
     return exception_;
   }
 
+  // Returns the Method* for the current method.
+  // This is used by the JNI implementation for logging and diagnostic purposes.
+  const Method* GetCurrentMethod() const {
+    return top_of_managed_stack_.GetMethod();
+  }
+
   Frame GetTopOfStack() const {
     return top_of_managed_stack_;
   }