Android Watchdog分析

2023-11-10

初始化

Watchdog作为一个独立的线程在SystemServer进程中被初始化:

 private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
         // Start the watchdog as early as possible so we can crash the system server
        // if we deadlock during early boot
        t.traceBegin("StartWatchdog");
        final Watchdog watchdog = Watchdog.getInstance();
        watchdog.start();
        t.traceEnd();
 }

Watchdog类没有在定义时实现Runnable接口,但其实现了run()方法,类变量 private final Thread mThread; 在构造器中被初始化,watchdog.start();开始执行此线程。

构造其中添加了"foreground thread",“main thread”,“ui thread”,“i/o thread”,“display thread”,“animation thread”,“surface animation thread”,"BinderThreadMonitor"等HandlerChecker。

    private Watchdog() {
        mThread = new Thread(this::run, "watchdog");
        // Initialize handler checkers for each common thread we want to check.  Note
        // that we are not currently checking the background thread, since it can
        // potentially hold longer running operations with no guarantees about the timeliness
        // of operations there.

        // 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));
        // And the animation thread.
        mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
                "animation thread", DEFAULT_TIMEOUT));
        // And the surface animation thread.
        mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),
                "surface animation thread", DEFAULT_TIMEOUT));

        // Initialize monitor for Binder threads.
        addMonitor(new BinderThreadMonitor());

        mInterestingJavaPids.add(Process.myPid());

        // See the notes on DEFAULT_TIMEOUT.
        assert DB ||
                DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;

        mTraceErrorLogger = new TraceErrorLogger();
    }

AMS, PKMS, WMS等会在自己的构造器中将自己添加到Watchdog的HandlerChecker中:

public ActivityManagerService() {
    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
}
public PackageManagerService() {
    Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
}
public WindowManagerService{
      public void onInitReady() {
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
    }
}
工作流程

Watchdog作为单独执行的线程,在run()方法中循环监测所有HandlerChecker的状态,导出异常进程的运行日志,必要时给当前进程(system_server)发送signal 9,杀掉此进程。

public void run{
        while (true) {
            synchronized (mLock) {
                //1. 遍历所有HandlerChecker
                for (int i=0; i<mHandlerCheckers.size(); i++) {
                    HandlerChecker hc = mHandlerCheckers.get(i);
                    hc.scheduleCheckLocked();
                }

                //2. mLock.wait(timeout);使当前线程处于等待状态,等待时间为timeout = CHECK_INTERVAL: 30s
                // NOTE: We use uptimeMillis() here because we do not want to increment the time we
                // wait while asleep. If the device is asleep then the thing that we are waiting
                // to timeout on is asleep as well and won't have a chance to run, causing a false
                // positive on when to kill things.
                long start = SystemClock.uptimeMillis();
                while (timeout > 0) {
                    Log.d(TAG, "run: timeout = " + timeout);
                    if (Debug.isDebuggerConnected()) {
                        debuggerWasConnected = 2;
                    }
                    try {
                        mLock.wait(timeout);
                        // Note: mHandlerCheckers and mMonitorChecker may have changed after waiting
                    } catch (InterruptedException e) {
                        Log.wtf(TAG, e);
                    }
                    if (Debug.isDebuggerConnected()) {
                        debuggerWasConnected = 2;
                    }
                    timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
                }

                //3. 监测HandlerChecker的完成状态
                final int waitState = evaluateCheckerCompletionLocked();
                if (waitState == COMPLETED) {
                    waitedHalf = false;
                    continue;
                } else if (waitState == WAITING) {
                    continue;
                } else if (waitState == WAITED_HALF) {
                    if (!waitedHalf) {
                        waitedHalf = true;
                        pids = new ArrayList<>(mInterestingJavaPids);
                        doWaitedHalfDump = true;
                    } else {
                        continue;
                    }
                } else {
                    // 存在超时的 HandlerChecker !!!
                    // something is overdue!
                    blockedCheckers = getBlockedCheckersLocked();
                    subject = describeCheckersLocked(blockedCheckers);
                    allowRestart = mAllowRestart;
                    pids = new ArrayList<>(mInterestingJavaPids);
                }
            } // END synchronized (mLock)

            // 4. 导出异常日志 ANR:/data/anr
            final File finalStack = ActivityManagerService.dumpStackTraces(
                    pids, processCpuTracker, new SparseArray<>(), nativePids,
                    tracesFileException, subject);

            // 5. 导出异常日志 dropbox:/data/system/dropbox/
            Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                    public void run() {
                        if (mActivity != null) {
                            mActivity.addErrorToDropBox(
                                    "watchdog", null, "system_server", null, null, null,
                                    null, report.toString(), finalStack, null, null, null,
                                    errorId);
                        }
                    }
            };
            dropboxThread.start();
            try {
                dropboxThread.join(2000);  // wait up to 2 seconds for it to return.
            } catch (InterruptedException ignored) {}

            // 6. 导出异常日志到 kernel log后关机(trigger kernel panic), 通过/proc/sysrq-trigger触发
            if (crashOnWatchdog) {
                // Trigger the kernel to dump all blocked threads, and backtraces
                // on all CPUs to the kernel log
                Slog.e(TAG, "Triggering SysRq for system_server watchdog");
                doSysRq('w');
                doSysRq('l');

                // wait until the above blocked threads be dumped into kernel log
                SystemClock.sleep(3000);

                doSysRq('c');
            }

            // 7. 向ActivityController汇报当前状态
            IActivityController controller;
            if (controller != null) {
                Slog.i(TAG, "Reporting stuck state to activity controller");
                try {
                    Binder.setDumpDisabled("Service dumps disabled due to hung system process.");
                    // 1 = keep waiting, -1 = kill system
                    int res = controller.systemNotResponding(subject);
                    if (res >= 0) {
                        Slog.i(TAG, "Activity controller requested to coninue to wait");
                        waitedHalf = false;
                        continue;
                    }
                } catch (RemoteException e) {
                }
            }

            // 8. 判断是否需要杀掉当前进程(system_server进程) Process.killProcess(Process.myPid())
            // Only kill the process if the debugger is not attached.
            if (Debug.isDebuggerConnected()) {
                debuggerWasConnected = 2;
            }
            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);
                WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
                Slog.w(TAG, "*** GOODBYE!");
                if(SmartTraceUtils.isPerfettoDumpEnabled() && dueTime > SystemClock.uptimeMillis()){
                    long timeDelta = dueTime - SystemClock.uptimeMillis();
                    // wait until perfetto log to be dumped completely
                    Slog.i(TAG,"Sleep "+ timeDelta
                            +" ms to make sure perfetto log to be dumped completely");
                    SystemClock.sleep(timeDelta);
                }
                if (!Build.IS_USER && isCrashLoopFound()
                        && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {
                    breakCrashLoop();
                }
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
            waitedHalf = false;
        }
    }
检测机制

Watchdog在初始化时将一些重要进程添加到HandlerChecker列表中,通过HandlerChecker对各个监测对象进行监测。

HandlerChecker大致可以分为两类:

  • Monitor Checker,用于检查是Monitor对象可能发生的死锁, FgThread, AMS, WMS等核心的系统服务都是Monitor对象。
  • Looper Checker,用于检查线程的消息队列是否长时间处于工作状态。Watchdog自身的消息队列,Ui, Io, Display这些全局的消息队列都是被检查的对象。此外,一些重要的线程的消息队列,也会加入到Looper Checker中,譬如AMS, PKMS,这些是在对应的对象初始化时加入的。
    public void addMonitor(Monitor monitor) {
        synchronized (mLock) {
            mMonitorChecker.addMonitorLocked(monitor);
        }
    }

    public void addThread(Handler thread) {
        addThread(thread, DEFAULT_TIMEOUT);
    }

HandlerChecker是Watchdog的内部类,也实现了Runnable接口。

从上面Watchdog的工作流程中可以看到,Watchdog主要通过HandlerChecker的scheduleCheckLocked()方法监测进程状态。

在scheduleCheckLocked()方法开始初始化类变量mMonitors,mMonitors变量包含了所有的Monitor Checker对象,如上文说的FgThread, AMS, WMS等。

下面主要关注scheduleCheckLocked()方法中的两行代码:

  1. 通过*mHandler.getLooper().getQueue().isPolling()*方法判断Loop对象是否依然活跃而不是卡住。对于Looper Checker而言,会判断线程的消息队列是否处于空闲状态。 如果被监测的消息队列一直闲不下来,则说明可能已经阻塞等待了很长时间

  2. mHandler.postAtFrontOfQueue(this); 将Monitor Checker的对象置于消息队列之前,优先运行。mHandler.postAtFrontOfQueue(Runable r)参数为Runable对象,将HandlerChecker类中实现的run()方法放在监测对象mHandler进程中执行,调用其实现的monitor()方法,方法实现一般很简单,就是获取当前类的对象锁,如果当前对象锁已经被持有,则monitor()会一直处于wait状态,直到超时,这种情况下,很可能是线程发生了死锁。

public final class HandlerChecker implements Runnable {
		public void scheduleCheckLocked() {
            if (mCompleted) {
                // Safe to update monitors in queue, Handler is not in the middle of work
                mMonitors.addAll(mMonitorQueue);
                mMonitorQueue.clear();
            }
            if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
                    || (mPauseCount > 0)) {
                // Don't schedule until after resume OR
                // If the target looper has recently been polling, then
                // there is no reason to enqueue our checker on it since that
                // is as good as it not being deadlocked.  This avoid having
                // to do a context switch to check the thread. Note that we
                // only do this if we have no monitors since those would need to
                // be executed at this point.
                mCompleted = true;
                return;
            }
            if (!mCompleted) {
                // we already have a check in flight, so no need
                return;
            }

            mCompleted = false;
            mCurrentMonitor = null;
            mStartTime = SystemClock.uptimeMillis();
            mHandler.postAtFrontOfQueue(this);
        }
    
        @Override
        public void run() {
            // Once we get here, we ensure that mMonitors does not change even if we call
            // #addMonitorLocked because we first add the new monitors to mMonitorQueue and
            // move them to mMonitors on the next schedule when mCompleted is true, at which
            // point we have completed execution of this method.
            final int size = mMonitors.size();
            for (int i = 0 ; i < size ; i++) {
                synchronized (mLock) {
                    mCurrentMonitor = mMonitors.get(i);
                }
                mCurrentMonitor.monitor();
            }

            synchronized (mLock) {
                mCompleted = true;
                mCurrentMonitor = null;
            }
        }
}
Monitor

Monitor是Watchdog的内部接口:

public class Watchdog {
    public interface Monitor {
        void monitor();
    }
}

AMS的monitor()实现:

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
        
            /** In this method we try to acquire our lock to make sure that we have not deadlocked */
            public void monitor() {
                synchronized (this) { }
            }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android Watchdog分析 的相关文章

随机推荐

  • BC1.2快充协议介绍

    BC1 2定义 BC1 2 Battery Charging v1 2 是USB IF下属的BC Battery Charging 小组制定的协议 主要用于规范电池充电的需求 该协议最早基于USB2 0协议来实现 BC1 2充电端口 USB
  • 有无监督,上下游任务,高斯分布,BN总结

    1 无监督和有监督的区别 有无标号 label与预测结果做损失loss transformer有监督的 BERT 在预训练中在没有标号的数据集上训练 在微调的时候同样是用一个BERT模型 但是它的权重被初始化成在预训练中得到的权重 有标号的
  • 华为OD机试 - 字符串加密(Java)

    题目描述 给你一串未加密的字符串str 通过对字符串的每一个字母进行改变来实现加密 加密方式是在每一个字母str i 偏移特定数组元素a i 的量 数组a前三位已经赋值 a 0 1 a 1 2 a 2 4 当i gt 3时 数组元素a i
  • 软件工程学习过程中工具、资料汇总与心得

    因为在上了半年课以后 发现学的课程太杂 要的工具太多 回顾当初找工具找到病毒工具的苦不堪言的黑历史 在此整理学习用到的所有工具 保持更新 因为文件已经被别人上传了 还要积分什么鬼的 信息化时代共享不好嘛 因此将文件均上传至百度网盘 下载缓慢
  • vue+nodejs 搭建网站全过程

    Vue js Node js MongoDB 的网站搭建示例 安装和初始化项目 使用 Vue Cli 初始化前端项目 vue create my site 使用 Express 初始化后端项目 npx express generator m
  • 如何解决局域网广播风暴

    晚唐诗人许浑曾写过一首诗 咸阳城东楼 其中有一句名句 被传诵千古 山雨欲来风满楼 山雨欲来风满楼 是全诗的警句 周围的群山 雨意越来越浓 大雨即将到来 城楼上 已是满楼的狂风 全句只有寥寥七个字 却十分形象地写出了山城暴雨即将来临时的情景
  • 华为OD机试 - 阿里巴巴找黄金宝箱(I)(Python)

    题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上 无意中发现了强盗集团的藏宝地 藏宝地有编号从0 N的箱子 每个箱子上面贴有一个数字 箱子中可能有一个黄金宝箱 黄金宝箱满足排在它之前的所有箱子数字和等于排在它之后的所有箱子数字之和 第一个箱子
  • SQL SERVER行列不同分类的展示---PIVOT

    行列不同分类的SQL SERVER展示 由于工作的需求 需要对行不同分类 列也不同分类 可以将行分类之后对列进行每列CASE WHEN 进行展示 但是这种方法太蠢了 而且代码不够简洁 因此网上寻找了行转列函数 上最基础的做法 select
  • 关于javascript中number类型与string类型的比较

    javascript中number类型与string类型的比较 应该是根据number类型的数值情况 将string转换为与number数值相对应的值再比较 var numVal 10 00 if numVal 10 0000 consol
  • 搭建QNX开发环境-qnx系统环境开发

    锋影 e mail 174176320 qq com QNX是可以提供试用30天 目前发布最新的是qnx7 0版本 申请也是只能7 0 做好白老鼠的准备 老版本不再申请试用 其实多数时候 老版本的650 650sp1 和较新稳定的660版本
  • caffe 查看caffemodel中的参数与数据

    在用caffe训练完一个模型之后 我们想更加直观的查看这个模型该怎么做呢 caffe框架训练出来的caffemodel是一个类似于黑盒的东西 我们无法直接看到它的本质 需要借助caffe所定义的接口来协助我们 详细的文档在caffe官网上都
  • gcc链接脚本和启动文件详解

    C代码生成可执行程序分为 预编译 编译 汇编 链接四个阶段 预处理器把源程序聚合在一起 并把宏定义转换为源语言 编译器根据预处理的源程序生成汇编程序 汇编器处理汇编程序 生成可重定位的机器代码 连接器将可重定位的目标代码和库文件连接到一起
  • 基于51单片机直流电机PID调速PWM输出LCD1602液晶显示设计

    视频演示地址 https www bilibili com video BV1LK4y1R7ju 该设计是由AT89C51单片机为主控芯片显示为1602液晶构成直流电机调速 开机默认不转按下启动后电机开始运行 PID控制PWM进行调速 按键
  • Arduino使用ESP8266模块联网

    ESP8266模块准备 1 透传程序烧写 2 Arduino与ESP8266接线 Arduino模块程序 测试 总结 上一篇文章已经介绍了 利用 ArduinoIDE开发ESP8266模块 这篇文章介绍一下arduino怎么通过ESP826
  • unity鼠标事件

    鼠标事件 鼠标事件 都是当鼠标和gui或者碰撞体 Collider 交互时候触发 需要说明的是drag其实就是鼠标down后up之前持续每帧都会发送此消息 OnMouseDown 当鼠标上的按钮被按下时触发的事件 OnMouseDrag 当
  • LLVM IR格式的基本介绍

    LLVM IR以两种格式存储在磁盘上 1 位码 bc文件 2 汇编文本 ll文件 以sum c源代码为例 int sum int a int b return a b 使用Clang生成位码 命令如下 clang sum c emit ll
  • 单片机数码管从00到99C语言_51单片机数码管实现1到99显示

    在 51 单片机上实现用数码管显示 1 到 99 的数字 并且时间间隔为 1 秒 全部代码如下 include define uchar unsigned char define uint unsigned int sbit dula P2
  • C语言之tentative definition

    参考链接 What Are Tentative Symbols
  • redisson究极爽文-手把手带你实现redisson的发布订阅,消息队列,延迟队列(死信队列),(模仿)分布式线程池

    参考资料 分布式中间件实战 java版 书籍 多线程视频教程 视频 项目启动环境 导入依赖
  • Android Watchdog分析

    初始化 Watchdog作为一个独立的线程在SystemServer进程中被初始化 private void startBootstrapServices NonNull TimingsTraceAndSlog t Start the wa