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 17 package com.android.internal.os; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.testng.Assert.assertThrows; 25 26 import android.content.Context; 27 import android.os.FileUtils; 28 import android.platform.test.annotations.IgnoreUnderRavenwood; 29 import android.platform.test.annotations.Presubmit; 30 import android.platform.test.ravenwood.RavenwoodRule; 31 32 import androidx.test.InstrumentationRegistry; 33 import androidx.test.ext.junit.runners.AndroidJUnit4; 34 import androidx.test.filters.SmallTest; 35 36 import org.junit.After; 37 import org.junit.Before; 38 import org.junit.Rule; 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 import java.io.File; 43 import java.io.IOException; 44 import java.io.OutputStream; 45 import java.nio.file.Files; 46 import java.nio.file.Path; 47 import java.util.ArrayList; 48 import java.util.Comparator; 49 import java.util.function.Predicate; 50 51 @Presubmit 52 @SmallTest 53 @RunWith(AndroidJUnit4.class) 54 @IgnoreUnderRavenwood(reason = "Needs kernel support") 55 public class KernelCpuThreadReaderTest { 56 @Rule 57 public final RavenwoodRule mRavenwood = new RavenwoodRule(); 58 59 private File mProcDirectory; 60 61 @Before setUp()62 public void setUp() { 63 Context context = InstrumentationRegistry.getContext(); 64 mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); 65 } 66 67 @After tearDown()68 public void tearDown() throws Exception { 69 FileUtils.deleteContents(mProcDirectory); 70 } 71 72 @Test testReader_byUids()73 public void testReader_byUids() throws IOException { 74 int[] uids = new int[]{0, 2, 3, 4, 5, 6000}; 75 Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4; 76 int[] expectedUids = new int[]{0, 4, 5, 6000}; 77 KernelCpuThreadReader.Injector processUtils = 78 new KernelCpuThreadReader.Injector() { 79 @Override 80 public int getUidForPid(int pid) { 81 return pid; 82 } 83 }; 84 85 for (int uid : uids) { 86 setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)), 87 new int[]{uid * 10}, 88 "process" + uid, new String[]{"thread" + uid}, new int[]{1000}, 89 new int[][]{{uid}}); 90 } 91 final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader( 92 8, 93 uidPredicate, 94 mProcDirectory.toPath(), 95 mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"), 96 processUtils); 97 ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids = 98 kernelCpuThreadReader.getProcessCpuUsage(); 99 processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId)); 100 101 assertEquals(expectedUids.length, processCpuUsageByUids.size()); 102 for (int i = 0; i < expectedUids.length; i++) { 103 KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = 104 processCpuUsageByUids.get(i); 105 int uid = expectedUids[i]; 106 checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), 107 uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid}, 108 new int[]{1000}, new int[][]{{uid}}); 109 } 110 } 111 setupDirectory(Path processPath, int[] threadIds, String processName, String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes)112 private void setupDirectory(Path processPath, int[] threadIds, String processName, 113 String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException { 114 // Make /proc/$PID 115 assertTrue(processPath.toFile().mkdirs()); 116 117 // Make /proc/$PID/task 118 final Path selfThreadsPath = processPath.resolve("task"); 119 assertTrue(selfThreadsPath.toFile().mkdirs()); 120 121 // Make /proc/$PID/cmdline 122 Files.write(processPath.resolve("cmdline"), processName.getBytes()); 123 124 // Make thread directories in reverse order, as they are read in order of creation by 125 // CpuThreadProcReader 126 for (int i = 0; i < threadIds.length; i++) { 127 // Make /proc/$PID/task/$TID 128 final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); 129 assertTrue(threadPath.toFile().mkdirs()); 130 131 // Make /proc/$PID/task/$TID/comm 132 Files.write(threadPath.resolve("comm"), threadNames[i].getBytes()); 133 134 // Make /proc/$PID/task/$TID/time_in_state 135 final OutputStream timeInStateStream = 136 Files.newOutputStream(threadPath.resolve("time_in_state")); 137 for (int j = 0; j < cpuFrequencies.length; j++) { 138 final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; 139 timeInStateStream.write(line.getBytes()); 140 } 141 timeInStateStream.close(); 142 } 143 } 144 checkResults(KernelCpuThreadReader.ProcessCpuUsage processCpuUsage, int[] readerCpuFrequencies, int uid, int processId, int[] threadIds, String processName, String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes)145 private void checkResults(KernelCpuThreadReader.ProcessCpuUsage processCpuUsage, 146 int[] readerCpuFrequencies, int uid, int processId, int[] threadIds, String processName, 147 String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) { 148 assertNotNull(processCpuUsage); 149 assertEquals(processId, processCpuUsage.processId); 150 assertEquals(uid, processCpuUsage.uid); 151 assertEquals(processName, processCpuUsage.processName); 152 assertEquals(threadIds.length, processCpuUsage.threadCpuUsages.size()); 153 154 // Sort the thread CPU usages to compare with test case 155 final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = 156 new ArrayList<>(processCpuUsage.threadCpuUsages); 157 threadCpuUsages.sort(Comparator.comparingInt(a -> a.threadId)); 158 159 int threadCount = 0; 160 for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage : threadCpuUsages) { 161 assertEquals(threadIds[threadCount], threadCpuUsage.threadId); 162 assertEquals(threadNames[threadCount], threadCpuUsage.threadName); 163 164 for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) { 165 assertEquals( 166 cpuTimes[threadCount][i] * 10, 167 threadCpuUsage.usageTimesMillis[i]); 168 assertEquals( 169 cpuFrequencies[i], 170 readerCpuFrequencies[i]); 171 } 172 threadCount++; 173 } 174 175 assertEquals(threadCount, threadIds.length); 176 } 177 178 @Test testBucketSetup_simple()179 public void testBucketSetup_simple() { 180 long[] frequencies = {1, 2, 3, 4, 1, 2, 3, 4}; 181 KernelCpuThreadReader.FrequencyBucketCreator 182 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 183 frequencies, 4); 184 assertArrayEquals( 185 new int[]{1, 3, 1, 3}, 186 frequencyBucketCreator.bucketFrequencies(frequencies)); 187 assertArrayEquals( 188 new int[]{2, 2, 2, 2}, 189 frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1})); 190 } 191 192 @Test testBucketSetup_noBig()193 public void testBucketSetup_noBig() { 194 long[] frequencies = {1, 2, 3, 4, 5, 6, 7, 8}; 195 KernelCpuThreadReader.FrequencyBucketCreator 196 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 197 frequencies, 4); 198 assertArrayEquals( 199 new int[]{1, 3, 5, 7}, 200 frequencyBucketCreator.bucketFrequencies(frequencies)); 201 assertArrayEquals( 202 new int[]{2, 2, 2, 2}, 203 frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1})); 204 } 205 206 @Test testBucketSetup_moreLittle()207 public void testBucketSetup_moreLittle() { 208 long[] frequencies = {1, 2, 3, 4, 5, 1, 2, 3}; 209 KernelCpuThreadReader.FrequencyBucketCreator 210 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 211 frequencies, 4); 212 assertArrayEquals( 213 new int[]{1, 3, 1, 2}, 214 frequencyBucketCreator.bucketFrequencies(frequencies)); 215 assertArrayEquals( 216 new int[]{2, 3, 1, 2}, 217 frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1})); 218 } 219 220 @Test testBucketSetup_moreBig()221 public void testBucketSetup_moreBig() { 222 long[] frequencies = {1, 2, 3, 1, 2, 3, 4, 5}; 223 KernelCpuThreadReader.FrequencyBucketCreator 224 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 225 frequencies, 4); 226 assertArrayEquals( 227 new int[]{1, 2, 1, 3}, 228 frequencyBucketCreator.bucketFrequencies(frequencies)); 229 assertArrayEquals( 230 new int[]{1, 2, 2, 3}, 231 frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1})); 232 } 233 234 @Test testBucketSetup_equalBuckets()235 public void testBucketSetup_equalBuckets() { 236 long[] frequencies = {1, 2, 3, 4, 1, 2, 3, 4}; 237 KernelCpuThreadReader.FrequencyBucketCreator 238 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 239 frequencies, 8); 240 assertArrayEquals( 241 new int[]{1, 2, 3, 4, 1, 2, 3, 4}, 242 frequencyBucketCreator.bucketFrequencies(frequencies)); 243 assertArrayEquals( 244 new int[]{1, 1, 1, 1, 1, 1, 1, 1}, 245 frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1})); 246 } 247 248 @Test testBucketSetup_moreBigBucketsThanFrequencies()249 public void testBucketSetup_moreBigBucketsThanFrequencies() { 250 long[] frequencies = {1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3}; 251 KernelCpuThreadReader.FrequencyBucketCreator 252 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 253 frequencies, 8); 254 assertArrayEquals( 255 new int[]{1, 3, 5, 7, 1, 2, 3}, 256 frequencyBucketCreator.bucketFrequencies(frequencies)); 257 assertArrayEquals( 258 new int[]{2, 2, 2, 3, 1, 1, 1}, 259 frequencyBucketCreator.bucketValues( 260 new long[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})); 261 } 262 263 @Test testBucketSetup_oneBucket()264 public void testBucketSetup_oneBucket() { 265 long[] frequencies = {1, 2, 3, 4, 2, 3, 4, 5}; 266 KernelCpuThreadReader.FrequencyBucketCreator 267 frequencyBucketCreator = new KernelCpuThreadReader.FrequencyBucketCreator( 268 frequencies, 1); 269 assertArrayEquals( 270 new int[]{1}, 271 frequencyBucketCreator.bucketFrequencies(frequencies)); 272 assertArrayEquals( 273 new int[]{8}, 274 frequencyBucketCreator.bucketValues( 275 new long[]{1, 1, 1, 1, 1, 1, 1, 1})); 276 } 277 278 @Test testBucketSetup_threeClusters()279 public void testBucketSetup_threeClusters() { 280 long[] frequencies = {1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6}; 281 KernelCpuThreadReader.FrequencyBucketCreator frequencyBucketCreator = 282 new KernelCpuThreadReader.FrequencyBucketCreator(frequencies, 6); 283 assertArrayEquals( 284 new int[] {1, 3, 2, 4, 3, 5}, 285 frequencyBucketCreator.bucketFrequencies(frequencies)); 286 assertArrayEquals( 287 new int[] {2, 2, 2, 2, 2, 2}, 288 frequencyBucketCreator.bucketValues( 289 new long[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})); 290 } 291 292 @Test testBucketSetup_moreClustersThanBuckets()293 public void testBucketSetup_moreClustersThanBuckets() { 294 long[] frequencies = {1, 1, 1, 1, 1, 1, 1, 1}; 295 KernelCpuThreadReader.FrequencyBucketCreator frequencyBucketCreator = 296 new KernelCpuThreadReader.FrequencyBucketCreator(frequencies, 4); 297 assertArrayEquals( 298 new int[] {1, 1, 1, 1}, frequencyBucketCreator.bucketFrequencies(frequencies)); 299 assertArrayEquals( 300 new int[] {1, 1, 1, 5}, 301 frequencyBucketCreator.bucketValues(new long[] {1, 1, 1, 1, 1, 1, 1, 1})); 302 } 303 304 @Test testUidPredicate_singleRange()305 public void testUidPredicate_singleRange() { 306 KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = 307 KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1000-1999"); 308 assertTrue(uidPredicate.test(1000)); 309 assertTrue(uidPredicate.test(1050)); 310 assertTrue(uidPredicate.test(1999)); 311 assertFalse(uidPredicate.test(2000)); 312 assertFalse(uidPredicate.test(0)); 313 assertFalse(uidPredicate.test(10000)); 314 assertFalse(uidPredicate.test(-100)); 315 } 316 317 @Test testUidPredicate_singleUid()318 public void testUidPredicate_singleUid() { 319 KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = 320 KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1234-1234"); 321 assertTrue(uidPredicate.test(1234)); 322 assertFalse(uidPredicate.test(1235)); 323 assertFalse(uidPredicate.test(1232)); 324 assertFalse(uidPredicate.test(0)); 325 assertFalse(uidPredicate.test(-1234)); 326 } 327 328 @Test testUidPredicate_uidAndRange()329 public void testUidPredicate_uidAndRange() { 330 KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = 331 KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString( 332 "1000-1000;1050-1060"); 333 assertTrue(uidPredicate.test(1000)); 334 assertTrue(uidPredicate.test(1050)); 335 assertTrue(uidPredicate.test(1054)); 336 assertTrue(uidPredicate.test(1060)); 337 assertFalse(uidPredicate.test(1040)); 338 assertFalse(uidPredicate.test(1001)); 339 assertFalse(uidPredicate.test(0)); 340 assertFalse(uidPredicate.test(-1000)); 341 } 342 343 @Test testUidPredicate_multiple()344 public void testUidPredicate_multiple() { 345 KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = 346 KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString( 347 "1000-1000;1050-1060;1001-1001;2000-3000"); 348 assertTrue(uidPredicate.test(1000)); 349 assertTrue(uidPredicate.test(1001)); 350 assertTrue(uidPredicate.test(1050)); 351 assertTrue(uidPredicate.test(1054)); 352 assertTrue(uidPredicate.test(1060)); 353 assertTrue(uidPredicate.test(1001)); 354 assertTrue(uidPredicate.test(2000)); 355 assertTrue(uidPredicate.test(2444)); 356 assertTrue(uidPredicate.test(3000)); 357 assertFalse(uidPredicate.test(0)); 358 assertFalse(uidPredicate.test(1040)); 359 assertFalse(uidPredicate.test(3001)); 360 assertFalse(uidPredicate.test(1999)); 361 } 362 363 @Test testUidPredicate_zero()364 public void testUidPredicate_zero() { 365 KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate = 366 KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("0-0"); 367 assertTrue(uidPredicate.test(0)); 368 assertFalse(uidPredicate.test(1)); 369 assertFalse(uidPredicate.test(2000)); 370 assertFalse(uidPredicate.test(10000)); 371 assertFalse(uidPredicate.test(-100)); 372 } 373 374 @Test testUidPredicate_emptyRangeString()375 public void testUidPredicate_emptyRangeString() { 376 assertThrows( 377 NumberFormatException.class, 378 () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("")); 379 } 380 381 @Test testUidPredicate_singleNumber()382 public void testUidPredicate_singleNumber() { 383 assertThrows( 384 NumberFormatException.class, 385 () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1000")); 386 } 387 388 @Test testUidPredicate_lettersInRange()389 public void testUidPredicate_lettersInRange() { 390 assertThrows( 391 NumberFormatException.class, 392 () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString( 393 "0-0;1-1;a;3-3")); 394 } 395 396 @Test testUidPredicate_onlyLetters()397 public void testUidPredicate_onlyLetters() { 398 assertThrows( 399 NumberFormatException.class, 400 () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("abc")); 401 } 402 403 @Test testUidPredicate_backwardsRange()404 public void testUidPredicate_backwardsRange() { 405 assertThrows( 406 IllegalArgumentException.class, 407 () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("20-10")); 408 } 409 410 @Test testUidPredicate_comma()411 public void testUidPredicate_comma() { 412 assertThrows( 413 NumberFormatException.class, 414 () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1-1,2-2,3-3")); 415 } 416 } 417