一、昨天看了Monkey的代码,里边有几个adb shell的命令,记录一下。
1、procrank
2、cat /data/anr/traces.txt
抓取anr日志
3、dumpsys meminfo
查看内存详情
4、/data/tombstones
每当有native crash就会在这个目录下生成一个新文件,里边有native crash的堆栈及其他详细信息
要看native crash的堆栈我们也会用一下命令:
adb logcat | ndk-stack -sym navisdk/src/main/obj/local/armeabi
但这个要在crash发生前调用才生效。
5、这些命令是在android里通过代码执行的
/**
* Print report from a single command line.
* @param reportName Simple tag that will print before the report and in various annotations.
* @param command Command line to execute.
* TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both streams (might be
* important for some command lines)
*/
private void commandLineReport(String reportName, String command) {
System.err.println(reportName + ":");
Runtime rt = Runtime.getRuntime();
try {
// Process must be fully qualified here because android.os.Process is used elsewhere
java.lang.Process p = Runtime.getRuntime().exec(command);
// pipe everything from process stdout -> System.err
InputStream inStream = p.getInputStream();
InputStreamReader inReader = new InputStreamReader(inStream);
BufferedReader inBuffer = new BufferedReader(inReader);
String s;
while ((s = inBuffer.readLine()) != null) {
System.err.println(s);
}
int status = p.waitFor();
System.err.println("// " + reportName + " status was " + status);
} catch (Exception e) {
System.err.println("// Exception from " + reportName + ":");
System.err.println(e.toString());
}
}
二、从源码里看下monkey的日志打印机制
1、
public int appNotResponding(String processName, int pid,
String processStats) {
System.err.println("// NOT RESPONDING: " + processName
+ " (pid " + pid + ")");
System.err.println(processStats);
reportProcRank();
synchronized (Monkey.this) {
mRequestAnrTraces = true;
mRequestDumpsysMemInfo = true;
}
if (!mIgnoreTimeouts) {
synchronized (Monkey.this) {
mAbort = true;
}
return (mKillProcessAfterError) ? -1 : 1;
}
return 1;
}
以上是moneky处理anr异常的代码,因为打印开关是由mRequestAnrTraces和mRequestDumpsysMemInfo两个标志位控制的。另外出现anr的地方会马上打印procrank命令输出,可以通过在日志文件里搜索“NOT RESPONDING”来看是否有anr异常。
2、
public boolean appCrashed(String processName, int pid, String shortMsg,
String longMsg, byte[] crashData) {
System.err.println("// CRASH: " + processName + " (pid " + pid
+ ")");
System.err.println("// Short Msg: " + shortMsg);
System.err.println("// Long Msg: " + longMsg);
if (crashData != null) {
try {
CrashData cd = new CrashData(new DataInputStream(
new ByteArrayInputStream(crashData)));
System.err.println("// Build Label: "
+ cd.getBuildData().getFingerprint());
System.err.println("// Build Changelist: "
+ cd.getBuildData().getIncrementalVersion());
System.err.println("// Build Time: "
+ cd.getBuildData().getTime());
System.err.println("// ID: " + cd.getId());
System.err.println("// Tag: " + cd.getActivity());
System.err.println(cd.getThrowableData().toString(
"// "));
} catch (IOException e) {
System.err.println("// BAD STACK CRAWL");
}
}
if (!mIgnoreCrashes) {
synchronized (Monkey.this) {
mAbort = true;
}
return !mKillProcessAfterError;
}
return false;
}
如果设置了--ignore-crashes,mIgnoreCrashes会置为true,crash会在moneky日志中打印,但不会停止发送事件,要搜crash日志可以搜索
“CRASH”关键字(见以上代码),要注意此时moneky日志结束处没有crash的提示。
3、native crash
private int runMonkeyCycles() {
int i = 0;
int lastKey = 0;
boolean systemCrashed = false;
while (!systemCrashed && i < mCount) {
synchronized (this) {
if (mRequestAnrTraces) {
reportAnrTraces();
mRequestAnrTraces = false;
}
if (mRequestDumpsysMemInfo) {
reportDumpsysMemInfo();
mRequestDumpsysMemInfo = false;
}
if (mMonitorNativeCrashes) {
// first time through, when i == 0, just set up the watcher (ignore the error)
if (checkNativeCrashes() && (i > 0)) {
System.out.println("** New native crash detected.");
mAbort = mAbort || mKillProcessAfterError;
}
}
if (mAbort) {
System.out.println("** Monkey aborted due to error.");
System.out.println("Events injected: " + i);
return i;
}
}
主循环中每次发出一系列事件前会检查,如果上次的一系列事件出现了anr,会再下一系列事件发送前打印traces.txt和执行dumpsys meminfo,同时把标志位复位。另外如果监听native crash,会打印
"** New native crash detected.",事后要知道是具体什么原因的话,只能自己去查/data/tombstones目录了。
4、下面就是monkey的不同类别动作会按到的一些按键,都定义在代码里
/** Key events that move around the UI. */
private static final int[] NAV_KEYS = {
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
};
/**
* Key events that perform major navigation options (so shouldn't be sent
* as much).
*/
private static final int[] MAJOR_NAV_KEYS = {
KeyEvent.KEYCODE_MENU, /*KeyEvent.KEYCODE_SOFT_RIGHT,*/
KeyEvent.KEYCODE_DPAD_CENTER,
};
/** Key events that perform system operations. */
private static final int[] SYS_KEYS = {
KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_CALL, KeyEvent.KEYCODE_ENDCALL,
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN,
KeyEvent.KEYCODE_MUTE,
};
/** Nice names for all key events. */
private static final String[] KEY_NAMES = {
"KEYCODE_UNKNOWN",
"KEYCODE_MENU",
"KEYCODE_SOFT_RIGHT",
"KEYCODE_HOME",
"KEYCODE_BACK",
"KEYCODE_CALL",
"KEYCODE_ENDCALL",
"KEYCODE_0",
"KEYCODE_1",
"KEYCODE_2",
"KEYCODE_3",
"KEYCODE_4",
"KEYCODE_5",
"KEYCODE_6",
"KEYCODE_7",
"KEYCODE_8",
"KEYCODE_9",
"KEYCODE_STAR",
"KEYCODE_POUND",
"KEYCODE_DPAD_UP",
"KEYCODE_DPAD_DOWN",
"KEYCODE_DPAD_LEFT",
"KEYCODE_DPAD_RIGHT",
"KEYCODE_DPAD_CENTER",
"KEYCODE_VOLUME_UP",
"KEYCODE_VOLUME_DOWN",
"KEYCODE_POWER",
"KEYCODE_CAMERA",
"KEYCODE_CLEAR",
"KEYCODE_A",
"KEYCODE_B",
"KEYCODE_C",
"KEYCODE_D",
"KEYCODE_E",
"KEYCODE_F",
"KEYCODE_G",
"KEYCODE_H",
"KEYCODE_I",
"KEYCODE_J",
"KEYCODE_K",
"KEYCODE_L",
"KEYCODE_M",
"KEYCODE_N",
"KEYCODE_O",
"KEYCODE_P",
"KEYCODE_Q",
"KEYCODE_R",
"KEYCODE_S",
"KEYCODE_T",
"KEYCODE_U",
"KEYCODE_V",
"KEYCODE_W",
"KEYCODE_X",
"KEYCODE_Y",
"KEYCODE_Z",
"KEYCODE_COMMA",
"KEYCODE_PERIOD",
"KEYCODE_ALT_LEFT",
"KEYCODE_ALT_RIGHT",
"KEYCODE_SHIFT_LEFT",
"KEYCODE_SHIFT_RIGHT",
"KEYCODE_TAB",
"KEYCODE_SPACE",
"KEYCODE_SYM",
"KEYCODE_EXPLORER",
"KEYCODE_ENVELOPE",
"KEYCODE_ENTER",
"KEYCODE_DEL",
"KEYCODE_GRAVE",
"KEYCODE_MINUS",
"KEYCODE_EQUALS",
"KEYCODE_LEFT_BRACKET",
"KEYCODE_RIGHT_BRACKET",
"KEYCODE_BACKSLASH",
"KEYCODE_SEMICOLON",
"KEYCODE_APOSTROPHE",
"KEYCODE_SLASH",
"KEYCODE_AT",
"KEYCODE_NUM",
"KEYCODE_HEADSETHOOK",
"KEYCODE_FOCUS",
"KEYCODE_PLUS",
"KEYCODE_MENU",
"KEYCODE_NOTIFICATION",
"KEYCODE_SEARCH",
"KEYCODE_PLAYPAUSE",
"KEYCODE_STOP",
"KEYCODE_NEXTSONG",
"KEYCODE_PREVIOUSSONG",
"KEYCODE_REWIND",
"KEYCODE_FORWARD",
"KEYCODE_MUTE",
//BORQS_EXT
"KEYCODE_MOBILETV",
"KEYCODE_LANGUAGE",
"KEYCODE_VOICE_RECOGNIZER",
"KEYCODE_CUSTOMER",
//END
"TAG_LAST_KEYCODE" // EOL. used to keep the lists in sync
};
public static final int FACTOR_TOUCH = 0;
public static final int FACTOR_MOTION = 1;
public static final int FACTOR_TRACKBALL = 2;
public static final int FACTOR_NAV = 3;
public static final int FACTOR_MAJORNAV = 4;
public static final int FACTOR_SYSOPS = 5;
public static final int FACTOR_APPSWITCH = 6;
public static final int FACTOR_FLIP = 7;
public static final int FACTOR_ANYTHING = 8;
public static final int FACTORZ_COUNT = 9; // should be last+1
最下面的那个10个int类型变量就是对应monkey日志开头的那些百分比
// Event percentages:
// 0: 55.0%
// 1: 45.0%
// 2: 0.0%
// 3: -0.0%
// 4: -0.0%
// 5: -0.0%
// 6: -0.0%
// 7: -0.0%
// 8: -0.0%
// 9: 0.0%
// 10: -0.0%