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