性能监控工具 | ANRdaemon

性能监控工具 | ANRdaemon

一、README

ANRdaemon 是一个守护程序,用于分析由 CPU 占用过高导致的 ANR 问题。守护程序借助 debugfs 来实现信息记录。要抓的 trace 模块提前在 /d/tracing 中配置好。根据 CPU 使用级别的不同,trace 可以通过全局开关 /d/trace/trace_on 来控制。原始 trace 文件存储在 /d/tracing/trace

如下操作会让守护程序会启动:

$ anrd -t 9990 sched gfx am

这意味着在 99.90% 的CPU利用率以上将开始抓 trace,抓的模块是 sched gfx am

使用 ANRdaemon_get_trace.sh [device serial]dump 和获取压缩的 trace 文件。

可以使用 systrace 解压:

$ systrace.py --from-file=<path to compressed trace file>

已知问题:

在 systrace 输出中,当跟踪未运行时,将显示 anrdaemon。 这是因为守护进程在 CPU 使用率下降时关闭跟踪,它留在原始跟踪文件中的最后一个条目是调度程序从某个其他进程切换到守护进程。 然后一段时间后(比如 20 秒后),当 CPU 使用率变高并且守护进程再次打开跟踪时,sched 记录的 /d/tracing/trace 中的第一个条目正在从守护进程切换到某个其他进程。 由于这个机制,当 systrace.py 解析原始跟踪文件时,守护进程显示为运行了整个 20 秒(因为从 systrace 的角度来看,关于守护进程的两个间隔 20 秒的 sched 跟踪条目表示守护进程 连续运行所有 20 秒)。 但是,在高 CPU 使用率情况下,这不会影响实际捕获的跟踪。

二、使用

anrd help 信息

emulator_x86_64:/ # anrd  -h                                                                                                        
usage: ANRdaemon [options] [categoris...]
Options includes:
   -a appname  enable app-level tracing for a comma separated list of cmdlines
   -t N        cpu threshold for logging to start (uint = 0.01%, min = 5000, max = 9999, default = 9990)
   -s N        use a trace buffer size of N KB default to 2048KB
   -h          show helps
Categoris includes:
   am         - activity manager
   sm         - sync manager
   input      - input
   dalvik     - dalvik VM
   audio      - Audio
   gfx        - Graphics
   rs         - RenderScript
   hal        - Hardware Modules
   irq        - kernel irq events
   sched      - kernel scheduler activity
   stack      - kernel stack
   sync       - kernel sync activity
   workq      - kernel work queues
Control includes:
   SIGQUIT: terminate the process
   SIGSTOP: suspend all function of the daemon
   SIGCONT: resume the normal function
   SIGUSR1: dump the current logging in a compressed form

使用见 readme 就好了,就是运行个监控程序,CPU 超过设定值他就抓 trace。

:/ # anrd -t 5000 sched gfx am
:/ # ps -e |grep anr
root           5715      1 12346524  2064 __arm64_sys_nanosleep 0 S anrd
:/ # logcat |grep 5715
08-25 18:19:21.768  5715  5715 I anrdaemon: ANRdaemon starting

你觉得抓完了就可以停了,把 trace 导出来。

设备上的日志:

:/ # logcat |grep anrd
08-25 18:34:13.727  9882  9882 D anrdaemon: High cpu usage, start logging.
08-25 18:34:14.233  9882  9882 D anrdaemon: Usage back to low, stop logging.
08-25 18:38:07.754  9882  9882 I anrdaemon: Started to dump ANRdaemon trace.
08-25 18:38:16.081  9882  9882 I anrdaemon: Finished dump. Output file stored at: /data/misc/anrd/dump_of_anrdaemon.2021-08-25.18:38:07

PC 端执行 sh 导出 trace:

qiucheng@haha:~/aosp/system/extras/ANRdaemon$ ./ANRdaemon_get_trace.sh 
/data/misc/anrd/dump_of_anrdaemon.2021-08-25.18:38:07: 1 file pulled, 0 skipped. 27.1 MB/s (5279290 bytes in 0.186s)
SUCCEED!
Trace stored at /home/qiucheng/aosp/system/extras/ANRdaemon/dump_of_anrdaemon.2021-08-25.18:38:07

三、源码

代码位置:

system/extras/ANRdaemon/

ANRdaemon.cpp
ANRdaemon_get_trace.sh

就俩文件,一个 CPP 编译成设备端的二进制可执行文件,另一个是 PC 上的 shell 脚本,用来导出抓好的 trace

先看 anrd 可执行程序吧

anrd

system/extras/ANRdaemon/ANRdaemon.cpp

int main(int argc, char* argv[]) {
    if (get_options(argc, argv) != 0) return 1;

    if (daemon(0, 0) != 0) return 1;
    //注释1:信号监听,收信号时保存 trace 文件
    register_sighandler();
    //注释2:新的 "/d/tracing/trace" fd
    /* Clear any the trace log file by overwrite it with a new file */
    int fd = creat(dfs_trace_output_path, 0);
    if (fd == -1) {
        ALOGE("Faield to open and cleaup previous log");
        return 1;
    }
    close(fd);
    //注释3:开启死循环监听 CPU 负载监听 signal,抓 trace、保存 trace
    ALOGI("ANRdaemon starting");
    start();

    if (err) ALOGE("ANRdaemon stopped due to Error: %s", err_msg);

    ALOGI("ANRdaemon terminated.");

    return (err ? 1 : 0);
}
----------------------------------------------------------------
    static void start(void) {
    if ((set_tracing_buffer_size()) != 0) return;

    dfs_set_property(tag, apps, true);
    dfs_poke_binder();

    get_cpu_stat(&old_cpu);
    sleep(check_period);

    while (!quit && !err) {
        if (!suspend && is_heavy_load()) {//注释4:检测 CPU 负载
            /*
             * Increase process priority to make sure we can stop logging when
             * necessary and do not overwrite the buffer
             */
            setpriority(PRIO_PROCESS, 0, -20);
            start_tracing();//注释5:抓 trace 和保存 trace
            setpriority(PRIO_PROCESS, 0, 0);
        }
        sleep(check_period);
    }
    return;
}
--------------------------------------------------------
static void handle_signal(int signo) {//注释6:拦截信号做不同反应
    switch (signo) {
        case SIGQUIT:
            suspend = true;
            quit = true;
            break;
        case SIGSTOP:
            suspend = true;
            break;
        case SIGCONT:
            suspend = false;
            break;
        case SIGUSR1:
            request_dump_trace();

原理就是 readme 里讲的那么简单,开启一个死循环,不断检查 CPU 负载,超过设定值就抓 trace

下面看看导出 trace 的 shell 脚本

ANRdaemon_get_trace.sh

#!/bin/bash

TRACE_DIR=/data/misc/anrd
TRACE_FILE_PATTEN=dump_of_anrdaemon

if [ $# -eq 1 ]; then
    DEVICE=$(echo "-s $1")
else
    DEVICE=""
fi
#注释1:检测anrd在运行
PID=$(adb $DEVICE shell "ps | grep anrd")

if [ $? -ne 0 ]; then
    echo "FAILED. ADB failed or Daemon is not running."
    exit 1
fi
#注释2:给 anrd 发 signal,触发 trace 的保存
PID=$(echo "$PID" | awk '{ print $2 }')
adb $DEVICE shell "kill -s SIGUSR1 $PID"
#注释3:找到最后一个生成的 trace 文件
TRACE_FILE=$(adb $DEVICE shell "ls $TRACE_DIR \
    | grep $TRACE_FILE_PATTEN | tail -n1" | tr -d '\r')
#注释4:查看 anrd 进程打开的文件
# Wiat the trace file generation to complete
adb $DEVICE shell "lsof -p $PID" | grep $TRACE_FILE > /dev/null
while [ $? -eq 0 ];
do
    sleep 1
    adb $DEVICE shell "lsof -p $PID" | grep "$TRACE_FILE" > /dev/null
done

if [ -z "$TRACE_FILE" ]; then
    echo "FAILED. Trace file not created"
fi
#注释5:把 trace pull 到 PC
adb $DEVICE pull "${TRACE_DIR}/${TRACE_FILE}" ${TRACE_FILE}

CURRENT_DIR=$(pwd)
echo SUCCEED!
echo Trace stored at ${CURRENT_DIR}/${TRACE_FILE}

简单的顺序流程。到设备里找 anrd 进程,然后发信号 SIGUSR1 触发 anrd 中的信号拦截,然后保存好 trace 后把最新的 trace pull 到本地。

四、有bug

anrd 和 ANRdaemon

最初的可执行程序叫 anrdaemon,后面改成了 anrd,usage 和 readme 中的写法不对。这需要修正。

'/data/misc/anrd/': No such file or directory

看了下 git log,从来就没有将 trace 保存的目录创建到文件系统上过。所以一开始开发的时候就是手动创建?还需要确认下要不要修改,以及在 rc 里改还是在 anrd 里改。

而像 /data/misc/wmtrace/ 这样的调试目录,都是在 init.rc 里开机阶段创建好的

system/core/rootdir/init.rc

mkdir /data/misc/wmtrace 0700 system system
on property:ro.debuggable=1
    # Give writes to anyone for the trace folder on debug builds.
    # The folder is used to store method traces.
    chmod 0773 /data/misc/trace
    # Give reads to anyone for the window trace folder on debug builds.
    chmod 0775 /data/misc/wmtrace

不过 root 后手动创建后是可以让程序顺利保存并导出 trace 的。

posted @ 2021-11-28 18:59  秋城  阅读(427)  评论(0编辑  收藏  举报