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 
     17 package android.dumpsys.cts;
     18 
     19 import com.android.tradefed.log.LogUtil.CLog;
     20 
     21 import java.io.BufferedReader;
     22 import java.io.StringReader;
     23 import java.util.HashSet;
     24 import java.util.Set;
     25 import java.util.regex.Matcher;
     26 import java.util.regex.Pattern;
     27 
     28 /**
     29  * Test to check the format of the dumps of the processstats test.
     30  */
     31 public class ProcessStatsDumpsysTest extends BaseDumpsysTest {
     32     private static final String DEVICE_SIDE_TEST_APK = "CtsProcStatsApp.apk";
     33     private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.procstats";
     34 
     35     private static final String DEVICE_SIDE_HELPER_APK = "CtsProcStatsHelperApp.apk";
     36     private static final String DEVICE_SIDE_HELPER_PACKAGE = "com.android.server.cts.procstatshelper";
     37 
     38     /**
     39      * Maximum allowance scale factor when checking a duration time.
     40      *
     41      * If [actual value] > [expected value] * {@link #DURATION_TIME_MAX_FACTOR},
     42      * then the test fails.
     43      *
     44      * Because the run duration time may include the process startup time, we need a rather big
     45      * allowance.
     46      */
     47     private static final double DURATION_TIME_MAX_FACTOR = 2;
     48 
     49     /**
     50      * Tests the output of "dumpsys procstats -c". This is a proxy for testing "dumpsys procstats
     51      * --checkin", since the latter is not idempotent.
     52      */
     53     public void testProcstatsOutput() throws Exception {
     54         if (true) {
     55             return; // TODO http://b/70858729
     56         }
     57         // First, run the helper app so that we have some interesting records in the output.
     58         checkWithProcStatsApp();
     59 
     60         String procstats = mDevice.executeShellCommand("dumpsys procstats -c");
     61         assertNotNull(procstats);
     62         assertTrue(procstats.length() > 0);
     63 
     64         final int sep24h = procstats.indexOf("AGGREGATED OVER LAST 24 HOURS:");
     65         final int sep3h = procstats.indexOf("AGGREGATED OVER LAST 3 HOURS:");
     66 
     67         assertTrue("24 hour stats not found.", sep24h > 1);
     68         assertTrue("3 hour stats not found.", sep3h > 1);
     69 
     70         // Current
     71         checkProcStateOutput(procstats.substring(0, sep24h), /*checkAvg=*/ true);
     72 
     73         // Last 24 hours
     74         checkProcStateOutput(procstats.substring(sep24h, sep3h), /*checkAvg=*/ false);
     75 
     76         // Last 3 hours
     77         checkProcStateOutput(procstats.substring(sep3h), /*checkAvg=*/ false);
     78     }
     79 
     80     private static String[] commaSplit(String line) {
     81         if (line.endsWith(",")) {
     82             line = line + " ";
     83         }
     84         final String[] values = line.split(",");
     85         if (" ".equals(values[values.length - 1])) {
     86             values[values.length - 1] = "";
     87         }
     88         return values;
     89     }
     90 
     91     private void checkProcStateOutput(String text, boolean checkAvg) throws Exception {
     92         final Set<String> seenTags = new HashSet<>();
     93 
     94         try (BufferedReader reader = new BufferedReader(
     95                 new StringReader(text))) {
     96 
     97             String line;
     98             while ((line = reader.readLine()) != null) {
     99                 if (line.isEmpty()) {
    100                     continue;
    101                 }
    102                 CLog.d("Checking line: " + line);
    103 
    104                 String[] parts = commaSplit(line);
    105                 seenTags.add(parts[0]);
    106 
    107                 switch (parts[0]) {
    108                     case "vers":
    109                         assertEquals(2, parts.length);
    110                         assertEquals(5, Integer.parseInt(parts[1]));
    111                         break;
    112                     case "period":
    113                         checkPeriod(parts);
    114                         break;
    115                     case "pkgproc":
    116                         checkPkgProc(parts);
    117                         break;
    118                     case "pkgpss":
    119                         checkPkgPss(parts, checkAvg);
    120                         break;
    121                     case "pkgsvc-bound":
    122                     case "pkgsvc-exec":
    123                     case "pkgsvc-run":
    124                     case "pkgsvc-start":
    125                         checkPkgSvc(parts);
    126                         break;
    127                     case "pkgkills":
    128                         checkPkgKills(parts, checkAvg);
    129                         break;
    130                     case "proc":
    131                         checkProc(parts);
    132                         break;
    133                     case "pss":
    134                         checkPss(parts, checkAvg);
    135                         break;
    136                     case "kills":
    137                         checkKills(parts, checkAvg);
    138                         break;
    139                     case "total":
    140                         checkTotal(parts);
    141                         break;
    142                     default:
    143                         break;
    144                 }
    145             }
    146         }
    147 
    148         assertSeenTag(seenTags, "vers");
    149         assertSeenTag(seenTags, "period");
    150         assertSeenTag(seenTags, "pkgproc");
    151         assertSeenTag(seenTags, "proc");
    152         assertSeenTag(seenTags, "pss");
    153         assertSeenTag(seenTags, "total");
    154         assertSeenTag(seenTags, "weights");
    155         assertSeenTag(seenTags, "availablepages");
    156     }
    157 
    158     private void checkPeriod(String[] parts) {
    159         assertTrue("Length should be >= 5, found: " + parts.length,
    160                 parts.length >= 5);
    161         assertNotNull(parts[1]); // date
    162         assertLesserOrEqual(parts[2], parts[3]); // start time and end time (msec)
    163         for (int i = 4; i < parts.length; i++) {
    164             switch (parts[i]) {
    165                 case "shutdown":
    166                 case "sysprops":
    167                 case "complete":
    168                 case "partial":
    169                 case "swapped-out-pss":
    170                     continue;
    171             }
    172             fail("Invalid value '" + parts[i] + "' found.");
    173         }
    174     }
    175 
    176     private void checkPkgProc(String[] parts) {
    177         int statesStartIndex;
    178 
    179         assertTrue(parts.length >= 5);
    180         assertNotNull(parts[1]); // package name
    181         assertNonNegativeInteger(parts[2]); // uid
    182         assertNonNegativeInteger(parts[3]); // app version
    183         assertNotNull(parts[4]); // process
    184         statesStartIndex = 5;
    185 
    186         for (int i = statesStartIndex; i < parts.length; i++) {
    187             String[] subparts = parts[i].split(":");
    188             assertEquals(2, subparts.length);
    189             checkTag(subparts[0], true); // tag
    190             assertNonNegativeInteger(subparts[1]); // duration (msec)
    191         }
    192     }
    193 
    194     private void checkTag(String tag, boolean hasProcess) {
    195         assertEquals(hasProcess ? 3 : 2, tag.length());
    196 
    197         // screen: 0 = off, 1 = on
    198         char s = tag.charAt(0);
    199         if (s != '0' && s != '1') {
    200             fail("malformed tag: " + tag);
    201         }
    202 
    203         // memory: n = normal, m = moderate, l = low, c = critical
    204         char m = tag.charAt(1);
    205         if (m != 'n' && m != 'm' && m != 'l' && m != 'c') {
    206             fail("malformed tag: " + tag);
    207         }
    208 
    209         if (hasProcess) {
    210             char p = tag.charAt(2);
    211             assertTrue("malformed tag: " + tag, "ptfbuwsxrhlace".indexOf(p) >= 0);
    212         }
    213     }
    214 
    215     private void checkPkgPss(String[] parts, boolean checkAvg) {
    216         int statesStartIndex;
    217 
    218         assertTrue(parts.length >= 5);
    219         assertNotNull(parts[1]); // package name
    220         assertNonNegativeInteger(parts[2]); // uid
    221         assertNonNegativeInteger(parts[3]); // app version
    222         assertNotNull(parts[4]); // process
    223         statesStartIndex = 5;
    224 
    225         for (int i = statesStartIndex; i < parts.length; i++) {
    226             String[] subparts = parts[i].split(":");
    227             assertEquals(8, subparts.length);
    228             checkTag(subparts[0], true); // tag
    229             assertNonNegativeInteger(subparts[1]); // sample size
    230             assertMinAvgMax(subparts[2], subparts[3], subparts[4], checkAvg); // pss
    231             assertMinAvgMax(subparts[5], subparts[6], subparts[7], checkAvg); // uss
    232         }
    233     }
    234 
    235     private void checkPkgSvc(String[] parts) {
    236         int statesStartIndex;
    237 
    238         assertTrue(parts.length >= 6);
    239         assertNotNull(parts[1]); // package name
    240         assertNonNegativeInteger(parts[2]); // uid
    241         assertNonNegativeInteger(parts[3]); // app version
    242         assertNotNull(parts[4]); // service name
    243         assertNonNegativeInteger(parts[5]); // count
    244         statesStartIndex = 6;
    245 
    246         for (int i = statesStartIndex; i < parts.length; i++) {
    247             String[] subparts = parts[i].split(":");
    248             assertEquals(2, subparts.length);
    249             checkTag(subparts[0], false); // tag
    250             assertNonNegativeInteger(subparts[1]); // duration (msec)
    251         }
    252     }
    253 
    254     private void checkPkgKills(String[] parts, boolean checkAvg) {
    255         String pssStr;
    256 
    257         assertEquals(9, parts.length);
    258         assertNotNull(parts[1]); // package name
    259         assertNonNegativeInteger(parts[2]); // uid
    260         assertNonNegativeInteger(parts[3]); // app version
    261         assertNotNull(parts[4]); // process
    262         assertNonNegativeInteger(parts[5]); // wakes
    263         assertNonNegativeInteger(parts[6]); // cpu
    264         assertNonNegativeInteger(parts[7]); // cached
    265         pssStr = parts[8];
    266 
    267         String[] subparts = pssStr.split(":");
    268         assertEquals(3, subparts.length);
    269         assertMinAvgMax(subparts[0], subparts[1], subparts[2], checkAvg); // pss
    270     }
    271 
    272     private void checkProc(String[] parts) {
    273         assertTrue(parts.length >= 3);
    274         assertNotNull(parts[1]); // package name
    275         assertNonNegativeInteger(parts[2]); // uid
    276 
    277         for (int i = 3; i < parts.length; i++) {
    278             String[] subparts = parts[i].split(":");
    279             assertEquals(2, subparts.length);
    280             checkTag(subparts[0], true); // tag
    281             assertNonNegativeInteger(subparts[1]); // duration (msec)
    282         }
    283     }
    284 
    285     private void checkPss(String[] parts, boolean checkAvg) {
    286         assertTrue(parts.length >= 3);
    287         assertNotNull(parts[1]); // package name
    288         assertNonNegativeInteger(parts[2]); // uid
    289 
    290         for (int i = 3; i < parts.length; i++) {
    291             String[] subparts = parts[i].split(":");
    292             assertEquals(8, subparts.length);
    293             checkTag(subparts[0], true); // tag
    294             assertNonNegativeInteger(subparts[1]); // sample size
    295             assertMinAvgMax(subparts[2], subparts[3], subparts[4], checkAvg); // pss
    296             assertMinAvgMax(subparts[5], subparts[6], subparts[7], checkAvg); // uss
    297         }
    298     }
    299 
    300     private void checkKills(String[] parts, boolean checkAvg) {
    301         assertEquals(7, parts.length);
    302         assertNotNull(parts[1]); // package name
    303         assertNonNegativeInteger(parts[2]); // uid
    304         assertNonNegativeInteger(parts[3]); // wakes
    305         assertNonNegativeInteger(parts[4]); // cpu
    306         assertNonNegativeInteger(parts[5]); // cached
    307         String pssStr = parts[6];
    308 
    309         String[] subparts = pssStr.split(":");
    310         assertEquals(3, subparts.length);
    311         assertMinAvgMax(subparts[0], subparts[1], subparts[2], checkAvg); // pss
    312     }
    313 
    314     private void checkTotal(String[] parts) {
    315         assertTrue(parts.length >= 2);
    316         for (int i = 1; i < parts.length; i++) {
    317             String[] subparts = parts[i].split(":");
    318             checkTag(subparts[0], false); // tag
    319 
    320             assertNonNegativeInteger(subparts[1]); // duration (msec)
    321         }
    322     }
    323 
    324     /**
    325      * Find the first line with the prefix, and return the rest of the line.
    326      */
    327     private static String findLine(String prefix, String[] lines) {
    328         for (String line : lines) {
    329             if (line.startsWith(prefix)) {
    330                 CLog.d("Found line: " + line);
    331                 return line.substring(prefix.length());
    332             }
    333         }
    334         fail("Line with prefix '" + prefix + "' not found.");
    335         return null;
    336     }
    337 
    338     private static long getTagValueSum(String[] parts, String tagRegex) {
    339         final Pattern tagPattern = Pattern.compile("^" + tagRegex + "\\:");
    340 
    341         boolean found = false;
    342         long sum = 0;
    343         for (int i = 0; i < parts.length; i++){
    344             final String part = parts[i];
    345             final Matcher m = tagPattern.matcher(part);
    346             if (!m.find()) {
    347                 continue;
    348             }
    349             // Extract the rest of the part and parse as a long.
    350             sum += assertInteger(parts[i].substring(m.end(0)));
    351             found = true;
    352         }
    353         assertTrue("Tag '" + tagRegex + "' not found.", found);
    354         return sum;
    355     }
    356 
    357     private static void assertTagValueLessThan(String[] parts, String tagRegex,
    358             long expectedMax) {
    359         final long sum = getTagValueSum(parts, tagRegex);
    360 
    361         assertTrue("Total values for '" + tagRegex
    362                 + "' expected to be <= (" + expectedMax + ") but was: "
    363                 + sum, sum <= expectedMax);
    364     }
    365 
    366     private static void assertTagValueSumAbout(String[] parts, String tagRegex,
    367             long expectedValue) {
    368         final long sum = getTagValueSum(parts, tagRegex);
    369 
    370         assertTrue("Total values for '" + tagRegex
    371                 + "' expected to be >= " + expectedValue + " but was: "
    372                 + sum, sum >= expectedValue);
    373         assertTrue("Total values for '" + tagRegex
    374                 + "' expected to be <= (" + expectedValue + ") * "
    375                 + DURATION_TIME_MAX_FACTOR + " but was: "
    376                 + sum, sum <= (expectedValue * DURATION_TIME_MAX_FACTOR));
    377     }
    378 
    379     private void checkWithProcStatsApp() throws Exception {
    380         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
    381         getDevice().uninstallPackage(DEVICE_SIDE_HELPER_PACKAGE);
    382 
    383         final long startNs = System.nanoTime();
    384 
    385         installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
    386         installPackage(DEVICE_SIDE_HELPER_APK, /* grantPermissions= */ true);
    387 
    388         final int helperAppUid = Integer.parseInt(execCommandAndGetFirstGroup(
    389                 "dumpsys package " + DEVICE_SIDE_HELPER_PACKAGE, "userId=(\\d+)"));
    390         final String uid = String.valueOf(helperAppUid);
    391 
    392         CLog.i("Start: Helper app UID: " + helperAppUid);
    393 
    394         try {
    395             // Run the device side test which makes some network requests.
    396             runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
    397                     "com.android.server.cts.procstats.ProcStatsTest", "testLaunchApp");
    398         } finally {
    399             getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
    400             getDevice().uninstallPackage(DEVICE_SIDE_HELPER_PACKAGE);
    401         }
    402         final long finishNs = System.nanoTime();
    403         CLog.i("Finish: Took " + ((finishNs - startNs) / 1000000) + " ms");
    404 
    405         // The total running duration should be less than this, since we've uninstalled the app.
    406         final long maxRunTime = (finishNs - startNs) / 1000000;
    407 
    408         // Get the current procstats.
    409         final String procstats = mDevice.executeShellCommand("dumpsys procstats -c --current");
    410         assertNotNull(procstats);
    411         assertTrue(procstats.length() > 0);
    412 
    413         final String[] lines = procstats.split("\n");
    414 
    415         // Start checking.
    416         String parts[] = commaSplit(findLine(
    417                 "pkgproc,com.android.server.cts.procstatshelper,$U,32123,,".replace("$U", uid),
    418                 lines));
    419         assertTagValueSumAbout(parts, "0.t", 2000); // Screen off, foreground activity.
    420         assertTagValueSumAbout(parts, "1.t", 2000); // Screen on, foreground activity.
    421         assertTagValueSumAbout(parts, "1.f", 1000); // Screen on, foreground service.
    422         assertTagValueSumAbout(parts, "1.s", 500); // Screen on, background service.
    423         assertTagValueLessThan(parts, "...", maxRunTime); // total time.
    424 
    425 //      We can't really assert there's always "pss".  If there is, then we do check the format in
    426 //      checkProcStateOutput().
    427 //        parts = commaSplit(findLine(
    428 //                "pkgpss,com.android.server.cts.procstatshelper,$U,32123,,".replace("$U", uid),
    429 //                lines));
    430 
    431         parts = commaSplit(findLine(
    432                 ("pkgproc,com.android.server.cts.procstatshelper,$U,32123,"
    433                 + "com.android.server.cts.procstatshelper:proc2,").replace("$U", uid),
    434                 lines));
    435 
    436         assertTagValueSumAbout(parts, "0.f", 1000); // Screen off, foreground service.
    437         assertTagValueSumAbout(parts, "0.s", 500); // Screen off, background service.
    438 
    439         assertTagValueLessThan(parts, "...", maxRunTime); // total time.
    440 
    441 //      We can't really assert there's always "pss".  If there is, then we do check the format in
    442 //      checkProcStateOutput().
    443 //        parts = commaSplit(findLine(
    444 //                ("pkgpss,com.android.server.cts.procstatshelper,$U,32123,"
    445 //                + "com.android.server.cts.procstatshelper:proc2,").replace("$U", uid),
    446 //                lines));
    447 
    448         parts = commaSplit(findLine(
    449                 ("pkgsvc-run,com.android.server.cts.procstatshelper,$U,32123,"
    450                 + ".ProcStatsHelperServiceMain,").replace("$U", uid),
    451                 lines));
    452 
    453         assertTagValueSumAbout(parts, "1.", 1500); // Screen on, running.
    454 
    455         parts = commaSplit(findLine(
    456                 ("pkgsvc-start,com.android.server.cts.procstatshelper,$U,32123,"
    457                 + ".ProcStatsHelperServiceMain,").replace("$U", uid),
    458                 lines));
    459 
    460         assertTagValueSumAbout(parts, "1.", 1500); // Screen on, running.
    461 
    462 //      Dose it always exist?
    463 //        parts = commaSplit(findLine(
    464 //                ("pkgsvc-exec,com.android.server.cts.procstatshelper,$U,32123,"
    465 //                + ".ProcStatsHelperServiceMain,").replace("$U", uid),
    466 //                lines));
    467 
    468         parts = commaSplit(findLine(
    469                 ("pkgsvc-run,com.android.server.cts.procstatshelper,$U,32123,"
    470                 + ".ProcStatsHelperServiceSub,").replace("$U", uid),
    471                 lines));
    472 
    473         assertTagValueSumAbout(parts, "0.", 1500); // Screen off, running.
    474 
    475         parts = commaSplit(findLine(
    476                 ("pkgsvc-start,com.android.server.cts.procstatshelper,$U,32123,"
    477                 + ".ProcStatsHelperServiceSub,").replace("$U", uid),
    478                 lines));
    479 
    480         assertTagValueSumAbout(parts, "0.", 1500); // Screen off, running.
    481 
    482 //      Dose it always exist?
    483 //        parts = commaSplit(findLine(
    484 //                ("pkgsvc-exec,com.android.server.cts.procstatshelper,$U,32123,"
    485 //                + ".ProcStatsHelperServiceSub,").replace("$U", uid),
    486 //                lines));
    487 
    488     }
    489 }
    490