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