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;
}
when a program crashed, the socket will be connected and the function handle_crashing_process(fd) will be called.
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);
}
and in the "debugger_signal_handler" , we will send our pid to the server
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;
}
}
...
}
it was done int the "engrave_tombstone"
the function did a lot of things, and we will discuss the detail later.