首先对ADB作简单的阐述,接下来对adb shell dumpsys SurfaceFlinger服务的dump信息的查看、以及ANR问题如何获取trace文件并简单分析。
-×**************************************************************
目录:
一、ADB概述
二、ADB常用命令
(1)基本命令
(2)软件操作命令
(3)文件操作命令
(4)日志操作命令
三、dumpsys使用
四、dump SurfaceFlinger的打印信息分析
五、trace分析
-×*************************************************************
一、ADB概述
adb(Android Debug Bridge),安卓平台调试桥,是连接Android手机与PC端的桥梁,通过adb可以管理、操作模拟器和设备,如安装软件、查看设备软硬件参数、系统升级、运行shell命令等。
手机启动USB调试模式,设备连接电脑。
注:我的手机一加6的USB调试模式打开方式如下:
(1)在手机设置的关于手机找到版本号,双击七次打开开发者模式;
(2)在开发者选项中打开USB调试选项;
(3)设备连接
二、ADB常用命令
(1)基本命令:
1、adb devices 查看设备连接情况
2、adb version 查看版本
3、adb kill-server 关闭adb服务
4、adb start-server 启动adb服务
5、adb 查看帮助
6、adb shell 进入adb sehll命令
7、adb shell top 查看手机当前进程占用手机内存情况
8、adb shell kill -3 pid 杀掉进程
9、adb logcat -v process |grep 8607 //8607 是进程 PID
(2)软件操作命令:
安装软件:
adb install
adb install <apk文件路径> :这个命令将指定的apk文件安装到设备上
卸载软件:
adb uninstall <软件名>
adb uninstall -k <软件名> 如果加 -k 参数,为卸载软件但是保留配置和缓存文件
(3)文件操作命令:
从电脑上发送文件到设备
adb push <本地路径> <远程路径>
用push命令可以把本机电脑上的文件或者文件夹复制到设备(手机)
从设备上下载文件到电脑
adb pull <远程路径> <本地路径>
用pull命令可以把设备(手机)上的文件或者文件夹复制到本机电脑
(4)日志操作命令
日志等级(由上往下级别递增):
V verbase,级别最低,琐碎、不重要的日志信息
D debug,调试信息
I info,重要信息
W warning,警告信息
E error,错误信息
F fatal,严重错误信息
S slient,无记载
- adb logcat --help 查看帮助信息,获取该命令可配置的参数选项
- adb logcat -b <buffer> 加载一个可使用的日志缓冲区供查看
buffer选项可以填为:
radio 通信系统
system 系统组件
event event事件模块
main java层
kernel linux内核
- adb logcat [tag:level] 指定标签,指定级别过滤显示日志
例如:adb logcat Test:I
- adb logcat > home/mylog.txt 日志保存到电脑某路径
- adb logcat -d -f /sdcard/mylog.txt 保存到手机上指定位置(-d 日志显示在控制台)
- adb logcat -f /scard/log.txt 输出到手机指定位置
- adb logcat -s System.out 设置标签(某个字符串),过滤显示日志
- adb logcat -v <format> 设置日志输入格式控制输出字段
format选项可以填为:
brief 显示优先级/标记和原始进程的PID(默认)
process 只显示进程PID
tag 只显示优先级/标记
thread 只显示进程、线程、优先级/标记
raw
time
long
例如:adb logcat -v process
(5)adb shell命令
- adb shell pm list packages 查看2、adb shell pm list packages -f 查看包名对应的apk路径及名称
3、adb shell dumpsys 列出手机所有apk的详细信息
三、dumpsys使用
(1)adb shell 进入shell
(2)dumpsys -l 查看所有正在运行的服务名
service list 查看这些服务名称调用了哪个服务
下面列举了其中一些服务名:
服务名 | 类名 | 功能 |
activity |
ActivityManagerService |
AMS相关信息 |
package |
PackageManagerService |
PMS相关信息 |
window |
WindowManagerService |
WMS相关信息 |
input |
InputManagerService |
IMS相关信息 |
power |
PowerManagerService |
PMS相关信息 |
batterystats |
BatterystatsService |
电池统计信息 |
battery |
BatteryService |
电池信息 |
alarm |
AlarmManagerService |
闹钟信息 |
dropbox |
DropboxManagerService |
调试相关 |
procstats |
ProcessStatsService |
进程统计 |
cpuinfo |
CpuBinder |
CPU |
meminfo |
MemBinder |
内存 |
gfxinfo |
GraphicsBinder |
图像 |
dbinfo |
DbBinder |
数据库 |
服务名 | 功能 |
SurfaceFlinger |
图像相关 |
appops |
app使用情况 |
permission |
权限 |
processinfo |
进程服务 |
batteryproperties |
电池相关 |
audio |
查看声音信息 |
netstats |
查看网络统计信息 |
diskstats |
查看空间free状态 |
jobscheduler |
查看任务计划 |
wifi |
wifi信息 |
diskstats |
磁盘情况 |
usagestats |
用户使用情况 |
devicestoragemonitor |
设备信息 |
(3)dumpsys <service>
打印具体某一项服务(service就是前面表格中的服务名)
- dumpsys cpuinfo //打印一段时间进程的CPU使用百分比排行榜
- dumpsys meminfo -h //查看dump内存的帮助信息
- dumpsys package <packagename> //查看指定包的信息
- dumpsys SurfaceFLinger //查看SF服务
四、dump SurfaceFlinger的打印信息分析
SurfaceFlinger的dump信息主要通过dumpAllLocked 函数来获取。
一般包含:
1、layer的信息,layer一般对应于一个surface;
2、opengl的信息。一般是跟gpu比较相关的参数,opengl是标准的接口;
3、display。安卓支持三种类型的display,可以导出display当前的显示状态,也就是各个surface(layer)在各个display的显示属性;
4、surfaceflinger管理graphis buffer的信息。主要是layer申请的帧数据内存;
5、hwcomopser的如果实现dump接口也能知道hwcomposer的一些参数;
6、gralloc的内存分配信息。如果gralloc有实现dump接口的话;
(1)特殊宏的打开
- Build configuration: [sf] [libui] [libgui]
(2)打印目前正在使用的Sync机制
- Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]
(3)打印Layer
- Visible layers (count = 9)
count的值来源于layersSortedByZ中layer的数量,接下来就进入各个layer的dump。
例如:
> 0xb3f92000指向当前layer对象的值,括号中是当前layer的名称,id是创建layer时产生的序列号:
- + Layer 0xb3f92000 (com.sec.android.app.launcher/com.android.launcher2.Launcher) id=87
> 区域信息,两段是两个Region的dump,每个region可能包含多个区域,所以这里count也可能不等于1,
前两行的值来源于activeTransparentRegion,表示的是这个layer里面透明区域的大小,
后两行值来源于visibleRegion,表示可见区域的大小:
- Region transparentRegion (this=0xb3f92164, count=1)
- [ 0, 0, 0, 0]
- Region visibleRegion (this=0xb3f92008, count=1)
- [ 0, 0, 1440, 2560]
> 基本信息:
- layerStack= 0, z= 21010, pos=(0,0), size=(1440,2560), crop=(0, 0,1440,2560), isOpaque=0,
- invalidate=0, alpha=0xff, flags=0x00000000, tr=[1.00, 0.00][0.00, 1.00]
- client=0xb11160c0
对应的dumpAllLock中的源码:
- result.appendFormat(
- "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), "
- "isOpaque=%1d, invalidate=%1d, "
- "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
- " client=%p\n",
- s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h,
- s.active.crop.left, s.active.crop.top,
- s.active.crop.right, s.active.crop.bottom,
- isOpaque(s), contentDirty,
- s.alpha, s.flags,
- s.transform[0][0], s.transform[0][1],
- s.transform[1][0], s.transform[1][1],
- client.get());
-
layerStack表示这个layer是保存在哪个layerstack中(不同的display是有不同的layerstack的);
-
z表示Z轴坐标,z值越大,layer越靠上;
-
pos的值是layer左上角的位置,这个值比较特殊的是ImageWallpaper这个layer的pos值,因为ImageWallpaper的大小大于屏幕大小,所以ImageWallpaper的pos值在屏幕的外面(note4是pos=(-560,0)).
-
size自然是layer的大小;
- crop代表裁剪区域,这点依然是对于壁纸很明显,因为壁纸layer大小大于屏幕,必须涉及到需要裁剪一部分显示在屏幕上,因此它的裁剪区域是crop=( 560, 0,2000,2560);
- isOpaque代表是否是不透明的,只有完全不透明的layer这个值才是1,比如壁纸,像状态栏和launcher他们都是0,代表不是完全不透明;
- invalidate表示这个layer的数据是失效的,这个值绝大多数情况下都是0.因为我们看到的一般都是绘制好的有效的数据.一种情况下这值特别频繁的多见为1,就是刚刚锁屏(解锁)时.因为突然锁屏,会导致绘制的内容和要显示的内容完全不同,导致layer的各种数据要重新计算,所以将layer置为失效;
- alpha表示了这张layer的透明度,这个值跟isOpaque是有区别的.isOpaque表示了这个layer可以是透明的,也就是没有显示数据的地方,可以透明;而alpha表示透明度,也即是有数据的地方也可以因为透明度而收到影响产生透明的效果;
- flag值含义丰富,它是众多flag或出来的结果,会a影星layer的状态;
- 接下来的一组tr数据代表屏幕的旋转和缩放程度.大多数的layer实际上是不需要旋转和缩放的,因为他们定义的大小就是跟屏幕一致的,所以他们的这组数据是[1.00, 0.00][0.00, 1.00],实际上如果你使用这组数据来做矩阵变换的话,矩阵是不会发生变化的.需要旋转的比较典型的场景是照相机.横着拿相机时它的layer的变换矩阵是[-1.00, 0.00][-0.00, -1.00],也就是旋转180°. 这个值的来源是上层调用setMatrix函数设置的.
- client含义比较简单,值的来源是创建layer时,对应的SurfaceSession中mNativeClient.这东西也是跟SurfaceSession一一对应的,也就是跟SurfaceFlinger连接时一一对应的.从这个值我们可以判断,client值相同的layer,必然来自同一个进程(因为他们是由同一个连接创建出来的).
(4)打印Displays信息
首先会打印当前display的数量,数量基于mDisplays的大小,这个容器在SurfaceFlinger初始化时会生成数据,后面根据收到不同的消息在handleTransactionLocked函数中也会调整.
正常情况下是1,也就是只有一个display(Built-in Screen),当设备连接了HDMI或者使用了屏幕共享等功能时,会有额外的display加入。
- Displays (2 entries) //这个是连接了HDMI后的数据
- + DisplayDevice: HDMI Screen
- type=1, hwcId=1, layerStack=6, (1920x1080), ANativeWindow=0xb4d94d08, orient= 0 (type=00000000), flips=1173, isSecure=1,
- secureVis=0, powerMode=2, activeConfig=0, numLayers=1
- v:[0,0,1920,1080], f:[0,0,1920,1080], s:[0,0,1920,1080],transform:[[1.000,0.000,-0.000][0.000,1.000,-0.000][0.000,0.000,1.000]]
- mAbandoned=0
- -BufferQueue mMaxAcquiredBufferCount=2, mDequeueBufferCannotBlock=0, default-size=[1920x1080], default-format=1, transform-hint=00,
- FIFO(0)={}
- [00:0xb6418c80] state=FREE , 0xb43ed880 [1920x1080:1920, 1]
- [01:0xb43cb300] state=FREE , 0xb640d970 [1920x1080:1920, 1]
- >[02:0xb43cb280] state=ACQUIRED, 0xb43ed830 [1920x1080:1920, 1]
- + DisplayDevice: Built-in Screen //DisplayDevice是设备的名字,这个可以调用接口设置,但是比较常见的值一般有:Built-in Screen,HDMI Screen,Virtual Screen,wfdservice等等
- type=0, hwcId=0, layerStack=0, (1080x1920), ANativeWindow=0xb4d94608, orient= 0 (type=00000000), flips=3140, isSecure=1,
- secureVis=0, powerMode=2, activeConfig=0, numLayers=2
- v:[0,0,1080,1920], f:[0,0,1080,1920], s:[0,0,1080,1920],transform:[[1.000,0.000,-0.000][0.000,1.000,-0.000][0.000,0.000,1.000]]
五、trace分析
trace.txt生成:当APP(包括系统APP和用户APP)进程出现ANR、应用响应慢或WatchDog的监视没有得到回馈时,系统会dump此时的top进程,进程中Thread的运行状态就都dump到这个Trace文件中了。
ANR一般有三种类型:
1、KeyDispatchTimeout(5 seconds) --主要类型 按键或触摸事件在特定时间内无响应
2、BroadcastTimeout(10 seconds) BroadcastReceiver在特定时间内无法处理完成
3、ServiceTimeout(20 seconds) --小概率类型 Service在特定的时间内无法处理完成
---------------------------------------------------------------
1、adb shell 进入手机的/data/anr文件目录下面查看生成的trace.txt文件
如果ls查看文件列表没有权限,可以先adb root一下
2、adb pull /data/ ./ 将该文件导出,然后分析
---------------------------------------------------------------
log打印了ANR的基本信息(adb shell top查看cg进程,adb logcat -v process |grep PID查看日志),
可以分析CPU使用率得知ANR的简单情况;如果CPU使用率很高,接近100%,可能是在进行大规模的计算更可能是陷入死循环;如果CUP使用率很低,说明主线程被阻塞了,并且当IOwait很高,可能是主线程在等待I/O操作的完成。
对于ANR只是分析Log很难知道问题所在,我们还需要通过Trace文件分析stack调用情况,在log中显示的pid在traces文件中与之对应,然后通过查看堆栈调用信息分析ANR的代码:
(此处trace的分析参考 https://blog.csdn.net/qq_25804863/article/details/49111005 )
- ----- pid 17027 at 2017-06-22 10:37:39 ----- // ANR出现的进程pid和时间
- Cmd line: org.code:MessengerService // ANR出现的进程名(或者进程包名)
- Build fingerprint: 'Homecare/qucii8976v3_64:6.0.1/pansen06141150:eng/test-keys' // 下面记录系统版本,内存等状态信息
- ABI: 'arm64'
- Build type: optimized
- Zygote loaded classes=6576 post zygote classes=13
- Intern table: 13780 strong; 17 weak
- JNI: CheckJNI is on; globals=283 (plus 158 weak)
- Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so /system/lib64/libjavacrypto.so /system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libwebviewchromium_loader.so libjavacore.so (7)
- Heap: 29% free, 8MB/12MB; 75731 objects
- Dumping cumulative Gc timings
- Total number of allocations 75731
- Total bytes allocated 8MB
- Total bytes freed 0B
- Free memory 3MB
- Free memory until GC 3MB
- Free memory until OOME 183MB
- Total memory 12MB
- Max memory 192MB
- Zygote space size 3MB
- Total mutator paused time: 0
- Total time waiting for GC to complete: 0
- Total GC count: 0
- Total GC time: 0
- Total blocking GC count: 0
- Total blocking GC time: 0
- suspend all histogram: Sum: 76us 99% C.I. 0.100us-28us Avg: 7.600us Max: 28us
- DALVIK THREADS (15):
- // Signal Catcher是记录traces信息的线程
- // Signal Catche(线程名)、(daemon)表示守护进程、prio(线程优先级,默认是5)、tid(线程唯一标识ID)、Runnable(线程当前状态)
- "Signal Catcher" daemon prio=5 tid=3 Runnable
- //线程组名称、suspendCount、debugSuspendCount、线程的Java对象地址、线程的Native对象地址
- | group="system" sCount=0 dsCount=0 obj=0x12d8f0a0 self=0x5598ae55d0
- //sysTid是线程号(主线程的线程号和进程号相同)
- | sysTid=17033 nice=0 cgrp=default sched=0/0 handle=0x7fb2350450
- | state=R schedstat=( 4348125 172343 3 ) utm=0 stm=0 core=1 HZ=100
- | stack=0x7fb2254000-0x7fb2256000 stackSize=1013KB
- | held mutexes= "mutator lock"(shared held)
- native: #00 pc 0000000000489e28 /system/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, char const*, art::ArtMethod*, void*)+236)
- native: #01 pc 0000000000458fe8 /system/lib64/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&) const+220)
- native: #02 pc 0000000000465bc8 /system/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+688)
- native: #03 pc 0000000000466ae0 /system/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*)+276)
- native: #04 pc 000000000046719c /system/lib64/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+188)
- native: #05 pc 0000000000467a84 /system/lib64/libart.so (art::ThreadList::DumpForSigQuit(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+492)
- native: #06 pc 0000000000431194 /system/lib64/libart.so (art::Runtime::DumpForSigQuit(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+96)
- native: #07 pc 000000000043e604 /system/lib64/libart.so (art::SignalCatcher::HandleSigQuit()+1256)
- native: #08 pc 000000000043f214 /system/lib64/libart.so (art::SignalCatcher::Run(void*)+452)
- native: #09 pc 0000000000068714 /system/lib64/libc.so (__pthread_start(void*)+52)
- native: #10 pc 000000000001c604 /system/lib64/libc.so (__start_thread+16)
- (no managed stack frames)
- //main(线程名)、prio(线程优先级,默认是5)、tid(线程唯一标识ID)、Sleeping(线程当前状态)
- "main" prio=5 tid=1 Sleeping
- | group="main" sCount=1 dsCount=0 obj=0x73132d10 self=0x5598a5f5e0
- //sysTid是线程号(主线程的线程号和进程号相同)
- | sysTid=17027 nice=0 cgrp=default sched=0/0 handle=0x7fb6db6fe8
- | state=S schedstat=( 420582038 5862546 143 ) utm=24 stm=18 core=6 HZ=100
- | stack=0x7fefba3000-0x7fefba5000 stackSize=8MB
- | held mutexes=
- // java 堆栈调用信息(这里可查看导致ANR的代码调用流程)(分析ANR最重要的信息)
- at java.lang.Thread.sleep!(Native method)
- - sleeping on <0x0c60f3c7> (a java.lang.Object)
- at java.lang.Thread.sleep(Thread.java:1031)
- - locked <0x0c60f3c7> (a java.lang.Object) // 锁住对象0x0c60f3c7
- at java.lang.Thread.sleep(Thread.java:985)
- at android.os.SystemClock.sleep(SystemClock.java:120)
- at org.code.ipc.MessengerService.onCreate(MessengerService.java:63) //导致ANR的代码
- at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877)
- at android.app.ActivityThread.access$1900(ActivityThread.java:150)
- at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
- at android.os.Handler.dispatchMessage(Handler.java:102)
- at android.os.Looper.loop(Looper.java:148)
- at android.app.ActivityThread.main(ActivityThread.java:5417)
- at java.lang.reflect.Method.invoke!(Native method)
- at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
- at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Traces中显示的线程状态都是C代码定义的.我们可以通过查看线程状态对应的信息分析ANR问题
如: TimedWaiting对应的线程状态是TIMED_WAITING
kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout 执行了无超时参数的wait函数
kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep() 执行了带有超时参数的sleep函数
ZOMBIE 线程死亡,终止运行
RUNNING/RUNNABLE 线程可运行或正在运行
TIMED_WAIT 执行了带有超时参数的wait、sleep或join函数
MONITOR 线程阻塞,等待获取对象锁
WAIT 执行了无超时参数的wait函数
INITIALIZING 新建,正在初始化,为其分配资源
STARTING 新建,正在启动
NATIVE 正在执行JNI本地函数
VMWAIT 正在等待VM资源
SUSPENDED 线程暂停,通常是由于GC或debug被暂停