Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.server.cts;
     17 
     18 import android.service.GraphicsStatsHistogramBucketProto;
     19 import android.service.GraphicsStatsJankSummaryProto;
     20 import android.service.GraphicsStatsProto;
     21 import android.service.GraphicsStatsServiceDumpProto;
     22 
     23 import java.util.ArrayList;
     24 import java.util.Date;
     25 import java.util.List;
     26 
     27 public class GraphicsStatsValidationTest extends ProtoDumpTestCase {
     28     private static final String TAG = "GraphicsStatsValidationTest";
     29 
     30     private static final String DEVICE_SIDE_TEST_APK = "CtsGraphicsStatsApp.apk";
     31     private static final String DEVICE_SIDE_TEST_PACKAGE
     32             = "com.android.server.cts.device.graphicsstats";
     33 
     34     @Override
     35     protected void tearDown() throws Exception {
     36         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
     37         super.tearDown();
     38     }
     39 
     40     @Override
     41     protected void setUp() throws Exception {
     42         super.setUp();
     43         installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
     44         turnScreenOn();
     45         // Ensure that we have a starting point for our stats
     46         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".SimpleDrawFrameTests",
     47                 "testDrawTenFrames");
     48         // Kill to ensure that stats persist/merge across process death
     49         killTestApp();
     50     }
     51 
     52     private void turnScreenOn() throws Exception {
     53         getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
     54         getDevice().executeShellCommand("wm dismiss-keyguard");
     55     }
     56 
     57     public void testBasicDrawFrame() throws Exception {
     58         GraphicsStatsProto[] results = runDrawTest("testDrawTenFrames");
     59         GraphicsStatsProto statsBefore = results[0];
     60         GraphicsStatsProto statsAfter = results[1];
     61         GraphicsStatsJankSummaryProto summaryBefore = statsBefore.getSummary();
     62         GraphicsStatsJankSummaryProto summaryAfter = statsAfter.getSummary();
     63         assertTrue(summaryAfter.getTotalFrames() > summaryBefore.getTotalFrames());
     64 
     65         int frameDelta = summaryAfter.getTotalFrames() - summaryBefore.getTotalFrames();
     66         int jankyDelta = summaryAfter.getJankyFrames() - summaryBefore.getJankyFrames();
     67         // We expect 11 frames to have been drawn (first frame + the 10 more explicitly requested)
     68         assertEquals(11, frameDelta);
     69         assertTrue(jankyDelta < 5);
     70         int veryJankyDelta = countFramesAbove(statsAfter, 40) - countFramesAbove(statsBefore, 40);
     71         // The 1st frame could be >40ms, but nothing after that should be
     72         assertTrue(veryJankyDelta <= 1);
     73     }
     74 
     75     public void testJankyDrawFrame() throws Exception {
     76         GraphicsStatsProto[] results = runDrawTest("testDrawJankyFrames");
     77         GraphicsStatsProto statsBefore = results[0];
     78         GraphicsStatsProto statsAfter = results[1];
     79         GraphicsStatsJankSummaryProto summaryBefore = statsBefore.getSummary();
     80         GraphicsStatsJankSummaryProto summaryAfter = statsAfter.getSummary();
     81         assertTrue(summaryAfter.getTotalFrames() > summaryBefore.getTotalFrames());
     82 
     83         int frameDelta = summaryAfter.getTotalFrames() - summaryBefore.getTotalFrames();
     84         int jankyDelta = summaryAfter.getJankyFrames() - summaryBefore.getJankyFrames();
     85         // Test draws 50 frames + 1 initial frame. We expect 40 of them to be janky,
     86         // 10 of each of ANIMATION, LAYOUT, RECORD_DRAW, and MISSED_VSYNC
     87         assertEquals(51, frameDelta);
     88         assertTrue(jankyDelta >= 40);
     89         assertTrue(jankyDelta < 45);
     90 
     91         // Although our current stats don't distinguish between ANIMATION, LAYOUT, and RECORD_DRAW
     92         // so this will just be slowUi +30
     93         int slowUiDelta = summaryAfter.getSlowUiThreadCount() - summaryBefore.getSlowUiThreadCount();
     94         assertTrue(slowUiDelta >= 30);
     95         int missedVsyncDelta = summaryAfter.getMissedVsyncCount()
     96                 - summaryBefore.getMissedVsyncCount();
     97         assertTrue(missedVsyncDelta >= 10);
     98         assertTrue(missedVsyncDelta <= 11);
     99 
    100         int veryJankyDelta = countFramesAbove(statsAfter, 60) - countFramesAbove(statsBefore, 60);
    101         // The 1st frame could be >40ms, but nothing after that should be
    102         assertTrue(veryJankyDelta <= 1);
    103     }
    104 
    105     public void testDaveyDrawFrame() throws Exception {
    106         GraphicsStatsProto[] results = runDrawTest("testDrawDaveyFrames");
    107         GraphicsStatsProto statsBefore = results[0];
    108         GraphicsStatsProto statsAfter = results[1];
    109         GraphicsStatsJankSummaryProto summaryBefore = statsBefore.getSummary();
    110         GraphicsStatsJankSummaryProto summaryAfter = statsAfter.getSummary();
    111         assertTrue(summaryAfter.getTotalFrames() > summaryBefore.getTotalFrames());
    112 
    113         int frameDelta = summaryAfter.getTotalFrames() - summaryBefore.getTotalFrames();
    114         int jankyDelta = summaryAfter.getJankyFrames() - summaryBefore.getJankyFrames();
    115         // Test draws 40 frames + 1 initial frame. We expect 10 of them to be daveys,
    116         // 10 of them to be daveyjrs, and 20 to jank from missed vsync (from the davey/daveyjr prior to it)
    117         assertEquals(41, frameDelta);
    118         assertTrue(jankyDelta >= 20);
    119         assertTrue(jankyDelta < 25);
    120 
    121         int gt150msDelta = countFramesAbove(statsAfter, 150) - countFramesAbove(statsBefore, 150);
    122         assertTrue(gt150msDelta >= 20); // 10 davey jrs + 10 daveys + maybe first frame
    123         assertTrue(gt150msDelta <= 21);
    124         int gt700msDelta = countFramesAbove(statsAfter, 700) - countFramesAbove(statsBefore, 700);
    125         assertEquals(10, gt700msDelta); // 10 daveys
    126     }
    127 
    128     private GraphicsStatsProto[] runDrawTest(String testName)  throws Exception  {
    129         return doRunDrawTest(testName, true);
    130     }
    131 
    132     private GraphicsStatsProto[] doRunDrawTest(String testName, boolean canRetry) throws Exception {
    133         GraphicsStatsProto statsBefore = fetchStats();
    134         assertNotNull(statsBefore);
    135         killTestApp();
    136         turnScreenOn();
    137         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".SimpleDrawFrameTests",  testName);
    138         killTestApp();
    139         GraphicsStatsProto statsAfter = fetchStats();
    140         assertNotNull(statsAfter);
    141         // If we get extremely unlucky a log rotate might have happened. If so we retry, but only once
    142         // It's a failure if this test takes >24 hours such that 2 rotates could happen while running
    143         // this test case, or more likely if stats are not being merged/persisted properly
    144         if (canRetry) {
    145             if (statsBefore.getStatsStart() != statsAfter.getStatsStart()) {
    146                 return doRunDrawTest(testName, false);
    147             }
    148         } else {
    149             assertEquals(statsBefore.getStatsStart(), statsAfter.getStatsStart());
    150         }
    151         validate(statsBefore);
    152         validate(statsAfter);
    153         return new GraphicsStatsProto[] { statsBefore, statsAfter };
    154     }
    155 
    156     private void validate(GraphicsStatsProto proto) {
    157         assertNotNull(proto.getPackageName());
    158         assertFalse(proto.getPackageName().isEmpty());
    159         assertTrue(proto.getVersionCode() > 0);
    160         assertTrue(proto.getStatsStart() > 0);
    161         assertTrue(proto.getStatsEnd() > 0);
    162         assertTrue(proto.hasSummary());
    163         GraphicsStatsJankSummaryProto summary = proto.getSummary();
    164         assertTrue(summary.getTotalFrames() > 0);
    165         // Our test app won't produce that many frames, so we can assert this is a realistic
    166         // number. We cap it at 1,000,000 in case the test is repeated many, many times in one day
    167         assertTrue(summary.getTotalFrames() < 1000000);
    168         // We can't generically assert things about the janky frames, so just assert they fall into
    169         // valid ranges.
    170         assertTrue(summary.getJankyFrames() <= summary.getTotalFrames());
    171         assertTrue(summary.getMissedVsyncCount() <= summary.getJankyFrames());
    172         assertTrue(summary.getHighInputLatencyCount() <= summary.getJankyFrames());
    173         assertTrue(summary.getSlowUiThreadCount() <= summary.getJankyFrames());
    174         assertTrue(summary.getSlowBitmapUploadCount() <= summary.getJankyFrames());
    175         assertTrue(summary.getSlowDrawCount() <= summary.getJankyFrames());
    176         assertTrue(proto.getHistogramCount() > 0);
    177 
    178         int histogramTotal = countTotalFrames(proto);
    179         assertEquals(summary.getTotalFrames(), histogramTotal);
    180     }
    181 
    182     private int countFramesAbove(GraphicsStatsProto proto, int thresholdMs) {
    183         int totalFrames = 0;
    184         for (GraphicsStatsHistogramBucketProto bucket : proto.getHistogramList()) {
    185             if (bucket.getRenderMillis() >= thresholdMs) {
    186                 totalFrames += bucket.getFrameCount();
    187             }
    188         }
    189         return totalFrames;
    190     }
    191 
    192     private int countTotalFrames(GraphicsStatsProto proto) {
    193         return countFramesAbove(proto, 0);
    194     }
    195 
    196     private void killTestApp() throws Exception {
    197         getDevice().executeShellCommand("am kill " + DEVICE_SIDE_TEST_PACKAGE);
    198     }
    199 
    200     private GraphicsStatsProto fetchStats() throws Exception {
    201         GraphicsStatsServiceDumpProto serviceDumpProto = getDump(GraphicsStatsServiceDumpProto.parser(),
    202                 "dumpsys graphicsstats --proto");
    203         List<GraphicsStatsProto> protos = filterPackage(serviceDumpProto, DEVICE_SIDE_TEST_PACKAGE);
    204         return findLatest(protos);
    205     }
    206 
    207     private List<GraphicsStatsProto> filterPackage(GraphicsStatsServiceDumpProto dump, String pkgName) {
    208         return filterPackage(dump.getStatsList(), pkgName);
    209     }
    210 
    211     private List<GraphicsStatsProto> filterPackage(List<GraphicsStatsProto> list, String pkgName) {
    212         ArrayList<GraphicsStatsProto> filtered = new ArrayList<>();
    213         for (GraphicsStatsProto proto : list) {
    214             if (pkgName.equals(proto.getPackageName())) {
    215                 filtered.add(proto);
    216             }
    217         }
    218         return filtered;
    219     }
    220 
    221     private GraphicsStatsProto findLatest(List<GraphicsStatsProto> list) {
    222         if (list.size() == 0) { return null; }
    223         GraphicsStatsProto latest = list.get(0);
    224         Date latestDate = new Date();
    225         Date compareTo = new Date();
    226         latestDate.setTime(latest.getStatsEnd());
    227         for (int i = 1; i < list.size(); i++) {
    228             GraphicsStatsProto proto = list.get(i);
    229             compareTo.setTime(proto.getStatsEnd());
    230             if (compareTo.after(latestDate)) {
    231                 latestDate.setTime(proto.getStatsEnd());
    232                 latest = proto;
    233             }
    234         }
    235         return latest;
    236     }
    237 }
    238