GTS 中testPersistentProcessMemory fail 详解

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 值

另外,在case 调用最开始会确认是否为 GO device:

    public void checkGoDevice() {
        Assume.assumeTrue("Skipping MemoryTest on non-Go device", GmsUtil.isGoDevice());
        this.mActivityManager = (ActivityManager) InstrumentationRegistry.getTargetContext().getSystemService(ActivityManager.class);
    }

该 case 只有在 GO device 下才会调用,而非 GO device,则会认为 ASSUMPTION_FAILURE 而跳过该 case,并在运行的时候打印:

9-22 10:57:49 I/ModuleListener: [1/1] caab02bd com.google.android.memory.gts.MemoryTest#testPersistentProcessMemory ASSUMPTION_FAILURE: org.junit.AssumptionViolatedException: Skipping MemoryTest on non-Go device
	at org.junit.Assume.assumeTrue(Assume.java:68)
	at com.google.android.memory.gts.MemoryTest.checkGoDevice(MemoryTest.java:62)
	at java.lang.reflect.Method.invoke(Native Method)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:61)
	at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:76)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.-$$Nest$mrunChildren(Unknown Source:0)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at androidx.test.runner.AndroidJUnit4.run(AndroidJUnit4.java:111)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:27)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.-$$Nest$mrunChildren(Unknown Source:0)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:67)
	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:445)
	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)

参考:

https://blog.csdn.net/yaomingyang/article/details/79175333

猜你喜欢

转载自blog.csdn.net/jingerppp/article/details/132882587
今日推荐