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_;
}