blob: 6e42aee3ba2aa4885ad5577fc611b8e59998472f [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;
Neil Fullere9d53752015-07-07 16:58:00 +010023import android.system.UnixSocketAddress;
Christopher Tate58d380d2013-03-19 13:10:03 -070024import android.util.Slog;
25
Elliott Hughes02e85742014-04-24 23:59:05 +000026import static android.system.OsConstants.*;
Christopher Tate58d380d2013-03-19 13:10:03 -070027
28import java.io.ByteArrayOutputStream;
29import java.io.File;
30import java.io.FileDescriptor;
Neil Fullerd720ef52014-04-23 16:48:34 +000031import java.io.InterruptedIOException;
Christopher Tate58d380d2013-03-19 13:10:03 -070032
33/**
34 * Set up a Unix domain socket that debuggerd will connect() to in
35 * order to write a description of a native crash. The crash info is
36 * then parsed and forwarded to the ActivityManagerService's normal
37 * crash handling code.
38 *
39 * Note that this component runs in a separate thread.
40 */
Dianne Hackbornbe4e6aa2013-06-07 13:25:29 -070041final class NativeCrashListener extends Thread {
Christopher Tate58d380d2013-03-19 13:10:03 -070042 static final String TAG = "NativeCrashListener";
43 static final boolean DEBUG = false;
Christopher Tate1b645982013-04-16 16:08:12 -070044 static final boolean MORE_DEBUG = DEBUG && false;
Christopher Tate58d380d2013-03-19 13:10:03 -070045
46 // Must match the path defined in debuggerd.c.
47 static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
48
49 // Use a short timeout on socket operations and abandon the connection
Christopher Tate404fd652016-04-22 15:27:10 -070050 // on hard errors, just in case debuggerd goes out to lunch.
51 static final long SOCKET_TIMEOUT_MILLIS = 10000; // 10 seconds
Christopher Tate58d380d2013-03-19 13:10:03 -070052
53 final ActivityManagerService mAm;
54
55 /*
56 * Spin the actual work of handling a debuggerd crash report into a
57 * separate thread so that the listener can go immediately back to
58 * accepting incoming connections.
59 */
60 class NativeCrashReporter extends Thread {
61 ProcessRecord mApp;
62 int mSignal;
63 String mCrashReport;
64
65 NativeCrashReporter(ProcessRecord app, int signal, String report) {
66 super("NativeCrashReport");
67 mApp = app;
68 mSignal = signal;
69 mCrashReport = report;
70 }
71
72 @Override
73 public void run() {
74 try {
75 CrashInfo ci = new CrashInfo();
76 ci.exceptionClassName = "Native crash";
Elliott Hughes02e85742014-04-24 23:59:05 +000077 ci.exceptionMessage = Os.strsignal(mSignal);
Christopher Tate58d380d2013-03-19 13:10:03 -070078 ci.throwFileName = "unknown";
79 ci.throwClassName = "unknown";
80 ci.throwMethodName = "unknown";
81 ci.stackTrace = mCrashReport;
82
83 if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
Eric Rowe88d842c2013-04-08 15:00:27 -070084 mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
Christopher Tate58d380d2013-03-19 13:10:03 -070085 if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
86 } catch (Exception e) {
87 Slog.e(TAG, "Unable to report native crash", e);
88 }
89 }
90 }
91
92 /*
93 * Daemon thread that accept()s incoming domain socket connections from debuggerd
94 * and processes the crash dump that is passed through.
95 */
Jeff Brown4ccb8232014-01-16 22:16:42 -080096 NativeCrashListener(ActivityManagerService am) {
97 mAm = am;
Christopher Tate58d380d2013-03-19 13:10:03 -070098 }
99
100 @Override
101 public void run() {
102 final byte[] ackSignal = new byte[1];
103
104 if (DEBUG) Slog.i(TAG, "Starting up");
105
Josh Gaob9eb0932016-11-04 15:22:02 -0700106 // The file system entity for this socket is created with 0777 perms, owned
107 // by system:system. selinux restricts things so that only crash_dump can
108 // access it.
Christopher Tate58d380d2013-03-19 13:10:03 -0700109 {
110 File socketFile = new File(DEBUGGERD_SOCKET_PATH);
111 if (socketFile.exists()) {
112 socketFile.delete();
113 }
114 }
115
116 try {
Elliott Hughes02e85742014-04-24 23:59:05 +0000117 FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
Neil Fullere9d53752015-07-07 16:58:00 +0100118 final UnixSocketAddress sockAddr = UnixSocketAddress.createFileSystem(
119 DEBUGGERD_SOCKET_PATH);
120 Os.bind(serverFd, sockAddr);
Elliott Hughes02e85742014-04-24 23:59:05 +0000121 Os.listen(serverFd, 1);
Josh Gaob9eb0932016-11-04 15:22:02 -0700122 Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);
Christopher Tate58d380d2013-03-19 13:10:03 -0700123
124 while (true) {
Christopher Tate58d380d2013-03-19 13:10:03 -0700125 FileDescriptor peerFd = null;
126 try {
Christopher Tate1b645982013-04-16 16:08:12 -0700127 if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
Neil Fullere64f3e32015-07-15 17:38:28 +0100128 peerFd = Os.accept(serverFd, null /* peerAddress */);
Christopher Tate1b645982013-04-16 16:08:12 -0700129 if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
Christopher Tate58d380d2013-03-19 13:10:03 -0700130 if (peerFd != null) {
Josh Gaob9eb0932016-11-04 15:22:02 -0700131 // the reporting thread may take responsibility for
132 // acking the debugger; make sure we play along.
133 consumeNativeCrashData(peerFd);
Christopher Tate58d380d2013-03-19 13:10:03 -0700134 }
135 } catch (Exception e) {
136 Slog.w(TAG, "Error handling connection", e);
137 } finally {
Josh Gaob9eb0932016-11-04 15:22:02 -0700138 // Always ack crash_dump's connection to us. The actual
Christopher Tate58d380d2013-03-19 13:10:03 -0700139 // byte written is irrelevant.
140 if (peerFd != null) {
141 try {
Elliott Hughes02e85742014-04-24 23:59:05 +0000142 Os.write(peerFd, ackSignal, 0, 1);
Christopher Tate1b645982013-04-16 16:08:12 -0700143 } catch (Exception e) {
144 /* we don't care about failures here */
145 if (MORE_DEBUG) {
146 Slog.d(TAG, "Exception writing ack: " + e.getMessage());
147 }
148 }
Nick Kralevich1b2d5952013-06-18 17:56:28 -0700149 try {
Elliott Hughes02e85742014-04-24 23:59:05 +0000150 Os.close(peerFd);
Nick Kralevich1b2d5952013-06-18 17:56:28 -0700151 } catch (ErrnoException e) {
152 if (MORE_DEBUG) {
153 Slog.d(TAG, "Exception closing socket: " + e.getMessage());
154 }
155 }
Christopher Tate58d380d2013-03-19 13:10:03 -0700156 }
157 }
158 }
159 } catch (Exception e) {
160 Slog.e(TAG, "Unable to init native debug socket!", e);
161 }
162 }
163
164 static int unpackInt(byte[] buf, int offset) {
165 int b0, b1, b2, b3;
166
167 b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension
168 b1 = ((int) buf[offset+1]) & 0xFF;
169 b2 = ((int) buf[offset+2]) & 0xFF;
170 b3 = ((int) buf[offset+3]) & 0xFF;
171 return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
172 }
173
174 static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
Neil Fullerd720ef52014-04-23 16:48:34 +0000175 throws ErrnoException, InterruptedIOException {
Christopher Tate58d380d2013-03-19 13:10:03 -0700176 int totalRead = 0;
177 while (numBytes > 0) {
Elliott Hughes02e85742014-04-24 23:59:05 +0000178 int n = Os.read(fd, buffer, offset + totalRead, numBytes);
Christopher Tate58d380d2013-03-19 13:10:03 -0700179 if (n <= 0) {
180 if (DEBUG) {
181 Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
182 }
183 return -1; // premature EOF or timeout
184 }
185 numBytes -= n;
186 totalRead += n;
187 }
188 return totalRead;
189 }
190
Josh Gaob9eb0932016-11-04 15:22:02 -0700191 // Read a crash report from the connection
Christopher Tate58d380d2013-03-19 13:10:03 -0700192 void consumeNativeCrashData(FileDescriptor fd) {
Christopher Tate1b645982013-04-16 16:08:12 -0700193 if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
Christopher Tate58d380d2013-03-19 13:10:03 -0700194 final byte[] buf = new byte[4096];
195 final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
196
197 try {
198 StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
Elliott Hughes02e85742014-04-24 23:59:05 +0000199 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
200 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
Christopher Tate58d380d2013-03-19 13:10:03 -0700201
Josh Gaob9eb0932016-11-04 15:22:02 -0700202 // The socket is guarded by an selinux neverallow rule that only
203 // permits crash_dump to connect to it. This allows us to trust the
204 // received values.
205
Christopher Tate58d380d2013-03-19 13:10:03 -0700206 // first, the pid and signal number
207 int headerBytes = readExactly(fd, buf, 0, 8);
208 if (headerBytes != 8) {
209 // protocol failure; give up
210 Slog.e(TAG, "Unable to read from debuggerd");
211 return;
212 }
213
214 int pid = unpackInt(buf, 0);
215 int signal = unpackInt(buf, 4);
216 if (DEBUG) {
217 Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
218 }
219
220 // now the text of the dump
221 if (pid > 0) {
222 final ProcessRecord pr;
223 synchronized (mAm.mPidsSelfLocked) {
224 pr = mAm.mPidsSelfLocked.get(pid);
225 }
226 if (pr != null) {
Christopher Tateb86d81d2013-04-19 17:36:54 -0700227 // Don't attempt crash reporting for persistent apps
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700228 if (pr.isPersistent()) {
Christopher Tateb86d81d2013-04-19 17:36:54 -0700229 if (DEBUG) {
230 Slog.v(TAG, "Skipping report for persistent app " + pr);
231 }
232 return;
233 }
234
Christopher Tate58d380d2013-03-19 13:10:03 -0700235 int bytes;
236 do {
237 // get some data
Elliott Hughes02e85742014-04-24 23:59:05 +0000238 bytes = Os.read(fd, buf, 0, buf.length);
Christopher Tate58d380d2013-03-19 13:10:03 -0700239 if (bytes > 0) {
Christopher Tate1b645982013-04-16 16:08:12 -0700240 if (MORE_DEBUG) {
Christopher Tate58d380d2013-03-19 13:10:03 -0700241 String s = new String(buf, 0, bytes, "UTF-8");
242 Slog.v(TAG, "READ=" + bytes + "> " + s);
243 }
244 // did we just get the EOD null byte?
245 if (buf[bytes-1] == 0) {
246 os.write(buf, 0, bytes-1); // exclude the EOD token
247 break;
248 }
249 // no EOD, so collect it and read more
250 os.write(buf, 0, bytes);
251 }
252 } while (bytes > 0);
253
254 // Okay, we've got the report.
255 if (DEBUG) Slog.v(TAG, "processing");
256
257 // Mark the process record as being a native crash so that the
258 // cleanup mechanism knows we're still submitting the report
259 // even though the process will vanish as soon as we let
260 // debuggerd proceed.
261 synchronized (mAm) {
Wale Ogunwale9e4f3e02018-05-17 09:35:39 -0700262 pr.setCrashing(true);
Christopher Tate58d380d2013-03-19 13:10:03 -0700263 pr.forceCrashReport = true;
264 }
265
266 // Crash reporting is synchronous but we want to let debuggerd
267 // go about it business right away, so we spin off the actual
268 // reporting logic on a thread and let it take it's time.
269 final String reportString = new String(os.toByteArray(), "UTF-8");
270 (new NativeCrashReporter(pr, signal, reportString)).start();
271 } else {
272 Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
273 }
274 } else {
275 Slog.e(TAG, "Bogus pid!");
276 }
277 } catch (Exception e) {
278 Slog.e(TAG, "Exception dealing with report", e);
279 // ugh, fail.
280 }
281 }
282
283}