zoukankan      html  css  js  c++  java
  • 性能工具|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

  • 相关阅读:
    Android Theme主题
    Android AbsoluteLayout绝对布局
    Android FrameLayout单帧布局
    Android TableLayout 表格布局
    Android LinearLayout线性布局
    Android RelativeLayout相对布局
    Unity中几种简单的相机跟随
    Android适配API23之后权限的动态申请
    natural gradient笔记
    优化整理
  • 原文地址:https://www.cnblogs.com/wanghongzhu/p/15616214.html
Copyright © 2011-2022 走看看