NativeCrashListenerpublic final class NativeCrashListener extends Thread Set up a Unix domain socket that debuggerd will connect() to in
order to write a description of a native crash. The crash info is
then parsed and forwarded to the ActivityManagerService's normal
crash handling code.
Note that this component runs in a separate thread. |
Fields Summary |
---|
static final String | TAG | static final boolean | DEBUG | static final boolean | MORE_DEBUG | static final String | DEBUGGERD_SOCKET_PATH | static final long | SOCKET_TIMEOUT_MILLIS | final ActivityManagerService | mAm |
Methods Summary |
---|
void | consumeNativeCrashData(java.io.FileDescriptor fd)
if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
final byte[] buf = new byte[4096];
final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
try {
StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
// first, the pid and signal number
int headerBytes = readExactly(fd, buf, 0, 8);
if (headerBytes != 8) {
// protocol failure; give up
Slog.e(TAG, "Unable to read from debuggerd");
return;
}
int pid = unpackInt(buf, 0);
int signal = unpackInt(buf, 4);
if (DEBUG) {
Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
}
// now the text of the dump
if (pid > 0) {
final ProcessRecord pr;
synchronized (mAm.mPidsSelfLocked) {
pr = mAm.mPidsSelfLocked.get(pid);
}
if (pr != null) {
// Don't attempt crash reporting for persistent apps
if (pr.persistent) {
if (DEBUG) {
Slog.v(TAG, "Skipping report for persistent app " + pr);
}
return;
}
int bytes;
do {
// get some data
bytes = Os.read(fd, buf, 0, buf.length);
if (bytes > 0) {
if (MORE_DEBUG) {
String s = new String(buf, 0, bytes, "UTF-8");
Slog.v(TAG, "READ=" + bytes + "> " + s);
}
// did we just get the EOD null byte?
if (buf[bytes-1] == 0) {
os.write(buf, 0, bytes-1); // exclude the EOD token
break;
}
// no EOD, so collect it and read more
os.write(buf, 0, bytes);
}
} while (bytes > 0);
// Okay, we've got the report.
if (DEBUG) Slog.v(TAG, "processing");
// Mark the process record as being a native crash so that the
// cleanup mechanism knows we're still submitting the report
// even though the process will vanish as soon as we let
// debuggerd proceed.
synchronized (mAm) {
pr.crashing = true;
pr.forceCrashReport = true;
}
// Crash reporting is synchronous but we want to let debuggerd
// go about it business right away, so we spin off the actual
// reporting logic on a thread and let it take it's time.
final String reportString = new String(os.toByteArray(), "UTF-8");
(new NativeCrashReporter(pr, signal, reportString)).start();
} else {
Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
}
} else {
Slog.e(TAG, "Bogus pid!");
}
} catch (Exception e) {
Slog.e(TAG, "Exception dealing with report", e);
// ugh, fail.
}
| static int | readExactly(java.io.FileDescriptor fd, byte[] buffer, int offset, int numBytes)
int totalRead = 0;
while (numBytes > 0) {
int n = Os.read(fd, buffer, offset + totalRead, numBytes);
if (n <= 0) {
if (DEBUG) {
Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
}
return -1; // premature EOF or timeout
}
numBytes -= n;
totalRead += n;
}
return totalRead;
| public void | run()
final byte[] ackSignal = new byte[1];
if (DEBUG) Slog.i(TAG, "Starting up");
// The file system entity for this socket is created with 0700 perms, owned
// by system:system. debuggerd runs as root, so is capable of connecting to
// it, but 3rd party apps cannot.
{
File socketFile = new File(DEBUGGERD_SOCKET_PATH);
if (socketFile.exists()) {
socketFile.delete();
}
}
try {
FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH);
Os.bind(serverFd, sockAddr, 0);
Os.listen(serverFd, 1);
while (true) {
InetSocketAddress peer = new InetSocketAddress();
FileDescriptor peerFd = null;
try {
if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
peerFd = Os.accept(serverFd, peer);
if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
if (peerFd != null) {
// Only the superuser is allowed to talk to us over this socket
StructUcred credentials =
Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
if (credentials.uid == 0) {
// the reporting thread may take responsibility for
// acking the debugger; make sure we play along.
consumeNativeCrashData(peerFd);
}
}
} catch (Exception e) {
Slog.w(TAG, "Error handling connection", e);
} finally {
// Always ack debuggerd's connection to us. The actual
// byte written is irrelevant.
if (peerFd != null) {
try {
Os.write(peerFd, ackSignal, 0, 1);
} catch (Exception e) {
/* we don't care about failures here */
if (MORE_DEBUG) {
Slog.d(TAG, "Exception writing ack: " + e.getMessage());
}
}
try {
Os.close(peerFd);
} catch (ErrnoException e) {
if (MORE_DEBUG) {
Slog.d(TAG, "Exception closing socket: " + e.getMessage());
}
}
}
}
}
} catch (Exception e) {
Slog.e(TAG, "Unable to init native debug socket!", e);
}
| static int | unpackInt(byte[] buf, int offset)
int b0, b1, b2, b3;
b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension
b1 = ((int) buf[offset+1]) & 0xFF;
b2 = ((int) buf[offset+2]) & 0xFF;
b3 = ((int) buf[offset+3]) & 0xFF;
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
|