jdwp_service.c

来自「Android 一些工具」· C语言 代码 · 共 710 行 · 第 1/2 页

C
710
字号
/* implement the "debug-ports" and "track-debug-ports" device services */#include "sysdeps.h"#define  TRACE_TAG   TRACE_JDWP#include "adb.h"#include <errno.h>#include <stdio.h>#include <string.h>/* here's how these things work.   when adbd starts, it creates a unix server socket   named @vm-debug-control (@ is a shortcut for "first byte is zero"   to use the private namespace instead of the file system)   when a new JDWP daemon thread starts in a new VM process, it creates   a connection to @vm-debug-control to announce its availability.     JDWP thread                             @vm-debug-control         |                                         |         |------------------------------->         |         | hello I'm in process <pid>              |         |                                         |         |                                         |    the connection is kept alive. it will be closed automatically if    the JDWP process terminates (this allows adbd to detect dead    processes).    adbd thus maintains a list of "active" JDWP processes. it can send    its content to clients through the "device:debug-ports" service,    or even updates through the "device:track-debug-ports" service.    when a debugger wants to connect, it simply runs the command    equivalent to  "adb forward tcp:<hostport> jdwp:<pid>"    "jdwp:<pid>" is a new forward destination format used to target    a given JDWP process on the device. when sutch a request arrives,    adbd does the following:      - first, it calls socketpair() to create a pair of equivalent        sockets.      - it attaches the first socket in the pair to a local socket        which is itself attached to the transport's remote socket:      - it sends the file descriptor of the second socket directly        to the JDWP process with the help of sendmsg()     JDWP thread                             @vm-debug-control         |                                         |         |                  <----------------------|         |           OK, try this file descriptor  |         |                                         |         |                                         |   then, the JDWP thread uses this new socket descriptor as its   pass-through connection to the debugger (and receives the   JDWP-Handshake message, answers to it, etc...)   this gives the following graphics:                    ____________________________________                   |                                    |                   |          ADB Server (host)         |                   |                                    |        Debugger <---> LocalSocket <----> RemoteSocket  |                   |                           ^^       |                   |___________________________||_______|                                               ||                                     Transport ||           (TCP for emulator - USB for device) ||                                               ||                    ___________________________||_______                   |                           ||       |                   |          ADBD  (device)   ||       |                   |                           VV       |         JDWP <======> LocalSocket <----> RemoteSocket  |                   |                                    |                   |____________________________________|    due to the way adb works, this doesn't need a special socket    type or fancy handling of socket termination if either the debugger    or the JDWP process closes the connection.    THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN    TO HAVE A BETTER IDEA, LET ME KNOW - Digit**********************************************************************//** JDWP PID List Support Code ** for each JDWP process, we record its pid and its connected socket **/#define  MAX_OUT_FDS   4#if !ADB_HOST#include <sys/socket.h>#include <sys/un.h>typedef struct JdwpProcess  JdwpProcess;struct JdwpProcess {    JdwpProcess*  next;    JdwpProcess*  prev;    int           pid;    int           socket;    fdevent*      fde;    char          in_buff[4];  /* input character to read PID */    int           in_len;      /* number from JDWP process    */    int           out_fds[MAX_OUT_FDS]; /* output array of file descriptors */    int           out_count;            /* to send to the JDWP process      */};static JdwpProcess  _jdwp_list;static intjdwp_process_list( char*  buffer, int  bufferlen ){    char*         end  = buffer + bufferlen;    char*         p    = buffer;    JdwpProcess*  proc = _jdwp_list.next;    for ( ; proc != &_jdwp_list; proc = proc->next ) {        int  len;        /* skip transient connections */        if (proc->pid < 0)            continue;        len = snprintf(p, end-p, "%d\n", proc->pid);        if (p + len >= end)            break;        p += len;    }    p[0] = 0;    return (p - buffer);}static intjdwp_process_list_msg( char*  buffer, int  bufferlen ){    char  head[5];    int   len = jdwp_process_list( buffer+4, bufferlen-4 );    snprintf(head, sizeof head, "%04x", len);    memcpy(buffer, head, 4);    return len + 4;}static void  jdwp_process_list_updated(void);static voidjdwp_process_free( JdwpProcess*  proc ){    if (proc) {        int  n;        proc->prev->next = proc->next;        proc->next->prev = proc->prev;        if (proc->socket >= 0) {            shutdown(proc->socket, SHUT_RDWR);            adb_close(proc->socket);            proc->socket = -1;        }        if (proc->fde != NULL) {            fdevent_destroy(proc->fde);            proc->fde = NULL;        }        proc->pid = -1;        for (n = 0; n < proc->out_count; n++) {            adb_close(proc->out_fds[n]);        }        proc->out_count = 0;        free(proc);        jdwp_process_list_updated();    }}static void  jdwp_process_event(int, unsigned, void*);  /* forward */static JdwpProcess*jdwp_process_alloc( int  socket ){    JdwpProcess*  proc = calloc(1,sizeof(*proc));    if (proc == NULL) {        D("not enough memory to create new JDWP process\n");        return NULL;    }    proc->socket = socket;    proc->pid    = -1;    proc->next   = proc;    proc->prev   = proc;    proc->fde = fdevent_create( socket, jdwp_process_event, proc );    if (proc->fde == NULL) {        D("could not create fdevent for new JDWP process\n" );        free(proc);        return NULL;    }    proc->fde->state |= FDE_DONT_CLOSE;    proc->in_len      = 0;    proc->out_count   = 0;    /* append to list */    proc->next = &_jdwp_list;    proc->prev = proc->next->prev;    proc->prev->next = proc;    proc->next->prev = proc;    /* start by waiting for the PID */    fdevent_add(proc->fde, FDE_READ);    return proc;}static voidjdwp_process_event( int  socket, unsigned  events, void*  _proc ){    JdwpProcess*  proc = _proc;    if (events & FDE_READ) {        if (proc->pid < 0) {            /* read the PID as a 4-hexchar string */            char*  p    = proc->in_buff + proc->in_len;            int    size = 4 - proc->in_len;            char   temp[5];            while (size > 0) {                int  len = recv( socket, p, size, 0 );                if (len < 0) {                    if (errno == EINTR)                        continue;                    if (errno == EAGAIN)                        return;                    /* this can fail here if the JDWP process crashes very fast */                    D("weird unknown JDWP process failure: %s\n",                      strerror(errno));                    goto CloseProcess;                }                if (len == 0) {  /* end of stream ? */                    D("weird end-of-stream from unknown JDWP process\n");                    goto CloseProcess;                }                p            += len;                proc->in_len += len;                size         -= len;            }            /* we have read 4 characters, now decode the pid */            memcpy(temp, proc->in_buff, 4);            temp[4] = 0;            if (sscanf( temp, "%04x", &proc->pid ) != 1) {                D("could not decode JDWP %p PID number: '%s'\n", proc, temp);                goto CloseProcess;            }            /* all is well, keep reading to detect connection closure */            D("Adding pid %d to jdwp process list\n", proc->pid);            jdwp_process_list_updated();        }        else        {            /* the pid was read, if we get there it's probably because the connection             * was closed (e.g. the JDWP process exited or crashed) */            char  buf[32];            for (;;) {                int  len = recv(socket, buf, sizeof(buf), 0);                if (len <= 0) {                    if (len < 0 && errno == EINTR)                        continue;		    if (len < 0 && errno == EAGAIN)                        return;                    else {                        D("terminating JDWP %d connection: %s\n", proc->pid,                          strerror(errno));                        break;                    }                }		else {                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",                       proc->pid, len );                }            }        CloseProcess:            if (proc->pid >= 0)                D( "remove pid %d to jdwp process list\n", proc->pid );            jdwp_process_free(proc);            return;        }    }    if (events & FDE_WRITE) {        D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",          proc->pid, proc->out_count, proc->out_fds[0]);        if (proc->out_count > 0) {            int  fd = proc->out_fds[0];            int  n, ret;            struct cmsghdr*  cmsg;            struct msghdr    msg;            struct iovec     iov;            char             dummy = '!';            char             buffer[sizeof(struct cmsghdr) + sizeof(int)];            iov.iov_base       = &dummy;            iov.iov_len        = 1;            msg.msg_name       = NULL;            msg.msg_namelen    = 0;            msg.msg_iov        = &iov;            msg.msg_iovlen     = 1;            msg.msg_flags      = 0;            msg.msg_control    = buffer;            msg.msg_controllen = sizeof(buffer);            cmsg = CMSG_FIRSTHDR(&msg);            cmsg->cmsg_len   = msg.msg_controllen;            cmsg->cmsg_level = SOL_SOCKET;            cmsg->cmsg_type  = SCM_RIGHTS;            ((int*)CMSG_DATA(cmsg))[0] = fd;            for (;;) {                ret = sendmsg(proc->socket, &msg, 0);                if (ret >= 0)                    break;                if (errno == EINTR)                    continue;                D("sending new file descriptor to JDWP %d failed: %s\n",                  proc->pid, strerror(errno));                goto CloseProcess;            }            D("sent file descriptor %d to JDWP process %d\n",              fd, proc->pid);            for (n = 1; n < proc->out_count; n++)                proc->out_fds[n-1] = proc->out_fds[n];

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?