Android debuggerd 源码分析

2024-02-05 10:32

本文主要是介绍Android debuggerd 源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/yangwen123/article/details/38397359


目录(?)[+]

debuggerd 简介

Android系统自带一个实用的程序异常退出的诊断daemon debuggerd。此进程可以侦测到程序崩溃,并将崩溃时的进程状态信息输出到文件和串口中,以供开发人员分析调试使用。Debuggerd的数据被保存在/data/tombstone/目录下,共可保存10个文件,当超过10个时,会覆盖重写最早生产的文件。串口中,则直接用DEBUG的tag,输出logcat信息。 Linux kernel有自己的一套signal机制,在应用程序崩溃时,通常系统内核都会发送signal到出问题的进程,以通知进程出现什么异常,这些进程可以捕获这些signal并对其做相应的处理。通常对于程序异常信号的处理,就是退出。Android在此机制上实现了一个更实用的功能:拦截这些信号,dump进程信息以供调试。

debuggerd的运行原理

debuggerd创建一个名为 “Android:debuggerd”的socket,作为server端等待其他client端进程的连接,接收client端进程发送来的tid和action信息将由tid指定的那个进程的运行信息,按照由action指定的动作dump到文件或者控制台中可以作为debuggerd的client端的进程主要有几种:

1.       异常的C/C++程序

这种程序由bionic的linker安装异常信号的处理函数,当程序产生异常信号时,进入信号处理函数,与debuggerd建立。

2.       debuggerd程序

debuggerd可以在控制台中以命令debuggerd -b [<tid>]启动 ,然后与debuggerd daemon建立连接。这样debuggerd可以在不中断进程执行的情况下dump由tid指定的进程的信息。

3.       dumpstate

控制台中运行命令dumpstate,并指定必要的参数,命令中会调用dump_backtrace_to_file与debuggerd交互。

debuggerd的使用方法

产生异常信号的C/C++程序与debuggerd建立连接后,debuggerd将进程信息dump到tombstone_XX文件中保存到/data/tombstone/文件夹下。可通过查看tombstone_XX分析异常进程的堆栈信息。

在控制台中以命令debuggerd -b [<tid>]启动。如果加上-b参数,则由tid指定的进程的信息将dump到控制台上,否则dump到tombstone文件中。控制台中运行命令callstack/dumpstate,进程信息会写入这两个命令指定的文件中。

应用程序异常处理过程

应用程序入口属于bionic实现的一部分,则对所有android的程序有效。在应用程序入口地址__start后,__linker_init中调用debugger_init()函数来注册异常信号处理handler,以实现拦截系统异常的几个singal:SIGILL,SIGABRT, SIGBUS, SIGFPE,SIGSEGV和SIGPIPE:

linker/arch/arm/begin.S

[cpp]  view plain copy
  1. start:  
  2.         mov     r0, sp  
  3.         mov     r1, #0  
  4.         bl      __linker_init  

bionic\linker\ Linker.cpp

[cpp]  view plain copy
  1. extern "C" Elf32_Addr __linker_init(void* raw_args) {  
  2.   …  
  3.   Elf32_Addr start_address = __linker_init_post_relocation(args, linker_addr);  
  4.   set_soinfo_pool_protection(PROT_READ);  
  5.   // Return the address that the calling assembly stub should jump to.  
  6.   return start_address;  
  7. }  

 

[cpp]  view plain copy
  1. static Elf32_Addr __linker_init_post_relocation(KernelArgumentBlock& args, Elf32_Addr linker_base) {  
  2.      ...  
  3.     debuggerd_init();  
  4.      ...  
  5. }  

bionic\linker\Debugger.c

[cpp]  view plain copy
  1. void debugger_init()  
  2. {  
  3.     struct sigaction act;  
  4.     memset(&act, 0, sizeof(act));  
  5.     act.sa_sigaction = debugger_signal_handler;  
  6.     act.sa_flags = SA_RESTART | SA_SIGINFO;  
  7.     sigemptyset(&act.sa_mask);  
  8.     sigaction(SIGILL, &act, NULL);  
  9.     sigaction(SIGABRT, &act, NULL);  
  10.     sigaction(SIGBUS, &act, NULL);  
  11.     sigaction(SIGFPE, &act, NULL);  
  12.     sigaction(SIGSEGV, &act, NULL);  
  13.     sigaction(SIGSTKFLT, &act, NULL);  
  14.     sigaction(SIGPIPE, &act, NULL);  
  15. }  

bionic库中的链接器会对以下七种信号设置Handler(debugger_signal_handler):

  1. SIGILL(非法指令异常)
  2. SIGABRT(abort退出异常)
  3. SIGBUS(硬件访问异常)
  4. SIGFPE(浮点运算异常)
  5. SIGSEGV(内存访问异常)
  6. SIGSTKFLT(协处理器栈异常)
  7. SIGPIPE(管道异常)

debugger_init中act.sa_flags = SA_RESTART | SA_SIGINFO的涵义:

1)      SA_RESTART

如果指定该参数,表示若信号中断了进程的某个系统调用,则系统自动启动该系统调用。如果不指定该参数,则被中断的系统调用返回失败,错误码为EINTR。这个标志位只要用于处理慢系统调用(可能会被阻塞的系统调用)。比如调用write系统调用写某个设备被阻塞,这时进程捕获某个信号且进入相应信号处理函数返回时,该系统调用可能要返回ENINTR错误。指定这个参数后,系统调用会重启,与RETRY_ON_EINTR宏配合使用则可以保证写操作的完成

2)      SA_SIGINFO

如果指定该参数,表示信号附带的参数(siginfo_t结构体)可以被传递到信号处理函数中。

链接到bionic库上的C/C++程序崩溃时,内核会发送相应的signal,进程收到异常信号后,会转入debugger_signal_handler函数中进行处理。

[cpp]  view plain copy
  1. void debugger_signal_handler(int n, siginfo_t* info, void* unused)  
  2. {  
  3.     char msgbuf[128];  
  4.     unsigned tid;  
  5.     int s;  
  6.   
  7.     logSignalSummary(n, info);  
  8.    
  9.     tid = gettid();  
  10.     //"android:debuggerd"  
  11.     s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);  
  12.    
  13.     if (s >= 0) {  
  14.         /* debugger knows our pid from the credentials on the 
  15.          * local socket but we need to tell it our tid.  It 
  16.          * is paranoid and will verify that we are giving a tid 
  17.          * that's actually in our process 
  18.          */  
  19.         int  ret;  
  20.         debugger_msg_t msg;  
  21.         msg.action = DEBUGGER_ACTION_CRASH;  
  22.         msg.tid = tid;  
  23.         RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg)));  
  24.         if (ret == sizeof(msg)) {  
  25.             /* if the write failed, there is no point to read on 
  26.              * the file descriptor. */  
  27.             RETRY_ON_EINTR(ret, read(s, &tid, 1));  
  28.             int savedErrno = errno;  
  29.             notify_gdb_of_libraries();  
  30.             errno = savedErrno;  
  31.         }  
  32.    
  33.         if (ret < 0) {  
  34.             /* read or write failed -- broken connection? */  
  35.             format_buffer(msgbuf, sizeof(msgbuf),  
  36.                 "Failed while talking to debuggerd: %s", strerror(errno));  
  37.             __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf);  
  38.         }  
  39.    
  40.         close(s);  
  41.     } else {  
  42.         /* socket failed; maybe process ran out of fds */  
  43.         format_buffer(msgbuf, sizeof(msgbuf),  
  44.             "Unable to open connection to debuggerd: %s", strerror(errno));  
  45.         __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf);  
  46.     }  
  47.    
  48.     /* remove our net so we fault for real when we return */  
  49.     signal(n, SIG_DFL);  
  50.    
  51.     /* 
  52.      * These signals are not re-thrown when we resume.  This means that 
  53.      * crashing due to (say) SIGPIPE doesn't work the way you'd expect it 
  54.      * to.  We work around this by throwing them manually.  We don't want 
  55.      * to do this for *all* signals because it'll screw up the address for 
  56.      * faults like SIGSEGV. 
  57.      */  
  58.     switch (n) {  
  59.         case SIGABRT:  
  60.         case SIGFPE:  
  61.         case SIGPIPE:  
  62.         case SIGSTKFLT:  
  63.             (void) tgkill(getpid(), gettid(), n);  
  64.             break;  
  65.         default:    // SIGILL, SIGBUS, SIGSEGV  
  66.             break;  
  67.     }  
  68. }  

debugger_signal_handler函数处理流程:

1) 调用logSignalSummary将signal信息写入文件;

[cpp]  view plain copy
  1. static void logSignalSummary(int signum, const siginfo_t* info)  
  2. {  
  3.     char buffer[128];  
  4.     char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination  
  5.     char* signame;  
  6.     switch (signum) {  
  7.         case SIGILL:    signame = "SIGILL";     break;  
  8.         case SIGABRT:   signame = "SIGABRT";    break;  
  9.         case SIGBUS:    signame = "SIGBUS";     break;  
  10.         case SIGFPE:    signame = "SIGFPE";     break;  
  11.         case SIGSEGV:   signame = "SIGSEGV";    break;  
  12.         case SIGSTKFLT: signame = "SIGSTKFLT";  break;  
  13.         case SIGPIPE:   signame = "SIGPIPE";    break;  
  14.         default:        signame = "???";        break;  
  15.     }  
  16.   
  17.     if (prctl(PR_GET_NAME, (unsigned long)threadname, 0, 0, 0) != 0) {  
  18.         strcpy(threadname, "<name unknown>");  
  19.     } else {  
  20.         // short names are null terminated by prctl, but the manpage  
  21.         // implies that 16 byte names are not.  
  22.         threadname[MAX_TASK_NAME_LEN] = 0;  
  23.     }  
  24.     format_buffer(buffer, sizeof(buffer),  
  25.         "Fatal signal %d (%s) at 0x%08x (code=%d), thread %d (%s)",  
  26.         signum, signame, info->si_addr, info->si_code, gettid(), threadname);  
  27.   
  28.     __libc_android_log_write(ANDROID_LOG_FATAL, "libc", buffer);  
  29. }  

获取异常信号的名字和thread名字,并格式化字符串,调用函数__libc_android_log_write函数写入”/dev/log/main”中。

2) 调用socket_abstract_client函数与debuggerd建立socket连接;

[cpp]  view plain copy
  1. s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);  

3) 如果连接建立成功,则设置结构体debugger_msg_t,并发送给debuggerd;

[cpp]  view plain copy
  1. msg.action = DEBUGGER_ACTION_CRASH;//告诉debuggerd采取何种行  
  2. msg.tid = tid;//线程号  
  3. RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg)));  

4) 等待debuggerd的回复,阻塞在下面的调用中,收到回复后接着执行下面的流程;

[cpp]  view plain copy
  1. RETRY_ON_EINTR(ret, read(s, &tid, 1));  

5)  重新设置信号处理函数为SIG_DFL,即采取默认的动作;

[cpp]  view plain copy
  1. signal(n, SIG_DFL);  

6)  重新发送信号,进程从当前信号处理函数返回后,会处理这个信号,进行默认的信号处理动作,即中断进程。

debuggerd的源码分析

1.       在init进程中以deamon的方式启动,在init.rc中

[csharp]  view plain copy
  1. service debuggerd /system/bin/debuggerd   
  2. class main  

以这种方式启动的话,进入main函数后,将调用do_server函数,作为server端为其他进程提供dump进程信息的服务。

2.       直接运行system/bin/debuggerd可执行文件,需要指定参数,用法为:

[cpp]  view plain copy
  1. debuggerd -b [<tid>] //参数-b表示在控制台中输出backtrace  

以这种方式启动的话,进入main函数后,将调用do_explicit_dump函数与debuggerd daemon通信,将指定进程的信息dump到文件或控制台。

服务启动方式
[cpp]  view plain copy
  1. int main(int argc, char** argv) {  
  2.     if (argc == 1) {  
  3.         return do_server();  
  4.     }  
  5. }  

当启动debuggerd进程传递的参数个数为1时,此时启动的debuggerd将作为一个后台服务进程,专门接收应用程序异常退出消息而产生tombstone。

[cpp]  view plain copy
  1. static int do_server() {  
  2.     int s;  
  3.     struct sigaction act;  
  4.     int logsocket = -1;  
  5.    
  6.     /* 
  7.      * debuggerd crashes can't be reported to debuggerd.  Reset all of the 
  8.      * crash handlers. 
  9.      */  
  10.     signal(SIGILL, SIG_DFL);  
  11.     signal(SIGABRT, SIG_DFL);  
  12.     signal(SIGBUS, SIG_DFL);  
  13.     signal(SIGFPE, SIG_DFL);  
  14.     signal(SIGSEGV, SIG_DFL);  
  15.     signal(SIGPIPE, SIG_IGN);  
  16.     signal(SIGSTKFLT, SIG_DFL);  
  17.    
  18.     logsocket = socket_local_client("logd",  
  19.             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);  
  20.     if(logsocket < 0) {  
  21.         logsocket = -1;  
  22.     } else {  
  23.         fcntl(logsocket, F_SETFD, FD_CLOEXEC);  
  24.     }  
  25.    
  26.     act.sa_handler = SIG_DFL;  
  27.     sigemptyset(&act.sa_mask);  
  28.     sigaddset(&act.sa_mask,SIGCHLD);  
  29.     act.sa_flags = SA_NOCLDWAIT;  
  30.     sigaction(SIGCHLD, &act, 0);  
  31.    
  32.     s = socket_local_server(DEBUGGER_SOCKET_NAME,  
  33.             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);  
  34.     if(s < 0) return 1;  
  35.     fcntl(s, F_SETFD, FD_CLOEXEC);  
  36.    
  37.     LOG("debuggerd: " __DATE__ " " __TIME__ "\n");  
  38.    
  39.     //check corefile limit.  
  40.     (void)check_corefile_limit();  
  41.    
  42.     for(;;) {  
  43.         struct sockaddr addr;  
  44.         socklen_t alen;  
  45.         int fd;  
  46.         alen = sizeof(addr);  
  47.         XLOG("waiting for connection\n");  
  48.         fd = accept(s, &addr, &alen);  
  49.         if(fd < 0) {  
  50.             XLOG("accept failed: %s\n", strerror(errno));  
  51.             continue;  
  52.         }  
  53.    
  54.         fcntl(fd, F_SETFD, FD_CLOEXEC);  
  55.    
  56.         handle_request(fd);  
  57.     }  
  58.     return 0;  
  59. }  

1.       忽略debuggerd自身crash的处理;

2.       建立socket通信的server端;

3.       进入无限循环中,等待并接收客户端进程连接请求,并通过handle_request()函数处理请求;

handle_request
[cpp]  view plain copy
  1. static void handle_request(int fd) {  
  2.     XLOG("handle_request(%d)\n", fd);  
  3.    
  4.     debugger_request_t request;  
  5.     int status = read_request(fd, &request);  
  6.     if (!status) {  
  7.         XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n",  
  8.             request.pid, request.uid, request.gid, request.tid);  
  9.    
  10.         /* At this point, the thread that made the request is blocked in 
  11.          * a read() call.  If the thread has crashed, then this gives us 
  12.          * time to PTRACE_ATTACH to it before it has a chance to really fault. 
  13.          * 
  14.          * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it 
  15.          * won't necessarily have stopped by the time ptrace() returns.  (We 
  16.          * currently assume it does.)  We write to the file descriptor to 
  17.          * ensure that it can run as soon as we call PTRACE_CONT below. 
  18.          * See details in bionic/libc/linker/debugger.c, in function 
  19.          * debugger_signal_handler(). 
  20.          */  
  21.         if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {  
  22.             LOG("ptrace attach failed: %s\n", strerror(errno));  
  23.         } else {  
  24.             bool detach_failed = false;  
  25.             bool attach_gdb = should_attach_gdb(&request);  
  26.             if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {  
  27.                 LOG("failed responding to client: %s\n", strerror(errno));  
  28.             } else {  
  29.                 char* tombstone_path = NULL;  
  30.    
  31.                 if (request.action == DEBUGGER_ACTION_CRASH) {  
  32.                     close(fd);  
  33.                     fd = -1;  
  34.                 }  
  35.    
  36.                 int total_sleep_time_usec = 0;  
  37.                 for (;;) {  
  38.                     int signal = wait_for_signal(request.tid, &total_sleep_time_usec);  
  39.                     if (signal < 0) {  
  40.                         break;  
  41.                     }  
  42.    
  43.                     switch (signal) {  
  44.                     case SIGSTOP:  
  45.                         if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {  
  46.                             XLOG("stopped -- dumping to tombstone\n");  
  47.                             tombstone_path = engrave_tombstone(request.pid, request.tid,  
  48.                                     signal, truetrue, &detach_failed,  
  49.                                     &total_sleep_time_usec);  
  50.                         } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {  
  51.                             XLOG("stopped -- dumping to fd\n");  
  52.                             dump_backtrace(fd, request.pid, request.tid, &detach_failed,  
  53.                                     &total_sleep_time_usec);  
  54.                         } else {  
  55.                             XLOG("stopped -- continuing\n");  
  56.                             status = ptrace(PTRACE_CONT, request.tid, 0, 0);  
  57.                             if (status) {  
  58.                                 LOG("ptrace continue failed: %s\n", strerror(errno));  
  59.                             }  
  60.                             continue/* loop again */  
  61.                         }  
  62.                         break;  
  63.    
  64.                     case SIGILL:  
  65.                     case SIGABRT:  
  66.                     case SIGBUS:  
  67.                     case SIGFPE:  
  68.                     case SIGSEGV:  
  69.                     case SIGSTKFLT: {  
  70.                         XLOG("stopped -- fatal signal\n");  
  71.                         /* 
  72.                          * Send a SIGSTOP to the process to make all of 
  73.                          * the non-signaled threads stop moving.  Without 
  74.                          * this we get a lot of "ptrace detach failed: 
  75.                          * No such process". 
  76.                          */  
  77.                         kill(request.pid, SIGSTOP);  
  78.                         /* don't dump sibling threads when attaching to GDB because it 
  79.                          * makes the process less reliable, apparently... */  
  80.                         tombstone_path = engrave_tombstone(request.pid, request.tid,  
  81.                                 signal, !attach_gdb, false, &detach_failed,  
  82.                                 &total_sleep_time_usec);  
  83.                         break;  
  84.                     }  
  85.    
  86.                     case SIGPIPE:  
  87.                         LOG("socket-client process stopped due to SIGPIPE! \n");  
  88.                         break;  
  89.    
  90.                     default:  
  91.                         XLOG("stopped -- unexpected signal\n");  
  92.                         LOG("process stopped due to unexpected signal %d\n", signal);  
  93.                         break;  
  94.                     }  
  95.                     break;  
  96.                 }  
  97.    
  98.                 if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {  
  99.                     if (tombstone_path) {  
  100.                         write(fd, tombstone_path, strlen(tombstone_path));  
  101.                     }  
  102.                     close(fd);  
  103.                     fd = -1;  
  104.                 }  
  105.                 free(tombstone_path);  
  106.             }  
  107.    
  108.             XLOG("detaching\n");  
  109.             if (attach_gdb) {  
  110.                 /* stop the process so we can debug */  
  111.                 kill(request.pid, SIGSTOP);  
  112.    
  113.                 /* detach so we can attach gdbserver */  
  114.                 if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {  
  115.                     LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno));  
  116.                     detach_failed = true;  
  117.                 }  
  118.    
  119.                 /* 
  120.                  * if debug.db.uid is set, its value indicates if we should wait 
  121.                  * for user action for the crashing process. 
  122.                  * in this case, we log a message and turn the debug LED on 
  123.                  * waiting for a gdb connection (for instance) 
  124.                  */  
  125.                 wait_for_user_action(request.pid);  
  126.             } else {  
  127.                 /* just detach */  
  128.                 if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {  
  129.                     LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno));  
  130.                     detach_failed = true;  
  131.                 }  
  132.             }  
  133.    
  134.             /* resume stopped process (so it can crash in peace). */  
  135.             kill(request.pid, SIGCONT);  
  136.    
  137.             /* If we didn't successfully detach, we're still the parent, and the 
  138.              * actual parent won't receive a death notification via wait(2).  At this point 
  139.              * there's not much we can do about that. */  
  140.             if (detach_failed) {  
  141.                 LOG("debuggerd committing suicide to free the zombie!\n");  
  142.                 kill(getpid(), SIGKILL);  
  143.             }  
  144.         }  
  145.    
  146.     }  
  147.     if (fd >= 0) {  
  148.         close(fd);  
  149.     }  
  150. }  

 1)  调用read_request函数读取client端进程发送来的数据:

[cpp]  view plain copy
  1. static int read_request(int fd, debugger_request_t* out_request) {  
  2.     struct ucred cr;  
  3.     int len = sizeof(cr);  
  4.     int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);  
  5.     if (status != 0) {  
  6.         LOG("cannot get credentials\n");  
  7.         return -1;  
  8.     }  
  9.   
  10.     XLOG("reading tid\n");  
  11.     fcntl(fd, F_SETFL, O_NONBLOCK);  
  12.   
  13.     struct pollfd pollfds[1];  
  14.     pollfds[0].fd = fd;  
  15.     pollfds[0].events = POLLIN;  
  16.     pollfds[0].revents = 0;  
  17.     status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));  
  18.     if (status != 1) {  
  19.         LOG("timed out reading tid\n");  
  20.         return -1;  
  21.     }  
  22.   
  23.     debugger_msg_t msg;  
  24.     status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));  
  25.     if (status < 0) {  
  26.         LOG("read failure? %s\n", strerror(errno));  
  27.         return -1;  
  28.     }  
  29.     if (status != sizeof(msg)) {  
  30.         LOG("invalid crash request of size %d\n", status);  
  31.         return -1;  
  32.     }  
  33.   
  34.     out_request->action = msg.action;  
  35.     out_request->tid = msg.tid;  
  36.     out_request->pid = cr.pid;  
  37.     out_request->uid = cr.uid;  
  38.     out_request->gid = cr.gid;  
  39.   
  40.     if (msg.action == DEBUGGER_ACTION_CRASH) {  
  41.         /* Ensure that the tid reported by the crashing process is valid. */  
  42.         char buf[64];  
  43.         struct stat s;  
  44.         snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);  
  45.         if(stat(buf, &s)) {  
  46.             LOG("tid %d does not exist in pid %d. ignoring debug request\n",  
  47.                     out_request->tid, out_request->pid);  
  48.             return -1;  
  49.         }  
  50.     } else if (cr.uid == 0  
  51.             || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {  
  52.         /* Only root or system can ask us to attach to any process and dump it explicitly. 
  53.          * However, system is only allowed to collect backtraces but cannot dump tombstones. */  
  54.         status = get_process_info(out_request->tid, &out_request->pid,  
  55.                 &out_request->uid, &out_request->gid);  
  56.         if (status < 0) {  
  57.             LOG("tid %d does not exist. ignoring explicit dump request\n",  
  58.                     out_request->tid);  
  59.             return -1;  
  60.         }  
  61.     } else {  
  62.         /* No one else is not allowed to dump arbitrary processes. */  
  63.         return -1;  
  64.     }  
  65.     return 0;  
  66. }  

☞  从socket中读取client端进程的pid uid gid

[cpp]  view plain copy
  1. getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);  

☞  轮询socket句柄

[cpp]  view plain copy
  1. struct pollfd pollfds[1];  
  2. pollfds[0].fd = fd;  
  3. pollfds[0].events = POLLIN;  
  4. pollfds[0].revents = 0;  
  5. status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));  

☞  从socket上读取debugger_msg_t结构体

[cpp]  view plain copy
  1. debugger_msg_t msg;  
  2. status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));  
  3. if (status < 0) {  
  4.     LOG("read failure? %s\n", strerror(errno));  
  5.     return -1;  
  6. }  
  7. if (status != sizeof(msg)) {  
  8.     LOG("invalid crash request of size %d\n", status);  
  9.     return -1;  
  10. }  
  11. out_request->action = msg.action;  
  12. out_request->tid = msg.tid;  
  13. out_request->pid = cr.pid;  
  14. out_request->uid = cr.uid;  
  15. out_request->gid = cr.gid;  

☞ 如果debugger_msg_t中设置的action为DEBUGGER_ACTION_CRASH,说明是crash的C/C++进程发来的请求,则判断传进来的tid是否有效。

[cpp]  view plain copy
  1. if (msg.action == DEBUGGER_ACTION_CRASH) {  
  2.     /* Ensure that the tid reported by the crashing process is valid. */  
  3.     char buf[64];  
  4.     struct stat s;  
  5.     snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);  
  6.     if(stat(buf, &s)) {  
  7.         LOG("tid %d does not exist in pid %d. ignoring debug request\n",  
  8.                 out_request->tid, out_request->pid);  
  9.         return -1;  
  10.     }  
  11. }  

☞   如果debugger_msg_t中设置的action为DEBUGGER_ACTION_DUMP_BACKTRACE说明是其他方式(debuggerd)发来的请求,则要求必须为root权限或者system权限,然后再判断tid是否有效。

 

 2)  从read_request返回后,调用ptrace函数attach到tid指定的进程,此时debuggerd将变为被attache进程的父进程,然后ptrace函数会向子进程发送SIGSTOP信号将子进程停下来。此时,父进程有机会检查子进程核心image和寄存器的值。

[cpp]  view plain copy
  1. ptrace(PTRACE_ATTACH, request.tid, 0, 0)  

 3)  调用下面的语句给client端子进程回复消息,使clinet端的进程能从read调用中返回。

[cpp]  view plain copy
  1. TEMP_FAILURE_RETRY(write(fd, "\0", 1)  

 4)  在for循环中等待子进程停止。

[cpp]  view plain copy
  1. int signal = wait_for_signal(request.tid, &total_sleep_time_usec);  

 5)  子进程根据收到的不同信号、不同的action进行不同的处理

[cpp]  view plain copy
  1. switch (signal) {  
  2.     case SIGSTOP:  
  3.         if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {  
  4.             XLOG("stopped -- dumping to tombstone\n");  
  5.             tombstone_path = engrave_tombstone(request.pid, request.tid,  
  6.                     signal, truetrue, &detach_failed,  
  7.                     &total_sleep_time_usec);  
  8.         } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {  
  9.             XLOG("stopped -- dumping to fd\n");  
  10.             dump_backtrace(fd, request.pid, request.tid, &detach_failed,  
  11.                     &total_sleep_time_usec);  
  12.         } else {  
  13.             XLOG("stopped -- continuing\n");  
  14.             status = ptrace(PTRACE_CONT, request.tid, 0, 0);  
  15.             if (status) {  
  16.                 LOG("ptrace continue failed: %s\n", strerror(errno));  
  17.             }  
  18.             continue/* loop again */  
  19.         }  
  20.         break;  
  21.     case SIGILL:  
  22.     case SIGABRT:  
  23.     case SIGBUS:  
  24.     case SIGFPE:  
  25.     case SIGSEGV:  
  26.     case SIGSTKFLT: {  
  27.         XLOG("stopped -- fatal signal\n");  
  28.         kill(request.pid, SIGSTOP);  
  29.         tombstone_path = engrave_tombstone(request.pid, request.tid,  
  30.                 signal, !attach_gdb, false, &detach_failed,  
  31.                 &total_sleep_time_usec);  
  32.         break;  
  33.     }  
  34.     case SIGPIPE:  
  35.         LOG("socket-client process stopped due to SIGPIPE! \n");  
  36.         break;  
  37.     default:  
  38.         XLOG("stopped -- unexpected signal\n");  
  39.         LOG("process stopped due to unexpected signal %d\n", signal);  
  40.         break;  
  41. }  

☞子进程收到SIGSTOP说明进程并没有发生crash,根据action不同将进程信息写入tombstone文件。

☞子进程收到七种异常信号说明是进程发生crash,调用engrave_tombstone直接将dump的信息写到tombstone。

 6)  调用ptrace(PTRACE_DETACH, request.tid, 0, 0)解除对子进程的追踪;

[cpp]  view plain copy
  1. if (attach_gdb) {  
  2.     kill(request.pid, SIGSTOP);  
  3.     if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {  
  4.         LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno));  
  5.         detach_failed = true;  
  6.     }  
  7.     wait_for_user_action(request.pid);  
  8. else {  
  9.     if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {  
  10.         LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno));  
  11.         detach_failed = true;  
  12.     }  
  13. }  

如果运行了类似以下指令:adb shell setprop debug.db.uid 10000;则所有uid<10000的进程发生crash的时候attach_gdb为true,将停止crash进程,并调用ptrace(PTRACE_DETACH, request.tid, 0, 0) 解除对crash进程的追踪后,开始等待gdb的连接。

adb forward tcp:5039 tcp:5039

adb shell gdbserver :5039 --attach pid &

用户按下HOME或者VOLUME DOWN按键,可以使进程继续进行,自然crash

attach_gdb为false时,只会解除对子进程的追踪。

 7)  调用kill(request.pid, SIGCONT)恢复被停止的子进程,并让其自然终止;

engrave_tombstone
[cpp]  view plain copy
  1. char* engrave_tombstone(pid_t pid, pid_t tid, int signal,  
  2.         bool dump_sibling_threads, bool quiet, bool* detach_failed,  
  3.         int* total_sleep_time_usec) {  
  4.     mkdir(TOMBSTONE_DIR, 0755);  
  5.     chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);  
  6.   
  7.     //dump maps & check corefile limit .  
  8.     dump_creash_maps(pid);  //creat maps file  
  9.   
  10.     int fd;  
  11.     char* path = find_and_open_tombstone(&fd);  
  12.     if (!path) {  
  13.         *detach_failed = false;  
  14.         return NULL;  
  15.     }  
  16.   
  17.     log_t log;  
  18.     log.tfd = fd;  
  19.     log.quiet = quiet;  
  20.     *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,  
  21.             total_sleep_time_usec);  
  22.   
  23.     close(fd);  
  24.     return path;  
  25. }  

对于crash的C/C++进程,主要通过这个函数dump进程信息

1.创建”/data/tombstones”文件夹并修改权限

2.调用函数find_and_open_tombstone,tombstone_XX文件最多10个,超过则覆盖最早的

3.调用dump_crash将所有信息dump到tombstone文件:

☞ dump_build_info(log);

☞ dump_thread_info(log, pid, tid, true);

☞ dump_fault_addr(log, tid, signal);

☞ dump_thread(context, log, tid, true, total_sleep_time_usec) dump进程的上下文信息

☞ dump_logs(log, pid, true);

☞ dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec);

dump_backtrace
[cpp]  view plain copy
  1. void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed,  
  2.         int* total_sleep_time_usec) {  
  3.     log_t log;  
  4.     log.tfd = fd;  
  5.     log.quiet = true;  
  6.   
  7.     ptrace_context_t* context = load_ptrace_context(tid);  
  8.     dump_process_header(&log, pid);  
  9.     dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec);  
  10.   
  11.     char task_path[64];  
  12.     snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);  
  13.     DIR* d = opendir(task_path);  
  14.     if (d) {  
  15.         struct dirent debuf;  
  16.         struct dirent *de;  
  17.         while (!readdir_r(d, &debuf, &de) && de) {  
  18.             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {  
  19.                 continue;  
  20.             }  
  21.   
  22.             char* end;  
  23.             pid_t new_tid = strtoul(de->d_name, &end, 10);  
  24.             if (*end || new_tid == tid) {  
  25.                 continue;  
  26.             }  
  27.   
  28.             dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec);  
  29.         }  
  30.         closedir(d);  
  31.     }  
  32.   
  33.     dump_process_footer(&log, pid);  
  34.     free_ptrace_context(context);  
  35. }  

☞  dump_process_header(&log, pid);

☞  dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec);

☞ dump_process_footer(&log, pid);      

调试工具方式
[cpp]  view plain copy
  1. int main(int argc, char** argv) {  
  2.     bool dump_backtrace = false;  
  3.     bool have_tid = false;  
  4.     pid_t tid = 0;  
  5.     for (int i = 1; i < argc; i++) {  
  6.         if (!strcmp(argv[i], "-b")) {  
  7.             dump_backtrace = true;  
  8.         } else if (!have_tid) {  
  9.             tid = atoi(argv[i]);  
  10.             have_tid = true;  
  11.         } else {  
  12.             usage();  
  13.             return 1;  
  14.         }  
  15.     }  
  16.     if (!have_tid) {  
  17.         usage();  
  18.         return 1;  
  19.     }  
  20.     return do_explicit_dump(tid, dump_backtrace);  
  21. }  

通过do_explicit_dump函数dump出指定进程的栈信息等

[cpp]  view plain copy
  1. static int do_explicit_dump(pid_t tid, bool dump_backtrace) {  
  2.     fprintf(stdout, "Sending request to dump task %d.\n", tid);  
  3.    
  4.     if (dump_backtrace) {  
  5.         fflush(stdout);  
  6.         if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {  
  7.             fputs("Error dumping backtrace.\n", stderr);  
  8.             return 1;  
  9.         }  
  10.     } else {  
  11.         char tombstone_path[PATH_MAX];  
  12.         if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {  
  13.             fputs("Error dumping tombstone.\n", stderr);  
  14.             return 1;  
  15.         }  
  16.         fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);  
  17.     }  
  18.     return 0;  
  19. }  

☞  dump_backtrace_to_file(tid, fileno(stdout))

☞  dump_tombstone(tid, tombstone_path, sizeof(tombstone_path))

0

这篇关于Android debuggerd 源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/680633

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存