Android学习笔记android知识干货系统源码分析利用优化

Android DropboxManager介绍

2016-09-20  本文已影响1577人  Viking_Den

最近工作中需要对Crash的应用进行处理,看了下Android有提供相关的Manager对各类异常进行记录,也就是DropboxManager。

什么是 DropBoxManager ?

Enqueues chunks of data (from various sources – application crashes, kernel log records, etc.). The queue is size bounded and will drop old data if the enqueued data exceeds the maximum size. You can think of this as a persistent, system-wide, blob-oriented “logcat”.DropBoxManager entries are not sent anywhere directly, but other system. services and debugging tools may scan and upload entries for processing

DropBoxManager 是 Android 在 Froyo(API level 8) 引入的用来持续化存储系统数据的机制, 主要用于记录 Android 运行过程中, 内核, 系统进程, 用户进程等出现严重问题时的 log, 可以认为这是一个可持续存储的系统级别的 logcat.

我们可以通过用参数 DROPBOX_SERVICE 调用 getSystemService(String) 来获得这个服务, 并查询出所有存储在 DropBoxManager 里的系统错误记录.

Android 缺省能记录哪些系统错误 ?

具体能记录哪些系统错误,官方的文档中没有提及,我们在DropboxManager.java源代码文件中的EXTRA_TAG(tag)中找到相关信息。

 /**
 * Used by {@link com.android.internal.os.RuntimeInit} to report when an application crashes.
 * The application process will exit immediately after this call returns.
 * @param app object of the crashing app, null for the system server
 * @param crashInfo describing the exception
 */
 public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
       ProcessRecord r = findAppProcess(app, "Crash");
       final String processName = app == null ? "system_server": (r == null ? "unknown" : r.processName);
       handleApplicationCrashInner("crash", r, processName, crashInfo);
 }

 /* 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);
 }
final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) { 
      //...... 
      addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null);
      //......
}
    /**
     * Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
     * @param app object of the crashing app, null for the system server
     * @param tag reported by the caller
     * @param system whether this wtf is coming from the system
     * @param crashInfo describing the context of the error
     * @return true if the process should exit immediately (WTF is fatal)
     */
    public boolean handleApplicationWtf(final IBinder app, final String tag, boolean system,
            final ApplicationErrorReport.CrashInfo crashInfo) {
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        if (system) {
            // If this is coming from the system, we could very well have low-level
            // system locks held, so we want to do this all asynchronously.  And we
            // never want this to become fatal, so there is that too.
            mHandler.post(new Runnable() {
                @Override public void run() {
                    handleApplicationWtfInner(callingUid, callingPid, app, tag, crashInfo);
                }
            });
            return false;
        }
        final ProcessRecord r = handleApplicationWtfInner(callingUid, callingPid, app, tag,
                crashInfo);
        if (r != null && r.pid != Process.myPid() &&
                Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.WTF_IS_FATAL, 0) != 0) {
            crashApplication(r, crashInfo);
            return true;
        } else {
            return false;
        }
    }
    ProcessRecord handleApplicationWtfInner(int callingUid, int callingPid, IBinder app, String tag,
            final ApplicationErrorReport.CrashInfo crashInfo) {
        final ProcessRecord r = findAppProcess(app, "WTF");
        final String processName = app == null ? "system_server"
                : (r == null ? "unknown" : r.processName);
        EventLog.writeEvent(EventLogTags.AM_WTF, UserHandle.getUserId(callingUid), callingPid,
                processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage);

        addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);

        return r;
    }

    public void handleApplicationStrictModeViolation(
            IBinder app,
            int violationMask,
            StrictMode.ViolationInfo info) {
        ProcessRecord r = findAppProcess(app, "StrictMode");
        if (r == null) {
            return;
        }
        if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
            Integer stackFingerprint = info.hashCode();
            boolean logIt = true;
            synchronized (mAlreadyLoggedViolatedStacks) {
                if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
                    logIt = false;
                    // TODO: sub-sample into EventLog for these, with
                    // the info.durationMillis?  Then we'd get
                    // the relative pain numbers, without logging all
                    // the stack traces repeatedly.  We'd want to do
                    // likewise in the client code, which also does
                    // dup suppression, before the Binder call.
                } else {
                    if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {
                        mAlreadyLoggedViolatedStacks.clear();
                    }
                    mAlreadyLoggedViolatedStacks.add(stackFingerprint);
                }
            }
            if (logIt) {
                logStrictModeViolationToDropBox(r, info);
            }
        }
    //......
}
// Depending on the policy in effect, there could be a bunch of
    // these in quick succession so we try to batch these together to
    // minimize disk writes, number of dropbox entries, and maximize
    // compression, by having more fewer, larger records.
    private void logStrictModeViolationToDropBox(
            ProcessRecord process,
            StrictMode.ViolationInfo info) {
        if (info == null) {
            return;
        }
        final boolean isSystemApp = process == null ||
                (process.info.flags & (ApplicationInfo.FLAG_SYSTEM |
                                       ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
        final String processName = process == null ? "unknown" : process.processName;
        final String dropboxTag = isSystemApp ? "system_app_strictmode" : "data_app_strictmode";
        final DropBoxManager dbox = (DropBoxManager)
                mContext.getSystemService(Context.DROPBOX_SERVICE);
        // Exit early if the dropbox isn't configured to accept this report type.
        if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
        boolean bufferWasEmpty;
        boolean needsFlush;
        final StringBuilder sb = isSystemApp ? mStrictModeBuffer : new StringBuilder(1024);
        synchronized (sb) {
            bufferWasEmpty = sb.length() == 0;
            appendDropBoxProcessHeaders(process, processName, sb);
            sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
            sb.append("System-App: ").append(isSystemApp).append("\n");
            sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
            if (info.violationNumThisLoop != 0) {
                sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n");
            }
            if (info.numAnimationsRunning != 0) {
                sb.append("Animations-Running: ").append(info.numAnimationsRunning).append("\n");
            }
            if (info.broadcastIntentAction != null) {
                sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n");
            }
            if (info.durationMillis != -1) {
                sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
            }
            if (info.numInstances != -1) {
                sb.append("Instance-Count: ").append(info.numInstances).append("\n");
            }
            if (info.tags != null) {
                for (String tag : info.tags) {
                    sb.append("Span-Tag: ").append(tag).append("\n");
                }
            }
            sb.append("\n");
            if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
                sb.append(info.crashInfo.stackTrace);
            }
            sb.append("\n");
            // Only buffer up to ~64k.  Various logging bits truncate
            // things at 128k.
            needsFlush = (sb.length() > 64 * 1024);
        }
        // Flush immediately if the buffer's grown too large, or this
        // is a non-system app.  Non-system apps are isolated with a
        // different tag & policy and not batched.
        //
        // Batching is useful during internal testing with
        // StrictMode settings turned up high.  Without batching,
        // thousands of separate files could be created on boot.
        if (!isSystemApp || needsFlush) {
            new Thread("Error dump: " + dropboxTag) {
                @Override
                public void run() {
                    String report;
                    synchronized (sb) {
                        report = sb.toString();
                        sb.delete(0, sb.length());
                        sb.trimToSize();
                    }
                    if (report.length() != 0) {
                        dbox.addText(dropboxTag, report);
                    }
                }
            }.start();
            return;
        }
        // System app batching:
        if (!bufferWasEmpty) {
            // An existing dropbox-writing thread is outstanding, so
            // we don't need to start it up.  The existing thread will
            // catch the buffer appends we just did.
            return;
        }
        // Worker thread to both batch writes and to avoid blocking the caller on I/O.
        // (After this point, we shouldn't access AMS internal data structures.)
        new Thread("Error dump: " + dropboxTag) {
            @Override
            public void run() {
                // 5 second sleep to let stacks arrive and be batched together
                try {
                    Thread.sleep(5000);  // 5 seconds
                } catch (InterruptedException e) {}
                String errorReport;
                synchronized (mStrictModeBuffer) {
                    errorReport = mStrictModeBuffer.toString();
                    if (errorReport.length() == 0) {
                        return;
                    }
                    mStrictModeBuffer.delete(0, mStrictModeBuffer.length());
                    mStrictModeBuffer.trimToSize();
                }
                dbox.addText(dropboxTag, errorReport);
            }
        }.start();
    }
    public void handleMessage(Message msg) {
        switch (msg.what) {
        //...
        case REPORT_MEM_USAGE: {
            //......
            Thread thread = new Thread() {
                @Override public void run() {
                    StringBuilder dropBuilder = new StringBuilder(1024);
                    StringBuilder logBuilder = new StringBuilder(1024);
                    //......
                    addErrorToDropBox("lowmem", null, "system_server", null,
                            null, tag.toString(), dropBuilder.toString(), null, null);
                    //......
                }
            };
            thread.start();
            break;
        }
        //......
    }
/** This class calls its monitor every minute. Killing this process if they don't return **/
public class Watchdog extends Thread {
    //......
    @Override
    public void run() {
        boolean waitedHalf = false;
        while (true) {
            //......

            // If we got here, that means that the system is most likely hung.
            // First collect stack traces from all threads of the system process.
            // Then kill this process so that the system will restart.

            //......

            // Try to add the error to the dropbox, but assuming that the ActivityManager
            // itself may be deadlocked.  (which has happened, causing this statement to
            // deadlock and the watchdog as a whole to be ineffective)
            Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                    public void run() {
                        mActivity.addErrorToDropBox(
                                "watchdog", null, "system_server", null, null,
                                name, null, stack, null);
                    }
                };
            dropboxThread.start();
            try {
                dropboxThread.join(2000);  // wait up to 2 seconds for it to return.
            } catch (InterruptedException ignored) {}

            //......
        }
    }

    //......
}
private void logBootEvents(Context ctx) throws IOException {
    final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);
    final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE);
    final String headers = new StringBuilder(512)
        .append("Build: ").append(Build.FINGERPRINT).append("\n")
        .append("Hardware: ").append(Build.BOARD).append("\n")
        .append("Revision: ")
        .append(SystemProperties.get("ro.revision", "")).append("\n")
        .append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
        .append("Radio: ").append(Build.RADIO).append("\n")
        .append("Kernel: ")
        .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
        .append("\n").toString();

    String recovery = RecoverySystem.handleAftermath();
    if (recovery != null && db != null) {
        db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
    }

    if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
        String now = Long.toString(System.currentTimeMillis());
        SystemProperties.set("ro.runtime.firstboot", now);
        if (db != null) db.addText("SYSTEM_BOOT", headers);

        // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
        addFileToDropBox(db, prefs, headers, "/proc/last_kmsg",
                -LOG_SIZE, "SYSTEM_LAST_KMSG");
        addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
                -LOG_SIZE, "SYSTEM_RECOVERY_LOG");
        addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
                -LOG_SIZE, "APANIC_CONSOLE");
        addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads",
                -LOG_SIZE, "APANIC_THREADS");
    } else {
        if (db != null) db.addText("SYSTEM_RESTART", headers);
    }

    // Scan existing tombstones (in case any new ones appeared)
    File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
    for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
        addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(),
                LOG_SIZE, "SYSTEM_TOMBSTONE");
    }

    // Start watching for new tombstone files; will record them as they occur.
    // This gets registered with the singleton file observer thread.
    sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
        @Override
        public void onEvent(int event, String path) {
            try {
                String filename = new File(TOMBSTONE_DIR, path).getPath();
                addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE");
            } catch (IOException e) {
                Slog.e(TAG, "Can't log tombstone", e);
            }
        }
    };

    sTombstoneObserver.startWatching();
}
$ adb shell ls -l /data/system/dropbox
-rw------- system   system        258 2012-11-21 11:36 SYSTEM_RESTART@1353469017940.txt
-rw------- system   system         39 2012-11-21 11:40 event_data@1353469222884.txt
-rw------- system   system         39 2012-11-21 12:10 event_data@1353471022975.txt
-rw------- system   system         34 2012-11-21 18:10 event_log@1353492624170.txt
-rw------- system   system         34 2012-11-21 18:40 event_log@1353494424296.txt
-rw------- system   system         34 2012-11-22 10:10 event_log@1353550227432.txt
-rw------- system   system       1528 2012-11-21 22:54 system_app_crash@1353509648395.txt
-rw------- system   system       1877 2012-11-21 11:36 system_app_strictmode@1353469014395.txt
-rw------- system   system       3724 2012-11-21 11:36 system_app_strictmode@1353469014924.txt.gz

如何利用 DropBoxManager ?

参考
介绍 Android DropBoxManager Service
Android Official Site
DropBoxManager Overview
ActivityManager Service Overview
Android StrictMode Overview

上一篇 下一篇

猜你喜欢

热点阅读