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 com.android.car.storagemonitoring; 18 19 import android.car.storagemonitoring.IoStatsEntry; 20 import android.car.storagemonitoring.UidIoRecord; 21 import android.util.SparseArray; 22 23 import androidx.test.filters.MediumTest; 24 25 import com.android.car.procfsinspector.ProcessInfo; 26 import com.android.car.systeminterface.SystemStateInterface; 27 import com.android.internal.annotations.GuardedBy; 28 29 import junit.framework.TestCase; 30 31 import java.time.Duration; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.stream.Collectors; 36 37 /** 38 * Tests IoStatsTracker functionality. 39 */ 40 @MediumTest 41 public class IoStatsTrackerTest extends TestCase { 42 private static final int SAMPLE_WINDOW_MS = 1000; 43 private static final List<IoStatsEntry> EMPTY = Collections.emptyList(); 44 private static final String TAG = IoStatsTrackerTest.class.getSimpleName(); 45 testNewUsersAppear()46 public void testNewUsersAppear() throws Exception { 47 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 48 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 49 SAMPLE_WINDOW_MS, mockSystemStateInterface); 50 51 assertEquals(0, ioStatsTracker.getCurrentSample().size()); 52 assertEquals(0, ioStatsTracker.getTotal().size()); 53 54 UserActivity user0 = new UserActivity(0); 55 user0.foreground_rchar = 50; 56 user0.background_wchar = 10; 57 58 UserActivity user1 = new UserActivity(1); 59 user1.foreground_rchar = 30; 60 user1.background_wchar = 50; 61 62 UidIoRecord process0 = user0.updateSystemState(mockSystemStateInterface); 63 UidIoRecord process1 = user1.updateSystemState(mockSystemStateInterface); 64 65 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 66 67 assertEquals(2, ioStatsTracker.getCurrentSample().size()); 68 assertEquals(2, ioStatsTracker.getTotal().size()); 69 70 assertTrue(ioStatsTracker.getCurrentSample().get(0).representsSameMetrics(process0)); 71 assertTrue(ioStatsTracker.getCurrentSample().get(1).representsSameMetrics(process1)); 72 73 assertTrue(ioStatsTracker.getTotal().get(0).representsSameMetrics(process0)); 74 assertTrue(ioStatsTracker.getTotal().get(1).representsSameMetrics(process1)); 75 } 76 testUserMetricsChange()77 public void testUserMetricsChange() throws Exception { 78 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 79 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 80 SAMPLE_WINDOW_MS, mockSystemStateInterface); 81 82 UserActivity user0 = new UserActivity(0); 83 user0.foreground_rchar = 50; 84 user0.background_wchar = 10; 85 86 user0.updateSystemState(mockSystemStateInterface); 87 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 88 89 user0.foreground_rchar = 60; 90 user0.foreground_wchar = 10; 91 UidIoRecord process0 = user0.updateSystemState(mockSystemStateInterface); 92 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 93 94 assertEquals(1, ioStatsTracker.getCurrentSample().size()); 95 assertEquals(1, ioStatsTracker.getTotal().size()); 96 97 IoStatsEntry sample0 = ioStatsTracker.getCurrentSample().get(0); 98 IoStatsEntry total0 = ioStatsTracker.getTotal().get(0); 99 100 assertNotNull(sample0); 101 assertNotNull(total0); 102 103 assertTrue(total0.representsSameMetrics(process0)); 104 105 assertEquals(10, sample0.foreground.bytesRead); 106 assertEquals(10, sample0.foreground.bytesWritten); 107 assertEquals(0, sample0.background.bytesWritten); 108 } 109 testUpdateNoIoProcessActive()110 public void testUpdateNoIoProcessActive() throws Exception { 111 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 112 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 113 SAMPLE_WINDOW_MS, mockSystemStateInterface); 114 115 UserActivity user0 = new UserActivity(0); 116 user0.foreground_rchar = 50; 117 user0.background_wchar = 10; 118 119 user0.updateSystemState(mockSystemStateInterface); 120 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 121 122 user0.spawnProcess(); 123 user0.updateSystemState(mockSystemStateInterface); 124 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 125 126 assertEquals(1, ioStatsTracker.getCurrentSample().size()); 127 assertEquals(1, ioStatsTracker.getTotal().size()); 128 129 IoStatsEntry sample0 = ioStatsTracker.getCurrentSample().get(0); 130 IoStatsEntry total0 = ioStatsTracker.getTotal().get(0); 131 132 assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis); 133 assertEquals(2 * SAMPLE_WINDOW_MS, total0.runtimeMillis); 134 135 assertEquals(0, sample0.foreground.bytesRead); 136 assertEquals(0, sample0.background.bytesWritten); 137 } 138 testUpdateNoIoProcessInactive()139 public void testUpdateNoIoProcessInactive() throws Exception { 140 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 141 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 142 SAMPLE_WINDOW_MS, mockSystemStateInterface); 143 144 UserActivity user0 = new UserActivity(0); 145 user0.foreground_rchar = 50; 146 user0.background_wchar = 10; 147 148 user0.updateSystemState(mockSystemStateInterface); 149 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 150 151 user0.killProcess(); 152 UidIoRecord record0 = user0.updateSystemState(mockSystemStateInterface); 153 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 154 155 assertEquals(0, ioStatsTracker.getCurrentSample().size()); 156 assertEquals(1, ioStatsTracker.getTotal().size()); 157 158 IoStatsEntry total0 = ioStatsTracker.getTotal().get(0); 159 assertEquals(SAMPLE_WINDOW_MS, total0.runtimeMillis); 160 assertTrue(total0.representsSameMetrics(record0)); 161 } 162 testUpdateIoHappens()163 public void testUpdateIoHappens() throws Exception { 164 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 165 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 166 SAMPLE_WINDOW_MS, mockSystemStateInterface); 167 168 UserActivity user0 = new UserActivity(0); 169 user0.foreground_rchar = 50; 170 user0.background_wchar = 10; 171 172 user0.updateSystemState(mockSystemStateInterface); 173 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 174 175 user0.foreground_rchar = 60; 176 UidIoRecord record0 = user0.updateSystemState(mockSystemStateInterface); 177 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 178 179 assertEquals(1, ioStatsTracker.getCurrentSample().size()); 180 assertEquals(1, ioStatsTracker.getTotal().size()); 181 182 IoStatsEntry sample0 = ioStatsTracker.getCurrentSample().get(0); 183 IoStatsEntry total0 = ioStatsTracker.getTotal().get(0); 184 185 assertTrue(total0.representsSameMetrics(record0)); 186 assertEquals(2 * SAMPLE_WINDOW_MS, total0.runtimeMillis); 187 assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis); 188 assertEquals(10, sample0.foreground.bytesRead); 189 assertEquals(0, sample0.background.bytesWritten); 190 } 191 testUpdateGoAwayComeBackProcess()192 public void testUpdateGoAwayComeBackProcess() throws Exception { 193 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 194 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 195 SAMPLE_WINDOW_MS, mockSystemStateInterface); 196 197 UserActivity user0 = new UserActivity(0); 198 user0.foreground_rchar = 50; 199 user0.background_wchar = 10; 200 201 user0.updateSystemState(mockSystemStateInterface); 202 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 203 204 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 205 206 assertEquals(0, ioStatsTracker.getCurrentSample().size()); 207 assertEquals(1, ioStatsTracker.getTotal().size()); 208 209 user0.spawnProcess(); 210 user0.updateSystemState(mockSystemStateInterface); 211 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 212 213 assertEquals(1, ioStatsTracker.getCurrentSample().size()); 214 IoStatsEntry sample0 = ioStatsTracker.getCurrentSample().get(0); 215 assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis); 216 } 217 testUpdateGoAwayComeBackIo()218 public void testUpdateGoAwayComeBackIo() throws Exception { 219 final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface(); 220 IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY, 221 SAMPLE_WINDOW_MS, mockSystemStateInterface); 222 223 UserActivity user0 = new UserActivity(0); 224 user0.foreground_rchar = 50; 225 user0.background_wchar = 10; 226 227 user0.updateSystemState(mockSystemStateInterface); 228 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 229 230 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 231 232 assertEquals(0, ioStatsTracker.getCurrentSample().size()); 233 assertEquals(1, ioStatsTracker.getTotal().size()); 234 235 user0.foreground_fsync = 1; 236 237 user0.updateSystemState(mockSystemStateInterface); 238 ioStatsTracker.update(mockSystemStateInterface.mIoRecords); 239 240 assertEquals(1, ioStatsTracker.getCurrentSample().size()); 241 242 IoStatsEntry sample0 = ioStatsTracker.getCurrentSample().get(0); 243 assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis); 244 assertEquals(1, sample0.foreground.fsyncCalls); 245 } 246 247 private static final class UserActivity { 248 private final int mUid; 249 private boolean mHasProcess; 250 251 private long foreground_rchar; 252 private long foreground_wchar; 253 private long foreground_read_bytes; 254 private long foreground_write_bytes; 255 private long foreground_fsync; 256 257 private long background_rchar; 258 private long background_wchar; 259 private long background_read_bytes; 260 private long background_write_bytes; 261 private long background_fsync; 262 UserActivity(int uid)263 UserActivity(int uid) { 264 mUid = uid; 265 } 266 spawnProcess()267 void spawnProcess() { 268 mHasProcess = true; 269 } killProcess()270 void killProcess() { 271 mHasProcess = false; 272 } 273 updateSystemState(MockSystemStateInterface systemState)274 UidIoRecord updateSystemState(MockSystemStateInterface systemState) { 275 UidIoRecord uidIoRecord = new UidIoRecord(mUid, 276 foreground_rchar, 277 foreground_wchar, 278 foreground_read_bytes, 279 foreground_write_bytes, 280 foreground_fsync, 281 background_rchar, 282 background_wchar, 283 background_read_bytes, 284 background_write_bytes, 285 background_fsync); 286 287 systemState.addIoRecord(uidIoRecord); 288 if (mHasProcess) { 289 systemState.addProcess(new ProcessInfo(1, mUid)); 290 } else { 291 systemState.removeUserProcesses(mUid); 292 } 293 294 return uidIoRecord; 295 } 296 } 297 298 private static final class MockSystemStateInterface implements SystemStateInterface { 299 300 private final Object mLock = new Object(); 301 302 @GuardedBy("mLock") 303 private final List<ProcessInfo> mProcesses = new ArrayList<>(); 304 305 @GuardedBy("mLock") 306 private final SparseArray<UidIoRecord> mIoRecords = new SparseArray<>(); 307 308 @Override shutdown()309 public void shutdown() { 310 } 311 312 @Override enterDeepSleep()313 public boolean enterDeepSleep() { 314 return true; 315 } 316 317 @Override enterHibernation()318 public boolean enterHibernation() { 319 return true; 320 } 321 322 @Override scheduleActionForBootCompleted(Runnable action, Duration delay, Duration delayRange)323 public void scheduleActionForBootCompleted(Runnable action, Duration delay, 324 Duration delayRange) { 325 } 326 327 @Override isWakeupCausedByTimer()328 public boolean isWakeupCausedByTimer() { 329 return false; 330 } 331 332 @Override isSystemSupportingDeepSleep()333 public boolean isSystemSupportingDeepSleep() { 334 return false; 335 } 336 337 @Override getRunningProcesses()338 public List<ProcessInfo> getRunningProcesses() { 339 synchronized (mLock) { 340 return mProcesses; 341 } 342 } 343 addProcess(ProcessInfo processInfo)344 void addProcess(ProcessInfo processInfo) { 345 synchronized (mLock) { 346 mProcesses.add(processInfo); 347 } 348 } 349 removeUserProcesses(int uid)350 void removeUserProcesses(int uid) { 351 synchronized (mLock) { 352 mProcesses.removeAll( 353 mProcesses.stream().filter(pi -> pi.uid == uid).collect( 354 Collectors.toList())); 355 } 356 } 357 addIoRecord(UidIoRecord record)358 void addIoRecord(UidIoRecord record) { 359 synchronized (mLock) { 360 mIoRecords.put(record.uid, record); 361 } 362 } 363 } 364 } 365