0. 前言
GTS 在测试 case armeabi-v7a GtsMemoryTestCases 的时候出现下面异常,本文总结一下。
com.google.android.memory.gts.MemoryTest#testPersistentProcessMemory
1. error log
09-14 09:41:40.523 10182 13340 13359 E TestRunner: failed: testPersistentProcessMemory(com.google.android.memory.gts.MemoryTest)
09-14 09:41:40.523 10182 13340 13359 E TestRunner: ----- begin exception -----
09-14 09:41:40.525 10182 13340 13359 E TestRunner: java.lang.AssertionError: memory usage for persistent processes is too high: 140867 > 133120
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.Assert.fail(Assert.java:89)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.Assert.assertTrue(Assert.java:42)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at com.google.android.memory.gts.MemoryTest.testPersistentProcessMemory(MemoryTest.java:75)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at java.lang.reflect.Method.invoke(Native Method)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:61)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:148)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:142)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: at java.lang.Thread.run(Thread.java:923)
09-14 09:41:40.525 10182 13340 13359 E TestRunner: ----- end exception -----
09-14 09:41:40.530 10182 13340 13359 I TestRunner: finished: testPersistentProcessMemory(com.google.android.memory.gts.MemoryTest)
从log 上来看,出现了 persistent processes 使用的memory 太高了,达到了140867Kb ,而GTS 测试允许的 persistent processes memory 为 133120Kb。
E TestRunner: java.lang.AssertionError: memory usage for persistent processes is too high: 140867 > 133120
2. source code
同其他的GTS 测试,找到对应的测试case,将其反编译之后就可以查看源码:
package com.google.android.memory.gts;
import android.app.ActivityManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.DynamicConfigDeviceSide;
import com.android.compatibility.common.util.SystemUtil;
import com.android.xts.common.util.GmsUtil;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class MemoryTest {
private static final Pattern DEVICE_HEIGHT_PATTERN = Pattern.compile("deviceHeight=(\\d+)");
private static final Pattern DEVICE_WIDTH_PATTERN = Pattern.compile("deviceWidth=(\\d+)");
private static final String DUMPSYS_DISPLAY = "dumpsys display";
private static final String DUMPSYS_MEMINFO_CMD = "dumpsys -t 30 meminfo";
private static final String MEMORY_USAGE_HIGH_MSG = "memory usage for persistent processes is too high: %d > %d";
private static final long ONE_MEGABYTE = 1048576;
private static final String PERSISTENT_MEMORY_KEY_FORMAT = "max_memory_persistent_%s_%s";
private static final String PERSISTENT_MEMORY_REGEX = "(?=([\\d,]+)K: Persistent)(?!.*Service).*";
private ActivityManager mActivityManager;
@Before
public void checkGoDevice() {
Assume.assumeTrue("Skipping MemoryTest on non-Go device", GmsUtil.isGoDevice());
this.mActivityManager = (ActivityManager) InstrumentationRegistry.getTargetContext().getSystemService(ActivityManager.class);
}
@Test
public void testPersistentProcessMemory() throws Exception {
long memoryKb = calculatePersistentMemoryUsage();
long maxMemoryKb = getMaxPersistentMemoryAllowed();
boolean z = false;
String format = String.format(MEMORY_USAGE_HIGH_MSG, new Object[]{Long.valueOf(memoryKb), Long.valueOf(maxMemoryKb)});
if (memoryKb < maxMemoryKb) {
z = true;
}
Assert.assertTrue(format, z);
}
private long calculatePersistentMemoryUsage() throws Exception {
String memoryUsage = findMatch(PERSISTENT_MEMORY_REGEX, SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), DUMPSYS_MEMINFO_CMD));
Assert.assertNotNull("Could not get meminfo total", memoryUsage);
return Long.valueOf(memoryUsage.replaceAll("[^0-9]", "")).longValue();
}
private long getMaxPersistentMemoryAllowed() throws Exception {
return getLongFromConfig(keyForDeviceBasedOnMemoryAndScreenLayoutSize());
}
private String findMatch(String regex, String input) {
Matcher matcher = Pattern.compile(regex).matcher(input);
ArrayList arrayList = new ArrayList();
while (matcher.find()) {
arrayList.add(matcher.group(0));
}
if (arrayList.isEmpty()) {
return null;
}
return (String) (arrayList.size() > 1 ? arrayList.get(1) : arrayList.get(0));
}
private long getLongFromConfig(String key) throws Exception {
DynamicConfigDeviceSide dcds = new DynamicConfigDeviceSide("GtsMemoryTestCases");
Assert.assertNotNull("Unable to get key from config: " + key, dcds.getValue(key));
return new Long(dcds.getValue(key)).longValue();
}
private String keyForDeviceBasedOnMemoryAndScreenLayoutSize() throws Exception {
return String.format(PERSISTENT_MEMORY_KEY_FORMAT, new Object[]{memoryClass(), calculateScreenLayoutSize()});
}
private String memoryClass() throws Exception {
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
this.mActivityManager.getMemoryInfo(memoryInfo);
long totalMemoryBytes = memoryInfo.totalMem;
if (totalMemoryBytes <= 536870912) {
return "512";
}
if (totalMemoryBytes <= 1073741824) {
return "1gb";
}
return "2gb";
}
private String calculateScreenLayoutSize() throws Exception {
Matcher matcher = DEVICE_WIDTH_PATTERN.matcher(SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), DUMPSYS_DISPLAY));
Assert.assertTrue("deviceWidth not found", matcher.find());
long deviceWidth = Long.valueOf(matcher.group(1)).longValue();
matcher.usePattern(DEVICE_HEIGHT_PATTERN);
Assert.assertTrue("deviceHeight not found", matcher.find());
long deviceHeight = Long.valueOf(matcher.group(1)).longValue();
long shortSide = Math.min(deviceWidth, deviceHeight);
long longSide = Math.max(deviceWidth, deviceHeight);
if (shortSide <= 480 && longSide <= 640) {
return "vga";
}
if (shortSide <= 480 && longSide <= 854) {
return "wvga";
}
if (shortSide > 540 || longSide > 960) {
return "hd";
}
return "qhd";
}
}
进入测试函数 testPersistentProcessMemory():
- 首先通过 calculatePersistentMemoryUsage() 函数确定经过 dupmsys -t 30 meminfo 之后所有 Persistent 进程的内存,存放在变量 memoryKb 中。
- 接着通过 getMaxPersistentMemoryAllowed() 函数,确定 GTS 允许的最大 persistent memory,存放在变量 maxMemoryKb中。
- 主要是解析 GtsMemoryTestCases.dynamic 文件中的某一个属性值;
- 该dynamic 中属性 key 通过函数 keyForDeviceBasedOnMemoryAndScreenLayoutSize() 获取;
- 从 log 来看,显然解析的是 max_memory_persistent_2gb_hd 属性的值 133120;
- GTS 要求,memoryKb 不能大于等于 maxMemroyKb;
另外,需要注意的是memoryKb 是通过 Java 的Pattern 类进行正则表达式匹配:
private static final String PERSISTENT_MEMORY_REGEX = "(?=([\\d,]+)K: Persistent)(?!.*Service).*";
应该就是匹配 dumpsys meminfo 中带有 Persistent 的一行,但不包括Persistent Service 这一行。例如,
261,364K: Persistent
但不统计:
26,620K: Persistent Service
2.1 GtsMemroyTestCases.synamic
<dynamicConfig>
<entry key="max_memory_persistent">
<value>90000</value>
</entry>
<entry key="max_memory_persistent_512_vga">
<value>81920</value>
</entry>
<entry key="max_memory_persistent_512_wvga">
<value>87040</value>
</entry>
<entry key="max_memory_persistent_1gb_vga">
<value>87040</value>
</entry>
<entry key="max_memory_persistent_1gb_wvga">
<value>92160</value>
</entry>
<entry key="max_memory_persistent_1gb_qhd">
<value>97280</value>
</entry>
<entry key="max_memory_persistent_1gb_hd">
<value>102400</value>
</entry>
<entry key="max_memory_persistent_2gb_vga">
<value>102400</value>
</entry>
<entry key="max_memory_persistent_2gb_wvga">
<value>112640</value>
</entry>
<entry key="max_memory_persistent_2gb_qhd">
<value>122880</value>
</entry>
<entry key="max_memory_persistent_2gb_hd">
<value>133120</value>
</entry>
</dynamicConfig>
代码中会通过 dumpsys display 解析设备的 width 和 height:
mViewports=[DisplayViewport{type=INTERNAL, valid=true, isActive=false, displayId=0, uniqueId='local:0', physicalPort=0, orientation=0, logicalFrame=Rect(0, 0 - 720, 1650), physicalFrame=Rect(0, 0 - 720, 1650), deviceWidth=720, deviceHeight=1650}]
在函数 calculateScreenLayoutSiz() 中确定 layout size
3. 解决方案
dumpsys meminfo,确认 persistent 进程的内存使用,要求不要超过 GTS 设置的max 值
参考:
https://blog.csdn.net/yaomingyang/article/details/79175333