blob: a9454bd4a4befb30db22511cc47508db82b55985 [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;
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;
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";
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()");
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 */
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 {
Christopher Tate1b645982013-04-16 16:08:12 -0700128 if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
Christopher Tate58d380d2013-03-19 13:10:03 -0700129 peerFd = Libcore.os.accept(serverFd, peer);
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 =
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);
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 }
Christopher Tate58d380d2013-03-19 13:10:03 -0700155 }
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) {
Christopher Tate1b645982013-04-16 16:08:12 -0700192 if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
Christopher Tate58d380d2013-03-19 13:10:03 -0700193 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) {
Christopher Tateb86d81d2013-04-19 17:36:54 -0700222 // 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
Christopher Tate58d380d2013-03-19 13:10:03 -0700230 int bytes;
231 do {
232 // get some data
233 bytes = Libcore.os.read(fd, buf, 0, buf.length);
234 if (bytes > 0) {
Christopher Tate1b645982013-04-16 16:08:12 -0700235 if (MORE_DEBUG) {
Christopher Tate58d380d2013-03-19 13:10:03 -0700236 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}