Upload
kishima7
View
5.014
Download
1
Tags:
Embed Size (px)
DESCRIPTION
横浜AndroidPF部で発表しようとして、できなかったときの資料です。 http://silentworlds.info/pukiwiki/?%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0
Citation preview
Dalvikのソースコードを読んで分かったこと(1)
@kishimahttp://silentworlds.info/
2011/6/5 横浜AndroidPF部勉強会(で発表する予定だったもの)
12011年6月12日日曜日
今日話すこと(★がついているもの)
★ソースツリー構成★Zygoteの起動 ・プレロード<宿題★アプリの起動 ★ソケット通信 ・fork<宿題★VMの起動 ★dexの読み込み ★ dexopt の入り口まで★インタプリタの基本的な動き
22011年6月12日日曜日
ソース構成dalvik/ dalvikvm/ DalvikVMのmain()が入っている。 dexdump/ dexファイルの解析 dexlist/ ? dexopt/ dexの最適化 dexロード時に行われる docs/ ドキュメント dvz/ zygoteの制御を行うツール dx/ バイトコードをdexへ変換 hit/ ? libdex/ dex操作関連の処理(vmからコール)
libnativehelper/ ? tests/ テスト tools/ ツール類 vm/ VMの本体
frameworks/base/cmds/app_process/ が参照 Zygote(system-server)プロセスのエントリポイント
system/core/libcutils/zygote.c を参照 zygote操作用のちょっとした関数が入っている
32011年6月12日日曜日
ソース構成2dalvik/ vm/ alloc/ メモリ周り(確保、解放、GC) analysis/ 解析 arch/ アーキテクチャ依存 compiler/ JITコンパイラ hprof/ ? interp/ インタプリタ(実体はmterpにある) jdwp/ ? mterp/ インタプリタの実体 (アーキテクチャ向けに最適化されたアセンブラコードを含む) native/ JNIで呼ばれる関数 oo/ ? reflect/ ? test/ テスト VMの基本的な機能たち
42011年6月12日日曜日
Zygote起動1
init.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
52011年6月12日日曜日
Zygote起動2
int main(int argc, const char* const argv[]){--- // Next arg is startup classname or "--zygote" if (i < argc) { arg = argv[i++]; if (0 == strcmp("--zygote", arg)) { bool startSystemServer = (i < argc) ? strcmp(argv[i], "--start-system-server") == 0 : false; setArgv0(argv0, "zygote"); set_process_name("zygote"); runtime.start("com.android.internal.os.ZygoteInit", startSystemServer);---
frameworks/base/cmds/app_process/app_main.cpp
initから起動されたZygoteプロセスの入り口
起動処理へ
62011年6月12日日曜日
frameworks/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". */void AndroidRuntime::start(const char* className, const bool startSystemServer){--- /* start the virtual machine */ if (startVm(&mJavaVM, &env) != 0)--- /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */--- startClass = env->FindClass(slashClassName);--- startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");--- env->CallStaticVoidMethod(startClass, startMeth, strArray);---}
“com.android.internal.os.ZygoteInit”
Zygote起動3
VMの起動処理
JNIでJava側のmainメソッドを呼び出す
72011年6月12日日曜日
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) { try { VMRuntime.getRuntime().setMinimumHeapSize(5 * 1024 * 1024);
// Start profiling the zygote initialization. SamplingProfilerIntegration.start();
registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preloadClasses(); preloadResources(); 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("true")) { startSystemServer(); } else if (!argv[1].equals("false")) { 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; } }
AndroidRuntime.cppからJNIで呼ばれる
PreLoad
ソケットを作る
Fork?
Zygote起動4
82011年6月12日日曜日
アプリ起動1dvzのソースを参考にしてみる
system/core/libcutils/zygote.c
int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int)){ fd = socket_local_client(ZYGOTE_SOCKET, ANDROID_SOCKET_NAMESPACE_RESERVED, AF_LOCAL);--- // The command socket is passed to the peer as close-on-exec // and will close when the peer dies newargv[0] = "--peer-wait"; memcpy(newargv + 1, argv, argc * sizeof(*argv));
pid = send_request(fd, 1, argc + 1, newargv);---}
dalvik/dvzint main (int argc, const char **argv) {--- err = zygote_run_wait(argc - 1, argv + 1, post_run_func);
/dec/socket/zygoteのローカルソケットを開くこのソケットを介してzygoteプロセスと通信
次のページ
92011年6月12日日曜日
アプリ起動2static int send_request(int fd, int sendStdio, int argc, const char **argv){--- struct msghdr msg;--- ret = sendmsg(fd, &msg, MSG_NOSIGNAL);--- // replace any newlines with spaces and send the args For (i = 0; i < argc; i++) {--- toprint = argv[i];--- ivs[0].iov_base = (char *)toprint; ivs[0].iov_len = strlen(toprint); ivs[1].iov_base = (char *)newline_string; //中身は”¥n”
ivs[1].iov_len = 1;
msg.msg_iovlen = 2;
do { ret = sendmsg(fd, &msg, MSG_NOSIGNAL); } while (ret < 0 && errno == EINTR);--- // Read the pid, as a 4-byte network-order integer ivs[0].iov_base = &pid;--- ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL);
sendmsgでメッセージを送ってる1.stdioディスクリプタとかを送信2.argvの文字列を送信3.PIDが応答で返ってくるのを待つ (新しくforkされたプロセスのPID
と思われる)
system/core/libcutils/zygote.c
102011年6月12日日曜日
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv){--- 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--- /* * 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) {---}
frameworks/base/core/jni/AndroidRuntime.cpp
JIT使用の設定他にも色々オプションを設定しているヒープサイズ設定とか。
VMのインスタンス生成
VMの起動1
112011年6月12日日曜日
dalvik/vm/Jni.cjint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args){ /* * Set up structures for JNIEnv and VM. */ pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
memset(pVM, 0, sizeof(JavaVMExt)); pVM->funcTable = &gInvokeInterface; pVM->envList = pEnv;
--- /* set this up before initializing VM, so it can create some JNIEnvs */ gDvm.vmList = (JavaVM*) pVM;
/* * Create an env for 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. */ pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);--- /* initialize VM */ gDvm.initializing = true; if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) { --- }--- *p_env = (JNIEnv*) pEnv; *p_vm = (JavaVM*) pVM;---}
VMのインスタンス生成
VMがリストで管理されてる※複数も想定してる?現在は想定してないとコメントがどこかにあった
初期化でJNI使うので準備する
VMの起動2
122011年6月12日日曜日
VMの構造体
JavaVMlibnativehelper/include/nativehelper/jni.hstruct JNIInvokeInterface{ jint (*DestroyJavaVM)(JavaVM*); jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*); jint (*DetachCurrentThread)(JavaVM*); jint (*GetEnv)(JavaVM*, void**, jint); jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);}
JNIEnvlibnativehelper/include/nativehelper/jni.h struct JNINativeInterface{} VMへのアクセス関数が大量に
VMの本体
JNIのための関数ポインタが大量に登録されてる
132011年6月12日日曜日
VMの構造体2struct JavaVMExt;
typedef struct JNIEnvExt { const struct JNINativeInterface* funcTable; /* must be first */
const struct JNINativeInterface* baseFuncTable;
/* pointer to the VM we are a part of */ struct JavaVMExt* vm;
u4 envThreadId; Thread* self;
/* if nonzero, we are in a "critical" JNI call */ int critical;
/* keep a copy of this here for speed */ bool forceDataCopy;
struct JNIEnvExt* prev; struct JNIEnvExt* next;} JNIEnvExt;
typedef struct JavaVMExt { const struct JNIInvokeInterface* funcTable; /* must be first */
const struct JNIInvokeInterface* baseFuncTable;
/* if multiple VMs are desired, add doubly-linked list stuff here */
/* per-VM feature flags */ bool useChecked; bool warnError; bool forceDataCopy;
/* head of list of JNIEnvs associated with this VM */ JNIEnvExt* envList; pthread_mutex_t envListLock;} JavaVMExt;
142011年6月12日日曜日
dex読み込み1
/* * Open a Jar file. It's okay if it's just a Zip archive without all of * the Jar trimmings, but we do insist on finding "classes.dex" inside * or an appropriately-named ".odex" file alongside. * * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as * being part of a different class loader. */int dvmJarFileOpen(const char* fileName, const char* odexOutputName, JarFile** ppJarFile, bool isBootstrap){--- /* First, look for a ".odex" alongside the jar file. It will * have the same name/path except for the extension. */ fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);--- /* * Pre-created .odex absent or stale. Look inside the jar for a * "classes.dex". */ entry = dexZipFindEntry(&archive, kDexInJarName);
dalvik/vm/JarFile.c
classes.dexまたは.odexのファイルを開こうとする
まずは.odexを開こうとする.odexが開けたら、その先頭ポインタを返却して終了。
もしなかったらclasses.dexを開く
152011年6月12日日曜日
/* * We've found the one we want. See if there's an up-to-date copy * in the cache. * * On return, "fd" will be seeked just past the "opt" header. * * If a stale .odex file is present and classes.dex exists in * the archive, this will *not* return an fd pointing to the * .odex file; the fd will point into dalvik-cache like any * other jar. */ if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName);--- /* * If fd points to a new file (because there was no cached version, * or the cached version was stale), generate the optimized DEX. * The file descriptor returned is still locked, and is positioned * just past the optimization header. */ if (newFile) {--- result = dvmOptimizeDexFile(fd, dexOffset, dexGetZipEntryUncompLen(&archive, entry), fileName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap);---
dex読み込み2classes.dexが見つかると、キャッシュから最新を探す。
(戻るときには、fdはoptヘッダを超えた位置を指している)
(古くなったodexがあり、かつclasses.dexがある場合は、fdはodexを指さず、他のjarのようにキャッシュを指す)
キャッシュされたものが無い、もしくは古くなってる場合、最適化されたDEXを生成する。(これがodex?)
odex生成。後述。
dalvik/vm/JarFile.c
162011年6月12日日曜日
--- /* * Map the cached version. This immediately rewinds the fd, so it * doesn't have to be seeked anywhere in particular. */ if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {---
*ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); (*ppJarFile)->archive = archive; (*ppJarFile)->cacheFileName = cachedName; (*ppJarFile)->pDvmDex = pDvmDex; ---(読み込み完了)
}
dex読み込み3
キャッシュされたものをmmapする。seekする必要は特にない
dalvik/libdex/SysUtil.c
fdの現在位置から終端までを書き込み可能な読み込み専用領域(writable read-only)として 、pDvmDexにmmapしてるメモリ節約の意図がある
mmapのオプション: prot=PROT_READ | PROT_WRITE
flags=MAP_FILE | MAP_PRIVATE (copy-on-write)
mprotectのオプション: PROT_READ
dalvik/DvmDex.c dvmDexFileOpenFromFd(){ ・ファイル展開 ・ヘッダを読み飛ばす ・チェックサムの確認 ・SHA-1署名のチェック(最初だけ) ・クラスやメソッドのリファレンス領域を確保}
dalvik/vm/JarFile.c
172011年6月12日日曜日
dexの最適化1/* * Given a descriptor for a file with DEX data in it, produce an * optimized version. * * The file pointed to by "fd" is expected to be a locked shared resource * (or private); we make no efforts to enforce multi-process correctness * here. * * "fileName" is only used for debug output. "modWhen" and "crc" are stored * in the dependency set. * * The "isBootstrap" flag determines how the optimizer and verifier handle * package-scope access checks. When optimizing, we only load the bootstrap * class DEX files and the target DEX, so the flag determines whether the * target DEX classes are given a (synthetic) non-NULL classLoader pointer. * This only really matters if the target DEX contains classes that claim to * be in the same package as bootstrap classes. * * The optimizer will need to load every class in the target DEX file. * This is generally undesirable, so we start a subprocess to do the * work and wait for it to complete. * * Returns "true" on success. All data will have been written to "fd". */bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
dalvik/vm/analysis/DexPrepare.cfdはロックされた共有メモリなんでマルチプロセスのことは気にしなくてOK。
最適化するために、dexファイルの全てのクラスを読み込む。それはあんまり好ましくないので、作業のためにサブプロセスを起こし、完了するまで待つことにする。完了した時点で、全てのデータはfd
に書き込まれている。
※なんでだろう?
bootstrapフラグ ※あんまり理解できてません
182011年6月12日日曜日
dexの最適化2
bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap){--- pid = fork(); if (pid == 0) { static const int kUseValgrind = 0; static const char* kDexOptBin = "/bin/dexopt"; static const char* kValgrinder = "/usr/bin/valgrind";--- strcpy(execFile, androidRoot); strcat(execFile, kDexOptBin);--- if (kUseValgrind) execv(kValgrinder, argv); else execv(execFile, argv);--- } else {--- /* * Wait for the optimization process to finish. We go into VMWAIT * mode here so GC suspension won't have to wait for us. */ oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);--- }}
dalvik/vm/analysis/DexPrepare.c
引数の準備をして、dexoptを、execv()する詳しくはdexoptにて
処理が終わるまで待つVMをVMWAITモードにする。※GCを動かせるようにしておくって意味?
※forkされた側
※forkした側
192011年6月12日日曜日
インタプリタの基本的な動き1(ソース構成から)dalvik/ vm/ mterp/ インタプリタの実体 gen-mterp.py ソースコード生成スクリプト config-xxx アーキテクチャxxxの設定 config-yyy アーキテクチャyyyの設定 ・・・ config-portstd アーキテクチャ非依存の設定ファイル rebuild.sh ビルドスクリプト xxx/ アーキテクチャxxxのコード(xxxのアセンブリで書かれている) yyy/ アーキテクチャyyyのコード(yyyのアセンブリで書かれている) c/ アーキテクチャ非依存のコード(Cで書かれている) out/ 生成されたソースコードの出力先 ・・・
まずは、outに出力されたコードを読んでみます
各アーキテクチャ向けに数百ある大量のOPコード実装を効率よく管理するために、スクリプトによるソースコードの半自動生成を行っている
202011年6月12日日曜日
インタプリタの基本的な動き2dalvik/vm/mterp/out/InterpC-portstd.c
/* File: portable/entry.c */ * Main interpreter loop.bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState){--- /* copy state in */ curMethod = interpState->method; pc = interpState->pc; fp = interpState->fp;--- methodClassDex = curMethod->clazz->pDvmDex;--- while (1) {--- /* fetch the next 16 bits from the instruction stream */ inst = FETCH(0);
switch (INST_INST(inst)) {---/* File: c/OP_NOP.c */HANDLE_OPCODE(OP_NOP) FINISH(1);OP_END---・・・---}
インタプリタの実体(汎用C版の場合)ところどころマクロ魔術が使われているので注意
#define FETCH(_offset) (pc[(_offset)])#define INST_INST(_inst) ((_inst) & 0xff)# define HANDLE_OPCODE(_op) case _op:# define ADJUST_PC(_offset) do { \ pc += _offset; \ EXPORT_EXTRA_PC(); \ } while (false)# define FINISH(_offset) { ADJUST_PC(_offset); break; }
1OPコード:1ファイルで実装されたソースがここに展開されて並ぶ1つのOPコードが1つのcaseに相当する
マクロを駆使してでサブルーチン的な実装を行っていたりする(メソッド呼び出しとか。JNI調査の入り口)
PCをいくつ進めるかを示している
PC(プログラムカウンタ)の位置を起点に、命令(instruction)
を取り出す
例外とか起きるまで無限ループ
212011年6月12日日曜日
(欲張りな) 今後調べること
・プレロードのあたり・Zygoteの実際にforkしているあたり・dexoptの中身・VM起動->fork->dex読み込み->インタプリタで実行 の流れ・インタプリタのもっと細かいところ・JNI
・JIT成果は↓でまとめていきます
http://silentworlds.info/pukiwiki/
222011年6月12日日曜日