一、首先第一步安装Android SDK,完成后。编写测试脚本,我的脚本已经编写好。具体大家可以从网上Google针对自己的情况再进行详细的编改。
@ECHO OFF
ECHO.:::::::::::::::::::::::::::::::::::::::::::::::::
ECHO.:: 分析Monkey日志 ::
ECHO.:: 版本:V1.0.0 ::
ECHO.:: 部门:新通路 ::
ECHO.:: 脚本编写:宋小宝 ::
ECHO.:: 异常处理:王宝强 ::
ECHO.:::::::::::::::::::::::::::::::::::::::::::::::::
REM 设置Monkey日志路径
SET monkeyLogFile=%cd%\%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%.txt
REM 方法二:直接将Monkey日志拖到此bat文件上
IF NOT "%1"=="" (
SET monkeyLogFile=%1
) ELSE (
ECHO.[ INFO ] Monkey执行中...
REM Monkey执行命令
adb shell monkey -p com.jd.b2r --throttle 500 --ignore-crashes --ignore-timeouts --ignore-security-exceptions --ignore-native-crashes --monitor-native-crashes -v -v -v 30000 > "%monkeyLogFile%"
)
ECHO.[ INFO ] Monkey日志: %monkeyLogFile%
ECHO.[ INFO ] 开始分析
SET blnException=0
ECHO.
ECHO.
REM 如果觉得分析太快,没有感觉,把下面注释去掉假装分析中,有停顿感
ping -n 2 127.0.0.1>nul
::ANR日志
FOR /F "delims=" %%a IN ('FINDSTR /C:"ANR" %monkeyLogFile%') DO (
SET strANR=%%a
)
::崩溃日志
FOR /F "delims=" %%a IN ('FINDSTR /C:"CRASH" %monkeyLogFile%') DO (
SET strCRASH=%%a
)
::异常日志
FOR /F "delims=" %%a IN ('FINDSTR /C:"Exception" %monkeyLogFile%') DO (
SET strException=%%a
)
::正常
FOR /F "delims=" %%a IN ('FINDSTR /C:"Monkey finished" %monkeyLogFile%') DO (
SET strFinished=%%a
)
IF NOT "%strANR%" == "" (
ECHO.[ INFO ] 分析Monkey日志存在: ANR
ECHO.[ INFO ] ------------------------------------
ECHO. "%strANR%"
SET /a blnException+=1
ECHO.
)
IF NOT "%strCRASH%" == "" (
ECHO.[ INFO ] 分析Monkey日志存在: CRASH
ECHO.[ INFO ] ------------------------------------
ECHO. "%strCRASH%"
SET /a blnException+=1
ECHO.
)
IF NOT "%strException%" == "" (
ECHO.[ INFO ] 分析Monkey日志存在: 异常
ECHO.[ INFO ] ------------------------------------
ECHO. "%strException%"
SET /a blnException+=1
)
IF NOT "%strFinished%" == "" (
ECHO.[ INFO ] 分析Monkey日志存在: 执行成功标记
ECHO.[ INFO ] ------------------------------------
ECHO. "%strFinished%"
ECHO.
) ELSE (
IF %blnException% EQU 0 ECHO.[ INFO ] 分析Monkey日志结果: Monkey执行异常中断,请重新执行Monkey脚本!
ECHO.
)
REM 如果blnException不为0,说明存在异常,改变字体为淡紫色
IF %blnException% NEQ 0 (
Color 0D
ECHO.[ INFO ] 分析Monkey日志结果:存在异常日志,请手工再仔细检查!
ECHO.
) ELSE (
ECHO.[ INFO ] 分析Monkey日志结果:正常
ECHO.
)
ECHO.
ECHO.[ EXIT ] 按任意键关闭窗口...
PAUSE>nul
二、将以上脚本复制到一个文本文件中,命名为xx.bat
三、对脚本文件进行二次编辑
- 获取包名:①aapt dump badging [XXaaayy.apk],例如:package: name='com.jddl.rbr'②只记得大概,不记得详细包名。例如adb shell pm list packages b2
- 修改命令adb shell monkey -p com.jddl.rbr --throttle 500 --ignore-crashes --ignore-timeouts --ignore-security-exceptions --ignore-native-crashes --monitor-native-crashes -v -v -v 100000 > "%monkeyLogFile%"中的包名,如:com.jddl.rbr
- 修改monkey执行次数:-v -v -v 100000,这个100000代表执行100000次点击。
- 保存,连接手机,打开手机开发调试模式
- Monkey命令停止:cmd命令下输入adb shell 进入命名模式,执行命令ps | grep monkey 查看进程ID,kill +进程ID,杀掉进程
四、命令简解
100000 执行伪随机事件的总操作次数
-p com.jddl.rbr 指定包名,如果未指定,如:adb shell monkey 1000。将命令随机发送到手机中的所有APP,随机执行1000个次事件
--throttle 500 单位毫秒。每执行一系列操作事件,停顿休息500ms
-v 日志级别。最高为三个,-v -v -v为打印输出最详细的日志,自己可以修改查看具体效果
--ignore-crashes 忽略崩溃/--ignore-timeouts 忽略超时/--ignore-security-exceptions 忽略安全异常。APP在操作过程中会存在崩溃、超时、异常等情况,导致monkey直接停止测试。忽略这些异常崩溃可继续执行,直到执行随机时间次数达到设定值。当不作任何配置的时候,默认为--pct-anyevent <percent>,此时所有事件的触发均为随机的。当所有事件的百分比加起重来小于100%的时候,多余的随机事件也将被转化为--pct-anyevent <percent>事件。超过时,系统会重新运算相应的比例
-s 伪随机数生成器的seed值。monkey允许对随机事件的seed值做设置,当两次seed值一样,整个测试效果将会产生相同的事件序列。例如:monkey -pcom.tencent.mtaexample -s 23 --throttle2000 --ignore-crashes --ignore-timeouts -v -v -v 100000>/data/local/tmp/log.txt 2>&1 &(其中-s后面是对应的种子数,好像就是操作步骤,根据她们测试的经验,一般种子数在23,同步她们测试的结果,一般种子的个数固定为23,和她们选择的操作步骤就是同步的。2>&1 固定的写法,这个也很重要,代表的意思是中间忽略的东东的日志一并输入到指定的文件中。最后单独的一个"&" 是一旦Monkey测试开始了,之后可以拔掉数据线,不会影响Monkey测试。)
* --pct-touch <percent>:指定触摸事件的百分比。
* --pct-motion <percent>:指定滑动事件的百分比。
* --pct-trackball <percent>:指定轨迹球事件的百分比。
* --pct-nav <percent>:指定导航事件中,up、down、left、right等事件的百分比。
* --pct-majornav <percent>:指定导航事件中,back、menu等事件的百分比。
* --pct-syskeys <percent>:指定系统按键的百分比,包括HOME、Back、音量等。
* --pct-appswitch <percent>:指定Activity之间切换的比例。
* --pct-anyevent <percent>:指定任意事件的百分比。
执行adb shell --help,查看帮助
usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]//调试的应用包名
[-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]//测试对应的categrory
[--ignore-crashes] [--ignore-timeouts]
[--ignore-security-exceptions]
[--monitor-native-crashes] [--ignore-native-crashes]//忽略系列的大爷们
[--kill-process-after-error] [--hprof]//默认
[--pct-touch PERCENT] [--pct-motion PERCENT]
[--pct-trackball PERCENT] [--pct-syskeys PERCENT]
[--pct-nav PERCENT] [--pct-majornav PERCENT]
[--pct-appswitch PERCENT] [--pct-flip PERCENT]
[--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]//事件百分比设置
[--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]
[--pkg-whitelist-file PACKAGE_WHITELIST_FILE]
[--wait-dbg] [--dbg-no-events]
[--setup scriptfile] [-f scriptfile [-f scriptfile] ...]
[--port port]
[-s SEED] [-v [-v] ...]//设置随机数生成器的seed值
[--throttle MILLISEC] [--randomize-throttle]//延时操作
[--profile-wait MILLISEC]
[--device-sleep-time MILLISEC]
[--randomize-script]
[--script-log]
[--bugreport]
[--periodic-bugreport]
COUNT
五、日志分析
Monkey测试结果分析
一. 初步分析方法:
Monkey测试出现错误后,一般的查错步骤为以下几步:
1、找到是monkey里面的哪个地方出错
2、查看Monkey里面出错前的一些事件动作,并手动执行该动作
3、若以上步骤还不能找出,可以使用之前执行的monkey命令再执行一遍,注意seed值要一样--复现
一般的测试结果分析:
1、 ANR问题:在日志中搜索“ANR”
2、崩溃问题:在日志中搜索“Exception” Force Close
二. 详细分析monkey日志:
将执行Monkey生成的log,从手机中导出并打开查看该log;在log的最开始都会显示Monkey执行的seed值、执行次数和测试的包名。
首先我们需要查看Monkey测试中是否出现了ANR或者异常,具体方法如上述。
然后我们要分析log中的具体信息,方法如下:
查看log中第一个Switch,主要是查看Monkey执行的是那一个Activity,譬如下面的log中,执行的是com.tencent.smtt.SplashActivity,在下一个swtich之间的,如果出现了崩溃或其他异常,可以在该Activity中查找问题的所在。
:Switch:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=com.tencent.smtt/.SplashActivity;end
// Allowing start of Intent {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]cmp=com.tencent.smtt/.SplashActivity } in package com.tencent.smtt
在下面的log中,Sending Pointer ACTION_DOWN和Sending Pointer ACTION_UP代表当前执行了一个单击的操作;
Sleeping for 500 milliseconds这句log是执行Monkey测试时,throttle设定的间隔时间,每出现一次,就代表一个事件。
SendKey(ACTION_DOWN) //KEYCODE_DPAD_DOWN 代表当前执行了一个点击下导航键的操作;
Sending Pointer ACTION_MOVE 代表当前执行了一个滑动界面的操作。
:Sending Pointer ACTION_DOWN x=47.0 y=438.0
:Sending Pointer ACTION_MOVE x=-2.0 y=-4.0
ANR
如果Monkey测试顺利执行完成,在log的最后,会打印出当前执行事件的次数和所花费的时间;// Monkey finished代表执行完成。Monkey执行中断,在log的最后也能查看到当前已执行的次数。Monkey执行完成的log具体如下:
Events injected: 6000
:Dropped: keys=0 pointers=9 trackballs=0 flips=0
## Network stats: elapsed time=808384ms (0ms mobile, 808384ms wifi, 0msnot connected)
// Monkey finished
范例:
Monkey测试结果:
# monkey -p wfh.LessonTable -v -v -v 200
:Monkey: seed=0 count=200
:AllowPackage: wfh.LessonTable
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
// Selecting main activities from category android.intent.category.LAUNCHER
// - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser)
// Seeded: 0
// Event percentages:
// 0: 15.0% --事件0 为touch事件,就是相当于按下之后弹起来的一个动作
// 1: 10.0% --事件1 为motion,相当于说从起始点到终点有移动了多少步,就是步骤数量
// 2: 2.0% --事件2 为pinchzoom,为两个手指有同时按下去后,都向中间移动后up起来,相当于一个缩放的动作。
// 3: 15.0% --事件3 为trackball,为轨迹球事件
// 4: -0.0% --事件4 为rotation 为屏幕旋转百分比隐藏事件
// 5: 25.0% --事件5 为nav导航事件,就是上下左右
// 6: 15.0% --事件6 为majornav主导航事件,会产生一些窗口的事件
// 7: 2.0% --事件7 为系统按键
// 8: 2.0% --事件8,app应用的打开就是用的这个事件
// 9: 1.0% --事件9,键盘的开,关
// 10: 13.0% --事件10,按键按下在弹起等动作
:Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=wfh.LessonTable/.MainTable;end
// Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=wfh.LessonTable/.MainTable } in package wfh.LessonTable
Sleeping for 0 milliseconds
:SendKey (ACTION_DOWN): 21 // KEYCODE_DPAD_LEFT
:SendKey (ACTION_UP): 21 // KEYCODE_DPAD_LEFT
Sleeping for 0 milliseconds //------------------------------------用--throttle来设置一个起效的事件发生后时延时。
:Sending Pointer ACTION_DOWN x=0.0 y=0.0
:Sending Pointer ACTION_UP x=0.0 y=0.0
Sleeping for 0 milliseconds
:Sending Pointer ACTION_MOVE x=0.0 y=0.0
当测试到ACTION_MOVE x=0.0 y=0.0这个动作时,发生了FC(Force Close)错误,以下为输出错误信息。同时在LogCat里面也有错误输出,而且LogCat里面的错误信息更为详细,在实际的测试中应该结合两者输出的信息进行调试程序。
// CRASH: wfh.LessonTable (pid 1973)
// Short Msg: java.lang.NullPointerException
// Long Msg: java.lang.NullPointerException
// Build Label: android:generic/sdk/generic/:2.1-update1/ECLAIR/35983:eng/test-keys
// Build Changelist: 35983
// Build Time: 1273161972
// ID:
// Tag: AndroidRuntime
// java.lang.NullPointerException:
// at android.widget.TabHost.dispatchKeyEvent(TabHost.java:279)
// at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:748)
** Monkey aborted due to error.
Events injected: 190
:Dropped: keys=0 pointers=11 trackballs=0 flips=0
## Network stats: elapsed time=27954ms (27954ms mobile, 0ms wifi, 0ms not connected)
** System appears to have crashed at event 190 of 200 using seed 0
#
开始monkey测试时android的LogCat输出的信息:
11-01 08:52:53.712: DEBUG/AndroidRuntime(2077): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
11-01 08:52:53.742: DEBUG/AndroidRuntime(2077): CheckJNI is ON
11-01 08:52:54.453: DEBUG/AndroidRuntime(2077): ---
以下为LogCat输出的错误信息,在以下的信息中首先从自己的包中找错误,如果没有自己的包的话就再找发生错误的包的第一个发生了异常。由错误提示可以看出很大的可能是因为TabHost引发的异常。经过查看代码发现是由于TabHost的编写不规范,TabHost与其中一个view放在了一起,在monkey测试做滚球上下滚动时当滚到TabHost时就发生了异常了。所以把TabHost与Activity分开写就不会出现些问题了。
11-01 08:53:27.113: ERROR/AndroidRuntime(1973): Uncaught handler: thread main exiting due to uncaught exception
11-01 08:53:27.133: ERROR/AndroidRuntime(1973): java.lang.NullPointerException
11-01 08:53:27.133: ERROR/AndroidRuntime(1973): at android.widget.TabHost.dispatchKeyEvent(TabHost.java:279)
11-01 08:53:27.133: ERROR/AndroidRuntime(1973): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:748)
六、附录:
以下内容为android系统中的keycode值,在以后的调试中会经常需要查询:
字母和数字键的键码值(keyCode) | |||||||
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
A |
65 |
J |
74 |
S |
83 |
1 |
49 |
B |
66 |
K |
75 |
T |
84 |
2 |
50 |
C |
67 |
L |
76 |
U |
85 |
3 |
51 |
D |
68 |
M |
77 |
V |
86 |
4 |
52 |
E |
69 |
N |
78 |
W |
87 |
5 |
53 |
F |
70 |
O |
79 |
X |
88 |
6 |
54 |
G |
71 |
P |
80 |
Y |
89 |
7 |
55 |
H |
72 |
Q |
81 |
Z |
90 |
8 |
56 |
I |
73 |
R |
82 |
0 |
48 |
9 |
57 |
数字键盘上的键的键码值(keyCode) |
功能键键码值(keyCode) |
||||||
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
0 |
96 |
8 |
104 |
F1 |
112 |
F7 |
118 |
1 |
97 |
9 |
105 |
F2 |
113 |
F8 |
119 |
2 |
98 |
* |
106 |
F3 |
114 |
F9 |
120 |
3 |
99 |
+ |
107 |
F4 |
115 |
F10 |
121 |
4 |
100 |
Enter |
108 |
F5 |
116 |
F11 |
122 |
5 |
101 |
- |
109 |
F6 |
117 |
F12 |
123 |
6 |
102 |
. |
110 |
|
|
|
|
7 |
103 |
/ |
111 |
|
|
|
|
控制键键码值(keyCode) | |||||||
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
BackSpace |
8 |
Esc |
27 |
Right Arrow |
39 |
-_ |
189 |
Tab |
9 |
Spacebar |
32 |
Dw Arrow |
40 |
.> |
190 |
Clear |
12 |
Page Up |
33 |
Insert |
45 |
/? |
191 |
Enter |
13 |
Page Down |
34 |
Delete |
46 |
`~ |
192 |
Shift |
16 |
End |
35 |
Num Lock |
144 |
[{ |
219 |
Control |
17 |
Home |
36 |
;: |
186 |
\| |
220 |
Alt |
18 |
Left Arrow |
37 |
=+ |
187 |
]} |
221 |
Cape Lock |
20 |
Up Arrow |
38 |
,< |
188 |
'" |
222 |
多媒体键码值(keyCode) | |||||||
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
按键 |
键码 |
音量加 |
175 |
|
|
|
|
|
|
音量减 |
174 |
|
|
|
|
|
|
停止 |
179 |
|
|
|
|
|
|
静音 |
173 |
|
|
|
|
|
|
浏览器 |
172 |
|
|
|
|
|
|
邮件 |
180 |
|
|
|
|
|
|
搜索 |
170 |
|
|
|
|
|
|
收藏 |
171 |
|
|
|
|
|
|
摘录另一种日志分析:https://www.cnblogs.com/kekouwen/p/9995635.html
1.在日志中搜索关键字:
1)搜索报告中的关键字“ANR”,看有无应用无响应的事件(Application Not Responding)
2)搜索报告中的关键字“crash”,看有无崩溃的事件
3)搜索报告中的关键字“exception”,看有无其他异常事件。(如果出现空指针, NullPointerException,需格外重视)
下面的属于monkey自己的问题。不用管。
:Sending Flip keyboardOpen=false
Got IOException performing flipjava.io.IOException: write failed: EINVAL (Invalid argument)
// Injection Failed
4)内存泄露问题搜索"GC"(需进一步分析)
2. 初步分析法: monkey出现错误后,一般的分析步骤
1)先找到出现错误的位置
2)查看出现错误之前2个switch之间的activity
3)手动执行事件,复现问题
4)若以上步骤还不能找出,产生错误时,有会seed值,输入相同的seed值,重新按照之前命令跑monkey
3.详细分析法:
1) ANR问题:在日志中搜索“ANR”(“Application Not Responding"),说明有bug,出现ANR,一般是主线程的响应超过5秒,或者BroadcastReceiver没有在10秒内作出响应。这个就是一个比较严重的缺陷。把耗时的操作另起线程来处理就可以了。
2)分析log中的具体信息:
查看log中第一个Switch,主要是查看Monkey执行的是那一个Activity,譬如下面的log中,执行的是com.tencent.smtt.SplashActivity,在下一个swtich之间的,如果出现了崩溃或其他异常,可以在该Activity中查找问题的所在。
:Switch:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=com.tencent.smtt/.SplashActivity;end
// Allowing start of Intent {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]cmp=com.tencent.smtt/.SplashActivity } in package com.tencent.smtt
3)内存泄漏
1. 内存泄漏弹出out of memory对话框
2. 对于有内存泄漏但是没有单出out of memory对话框的情况,可以通过logcat文件GC出信息,(GC:java的垃圾回收机制)
GC_FOR_ALLOC: 因为在分配内存时候内存不够引起的
GC_EXPLICIT 表明GC被显式请求触发的,如System.gc调用,
GC_CONCCURRENT: 表明GC在内存使用率达到一定的警戒值时候,自动触发
GC_BEFORE_OOM 表明在虚拟机抛出内存不够异常oom之前,执行最后一次回收内存垃圾
2.发现内存泄露--内存报告分析(利用hprof参数的内存快照生成内存报告)
在发现内存泄露后,可以执行相同的monkey,只需多加一个参数--hprof
adb shell monkey -p 包名 --hprof --throttle 100 --pct-touch 50 --pct-motion 50 -v -v -v 1000 >c:\monkey.txt
如果指定了这个选项,monkey会在发送时间的前后生成app内存快照文件,一般会在手机设备的/data/misc目录下生成hprof的文件。(注: /data/misc 需要root权限,可
以在手机上安装个RE查看或通过手机助手查看)
ps:文件转换:配置monkey测试时的sdk-tools下查看是否hprof-conv命令,在命令行输入hprof-conv -help得知文件转化用法,直接转化就行,由.hprof转化成.conv格式。
转化后的文件用eclipse的Memory Analyzer tool(MAT)查看(此插件可以下载),可以点击 Reports->Leak Suspects链接来生成报告。