jdwp(java debug wire protocol)是dalvik VM的一個線程,可以建立在adb或者tcp基礎上,與DDMS或debugger進行通信。
代碼位置:
dalvik/vm/jdwp
frameworks/base/core/jni
java虛擬機初始化後,或者每次“zygote fork”出一個新進程時,會啟動jdwp線程。
主要調用路徑:dvmStartup->dvmInitAfterZygote->dvmInitJDWP。
dvmInitJDWP線程啟動之前會可能會阻塞VM,依賴於配置suspend=n,所以dvmInitJDWP放在dvmStartup的最後步驟來執行。
dvmInitJDWP首先判斷jdwp是否允許並且已經配置好,如果是,則讀取jdwp的配置,這些配置是AndroidRuntime::startVm中配置的。
C++代碼
- /* enable debugging; set suspend=y to pause during VM init */
- #ifdef HAVE_ANDROID_OS
- /* use android ADB transport */
- opt.optionString =
- "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
- #else
- /* use TCP socket; address=0 means start at port 8000 and probe up */
- LOGI("Using TCP socket for JDWP\n");
- opt.optionString =
- "-agentlib:jdwp=transport=dt_socket,suspend=n,server=y,address=0";
- #endif
這些配置保存在虛擬機全局變量gDvm中,gDvm是DvmGlobals變量,需要讀取的配置包括transport,負責與ADB或TCP交換數據。
C++代碼
- if (gDvm.jdwpAllowed && gDvm.jdwpConfigured) {
- JdwpStartupParams params;
-
-
- if (gDvm.jdwpHost != NULL) {
- if (strlen(gDvm.jdwpHost) >= sizeof(params.host)-1) {
- LOGE("ERROR: hostname too long: '%s'\n", gDvm.jdwpHost);
- return false;
- }
- strcpy(params.host, gDvm.jdwpHost);
- } else {
- params.host[0] = '\0';
- }
- params.transport = gDvm.jdwpTransport;
- params.server = gDvm.jdwpServer;
- params.suspend = gDvm.jdwpSuspend;
- params.port = gDvm.jdwpPort;
-
-
- gDvm.jdwpState = dvmJdwpStartup(¶ms);
- if (gDvm.jdwpState == NULL) {
- LOGW("WARNING: debugger thread failed to initialize\n");
- /* TODO: ignore? fail? need to mimic "expected" behavior */
- }
- }
其中gDvm.jdwpAllowed在dalvik_system_Zygote.c中配置:
gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
gDvm.jdwpConfigured在調用dvmStartup->dvmProcessOptions->parseJdwpOptions時配置。
參考上面的參數,對照Init.c的handleJdwpOption的函數,可知gDvm.jdwpTransport為kJdwpTransportAndroidAdb,gDvm.jdwpServer為true,gDvm.jdwpSuspend為false,gDvm.jdwpPort沒有配置,這些參數保存到dvmJdwpStartup的參數裡面。
再看dvmJdwpStartup,在裡面,創建jdwp相關的結構體JdwpState,是先初始化一些互斥鎖和條件鎖,初始化transport。
C++代碼
- switch (pParams->transport) {
- case kJdwpTransportSocket:
- // LOGD("prepping for JDWP over TCP\n");
- state->transport = dvmJdwpSocketTransport();
- break;
- case kJdwpTransportAndroidAdb:
- // LOGD("prepping for JDWP over ADB\n");
- state->transport = dvmJdwpAndroidAdbTransport();
- /* TODO */
- break;
- default:
- LOGE("Unknown transport %d\n", pParams->transport);
- assert(false);
- goto fail;
- }
由上文可知,這裡執行dvmJdwpAndroidAdbTransport,返回一個JdwpTransport的接口指針結構體,這些在ADB和TCP上各有一套實現方法,對應不同類型的transport。
C++代碼
- typedef struct JdwpTransport {
- bool (*startup)(struct JdwpState* state, const JdwpStartupParams* pParams);
- bool (*accept)(struct JdwpState* state);
- bool (*establish)(struct JdwpState* state);
- void (*close)(struct JdwpState* state);
- void (*shutdown)(struct JdwpState* state);
- void (*free)(struct JdwpState* state);
- bool (*isConnected)(struct JdwpState* state);
- bool (*awaitingHandshake)(struct JdwpState* state);
- bool (*processIncoming)(struct JdwpState* state);
- bool (*sendRequest)(struct JdwpState* state, ExpandBuf* pReq);
- bool (*sendBufferedRequest)(struct JdwpState* state,
- const struct iovec* iov, int iovcnt);
- } JdwpTransport;
然後,調用dvmJdwpNetStartup,在裡面實際執行JdwpTransport在ADB上的startup接口。在JdwpADB.c的start函數內,初始化與adb有關的一些網絡參數,比如socket名稱。
C++代碼
- #define kJdwpControlName "\0jdwp-control"
這個socket名稱已經被adbd綁定。
然後,調用dvmCreateInternalThread啟動jdwp線程,dvmCreateInternalThread是pthread_create的包裝,最終線程的執行函數是jdwpThreadStart。
在jdwpThreadStart中,會調用dvmJdwpEstablishConnection與adbd建立連接。在dvmJdwpEstablishConnection中,會一直等待與adbd的連接,直到連接成功。
C++代碼
- int ret = connect(netState->controlSock,
- &netState->controlAddr.controlAddrPlain,
- netState->controlAddrLen);
如果執行connect成功,則將pid發送給adbd。
C++代碼
- snprintf(buff, sizeof(buff), "%04x", getpid());
- buff[4] = 0;
- do {
- ret = send( netState->controlSock, buff, 4, 0 );
- } while (ret < 0 && errno == EINTR);
接著,jdwp等待adbd返回一個客戶文件描述符。
C++代碼
- netState->clientSock = receiveClientFd(netState);
如果返回成功,這個clientSock將用來直接與debugger或DDMS通信。可以想象,這裡的clientSock就是TCP:5037對應的client描述符。
這樣acceptConnection也成功返回了,回到jdwp線程處理函數jdwpThreadStart,接著進入一個while循環從adbd讀取並處理握手消息。
C++代碼
- while (true) {
- // sanity check -- shouldn't happen?
- if (dvmThreadSelf()->status != THREAD_VMWAIT) {
- LOGE("JDWP thread no longer in VMWAIT (now %d); resetting\n",
- dvmThreadSelf()->status);
- dvmDbgThreadWaiting();
- }
-
-
- if (!dvmJdwpProcessIncoming(state)) /* blocking read */
- break;
-
-
- if (first && !dvmJdwpAwaitingHandshake(state)) {
- /* handshake worked, tell the interpreter that we're active */
- first = false;
-
-
- /* set thread ID; requires object registry to be active */
- state->debugThreadId = dvmDbgGetThreadSelfId();
-
-
- /* wake anybody who's waiting for us */
- dvmDbgLockMutex(&state->attachLock);
- dvmDbgCondBroadcast(&state->attachCond);
- dvmDbgUnlockMutex(&state->attachLock);
- }
- }
先看dvmJdwpProcessIncoming函數,在裡面執行select,可能會收到三種數據,對應三個文件描述被set,其中wakeFds是定時喚醒作用,丟棄,controlSock的set也忽略,因為不需要接收第二個debugger文件描述服。
之後收到debugger的數據,也就是clientSock被set的時候調用read讀取數據,如果不是一個單位數據包長度,則dvmJdwpProcessIncoming返回。否則,調用handlePacket處理數據包:handlePacket->dvmJdwpProcessRequest->write。handlePacket將包中的數據還原成JdwpReqHeader和數據起始指針,送給dvmJdwpProcessRequest處理,dvmJdwpProcessRequest從gHandlerMap調出處理函數func。
C++代碼
- typedef struct {
- u1 cmdSet;
- u1 cmd;
- JdwpRequestHandler func;
- const char* descr;
- } JdwpHandlerMap;
write將結果寫回給adbd,adbd處理之後在發回給HOST端。