Android use UNIX Domain Socket for get debug log.
usually name "tombstone_0X" and so on in /data/log/logcat/
1. Server
First , it has a socket server. it's a executable program.
The code was in "system/core/debuggerd/debuggerd.c"
in the main, it open a IPC socket
- int main()
- {
- int s;
- struct sigaction act;
- int logsocket = -1;
- logsocket = socket_local_client("logd",
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
- if(logsocket < 0) {
- logsocket = -1;
- } else {
- fcntl(logsocket, F_SETFD, FD_CLOEXEC);
- }
- act.sa_handler = SIG_DFL;
- sigemptyset(&act.sa_mask);
- sigaddset(&act.sa_mask,SIGCHLD);
- act.sa_flags = SA_NOCLDWAIT;
- sigaction(SIGCHLD, &act, 0);
- s = socket_local_server("android:debuggerd",
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- if(s < 0) return -1;
- fcntl(s, F_SETFD, FD_CLOEXEC);
- LOG("debuggerd: " __DATE__ " " __TIME__ "\n");
- for(;;) {
- struct sockaddr addr;
- socklen_t alen;
- int fd;
- alen = sizeof(addr);
- fd = accept(s, &addr, &alen);
- if(fd < 0) continue;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- handle_crashing_process(fd);
- }
- return 0;
- }
and the log is generated in this function.
2. Client
how could a crashed program connect to the server?
Since every crashed program will receive a signal from the system, so we use "signal" register a signal handle function to the client.
So we can do something before the program die.
the code is "boinic/linker/Debugger.c"
- void debugger_init()
- {
- signal(SIGILL, debugger_signal_handler);
- signal(SIGABRT, debugger_signal_handler);
- signal(SIGBUS, debugger_signal_handler);
- signal(SIGFPE, debugger_signal_handler);
- signal(SIGSEGV, debugger_signal_handler);
- signal(SIGSTKFLT, debugger_signal_handler);
- signal(SIGPIPE, debugger_signal_handler);
- }
- void debugger_signal_handler(int n)
- {
- unsigned tid;
- int s;
- /* avoid picking up GC interrupts */
- signal(SIGUSR1, SIG_IGN);
- tid = gettid();
- s = socket_abstract_client("android:debuggerd", SOCK_STREAM);
- if(s >= 0) {
- /* debugger knows our pid from the credentials on the
- * local socket but we need to tell it our tid. It
- * is paranoid and will verify that we are giving a tid
- * that's actually in our process
- */
- int ret;
- RETRY_ON_EINTR(ret, write(s, &tid, sizeof(unsigned)));
- if (ret == sizeof(unsigned)) {
- /* if the write failed, there is no point to read on
- * the file descriptor. */
- RETRY_ON_EINTR(ret, read(s, &tid, 1));
- notify_gdb_of_libraries();
- }
- close(s);
- }
- /* remove our net so we fault for real when we return */
- signal(n, SIG_IGN);
- }
3. how is the log file produced
in the client, we just write out tid to the server, of course the log is generated by the server.
in it's "handle_crashing_process" function.
- static void handle_crashing_process(int fd)
- {
- .....
- for(;;) {
- n = waitpid(tid, &status, __WALL);
- if(n < 0) {
- if(errno == EAGAIN) continue;
- LOG("waitpid failed: %s\n", strerror(errno));
- goto done;
- }
- LOG("waitpid: n=%d status=%08x\n", n, status);
- if(WIFSTOPPED(status)){
- n = WSTOPSIG(status);
- switch(n) {
- case SIGSTOP:
- LOG("stopped -- continuing\n");
- n = ptrace(PTRACE_CONT, tid, 0, 0);
- if(n) {
- LOG("ptrace failed: %s\n", strerror(errno));
- goto done;
- }
- continue;
- case SIGILL:
- case SIGABRT:
- case SIGBUS:
- case SIGFPE:
- case SIGSEGV:
- case SIGSTKFLT: {
- LOG("stopped -- fatal signal\n");
- need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n);
- kill(tid, SIGSTOP);
- goto done;
- }
- default:
- LOG("stopped -- unexpected signal\n");
- goto done;
- }
- } else {
- LOG("unexpected waitpid response\n");
- goto done;
- }
- }
- ...
- }
the function did a lot of things, and we will discuss the detail later.