从Zygote的启动开始看代码
####app_process
/frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, const char* const argv[])
{
// These are global variables in ProcessState.cpp
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for (int i=0; i<argc; i++) {
mArgLen += strlen(argv[i]) + 1;
}
mArgLen--;
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
while (i < argc) {
const char* arg = argv[i++];
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
className = arg;
break;
}
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mParentDir = parentDir;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
} else if (className) {
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}
//
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");
其中startSystemServer 由init.rc指定,在目录system\core\rootdir中的init.rc.
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
代码进入到base/core/jni/AndroidRuntime.cpp里面。
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const char* options)
{
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
blockSigpipe();
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
if (strcmp(options, "start-system-server") == 0) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("Unable to register all android natives\n");
return;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
jstring optionsStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(2, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
optionsStr = env->NewStringUTF(options);
env->SetObjectArrayElement(strArray, 1, optionsStr);
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
LOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
}
//init.rc 已经建立global 环境
# setup the global environment
export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
export LD_LIBRARY_PATH /vendor/lib:/system/lib
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ASEC_MOUNTPOINT /mnt/asec
export LOOP_MOUNTPOINT /mnt/obb
export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
//start vm
/*
* Start the Dalvik Virtual Machine.
*
* Various arguments, most determined by system properties, are passed in.
* The "mOptions" vector is updated.
*
* Returns 0 on success.
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
int result = -1;
JavaVMInitArgs initArgs;
JavaVMOption opt;
char propBuf[PROPERTY_VALUE_MAX];
char stackTraceFileBuf[PROPERTY_VALUE_MAX];
char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
char* stackTraceFile = NULL;
bool checkJni = false;
bool checkDexSum = false;
bool logStdio = false;
enum {
kEMDefault,
kEMIntPortable,
kEMIntFast,
#if defined(WITH_JIT)
kEMJitCompiler,
#endif
} executionMode = kEMDefault;
property_get("dalvik.vm.checkjni", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkJni = true;
} else if (strcmp(propBuf, "false") != 0) {
/* property is neither true nor false; fall back on kernel parameter */
property_get("ro.kernel.android.checkjni", propBuf, "");
if (propBuf[0] == '1') {
checkJni = true;
}
}
property_get("dalvik.vm.execution-mode", propBuf, "");
if (strcmp(propBuf, "int:portable") == 0) {
executionMode = kEMIntPortable;
} else if (strcmp(propBuf, "int:fast") == 0) {
executionMode = kEMIntFast;
#if defined(WITH_JIT)
} else if (strcmp(propBuf, "int:jit") == 0) {
executionMode = kEMJitCompiler;
#endif
}
property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, "");
property_get("dalvik.vm.check-dex-sum", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkDexSum = true;
}
property_get("log.redirect-stdio", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
logStdio = true;
}
strcpy(enableAssertBuf, "-ea:");
property_get("dalvik.vm.enableassertions", enableAssertBuf+4, "");
strcpy(jniOptsBuf, "-Xjniopts:");
property_get("dalvik.vm.jniopts", jniOptsBuf+10, "");
/* route exit() to our handler */
opt.extraInfo = (void*) runtime_exit;
opt.optionString = "exit";
mOptions.add(opt);
/* route fprintf() to our handler */
opt.extraInfo = (void*) runtime_vfprintf;
opt.optionString = "vfprintf";
mOptions.add(opt);
/* register the framework-specific "is sensitive thread" hook */
opt.extraInfo = (void*) runtime_isSensitiveThread;
opt.optionString = "sensitiveThread";
mOptions.add(opt);
opt.extraInfo = NULL;
/* enable verbose; standard options are { jni, gc, class } */
//options[curOpt++].optionString = "-verbose:jni";
opt.optionString = "-verbose:gc";
mOptions.add(opt);
//options[curOpt++].optionString = "-verbose:class";
/*
* The default starting and maximum size of the heap. Larger
* values should be specified in a product property override.
*/
strcpy(heapstartsizeOptsBuf, "-Xms");
property_get("dalvik.vm.heapstartsize", heapstartsizeOptsBuf+4, "4m");
opt.optionString = heapstartsizeOptsBuf;
mOptions.add(opt);
strcpy(heapsizeOptsBuf, "-Xmx");
property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
opt.optionString = heapsizeOptsBuf;
mOptions.add(opt);
// Increase the main thread's interpreter stack size for bug 6315322.
opt.optionString = "-XX:mainThreadStackSize=24K";
mOptions.add(opt);
strcpy(heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
property_get("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf+20, "");
if (heapgrowthlimitOptsBuf[20] != '\0') {
opt.optionString = heapgrowthlimitOptsBuf;
mOptions.add(opt);
}
/*
* Enable or disable dexopt features, such as bytecode verification and
* calculation of register maps for precise GC.
*/
property_get("dalvik.vm.dexopt-flags", dexoptFlagsBuf, "");
if (dexoptFlagsBuf[0] != '\0') {
const char* opc;
const char* val;
opc = strstr(dexoptFlagsBuf, "v="); /* verification */
if (opc != NULL) {
switch (*(opc+2)) {
case 'n': val = "-Xverify:none"; break;
case 'r': val = "-Xverify:remote"; break;
case 'a': val = "-Xverify:all"; break;
default: val = NULL; break;
}
if (val != NULL) {
opt.optionString = val;
mOptions.add(opt);
}
}
opc = strstr(dexoptFlagsBuf, "o="); /* optimization */
if (opc != NULL) {
switch (*(opc+2)) {
case 'n': val = "-Xdexopt:none"; break;
case 'v': val = "-Xdexopt:verified"; break;
case 'a': val = "-Xdexopt:all"; break;
case 'f': val = "-Xdexopt:full"; break;
default: val = NULL; break;
}
if (val != NULL) {
opt.optionString = val;
mOptions.add(opt);
}
}
opc = strstr(dexoptFlagsBuf, "m=y"); /* register map */
if (opc != NULL) {
opt.optionString = "-Xgenregmap";
mOptions.add(opt);
/* turn on precise GC while we're at it */
opt.optionString = "-Xgc:precise";
mOptions.add(opt);
}
}
/* enable debugging; set suspend=y to pause during VM init */
/* use android ADB transport */
opt.optionString =
"-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
mOptions.add(opt);
ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
if (checkJni) {
/* extended JNI checking */
opt.optionString = "-Xcheck:jni";
mOptions.add(opt);
/* set a cap on JNI global references */
opt.optionString = "-Xjnigreflimit:2000";
mOptions.add(opt);
/* with -Xcheck:jni, this provides a JNI function call trace */
//opt.optionString = "-verbose:jni";
//mOptions.add(opt);
}
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
property_get("dalvik.vm.lockprof.threshold", propBuf, "");
if (strlen(propBuf) > 0) {
strcpy(lockProfThresholdBuf, "-Xlockprofthreshold:");
strcat(lockProfThresholdBuf, propBuf);
opt.optionString = lockProfThresholdBuf;
mOptions.add(opt);
}
#if defined(WITH_JIT)
/* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
property_get("dalvik.vm.jit.op", propBuf, "");
if (strlen(propBuf) > 0) {
strcpy(jitOpBuf, "-Xjitop:");
strcat(jitOpBuf, propBuf);
opt.optionString = jitOpBuf;
mOptions.add(opt);
}
/* Force interpreter-only mode for selected methods */
char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
property_get("dalvik.vm.jit.method", propBuf, "");
if (strlen(propBuf) > 0) {
strcpy(jitMethodBuf, "-Xjitmethod:");
strcat(jitMethodBuf, propBuf);
opt.optionString = jitMethodBuf;
mOptions.add(opt);
}
#endif
if (executionMode == kEMIntPortable) {
opt.optionString = "-Xint:portable";
mOptions.add(opt);
} else if (executionMode == kEMIntFast) {
opt.optionString = "-Xint:fast";
mOptions.add(opt);
#if defined(WITH_JIT)
} else if (executionMode == kEMJitCompiler) {
opt.optionString = "-Xint:jit";
mOptions.add(opt);
#endif
}
if (checkDexSum) {
/* perform additional DEX checksum tests */
opt.optionString = "-Xcheckdexsum";
mOptions.add(opt);
}
if (logStdio) {
/* convert stdout/stderr to log messages */
opt.optionString = "-Xlog-stdio";
mOptions.add(opt);
}
if (enableAssertBuf[4] != '\0') {
/* accept "all" to mean "all classes and packages" */
if (strcmp(enableAssertBuf+4, "all") == 0)
enableAssertBuf[3] = '\0';
ALOGI("Assertions enabled: '%s'\n", enableAssertBuf);
opt.optionString = enableAssertBuf;
mOptions.add(opt);
} else {
ALOGV("Assertions disabled\n");
}
if (jniOptsBuf[10] != '\0') {
ALOGI("JNI options: '%s'\n", jniOptsBuf);
opt.optionString = jniOptsBuf;
mOptions.add(opt);
}
if (stackTraceFileBuf[0] != '\0') {
static const char* stfOptName = "-Xstacktracefile:";
stackTraceFile = (char*) malloc(strlen(stfOptName) +
strlen(stackTraceFileBuf) +1);
strcpy(stackTraceFile, stfOptName);
strcat(stackTraceFile, stackTraceFileBuf);
opt.optionString = stackTraceFile;
mOptions.add(opt);
}
/* extra options; parse this late so it overrides others */
property_get("dalvik.vm.extra-opts", extraOptsBuf, "");
parseExtraOpts(extraOptsBuf);
/* Set the properties for locale */
{
char langOption[sizeof("-Duser.language=") + 3];
char regionOption[sizeof("-Duser.region=") + 3];
strcpy(langOption, "-Duser.language=");
strcpy(regionOption, "-Duser.region=");
readLocale(langOption, regionOption);
opt.extraInfo = NULL;
opt.optionString = langOption;
mOptions.add(opt);
opt.optionString = regionOption;
mOptions.add(opt);
}
/*
* We don't have /tmp on the device, but we often have an SD card. Apps
* shouldn't use this, but some test suites might want to exercise it.
*/
opt.optionString = "-Djava.io.tmpdir=/sdcard";
mOptions.add(opt);
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
goto bail;
}
result = 0;
bail:
free(stackTraceFile);
return result;
}
能发现JNI_CreateJavaVM 的原型在dalvik/vm/Jni.cpp 可以找到
Cpp代码
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
if (args->version < JNI_VERSION_1_2) {
return JNI_EVERSION;
}
// TODO: don't allow creation of multiple VMs -- one per customer for now
/* zero globals; not strictly necessary the first time a VM is started */
memset(&gDvm, 0, sizeof(gDvm));
/*
* Set up structures for JNIEnv and VM.
*/
JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
pVM->funcTable = &gInvokeInterface;
pVM->envList = NULL;
dvmInitMutex(&pVM->envListLock);
UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
/*
* Convert JNI args to argv.
*
* We have to pull out vfprintf/exit/abort, because they use the
* "extraInfo" field to pass function pointer "hooks" in. We also
* look for the -Xcheck:jni stuff here.
*/
int argc = 0;
for (int i = 0; i < args->nOptions; i++) {
const char* optStr = args->options[i].optionString;
if (optStr == NULL) {
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
return JNI_ERR;
} else if (strcmp(optStr, "vfprintf") == 0) {
gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
} else if (strcmp(optStr, "exit") == 0) {
gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
} else if (strcmp(optStr, "abort") == 0) {
gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
} else if (strcmp(optStr, "sensitiveThread") == 0) {
gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
} else if (strcmp(optStr, "-Xcheck:jni") == 0) {
gDvmJni.useCheckJni = true;
} else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
char* jniOpts = strdup(optStr + 10);
size_t jniOptCount = 1;
for (char* p = jniOpts; *p != 0; ++p) {
if (*p == ',') {
++jniOptCount;
*p = 0;
}
}
char* jniOpt = jniOpts;
for (size_t i = 0; i < jniOptCount; ++i) {
if (strcmp(jniOpt, "warnonly") == 0) {
gDvmJni.warnOnly = true;
} else if (strcmp(jniOpt, "forcecopy") == 0) {
gDvmJni.forceCopy = true;
} else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {
gDvmJni.logThirdPartyJni = true;
} else {
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
jniOpt);
return JNI_ERR;
}
jniOpt += strlen(jniOpt) + 1;
}
free(jniOpts);
} else {
/* regular option */
argv[argc++] = optStr;
}
}
if (gDvmJni.useCheckJni) {
dvmUseCheckedJniVm(pVM);
}
if (gDvmJni.jniVm != NULL) {
dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
return JNI_ERR;
}
gDvmJni.jniVm = (JavaVM*) pVM;
/*
* Create a JNIEnv for the main thread. We need to have something set up
* here because some of the class initialization we do when starting
* up the VM will call into native code.
*/
JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
/* Initialize VM. */
gDvm.initializing = true;
std::string status =
dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
gDvm.initializing = false;
if (!status.empty()) {
free(pEnv);
free(pVM);
ALOGW("CreateJavaVM failed: %s", status.c_str());
return JNI_ERR;
}
/*
* Success! Return stuff to caller.
*/
dvmChangeStatus(NULL, THREAD_NATIVE);
*p_env = (JNIEnv*) pEnv;
*p_vm = (JavaVM*) pVM;
ALOGV("CreateJavaVM succeeded");
return JNI_OK;
}
然后调用Jni.cpp create JNI environment.
/*
* Create a new JNIEnv struct and add it to the VM's list.
*
* "self" will be NULL for the main thread, since the VM hasn't started
* yet; the value will be filled in later.
*/
JNIEnv* dvmCreateJNIEnv(Thread* self) {
JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
//if (self != NULL)
// LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
assert(vm != NULL);
JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
newEnv->funcTable = &gNativeInterface;
if (self != NULL) {
dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
assert(newEnv->envThreadId != 0);
} else {
/* make it obvious if we fail to initialize these later */
newEnv->envThreadId = 0x77777775;
newEnv->self = (Thread*) 0x77777779;
}
if (gDvmJni.useCheckJni) {
dvmUseCheckedJniEnv(newEnv);
}
ScopedPthreadMutexLock lock(&vm->envListLock);
/* insert at head of list */
newEnv->next = vm->envList;
assert(newEnv->prev == NULL);
if (vm->envList == NULL) {
// rare, but possible
vm->envList = newEnv;
} else {
vm->envList->prev = newEnv;
}
vm->envList = newEnv;
//if (self != NULL)
// LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
return (JNIEnv*) newEnv;
}
让我们看看JNI environment type
struct JavaVMExt;
struct JNIEnvExt {
const struct JNINativeInterface* funcTable; /* must be first */
const struct JNINativeInterface* baseFuncTable;
u4 envThreadId;
Thread* self;
/* if nonzero, we are in a "critical" JNI call */
int critical;
struct JNIEnvExt* prev;
struct JNIEnvExt* next;
};
struct JavaVMExt {
const struct JNIInvokeInterface* funcTable; /* must be first */
const struct JNIInvokeInterface* baseFuncTable;
/* head of list of JNIEnvs associated with this VM */
JNIEnvExt* envList;
pthread_mutex_t envListLock;
};
启动dvm 的方法。你能在Jni.cpp 发现启动代码
std::string status =
dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
在Dalvik/vm/Init.cpp中
/*
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
ScopedShutdown scopedShutdown;
assert(gDvm.initializing);
//init arguments
ALOGV("VM init args (%d):", argc);
for (int i = 0; i < argc; i++) {
ALOGV(" %d: '%s'", i, argv[i]);
}
setCommandLineDefaults();
/*
* Process the option flags (if any).
*/
int cc = processOptions(argc, argv, ignoreUnrecognized);
if (cc != 0) {
if (cc < 0) {
dvmFprintf(stderr, "\n");
usage("dalvikvm");
}
return "syntax error";
}
#if WITH_EXTRA_GC_CHECKS > 1
/* only "portable" interp has the extra goodies */
if (gDvm.executionMode != kExecutionModeInterpPortable) {
ALOGI("Switching to 'portable' interpreter for GC checks");
gDvm.executionMode = kExecutionModeInterpPortable;
}
#endif
/* Configure group scheduling capabilities */
if (!access("/dev/cpuctl/tasks", F_OK)) {
ALOGV("Using kernel group scheduling, err: %s", strerror(errno));
gDvm.kernelGroupScheduling = 1;
} else {
ALOGV("Using kernel scheduler policies");
}
/* configure signal handling */
if (!gDvm.reduceSignals)
blockSignals();
/* verify system page size */
if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
return StringPrintf("expected page size %d, got %d",
SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
}
/* mterp setup */
ALOGV("Using executionMode %d", gDvm.executionMode);
dvmCheckAsmConstants();
/*
* Initialize components.
*/
if (!dvmAllocTrackerStartup()) {
return "dvmAllocTrackerStartup failed";
}
if (!dvmGcStartup()) {
return "dvmGcStartup failed";
}
if (!dvmThreadStartup()) {
return "dvmThreadStartup failed";
}
if (!dvmInlineNativeStartup()) {
return "dvmInlineNativeStartup";
}
if (!dvmRegisterMapStartup()) {
return "dvmRegisterMapStartup failed";
}
if (!dvmInstanceofStartup()) {
return "dvmInstanceofStartup failed";
}
if (!dvmClassStartup()) {
return "dvmClassStartup failed";
}
/*
* At this point, the system is guaranteed to be sufficiently
* initialized that we can look up classes and class members. This
* call populates the gDvm instance with all the class and member
* references that the VM wants to use directly.
*/
if (!dvmFindRequiredClassesAndMembers()) {
return "dvmFindRequiredClassesAndMembers failed";
}
if (!dvmStringInternStartup()) {
return "dvmStringInternStartup failed";
}
if (!dvmNativeStartup()) {
return "dvmNativeStartup failed";
}
if (!dvmInternalNativeStartup()) {
return "dvmInternalNativeStartup failed";
}
if (!dvmJniStartup()) {
return "dvmJniStartup failed";
}
if (!dvmProfilingStartup()) {
return "dvmProfilingStartup failed";
}
/*
* Create a table of methods for which we will substitute an "inline"
* version for performance.
*/
if (!dvmCreateInlineSubsTable()) {
return "dvmCreateInlineSubsTable failed";
}
/*
* Miscellaneous class library validation.
*/
if (!dvmValidateBoxClasses()) {
return "dvmValidateBoxClasses failed";
}
/*
* Do the last bits of Thread struct initialization we need to allow
* JNI calls to work.
*/
if (!dvmPrepMainForJni(pEnv)) {
return "dvmPrepMainForJni failed";
}
/*
* Explicitly initialize java.lang.Class. This doesn't happen
* automatically because it's allocated specially (it's an instance
* of itself). Must happen before registration of system natives,
* which make some calls that throw assertions if the classes they
* operate on aren't initialized.
*/
if (!dvmInitClass(gDvm.classJavaLangClass)) {
return "couldn't initialized java.lang.Class";
}
/*
* Register the system native methods, which are registered through JNI.
*/
if (!registerSystemNatives(pEnv)) {
return "couldn't register system natives";
}
/*
* Do some "late" initialization for the memory allocator. This may
* allocate storage and initialize classes.
*/
if (!dvmCreateStockExceptions()) {
return "dvmCreateStockExceptions failed";
}
/*
* At this point, the VM is in a pretty good state. Finish prep on
* the main thread (specifically, create a java.lang.Thread object to go
* along with our Thread struct). Note we will probably be executing
* some interpreted class initializer code in here.
*/
if (!dvmPrepMainThread()) {
return "dvmPrepMainThread failed";
}
/*
* Make sure we haven't accumulated any tracked references. The main
* thread should be starting with a clean slate.
*/
if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
{
ALOGW("Warning: tracked references remain post-initialization");
dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
}
/* general debugging setup */
if (!dvmDebuggerStartup()) {
return "dvmDebuggerStartup failed";
}
if (!dvmGcStartupClasses()) {
return "dvmGcStartupClasses failed";
}
/*
* Init for either zygote mode or non-zygote mode. The key difference
* is that we don't start any additional threads in Zygote mode.
*/
if (gDvm.zygote) {
if (!initZygote()) {
return "initZygote failed";
}
} else {
if (!dvmInitAfterZygote()) {
return "dvmInitAfterZygote failed";
}
}
#ifndef NDEBUG
if (!dvmTestHash())
ALOGE("dvmTestHash FAILED");
if (false /*noisy!*/ && !dvmTestIndirectRefTable())
ALOGE("dvmTestIndirectRefTable FAILED");
#endif
if (dvmCheckException(dvmThreadSelf())) {
dvmLogExceptionStackTrace();
return "Exception pending at end of VM initialization";
}
scopedShutdown.disarm();
return "";
}
class ScopedShutdown {
public:
ScopedShutdown() : armed_(true) {
}
~ScopedShutdown() {
if (armed_) {
dvmShutdown();
}
}
void disarm() {
armed_ = false;
}
private:
bool armed_;
};
//set default command line
static void setCommandLineDefaults()
{
const char* envStr = getenv("CLASSPATH");
if (envStr != NULL) {
gDvm.classPathStr = strdup(envStr);
} else {
gDvm.classPathStr = strdup(".");
}
envStr = getenv("BOOTCLASSPATH");
if (envStr != NULL) {
gDvm.bootClassPathStr = strdup(envStr);
} else {
gDvm.bootClassPathStr = strdup(".");
}
gDvm.properties = new std::vector<std::string>();
/* Defaults overridden by -Xms and -Xmx.
* TODO: base these on a system or application-specific default
*/
gDvm.heapStartingSize = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
gDvm.heapMaximumSize = 16 * 1024 * 1024; // Spec says 75% physical mem
gDvm.heapGrowthLimit = 0; // 0 means no growth limit
gDvm.stackSize = kDefaultStackSize;
gDvm.mainThreadStackSize = kDefaultStackSize;
gDvm.concurrentMarkSweep = true;
/* gDvm.jdwpSuspend = true; */
/* allowed unless zygote config doesn't allow it */
gDvm.jdwpAllowed = true;
/* default verification and optimization modes */
gDvm.classVerifyMode = VERIFY_MODE_ALL;
gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
gDvm.monitorVerification = false;
gDvm.generateRegisterMaps = true;
gDvm.registerMapMode = kRegisterMapModeTypePrecise;
/*
* Default execution mode.
*
* This should probably interact with the mterp code somehow, e.g. if
* we know we're using the "desktop" build we should probably be
* using "portable" rather than "fast".
*/
#if defined(WITH_JIT)
gDvm.executionMode = kExecutionModeJit;
#else
gDvm.executionMode = kExecutionModeInterpFast;
#endif
/*
* SMP support is a compile-time define, but we may want to have
* dexopt target a differently-configured device.
*/
gDvm.dexOptForSmp = (ANDROID_SMP != 0);
/*
* Default profiler configuration.
*/
gDvm.profilerClockSource = kProfilerClockSourceDual;
}
// parser and process option
/*
* Process an argument vector full of options. Unlike standard C programs,
* argv[0] does not contain the name of the program.
*
* If "ignoreUnrecognized" is set, we ignore options starting with "-X" or "_"
* that we don't recognize. Otherwise, we return with an error as soon as
* we see anything we can't identify.
*
* Returns 0 on success, -1 on failure, and 1 for the special case of
* "-version" where we want to stop without showing an error message.
*/
static int processOptions(int argc, const char* const argv[],
bool ignoreUnrecognized)
{
int i;
ALOGV("VM options (%d):", argc);
for (i = 0; i < argc; i++)
ALOGV(" %d: '%s'", i, argv[i]);
/*
* Over-allocate AssertionControl array for convenience. If allocated,
* the array must be able to hold at least one entry, so that the
* zygote-time activation can do its business.
*/
assert(gDvm.assertionCtrl == NULL);
if (argc > 0) {
gDvm.assertionCtrl =
(AssertionControl*) malloc(sizeof(AssertionControl) * argc);
if (gDvm.assertionCtrl == NULL)
return -1;
assert(gDvm.assertionCtrlCount == 0);
}
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "-help") == 0) {
/* show usage and stop */
return -1;
} else if (strcmp(argv[i], "-version") == 0) {
/* show version and stop */
showVersion();
return 1;
} else if (strcmp(argv[i], "-showversion") == 0) {
/* show version and continue */
showVersion();
} else if (strcmp(argv[i], "-classpath") == 0 ||
strcmp(argv[i], "-cp") == 0)
{
/* set classpath */
if (i == argc-1) {
dvmFprintf(stderr, "Missing classpath path list\n");
return -1;
}
free(gDvm.classPathStr); /* in case we have compiled-in default */
gDvm.classPathStr = strdup(argv[++i]);
} else if (strncmp(argv[i], "-Xbootclasspath:",
sizeof("-Xbootclasspath:")-1) == 0)
{
/* set bootclasspath */
const char* path = argv[i] + sizeof("-Xbootclasspath:")-1;
if (*path == '\0') {
dvmFprintf(stderr, "Missing bootclasspath path list\n");
return -1;
}
free(gDvm.bootClassPathStr);
gDvm.bootClassPathStr = strdup(path);
} else if (strncmp(argv[i], "-Xbootclasspath/a:",
sizeof("-Xbootclasspath/a:")-1) == 0) {
const char* appPath = argv[i] + sizeof("-Xbootclasspath/a:")-1;
if (*(appPath) == '\0') {
dvmFprintf(stderr, "Missing appending bootclasspath path list\n");
return -1;
}
char* allPath;
if (asprintf(&allPath, "%s:%s", gDvm.bootClassPathStr, appPath) < 0) {
dvmFprintf(stderr, "Can't append to bootclasspath path list\n");
return -1;
}
free(gDvm.bootClassPathStr);
gDvm.bootClassPathStr = allPath;
} else if (strncmp(argv[i], "-Xbootclasspath/p:",
sizeof("-Xbootclasspath/p:")-1) == 0) {
const char* prePath = argv[i] + sizeof("-Xbootclasspath/p:")-1;
if (*(prePath) == '\0') {
dvmFprintf(stderr, "Missing prepending bootclasspath path list\n");
return -1;
}
char* allPath;
if (asprintf(&allPath, "%s:%s", prePath, gDvm.bootClassPathStr) < 0) {
dvmFprintf(stderr, "Can't prepend to bootclasspath path list\n");
return -1;
}
free(gDvm.bootClassPathStr);
gDvm.bootClassPathStr = allPath;
} else if (strncmp(argv[i], "-D", 2) == 0) {
/* Properties are handled in managed code. We just check syntax. */
if (strchr(argv[i], '=') == NULL) {
dvmFprintf(stderr, "Bad system property setting: \"%s\"\n",
argv[i]);
return -1;
}
gDvm.properties->push_back(argv[i] + 2);
} else if (strcmp(argv[i], "-jar") == 0) {
// TODO: handle this; name of jar should be in argv[i+1]
dvmFprintf(stderr, "-jar not yet handled\n");
assert(false);
} else if (strncmp(argv[i], "-Xms", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1024);
if (val != 0) {
if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
gDvm.heapStartingSize = val;
} else {
dvmFprintf(stderr,
"Invalid -Xms '%s', range is %dKB to %dKB\n",
argv[i], kMinHeapStartSize/1024, kMaxHeapSize/1024);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -Xms option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xmx", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1024);
if (val != 0) {
if (val >= kMinHeapSize && val <= kMaxHeapSize) {
gDvm.heapMaximumSize = val;
} else {
dvmFprintf(stderr,
"Invalid -Xmx '%s', range is %dKB to %dKB\n",
argv[i], kMinHeapSize/1024, kMaxHeapSize/1024);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -Xmx option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-XX:HeapGrowthLimit=", 20) == 0) {
size_t val = parseMemOption(argv[i] + 20, 1024);
if (val != 0) {
gDvm.heapGrowthLimit = val;
} else {
dvmFprintf(stderr, "Invalid -XX:HeapGrowthLimit option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xss", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1);
if (val != 0) {
if (val >= kMinStackSize && val <= kMaxStackSize) {
gDvm.stackSize = val;
if (val > gDvm.mainThreadStackSize) {
gDvm.mainThreadStackSize = val;
}
} else {
dvmFprintf(stderr, "Invalid -Xss '%s', range is %d to %d\n",
argv[i], kMinStackSize, kMaxStackSize);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -Xss option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-XX:mainThreadStackSize=", strlen("-XX:mainThreadStackSize=")) == 0) {
size_t val = parseMemOption(argv[i] + strlen("-XX:mainThreadStackSize="), 1);
if (val != 0) {
if (val >= kMinStackSize && val <= kMaxStackSize) {
gDvm.mainThreadStackSize = val;
} else {
dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize '%s', range is %d to %d\n",
argv[i], kMinStackSize, kMaxStackSize);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-XX:+DisableExplicitGC", 22) == 0) {
gDvm.disableExplicitGc = true;
} else if (strcmp(argv[i], "-verbose") == 0 ||
strcmp(argv[i], "-verbose:class") == 0)
{
// JNI spec says "-verbose:gc,class" is valid, but cmd line
// doesn't work that way; may want to support.
gDvm.verboseClass = true;
} else if (strcmp(argv[i], "-verbose:jni") == 0) {
gDvm.verboseJni = true;
} else if (strcmp(argv[i], "-verbose:gc") == 0) {
gDvm.verboseGc = true;
} else if (strcmp(argv[i], "-verbose:shutdown") == 0) {
gDvm.verboseShutdown = true;
} else if (strncmp(argv[i], "-enableassertions", 17) == 0) {
enableAssertions(argv[i] + 17, true);
} else if (strncmp(argv[i], "-ea", 3) == 0) {
enableAssertions(argv[i] + 3, true);
} else if (strncmp(argv[i], "-disableassertions", 18) == 0) {
enableAssertions(argv[i] + 18, false);
} else if (strncmp(argv[i], "-da", 3) == 0) {
enableAssertions(argv[i] + 3, false);
} else if (strcmp(argv[i], "-enablesystemassertions") == 0 ||
strcmp(argv[i], "-esa") == 0)
{
enableAssertions(NULL, true);
} else if (strcmp(argv[i], "-disablesystemassertions") == 0 ||
strcmp(argv[i], "-dsa") == 0)
{
enableAssertions(NULL, false);
} else if (strncmp(argv[i], "-Xcheck:jni", 11) == 0) {
/* nothing to do now -- was handled during JNI init */
} else if (strcmp(argv[i], "-Xdebug") == 0) {
/* accept but ignore */
} else if (strncmp(argv[i], "-Xrunjdwp:", 10) == 0 ||
strncmp(argv[i], "-agentlib:jdwp=", 15) == 0)
{
const char* tail;
if (argv[i][1] == 'X')
tail = argv[i] + 10;
else
tail = argv[i] + 15;
if (strncmp(tail, "help", 4) == 0 || !parseJdwpOptions(tail)) {
showJdwpHelp();
return 1;
}
} else if (strcmp(argv[i], "-Xrs") == 0) {
gDvm.reduceSignals = true;
} else if (strcmp(argv[i], "-Xnoquithandler") == 0) {
/* disables SIGQUIT handler thread while still blocking SIGQUIT */
/* (useful if we don't want thread but system still signals us) */
gDvm.noQuitHandler = true;
} else if (strcmp(argv[i], "-Xzygote") == 0) {
gDvm.zygote = true;
#if defined(WITH_JIT)
gDvmJit.runningInAndroidFramework = true;
#endif
} else if (strncmp(argv[i], "-Xdexopt:", 9) == 0) {
if (strcmp(argv[i] + 9, "none") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_NONE;
else if (strcmp(argv[i] + 9, "verified") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
else if (strcmp(argv[i] + 9, "all") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_ALL;
else if (strcmp(argv[i] + 9, "full") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_FULL;
else {
dvmFprintf(stderr, "Unrecognized dexopt option '%s'\n",argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xverify:", 9) == 0) {
if (strcmp(argv[i] + 9, "none") == 0)
gDvm.classVerifyMode = VERIFY_MODE_NONE;
else if (strcmp(argv[i] + 9, "remote") == 0)
gDvm.classVerifyMode = VERIFY_MODE_REMOTE;
else if (strcmp(argv[i] + 9, "all") == 0)
gDvm.classVerifyMode = VERIFY_MODE_ALL;
else {
dvmFprintf(stderr, "Unrecognized verify option '%s'\n",argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xjnigreflimit:", 15) == 0) {
int lim = atoi(argv[i] + 15);
if (lim < 200 || (lim % 100) != 0) {
dvmFprintf(stderr, "Bad value for -Xjnigreflimit: '%s'\n",
argv[i]+15);
return -1;
}
gDvm.jniGrefLimit = lim;
} else if (strncmp(argv[i], "-Xjnitrace:", 11) == 0) {
gDvm.jniTrace = strdup(argv[i] + 11);
} else if (strcmp(argv[i], "-Xlog-stdio") == 0) {
gDvm.logStdio = true;
} else if (strncmp(argv[i], "-Xint", 5) == 0) {
if (argv[i][5] == ':') {
if (strcmp(argv[i] + 6, "portable") == 0)
gDvm.executionMode = kExecutionModeInterpPortable;
else if (strcmp(argv[i] + 6, "fast") == 0)
gDvm.executionMode = kExecutionModeInterpFast;
#ifdef WITH_JIT
else if (strcmp(argv[i] + 6, "jit") == 0)
gDvm.executionMode = kExecutionModeJit;
#endif
else {
dvmFprintf(stderr,
"Warning: Unrecognized interpreter mode %s\n",argv[i]);
/* keep going */
}
} else {
/* disable JIT if it was enabled by default */
gDvm.executionMode = kExecutionModeInterpFast;
}
} else if (strncmp(argv[i], "-Xlockprofthreshold:", 20) == 0) {
gDvm.lockProfThreshold = atoi(argv[i] + 20);
#ifdef WITH_JIT
} else if (strncmp(argv[i], "-Xjitop", 7) == 0) {
processXjitop(argv[i]);
} else if (strncmp(argv[i], "-Xjitmethod", 11) == 0) {
processXjitmethod(argv[i]);
} else if (strncmp(argv[i], "-Xjitblocking", 13) == 0) {
gDvmJit.blockingMode = true;
} else if (strncmp(argv[i], "-Xjitthreshold:", 15) == 0) {
gDvmJit.threshold = atoi(argv[i] + 15);
} else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) {
gDvmJit.includeSelectedOp = true;
} else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) {
gDvmJit.includeSelectedMethod = true;
} else if (strncmp(argv[i], "-Xjitcheckcg", 12) == 0) {
gDvmJit.checkCallGraph = true;
/* Need to enable blocking mode due to stack crawling */
gDvmJit.blockingMode = true;
} else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) {
gDvmJit.printMe = true;
} else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
gDvmJit.profileMode = kTraceProfilingContinuous;
} else if (strncmp(argv[i], "-Xjitdisableopt", 15) == 0) {
/* Disable selected optimizations */
if (argv[i][15] == ':') {
sscanf(argv[i] + 16, "%x", &gDvmJit.disableOpt);
/* Disable all optimizations */
} else {
gDvmJit.disableOpt = -1;
}
} else if (strncmp(argv[i], "-Xjitsuspendpoll", 16) == 0) {
gDvmJit.genSuspendPoll = true;
#endif
} else if (strncmp(argv[i], "-Xstacktracefile:", 17) == 0) {
gDvm.stackTraceFile = strdup(argv[i]+17);
} else if (strcmp(argv[i], "-Xgenregmap") == 0) {
gDvm.generateRegisterMaps = true;
} else if (strcmp(argv[i], "-Xnogenregmap") == 0) {
gDvm.generateRegisterMaps = false;
} else if (strcmp(argv[i], "Xverifyopt:checkmon") == 0) {
gDvm.monitorVerification = true;
} else if (strcmp(argv[i], "Xverifyopt:nocheckmon") == 0) {
gDvm.monitorVerification = false;
} else if (strncmp(argv[i], "-Xgc:", 5) == 0) {
if (strcmp(argv[i] + 5, "precise") == 0)
gDvm.preciseGc = true;
else if (strcmp(argv[i] + 5, "noprecise") == 0)
gDvm.preciseGc = false;
else if (strcmp(argv[i] + 5, "preverify") == 0)
gDvm.preVerify = true;
else if (strcmp(argv[i] + 5, "nopreverify") == 0)
gDvm.preVerify = false;
else if (strcmp(argv[i] + 5, "postverify") == 0)
gDvm.postVerify = true;
else if (strcmp(argv[i] + 5, "nopostverify") == 0)
gDvm.postVerify = false;
else if (strcmp(argv[i] + 5, "concurrent") == 0)
gDvm.concurrentMarkSweep = true;
else if (strcmp(argv[i] + 5, "noconcurrent") == 0)
gDvm.concurrentMarkSweep = false;
else if (strcmp(argv[i] + 5, "verifycardtable") == 0)
gDvm.verifyCardTable = true;
else if (strcmp(argv[i] + 5, "noverifycardtable") == 0)
gDvm.verifyCardTable = false;
else {
dvmFprintf(stderr, "Bad value for -Xgc");
return -1;
}
ALOGV("Precise GC configured %s", gDvm.preciseGc ? "ON" : "OFF");
} else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
gDvm.verifyDexChecksum = true;
} else if (strcmp(argv[i], "-Xprofile:threadcpuclock") == 0) {
gDvm.profilerClockSource = kProfilerClockSourceThreadCpu;
} else if (strcmp(argv[i], "-Xprofile:wallclock") == 0) {
gDvm.profilerClockSource = kProfilerClockSourceWall;
} else if (strcmp(argv[i], "-Xprofile:dualclock") == 0) {
gDvm.profilerClockSource = kProfilerClockSourceDual;
} else {
if (!ignoreUnrecognized) {
dvmFprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
return -1;
}
}
}
return 0;
}
vm/alloc/Alloc.cpp
/*
* Initialize the GC universe.
*
* We're currently using a memory-mapped arena to keep things off of the
* main heap. This needs to be replaced with something real.
*/
bool dvmGcStartup()
{
dvmInitMutex(&gDvm.gcHeapLock);
pthread_cond_init(&gDvm.gcHeapCond, NULL);
return dvmHeapStartup();
}
//dvm Heap start up
bool dvmHeapStartup()
{
GcHeap *gcHeap;
if (gDvm.heapGrowthLimit == 0) {
gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
}
gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
gDvm.heapMaximumSize,
gDvm.heapGrowthLimit);
if (gcHeap == NULL) {
return false;
}
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
gcHeap->ddmNhsgWhen = 0;
gcHeap->ddmNhsgWhat = 0;
gDvm.gcHeap = gcHeap;
/* Set up the lists we'll use for cleared reference objects.
*/
gcHeap->clearedReferences = NULL;
if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
LOGE_HEAP("card table startup failed.");
return false;
}
return true;
}
vm/Misc.cpp
/*
* Initializes the heap source; must be called before any other
* dvmHeapSource*() functions. Returns a GcHeap structure
* allocated from the heap source.
*/
GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
size_t growthLimit)
{
GcHeap *gcHeap;
HeapSource *hs;
mspace msp;
size_t length;
void *base;
assert(gHs == NULL);
if (!(startSize <= growthLimit && growthLimit <= maximumSize)) {
ALOGE("Bad heap size parameters (start=%zd, max=%zd, limit=%zd)",
startSize, maximumSize, growthLimit);
return NULL;
}
/*
* Allocate a contiguous region of virtual memory to subdivided
* among the heaps managed by the garbage collector.
*/
length = ALIGN_UP_TO_PAGE_SIZE(maximumSize);
base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
if (base == NULL) {
return NULL;
}
/* Create an unlocked dlmalloc mspace to use as
* a heap source.
*/
msp = createMspace(base, startSize, maximumSize);
if (msp == NULL) {
goto fail;
}
gcHeap = (GcHeap *)calloc(1, sizeof(*gcHeap));
if (gcHeap == NULL) {
LOGE_HEAP("Can't allocate heap descriptor");
goto fail;
}
hs = (HeapSource *)calloc(1, sizeof(*hs));
if (hs == NULL) {
LOGE_HEAP("Can't allocate heap source");
free(gcHeap);
goto fail;
}
hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
hs->startSize = startSize;
hs->maximumSize = maximumSize;
hs->growthLimit = growthLimit;
hs->idealSize = startSize;
hs->softLimit = SIZE_MAX; // no soft limit at first
hs->numHeaps = 0;
hs->sawZygote = gDvm.zygote;
hs->hasGcThread = false;
hs->heapBase = (char *)base;
hs->heapLength = length;
if (!addInitialHeap(hs, msp, growthLimit)) {
LOGE_HEAP("Can't add initial heap");
goto fail;
}
if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
LOGE_HEAP("Can't create liveBits");
goto fail;
}
if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
LOGE_HEAP("Can't create markBits");
dvmHeapBitmapDelete(&hs->liveBits);
goto fail;
}
if (!allocMarkStack(&gcHeap->markContext.stack, hs->maximumSize)) {
ALOGE("Can't create markStack");
dvmHeapBitmapDelete(&hs->markBits);
dvmHeapBitmapDelete(&hs->liveBits);
goto fail;
}
gcHeap->markContext.bitmap = &hs->markBits;
gcHeap->heapSource = hs;
gHs = hs;
return gcHeap;
fail:
munmap(base, length);
return NULL;
}
vm/Misc.cpp
void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
void *base;
int fd, ret;
byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
fd = ashmem_create_region(name, byteCount);
if (fd == -1) {
return NULL;
}
base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
ret = close(fd);
if (base == MAP_FAILED) {
return NULL;
}
if (ret == -1) {
return NULL;
}
return base;
}
vm/alloc/HeapSource.cpp
static mspace createMspace(void *base, size_t startSize, size_t maximumSize)
{
/* Create an unlocked dlmalloc mspace to use as
* a heap source.
*
* We start off reserving startSize / 2 bytes but
* letting the heap grow to startSize. This saves
* memory in the case where a process uses even less
* than the starting size.
*/
LOGV_HEAP("Creating VM heap of size %zu", startSize);
errno = 0;
mspace msp = create_contiguous_mspace_with_base(startSize/2,
maximumSize, /*locked=*/false, base);
if (msp != NULL) {
/* Don't let the heap grow past the starting size without
* our intervention.
*/
mspace_set_max_allowed_footprint(msp, startSize);
} else {
/* There's no guarantee that errno has meaning when the call
* fails, but it often does.
*/
LOGE_HEAP("Can't create VM heap of size (%zu,%zu): %s",
startSize/2, maximumSize, strerror(errno));
}
return msp;
}
/vm/alloc/CardTable.cpp
*
* Initializes the card table; must be called before any other
* dvmCardTable*() functions.
*/
bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit)
{
size_t length;
void *allocBase;
u1 *biasedBase;
GcHeap *gcHeap = gDvm.gcHeap;
void *heapBase = dvmHeapSourceGetBase();
assert(gcHeap != NULL);
assert(heapBase != NULL);
/* Set up the card table */
length = heapMaximumSize / GC_CARD_SIZE;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
"dalvik-card-table");
if (allocBase == NULL) {
return false;
}
gcHeap->cardTableBase = (u1*)allocBase;
gcHeap->cardTableLength = growthLimit / GC_CARD_SIZE;
gcHeap->cardTableMaxLength = length;
gcHeap->cardTableOffset = 0;
/* All zeros is the correct initial value; all clean. */
assert(GC_CARD_CLEAN == 0);
biasedBase = (u1 *)((uintptr_t)allocBase -
((uintptr_t)heapBase >> GC_CARD_SHIFT));
if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) {
int offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
gcHeap->cardTableOffset = offset + (offset < 0 ? 0x100 : 0);
biasedBase += gcHeap->cardTableOffset;
}
assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
gDvm.biasedCardTableBase = biasedBase;
return true;
}
vm/alloc/HeapSource.cpp
void *dvmHeapSourceGetBase()
{
return gHs->heapBase;
}
/vm/Thread.cpp
/*
* Initialize thread list and main thread's environment. We need to set
* up some basic stuff so that dvmThreadSelf() will work when we start
* loading classes (e.g. to check for exceptions).
*/
bool dvmThreadStartup()
{
Thread* thread;
/* allocate a TLS slot */
if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
ALOGE("ERROR: pthread_key_create failed");
return false;
}
/* test our pthread lib */
if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
ALOGW("WARNING: newly-created pthread TLS slot is not NULL");
/* prep thread-related locks and conditions */
dvmInitMutex(&gDvm.threadListLock);
pthread_cond_init(&gDvm.threadStartCond, NULL);
pthread_cond_init(&gDvm.vmExitCond, NULL);
dvmInitMutex(&gDvm._threadSuspendLock);
dvmInitMutex(&gDvm.threadSuspendCountLock);
pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
/*
* Dedicated monitor for Thread.sleep().
* TODO: change this to an Object* so we don't have to expose this
* call, and we interact better with JDWP monitor calls. Requires
* deferring the object creation to much later (e.g. final "main"
* thread prep) or until first use.
*/
gDvm.threadSleepMon = dvmCreateMonitor(NULL);
gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);
thread = allocThread(gDvm.mainThreadStackSize);
if (thread == NULL)
return false;
/* switch mode for when we run initializers */
thread->status = THREAD_RUNNING;
/*
* We need to assign the threadId early so we can lock/notify
* object monitors. We'll set the "threadObj" field later.
*/
prepareThread(thread);
gDvm.threadList = thread;
#ifdef COUNT_PRECISE_METHODS
gDvm.preciseMethods = dvmPointerSetAlloc(200);
#endif
return true;
}
vm/Sync.cpp
/*
* Create and initialize a monitor.
*/
Monitor* dvmCreateMonitor(Object* obj)
{
Monitor* mon;
mon = (Monitor*) calloc(1, sizeof(Monitor));
if (mon == NULL) {
ALOGE("Unable to allocate monitor");
dvmAbort();
}
if (((u4)mon & 7) != 0) {
ALOGE("Misaligned monitor: %p", mon);
dvmAbort();
}
mon->obj = obj;
dvmInitMutex(&mon->lock);
/* replace the head of the list with the new monitor */
do {
mon->next = gDvm.monitorList;
} while (android_atomic_release_cas((int32_t)mon->next, (int32_t)mon,
(int32_t*)(void*)&gDvm.monitorList) != 0);
return mon;
}
vm/Bitvector.cpp
/*
* Allocate a bit vector with enough space to hold at least the specified
* number of bits.
*/
BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable)
{
BitVector* bv;
unsigned int count;
assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
bv = (BitVector*) malloc(sizeof(BitVector));
count = (startBits + 31) >> 5;
bv->storageSize = count;
bv->expandable = expandable;
bv->storage = (u4*) calloc(count, sizeof(u4));
return bv;
}
vm/thread.cpp
/*
* Finish initialization of a Thread struct.
*
* This must be called while executing in the new thread, but before the
* thread is added to the thread list.
*
* NOTE: The threadListLock must be held by the caller (needed for
* assignThreadId()).
*/
static bool prepareThread(Thread* thread)
{
assignThreadId(thread);
thread->handle = pthread_self();
thread->systemTid = dvmGetSysThreadId();
//ALOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid,
// (int) getpid());
/*
* If we were called by dvmAttachCurrentThread, the self value is
* already correctly established as "thread".
*/
setThreadSelf(thread);
ALOGV("threadid=%d: interp stack at %p",
thread->threadId, thread->interpStackStart - thread->interpStackSize);
/*
* Initialize invokeReq.
*/
dvmInitMutex(&thread->invokeReq.lock);
pthread_cond_init(&thread->invokeReq.cv, NULL);
/*
* Initialize our reference tracking tables.
*
* Most threads won't use jniMonitorRefTable, so we clear out the
* structure but don't call the init function (which allocs storage).
*/
if (!thread->jniLocalRefTable.init(kJniLocalRefMin,
kJniLocalRefMax, kIndirectKindLocal)) {
return false;
}
if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
kInternalRefDefault, kInternalRefMax))
return false;
memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
pthread_cond_init(&thread->waitCond, NULL);
dvmInitMutex(&thread->waitMutex);
/* Initialize safepoint callback mechanism */
dvmInitMutex(&thread->callbackMutex);
return true;
}
vm/inlineNative.cpp
/*
* Allocate some tables.
*/
bool dvmInlineNativeStartup()
{
gDvm.inlinedMethods =
(Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
if (gDvm.inlinedMethods == NULL)
return false;
return true;
}
vm/analysis/RegisterMap.cpp
/*
* Prepare some things.
*/
bool dvmRegisterMapStartup()
{
#ifdef REGISTER_MAP_STATS
MapStats* pStats = calloc(1, sizeof(MapStats));
gDvm.registerMapStats = pStats;
#endif
return true;
}
vm/oo/TypeCheck.cpp
* Allocate cache.
*/
bool dvmInstanceofStartup()
{
gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE);
if (gDvm.instanceofCache == NULL)
return false;
return true;
}
vm/oo/Class.cpp
/*
* Initialize the bootstrap class loader.
*
* Call this after the bootclasspath string has been finalized.
*/
bool dvmClassStartup()
{
/* make this a requirement -- don't currently support dirs in path */
if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
ALOGE("ERROR: must specify non-'.' bootclasspath");
return false;
}
gDvm.loadedClasses =
dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
if (gDvm.pBootLoaderAlloc == NULL)
return false;
if (false) {
linearAllocTests();
exit(0);
}
/*
* Class serial number. We start with a high value to make it distinct
* in binary dumps (e.g. hprof).
*/
gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
/*
* Set up the table we'll use for tracking initiating loaders for
* early classes.
* If it's NULL, we just fall back to the InitiatingLoaderList in the
* ClassObject, so it's not fatal to fail this allocation.
*/
gDvm.initiatingLoaderList = (InitiatingLoaderList*)
calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
/*
* Create the initial classes. These are the first objects constructed
* within the nascent VM.
*/
if (!createInitialClasses()) {
return false;
}
/*
* Process the bootstrap class path. This means opening the specified
* DEX or Jar files and possibly running them through the optimizer.
*/
assert(gDvm.bootClassPath == NULL);
processClassPath(gDvm.bootClassPathStr, true);
if (gDvm.bootClassPath == NULL)
return false;
return true;
}
vm/InitRefs.cpp
bool dvmFindRequiredClassesAndMembers() {
/*
* Note: Under normal VM use, this is called by dvmStartup()
* in Init.c. For dex optimization, this is called as well, but in
* that case, the call is made from DexPrepare.c.
*/
return initClassReferences()
&& initFieldOffsets()
&& initConstructorReferences()
&& initDirectMethodReferences()
&& initVirtualMethodOffsets()
&& initFinalizerReference()
&& verifyStringOffsets();
}
vm/oo/Array.cpp
/*
* Find an array class, by name (e.g. "[I").
*
* If the array class doesn't exist, we generate it.
*
* If the element class doesn't exist, we return NULL (no exception raised).
*/
ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
{
ClassObject* clazz;
assert(descriptor[0] == '[');
//ALOGV("dvmFindArrayClass: '%s' %p", descriptor, loader);
clazz = dvmLookupClass(descriptor, loader, false);
if (clazz == NULL) {
ALOGV("Array class '%s' %p not found; creating", descriptor, loader);
clazz = createArrayClass(descriptor, loader);
if (clazz != NULL)
dvmAddInitiatingLoader(clazz, loader);
}
return clazz;
}
vm/oo/Class.cpp
ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
bool unprepOkay)
{
ClassMatchCriteria crit;
void* found;
u4 hash;
crit.descriptor = descriptor;
crit.loader = loader;
hash = dvmComputeUtf8Hash(descriptor);
LOGVV("threadid=%d: dvmLookupClass searching for '%s' %p",
dvmThreadSelf()->threadId, descriptor, loader);
dvmHashTableLock(gDvm.loadedClasses);
found = dvmHashTableLookup(gDvm.loadedClasses, hash, &crit,
hashcmpClassByCrit, false);
dvmHashTableUnlock(gDvm.loadedClasses);
/*
* The class has been added to the hash table but isn't ready for use.
* We're going to act like we didn't see it, so that the caller will
* go through the full "find class" path, which includes locking the
* object and waiting until it's ready. We could do that lock/wait
* here, but this is an extremely rare case, and it's simpler to have
* the wait-for-class code centralized.
*/
if (found && !unprepOkay && !dvmIsClassLinked((ClassObject*)found)) {
ALOGV("Ignoring not-yet-ready %s, using slow path",
((ClassObject*)found)->descriptor);
found = NULL;
}
return (ClassObject*) found;
}
vm/Hash.cpp
/*
* Look up an entry.
*
* We probe on collisions, wrapping around the table.
*/
void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
HashCompareFunc cmpFunc, bool doAdd)
{
HashEntry* pEntry;
HashEntry* pEnd;
void* result = NULL;
assert(pHashTable->tableSize > 0);
assert(item != HASH_TOMBSTONE);
assert(item != NULL);
/* jump to the first entry and probe for a match */
pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
pEnd = &pHashTable->pEntries[pHashTable->tableSize];
while (pEntry->data != NULL) {
if (pEntry->data != HASH_TOMBSTONE &&
pEntry->hashValue == itemHash &&
(*cmpFunc)(pEntry->data, item) == 0)
{
/* match */
//ALOGD("+++ match on entry %d", pEntry - pHashTable->pEntries);
break;
}
pEntry++;
if (pEntry == pEnd) { /* wrap around to start */
if (pHashTable->tableSize == 1)
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
//ALOGI("+++ look probing %d...", pEntry - pHashTable->pEntries);
}
if (pEntry->data == NULL) {
if (doAdd) {
pEntry->hashValue = itemHash;
pEntry->data = item;
pHashTable->numEntries++;
/*
* We've added an entry. See if this brings us too close to full.
*/
if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
> pHashTable->tableSize * LOAD_NUMER)
{
if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
/* don't really have a way to indicate failure */
ALOGE("Dalvik hash resize failure");
dvmAbort();
}
/* note "pEntry" is now invalid */
} else {
//ALOGW("okay %d/%d/%d",
// pHashTable->numEntries, pHashTable->tableSize,
// (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
}
/* full table is bad -- search for nonexistent never halts */
assert(pHashTable->numEntries < pHashTable->tableSize);
result = item;
} else {
assert(result == NULL);
}
} else {
result = pEntry->data;
}
return result;
}
vm/Intern.cpp
/*
* Prep string interning.
*/
bool dvmStringInternStartup()
{
dvmInitMutex(&gDvm.internLock);
gDvm.internedStrings = dvmHashTableCreate(256, NULL);
if (gDvm.internedStrings == NULL)
return false;
gDvm.literalStrings = dvmHashTableCreate(256, NULL);
if (gDvm.literalStrings == NULL)
return false;
return true;
}
vm/Native.cpp
/*
* Initialize the native code loader.
*/
bool dvmNativeStartup()
{
gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry);
if (gDvm.nativeLibs == NULL)
return false;
return true;
}
vm/native/InternalNative.cpp
/*
* Set up hash values on the class names.
*/
bool dvmInternalNativeStartup()
{
DalvikNativeClass* classPtr = gDvmNativeMethodSet;
while (classPtr->classDescriptor != NULL) {
classPtr->classDescriptorHash =
dvmComputeUtf8Hash(classPtr->classDescriptor);
classPtr++;
}
gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
if (gDvm.userDexFiles == NULL)
return false;
return true;
}
vm/Jni.cpp
bool dvmJniStartup() {
if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize,
kGlobalRefsTableMaxSize,
kIndirectKindGlobal)) {
return false;
}
if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize,
kGlobalRefsTableMaxSize,
kIndirectKindWeakGlobal)) {
return false;
}
dvmInitMutex(&gDvm.jniGlobalRefLock);
dvmInitMutex(&gDvm.jniWeakGlobalRefLock);
gDvm.jniGlobalRefLoMark = 0;
gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) {
return false;
}
dvmInitMutex(&gDvm.jniPinRefLock);
return true;
}
vm/Profile.cpp
/*
* Boot-time init.
*/
bool dvmProfilingStartup()
{
/*
* Initialize "dmtrace" method profiling.
*/
memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
dvmInitMutex(&gDvm.methodTrace.startStopLock);
pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
assert(!dvmCheckException(dvmThreadSelf()));
/*
* Allocate storage for instruction counters.
*/
gDvm.executedInstrCounts = (int*) calloc(kNumPackedOpcodes, sizeof(int));
if (gDvm.executedInstrCounts == NULL)
return false;
#ifdef UPDATE_MAGIC_PAGE
/*
* If we're running on the emulator, there's a magic page into which
* we can put interpreted method information. This allows interpreted
* methods to show up in the emulator's code traces.
*
* We could key this off of the "ro.kernel.qemu" property, but there's
* no real harm in doing this on a real device.
*/
int fd = open("/dev/qemu_trace", O_RDWR);
if (fd < 0) {
ALOGV("Unable to open /dev/qemu_trace,err: %s", strerror(errno));
} else {
gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
if (gDvm.emulatorTracePage == MAP_FAILED) {
ALOGE("Unable to mmap /dev/qemu_trace");
gDvm.emulatorTracePage = NULL;
} else {
*(u4*) gDvm.emulatorTracePage = 0;
}
}
#else
assert(gDvm.emulatorTracePage == NULL);
#endif
return true;
}
vm/analysis/Optimize.cpp
/*
* Create a table of inline substitutions. Sets gDvm.inlineSubs.
*
* TODO: this is currently just a linear array. We will want to put this
* into a hash table as the list size increases.
*/
bool dvmCreateInlineSubsTable()
{
const InlineOperation* ops = dvmGetInlineOpsTable();
const int count = dvmGetInlineOpsTableLength();
InlineSub* table;
int i, tableIndex;
assert(gDvm.inlineSubs == NULL);
/*
* One slot per entry, plus an end-of-list marker.
*/
table = (InlineSub*) calloc(count + 1, sizeof(InlineSub));
tableIndex = 0;
for (i = 0; i < count; i++) {
Method* method = dvmFindInlinableMethod(ops[i].classDescriptor,
ops[i].methodName, ops[i].methodSignature);
if (method == NULL) {
/*
* Not expected. We only use this for key methods in core
* classes, so we should always be able to find them.
*/
ALOGE("Unable to find method for inlining: %s.%s:%s",
ops[i].classDescriptor, ops[i].methodName,
ops[i].methodSignature);
return false;
}
table[tableIndex].method = method;
table[tableIndex].inlineIdx = i;
tableIndex++;
}
/* mark end of table */
table[tableIndex].method = NULL;
gDvm.inlineSubs = table;
return true;
}
vm/reflect/Reflect.cpp
/*
* For some of the reflection stuff we need to un-box primitives, e.g.
* convert a java/lang/Integer to int or even a float. We assume that
* the first instance field holds the value.
*
* To verify this, we either need to ensure that the class has only one
* instance field, or we need to look up the field by name and verify
* that it comes first. The former is simpler, and should work.
*/
bool dvmValidateBoxClasses()
{
static const char* classes[] = {
"Ljava/lang/Boolean;",
"Ljava/lang/Character;",
"Ljava/lang/Float;",
"Ljava/lang/Double;",
"Ljava/lang/Byte;",
"Ljava/lang/Short;",
"Ljava/lang/Integer;",
"Ljava/lang/Long;",
NULL
};
const char** ccp;
for (ccp = classes; *ccp != NULL; ccp++) {
ClassObject* clazz;
clazz = dvmFindClassNoInit(*ccp, NULL);
if (clazz == NULL) {
ALOGE("Couldn't find '%s'", *ccp);
return false;
}
if (clazz->ifieldCount != 1) {
ALOGE("Found %d instance fields in '%s'",
clazz->ifieldCount, *ccp);
return false;
}
}
return true;
}
vm/Thread.cpp
/*
* Finish preparing the parts of the Thread struct required to support
* JNI registration.
*/
bool dvmPrepMainForJni(JNIEnv* pEnv)
{
Thread* self;
/* main thread is always first in list at this point */
self = gDvm.threadList;
assert(self->threadId == kMainThreadId);
/* create a "fake" JNI frame at the top of the main thread interp stack */
if (!createFakeEntryFrame(self))
return false;
/* fill these in, since they weren't ready at dvmCreateJNIEnv time */
dvmSetJniEnvThreadId(pEnv, self);
dvmSetThreadJNIEnv(self, (JNIEnv*) pEnv);
return true;
}
vm/Init.cpp
/*
* Register java.* natives from our class libraries. We need to do
* this after we're ready for JNI registration calls, but before we
* do any class initialization.
*
* If we get this wrong, we will blow up in the ThreadGroup class init if
* interpreted code makes any reference to System. It will likely do this
* since it wants to do some java.io.File setup (e.g. for static in/out/err).
*
* We need to have gDvm.initializing raised here so that JNI FindClass
* won't try to use the system/application class loader.
*/
static bool registerSystemNatives(JNIEnv* pEnv)
{
// Main thread is always first in list.
Thread* self = gDvm.threadList;
// Must set this before allowing JNI-based method registration.
self->status = THREAD_NATIVE;
// Most JNI libraries can just use System.loadLibrary, but you can't
// if you're the library that implements System.loadLibrary!
loadJniLibrary("javacore");
loadJniLibrary("nativehelper");
// Back to run mode.
self->status = THREAD_RUNNING;
return true;
}
vm/Native.cpp
/*
* Load native code from the specified absolute pathname. Per the spec,
* if we've already loaded a library with the specified pathname, we
* return without doing anything.
*
* TODO? for better results we should absolutify the pathname. For fully
* correct results we should stat to get the inode and compare that. The
* existing implementation is fine so long as everybody is using
* System.loadLibrary.
*
* The library will be associated with the specified class loader. The JNI
* spec says we can't load the same library into more than one class loader.
*
* Returns "true" on success. On failure, sets *detail to a
* human-readable description of the error or NULL if no detail is
* available; ownership of the string is transferred to the caller.
*/
bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
char** detail)
{
SharedLib* pEntry;
void* handle;
bool verbose;
/* reduce noise by not chattering about system libraries */
verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
if (verbose)
ALOGD("Trying to load lib %s %p", pathName, classLoader);
*detail = NULL;
/*
* See if we've already loaded it. If we have, and the class loader
* matches, return successfully without doing anything.
*/
pEntry = findSharedLibEntry(pathName);
if (pEntry != NULL) {
if (pEntry->classLoader != classLoader) {
ALOGW("Shared lib '%s' already opened by CL %p; can't open in %p",
pathName, pEntry->classLoader, classLoader);
return false;
}
if (verbose) {
ALOGD("Shared lib '%s' already loaded in same CL %p",
pathName, classLoader);
}
if (!checkOnLoadResult(pEntry))
return false;
return true;
}
/*
* Open the shared library. Because we're using a full path, the system
* doesn't have to search through LD_LIBRARY_PATH. (It may do so to
* resolve this library's dependencies though.)
*
* Failures here are expected when java.library.path has several entries
* and we have to hunt for the lib.
*
* The current version of the dynamic linker prints detailed information
* about dlopen() failures. Some things to check if the message is
* cryptic:
* - make sure the library exists on the device
* - verify that the right path is being opened (the debug log message
* above can help with that)
* - check to see if the library is valid (e.g. not zero bytes long)
* - check config/prelink-linux-arm.map to ensure that the library
* is listed and is not being overrun by the previous entry (if
* loading suddenly stops working on a prelinked library, this is
* a good one to check)
* - write a trivial app that calls sleep() then dlopen(), attach
* to it with "strace -p <pid>" while it sleeps, and watch for
* attempts to open nonexistent dependent shared libs
*
* This can execute slowly for a large library on a busy system, so we
* want to switch from RUNNING to VMWAIT while it executes. This allows
* the GC to ignore us.
*/
Thread* self = dvmThreadSelf();
ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
handle = dlopen(pathName, RTLD_LAZY);
dvmChangeStatus(self, oldStatus);
if (handle == NULL) {
*detail = strdup(dlerror());
return false;
}
/* create a new entry */
SharedLib* pNewEntry;
pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));
pNewEntry->pathName = strdup(pathName);
pNewEntry->handle = handle;
pNewEntry->classLoader = classLoader;
dvmInitMutex(&pNewEntry->onLoadLock);
pthread_cond_init(&pNewEntry->onLoadCond, NULL);
pNewEntry->onLoadThreadId = self->threadId;
/* try to add it to the list */
SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);
if (pNewEntry != pActualEntry) {
ALOGI("WOW: we lost a race to add a shared lib (%s CL=%p)",
pathName, classLoader);
freeSharedLibEntry(pNewEntry);
return checkOnLoadResult(pActualEntry);
} else {
if (verbose)
ALOGD("Added shared lib %s %p", pathName, classLoader);
bool result = true;
void* vonLoad;
int version;
vonLoad = dlsym(handle, "JNI_OnLoad");
if (vonLoad == NULL) {
ALOGD("No JNI_OnLoad found in %s %p, skipping init",
pathName, classLoader);
} else {
/*
* Call JNI_OnLoad. We have to override the current class
* loader, which will always be "null" since the stuff at the
* top of the stack is around Runtime.loadLibrary(). (See
* the comments in the JNI FindClass function.)
*/
OnLoadFunc func = (OnLoadFunc)vonLoad;
Object* prevOverride = self->classLoaderOverride;
self->classLoaderOverride = classLoader;
oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
if (gDvm.verboseJni) {
ALOGI("[Calling JNI_OnLoad for \"%s\"]", pathName);
}
version = (*func)(gDvmJni.jniVm, NULL);
dvmChangeStatus(self, oldStatus);
self->classLoaderOverride = prevOverride;
if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&
version != JNI_VERSION_1_6)
{
ALOGW("JNI_OnLoad returned bad version (%d) in %s %p",
version, pathName, classLoader);
/*
* It's unwise to call dlclose() here, but we can mark it
* as bad and ensure that future load attempts will fail.
*
* We don't know how far JNI_OnLoad got, so there could
* be some partially-initialized stuff accessible through
* newly-registered native method calls. We could try to
* unregister them, but that doesn't seem worthwhile.
*/
result = false;
} else {
if (gDvm.verboseJni) {
ALOGI("[Returned from JNI_OnLoad for \"%s\"]", pathName);
}
}
}
if (result)
pNewEntry->onLoadResult = kOnLoadOkay;
else
pNewEntry->onLoadResult = kOnLoadFailed;
pNewEntry->onLoadThreadId = 0;
/*
* Broadcast a wakeup to anybody sleeping on the condition variable.
*/
dvmLockMutex(&pNewEntry->onLoadLock);
pthread_cond_broadcast(&pNewEntry->onLoadCond);
dvmUnlockMutex(&pNewEntry->onLoadLock);
return result;
}
}
vm/alloc/Alloc.cpp
/*
* Create some "stock" exceptions. These can be thrown when the system is
* too screwed up to allocate and initialize anything, or when we don't
* need a meaningful stack trace.
*
* We can't do this during the initial startup because we need to execute
* the constructors.
*/
bool dvmCreateStockExceptions()
{
/*
* Pre-allocate some throwables. These need to be explicitly added
* to the GC's root set (see dvmHeapMarkRootSet()).
*/
gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
"[memory exhausted]");
dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
"[pre-allocated]");
dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
gDvm.noClassDefFoundErrorObj =
createStockException("Ljava/lang/NoClassDefFoundError;",
"[generic]");
dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
gDvm.noClassDefFoundErrorObj == NULL)
{
ALOGW("Unable to create stock exceptions");
return false;
}
return true;
}
vm/Thread.cpp
/*
* Finish preparing the main thread, allocating some objects to represent
* it. As part of doing so, we finish initializing Thread and ThreadGroup.
* This will execute some interpreted code (e.g. class initializers).
*/
bool dvmPrepMainThread()
{
Thread* thread;
Object* groupObj;
Object* threadObj;
Object* vmThreadObj;
StringObject* threadNameStr;
Method* init;
JValue unused;
ALOGV("+++ finishing prep on main VM thread");
/* main thread is always first in list at this point */
thread = gDvm.threadList;
assert(thread->threadId == kMainThreadId);
/*
* Make sure the classes are initialized. We have to do this before
* we create an instance of them.
*/
if (!dvmInitClass(gDvm.classJavaLangClass)) {
ALOGE("'Class' class failed to initialize");
return false;
}
if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
!dvmInitClass(gDvm.classJavaLangThread) ||
!dvmInitClass(gDvm.classJavaLangVMThread))
{
ALOGE("thread classes failed to initialize");
return false;
}
groupObj = dvmGetMainThreadGroup();
if (groupObj == NULL)
return false;
/*
* Allocate and construct a Thread with the internal-creation
* constructor.
*/
threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
if (threadObj == NULL) {
ALOGE("unable to allocate main thread object");
return false;
}
dvmReleaseTrackedAlloc(threadObj, NULL);
threadNameStr = dvmCreateStringFromCstr("main");
if (threadNameStr == NULL)
return false;
dvmReleaseTrackedAlloc((Object*)threadNameStr, NULL);
init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
"(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
assert(init != NULL);
dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr,
THREAD_NORM_PRIORITY, false);
if (dvmCheckException(thread)) {
ALOGE("exception thrown while constructing main thread object");
return false;
}
/*
* Allocate and construct a VMThread.
*/
vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
if (vmThreadObj == NULL) {
ALOGE("unable to allocate main vmthread object");
return false;
}
dvmReleaseTrackedAlloc(vmThreadObj, NULL);
init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
"(Ljava/lang/Thread;)V");
dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
if (dvmCheckException(thread)) {
ALOGE("exception thrown while constructing main vmthread object");
return false;
}
/* set the VMThread.vmData field to our Thread struct */
assert(gDvm.offJavaLangVMThread_vmData != 0);
dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)thread);
/*
* Stuff the VMThread back into the Thread. From this point on, other
* Threads will see that this Thread is running (at least, they would,
* if there were any).
*/
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
vmThreadObj);
thread->threadObj = threadObj;
/*
* Set the "context class loader" field in the system class loader.
*
* Retrieving the system class loader will cause invocation of
* ClassLoader.getSystemClassLoader(), which could conceivably call
* Thread.currentThread(), so we want the Thread to be fully configured
* before we do this.
*/
Object* systemLoader = dvmGetSystemClassLoader();
if (systemLoader == NULL) {
ALOGW("WARNING: system class loader is NULL (setting main ctxt)");
/* keep going? */
} else {
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_contextClassLoader,
systemLoader);
dvmReleaseTrackedAlloc(systemLoader, NULL);
}
/* include self in non-daemon threads (mainly for AttachCurrentThread) */
gDvm.nonDaemonThreadCount++;
return true;
}
vm/Debugger.cpp
/*
* System init. We don't allocate the registry until first use.
* Make sure we do this before initializing JDWP.
*/
bool dvmDebuggerStartup()
{
if (!dvmBreakpointStartup())
return false;
gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
return (gDvm.dbgRegistry != NULL);
}
vm/alloc/Alloc.cpp
bool dvmGcStartupClasses()
{
ClassObject *klass = dvmFindSystemClass("Ljava/lang/Daemons;");
if (klass == NULL) {
return false;
}
Method *method = dvmFindDirectMethodByDescriptor(klass, "start", "()V");
if (method == NULL) {
return false;
}
Thread *self = dvmThreadSelf();
assert(self != NULL);
JValue unusedResult;
dvmCallMethod(self, method, NULL, &unusedResult);
return true;
}
vm/Init.cpp
/*
* Do zygote-mode-only initialization.
*/
static bool initZygote()
{
/* zygote goes into its own process group */
setpgid(0,0);
return true;
}
vm/Init.cpp
/*
* Do non-zygote-mode initialization. This is done during VM init for
* standard startup, or after a "zygote fork" when creating a new process.
*/
bool dvmInitAfterZygote()
{
u8 startHeap, startQuit, startJdwp;
u8 endHeap, endQuit, endJdwp;
startHeap = dvmGetRelativeTimeUsec();
/*
* Post-zygote heap initialization, including starting
* the HeapWorker thread.
*/
if (!dvmGcStartupAfterZygote())
return false;
endHeap = dvmGetRelativeTimeUsec();
startQuit = dvmGetRelativeTimeUsec();
/* start signal catcher thread that dumps stacks on SIGQUIT */
if (!gDvm.reduceSignals && !gDvm.noQuitHandler) {
if (!dvmSignalCatcherStartup())
return false;
}
/* start stdout/stderr copier, if requested */
if (gDvm.logStdio) {
if (!dvmStdioConverterStartup())
return false;
}
endQuit = dvmGetRelativeTimeUsec();
startJdwp = dvmGetRelativeTimeUsec();
/*
* Start JDWP thread. If the command-line debugger flags specified
* "suspend=y", this will pause the VM. We probably want this to
* come last.
*/
if (!initJdwp()) {
ALOGD("JDWP init failed; continuing anyway");
}
endJdwp = dvmGetRelativeTimeUsec();
ALOGV("thread-start heap=%d quit=%d jdwp=%d total=%d usec",
(int)(endHeap-startHeap), (int)(endQuit-startQuit),
(int)(endJdwp-startJdwp), (int)(endJdwp-startHeap));
#ifdef WITH_JIT
if (gDvm.executionMode == kExecutionModeJit) {
if (!dvmCompilerStartup())
return false;
}
#endif
return true;
}
看一下 com.android.internal.os.ZygoteInit.java的main
Java代码
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
gc();
// If requested, start system server directly from Zygote
if (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
if (argv[1].equals("start-system-server")) {
startSystemServer();
} else if (!argv[1].equals("")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
runSelectLoopMode();
}
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
####app_process
/frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, const char* const argv[])
{
// These are global variables in ProcessState.cpp
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for (int i=0; i<argc; i++) {
mArgLen += strlen(argv[i]) + 1;
}
mArgLen--;
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
while (i < argc) {
const char* arg = argv[i++];
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
className = arg;
break;
}
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mParentDir = parentDir;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
} else if (className) {
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}
//
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");
其中startSystemServer 由init.rc指定,在目录system\core\rootdir中的init.rc.
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
代码进入到base/core/jni/AndroidRuntime.cpp里面。
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const char* options)
{
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
blockSigpipe();
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
if (strcmp(options, "start-system-server") == 0) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("Unable to register all android natives\n");
return;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
jstring optionsStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(2, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
optionsStr = env->NewStringUTF(options);
env->SetObjectArrayElement(strArray, 1, optionsStr);
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
LOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
}
//init.rc 已经建立global 环境
# setup the global environment
export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
export LD_LIBRARY_PATH /vendor/lib:/system/lib
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ASEC_MOUNTPOINT /mnt/asec
export LOOP_MOUNTPOINT /mnt/obb
export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
//start vm
/*
* Start the Dalvik Virtual Machine.
*
* Various arguments, most determined by system properties, are passed in.
* The "mOptions" vector is updated.
*
* Returns 0 on success.
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
int result = -1;
JavaVMInitArgs initArgs;
JavaVMOption opt;
char propBuf[PROPERTY_VALUE_MAX];
char stackTraceFileBuf[PROPERTY_VALUE_MAX];
char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
char* stackTraceFile = NULL;
bool checkJni = false;
bool checkDexSum = false;
bool logStdio = false;
enum {
kEMDefault,
kEMIntPortable,
kEMIntFast,
#if defined(WITH_JIT)
kEMJitCompiler,
#endif
} executionMode = kEMDefault;
property_get("dalvik.vm.checkjni", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkJni = true;
} else if (strcmp(propBuf, "false") != 0) {
/* property is neither true nor false; fall back on kernel parameter */
property_get("ro.kernel.android.checkjni", propBuf, "");
if (propBuf[0] == '1') {
checkJni = true;
}
}
property_get("dalvik.vm.execution-mode", propBuf, "");
if (strcmp(propBuf, "int:portable") == 0) {
executionMode = kEMIntPortable;
} else if (strcmp(propBuf, "int:fast") == 0) {
executionMode = kEMIntFast;
#if defined(WITH_JIT)
} else if (strcmp(propBuf, "int:jit") == 0) {
executionMode = kEMJitCompiler;
#endif
}
property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, "");
property_get("dalvik.vm.check-dex-sum", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkDexSum = true;
}
property_get("log.redirect-stdio", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
logStdio = true;
}
strcpy(enableAssertBuf, "-ea:");
property_get("dalvik.vm.enableassertions", enableAssertBuf+4, "");
strcpy(jniOptsBuf, "-Xjniopts:");
property_get("dalvik.vm.jniopts", jniOptsBuf+10, "");
/* route exit() to our handler */
opt.extraInfo = (void*) runtime_exit;
opt.optionString = "exit";
mOptions.add(opt);
/* route fprintf() to our handler */
opt.extraInfo = (void*) runtime_vfprintf;
opt.optionString = "vfprintf";
mOptions.add(opt);
/* register the framework-specific "is sensitive thread" hook */
opt.extraInfo = (void*) runtime_isSensitiveThread;
opt.optionString = "sensitiveThread";
mOptions.add(opt);
opt.extraInfo = NULL;
/* enable verbose; standard options are { jni, gc, class } */
//options[curOpt++].optionString = "-verbose:jni";
opt.optionString = "-verbose:gc";
mOptions.add(opt);
//options[curOpt++].optionString = "-verbose:class";
/*
* The default starting and maximum size of the heap. Larger
* values should be specified in a product property override.
*/
strcpy(heapstartsizeOptsBuf, "-Xms");
property_get("dalvik.vm.heapstartsize", heapstartsizeOptsBuf+4, "4m");
opt.optionString = heapstartsizeOptsBuf;
mOptions.add(opt);
strcpy(heapsizeOptsBuf, "-Xmx");
property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
opt.optionString = heapsizeOptsBuf;
mOptions.add(opt);
// Increase the main thread's interpreter stack size for bug 6315322.
opt.optionString = "-XX:mainThreadStackSize=24K";
mOptions.add(opt);
strcpy(heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
property_get("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf+20, "");
if (heapgrowthlimitOptsBuf[20] != '\0') {
opt.optionString = heapgrowthlimitOptsBuf;
mOptions.add(opt);
}
/*
* Enable or disable dexopt features, such as bytecode verification and
* calculation of register maps for precise GC.
*/
property_get("dalvik.vm.dexopt-flags", dexoptFlagsBuf, "");
if (dexoptFlagsBuf[0] != '\0') {
const char* opc;
const char* val;
opc = strstr(dexoptFlagsBuf, "v="); /* verification */
if (opc != NULL) {
switch (*(opc+2)) {
case 'n': val = "-Xverify:none"; break;
case 'r': val = "-Xverify:remote"; break;
case 'a': val = "-Xverify:all"; break;
default: val = NULL; break;
}
if (val != NULL) {
opt.optionString = val;
mOptions.add(opt);
}
}
opc = strstr(dexoptFlagsBuf, "o="); /* optimization */
if (opc != NULL) {
switch (*(opc+2)) {
case 'n': val = "-Xdexopt:none"; break;
case 'v': val = "-Xdexopt:verified"; break;
case 'a': val = "-Xdexopt:all"; break;
case 'f': val = "-Xdexopt:full"; break;
default: val = NULL; break;
}
if (val != NULL) {
opt.optionString = val;
mOptions.add(opt);
}
}
opc = strstr(dexoptFlagsBuf, "m=y"); /* register map */
if (opc != NULL) {
opt.optionString = "-Xgenregmap";
mOptions.add(opt);
/* turn on precise GC while we're at it */
opt.optionString = "-Xgc:precise";
mOptions.add(opt);
}
}
/* enable debugging; set suspend=y to pause during VM init */
/* use android ADB transport */
opt.optionString =
"-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
mOptions.add(opt);
ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
if (checkJni) {
/* extended JNI checking */
opt.optionString = "-Xcheck:jni";
mOptions.add(opt);
/* set a cap on JNI global references */
opt.optionString = "-Xjnigreflimit:2000";
mOptions.add(opt);
/* with -Xcheck:jni, this provides a JNI function call trace */
//opt.optionString = "-verbose:jni";
//mOptions.add(opt);
}
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
property_get("dalvik.vm.lockprof.threshold", propBuf, "");
if (strlen(propBuf) > 0) {
strcpy(lockProfThresholdBuf, "-Xlockprofthreshold:");
strcat(lockProfThresholdBuf, propBuf);
opt.optionString = lockProfThresholdBuf;
mOptions.add(opt);
}
#if defined(WITH_JIT)
/* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
property_get("dalvik.vm.jit.op", propBuf, "");
if (strlen(propBuf) > 0) {
strcpy(jitOpBuf, "-Xjitop:");
strcat(jitOpBuf, propBuf);
opt.optionString = jitOpBuf;
mOptions.add(opt);
}
/* Force interpreter-only mode for selected methods */
char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
property_get("dalvik.vm.jit.method", propBuf, "");
if (strlen(propBuf) > 0) {
strcpy(jitMethodBuf, "-Xjitmethod:");
strcat(jitMethodBuf, propBuf);
opt.optionString = jitMethodBuf;
mOptions.add(opt);
}
#endif
if (executionMode == kEMIntPortable) {
opt.optionString = "-Xint:portable";
mOptions.add(opt);
} else if (executionMode == kEMIntFast) {
opt.optionString = "-Xint:fast";
mOptions.add(opt);
#if defined(WITH_JIT)
} else if (executionMode == kEMJitCompiler) {
opt.optionString = "-Xint:jit";
mOptions.add(opt);
#endif
}
if (checkDexSum) {
/* perform additional DEX checksum tests */
opt.optionString = "-Xcheckdexsum";
mOptions.add(opt);
}
if (logStdio) {
/* convert stdout/stderr to log messages */
opt.optionString = "-Xlog-stdio";
mOptions.add(opt);
}
if (enableAssertBuf[4] != '\0') {
/* accept "all" to mean "all classes and packages" */
if (strcmp(enableAssertBuf+4, "all") == 0)
enableAssertBuf[3] = '\0';
ALOGI("Assertions enabled: '%s'\n", enableAssertBuf);
opt.optionString = enableAssertBuf;
mOptions.add(opt);
} else {
ALOGV("Assertions disabled\n");
}
if (jniOptsBuf[10] != '\0') {
ALOGI("JNI options: '%s'\n", jniOptsBuf);
opt.optionString = jniOptsBuf;
mOptions.add(opt);
}
if (stackTraceFileBuf[0] != '\0') {
static const char* stfOptName = "-Xstacktracefile:";
stackTraceFile = (char*) malloc(strlen(stfOptName) +
strlen(stackTraceFileBuf) +1);
strcpy(stackTraceFile, stfOptName);
strcat(stackTraceFile, stackTraceFileBuf);
opt.optionString = stackTraceFile;
mOptions.add(opt);
}
/* extra options; parse this late so it overrides others */
property_get("dalvik.vm.extra-opts", extraOptsBuf, "");
parseExtraOpts(extraOptsBuf);
/* Set the properties for locale */
{
char langOption[sizeof("-Duser.language=") + 3];
char regionOption[sizeof("-Duser.region=") + 3];
strcpy(langOption, "-Duser.language=");
strcpy(regionOption, "-Duser.region=");
readLocale(langOption, regionOption);
opt.extraInfo = NULL;
opt.optionString = langOption;
mOptions.add(opt);
opt.optionString = regionOption;
mOptions.add(opt);
}
/*
* We don't have /tmp on the device, but we often have an SD card. Apps
* shouldn't use this, but some test suites might want to exercise it.
*/
opt.optionString = "-Djava.io.tmpdir=/sdcard";
mOptions.add(opt);
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
goto bail;
}
result = 0;
bail:
free(stackTraceFile);
return result;
}
能发现JNI_CreateJavaVM 的原型在dalvik/vm/Jni.cpp 可以找到
Cpp代码
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
if (args->version < JNI_VERSION_1_2) {
return JNI_EVERSION;
}
// TODO: don't allow creation of multiple VMs -- one per customer for now
/* zero globals; not strictly necessary the first time a VM is started */
memset(&gDvm, 0, sizeof(gDvm));
/*
* Set up structures for JNIEnv and VM.
*/
JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
pVM->funcTable = &gInvokeInterface;
pVM->envList = NULL;
dvmInitMutex(&pVM->envListLock);
UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
/*
* Convert JNI args to argv.
*
* We have to pull out vfprintf/exit/abort, because they use the
* "extraInfo" field to pass function pointer "hooks" in. We also
* look for the -Xcheck:jni stuff here.
*/
int argc = 0;
for (int i = 0; i < args->nOptions; i++) {
const char* optStr = args->options[i].optionString;
if (optStr == NULL) {
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
return JNI_ERR;
} else if (strcmp(optStr, "vfprintf") == 0) {
gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
} else if (strcmp(optStr, "exit") == 0) {
gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
} else if (strcmp(optStr, "abort") == 0) {
gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
} else if (strcmp(optStr, "sensitiveThread") == 0) {
gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
} else if (strcmp(optStr, "-Xcheck:jni") == 0) {
gDvmJni.useCheckJni = true;
} else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
char* jniOpts = strdup(optStr + 10);
size_t jniOptCount = 1;
for (char* p = jniOpts; *p != 0; ++p) {
if (*p == ',') {
++jniOptCount;
*p = 0;
}
}
char* jniOpt = jniOpts;
for (size_t i = 0; i < jniOptCount; ++i) {
if (strcmp(jniOpt, "warnonly") == 0) {
gDvmJni.warnOnly = true;
} else if (strcmp(jniOpt, "forcecopy") == 0) {
gDvmJni.forceCopy = true;
} else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {
gDvmJni.logThirdPartyJni = true;
} else {
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
jniOpt);
return JNI_ERR;
}
jniOpt += strlen(jniOpt) + 1;
}
free(jniOpts);
} else {
/* regular option */
argv[argc++] = optStr;
}
}
if (gDvmJni.useCheckJni) {
dvmUseCheckedJniVm(pVM);
}
if (gDvmJni.jniVm != NULL) {
dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
return JNI_ERR;
}
gDvmJni.jniVm = (JavaVM*) pVM;
/*
* Create a JNIEnv for the main thread. We need to have something set up
* here because some of the class initialization we do when starting
* up the VM will call into native code.
*/
JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
/* Initialize VM. */
gDvm.initializing = true;
std::string status =
dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
gDvm.initializing = false;
if (!status.empty()) {
free(pEnv);
free(pVM);
ALOGW("CreateJavaVM failed: %s", status.c_str());
return JNI_ERR;
}
/*
* Success! Return stuff to caller.
*/
dvmChangeStatus(NULL, THREAD_NATIVE);
*p_env = (JNIEnv*) pEnv;
*p_vm = (JavaVM*) pVM;
ALOGV("CreateJavaVM succeeded");
return JNI_OK;
}
然后调用Jni.cpp create JNI environment.
/*
* Create a new JNIEnv struct and add it to the VM's list.
*
* "self" will be NULL for the main thread, since the VM hasn't started
* yet; the value will be filled in later.
*/
JNIEnv* dvmCreateJNIEnv(Thread* self) {
JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
//if (self != NULL)
// LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
assert(vm != NULL);
JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
newEnv->funcTable = &gNativeInterface;
if (self != NULL) {
dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
assert(newEnv->envThreadId != 0);
} else {
/* make it obvious if we fail to initialize these later */
newEnv->envThreadId = 0x77777775;
newEnv->self = (Thread*) 0x77777779;
}
if (gDvmJni.useCheckJni) {
dvmUseCheckedJniEnv(newEnv);
}
ScopedPthreadMutexLock lock(&vm->envListLock);
/* insert at head of list */
newEnv->next = vm->envList;
assert(newEnv->prev == NULL);
if (vm->envList == NULL) {
// rare, but possible
vm->envList = newEnv;
} else {
vm->envList->prev = newEnv;
}
vm->envList = newEnv;
//if (self != NULL)
// LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
return (JNIEnv*) newEnv;
}
让我们看看JNI environment type
struct JavaVMExt;
struct JNIEnvExt {
const struct JNINativeInterface* funcTable; /* must be first */
const struct JNINativeInterface* baseFuncTable;
u4 envThreadId;
Thread* self;
/* if nonzero, we are in a "critical" JNI call */
int critical;
struct JNIEnvExt* prev;
struct JNIEnvExt* next;
};
struct JavaVMExt {
const struct JNIInvokeInterface* funcTable; /* must be first */
const struct JNIInvokeInterface* baseFuncTable;
/* head of list of JNIEnvs associated with this VM */
JNIEnvExt* envList;
pthread_mutex_t envListLock;
};
启动dvm 的方法。你能在Jni.cpp 发现启动代码
std::string status =
dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
在Dalvik/vm/Init.cpp中
/*
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
ScopedShutdown scopedShutdown;
assert(gDvm.initializing);
//init arguments
ALOGV("VM init args (%d):", argc);
for (int i = 0; i < argc; i++) {
ALOGV(" %d: '%s'", i, argv[i]);
}
setCommandLineDefaults();
/*
* Process the option flags (if any).
*/
int cc = processOptions(argc, argv, ignoreUnrecognized);
if (cc != 0) {
if (cc < 0) {
dvmFprintf(stderr, "\n");
usage("dalvikvm");
}
return "syntax error";
}
#if WITH_EXTRA_GC_CHECKS > 1
/* only "portable" interp has the extra goodies */
if (gDvm.executionMode != kExecutionModeInterpPortable) {
ALOGI("Switching to 'portable' interpreter for GC checks");
gDvm.executionMode = kExecutionModeInterpPortable;
}
#endif
/* Configure group scheduling capabilities */
if (!access("/dev/cpuctl/tasks", F_OK)) {
ALOGV("Using kernel group scheduling, err: %s", strerror(errno));
gDvm.kernelGroupScheduling = 1;
} else {
ALOGV("Using kernel scheduler policies");
}
/* configure signal handling */
if (!gDvm.reduceSignals)
blockSignals();
/* verify system page size */
if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
return StringPrintf("expected page size %d, got %d",
SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
}
/* mterp setup */
ALOGV("Using executionMode %d", gDvm.executionMode);
dvmCheckAsmConstants();
/*
* Initialize components.
*/
if (!dvmAllocTrackerStartup()) {
return "dvmAllocTrackerStartup failed";
}
if (!dvmGcStartup()) {
return "dvmGcStartup failed";
}
if (!dvmThreadStartup()) {
return "dvmThreadStartup failed";
}
if (!dvmInlineNativeStartup()) {
return "dvmInlineNativeStartup";
}
if (!dvmRegisterMapStartup()) {
return "dvmRegisterMapStartup failed";
}
if (!dvmInstanceofStartup()) {
return "dvmInstanceofStartup failed";
}
if (!dvmClassStartup()) {
return "dvmClassStartup failed";
}
/*
* At this point, the system is guaranteed to be sufficiently
* initialized that we can look up classes and class members. This
* call populates the gDvm instance with all the class and member
* references that the VM wants to use directly.
*/
if (!dvmFindRequiredClassesAndMembers()) {
return "dvmFindRequiredClassesAndMembers failed";
}
if (!dvmStringInternStartup()) {
return "dvmStringInternStartup failed";
}
if (!dvmNativeStartup()) {
return "dvmNativeStartup failed";
}
if (!dvmInternalNativeStartup()) {
return "dvmInternalNativeStartup failed";
}
if (!dvmJniStartup()) {
return "dvmJniStartup failed";
}
if (!dvmProfilingStartup()) {
return "dvmProfilingStartup failed";
}
/*
* Create a table of methods for which we will substitute an "inline"
* version for performance.
*/
if (!dvmCreateInlineSubsTable()) {
return "dvmCreateInlineSubsTable failed";
}
/*
* Miscellaneous class library validation.
*/
if (!dvmValidateBoxClasses()) {
return "dvmValidateBoxClasses failed";
}
/*
* Do the last bits of Thread struct initialization we need to allow
* JNI calls to work.
*/
if (!dvmPrepMainForJni(pEnv)) {
return "dvmPrepMainForJni failed";
}
/*
* Explicitly initialize java.lang.Class. This doesn't happen
* automatically because it's allocated specially (it's an instance
* of itself). Must happen before registration of system natives,
* which make some calls that throw assertions if the classes they
* operate on aren't initialized.
*/
if (!dvmInitClass(gDvm.classJavaLangClass)) {
return "couldn't initialized java.lang.Class";
}
/*
* Register the system native methods, which are registered through JNI.
*/
if (!registerSystemNatives(pEnv)) {
return "couldn't register system natives";
}
/*
* Do some "late" initialization for the memory allocator. This may
* allocate storage and initialize classes.
*/
if (!dvmCreateStockExceptions()) {
return "dvmCreateStockExceptions failed";
}
/*
* At this point, the VM is in a pretty good state. Finish prep on
* the main thread (specifically, create a java.lang.Thread object to go
* along with our Thread struct). Note we will probably be executing
* some interpreted class initializer code in here.
*/
if (!dvmPrepMainThread()) {
return "dvmPrepMainThread failed";
}
/*
* Make sure we haven't accumulated any tracked references. The main
* thread should be starting with a clean slate.
*/
if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
{
ALOGW("Warning: tracked references remain post-initialization");
dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
}
/* general debugging setup */
if (!dvmDebuggerStartup()) {
return "dvmDebuggerStartup failed";
}
if (!dvmGcStartupClasses()) {
return "dvmGcStartupClasses failed";
}
/*
* Init for either zygote mode or non-zygote mode. The key difference
* is that we don't start any additional threads in Zygote mode.
*/
if (gDvm.zygote) {
if (!initZygote()) {
return "initZygote failed";
}
} else {
if (!dvmInitAfterZygote()) {
return "dvmInitAfterZygote failed";
}
}
#ifndef NDEBUG
if (!dvmTestHash())
ALOGE("dvmTestHash FAILED");
if (false /*noisy!*/ && !dvmTestIndirectRefTable())
ALOGE("dvmTestIndirectRefTable FAILED");
#endif
if (dvmCheckException(dvmThreadSelf())) {
dvmLogExceptionStackTrace();
return "Exception pending at end of VM initialization";
}
scopedShutdown.disarm();
return "";
}
class ScopedShutdown {
public:
ScopedShutdown() : armed_(true) {
}
~ScopedShutdown() {
if (armed_) {
dvmShutdown();
}
}
void disarm() {
armed_ = false;
}
private:
bool armed_;
};
//set default command line
static void setCommandLineDefaults()
{
const char* envStr = getenv("CLASSPATH");
if (envStr != NULL) {
gDvm.classPathStr = strdup(envStr);
} else {
gDvm.classPathStr = strdup(".");
}
envStr = getenv("BOOTCLASSPATH");
if (envStr != NULL) {
gDvm.bootClassPathStr = strdup(envStr);
} else {
gDvm.bootClassPathStr = strdup(".");
}
gDvm.properties = new std::vector<std::string>();
/* Defaults overridden by -Xms and -Xmx.
* TODO: base these on a system or application-specific default
*/
gDvm.heapStartingSize = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
gDvm.heapMaximumSize = 16 * 1024 * 1024; // Spec says 75% physical mem
gDvm.heapGrowthLimit = 0; // 0 means no growth limit
gDvm.stackSize = kDefaultStackSize;
gDvm.mainThreadStackSize = kDefaultStackSize;
gDvm.concurrentMarkSweep = true;
/* gDvm.jdwpSuspend = true; */
/* allowed unless zygote config doesn't allow it */
gDvm.jdwpAllowed = true;
/* default verification and optimization modes */
gDvm.classVerifyMode = VERIFY_MODE_ALL;
gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
gDvm.monitorVerification = false;
gDvm.generateRegisterMaps = true;
gDvm.registerMapMode = kRegisterMapModeTypePrecise;
/*
* Default execution mode.
*
* This should probably interact with the mterp code somehow, e.g. if
* we know we're using the "desktop" build we should probably be
* using "portable" rather than "fast".
*/
#if defined(WITH_JIT)
gDvm.executionMode = kExecutionModeJit;
#else
gDvm.executionMode = kExecutionModeInterpFast;
#endif
/*
* SMP support is a compile-time define, but we may want to have
* dexopt target a differently-configured device.
*/
gDvm.dexOptForSmp = (ANDROID_SMP != 0);
/*
* Default profiler configuration.
*/
gDvm.profilerClockSource = kProfilerClockSourceDual;
}
// parser and process option
/*
* Process an argument vector full of options. Unlike standard C programs,
* argv[0] does not contain the name of the program.
*
* If "ignoreUnrecognized" is set, we ignore options starting with "-X" or "_"
* that we don't recognize. Otherwise, we return with an error as soon as
* we see anything we can't identify.
*
* Returns 0 on success, -1 on failure, and 1 for the special case of
* "-version" where we want to stop without showing an error message.
*/
static int processOptions(int argc, const char* const argv[],
bool ignoreUnrecognized)
{
int i;
ALOGV("VM options (%d):", argc);
for (i = 0; i < argc; i++)
ALOGV(" %d: '%s'", i, argv[i]);
/*
* Over-allocate AssertionControl array for convenience. If allocated,
* the array must be able to hold at least one entry, so that the
* zygote-time activation can do its business.
*/
assert(gDvm.assertionCtrl == NULL);
if (argc > 0) {
gDvm.assertionCtrl =
(AssertionControl*) malloc(sizeof(AssertionControl) * argc);
if (gDvm.assertionCtrl == NULL)
return -1;
assert(gDvm.assertionCtrlCount == 0);
}
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "-help") == 0) {
/* show usage and stop */
return -1;
} else if (strcmp(argv[i], "-version") == 0) {
/* show version and stop */
showVersion();
return 1;
} else if (strcmp(argv[i], "-showversion") == 0) {
/* show version and continue */
showVersion();
} else if (strcmp(argv[i], "-classpath") == 0 ||
strcmp(argv[i], "-cp") == 0)
{
/* set classpath */
if (i == argc-1) {
dvmFprintf(stderr, "Missing classpath path list\n");
return -1;
}
free(gDvm.classPathStr); /* in case we have compiled-in default */
gDvm.classPathStr = strdup(argv[++i]);
} else if (strncmp(argv[i], "-Xbootclasspath:",
sizeof("-Xbootclasspath:")-1) == 0)
{
/* set bootclasspath */
const char* path = argv[i] + sizeof("-Xbootclasspath:")-1;
if (*path == '\0') {
dvmFprintf(stderr, "Missing bootclasspath path list\n");
return -1;
}
free(gDvm.bootClassPathStr);
gDvm.bootClassPathStr = strdup(path);
} else if (strncmp(argv[i], "-Xbootclasspath/a:",
sizeof("-Xbootclasspath/a:")-1) == 0) {
const char* appPath = argv[i] + sizeof("-Xbootclasspath/a:")-1;
if (*(appPath) == '\0') {
dvmFprintf(stderr, "Missing appending bootclasspath path list\n");
return -1;
}
char* allPath;
if (asprintf(&allPath, "%s:%s", gDvm.bootClassPathStr, appPath) < 0) {
dvmFprintf(stderr, "Can't append to bootclasspath path list\n");
return -1;
}
free(gDvm.bootClassPathStr);
gDvm.bootClassPathStr = allPath;
} else if (strncmp(argv[i], "-Xbootclasspath/p:",
sizeof("-Xbootclasspath/p:")-1) == 0) {
const char* prePath = argv[i] + sizeof("-Xbootclasspath/p:")-1;
if (*(prePath) == '\0') {
dvmFprintf(stderr, "Missing prepending bootclasspath path list\n");
return -1;
}
char* allPath;
if (asprintf(&allPath, "%s:%s", prePath, gDvm.bootClassPathStr) < 0) {
dvmFprintf(stderr, "Can't prepend to bootclasspath path list\n");
return -1;
}
free(gDvm.bootClassPathStr);
gDvm.bootClassPathStr = allPath;
} else if (strncmp(argv[i], "-D", 2) == 0) {
/* Properties are handled in managed code. We just check syntax. */
if (strchr(argv[i], '=') == NULL) {
dvmFprintf(stderr, "Bad system property setting: \"%s\"\n",
argv[i]);
return -1;
}
gDvm.properties->push_back(argv[i] + 2);
} else if (strcmp(argv[i], "-jar") == 0) {
// TODO: handle this; name of jar should be in argv[i+1]
dvmFprintf(stderr, "-jar not yet handled\n");
assert(false);
} else if (strncmp(argv[i], "-Xms", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1024);
if (val != 0) {
if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
gDvm.heapStartingSize = val;
} else {
dvmFprintf(stderr,
"Invalid -Xms '%s', range is %dKB to %dKB\n",
argv[i], kMinHeapStartSize/1024, kMaxHeapSize/1024);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -Xms option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xmx", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1024);
if (val != 0) {
if (val >= kMinHeapSize && val <= kMaxHeapSize) {
gDvm.heapMaximumSize = val;
} else {
dvmFprintf(stderr,
"Invalid -Xmx '%s', range is %dKB to %dKB\n",
argv[i], kMinHeapSize/1024, kMaxHeapSize/1024);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -Xmx option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-XX:HeapGrowthLimit=", 20) == 0) {
size_t val = parseMemOption(argv[i] + 20, 1024);
if (val != 0) {
gDvm.heapGrowthLimit = val;
} else {
dvmFprintf(stderr, "Invalid -XX:HeapGrowthLimit option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xss", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1);
if (val != 0) {
if (val >= kMinStackSize && val <= kMaxStackSize) {
gDvm.stackSize = val;
if (val > gDvm.mainThreadStackSize) {
gDvm.mainThreadStackSize = val;
}
} else {
dvmFprintf(stderr, "Invalid -Xss '%s', range is %d to %d\n",
argv[i], kMinStackSize, kMaxStackSize);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -Xss option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-XX:mainThreadStackSize=", strlen("-XX:mainThreadStackSize=")) == 0) {
size_t val = parseMemOption(argv[i] + strlen("-XX:mainThreadStackSize="), 1);
if (val != 0) {
if (val >= kMinStackSize && val <= kMaxStackSize) {
gDvm.mainThreadStackSize = val;
} else {
dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize '%s', range is %d to %d\n",
argv[i], kMinStackSize, kMaxStackSize);
return -1;
}
} else {
dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize option '%s'\n", argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-XX:+DisableExplicitGC", 22) == 0) {
gDvm.disableExplicitGc = true;
} else if (strcmp(argv[i], "-verbose") == 0 ||
strcmp(argv[i], "-verbose:class") == 0)
{
// JNI spec says "-verbose:gc,class" is valid, but cmd line
// doesn't work that way; may want to support.
gDvm.verboseClass = true;
} else if (strcmp(argv[i], "-verbose:jni") == 0) {
gDvm.verboseJni = true;
} else if (strcmp(argv[i], "-verbose:gc") == 0) {
gDvm.verboseGc = true;
} else if (strcmp(argv[i], "-verbose:shutdown") == 0) {
gDvm.verboseShutdown = true;
} else if (strncmp(argv[i], "-enableassertions", 17) == 0) {
enableAssertions(argv[i] + 17, true);
} else if (strncmp(argv[i], "-ea", 3) == 0) {
enableAssertions(argv[i] + 3, true);
} else if (strncmp(argv[i], "-disableassertions", 18) == 0) {
enableAssertions(argv[i] + 18, false);
} else if (strncmp(argv[i], "-da", 3) == 0) {
enableAssertions(argv[i] + 3, false);
} else if (strcmp(argv[i], "-enablesystemassertions") == 0 ||
strcmp(argv[i], "-esa") == 0)
{
enableAssertions(NULL, true);
} else if (strcmp(argv[i], "-disablesystemassertions") == 0 ||
strcmp(argv[i], "-dsa") == 0)
{
enableAssertions(NULL, false);
} else if (strncmp(argv[i], "-Xcheck:jni", 11) == 0) {
/* nothing to do now -- was handled during JNI init */
} else if (strcmp(argv[i], "-Xdebug") == 0) {
/* accept but ignore */
} else if (strncmp(argv[i], "-Xrunjdwp:", 10) == 0 ||
strncmp(argv[i], "-agentlib:jdwp=", 15) == 0)
{
const char* tail;
if (argv[i][1] == 'X')
tail = argv[i] + 10;
else
tail = argv[i] + 15;
if (strncmp(tail, "help", 4) == 0 || !parseJdwpOptions(tail)) {
showJdwpHelp();
return 1;
}
} else if (strcmp(argv[i], "-Xrs") == 0) {
gDvm.reduceSignals = true;
} else if (strcmp(argv[i], "-Xnoquithandler") == 0) {
/* disables SIGQUIT handler thread while still blocking SIGQUIT */
/* (useful if we don't want thread but system still signals us) */
gDvm.noQuitHandler = true;
} else if (strcmp(argv[i], "-Xzygote") == 0) {
gDvm.zygote = true;
#if defined(WITH_JIT)
gDvmJit.runningInAndroidFramework = true;
#endif
} else if (strncmp(argv[i], "-Xdexopt:", 9) == 0) {
if (strcmp(argv[i] + 9, "none") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_NONE;
else if (strcmp(argv[i] + 9, "verified") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
else if (strcmp(argv[i] + 9, "all") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_ALL;
else if (strcmp(argv[i] + 9, "full") == 0)
gDvm.dexOptMode = OPTIMIZE_MODE_FULL;
else {
dvmFprintf(stderr, "Unrecognized dexopt option '%s'\n",argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xverify:", 9) == 0) {
if (strcmp(argv[i] + 9, "none") == 0)
gDvm.classVerifyMode = VERIFY_MODE_NONE;
else if (strcmp(argv[i] + 9, "remote") == 0)
gDvm.classVerifyMode = VERIFY_MODE_REMOTE;
else if (strcmp(argv[i] + 9, "all") == 0)
gDvm.classVerifyMode = VERIFY_MODE_ALL;
else {
dvmFprintf(stderr, "Unrecognized verify option '%s'\n",argv[i]);
return -1;
}
} else if (strncmp(argv[i], "-Xjnigreflimit:", 15) == 0) {
int lim = atoi(argv[i] + 15);
if (lim < 200 || (lim % 100) != 0) {
dvmFprintf(stderr, "Bad value for -Xjnigreflimit: '%s'\n",
argv[i]+15);
return -1;
}
gDvm.jniGrefLimit = lim;
} else if (strncmp(argv[i], "-Xjnitrace:", 11) == 0) {
gDvm.jniTrace = strdup(argv[i] + 11);
} else if (strcmp(argv[i], "-Xlog-stdio") == 0) {
gDvm.logStdio = true;
} else if (strncmp(argv[i], "-Xint", 5) == 0) {
if (argv[i][5] == ':') {
if (strcmp(argv[i] + 6, "portable") == 0)
gDvm.executionMode = kExecutionModeInterpPortable;
else if (strcmp(argv[i] + 6, "fast") == 0)
gDvm.executionMode = kExecutionModeInterpFast;
#ifdef WITH_JIT
else if (strcmp(argv[i] + 6, "jit") == 0)
gDvm.executionMode = kExecutionModeJit;
#endif
else {
dvmFprintf(stderr,
"Warning: Unrecognized interpreter mode %s\n",argv[i]);
/* keep going */
}
} else {
/* disable JIT if it was enabled by default */
gDvm.executionMode = kExecutionModeInterpFast;
}
} else if (strncmp(argv[i], "-Xlockprofthreshold:", 20) == 0) {
gDvm.lockProfThreshold = atoi(argv[i] + 20);
#ifdef WITH_JIT
} else if (strncmp(argv[i], "-Xjitop", 7) == 0) {
processXjitop(argv[i]);
} else if (strncmp(argv[i], "-Xjitmethod", 11) == 0) {
processXjitmethod(argv[i]);
} else if (strncmp(argv[i], "-Xjitblocking", 13) == 0) {
gDvmJit.blockingMode = true;
} else if (strncmp(argv[i], "-Xjitthreshold:", 15) == 0) {
gDvmJit.threshold = atoi(argv[i] + 15);
} else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) {
gDvmJit.includeSelectedOp = true;
} else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) {
gDvmJit.includeSelectedMethod = true;
} else if (strncmp(argv[i], "-Xjitcheckcg", 12) == 0) {
gDvmJit.checkCallGraph = true;
/* Need to enable blocking mode due to stack crawling */
gDvmJit.blockingMode = true;
} else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) {
gDvmJit.printMe = true;
} else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
gDvmJit.profileMode = kTraceProfilingContinuous;
} else if (strncmp(argv[i], "-Xjitdisableopt", 15) == 0) {
/* Disable selected optimizations */
if (argv[i][15] == ':') {
sscanf(argv[i] + 16, "%x", &gDvmJit.disableOpt);
/* Disable all optimizations */
} else {
gDvmJit.disableOpt = -1;
}
} else if (strncmp(argv[i], "-Xjitsuspendpoll", 16) == 0) {
gDvmJit.genSuspendPoll = true;
#endif
} else if (strncmp(argv[i], "-Xstacktracefile:", 17) == 0) {
gDvm.stackTraceFile = strdup(argv[i]+17);
} else if (strcmp(argv[i], "-Xgenregmap") == 0) {
gDvm.generateRegisterMaps = true;
} else if (strcmp(argv[i], "-Xnogenregmap") == 0) {
gDvm.generateRegisterMaps = false;
} else if (strcmp(argv[i], "Xverifyopt:checkmon") == 0) {
gDvm.monitorVerification = true;
} else if (strcmp(argv[i], "Xverifyopt:nocheckmon") == 0) {
gDvm.monitorVerification = false;
} else if (strncmp(argv[i], "-Xgc:", 5) == 0) {
if (strcmp(argv[i] + 5, "precise") == 0)
gDvm.preciseGc = true;
else if (strcmp(argv[i] + 5, "noprecise") == 0)
gDvm.preciseGc = false;
else if (strcmp(argv[i] + 5, "preverify") == 0)
gDvm.preVerify = true;
else if (strcmp(argv[i] + 5, "nopreverify") == 0)
gDvm.preVerify = false;
else if (strcmp(argv[i] + 5, "postverify") == 0)
gDvm.postVerify = true;
else if (strcmp(argv[i] + 5, "nopostverify") == 0)
gDvm.postVerify = false;
else if (strcmp(argv[i] + 5, "concurrent") == 0)
gDvm.concurrentMarkSweep = true;
else if (strcmp(argv[i] + 5, "noconcurrent") == 0)
gDvm.concurrentMarkSweep = false;
else if (strcmp(argv[i] + 5, "verifycardtable") == 0)
gDvm.verifyCardTable = true;
else if (strcmp(argv[i] + 5, "noverifycardtable") == 0)
gDvm.verifyCardTable = false;
else {
dvmFprintf(stderr, "Bad value for -Xgc");
return -1;
}
ALOGV("Precise GC configured %s", gDvm.preciseGc ? "ON" : "OFF");
} else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
gDvm.verifyDexChecksum = true;
} else if (strcmp(argv[i], "-Xprofile:threadcpuclock") == 0) {
gDvm.profilerClockSource = kProfilerClockSourceThreadCpu;
} else if (strcmp(argv[i], "-Xprofile:wallclock") == 0) {
gDvm.profilerClockSource = kProfilerClockSourceWall;
} else if (strcmp(argv[i], "-Xprofile:dualclock") == 0) {
gDvm.profilerClockSource = kProfilerClockSourceDual;
} else {
if (!ignoreUnrecognized) {
dvmFprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
return -1;
}
}
}
return 0;
}
vm/alloc/Alloc.cpp
/*
* Initialize the GC universe.
*
* We're currently using a memory-mapped arena to keep things off of the
* main heap. This needs to be replaced with something real.
*/
bool dvmGcStartup()
{
dvmInitMutex(&gDvm.gcHeapLock);
pthread_cond_init(&gDvm.gcHeapCond, NULL);
return dvmHeapStartup();
}
//dvm Heap start up
bool dvmHeapStartup()
{
GcHeap *gcHeap;
if (gDvm.heapGrowthLimit == 0) {
gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
}
gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
gDvm.heapMaximumSize,
gDvm.heapGrowthLimit);
if (gcHeap == NULL) {
return false;
}
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
gcHeap->ddmNhsgWhen = 0;
gcHeap->ddmNhsgWhat = 0;
gDvm.gcHeap = gcHeap;
/* Set up the lists we'll use for cleared reference objects.
*/
gcHeap->clearedReferences = NULL;
if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
LOGE_HEAP("card table startup failed.");
return false;
}
return true;
}
vm/Misc.cpp
/*
* Initializes the heap source; must be called before any other
* dvmHeapSource*() functions. Returns a GcHeap structure
* allocated from the heap source.
*/
GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
size_t growthLimit)
{
GcHeap *gcHeap;
HeapSource *hs;
mspace msp;
size_t length;
void *base;
assert(gHs == NULL);
if (!(startSize <= growthLimit && growthLimit <= maximumSize)) {
ALOGE("Bad heap size parameters (start=%zd, max=%zd, limit=%zd)",
startSize, maximumSize, growthLimit);
return NULL;
}
/*
* Allocate a contiguous region of virtual memory to subdivided
* among the heaps managed by the garbage collector.
*/
length = ALIGN_UP_TO_PAGE_SIZE(maximumSize);
base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
if (base == NULL) {
return NULL;
}
/* Create an unlocked dlmalloc mspace to use as
* a heap source.
*/
msp = createMspace(base, startSize, maximumSize);
if (msp == NULL) {
goto fail;
}
gcHeap = (GcHeap *)calloc(1, sizeof(*gcHeap));
if (gcHeap == NULL) {
LOGE_HEAP("Can't allocate heap descriptor");
goto fail;
}
hs = (HeapSource *)calloc(1, sizeof(*hs));
if (hs == NULL) {
LOGE_HEAP("Can't allocate heap source");
free(gcHeap);
goto fail;
}
hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
hs->startSize = startSize;
hs->maximumSize = maximumSize;
hs->growthLimit = growthLimit;
hs->idealSize = startSize;
hs->softLimit = SIZE_MAX; // no soft limit at first
hs->numHeaps = 0;
hs->sawZygote = gDvm.zygote;
hs->hasGcThread = false;
hs->heapBase = (char *)base;
hs->heapLength = length;
if (!addInitialHeap(hs, msp, growthLimit)) {
LOGE_HEAP("Can't add initial heap");
goto fail;
}
if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
LOGE_HEAP("Can't create liveBits");
goto fail;
}
if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
LOGE_HEAP("Can't create markBits");
dvmHeapBitmapDelete(&hs->liveBits);
goto fail;
}
if (!allocMarkStack(&gcHeap->markContext.stack, hs->maximumSize)) {
ALOGE("Can't create markStack");
dvmHeapBitmapDelete(&hs->markBits);
dvmHeapBitmapDelete(&hs->liveBits);
goto fail;
}
gcHeap->markContext.bitmap = &hs->markBits;
gcHeap->heapSource = hs;
gHs = hs;
return gcHeap;
fail:
munmap(base, length);
return NULL;
}
vm/Misc.cpp
void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
void *base;
int fd, ret;
byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
fd = ashmem_create_region(name, byteCount);
if (fd == -1) {
return NULL;
}
base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
ret = close(fd);
if (base == MAP_FAILED) {
return NULL;
}
if (ret == -1) {
return NULL;
}
return base;
}
vm/alloc/HeapSource.cpp
static mspace createMspace(void *base, size_t startSize, size_t maximumSize)
{
/* Create an unlocked dlmalloc mspace to use as
* a heap source.
*
* We start off reserving startSize / 2 bytes but
* letting the heap grow to startSize. This saves
* memory in the case where a process uses even less
* than the starting size.
*/
LOGV_HEAP("Creating VM heap of size %zu", startSize);
errno = 0;
mspace msp = create_contiguous_mspace_with_base(startSize/2,
maximumSize, /*locked=*/false, base);
if (msp != NULL) {
/* Don't let the heap grow past the starting size without
* our intervention.
*/
mspace_set_max_allowed_footprint(msp, startSize);
} else {
/* There's no guarantee that errno has meaning when the call
* fails, but it often does.
*/
LOGE_HEAP("Can't create VM heap of size (%zu,%zu): %s",
startSize/2, maximumSize, strerror(errno));
}
return msp;
}
/vm/alloc/CardTable.cpp
*
* Initializes the card table; must be called before any other
* dvmCardTable*() functions.
*/
bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit)
{
size_t length;
void *allocBase;
u1 *biasedBase;
GcHeap *gcHeap = gDvm.gcHeap;
void *heapBase = dvmHeapSourceGetBase();
assert(gcHeap != NULL);
assert(heapBase != NULL);
/* Set up the card table */
length = heapMaximumSize / GC_CARD_SIZE;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
"dalvik-card-table");
if (allocBase == NULL) {
return false;
}
gcHeap->cardTableBase = (u1*)allocBase;
gcHeap->cardTableLength = growthLimit / GC_CARD_SIZE;
gcHeap->cardTableMaxLength = length;
gcHeap->cardTableOffset = 0;
/* All zeros is the correct initial value; all clean. */
assert(GC_CARD_CLEAN == 0);
biasedBase = (u1 *)((uintptr_t)allocBase -
((uintptr_t)heapBase >> GC_CARD_SHIFT));
if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) {
int offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
gcHeap->cardTableOffset = offset + (offset < 0 ? 0x100 : 0);
biasedBase += gcHeap->cardTableOffset;
}
assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
gDvm.biasedCardTableBase = biasedBase;
return true;
}
vm/alloc/HeapSource.cpp
void *dvmHeapSourceGetBase()
{
return gHs->heapBase;
}
/vm/Thread.cpp
/*
* Initialize thread list and main thread's environment. We need to set
* up some basic stuff so that dvmThreadSelf() will work when we start
* loading classes (e.g. to check for exceptions).
*/
bool dvmThreadStartup()
{
Thread* thread;
/* allocate a TLS slot */
if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
ALOGE("ERROR: pthread_key_create failed");
return false;
}
/* test our pthread lib */
if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
ALOGW("WARNING: newly-created pthread TLS slot is not NULL");
/* prep thread-related locks and conditions */
dvmInitMutex(&gDvm.threadListLock);
pthread_cond_init(&gDvm.threadStartCond, NULL);
pthread_cond_init(&gDvm.vmExitCond, NULL);
dvmInitMutex(&gDvm._threadSuspendLock);
dvmInitMutex(&gDvm.threadSuspendCountLock);
pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
/*
* Dedicated monitor for Thread.sleep().
* TODO: change this to an Object* so we don't have to expose this
* call, and we interact better with JDWP monitor calls. Requires
* deferring the object creation to much later (e.g. final "main"
* thread prep) or until first use.
*/
gDvm.threadSleepMon = dvmCreateMonitor(NULL);
gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);
thread = allocThread(gDvm.mainThreadStackSize);
if (thread == NULL)
return false;
/* switch mode for when we run initializers */
thread->status = THREAD_RUNNING;
/*
* We need to assign the threadId early so we can lock/notify
* object monitors. We'll set the "threadObj" field later.
*/
prepareThread(thread);
gDvm.threadList = thread;
#ifdef COUNT_PRECISE_METHODS
gDvm.preciseMethods = dvmPointerSetAlloc(200);
#endif
return true;
}
vm/Sync.cpp
/*
* Create and initialize a monitor.
*/
Monitor* dvmCreateMonitor(Object* obj)
{
Monitor* mon;
mon = (Monitor*) calloc(1, sizeof(Monitor));
if (mon == NULL) {
ALOGE("Unable to allocate monitor");
dvmAbort();
}
if (((u4)mon & 7) != 0) {
ALOGE("Misaligned monitor: %p", mon);
dvmAbort();
}
mon->obj = obj;
dvmInitMutex(&mon->lock);
/* replace the head of the list with the new monitor */
do {
mon->next = gDvm.monitorList;
} while (android_atomic_release_cas((int32_t)mon->next, (int32_t)mon,
(int32_t*)(void*)&gDvm.monitorList) != 0);
return mon;
}
vm/Bitvector.cpp
/*
* Allocate a bit vector with enough space to hold at least the specified
* number of bits.
*/
BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable)
{
BitVector* bv;
unsigned int count;
assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
bv = (BitVector*) malloc(sizeof(BitVector));
count = (startBits + 31) >> 5;
bv->storageSize = count;
bv->expandable = expandable;
bv->storage = (u4*) calloc(count, sizeof(u4));
return bv;
}
vm/thread.cpp
/*
* Finish initialization of a Thread struct.
*
* This must be called while executing in the new thread, but before the
* thread is added to the thread list.
*
* NOTE: The threadListLock must be held by the caller (needed for
* assignThreadId()).
*/
static bool prepareThread(Thread* thread)
{
assignThreadId(thread);
thread->handle = pthread_self();
thread->systemTid = dvmGetSysThreadId();
//ALOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid,
// (int) getpid());
/*
* If we were called by dvmAttachCurrentThread, the self value is
* already correctly established as "thread".
*/
setThreadSelf(thread);
ALOGV("threadid=%d: interp stack at %p",
thread->threadId, thread->interpStackStart - thread->interpStackSize);
/*
* Initialize invokeReq.
*/
dvmInitMutex(&thread->invokeReq.lock);
pthread_cond_init(&thread->invokeReq.cv, NULL);
/*
* Initialize our reference tracking tables.
*
* Most threads won't use jniMonitorRefTable, so we clear out the
* structure but don't call the init function (which allocs storage).
*/
if (!thread->jniLocalRefTable.init(kJniLocalRefMin,
kJniLocalRefMax, kIndirectKindLocal)) {
return false;
}
if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
kInternalRefDefault, kInternalRefMax))
return false;
memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
pthread_cond_init(&thread->waitCond, NULL);
dvmInitMutex(&thread->waitMutex);
/* Initialize safepoint callback mechanism */
dvmInitMutex(&thread->callbackMutex);
return true;
}
vm/inlineNative.cpp
/*
* Allocate some tables.
*/
bool dvmInlineNativeStartup()
{
gDvm.inlinedMethods =
(Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
if (gDvm.inlinedMethods == NULL)
return false;
return true;
}
vm/analysis/RegisterMap.cpp
/*
* Prepare some things.
*/
bool dvmRegisterMapStartup()
{
#ifdef REGISTER_MAP_STATS
MapStats* pStats = calloc(1, sizeof(MapStats));
gDvm.registerMapStats = pStats;
#endif
return true;
}
vm/oo/TypeCheck.cpp
* Allocate cache.
*/
bool dvmInstanceofStartup()
{
gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE);
if (gDvm.instanceofCache == NULL)
return false;
return true;
}
vm/oo/Class.cpp
/*
* Initialize the bootstrap class loader.
*
* Call this after the bootclasspath string has been finalized.
*/
bool dvmClassStartup()
{
/* make this a requirement -- don't currently support dirs in path */
if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
ALOGE("ERROR: must specify non-'.' bootclasspath");
return false;
}
gDvm.loadedClasses =
dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
if (gDvm.pBootLoaderAlloc == NULL)
return false;
if (false) {
linearAllocTests();
exit(0);
}
/*
* Class serial number. We start with a high value to make it distinct
* in binary dumps (e.g. hprof).
*/
gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
/*
* Set up the table we'll use for tracking initiating loaders for
* early classes.
* If it's NULL, we just fall back to the InitiatingLoaderList in the
* ClassObject, so it's not fatal to fail this allocation.
*/
gDvm.initiatingLoaderList = (InitiatingLoaderList*)
calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
/*
* Create the initial classes. These are the first objects constructed
* within the nascent VM.
*/
if (!createInitialClasses()) {
return false;
}
/*
* Process the bootstrap class path. This means opening the specified
* DEX or Jar files and possibly running them through the optimizer.
*/
assert(gDvm.bootClassPath == NULL);
processClassPath(gDvm.bootClassPathStr, true);
if (gDvm.bootClassPath == NULL)
return false;
return true;
}
vm/InitRefs.cpp
bool dvmFindRequiredClassesAndMembers() {
/*
* Note: Under normal VM use, this is called by dvmStartup()
* in Init.c. For dex optimization, this is called as well, but in
* that case, the call is made from DexPrepare.c.
*/
return initClassReferences()
&& initFieldOffsets()
&& initConstructorReferences()
&& initDirectMethodReferences()
&& initVirtualMethodOffsets()
&& initFinalizerReference()
&& verifyStringOffsets();
}
vm/oo/Array.cpp
/*
* Find an array class, by name (e.g. "[I").
*
* If the array class doesn't exist, we generate it.
*
* If the element class doesn't exist, we return NULL (no exception raised).
*/
ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
{
ClassObject* clazz;
assert(descriptor[0] == '[');
//ALOGV("dvmFindArrayClass: '%s' %p", descriptor, loader);
clazz = dvmLookupClass(descriptor, loader, false);
if (clazz == NULL) {
ALOGV("Array class '%s' %p not found; creating", descriptor, loader);
clazz = createArrayClass(descriptor, loader);
if (clazz != NULL)
dvmAddInitiatingLoader(clazz, loader);
}
return clazz;
}
vm/oo/Class.cpp
ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
bool unprepOkay)
{
ClassMatchCriteria crit;
void* found;
u4 hash;
crit.descriptor = descriptor;
crit.loader = loader;
hash = dvmComputeUtf8Hash(descriptor);
LOGVV("threadid=%d: dvmLookupClass searching for '%s' %p",
dvmThreadSelf()->threadId, descriptor, loader);
dvmHashTableLock(gDvm.loadedClasses);
found = dvmHashTableLookup(gDvm.loadedClasses, hash, &crit,
hashcmpClassByCrit, false);
dvmHashTableUnlock(gDvm.loadedClasses);
/*
* The class has been added to the hash table but isn't ready for use.
* We're going to act like we didn't see it, so that the caller will
* go through the full "find class" path, which includes locking the
* object and waiting until it's ready. We could do that lock/wait
* here, but this is an extremely rare case, and it's simpler to have
* the wait-for-class code centralized.
*/
if (found && !unprepOkay && !dvmIsClassLinked((ClassObject*)found)) {
ALOGV("Ignoring not-yet-ready %s, using slow path",
((ClassObject*)found)->descriptor);
found = NULL;
}
return (ClassObject*) found;
}
vm/Hash.cpp
/*
* Look up an entry.
*
* We probe on collisions, wrapping around the table.
*/
void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
HashCompareFunc cmpFunc, bool doAdd)
{
HashEntry* pEntry;
HashEntry* pEnd;
void* result = NULL;
assert(pHashTable->tableSize > 0);
assert(item != HASH_TOMBSTONE);
assert(item != NULL);
/* jump to the first entry and probe for a match */
pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
pEnd = &pHashTable->pEntries[pHashTable->tableSize];
while (pEntry->data != NULL) {
if (pEntry->data != HASH_TOMBSTONE &&
pEntry->hashValue == itemHash &&
(*cmpFunc)(pEntry->data, item) == 0)
{
/* match */
//ALOGD("+++ match on entry %d", pEntry - pHashTable->pEntries);
break;
}
pEntry++;
if (pEntry == pEnd) { /* wrap around to start */
if (pHashTable->tableSize == 1)
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
//ALOGI("+++ look probing %d...", pEntry - pHashTable->pEntries);
}
if (pEntry->data == NULL) {
if (doAdd) {
pEntry->hashValue = itemHash;
pEntry->data = item;
pHashTable->numEntries++;
/*
* We've added an entry. See if this brings us too close to full.
*/
if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
> pHashTable->tableSize * LOAD_NUMER)
{
if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
/* don't really have a way to indicate failure */
ALOGE("Dalvik hash resize failure");
dvmAbort();
}
/* note "pEntry" is now invalid */
} else {
//ALOGW("okay %d/%d/%d",
// pHashTable->numEntries, pHashTable->tableSize,
// (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
}
/* full table is bad -- search for nonexistent never halts */
assert(pHashTable->numEntries < pHashTable->tableSize);
result = item;
} else {
assert(result == NULL);
}
} else {
result = pEntry->data;
}
return result;
}
vm/Intern.cpp
/*
* Prep string interning.
*/
bool dvmStringInternStartup()
{
dvmInitMutex(&gDvm.internLock);
gDvm.internedStrings = dvmHashTableCreate(256, NULL);
if (gDvm.internedStrings == NULL)
return false;
gDvm.literalStrings = dvmHashTableCreate(256, NULL);
if (gDvm.literalStrings == NULL)
return false;
return true;
}
vm/Native.cpp
/*
* Initialize the native code loader.
*/
bool dvmNativeStartup()
{
gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry);
if (gDvm.nativeLibs == NULL)
return false;
return true;
}
vm/native/InternalNative.cpp
/*
* Set up hash values on the class names.
*/
bool dvmInternalNativeStartup()
{
DalvikNativeClass* classPtr = gDvmNativeMethodSet;
while (classPtr->classDescriptor != NULL) {
classPtr->classDescriptorHash =
dvmComputeUtf8Hash(classPtr->classDescriptor);
classPtr++;
}
gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
if (gDvm.userDexFiles == NULL)
return false;
return true;
}
vm/Jni.cpp
bool dvmJniStartup() {
if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize,
kGlobalRefsTableMaxSize,
kIndirectKindGlobal)) {
return false;
}
if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize,
kGlobalRefsTableMaxSize,
kIndirectKindWeakGlobal)) {
return false;
}
dvmInitMutex(&gDvm.jniGlobalRefLock);
dvmInitMutex(&gDvm.jniWeakGlobalRefLock);
gDvm.jniGlobalRefLoMark = 0;
gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) {
return false;
}
dvmInitMutex(&gDvm.jniPinRefLock);
return true;
}
vm/Profile.cpp
/*
* Boot-time init.
*/
bool dvmProfilingStartup()
{
/*
* Initialize "dmtrace" method profiling.
*/
memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
dvmInitMutex(&gDvm.methodTrace.startStopLock);
pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
assert(!dvmCheckException(dvmThreadSelf()));
/*
* Allocate storage for instruction counters.
*/
gDvm.executedInstrCounts = (int*) calloc(kNumPackedOpcodes, sizeof(int));
if (gDvm.executedInstrCounts == NULL)
return false;
#ifdef UPDATE_MAGIC_PAGE
/*
* If we're running on the emulator, there's a magic page into which
* we can put interpreted method information. This allows interpreted
* methods to show up in the emulator's code traces.
*
* We could key this off of the "ro.kernel.qemu" property, but there's
* no real harm in doing this on a real device.
*/
int fd = open("/dev/qemu_trace", O_RDWR);
if (fd < 0) {
ALOGV("Unable to open /dev/qemu_trace,err: %s", strerror(errno));
} else {
gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
if (gDvm.emulatorTracePage == MAP_FAILED) {
ALOGE("Unable to mmap /dev/qemu_trace");
gDvm.emulatorTracePage = NULL;
} else {
*(u4*) gDvm.emulatorTracePage = 0;
}
}
#else
assert(gDvm.emulatorTracePage == NULL);
#endif
return true;
}
vm/analysis/Optimize.cpp
/*
* Create a table of inline substitutions. Sets gDvm.inlineSubs.
*
* TODO: this is currently just a linear array. We will want to put this
* into a hash table as the list size increases.
*/
bool dvmCreateInlineSubsTable()
{
const InlineOperation* ops = dvmGetInlineOpsTable();
const int count = dvmGetInlineOpsTableLength();
InlineSub* table;
int i, tableIndex;
assert(gDvm.inlineSubs == NULL);
/*
* One slot per entry, plus an end-of-list marker.
*/
table = (InlineSub*) calloc(count + 1, sizeof(InlineSub));
tableIndex = 0;
for (i = 0; i < count; i++) {
Method* method = dvmFindInlinableMethod(ops[i].classDescriptor,
ops[i].methodName, ops[i].methodSignature);
if (method == NULL) {
/*
* Not expected. We only use this for key methods in core
* classes, so we should always be able to find them.
*/
ALOGE("Unable to find method for inlining: %s.%s:%s",
ops[i].classDescriptor, ops[i].methodName,
ops[i].methodSignature);
return false;
}
table[tableIndex].method = method;
table[tableIndex].inlineIdx = i;
tableIndex++;
}
/* mark end of table */
table[tableIndex].method = NULL;
gDvm.inlineSubs = table;
return true;
}
vm/reflect/Reflect.cpp
/*
* For some of the reflection stuff we need to un-box primitives, e.g.
* convert a java/lang/Integer to int or even a float. We assume that
* the first instance field holds the value.
*
* To verify this, we either need to ensure that the class has only one
* instance field, or we need to look up the field by name and verify
* that it comes first. The former is simpler, and should work.
*/
bool dvmValidateBoxClasses()
{
static const char* classes[] = {
"Ljava/lang/Boolean;",
"Ljava/lang/Character;",
"Ljava/lang/Float;",
"Ljava/lang/Double;",
"Ljava/lang/Byte;",
"Ljava/lang/Short;",
"Ljava/lang/Integer;",
"Ljava/lang/Long;",
NULL
};
const char** ccp;
for (ccp = classes; *ccp != NULL; ccp++) {
ClassObject* clazz;
clazz = dvmFindClassNoInit(*ccp, NULL);
if (clazz == NULL) {
ALOGE("Couldn't find '%s'", *ccp);
return false;
}
if (clazz->ifieldCount != 1) {
ALOGE("Found %d instance fields in '%s'",
clazz->ifieldCount, *ccp);
return false;
}
}
return true;
}
vm/Thread.cpp
/*
* Finish preparing the parts of the Thread struct required to support
* JNI registration.
*/
bool dvmPrepMainForJni(JNIEnv* pEnv)
{
Thread* self;
/* main thread is always first in list at this point */
self = gDvm.threadList;
assert(self->threadId == kMainThreadId);
/* create a "fake" JNI frame at the top of the main thread interp stack */
if (!createFakeEntryFrame(self))
return false;
/* fill these in, since they weren't ready at dvmCreateJNIEnv time */
dvmSetJniEnvThreadId(pEnv, self);
dvmSetThreadJNIEnv(self, (JNIEnv*) pEnv);
return true;
}
vm/Init.cpp
/*
* Register java.* natives from our class libraries. We need to do
* this after we're ready for JNI registration calls, but before we
* do any class initialization.
*
* If we get this wrong, we will blow up in the ThreadGroup class init if
* interpreted code makes any reference to System. It will likely do this
* since it wants to do some java.io.File setup (e.g. for static in/out/err).
*
* We need to have gDvm.initializing raised here so that JNI FindClass
* won't try to use the system/application class loader.
*/
static bool registerSystemNatives(JNIEnv* pEnv)
{
// Main thread is always first in list.
Thread* self = gDvm.threadList;
// Must set this before allowing JNI-based method registration.
self->status = THREAD_NATIVE;
// Most JNI libraries can just use System.loadLibrary, but you can't
// if you're the library that implements System.loadLibrary!
loadJniLibrary("javacore");
loadJniLibrary("nativehelper");
// Back to run mode.
self->status = THREAD_RUNNING;
return true;
}
vm/Native.cpp
/*
* Load native code from the specified absolute pathname. Per the spec,
* if we've already loaded a library with the specified pathname, we
* return without doing anything.
*
* TODO? for better results we should absolutify the pathname. For fully
* correct results we should stat to get the inode and compare that. The
* existing implementation is fine so long as everybody is using
* System.loadLibrary.
*
* The library will be associated with the specified class loader. The JNI
* spec says we can't load the same library into more than one class loader.
*
* Returns "true" on success. On failure, sets *detail to a
* human-readable description of the error or NULL if no detail is
* available; ownership of the string is transferred to the caller.
*/
bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
char** detail)
{
SharedLib* pEntry;
void* handle;
bool verbose;
/* reduce noise by not chattering about system libraries */
verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
if (verbose)
ALOGD("Trying to load lib %s %p", pathName, classLoader);
*detail = NULL;
/*
* See if we've already loaded it. If we have, and the class loader
* matches, return successfully without doing anything.
*/
pEntry = findSharedLibEntry(pathName);
if (pEntry != NULL) {
if (pEntry->classLoader != classLoader) {
ALOGW("Shared lib '%s' already opened by CL %p; can't open in %p",
pathName, pEntry->classLoader, classLoader);
return false;
}
if (verbose) {
ALOGD("Shared lib '%s' already loaded in same CL %p",
pathName, classLoader);
}
if (!checkOnLoadResult(pEntry))
return false;
return true;
}
/*
* Open the shared library. Because we're using a full path, the system
* doesn't have to search through LD_LIBRARY_PATH. (It may do so to
* resolve this library's dependencies though.)
*
* Failures here are expected when java.library.path has several entries
* and we have to hunt for the lib.
*
* The current version of the dynamic linker prints detailed information
* about dlopen() failures. Some things to check if the message is
* cryptic:
* - make sure the library exists on the device
* - verify that the right path is being opened (the debug log message
* above can help with that)
* - check to see if the library is valid (e.g. not zero bytes long)
* - check config/prelink-linux-arm.map to ensure that the library
* is listed and is not being overrun by the previous entry (if
* loading suddenly stops working on a prelinked library, this is
* a good one to check)
* - write a trivial app that calls sleep() then dlopen(), attach
* to it with "strace -p <pid>" while it sleeps, and watch for
* attempts to open nonexistent dependent shared libs
*
* This can execute slowly for a large library on a busy system, so we
* want to switch from RUNNING to VMWAIT while it executes. This allows
* the GC to ignore us.
*/
Thread* self = dvmThreadSelf();
ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
handle = dlopen(pathName, RTLD_LAZY);
dvmChangeStatus(self, oldStatus);
if (handle == NULL) {
*detail = strdup(dlerror());
return false;
}
/* create a new entry */
SharedLib* pNewEntry;
pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));
pNewEntry->pathName = strdup(pathName);
pNewEntry->handle = handle;
pNewEntry->classLoader = classLoader;
dvmInitMutex(&pNewEntry->onLoadLock);
pthread_cond_init(&pNewEntry->onLoadCond, NULL);
pNewEntry->onLoadThreadId = self->threadId;
/* try to add it to the list */
SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);
if (pNewEntry != pActualEntry) {
ALOGI("WOW: we lost a race to add a shared lib (%s CL=%p)",
pathName, classLoader);
freeSharedLibEntry(pNewEntry);
return checkOnLoadResult(pActualEntry);
} else {
if (verbose)
ALOGD("Added shared lib %s %p", pathName, classLoader);
bool result = true;
void* vonLoad;
int version;
vonLoad = dlsym(handle, "JNI_OnLoad");
if (vonLoad == NULL) {
ALOGD("No JNI_OnLoad found in %s %p, skipping init",
pathName, classLoader);
} else {
/*
* Call JNI_OnLoad. We have to override the current class
* loader, which will always be "null" since the stuff at the
* top of the stack is around Runtime.loadLibrary(). (See
* the comments in the JNI FindClass function.)
*/
OnLoadFunc func = (OnLoadFunc)vonLoad;
Object* prevOverride = self->classLoaderOverride;
self->classLoaderOverride = classLoader;
oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
if (gDvm.verboseJni) {
ALOGI("[Calling JNI_OnLoad for \"%s\"]", pathName);
}
version = (*func)(gDvmJni.jniVm, NULL);
dvmChangeStatus(self, oldStatus);
self->classLoaderOverride = prevOverride;
if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&
version != JNI_VERSION_1_6)
{
ALOGW("JNI_OnLoad returned bad version (%d) in %s %p",
version, pathName, classLoader);
/*
* It's unwise to call dlclose() here, but we can mark it
* as bad and ensure that future load attempts will fail.
*
* We don't know how far JNI_OnLoad got, so there could
* be some partially-initialized stuff accessible through
* newly-registered native method calls. We could try to
* unregister them, but that doesn't seem worthwhile.
*/
result = false;
} else {
if (gDvm.verboseJni) {
ALOGI("[Returned from JNI_OnLoad for \"%s\"]", pathName);
}
}
}
if (result)
pNewEntry->onLoadResult = kOnLoadOkay;
else
pNewEntry->onLoadResult = kOnLoadFailed;
pNewEntry->onLoadThreadId = 0;
/*
* Broadcast a wakeup to anybody sleeping on the condition variable.
*/
dvmLockMutex(&pNewEntry->onLoadLock);
pthread_cond_broadcast(&pNewEntry->onLoadCond);
dvmUnlockMutex(&pNewEntry->onLoadLock);
return result;
}
}
vm/alloc/Alloc.cpp
/*
* Create some "stock" exceptions. These can be thrown when the system is
* too screwed up to allocate and initialize anything, or when we don't
* need a meaningful stack trace.
*
* We can't do this during the initial startup because we need to execute
* the constructors.
*/
bool dvmCreateStockExceptions()
{
/*
* Pre-allocate some throwables. These need to be explicitly added
* to the GC's root set (see dvmHeapMarkRootSet()).
*/
gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
"[memory exhausted]");
dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
"[pre-allocated]");
dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
gDvm.noClassDefFoundErrorObj =
createStockException("Ljava/lang/NoClassDefFoundError;",
"[generic]");
dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
gDvm.noClassDefFoundErrorObj == NULL)
{
ALOGW("Unable to create stock exceptions");
return false;
}
return true;
}
vm/Thread.cpp
/*
* Finish preparing the main thread, allocating some objects to represent
* it. As part of doing so, we finish initializing Thread and ThreadGroup.
* This will execute some interpreted code (e.g. class initializers).
*/
bool dvmPrepMainThread()
{
Thread* thread;
Object* groupObj;
Object* threadObj;
Object* vmThreadObj;
StringObject* threadNameStr;
Method* init;
JValue unused;
ALOGV("+++ finishing prep on main VM thread");
/* main thread is always first in list at this point */
thread = gDvm.threadList;
assert(thread->threadId == kMainThreadId);
/*
* Make sure the classes are initialized. We have to do this before
* we create an instance of them.
*/
if (!dvmInitClass(gDvm.classJavaLangClass)) {
ALOGE("'Class' class failed to initialize");
return false;
}
if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
!dvmInitClass(gDvm.classJavaLangThread) ||
!dvmInitClass(gDvm.classJavaLangVMThread))
{
ALOGE("thread classes failed to initialize");
return false;
}
groupObj = dvmGetMainThreadGroup();
if (groupObj == NULL)
return false;
/*
* Allocate and construct a Thread with the internal-creation
* constructor.
*/
threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
if (threadObj == NULL) {
ALOGE("unable to allocate main thread object");
return false;
}
dvmReleaseTrackedAlloc(threadObj, NULL);
threadNameStr = dvmCreateStringFromCstr("main");
if (threadNameStr == NULL)
return false;
dvmReleaseTrackedAlloc((Object*)threadNameStr, NULL);
init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
"(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
assert(init != NULL);
dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr,
THREAD_NORM_PRIORITY, false);
if (dvmCheckException(thread)) {
ALOGE("exception thrown while constructing main thread object");
return false;
}
/*
* Allocate and construct a VMThread.
*/
vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
if (vmThreadObj == NULL) {
ALOGE("unable to allocate main vmthread object");
return false;
}
dvmReleaseTrackedAlloc(vmThreadObj, NULL);
init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
"(Ljava/lang/Thread;)V");
dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
if (dvmCheckException(thread)) {
ALOGE("exception thrown while constructing main vmthread object");
return false;
}
/* set the VMThread.vmData field to our Thread struct */
assert(gDvm.offJavaLangVMThread_vmData != 0);
dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)thread);
/*
* Stuff the VMThread back into the Thread. From this point on, other
* Threads will see that this Thread is running (at least, they would,
* if there were any).
*/
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
vmThreadObj);
thread->threadObj = threadObj;
/*
* Set the "context class loader" field in the system class loader.
*
* Retrieving the system class loader will cause invocation of
* ClassLoader.getSystemClassLoader(), which could conceivably call
* Thread.currentThread(), so we want the Thread to be fully configured
* before we do this.
*/
Object* systemLoader = dvmGetSystemClassLoader();
if (systemLoader == NULL) {
ALOGW("WARNING: system class loader is NULL (setting main ctxt)");
/* keep going? */
} else {
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_contextClassLoader,
systemLoader);
dvmReleaseTrackedAlloc(systemLoader, NULL);
}
/* include self in non-daemon threads (mainly for AttachCurrentThread) */
gDvm.nonDaemonThreadCount++;
return true;
}
vm/Debugger.cpp
/*
* System init. We don't allocate the registry until first use.
* Make sure we do this before initializing JDWP.
*/
bool dvmDebuggerStartup()
{
if (!dvmBreakpointStartup())
return false;
gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
return (gDvm.dbgRegistry != NULL);
}
vm/alloc/Alloc.cpp
bool dvmGcStartupClasses()
{
ClassObject *klass = dvmFindSystemClass("Ljava/lang/Daemons;");
if (klass == NULL) {
return false;
}
Method *method = dvmFindDirectMethodByDescriptor(klass, "start", "()V");
if (method == NULL) {
return false;
}
Thread *self = dvmThreadSelf();
assert(self != NULL);
JValue unusedResult;
dvmCallMethod(self, method, NULL, &unusedResult);
return true;
}
vm/Init.cpp
/*
* Do zygote-mode-only initialization.
*/
static bool initZygote()
{
/* zygote goes into its own process group */
setpgid(0,0);
return true;
}
vm/Init.cpp
/*
* Do non-zygote-mode initialization. This is done during VM init for
* standard startup, or after a "zygote fork" when creating a new process.
*/
bool dvmInitAfterZygote()
{
u8 startHeap, startQuit, startJdwp;
u8 endHeap, endQuit, endJdwp;
startHeap = dvmGetRelativeTimeUsec();
/*
* Post-zygote heap initialization, including starting
* the HeapWorker thread.
*/
if (!dvmGcStartupAfterZygote())
return false;
endHeap = dvmGetRelativeTimeUsec();
startQuit = dvmGetRelativeTimeUsec();
/* start signal catcher thread that dumps stacks on SIGQUIT */
if (!gDvm.reduceSignals && !gDvm.noQuitHandler) {
if (!dvmSignalCatcherStartup())
return false;
}
/* start stdout/stderr copier, if requested */
if (gDvm.logStdio) {
if (!dvmStdioConverterStartup())
return false;
}
endQuit = dvmGetRelativeTimeUsec();
startJdwp = dvmGetRelativeTimeUsec();
/*
* Start JDWP thread. If the command-line debugger flags specified
* "suspend=y", this will pause the VM. We probably want this to
* come last.
*/
if (!initJdwp()) {
ALOGD("JDWP init failed; continuing anyway");
}
endJdwp = dvmGetRelativeTimeUsec();
ALOGV("thread-start heap=%d quit=%d jdwp=%d total=%d usec",
(int)(endHeap-startHeap), (int)(endQuit-startQuit),
(int)(endJdwp-startJdwp), (int)(endJdwp-startHeap));
#ifdef WITH_JIT
if (gDvm.executionMode == kExecutionModeJit) {
if (!dvmCompilerStartup())
return false;
}
#endif
return true;
}
看一下 com.android.internal.os.ZygoteInit.java的main
Java代码
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
gc();
// If requested, start system server directly from Zygote
if (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
if (argv[1].equals("start-system-server")) {
startSystemServer();
} else if (!argv[1].equals("")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
runSelectLoopMode();
}
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}