Androidシステムサービスデッドロック、ANR検出メカニズム

22247 ワード

Androidシステムの稼働後、System_サーバでは、何百ものスレッドが実行されている可能性があります.さまざまなサービス間で頻繁に呼び出され、複雑で、デッドロックや長時間応答していない問題が発生するのは避けられません.この問題はシステムにとって非常に深刻で、このような状況が発生すると、一連の合併症を招き、最終的にはインターフェースのカード死を招き、携帯電話の消費電力が急激に上昇し、発熱が深刻になるからだ.もちろん、私たちがしなければならない最初のステップは、このような状況をできるだけ避けることです.このようなテストと実践が必要です.Androidシステムは今よくやっていますが、このような状況が発生したら、システムの処理も考えなければなりません.本稿では主にframework層Watchdog,anr検出,処理に関する知識をレビューする.
Watchdog検出原理
watchdogは主にシステムの重要なサービスを検出し、処理し、ソースコードの観点からどのように実現されているかを分析します.watchdogはまずそれ自体がスレッドであり、Threadに継承され、system_server初期化中に起動します.
    private Watchdog() {
        super("watchdog");

        // The shared foreground thread is the main checker.  It is where we
        // will also dispatch monitor checks and do other work.
        mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
                "foreground thread", DEFAULT_TIMEOUT);
        mHandlerCheckers.add(mMonitorChecker);
        // Add checker for main thread.  We only do a quick check since there
        // can be UI running on the thread.
        mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
                "main thread", DEFAULT_TIMEOUT));
        // Add checker for shared UI thread.
        mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
                "ui thread", DEFAULT_TIMEOUT));
        // And also check IO thread.
        mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
                "i/o thread", DEFAULT_TIMEOUT));
        // And the display thread.
        mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
                "display thread", DEFAULT_TIMEOUT));
    }

まず、初期化の過程で、いくつかの重要なスレッドをmHandlerCheckersに追加します.これらのスレッドはすべてイベント駆動スレッドであり、HandlerThreadに継承され、HandlerChecker自体はRunnableオブジェクトです.フロントスレッドも最も主要な検出者であり,外部サービスにmonitor checkを追加することはmMonitor Checkerに追加される.
    public void addMonitor(Monitor monitor) {
        synchronized (this) {
            if (isAlive()) {
                throw new RuntimeException("Monitors can't be added once the Watchdog is running");
            }
            mMonitorChecker.addMonitor(monitor);
        }
    }

次にWatchdogが実行された後に何をしたかを見てみましょう.
 @Override
    public void run() {
        boolean waitedHalf = false;
        while (true) {
            final ArrayList blockedCheckers;
            final String subject;
            final boolean allowRestart;  //     ,     ,        
            int debuggerWasConnected = 0;
            synchronized (this) {
                long timeout = CHECK_INTERVAL; // 30s
                //          HandlerCheckers scheduleCheckLocked  
                //HandlerChecker       Handler  ,Handler     Looper
                for (int i=0; i 0) {
                    debuggerWasConnected--;
                }

                //      
                long start = SystemClock.uptimeMillis();
                while (timeout > 0) {
                    if (Debug.isDebuggerConnected()) {
                        debuggerWasConnected = 2;
                    }
                    try {
                        wait(timeout);  //  30s
                    } catch (InterruptedException e) {
                        Log.wtf(TAG, e);
                    }
                    if (Debug.isDebuggerConnected()) {
                        debuggerWasConnected = 2;
                    }
                    timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
                }

                //        ,waitState       HandlerCheck    
                final int waitState = evaluateCheckerCompletionLocked();
                if (waitState == COMPLETED) { //         ,    
                    // The monitors have returned; reset
                    waitedHalf = false;
                    continue;
                } else if (waitState == WAITING) {//     
                    // still waiting but within their configured intervals; back off and recheck
                    continue;
                } else if (waitState == WAITED_HALF) { 
                    //  30s HandleCheck    ,   native    
                    if (!waitedHalf) {
                        // We've waited half the deadlock-detection interval.  Pull a stack
                        // trace and wait another half.
                        ArrayList pids = new ArrayList();
                        pids.add(Process.myPid());
                        ActivityManagerService.dumpStackTraces(true, pids, null, null,
                                NATIVE_STACKS_OF_INTEREST);
                        waitedHalf = true;
                    }
                    continue;
                }

                //  1       ,     HandlerChecker   。
                blockedCheckers = getBlockedCheckersLocked();
                //           
                subject = describeCheckersLocked(blockedCheckers);
                allowRestart = mAllowRestart;
            }

            //   EventLog 
            EventLog.writeEvent(EventLogTags.WATCHDOG, subject);

            ArrayList pids = new ArrayList();
            pids.add(Process.myPid());
            if (mPhonePid > 0) pids.add(mPhonePid);
            //    native      
            final File stack = ActivityManagerService.dumpStackTraces(
                    !waitedHalf, pids, null, null, NATIVE_STACKS_OF_INTEREST);

            //    
            SystemClock.sleep(2000);

            //  kernel        
            if (RECORD_KERNEL_THREADS) {
                dumpKernelStackTraces();
            }

            //  kernel             
            try {
                FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger");
                sysrq_trigger.write("w");
                sysrq_trigger.close();
            } catch (IOException e) {
                Slog.e(TAG, "Failed to write to /proc/sysrq-trigger");
                Slog.e(TAG, e.getMessage());
            }

            //         dropbox  (data/system/dropbox)
            Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                    public void run() {
                        mActivity.addErrorToDropBox(
                                "watchdog", null, "system_server", null, null,
                                subject, null, stack, null);
                    }
                };
            dropboxThread.start();
            try {
                dropboxThread.join(2000);  // wait up to 2 seconds for it to return.
            } catch (InterruptedException ignored) {}

            //...

            //          allowRestart false    ,       
            if (debuggerWasConnected >= 2) {
                Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
            } else if (debuggerWasConnected > 0) {
                Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");
            } else if (!allowRestart) {
                Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
            } else {
                Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
                for (int i=0; i

前のメソッドのコードが多いので、3つのことをしました.
  • HandlerCheckerのscheduleCheckLockedメソッドを実行し、handlerが参照するlooperオブジェクトを介して、対応するスレッドのメッセージキューに自分自身を投げ込み、デッドロック検出を実行する.
  • whileサイクルでは、30 sを過ぎるごとにHandlerCheckerの検出結果が表示され、詰まっていなければ新規から、詰まっていれば3ステップ目に進みます.
  • 渋滞スレッド呼び出しスタックを印刷し、kernel渋滞スレッドスタック、コアnativeプロセスdump情報を含む各種ログを収集し、持続化し、最後に自分を殺し、initプロセスを再起動させる.

  • 次に、次のステップ、次のステップでそれぞれ何をしたかを学びます.
            public void scheduleCheckLocked() {
                if (mMonitors.size() == 0 && mHandler.getLooper().isIdling()) {
                    mCompleted = true;
                    return;
                }
    
                if (!mCompleted) {
                    // we already have a check in flight, so no need
                    return;
                }
    
                mCompleted = false;
                mCurrentMonitor = null;
                mStartTime = SystemClock.uptimeMillis();
                //     MessageQueue 
                mHandler.postAtFrontOfQueue(this);
            }
    
            //             ,  
            @Override
            public void run() {
                final int size = mMonitors.size();
                for (int i = 0 ; i < size ; i++) {
                    synchronized (Watchdog.this) {
                        mCurrentMonitor = mMonitors.get(i);
                    }
                    //        Monitor.monitor  
                    mCurrentMonitor.monitor();
                }
    
                //        ,     ,        。
                synchronized (Watchdog.this) {
                    mCompleted = true;
                    mCurrentMonitor = null;
                }
            }
    
    //     AMS   ,             。
    public final class ActivityManagerService extends ActivityManagerNative
            implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ...
         //      ,         ,      AMS   ,      AMS    “ ”
         public void monitor() {
            synchronized (this) { }
        }
        ...
    }
    
    

    前述の解析によれば、Watchdogは、HandlerCheckerのscheduleCheckLocked()メソッドを実行した後、30 s待ち、getBlockedCheckersLockedメソッドを実行する.
        private ArrayList getBlockedCheckersLocked() {
            ArrayList checkers = new ArrayList();
            for (int i=0; i mStartTime + mWaitMax);
        }
    
    

    ANR検出メカニズムと処理
    まずAndroidシステムがanrをトリガーする状況を見てみましょう.
  • フロントサービス20 s内実行完了
  • フロントブロードキャスト10 s内は実行完了、バックグラウンドブロードキャスト20 s内は実行完了していない
  • .
  • コンテンツプロバイダはpublishProviderを実行し、タイムアウト10 s
  • 入力イベントタイムアウト5 s
  • システムがAnrをトリガする原因は主にタイムアウトに影響し,ユーザの使用と体験に影響するためである.システムがanrをトリガする場所はいくつかあるが、検出メカニズムと処理メカニズムは実際には差が少なく、いずれも操作前に時間を記録し、メッセージキューにAnrをトリガしたメッセージを捨て、応答した操作を実行したときにメッセージを除去し、指定した時間を超えて除去しなければanr操作をトリガし、次にブロードキャストがタイムアウトをトリガしてanrをトリガする過程を具体的に見る.
    private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null, true);
        }
    
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                    //  intent  
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                        TAG, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break;
                    //      
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
    };
    
    //       
    int recIdx = r.nextReceiver++;
    
    //      
    r.receiverTime = SystemClock.uptimeMillis();
    if (recIdx == 0) {
        r.dispatchTime = r.receiverTime;
        r.dispatchClockTime = System.currentTimeMillis();
        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast ["
                                          + mQueueName + "] " + r);
    }
    if (! mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mTimeoutPeriod;
        if (DEBUG_BROADCAST) Slog.v(TAG,
                                    "Submitting BROADCAST_TIMEOUT_MSG ["
                                    + mQueueName + "] for " + r + " at " + timeoutTime);
        //        Anr       
        setBroadcastTimeoutLocked(timeoutTime);
    }
    
    final void setBroadcastTimeoutLocked(long timeoutTime) {
        if (! mPendingBroadcastTimeoutMessage) {
            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
            mHandler.sendMessageAtTime(msg, timeoutTime);
            mPendingBroadcastTimeoutMessage = true;
        }
    }
    

    次に、Anrトリガをキャンセルする遅延メッセージコードを示します.
    if (r.receivers == null || r.nextReceiver >= numReceivers
        || r.resultAbort || forceReceive) {
        // No more receivers for this broadcast!  Send the final
        // result if requested...
        if (r.resultTo != null) {
            try {
                if (DEBUG_BROADCAST) {
                    int seq = r.intent.getIntExtra("seq", -1);
                    Slog.i(TAG, "Finishing broadcast ["
                           + mQueueName + "] " + r.intent.getAction()
                           + " seq=" + seq + " app=" + r.callerApp);
                }
                //    
                performReceiveLocked(r.callerApp, r.resultTo,
                                     new Intent(r.intent), r.resultCode,
                                     r.resultData, r.resultExtras, false, false, r.userId);
                // Set this to null so that the reference
                // (local and remote) isn't kept in the mBroadcastHistory.
                r.resultTo = null;
            } catch (RemoteException e) {
                r.resultTo = null;
                Slog.w(TAG, "Failure ["
                       + mQueueName + "] sending broadcast result of "
                       + r.intent, e);
            }
        }
    
        //     ,    
        cancelBroadcastTimeoutLocked();
    
        // ... and on to the next...
        addBroadcastToHistoryLocked(r);
        mOrderedBroadcasts.remove(0);
        r = null;
        looped = true;
        continue;
    }
    } while (r == null);
    
    //    
    final void cancelBroadcastTimeoutLocked() {
        if (mPendingBroadcastTimeoutMessage) {
            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
            mPendingBroadcastTimeoutMessage = false;
        }
    }
    

    ブロードキャストイベントを処理した後、anrトリガの遅延メッセージをすぐに削除すると、anrはトリガーされません.次に、削除されていません.anrトリガの後、システムがどのようなことをしたかを見てみましょう.
    //      AMS  appNotResponding      anr
    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
                ActivityRecord parent, boolean aboveSystem, final String annotation) {
            ArrayList firstPids = new ArrayList(5);
            SparseArray lastPids = new SparseArray(20);
    
            //        ,  res -1       ,0       
            if (mController != null) {
                try {
                    // 0 == continue, -1 = kill process immediately
                    int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
                    if (res < 0 && app.pid != MY_PID) {
                        app.kill("anr", true);
                    }
                } catch (RemoteException e) {
                    mController = null;
                    Watchdog.getInstance().setActivityController(null);
                }
            }
            //    cpu  
            long anrTime = SystemClock.uptimeMillis();
            if (MONITOR_CPU_USAGE) {
                updateCpuStatsNow();
            }
    
            synchronized (this) {
                // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
                if (mShuttingDown) {
                    Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
                    return;
                } else if (app.notResponding) {
                    Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
                    return;
                } else if (app.crashing) {
                    Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
                    return;
                }
    
                // In case we come through here for the same app before completing
                // this one, mark as anring now so we will bail out.
                app.notResponding = true;
    
                //         
                EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                        app.processName, app.info.flags, annotation);
    
                //  firstPids   stacks
                //      anr  ,    system_server,    mLruProcesses  
                //persistent  
                firstPids.add(app.pid);
    
                int parentPid = app.pid;
                if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
                if (parentPid != app.pid) firstPids.add(parentPid);
    
                if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
    
                for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
                    ProcessRecord r = mLruProcesses.get(i);
                    if (r != null && r.thread != null) {
                        int pid = r.pid;
                        if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
                            if (r.persistent) {
                                firstPids.add(pid);
                            } else {
                                lastPids.put(pid, Boolean.TRUE);
                            }
                        }
                    }
                }
            }
    
            //    
            StringBuilder info = new StringBuilder();
            info.setLength(0);
            info.append("ANR in ").append(app.processName);
            if (activity != null && activity.shortComponentName != null) {
                info.append(" (").append(activity.shortComponentName).append(")");
            }
            info.append("
    "); info.append("PID: ").append(app.pid).append("
    "); if (annotation != null) { info.append("Reason: ").append(annotation).append("
    "); } if (parent != null && parent != activity) { info.append("Parent: ").append(parent.shortComponentName).append("
    "); } // cpu final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); // traces File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, NATIVE_STACKS_OF_INTEREST); String cpuInfo = null; if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); synchronized (mProcessCpuTracker) { cpuInfo = mProcessCpuTracker.printCurrentState(anrTime); } info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } info.append(processCpuTracker.printCurrentState(anrTime)); Slog.e(TAG, info.toString()); if (tracesFile == null) { // traces , signal 3 Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } // dropbox addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null); if (mController != null) { try { // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately int res = mController.appNotResponding(app.processName, app.pid, info.toString()); if (res != 0) { if (res < 0 && app.pid != MY_PID) { app.kill("anr", true); } else { synchronized (this) { mServices.scheduleServiceTimeoutLocked(app); } } return; } } catch (RemoteException e) { mController = null; Watchdog.getInstance().setActivityController(null); } } // anr boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; synchronized (this) { if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { app.kill("bg anr", true); return; } // Set the app's notResponding state, and look up the errorReportReceiver makeAppNotRespondingLocked(app, activity != null ? activity.shortComponentName : null, annotation != null ? "ANR " + annotation : "ANR", info.toString()); // Bring up the infamous App Not Responding dialog Message msg = Message.obtain(); HashMap map = new HashMap(); msg.what = SHOW_NOT_RESPONDING_MSG; msg.obj = map; msg.arg1 = aboveSystem ? 1 : 0; map.put("app", app); if (activity != null) { map.put("activity", activity); } mHandler.sendMessage(msg); } }

    まとめ
    上記では、コア・サービスのデッドロック問題に対するシステムの検出と処理、およびアプリケーション・レイヤのいくつかのシナリオでの動作に時間がかかりすぎてanrをトリガし、処理を主に分析した.では、私たちは以下の知識を学びました.
  • コードでスレッドデッドロックの問題を検出する方法.
  • スレッドに対して、あるイベントがタイムアウトしたかどうかを判断する方法を実行する.
  • /data/anr/traces.txtファイルとdropboxディレクトリの下のファイルには、詳細なログ
  • が記録されています.
    システムレベルの開発を行う学生は、Native、anr、crashを含むAMSで処理されるシステムのほとんどの異常を処理するモジュールをシステムに追加することができます.必要に応じて、プロファイルを構成し、必要なログを動的にフィルタリングし、保存し、アプリケーション層にインタフェースを提供することができます.プラットフォームは、条件フィルタ(時間、例外タイプなど)を使用してログを選択してアップロードできます.
    アプリケーション層の学生はオンライン監視をしたいので、FileObserverを使って/data/tracesを検出することができます.txt、またはシステム実装のように、指定されたイベントの実行時間に対してアップロードを監視する.それ以外はtxtファイルを分析するほか、いくつかのツールを使用して問題の調査に協力することもできます.
  • jstack javaプロセスのスタック状態を表示し、各スレッドの実行状態を表示し、デッドロック問題
  • を確認します.
  • topプロセス/スレッドが占めるcpuを表示し、メモリサイズ
  • meminfoプロセスメモリの使用状況、Android特定オブジェクトの生存数など
  • traceViewスレッドの消費時間とcpu占有率
  • を表示