blob: ca0d57865089321f71942a9d5a58c484846d57c3 [file] [log] [blame]
Christopher Tate58d380d2013-03-19 13:10:03 -07001/*
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;
Elliott Hughes02e85742014-04-24 23:59:05 +000020import android.system.ErrnoException;
21import android.system.Os;
22import android.system.StructTimeval;
23import android.system.StructUcred;
Neil Fullere9d53752015-07-07 16:58:00 +010024import android.system.UnixSocketAddress;
Christopher Tate58d380d2013-03-19 13:10:03 -070025import android.util.Slog;
26
Elliott Hughes02e85742014-04-24 23:59:05 +000027import static android.system.OsConstants.*;
Christopher Tate58d380d2013-03-19 13:10:03 -070028
29import java.io.ByteArrayOutputStream;
30import java.io.File;
31import java.io.FileDescriptor;
Neil Fullerd720ef52014-04-23 16:48:34 +000032import java.io.InterruptedIOException;
Christopher Tate58d380d2013-03-19 13:10:03 -070033import java.net.InetSocketAddress;
Christopher Tate58d380d2013-03-19 13:10:03 -070034
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 */
Dianne Hackbornbe4e6aa2013-06-07 13:25:29 -070043final class NativeCrashListener extends Thread {
Christopher Tate58d380d2013-03-19 13:10:03 -070044 static final String TAG = "NativeCrashListener";
45 static final boolean DEBUG = false;
Christopher Tate1b645982013-04-16 16:08:12 -070046 static final boolean MORE_DEBUG = DEBUG && false;
Christopher Tate58d380d2013-03-19 13:10:03 -070047
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
Christopher Tate1b645982013-04-16 16:08:12 -070053 static final long SOCKET_TIMEOUT_MILLIS = 2000; // 2 seconds
Christopher Tate58d380d2013-03-19 13:10:03 -070054
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";
Elliott Hughes02e85742014-04-24 23:59:05 +000079 ci.exceptionMessage = Os.strsignal(mSignal);
Christopher Tate58d380d2013-03-19 13:10:03 -070080 ci.throwFileName = "unknown";
81 ci.throwClassName = "unknown";
82 ci.throwMethodName = "unknown";
83 ci.stackTrace = mCrashReport;
84
85 if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
Eric Rowe88d842c2013-04-08 15:00:27 -070086 mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
Christopher Tate58d380d2013-03-19 13:10:03 -070087 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 */
Jeff Brown4ccb8232014-01-16 22:16:42 -080098 NativeCrashListener(ActivityManagerService am) {
99 mAm = am;
Christopher Tate58d380d2013-03-19 13:10:03 -0700100 }
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 {
Elliott Hughes02e85742014-04-24 23:59:05 +0000119 FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
Neil Fullere9d53752015-07-07 16:58:00 +0100120 final UnixSocketAddress sockAddr = UnixSocketAddress.createFileSystem(
121 DEBUGGERD_SOCKET_PATH);
122 Os.bind(serverFd, sockAddr);
Elliott Hughes02e85742014-04-24 23:59:05 +0000123 Os.listen(serverFd, 1);
Christopher Tate58d380d2013-03-19 13:10:03 -0700124
125 while (true) {
126 InetSocketAddress peer = new InetSocketAddress();
127 FileDescriptor peerFd = null;
128 try {
Christopher Tate1b645982013-04-16 16:08:12 -0700129 if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
Elliott Hughes02e85742014-04-24 23:59:05 +0000130 peerFd = Os.accept(serverFd, peer);
Christopher Tate1b645982013-04-16 16:08:12 -0700131 if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
Christopher Tate58d380d2013-03-19 13:10:03 -0700132 if (peerFd != null) {
133 // Only the superuser is allowed to talk to us over this socket
134 StructUcred credentials =
Elliott Hughes02e85742014-04-24 23:59:05 +0000135 Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
Christopher Tate58d380d2013-03-19 13:10:03 -0700136 if (credentials.uid == 0) {
137 // the reporting thread may take responsibility for
138 // acking the debugger; make sure we play along.
139 consumeNativeCrashData(peerFd);
140 }
141 }
142 } catch (Exception e) {
143 Slog.w(TAG, "Error handling connection", e);
144 } finally {
145 // Always ack debuggerd's connection to us. The actual
146 // byte written is irrelevant.
147 if (peerFd != null) {
148 try {
Elliott Hughes02e85742014-04-24 23:59:05 +0000149 Os.write(peerFd, ackSignal, 0, 1);
Christopher Tate1b645982013-04-16 16:08:12 -0700150 } catch (Exception e) {
151 /* we don't care about failures here */
152 if (MORE_DEBUG) {
153 Slog.d(TAG, "Exception writing ack: " + e.getMessage());
154 }
155 }
Nick Kralevich1b2d5952013-06-18 17:56:28 -0700156 try {
Elliott Hughes02e85742014-04-24 23:59:05 +0000157 Os.close(peerFd);
Nick Kralevich1b2d5952013-06-18 17:56:28 -0700158 } catch (ErrnoException e) {
159 if (MORE_DEBUG) {
160 Slog.d(TAG, "Exception closing socket: " + e.getMessage());
161 }
162 }
Christopher Tate58d380d2013-03-19 13:10:03 -0700163 }
164 }
165 }
166 } catch (Exception e) {
167 Slog.e(TAG, "Unable to init native debug socket!", e);
168 }
169 }
170
171 static int unpackInt(byte[] buf, int offset) {
172 int b0, b1, b2, b3;
173
174 b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension
175 b1 = ((int) buf[offset+1]) & 0xFF;
176 b2 = ((int) buf[offset+2]) & 0xFF;
177 b3 = ((int) buf[offset+3]) & 0xFF;
178 return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
179 }
180
181 static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
Neil Fullerd720ef52014-04-23 16:48:34 +0000182 throws ErrnoException, InterruptedIOException {
Christopher Tate58d380d2013-03-19 13:10:03 -0700183 int totalRead = 0;
184 while (numBytes > 0) {
Elliott Hughes02e85742014-04-24 23:59:05 +0000185 int n = Os.read(fd, buffer, offset + totalRead, numBytes);
Christopher Tate58d380d2013-03-19 13:10:03 -0700186 if (n <= 0) {
187 if (DEBUG) {
188 Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
189 }
190 return -1; // premature EOF or timeout
191 }
192 numBytes -= n;
193 totalRead += n;
194 }
195 return totalRead;
196 }
197
198 // Read the crash report from the debuggerd connection
199 void consumeNativeCrashData(FileDescriptor fd) {
Christopher Tate1b645982013-04-16 16:08:12 -0700200 if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
Christopher Tate58d380d2013-03-19 13:10:03 -0700201 final byte[] buf = new byte[4096];
202 final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
203
204 try {
205 StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
Elliott Hughes02e85742014-04-24 23:59:05 +0000206 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
207 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
Christopher Tate58d380d2013-03-19 13:10:03 -0700208
209 // first, the pid and signal number
210 int headerBytes = readExactly(fd, buf, 0, 8);
211 if (headerBytes != 8) {
212 // protocol failure; give up
213 Slog.e(TAG, "Unable to read from debuggerd");
214 return;
215 }
216
217 int pid = unpackInt(buf, 0);
218 int signal = unpackInt(buf, 4);
219 if (DEBUG) {
220 Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
221 }
222
223 // now the text of the dump
224 if (pid > 0) {
225 final ProcessRecord pr;
226 synchronized (mAm.mPidsSelfLocked) {
227 pr = mAm.mPidsSelfLocked.get(pid);
228 }
229 if (pr != null) {
Christopher Tateb86d81d2013-04-19 17:36:54 -0700230 // Don't attempt crash reporting for persistent apps
231 if (pr.persistent) {
232 if (DEBUG) {
233 Slog.v(TAG, "Skipping report for persistent app " + pr);
234 }
235 return;
236 }
237
Christopher Tate58d380d2013-03-19 13:10:03 -0700238 int bytes;
239 do {
240 // get some data
Elliott Hughes02e85742014-04-24 23:59:05 +0000241 bytes = Os.read(fd, buf, 0, buf.length);
Christopher Tate58d380d2013-03-19 13:10:03 -0700242 if (bytes > 0) {
Christopher Tate1b645982013-04-16 16:08:12 -0700243 if (MORE_DEBUG) {
Christopher Tate58d380d2013-03-19 13:10:03 -0700244 String s = new String(buf, 0, bytes, "UTF-8");
245 Slog.v(TAG, "READ=" + bytes + "> " + s);
246 }
247 // did we just get the EOD null byte?
248 if (buf[bytes-1] == 0) {
249 os.write(buf, 0, bytes-1); // exclude the EOD token
250 break;
251 }
252 // no EOD, so collect it and read more
253 os.write(buf, 0, bytes);
254 }
255 } while (bytes > 0);
256
257 // Okay, we've got the report.
258 if (DEBUG) Slog.v(TAG, "processing");
259
260 // Mark the process record as being a native crash so that the
261 // cleanup mechanism knows we're still submitting the report
262 // even though the process will vanish as soon as we let
263 // debuggerd proceed.
264 synchronized (mAm) {
265 pr.crashing = true;
266 pr.forceCrashReport = true;
267 }
268
269 // Crash reporting is synchronous but we want to let debuggerd
270 // go about it business right away, so we spin off the actual
271 // reporting logic on a thread and let it take it's time.
272 final String reportString = new String(os.toByteArray(), "UTF-8");
273 (new NativeCrashReporter(pr, signal, reportString)).start();
274 } else {
275 Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
276 }
277 } else {
278 Slog.e(TAG, "Bogus pid!");
279 }
280 } catch (Exception e) {
281 Slog.e(TAG, "Exception dealing with report", e);
282 // ugh, fail.
283 }
284 }
285
286}