Home | History | Annotate | Download | only in am
      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 
     17 package com.android.server.am;
     18 
     19 import android.app.ApplicationErrorReport.CrashInfo;
     20 import android.util.Slog;
     21 
     22 import libcore.io.ErrnoException;
     23 import libcore.io.Libcore;
     24 import libcore.io.StructTimeval;
     25 import libcore.io.StructUcred;
     26 
     27 import static libcore.io.OsConstants.*;
     28 
     29 import java.io.ByteArrayOutputStream;
     30 import java.io.File;
     31 import java.io.FileDescriptor;
     32 import java.net.InetSocketAddress;
     33 import 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  */
     43 class 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