blob: e2870d8a0758311118b75375512b100ea0c54d76 [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
Christopher Tate404fd652016-04-22 15:27:10 -070052 // on hard errors, just in case debuggerd goes out to lunch.
53 static final long SOCKET_TIMEOUT_MILLIS = 10000; // 10 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) {
Christopher Tate58d380d2013-03-19 13:10:03 -0700126 FileDescriptor peerFd = null;
127 try {
Christopher Tate1b645982013-04-16 16:08:12 -0700128 if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
Neil Fullere64f3e32015-07-15 17:38:28 +0100129 peerFd = Os.accept(serverFd, null /* peerAddress */);
Christopher Tate1b645982013-04-16 16:08:12 -0700130 if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
Christopher Tate58d380d2013-03-19 13:10:03 -0700131 if (peerFd != null) {
132 // Only the superuser is allowed to talk to us over this socket
133 StructUcred credentials =
Elliott Hughes02e85742014-04-24 23:59:05 +0000134 Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
Christopher Tate58d380d2013-03-19 13:10:03 -0700135 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 {
Elliott Hughes02e85742014-04-24 23:59:05 +0000148 Os.write(peerFd, ackSignal, 0, 1);
Christopher Tate1b645982013-04-16 16:08:12 -0700149 } 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 }
Nick Kralevich1b2d5952013-06-18 17:56:28 -0700155 try {
Elliott Hughes02e85742014-04-24 23:59:05 +0000156 Os.close(peerFd);
Nick Kralevich1b2d5952013-06-18 17:56:28 -0700157 } catch (ErrnoException e) {
158 if (MORE_DEBUG) {
159 Slog.d(TAG, "Exception closing socket: " + e.getMessage());
160 }
161 }
Christopher Tate58d380d2013-03-19 13:10:03 -0700162 }
163 }
164 }
165 } catch (Exception e) {
166 Slog.e(TAG, "Unable to init native debug socket!", e);
167 }
168 }
169
170 static int unpackInt(byte[] buf, int offset) {
171 int b0, b1, b2, b3;
172
173 b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension
174 b1 = ((int) buf[offset+1]) & 0xFF;
175 b2 = ((int) buf[offset+2]) & 0xFF;
176 b3 = ((int) buf[offset+3]) & 0xFF;
177 return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
178 }
179
180 static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
Neil Fullerd720ef52014-04-23 16:48:34 +0000181 throws ErrnoException, InterruptedIOException {
Christopher Tate58d380d2013-03-19 13:10:03 -0700182 int totalRead = 0;
183 while (numBytes > 0) {
Elliott Hughes02e85742014-04-24 23:59:05 +0000184 int n = Os.read(fd, buffer, offset + totalRead, numBytes);
Christopher Tate58d380d2013-03-19 13:10:03 -0700185 if (n <= 0) {
186 if (DEBUG) {
187 Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
188 }
189 return -1; // premature EOF or timeout
190 }
191 numBytes -= n;
192 totalRead += n;
193 }
194 return totalRead;
195 }
196
197 // Read the crash report from the debuggerd connection
198 void consumeNativeCrashData(FileDescriptor fd) {
Christopher Tate1b645982013-04-16 16:08:12 -0700199 if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
Christopher Tate58d380d2013-03-19 13:10:03 -0700200 final byte[] buf = new byte[4096];
201 final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
202
203 try {
204 StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
Elliott Hughes02e85742014-04-24 23:59:05 +0000205 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
206 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
Christopher Tate58d380d2013-03-19 13:10:03 -0700207
208 // first, the pid and signal number
209 int headerBytes = readExactly(fd, buf, 0, 8);
210 if (headerBytes != 8) {
211 // protocol failure; give up
212 Slog.e(TAG, "Unable to read from debuggerd");
213 return;
214 }
215
216 int pid = unpackInt(buf, 0);
217 int signal = unpackInt(buf, 4);
218 if (DEBUG) {
219 Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
220 }
221
222 // now the text of the dump
223 if (pid > 0) {
224 final ProcessRecord pr;
225 synchronized (mAm.mPidsSelfLocked) {
226 pr = mAm.mPidsSelfLocked.get(pid);
227 }
228 if (pr != null) {
Christopher Tateb86d81d2013-04-19 17:36:54 -0700229 // Don't attempt crash reporting for persistent apps
230 if (pr.persistent) {
231 if (DEBUG) {
232 Slog.v(TAG, "Skipping report for persistent app " + pr);
233 }
234 return;
235 }
236
Christopher Tate58d380d2013-03-19 13:10:03 -0700237 int bytes;
238 do {
239 // get some data
Elliott Hughes02e85742014-04-24 23:59:05 +0000240 bytes = Os.read(fd, buf, 0, buf.length);
Christopher Tate58d380d2013-03-19 13:10:03 -0700241 if (bytes > 0) {
Christopher Tate1b645982013-04-16 16:08:12 -0700242 if (MORE_DEBUG) {
Christopher Tate58d380d2013-03-19 13:10:03 -0700243 String s = new String(buf, 0, bytes, "UTF-8");
244 Slog.v(TAG, "READ=" + bytes + "> " + s);
245 }
246 // did we just get the EOD null byte?
247 if (buf[bytes-1] == 0) {
248 os.write(buf, 0, bytes-1); // exclude the EOD token
249 break;
250 }
251 // no EOD, so collect it and read more
252 os.write(buf, 0, bytes);
253 }
254 } while (bytes > 0);
255
256 // Okay, we've got the report.
257 if (DEBUG) Slog.v(TAG, "processing");
258
259 // Mark the process record as being a native crash so that the
260 // cleanup mechanism knows we're still submitting the report
261 // even though the process will vanish as soon as we let
262 // debuggerd proceed.
263 synchronized (mAm) {
264 pr.crashing = true;
265 pr.forceCrashReport = true;
266 }
267
268 // Crash reporting is synchronous but we want to let debuggerd
269 // go about it business right away, so we spin off the actual
270 // reporting logic on a thread and let it take it's time.
271 final String reportString = new String(os.toByteArray(), "UTF-8");
272 (new NativeCrashReporter(pr, signal, reportString)).start();
273 } else {
274 Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
275 }
276 } else {
277 Slog.e(TAG, "Bogus pid!");
278 }
279 } catch (Exception e) {
280 Slog.e(TAG, "Exception dealing with report", e);
281 // ugh, fail.
282 }
283 }
284
285}