1 /*
2  * Copyright (C) 2018 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 android.cts.statsd.validation;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import android.cts.statsd.atom.ProcStateTestCase;
21 import android.cts.statsd.metric.MetricsUtils;
22 import android.cts.statsdatom.lib.AtomTestUtils;
23 import android.cts.statsdatom.lib.ConfigUtils;
24 import android.cts.statsdatom.lib.DeviceUtils;
25 import android.cts.statsdatom.lib.ReportUtils;
26 import android.service.procstats.AggregatedProcessState;
27 
28 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
29 import com.android.os.AtomsProto;
30 import com.android.os.AtomsProto.Atom;
31 import com.android.os.AtomsProto.ProcessStateAggregated;
32 import com.android.os.AtomsProto.ProcessStatsProto;
33 import com.android.os.AtomsProto.ProcessStatsStateProto;
34 import com.android.tradefed.device.ITestDevice;
35 import com.android.tradefed.log.LogUtil;
36 import com.android.tradefed.util.RunUtil;
37 
38 import java.util.List;
39 
40 /**
41  * Side-by-side comparison between statsd and procstats.
42  */
43 public class ProcStatsValidationTests extends ProcStateTestCase {
44 
45     private static final String TAG = "Statsd.ProcStatsValidationTests";
46 
47     private static final String sBackgroundServiceName = "StatsdCtsBackgroundService";
48 
49     private static final int EXTRA_WAIT_TIME_MS = 1_000; // as buffer when proc state changing.
50 
51     private static final String DUMP_PROCSTATS_CMD = "dumpsys procstats";
52 
toggleScreenAndSleep(final long duration)53     private void toggleScreenAndSleep(final long duration) throws Exception {
54         final long half = duration >> 1;
55         RunUtil.getDefault().sleep(half);
56         DeviceUtils.turnScreenOff(getDevice());
57         RunUtil.getDefault().sleep(half);
58         DeviceUtils.turnScreenOn(getDevice());
59     }
60 
testProcessStateByPulling()61     public void testProcessStateByPulling() throws Exception {
62         startProcStatsTesting();
63         clearProcStats();
64         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
65 
66         // foreground service
67         executeForegroundService();
68         RunUtil.getDefault().sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
69         // background
70         DeviceUtils.executeBackgroundService(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
71                 sBackgroundServiceName, ACTION_BACKGROUND_SLEEP);
72         RunUtil.getDefault().sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
73         // top
74         executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
75         RunUtil.getDefault().sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
76         // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
77         DeviceUtils.executeBackgroundService(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
78                 sBackgroundServiceName, ACTION_END_IMMEDIATELY);
79         final int cacheTime = 2_000; // process should be in cached state for up to this long
80         RunUtil.getDefault().sleep(cacheTime);
81         // foreground
82         // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
83         executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
84         RunUtil.getDefault().sleep(EXTRA_WAIT_TIME_MS + 5_000);
85 
86         RunUtil.getDefault().sleep(60_000);
87         DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
88         stopProcStatsTesting();
89         commitProcStatsToDisk();
90         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
91 
92         final String fileName = "PROCSTATSQ_PULL.pbtxt";
93         StatsdConfig config = ValidationTestUtil.getConfig(fileName, mCtsBuild);
94         LogUtil.CLog.d("Updating the following config:\n" + config.toString());
95         ConfigUtils.uploadConfig(getDevice(), config.toBuilder());
96         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
97         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
98                 AtomsProto.AppBreadcrumbReported.State.START.getNumber(), 1);
99         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT + 5_000);
100 
101         List<Atom> statsdData = ReportUtils.getGaugeMetricAtoms(getDevice());
102 
103         List<android.service.procstats.ProcessStatsProto> processStatsProtoList
104                 = getAllProcStatsProtoForStatsd();
105 
106         // We pull directly from ProcessStatsService, so not necessary to compare every field.
107         // Make sure that 1. both capture statsd package 2. spot check some values are reasonable
108         LogUtil.CLog.d("======================");
109 
110         String statsdPkgName = "com.android.server.cts.device.statsd";
111         long rssAvgStatsd = 0;
112         for (Atom d : statsdData) {
113             for (ProcessStatsProto proc :
114                     d.getProcStats().getProcStatsSection().getProcessStatsList()) {
115                 if (proc.getProcess().equals(statsdPkgName)) {
116                     LogUtil.CLog.d("Got proto from statsd:");
117                     LogUtil.CLog.d(proc.toString());
118                     for (ProcessStatsStateProto state : proc.getStatesList()) {
119                         if (state.getProcessStateAggregated()
120                                 == ProcessStateAggregated.PROCESS_STATE_IMPORTANT_FOREGROUND) {
121                             rssAvgStatsd = state.getRss().getMeanKb();
122                         }
123                     }
124                 }
125             }
126         }
127 
128         long rssAvgProcstats = 0;
129         for (android.service.procstats.ProcessStatsProto process : processStatsProtoList) {
130             if (process.getProcess().equals(statsdPkgName)) {
131                 LogUtil.CLog.d("Got proto from procstats dumpsys:");
132                 LogUtil.CLog.d(process.toString());
133                 for (android.service.procstats.ProcessStatsStateProto state
134                         : process.getStatesList()) {
135                     if (AggregatedProcessState.AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND
136                             == state.getProcessStateAggregated()) {
137                         rssAvgProcstats = state.getRss().getMeanKb();
138                         break;
139                     }
140                 }
141             }
142         }
143 
144         assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
145     }
146 
testProcStatsPkgProcStats()147     public void testProcStatsPkgProcStats() throws Exception {
148         /**
149          * Temporarily disable this test as the proc stats data being pulled into the statsd
150          * doesn't include the pkg part now.
151          *
152          startProcStatsTesting();
153          clearProcStats();
154          RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
155 
156          // foreground service
157          executeForegroundService();
158          RunUtil.getDefault().sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
159          // background
160          executeBackgroundService(ACTION_BACKGROUND_SLEEP);
161          RunUtil.getDefault().sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
162          // top
163          executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
164          RunUtil.getDefault().sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
165          // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
166          executeBackgroundService(ACTION_END_IMMEDIATELY);
167          final int cacheTime = 2_000; // process should be in cached state for up to this long
168          RunUtil.getDefault().sleep(cacheTime);
169          // foreground
170          // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
171          executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
172          RunUtil.getDefault().sleep(EXTRA_WAIT_TIME_MS + 5_000);
173 
174          RunUtil.getDefault().sleep(60_000);
175          uninstallPackage();
176          stopProcStatsTesting();
177          commitProcStatsToDisk();
178          RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
179 
180          final String fileName = "PROCSTATSQ_PULL_PKG_PROC.pbtxt";
181          StatsdConfig config = createValidationUtil().getConfig(fileName);
182          LogUtil.CLog.d("Updating the following config:\n" + config.toString());
183          uploadConfig(config);
184          RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
185          setAppBreadcrumbPredicate();
186          RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
187 
188          List<Atom> statsdData = getGaugeMetricDataList();
189          assertThat(statsdData).isNotEmpty();
190          assertThat(
191          statsdData.get(0).getProcStatsPkgProc().getProcStatsSection()
192          .getProcessStatsList()
193          ).isNotEmpty();
194 
195          // We pull directly from ProcessStatsService, so not necessary to compare every field.
196          // Make sure that 1. both capture statsd package 2. spot check some values are reasonable
197          LogUtil.CLog.d("======================");
198 
199          String statsdPkgName = "com.android.server.cts.device.statsd";
200          long rssAvgStatsd = 0;
201          long durationStatsd = 0;
202          for (Atom d : statsdData) {
203          for (ProcessStatsPackageProto pkg : d.getProcStatsPkgProc().getProcStatsSection()
204          .getPackageStatsList()) {
205          if (pkg.getPackage().equals(statsdPkgName)) {
206          LogUtil.CLog.d("Got proto from statsd:");
207          LogUtil.CLog.d(pkg.toString());
208          for (ProcessStatsProto process : pkg.getProcessStatsList()) {
209          for (ProcessStatsStateProto state : process.getStatesList()) {
210          if (state.getProcessState()
211          == ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND) {
212          durationStatsd = state.getDurationMillis();
213          rssAvgStatsd = state.getRss().getAverage();
214          }
215          }
216          }
217          }
218          assertThat(pkg.getServiceStatsCount()).isEqualTo(0L);
219          assertThat(pkg.getAssociationStatsCount()).isEqualTo(0L);
220          }
221          }
222 
223          LogUtil.CLog.d("avg rss from statsd is " + rssAvgStatsd);
224 
225          List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
226 
227          long pssAvgProcstats = 0;
228          long ussAvgProcstats = 0;
229          long rssAvgProcstats = 0;
230          long durationProcstats = 0;
231          int serviceStatsCount = 0;
232          int associationStatsCount = 0;
233          for (ProcessStatsPackageProto pkg : processStatsPackageProtoList) {
234          if (pkg.getPackage().equals(statsdPkgName)) {
235          LogUtil.CLog.d("Got proto from procstats dumpsys:");
236          LogUtil.CLog.d(pkg.toString());
237          for (ProcessStatsProto process : pkg.getProcessStatsList()) {
238          for (ProcessStatsStateProto state : process.getStatesList()) {
239          if (state.getProcessState()
240          == ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND) {
241          durationProcstats = state.getDurationMillis();
242          pssAvgProcstats = state.getPss().getAverage();
243          ussAvgProcstats = state.getUss().getAverage();
244          rssAvgProcstats = state.getRss().getAverage();
245          }
246          }
247          }
248          }
249          serviceStatsCount += pkg.getServiceStatsCount();
250          associationStatsCount += pkg.getAssociationStatsCount();
251          }
252          assertThat(serviceStatsCount).isGreaterThan(0);
253          assertThat(associationStatsCount).isGreaterThan(0);
254 
255          LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
256          assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
257          */
258     }
259 
isPssProfilingDisabled()260     private boolean isPssProfilingDisabled() throws Exception {
261         ITestDevice device = getDevice();
262         final String disablePssProfilingKey = "disable_app_profiler_pss_profiling";
263         final String stringToCompare = " " + disablePssProfilingKey + "=true";
264 
265         final String dumpsys = device.executeShellCommand("dumpsys activity settings");
266         return (dumpsys.contains(stringToCompare));
267     }
268 
clearProcStats()269     protected void clearProcStats() throws Exception {
270         getDevice().executeShellCommand("dumpsys procstats --clear");
271     }
272 
startProcStatsTesting()273     private void startProcStatsTesting() throws Exception {
274         getDevice().executeShellCommand("dumpsys procstats --start-testing");
275     }
276 
stopProcStatsTesting()277     private void stopProcStatsTesting() throws Exception {
278         getDevice().executeShellCommand("dumpsys procstats --stop-testing");
279     }
280 
commitProcStatsToDisk()281     private void commitProcStatsToDisk() throws Exception {
282         getDevice().executeShellCommand("dumpsys procstats --commit");
283     }
284 
285     /*
286      * Get all processes' procstats statsd data in proto
287      */
getAllProcStatsProtoForStatsd()288     protected List<android.service.procstats.ProcessStatsProto> getAllProcStatsProtoForStatsd()
289             throws Exception {
290         try {
291             android.service.procstats.ProcessStatsSectionProto sectionProto = MetricsUtils.getDump(
292                     getDevice(),
293                     android.service.procstats.ProcessStatsSectionProto.parser(),
294                     String.join(" ", DUMP_PROCSTATS_CMD,
295                             "--statsd"));
296             List<android.service.procstats.ProcessStatsProto> processStatsProtoList
297                     = sectionProto.getProcessStatsList();
298             LogUtil.CLog.d("Got procstats:\n ");
299             for (android.service.procstats.ProcessStatsProto processStatsProto
300                     : processStatsProtoList) {
301                 LogUtil.CLog.d(processStatsProto.toString());
302             }
303             return processStatsProtoList;
304         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
305             LogUtil.CLog.e("Failed to dump procstats proto");
306             throw (e);
307         }
308     }
309 }
310