xref: /aosp_15_r20/frameworks/base/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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