性能分析:
从AS3.0,android使用Profile来进行分析,关于Profile的使用,请参考文章:
Android Profiler分析(一)概述
Android Profiler分析(二) Memory Profiler
Android Profiler分析(三) CPU Profiler
常用命令
-
Android studio的工具-Memory Monitor
Memory Monitor工具主要是用来监测APP的内存分配情况,判断是否存在内存泄漏
Dump Java Heap定位内存泄漏, 生成.hprof
Eclipse: ddms->update heap->cause gc生成总内存信息,然后会有data object -
运行时错误分析
/data/tombstones
全志平台:/mnt/extsd/tombstones
addr2line 工具和 objdump 工具来定位backtrace错误
工具位置:windows: ndk\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin
- arm-linux-androideabi-addr2line -C -f -e libss.so 00026cb3
-e 选项来指定可执行libss.so。
-f 选项,可以告诉工具输出函数名- arm-eabi-objdump -S libss.so > dvb_map.txt
- CPU
查看CPU占用空间:
adb shell dumpsys cpuinfo <package_name>/pid查看进程占用CPU
top -p < pid >查看CPU负载
adb shell uptime
20:09:54 up 71 days, 10:48, 1 user, load average: 0.99, 0.78, 0.86
Load后面的三个数字的意思分别是1分钟、5分钟、15分钟内系统的平均负荷查看CPU状态
online CPU数量:cat /sys/devices/system/cpu/online
查看具体CPU的当前频率:cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freqCPU各个频率下的运行时间,单位:10 mS
adb shell cat /sys/devices/system/cpu/cpu1/cpufreq/stats/time_in_state
频度 时间
652800 1813593
1036800 46484cpu governor
cpu gorvenor对应的是CPU的调频策略,默认的是interactive,performance是一直保持在最高频率
查看governor类型:cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor影响CPU hotplug的因素(以高通平台为例)
perfd,可以通过stop perfd关闭
thermel,可以通过stop thermal-engine关闭
core_crtl,可通过rmmod core_crtl来关闭调度
ps -p可以观察进程优先级,systrace也能看到
/dev/cpuctl,不同的组之间具有不一样的抢占能力
/dev/cpuset,不同的组可使用的cpu不一样,如果线程跑在小核,则线程在./system-background/tasks中,通过grep [pid] -rn ./ 查看
cat /proc/sched_debug,查看调度队列的信息
kill -3 (pid) 打印进程当前各个线程的调用栈
taskset -p [pid] 查看线程在大核还是小核
- LPM
CPU在idle时会进入low power mode,需要运行任务时再从LPM推出,在一些需要频繁进出idle的应用场景下,有的CPU会出现明显
的性能下降,可以disableLPM来验证这个问题
stop perfd (因为perfd会改写lpm的值)
echo N > /sys/module/lpm_levels/parameters/sleep_disabled- 将cpu固定在最高频
高通平台
stop perfd
stop thermal-engine
rmmod core_ctl
echo 1 > /sys/devices/system/cpu/cpu0/online(遍历每个core)
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor(遍历每个cluster)
MTK平台
adb shell “echo 0 > /proc/hps/enabled”
echo 1 > /sys/devices/system/cpu/cpu0/online(遍历每个core)
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor(遍历每个cluster)- 修改CPU频率
高通888为4+3+1结构,所以/sys/devices/system/cpu/cpufreq/下有三个节点policy0、policy4、policy7
cpu0~3的频率策略对应policy0、cpu7对应策略为policy7
通过scaling_available_frequencies查看支持的频率,比如844800 960000 1075200 1190400 1305600 1420800 1555200 1670400 1785600 1900800 2035200 2150400 2265600 2380800 2496000 2592000 2688000 2764800 2841600
然后echo 844800 > scaling_max_freq设置最大频率
- 内存
- 查看系统内存状态
cat /proc/meminfo
adb shell procrank (pid)
adb shell dumpsys meminfo/cpuinfo <package_name>/pid内查看本地层程序
top -t -m 5 -n 2 查询Cpu/内存的基本使用
adb shell cat /proc/buddyinfo
adb shell cat /proc/meminfo
adb shell cat /proc/pid/maps- 查看内存碎片状态
cat /proc/pagetypeinfo- 查看应用的graphic缓存(如纹理缓存)
dumpsys gfxinfo- 查看进程内的位图
dumpsys gfxinfo [pid] -b- 查看lowmemorykiller阈值
cat /sys/module/lowmemorykiller/parameters/minfree
cat /sys/module/lowmemorykiller/parameters/adj- 查看ion buffer使用情况,比如起camera的时候会有大量的ion使用
cat /d/ion/heaps/system
- IO
- drop cache
文件系统会有大量的page cache放在内存中,如果需要清理这个缓存,可以
echo 3 > /proc/sys/vm/drop_caches- read_ahead
文件page_fault时会预读一定数量的内容以便后续访问,不合适的预读大小可能会影响性能
查看预读的大小
cat /sys/class/block/mmcblk0/queue/read_ahead_kb- IO scheduler
查看IO scheduler类型
cat /sys/class/block/mmcblk0/queue/scheduler- IO速度
通常的IO测试是通过文件系统来读写文件,速度在很大程度上受到file cache的影响,
如果需要衡量纯粹的硬件读写能力,可以在read/write API使用O_DIRECT flag
Android手机的测试推荐使用FIO工具
-
查看磁盘占用空间
du -sH * 查看当前目录下文件空间
df 查看分区占用空间 -
对进程打开的所有文件进行监控: lsof -p 7005
-
dumpsys命令
dumpsys activity | grep mFocusedActivity 查看当前焦点activity
adb shell dumpsys meminfo/cpuinfo <package_name>/pid内查看本地层程序 -
老化影响
老化后,可能跟可能和thermal以及cache相关,建议测试时候每次都要做:
1.等待运行完毕
adb shell sm fstrim
adb shell sm idle-maint run
2. adb shell stop thermal-engine
adb shell stop mi_thermald
3. echo 3 > /proc/sys/vm/drop_caches
按键事件:
adb shell getevent –t 查看当前按下按键的值
事件类型(0001),事件代码(0074)以及事件的值(00000001)
adb shell getevent /dev/input/event2 获取event2设备的事件
adb shell input keyevent 4 返回按键
卡顿内容等性能分析:
-traceview
TraceView可视化工具可以看出代码在运行时的一些具体信息,方法调用时长,次数,时间比率,了解代码运行过程的效率问题,从而针对性改善代码。它主要用于分析Android中应用程序的hotspot。Traceview本身只是一个数据分析工具,而数据的采集则需要使用Android SDK中的Debug类或者利用DDMS工具。
TraceView用法
- Ddms打开, Start Method Profiling->操作界面->stop, 操作最好不要超过5s
- android.os.Debug.startMethodTracing();和android.os.Debug.stopMethodTracing();然后在sdcard目录下生产trace文件
- 打开tools目录下monitor.bat或者eclipse,脚本如下
@echo off
rem color 0A :设置cmd背景颜色和字体颜色 color 0A
rem title:设置cmd标题
title Start Android Studio Mointor
rem echo 请按任意键打开 Android Studio Mointor …
rem pause>nul call
D:\tools\adt-bundle-windows-x86_64-20140702\sdk\tools\monitor.bat
如果无法打开,则需要把androidstudio中的jre拷贝到monitor程序的目录下
Ubuntu:android/Sdk/tools/lib/monitor-x86_64
Profile Panel各列作用说明
名 | 描述 |
---|---|
Name | 该线程运行过程中所调用的函数名 |
Incl Cpu Time | 某函数占用的CPU时间,包含内部调用其它函数的CPU时间 |
Excl Cpu Time | 某函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间 |
Incl Real Time | 某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间 |
Excl Real Time | 某函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间 |
Call+Recur Calls/Total | 某函数被调用次数以及递归调用占总调用次数的百分比 |
Cpu Time/Call | 某函数调用CPU时间与调用次数的比。相当于该函数平均执行时间 |
Real Time/Call | 同CPU Time/Call类似,只不过统计单位换成了真实时间 |
一般而言,hotspot包括两种类型的函数: 一类是调用次数不多,但每次调用却需要花费很长时间的函数。先按降序对时间项进行排列(可以是时间百分比、真实时间或CPU时间),然后查找耗费时间最多的函数
一类是那些自身占用时间不长,但调用却非常频繁的函数。
Systrace
Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况(如surfaceflinger、WindowManagerService等Framework部分关键模块、服务)等,。
在Android平台中,它主要由3部分组成:
内核部分:Systrace利用了Linux Kernel中的ftrace功能。必须开启kernel中和ftrace相关的模块。
数据采集部分:Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。
数据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集ftrace统计数据并生成一个结果网页文件供用户查看。
从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace。Android 4.1为系统中的几个关键进程和模块都添加了Systrace功能
添加代码的跟踪方式native和java
native层用法
头文件:<utils/Trace.h>
定义ATRACE_TAG:如使用了ATRACE_TAG_GRAPHICS,表示它和Graphics相关。
ATRACE_INIT:用于统计某个变量使用的情况。ATRACE_INIT(“变量TAG”, 变量名)
ATRACE_CALL:用于统计函数的调用情况ATRACE_CALL()
app层
Trace.beginSection(“Fragement_onCreateView”);
// … 其他代码
// …
// … 结束处
Trace.endSection()
然后通过指令:python systrace.py --app=sectionName 指定APK,或者通过DDMS选择指定APK,抓取systrace分析
Java framework层
Trace.traceBegin(long traceTag, String methodName)
Trace.traceEnd(long traceTag)
打开方式:
进入Android/Sdk/platform-tools/systrace目录下
python systrace.py [options] [category1] [category2] … [categoryN]
[options]”是一些命令参数;“[category]”等是你感兴趣的系统模块,比如view代表View系统(包含绘制流程),am代表ActivityManager(包含Activity的创建过程等
如:python systrace.py --time=10 -o mynewtrace.html sched gfx view wm
要在 Pixel/Pixel XL 上调试抖动问题,请从以下命令开始:
./systrace.py sched freq idle am wm gfx view sync binder_driver irq workq input -b 96000
如果报错apt-get install python-six,请执行sudo apt-get install python-six
options:
options | 描述 |
---|---|
-o < FILE > | 输出的目标文件 |
-t N, –time=N | 执行时间,默认5s |
-b N, –buf-size=N | buffer大小(单位kB),用于限制trace总大小,默认无上限 |
-k < KFUNCS >,–ktrace=< KFUNCS > | 追踪kernel函数,用逗号分隔 |
-a < APP_NAME >,–app=< APP_NAME > | 追踪应用包名,用逗号分隔 |
–from-file=< FROM_FILE > | 从文件中创建互动的systrace |
-e < DEVICE_SERIAL >,–serial=< DEVICE_SERIAL > | 指定设备 |
-l, –list-categories | 列举可用的tags |
category常用取值
sched:CPU调度的信息,非常重要;你能看到CPU在每个时间段在运行什么线程;线程调度情况,比如锁信息。
gfx:Graphic系统的相关信息,包括SurfaceFlinger,VSYNC消息,Texture,RenderThread等;分析卡顿非常依赖这个。
view:View绘制系统的相关信息,比如onMeasure,onLayout等;对分析卡顿比较有帮助。
am:ActivityManager调用的相关信息;用来分析Activity的启动过程比较有效。
dalvik: 虚拟机相关信息,比如GC停顿等。
binder_driver:Binder驱动的相关信息,如果你怀疑是Binder IPC的问题,不妨打开这个。
core_services:SystemServer中系统核心Service的相关信息,分析特定问题用。
利用anr日志:
- java层:
默认trace位置: /data/anr/trace.txt
可以通过修改系统属性dalvik.vm.stack-trace-file改变trace文件路径
ANR分类
从发生的场景分类:
- Input事件超过5s没有被处理完
- Service处理超时,前台20s,后台200s
- BroadcastReceiver处理超时,前台10S,后台60s
- ContentProvider执行超时,比较少见
从发生的原因分:
- 主线程有耗时操作,如有复杂的layout布局,IO操作等。
- 被Binder对端block
- 被子线程同步锁block
- Binder被占满导致主线程无法和SystemServer通信
- 得不到系统资源(CPU/RAM/IO)
从进程的角度分:
- 问题出在当前进程:
- 主线程本身耗时, 或则主线程的消息队列存在耗时操作;
- 主线程被本进程的其他子线程所blocked;
- 问题出在远端进程(一般是binder call或socket等通信方式)
----- pid 27661 at 2017-06-16 16:16:20 -----
Cmd line: com.android.camera
"main" prio=5 tid=1 Waiting
| group="main" sCount=1 dsCount=0 obj=0x75a4b5c8 self=0xb4cf6500
| sysTid=27661 nice=-10 cgrp=default sched=0/0 handle=0xb6f6cb34
| state=S schedstat=( 11242036155 8689191757 38520 ) utm=895 stm=229 core=0 HZ=100
| stack=0xbe4ea000-0xbe4ec000 stackSize=8MB
| held mutexes=
at java.lang.Object.wait!(Native method)
- waiting on <0x09e6a059> (a java.lang.Object)
at java.lang.Thread.parkFor$(Thread.java:1220)
- locked <0x09e6a059> (a java.lang.Object)
at sun.misc.Unsafe.park(Unsafe.java:299)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:810)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:970)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1278)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:203)
at android.app.SharedPreferencesImpl$EditorImpl$1.run(SharedPreferencesImpl.java:366)
at android.app.QueuedWork.waitToFinish(QueuedWork.java:88)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3605)
at android.app.ActivityThread.access$1300(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1399)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:5528)
at java.lang.reflect.Method.invoke!(Native method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:740)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:630)
tid=1 | 线程号 |
---|---|
sysTid=27661 | 主线程的线程号和进程号相同 |
Waiting | 线程状态,其中state也是线程状态,如果state=D代表底层被blocked了 |
nice | nice值越小,则优先级越高。因为是主线程此处nice=-10, 可以看到优先级很高了 |
schedstat | 括号中的3个数字,依次是Running, Runable, Switch,Running时间。Running时间:CPU运行的时间,单位ns。 Runable时间:RQ队列的等待时间,单位ns |
utm | 该线程在用户态所执行的时间,单位是jiffies |
stm | 该线程在内核态所执行的时间,单位是jiffies |
sCount | 此线程被挂起的次数 |
dsCount | 线程被调试器挂起的次数,当一个进程被调试后,sCount会重置为0,调试完毕后sCount会根据是否被正常挂起增长,但是dsCount不会被重置为0,所以dsCount也可以用来判断这个线程是否被调试过 |
self | 线程本身的地址 |
一般的看trace有如下规律
- 发生ANR时,trace中找不到相应进程,检查一下Android Runtime是否因为应用的崩溃给ShutDown了,如果ShutDown了,此时要去查ShutDown的原因。
- 应用发生ANR,如果主线程正在执行getContentProvider,那么它正在请求另一个应用的ContentProvider,此时要查一下目标ContentProvider的宿主进程,看看正在做什么
- 主线程执行数据库操作或网络请求,应该是应用自身问题
- 主线程等待其他线程持有的锁,而目标线程执行数据库操作或网络请求,那么是应用自身问题。
ANR分析基本流程
- 抓取bugreport,搜索am_anr, ANR in,查看发生的时间和进程
- 根据进程寻找主线程的trace,发现被blocked的地方,如果是Binder call则,进一步确认下对端的情况;如果是耗时操作,直接修改成异步,怀疑系统执行慢可以看看binder_sample,dvm_lock等信息,其次gc多不多,lmk杀进程是不是很频繁,都可以看出系统的健康状态
- 结合源码进行分析解决
- 系统耗时关键字:binder_sample,dvm_lock_sample,am_lifecycle_sample,binder thread
ANR:
VM TRACES AT LAST ANR”,数据来源”/data/anr/traces.txt”
若存在该ANR则输出相应traces,否则输出
*** NO ANR VM TRACES FILE (/data/anr/traces.txt): No such file or directory
am_anr
12-17 06:02:14.463 1566 1583 I am_anr : [0,8769,com.android.updater,952680005,Broadcast of Intent { act=android.intent.action.BOOT_COMPLETED flg=0x9000010 cmp=com.android.updater/.BootCompletedReceiver (has extras) }]
- 发生anr的时间点 am_anr: 进程pid:8769、进程名:com.android.updater、发生ANR的类型是:BroadcastTimeout、的具体类或者原因:{ act=android.intent.action.BOOT_COMPLETED flg=0x9000010 cmp=com.android.updater/.BootCompletedReceiver (has extras) }
- 超时anr关键字
- KeyDispatchTimeout-主要类型按键或触摸事件,input事件在5S内没有处理完成发生ANR
日志关键字:Reason: Input dispatching timed out xxxx- ServiceTimeout-bind,create,start,unbind等在主线程处理耗时,前台Service在20s内,后台Service在200s内没有处理完成发生ANR
日志关键字:Timeout executing service:/executing service XXX- BroadcastTimeout- BroadcastReceiver onReceiver处理事务时前台广播在10S内,后台广播在60s内没有处理完成发生ANR
日志关键字:Timeout of broadcast XXX/Receiver during timeout:XXX/Broadcast of XXX- ProcessContentProviderPublishTimedOutLocked-ContentProvider publish在10s内没有处理完成发生ANR
日志关键字:timeout publishing content providers
- 造成ANR的常见原因
主线程耗时操作,如复杂的layout,庞大的for循环,IO等。
主线程被子线程同步锁block
主线程被Binder 对端block
Binder被占满导致主线程无法和SystemServer通信
得不到系统资源(CPU/Memory/IO)
bugreport
抓取方法:
adb shell bugreportz -p
/data/user_de/0/com.android.shell/files/bugreports/
hippo -l
hippo xx.zip meminfo
hippo xx.zip logs
hippo xx.zip events -m 3 //只显示最近 3 分钟的 events log
详细bugreport关键字,请参考bugreport关键字
Hprof
Zygote fork出来的process,如APP以及system_server,都会进行Java运行环境分析。其关键是分析Java Heap,以便快速知道某个Java变量的值,以及Java对象的分布和引用情况。
通常Java Heap的分析方式则是抓取Java Hprof,然后使用MAT等工具进行分析
抓取Hprof日志方法:
- 方式一:使用am 命令
db shell am dumpheap {Process} file
adb shell am dumpheap com.android.phone /data/anr/phone.hprof
adb pull /data/anr/phone.hprof
- 方式二:使用DDMS 命令
在DDMS中选择对应的process,然后在Devices按钮栏中选择Dump Hprof file,保存即可
- 方式三:通过代码的方式
在android.os.Debug这个class 中有定义相关的抓取hprof 的method。
如: public static void dumpHprofData(String fileName) throwsIOException;
这样即可在代码中直接将这个process的hprof 保存到相对应的文件中,注意这个只能抓取当时的process。
快速分析
- DVM的Hprof 和标准的Java Hprof 有一些差别,需要使用hprof-conv进行一次转换,将DVM格式的hprof转换成标准的java 命令 hprof-conv in.hprof out.hprof
- 使用如MAT Tool,打开转换后的hprof文件
- Native层:
添加代码直接抓取
Google 默认提供了CallStack API,请参考
system/core/include/libutils/CallStack.h
system/core/libutils/CallStack.cpp
可快速打印单个线程的backtrace
自动抓取
Natice层的进程发生异常后一般都在/data/tombstones目录下生成文件
arm-linux-androideabi -addr2line-f -C -e symbols address
MTK工具
利用debuggerd抓取Native backtrace的tool RTT(Runtime Trace),对应的执行命令是:
USAGE : rtt[-h] -f function -p pid [-t tid]
-ffuncion : current support functions: bt (Backtrace function)
-ppid : pid to trace
-ttid : tid to trace
nname : process name to trace
-h : help menu
-Kernel层
- AEE/RTT 工具
- cat proc/pid/task/tid/stack
- proc/kmsg
adb shell cat proc/kmsg > kmsg.txt
adb shell "echo 8 > proc/sys/kernel/printk“ //修改printk loglevel
adb shell "echo t > /proc/sysrq-trigger“ //打印所有的backtrace
adb shell "echo w > /proc/sysrq-trigger“//打印’-D’ status’D’的 process