ActivityManagerService native crash處理流程

在@AMS中會註冊接收native crash的監聽器, 以便在native進程crash時清理恢復app的生命週期等事項。
開機AMS在啓動時註冊監聽:

mActivityManagerService.systemReady(new Runnable() {
            @Override
            public void run() {
                Slog.i(TAG, "Making services ready");
                try {
                    mActivityManagerService.startObservingNativeCrashes();
                } catch (Throwable e) {
                    reportWtf("observing native crashes", e);
                }
...
    public void startObservingNativeCrashes() {
        final NativeCrashListener ncl = new NativeCrashListener(this);
        ncl.start();
    }

NativeCrashListener 線程開始不斷監聽debugger的socket:/data/system/ndebugsocket

    // Must match the path defined in debuggerd.c.-->tombstones.cpp
    static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
 final class NativeCrashListener extends Threadpublic void run() {
        final byte[] ackSignal = new byte[1];

        if (DEBUG) Slog.i(TAG, "Starting up");

        // The file system entity for this socket is created with 0700 perms, owned
        // by system:system.  debuggerd runs as root, so is capable of connecting to
        // it, but 3rd party apps cannot.
        {
            File socketFile = new File(DEBUGGERD_SOCKET_PATH);
            if (socketFile.exists()) {
                socketFile.delete();
            }
        }

        try {
            FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
            final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH);
            Os.bind(serverFd, sockAddr, 0);
            Os.listen(serverFd, 1);

            while (true) {
                InetSocketAddress peer = new InetSocketAddress();
                FileDescriptor peerFd = null;
                try {
                    if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
                    peerFd = Os.accept(serverFd, peer);
                    if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
                    if (peerFd != null) {
                        // Only the superuser is allowed to talk to us over this socket
                        StructUcred credentials =
                                Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
                        if (credentials.uid == 0) {
                            // the reporting thread may take responsibility for
                            // acking the debugger; make sure we play along.
                            consumeNativeCrashData(peerFd);
                        }
                    }
                } catch (Exception e) {
                    Slog.w(TAG, "Error handling connection", e);
                } finally {
                    // Always ack debuggerd's connection to us.  The actual
                    // byte written is irrelevant.
                    if (peerFd != null) {
                        try {
                            Os.write(peerFd, ackSignal, 0, 1);
                        } catch (Exception e) {
                            /* we don't care about failures here */
                            if (MORE_DEBUG) {
                                Slog.d(TAG, "Exception writing ack: " + e.getMessage());
                            }
                        }
                        try {
                            Os.close(peerFd);
                        } catch (ErrnoException e) {
                            if (MORE_DEBUG) {
                                Slog.d(TAG, "Exception closing socket: " + e.getMessage());
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Slog.e(TAG, "Unable to init native debug socket!", e);
        }
    }

有從debuggerd收到socket數據時,處理crash數據:

// Read the crash report from the debuggerd connection
    void consumeNativeCrashData(FileDescriptor fd) {
        if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
        final byte[] buf = new byte[4096];
        final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);

        try {
            StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);

            // first, the pid and signal number
            int headerBytes = readExactly(fd, buf, 0, 8);
            if (headerBytes != 8) {
                // protocol failure; give up
                Slog.e(TAG, "Unable to read from debuggerd");
                return;
            }

            int pid = unpackInt(buf, 0);
            int signal = unpackInt(buf, 4);
            if (DEBUG) {
                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
            }

            // now the text of the dump
            if (pid > 0) {
                final ProcessRecord pr;
                synchronized (mAm.mPidsSelfLocked) {
                    pr = mAm.mPidsSelfLocked.get(pid);
                }
                if (pr != null) {
                    // Don't attempt crash reporting for persistent apps
                    if (pr.persistent) {
                        if (DEBUG) {
                            Slog.v(TAG, "Skipping report for persistent app " + pr);
                        }
                        return;
                    }

                    int bytes;
                    do {
                        // get some data
                        bytes = Os.read(fd, buf, 0, buf.length);
                        if (bytes > 0) {
                            if (MORE_DEBUG) {
                                String s = new String(buf, 0, bytes, "UTF-8");
                                Slog.v(TAG, "READ=" + bytes + "> " + s);
                            }
                            // did we just get the EOD null byte?
                            if (buf[bytes-1] == 0) {
                                os.write(buf, 0, bytes-1);  // exclude the EOD token
                                break;
                            }
                            // no EOD, so collect it and read more
                            os.write(buf, 0, bytes);
                        }
                    } while (bytes > 0);

                    // Okay, we've got the report.
                    if (DEBUG) Slog.v(TAG, "processing");

                    // Mark the process record as being a native crash so that the
                    // cleanup mechanism knows we're still submitting the report
                    // even though the process will vanish as soon as we let
                    // debuggerd proceed.
                    synchronized (mAm) {
                        pr.crashing = true;
                        pr.forceCrashReport = true;
                    }

                    // Crash reporting is synchronous but we want to let debuggerd
                    // go about it business right away, so we spin off the actual
                    // reporting logic on a thread and let it take it's time.
                    final String reportString = new String(os.toByteArray(), "UTF-8");
                    (new NativeCrashReporter(pr, signal, reportString)).start();
                } else {
                    Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
                }
            } else {
                Slog.e(TAG, "Bogus pid!");
            }
        } catch (Exception e) {
            Slog.e(TAG, "Exception dealing with report", e);
            // ugh, fail.
        }

新開thread處理crash具體數據,讓AMS作對應處理:

class NativeCrashReporter extends Thread :

        public void run() {
            try {
                CrashInfo ci = new CrashInfo();
                ci.exceptionClassName = "Native crash";
                ci.exceptionMessage = Os.strsignal(mSignal);
                ci.throwFileName = "unknown";
                ci.throwClassName = "unknown";
                ci.throwMethodName = "unknown";
                ci.stackTrace = mCrashReport;

                if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
                mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
                if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
            } catch (Exception e) {
                Slog.e(TAG, "Unable to report native crash", e);
            }
        }

@AMS處理,

    /* Native crash reporting uses this inner version because it needs to be somewhat
     * decoupled from the AM-managed cleanup lifecycle
     */
    void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
            ApplicationErrorReport.CrashInfo crashInfo) {
        EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
                UserHandle.getUserId(Binder.getCallingUid()), processName,
                r == null ? -1 : r.info.flags,
                crashInfo.exceptionClassName,
                crashInfo.exceptionMessage,
                crashInfo.throwFileName,
                crashInfo.throwLineNumber);

        addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);

        crashApplication(r, crashInfo);
    }

native 部分:
開機啓動時會開啓debuggerd監控進程接收native進程異常信號量,如SIGSTOP,SIGABRT,SIGSEGV等,native處理完成後,即會往上面AMS裏介紹的final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH)這個socket通知,native詳細流程可以參考,
http://www.2cto.com/kf/201408/323895.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章