性能工具|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到本地pc。

四、有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

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

本文来自博客园,作者:秋城,转载请注明原文链接:https://www.cnblogs.com/wanghongzhu/p/15616214.html

原文地址:https://www.cnblogs.com/wanghongzhu/p/15616214.html