Implement the zygote debug options (late-enabling -Xcheck:jni and so on).
Change-Id: I03772e7fb95fce1a19dac13f6c5f870164a4d874
diff --git a/src/dalvik_system_Zygote.cc b/src/dalvik_system_Zygote.cc
index 3751e13..00a011c 100644
--- a/src/dalvik_system_Zygote.cc
+++ b/src/dalvik_system_Zygote.cc
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-#include "jni_internal.h"
-#include "JNIHelp.h"
-#include "ScopedLocalRef.h"
-#include "ScopedPrimitiveArray.h"
-#include "ScopedUtfChars.h"
-
-#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
-
#include <grp.h>
#include <paths.h>
#include <stdlib.h>
@@ -30,6 +22,13 @@
#include <sys/wait.h>
#include <unistd.h>
+#include "debugger.h"
+#include "jni_internal.h"
+#include "JniConstants.h"
+#include "JNIHelp.h"
+#include "ScopedLocalRef.h"
+#include "ScopedPrimitiveArray.h"
+#include "ScopedUtfChars.h"
#include "thread.h"
namespace art {
@@ -52,7 +51,7 @@
// This signal handler is for zygote mode, since the zygote must reap its children
-void sigchldHandler(int s) {
+void SigChldHandler(int s) {
pid_t pid;
int status;
@@ -104,10 +103,10 @@
//
// This ends up being called repeatedly before each fork(), but there's
// no real harm in that.
-void setSigchldHandler() {
+void SetSigChldHandler() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
- sa.sa_handler = sigchldHandler;
+ sa.sa_handler = SigChldHandler;
int err = sigaction(SIGCHLD, &sa, NULL);
if (err < 0) {
@@ -116,7 +115,7 @@
}
// Set the SIGCHLD handler back to default behavior in zygote children
-void unsetSigchldHandler() {
+void UnsetSigChldHandler() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
@@ -129,7 +128,7 @@
// Calls POSIX setgroups() using the int[] object as an argument.
// A NULL argument is tolerated.
-int setGids(JNIEnv* env, jintArray javaGids) {
+int SetGids(JNIEnv* env, jintArray javaGids) {
if (javaGids == NULL) {
return 0;
}
@@ -148,7 +147,7 @@
// treated as an empty array.
//
// -1 is returned on error.
-int setRlimits(JNIEnv* env, jobjectArray javaRlimits) {
+int SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
if (javaRlimits == NULL) {
return 0;
}
@@ -178,7 +177,7 @@
// Set Linux capability flags.
//
// Returns 0 on success, errno on failure.
-int setCapabilities(int64_t permitted, int64_t effective) {
+int SetCapabilities(int64_t permitted, int64_t effective) {
#ifdef HAVE_ANDROID_OS
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata;
@@ -200,13 +199,74 @@
return 0;
}
+void EnableDebugFeatures(uint32_t debug_flags) {
+ // Must match values in dalvik.system.Zygote.
+ enum {
+ DEBUG_ENABLE_DEBUGGER = 1,
+ DEBUG_ENABLE_CHECKJNI = 1 << 1,
+ DEBUG_ENABLE_ASSERT = 1 << 2,
+ DEBUG_ENABLE_SAFEMODE = 1 << 3,
+ DEBUG_ENABLE_JNI_LOGGING = 1 << 4,
+ };
+
+ if ((debug_flags & DEBUG_ENABLE_CHECKJNI) != 0) {
+ Runtime* runtime = Runtime::Current();
+ JavaVMExt* vm = runtime->GetJavaVM();
+ if (!vm->check_jni) {
+ LOG(DEBUG) << "Late-enabling -Xcheck:jni";
+ vm->EnableCheckJni();
+ // There's only one thread running at this point, so only one JNIEnv to fix up.
+ Thread::Current()->GetJniEnv()->EnableCheckJni();
+ } else {
+ LOG(DEBUG) << "Not late-enabling -Xcheck:jni (already on)";
+ }
+ debug_flags &= ~DEBUG_ENABLE_CHECKJNI;
+ }
+
+ if ((debug_flags & DEBUG_ENABLE_JNI_LOGGING) != 0) {
+ Runtime::Current()->GetJavaVM()->log_third_party_jni = true;
+ debug_flags &= ~DEBUG_ENABLE_JNI_LOGGING;
+ }
+
+ Dbg::SetJdwpAllowed((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0);
+#ifdef HAVE_ANDROID_OS
+ if ((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0) {
+ /* To let a non-privileged gdbserver attach to this
+ * process, we must set its dumpable bit flag. However
+ * we are not interested in generating a coredump in
+ * case of a crash, so also set the coredump size to 0
+ * to disable that
+ */
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ PLOG(ERROR) << "could not set dumpable bit flag for pid " << getpid();
+ } else {
+ struct rlimit rl;
+ rl.rlim_cur = 0;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+ PLOG(ERROR) << "could not disable core file generation for pid " << getpid();
+ }
+ }
+ }
+#endif
+ debug_flags &= ~DEBUG_ENABLE_DEBUGGER;
+
+ // These two are for backwards compatibility with Dalvik.
+ debug_flags &= ~DEBUG_ENABLE_ASSERT;
+ debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
+
+ if (debug_flags != 0) {
+ LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags);
+ }
+}
+
#ifdef HAVE_ANDROID_OS
extern "C" int gMallocLeakZygoteChild;
#endif
// Utility routine to fork zygote and specialize the child process.
-pid_t forkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
- jint debugFlags, jobjectArray javaRlimits,
+pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+ jint debug_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities)
{
Runtime* runtime = Runtime::Current();
@@ -215,7 +275,7 @@
LOG(FATAL) << "pre-fork heap failed";
}
- setSigchldHandler();
+ SetSigChldHandler();
// Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
Thread* self = Thread::Current();
@@ -238,12 +298,12 @@
}
#endif // HAVE_ANDROID_OS
- int err = setGids(env, javaGids);
+ int err = SetGids(env, javaGids);
if (err < 0) {
PLOG(FATAL) << "cannot setgroups()";
}
- err = setRlimits(env, javaRlimits);
+ err = SetRLimits(env, javaRlimits);
if (err < 0) {
PLOG(FATAL) << "cannot setrlimit()";
}
@@ -258,7 +318,7 @@
PLOG(FATAL) << "cannot setuid(" << uid << ")";
}
- err = setCapabilities(permittedCapabilities, effectiveCapabilities);
+ err = SetCapabilities(permittedCapabilities, effectiveCapabilities);
if (err != 0) {
PLOG(FATAL) << "cannot set capabilities ("
<< permittedCapabilities << "," << effectiveCapabilities << ")";
@@ -267,10 +327,9 @@
// Our system thread ID, etc, has changed so reset Thread state.
self->InitAfterFork();
- // configure additional debug options
- // enableDebugFeatures(debugFlags); // TODO: debugger
+ EnableDebugFeatures(debug_flags);
- unsetSigchldHandler();
+ UnsetSigChldHandler();
runtime->DidForkFromZygote();
} else if (pid > 0) {
// the parent process
@@ -279,15 +338,15 @@
}
jint Zygote_nativeForkAndSpecialize(JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
- jint debugFlags, jobjectArray rlimits) {
- return forkAndSpecializeCommon(env, uid, gid, gids, debugFlags, rlimits, 0, 0);
+ jint debug_flags, jobjectArray rlimits) {
+ return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0);
}
jint Zygote_nativeForkSystemServer(JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
- jint debugFlags, jobjectArray rlimits,
+ jint debug_flags, jobjectArray rlimits,
jlong permittedCapabilities, jlong effectiveCapabilities) {
- pid_t pid = forkAndSpecializeCommon(env, uid, gid, gids,
- debugFlags, rlimits,
+ pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
+ debug_flags, rlimits,
permittedCapabilities, effectiveCapabilities);
if (pid > 0) {
// The zygote process checks whether the child process has died or not.
diff --git a/src/debugger.cc b/src/debugger.cc
index c8f3cec..1580c76 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -20,6 +20,9 @@
namespace art {
+// JDWP is allowed unless the Zygote forbids it.
+static bool gJdwpAllowed = true;
+
// Was there a -Xrunjdwp or -agent argument on the command-line?
static bool gJdwpConfigured = false;
@@ -150,6 +153,10 @@
UNIMPLEMENTED(FATAL);
}
+void Dbg::SetJdwpAllowed(bool allowed) {
+ gJdwpAllowed = allowed;
+}
+
DebugInvokeReq* Dbg::GetInvokeReq() {
UNIMPLEMENTED(FATAL);
return NULL;
diff --git a/src/debugger.h b/src/debugger.h
index 388248b..2c2ade7 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -68,6 +68,8 @@
static bool DebuggerStartup();
static void DebuggerShutdown();
+ static void SetJdwpAllowed(bool allowed);
+
// Return the DebugInvokeReq for the current thread.
static DebugInvokeReq* GetInvokeReq();
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 02c8513..0ece455 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2525,13 +2525,13 @@
vm(vm),
local_ref_cookie(IRT_FIRST_SEGMENT),
locals(kLocalsInitial, kLocalsMax, kLocal),
- check_jni(vm->check_jni),
+ check_jni(false),
work_around_app_jni_bugs(vm->work_around_app_jni_bugs),
critical(false),
monitors("monitors", kMonitorsInitial, kMonitorsMax) {
functions = unchecked_functions = &gNativeInterface;
- if (check_jni) {
- functions = GetCheckJniNativeInterface();
+ if (vm->check_jni) {
+ EnableCheckJni();
}
// The JniEnv local reference values must be at a consistent offset or else cross-compilation
// errors will ensue.
@@ -2542,6 +2542,11 @@
JNIEnvExt::~JNIEnvExt() {
}
+void JNIEnvExt::EnableCheckJni() {
+ check_jni = true;
+ functions = GetCheckJniNativeInterface();
+}
+
void JNIEnvExt::DumpReferenceTables() {
locals.Dump();
monitors.Dump();
@@ -2660,7 +2665,7 @@
JavaVMExt::JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options)
: runtime(runtime),
check_jni_abort_hook(NULL),
- check_jni(options->check_jni_),
+ check_jni(false),
force_copy(false), // TODO: add a way to enable this
verbose_jni(options->IsVerbose("jni")),
log_third_party_jni(options->IsVerbose("third-party-jni")),
@@ -2675,8 +2680,8 @@
libraries_lock("JNI shared libraries map lock"),
libraries(new Libraries) {
functions = unchecked_functions = &gInvokeInterface;
- if (check_jni) {
- functions = GetCheckJniInvokeInterface();
+ if (options->check_jni_) {
+ EnableCheckJni();
}
}
@@ -2684,6 +2689,11 @@
delete libraries;
}
+void JavaVMExt::EnableCheckJni() {
+ check_jni = true;
+ functions = GetCheckJniInvokeInterface();
+}
+
void JavaVMExt::DumpReferenceTables() {
{
MutexLock mu(globals_lock);
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 09efd68..25e9e62 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -82,6 +82,8 @@
void DumpReferenceTables();
+ void EnableCheckJni();
+
void VisitRoots(Heap::RootVisitor*, void*);
Runtime* runtime;
@@ -126,6 +128,8 @@
void DumpReferenceTables();
+ void EnableCheckJni();
+
void PushFrame(int capacity);
void PopFrame();