1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.am; 18 19import android.app.ApplicationErrorReport.CrashInfo; 20import android.util.Slog; 21 22import libcore.io.ErrnoException; 23import libcore.io.Libcore; 24import libcore.io.StructTimeval; 25import libcore.io.StructUcred; 26 27import static libcore.io.OsConstants.*; 28 29import java.io.ByteArrayOutputStream; 30import java.io.File; 31import java.io.FileDescriptor; 32import java.net.InetSocketAddress; 33import java.net.InetUnixAddress; 34 35/** 36 * Set up a Unix domain socket that debuggerd will connect() to in 37 * order to write a description of a native crash. The crash info is 38 * then parsed and forwarded to the ActivityManagerService's normal 39 * crash handling code. 40 * 41 * Note that this component runs in a separate thread. 42 */ 43class NativeCrashListener extends Thread { 44 static final String TAG = "NativeCrashListener"; 45 static final boolean DEBUG = false; 46 static final boolean MORE_DEBUG = DEBUG && false; 47 48 // Must match the path defined in debuggerd.c. 49 static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket"; 50 51 // Use a short timeout on socket operations and abandon the connection 52 // on hard errors 53 static final long SOCKET_TIMEOUT_MILLIS = 2000; // 2 seconds 54 55 final ActivityManagerService mAm; 56 57 /* 58 * Spin the actual work of handling a debuggerd crash report into a 59 * separate thread so that the listener can go immediately back to 60 * accepting incoming connections. 61 */ 62 class NativeCrashReporter extends Thread { 63 ProcessRecord mApp; 64 int mSignal; 65 String mCrashReport; 66 67 NativeCrashReporter(ProcessRecord app, int signal, String report) { 68 super("NativeCrashReport"); 69 mApp = app; 70 mSignal = signal; 71 mCrashReport = report; 72 } 73 74 @Override 75 public void run() { 76 try { 77 CrashInfo ci = new CrashInfo(); 78 ci.exceptionClassName = "Native crash"; 79 ci.exceptionMessage = Libcore.os.strsignal(mSignal); 80 ci.throwFileName = "unknown"; 81 ci.throwClassName = "unknown"; 82 ci.throwMethodName = "unknown"; 83 ci.stackTrace = mCrashReport; 84 85 if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()"); 86 mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci); 87 if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned"); 88 } catch (Exception e) { 89 Slog.e(TAG, "Unable to report native crash", e); 90 } 91 } 92 } 93 94 /* 95 * Daemon thread that accept()s incoming domain socket connections from debuggerd 96 * and processes the crash dump that is passed through. 97 */ 98 NativeCrashListener() { 99 mAm = ActivityManagerService.self(); 100 } 101 102 @Override 103 public void run() { 104 final byte[] ackSignal = new byte[1]; 105 106 if (DEBUG) Slog.i(TAG, "Starting up"); 107 108 // The file system entity for this socket is created with 0700 perms, owned 109 // by system:system. debuggerd runs as root, so is capable of connecting to 110 // it, but 3rd party apps cannot. 111 { 112 File socketFile = new File(DEBUGGERD_SOCKET_PATH); 113 if (socketFile.exists()) { 114 socketFile.delete(); 115 } 116 } 117 118 try { 119 FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0); 120 final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH); 121 Libcore.os.bind(serverFd, sockAddr, 0); 122 Libcore.os.listen(serverFd, 1); 123 124 while (true) { 125 InetSocketAddress peer = new InetSocketAddress(); 126 FileDescriptor peerFd = null; 127 try { 128 if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection"); 129 peerFd = Libcore.os.accept(serverFd, peer); 130 if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd); 131 if (peerFd != null) { 132 // Only the superuser is allowed to talk to us over this socket 133 StructUcred credentials = 134 Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED); 135 if (credentials.uid == 0) { 136 // the reporting thread may take responsibility for 137 // acking the debugger; make sure we play along. 138 consumeNativeCrashData(peerFd); 139 } 140 } 141 } catch (Exception e) { 142 Slog.w(TAG, "Error handling connection", e); 143 } finally { 144 // Always ack debuggerd's connection to us. The actual 145 // byte written is irrelevant. 146 if (peerFd != null) { 147 try { 148 Libcore.os.write(peerFd, ackSignal, 0, 1); 149 } catch (Exception e) { 150 /* we don't care about failures here */ 151 if (MORE_DEBUG) { 152 Slog.d(TAG, "Exception writing ack: " + e.getMessage()); 153 } 154 } 155 } 156 } 157 } 158 } catch (Exception e) { 159 Slog.e(TAG, "Unable to init native debug socket!", e); 160 } 161 } 162 163 static int unpackInt(byte[] buf, int offset) { 164 int b0, b1, b2, b3; 165 166 b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension 167 b1 = ((int) buf[offset+1]) & 0xFF; 168 b2 = ((int) buf[offset+2]) & 0xFF; 169 b3 = ((int) buf[offset+3]) & 0xFF; 170 return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; 171 } 172 173 static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes) 174 throws ErrnoException { 175 int totalRead = 0; 176 while (numBytes > 0) { 177 int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes); 178 if (n <= 0) { 179 if (DEBUG) { 180 Slog.w(TAG, "Needed " + numBytes + " but saw " + n); 181 } 182 return -1; // premature EOF or timeout 183 } 184 numBytes -= n; 185 totalRead += n; 186 } 187 return totalRead; 188 } 189 190 // Read the crash report from the debuggerd connection 191 void consumeNativeCrashData(FileDescriptor fd) { 192 if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected"); 193 final byte[] buf = new byte[4096]; 194 final ByteArrayOutputStream os = new ByteArrayOutputStream(4096); 195 196 try { 197 StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS); 198 Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout); 199 Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout); 200 201 // first, the pid and signal number 202 int headerBytes = readExactly(fd, buf, 0, 8); 203 if (headerBytes != 8) { 204 // protocol failure; give up 205 Slog.e(TAG, "Unable to read from debuggerd"); 206 return; 207 } 208 209 int pid = unpackInt(buf, 0); 210 int signal = unpackInt(buf, 4); 211 if (DEBUG) { 212 Slog.v(TAG, "Read pid=" + pid + " signal=" + signal); 213 } 214 215 // now the text of the dump 216 if (pid > 0) { 217 final ProcessRecord pr; 218 synchronized (mAm.mPidsSelfLocked) { 219 pr = mAm.mPidsSelfLocked.get(pid); 220 } 221 if (pr != null) { 222 // Don't attempt crash reporting for persistent apps 223 if (pr.persistent) { 224 if (DEBUG) { 225 Slog.v(TAG, "Skipping report for persistent app " + pr); 226 } 227 return; 228 } 229 230 int bytes; 231 do { 232 // get some data 233 bytes = Libcore.os.read(fd, buf, 0, buf.length); 234 if (bytes > 0) { 235 if (MORE_DEBUG) { 236 String s = new String(buf, 0, bytes, "UTF-8"); 237 Slog.v(TAG, "READ=" + bytes + "> " + s); 238 } 239 // did we just get the EOD null byte? 240 if (buf[bytes-1] == 0) { 241 os.write(buf, 0, bytes-1); // exclude the EOD token 242 break; 243 } 244 // no EOD, so collect it and read more 245 os.write(buf, 0, bytes); 246 } 247 } while (bytes > 0); 248 249 // Okay, we've got the report. 250 if (DEBUG) Slog.v(TAG, "processing"); 251 252 // Mark the process record as being a native crash so that the 253 // cleanup mechanism knows we're still submitting the report 254 // even though the process will vanish as soon as we let 255 // debuggerd proceed. 256 synchronized (mAm) { 257 pr.crashing = true; 258 pr.forceCrashReport = true; 259 } 260 261 // Crash reporting is synchronous but we want to let debuggerd 262 // go about it business right away, so we spin off the actual 263 // reporting logic on a thread and let it take it's time. 264 final String reportString = new String(os.toByteArray(), "UTF-8"); 265 (new NativeCrashReporter(pr, signal, reportString)).start(); 266 } else { 267 Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid); 268 } 269 } else { 270 Slog.e(TAG, "Bogus pid!"); 271 } 272 } catch (Exception e) { 273 Slog.e(TAG, "Exception dealing with report", e); 274 // ugh, fail. 275 } 276 } 277 278} 279