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.internal.os; 18 19 import static org.mockito.Mockito.verify; 20 import static org.mockito.Mockito.verifyNoMoreInteractions; 21 import static org.mockito.Mockito.when; 22 23 import android.support.test.filters.SmallTest; 24 import android.support.test.runner.AndroidJUnit4; 25 26 import org.junit.Before; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.mockito.Mock; 30 import org.mockito.Mockito; 31 import org.mockito.MockitoAnnotations; 32 33 import java.nio.ByteBuffer; 34 import java.nio.ByteOrder; 35 import java.util.Random; 36 37 /** 38 * Test class for {@link KernelUidCpuActiveTimeReader}. 39 * 40 * To run it: 41 * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest 42 */ 43 @SmallTest 44 @RunWith(AndroidJUnit4.class) 45 public class KernelUidCpuActiveTimeReaderTest { 46 @Mock 47 private KernelCpuProcReader mProcReader; 48 @Mock 49 private KernelUidCpuActiveTimeReader.Callback mCallback; 50 private KernelUidCpuActiveTimeReader mReader; 51 52 @Before 53 public void setUp() { 54 MockitoAnnotations.initMocks(this); 55 mReader = new KernelUidCpuActiveTimeReader(mProcReader); 56 mReader.setThrottleInterval(0); 57 } 58 59 @Test 60 public void testReadDelta() { 61 final int cores = 8; 62 final int[] uids = {1, 22, 333, 4444, 5555}; 63 64 final long[][] times = increaseTime(new long[uids.length][cores]); 65 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times)); 66 mReader.readDelta(mCallback); 67 for (int i = 0; i < uids.length; i++) { 68 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); 69 } 70 verifyNoMoreInteractions(mCallback); 71 72 // Verify that a second call will only return deltas. 73 Mockito.reset(mCallback); 74 final long[][] times1 = increaseTime(times); 75 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1)); 76 mReader.readDelta(mCallback); 77 for (int i = 0; i < uids.length; i++) { 78 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i]))); 79 } 80 verifyNoMoreInteractions(mCallback); 81 82 // Verify that there won't be a callback if the proc file values didn't change. 83 Mockito.reset(mCallback); 84 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1)); 85 mReader.readDelta(mCallback); 86 verifyNoMoreInteractions(mCallback); 87 88 // Verify that calling with a null callback doesn't result in any crashes 89 Mockito.reset(mCallback); 90 final long[][] times2 = increaseTime(times1); 91 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times2)); 92 mReader.readDelta(null); 93 94 // Verify that the readDelta call will only return deltas when 95 // the previous call had null callback. 96 Mockito.reset(mCallback); 97 final long[][] times3 = increaseTime(times2); 98 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3)); 99 mReader.readDelta(mCallback); 100 for (int i = 0; i < uids.length; ++i) { 101 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i]))); 102 } 103 verifyNoMoreInteractions(mCallback); 104 } 105 106 @Test 107 public void testReadAbsolute() { 108 final int cores = 8; 109 final int[] uids = {1, 22, 333, 4444, 5555}; 110 111 final long[][] times = increaseTime(new long[uids.length][cores]); 112 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times)); 113 mReader.readAbsolute(mCallback); 114 for (int i = 0; i < uids.length; i++) { 115 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); 116 } 117 verifyNoMoreInteractions(mCallback); 118 119 // Verify that a second call still returns absolute values 120 Mockito.reset(mCallback); 121 final long[][] times1 = increaseTime(times); 122 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1)); 123 mReader.readAbsolute(mCallback); 124 for (int i = 0; i < uids.length; i++) { 125 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times1[i])); 126 } 127 verifyNoMoreInteractions(mCallback); 128 } 129 130 @Test 131 public void testReadDelta_malformedData() { 132 final int cores = 8; 133 final int[] uids = {1, 22, 333, 4444, 5555}; 134 final long[][] times = increaseTime(new long[uids.length][cores]); 135 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times)); 136 mReader.readDelta(mCallback); 137 for (int i = 0; i < uids.length; i++) { 138 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); 139 } 140 verifyNoMoreInteractions(mCallback); 141 142 // Verify that there is no callback if subsequent call is in wrong format. 143 Mockito.reset(mCallback); 144 final long[][] times1 = increaseTime(times); 145 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1).putInt(0, 5)); 146 mReader.readDelta(mCallback); 147 verifyNoMoreInteractions(mCallback); 148 149 // Verify that the internal state was not modified if the given core count does not match 150 // the following # of entries. 151 Mockito.reset(mCallback); 152 final long[][] times2 = increaseTime(times); 153 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times2)); 154 mReader.readDelta(mCallback); 155 for (int i = 0; i < uids.length; i++) { 156 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i]))); 157 } 158 verifyNoMoreInteractions(mCallback); 159 160 // Verify that there is no callback if any value in the proc file is -ve. 161 Mockito.reset(mCallback); 162 final long[][] times3 = increaseTime(times2); 163 times3[uids.length - 1][cores - 1] *= -1; 164 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3)); 165 mReader.readDelta(mCallback); 166 for (int i = 0; i < uids.length - 1; ++i) { 167 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i]))); 168 } 169 verifyNoMoreInteractions(mCallback); 170 171 // Verify that the internal state was not modified when the proc file had -ve value. 172 Mockito.reset(mCallback); 173 for (int i = 0; i < cores; i++) { 174 times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 2520; 175 } 176 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3)); 177 mReader.readDelta(mCallback); 178 verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], 179 getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1]))); 180 verifyNoMoreInteractions(mCallback); 181 182 // Verify that there is no callback if the values in the proc file are decreased. 183 Mockito.reset(mCallback); 184 final long[][] times4 = increaseTime(times3); 185 System.arraycopy(times3[uids.length - 1], 0, times4[uids.length - 1], 0, cores); 186 times4[uids.length - 1][cores - 1] -= 100; 187 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times4)); 188 mReader.readDelta(mCallback); 189 for (int i = 0; i < uids.length - 1; ++i) { 190 verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i]))); 191 } 192 verifyNoMoreInteractions(mCallback); 193 194 // Verify that the internal state was not modified when the proc file had decreased values. 195 Mockito.reset(mCallback); 196 for (int i = 0; i < cores; i++) { 197 times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 2520; 198 } 199 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times4)); 200 mReader.readDelta(mCallback); 201 verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], 202 getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1]))); 203 verifyNoMoreInteractions(mCallback); 204 } 205 206 private long[] subtract(long[] a1, long[] a2) { 207 long[] val = new long[a1.length]; 208 for (int i = 0; i < val.length; ++i) { 209 val[i] = a1[i] - a2[i]; 210 } 211 return val; 212 } 213 214 /** 215 * Unit of original and return value is 10ms. What's special about 2520? 2520 is LCM of 1, 2, 3, 216 * ..., 10. So that when wedivide shared cpu time by concurrent thread count, we always get a 217 * nice integer, avoiding rounding errors. 218 */ 219 private long[][] increaseTime(long[][] original) { 220 long[][] newTime = new long[original.length][original[0].length]; 221 Random rand = new Random(); 222 for (int i = 0; i < original.length; i++) { 223 for (int j = 0; j < original[0].length; j++) { 224 newTime[i][j] = original[i][j] + rand.nextInt(1000) * 2520 + 2520; 225 } 226 } 227 return newTime; 228 } 229 230 // Unit of times is 10ms 231 private long getTotal(long[] times) { 232 long sum = 0; 233 for (int i = 0; i < times.length; i++) { 234 sum += times[i] * 10 / (i + 1); 235 } 236 return sum; 237 } 238 239 /** 240 * Format uids and times (in 10ms) into the following format: 241 * [n, uid0, time0a, time0b, ..., time0n, 242 * uid1, time1a, time1b, ..., time1n, 243 * uid2, time2a, time2b, ..., time2n, etc.] 244 * where n is the total number of cpus (num_possible_cpus) 245 */ 246 private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) { 247 int size = (1 + uids.length * (times[0].length + 1)) * 4; 248 ByteBuffer buf = ByteBuffer.allocate(size); 249 buf.order(ByteOrder.nativeOrder()); 250 buf.putInt(times[0].length); 251 for (int i = 0; i < uids.length; i++) { 252 buf.putInt(uids[i]); 253 for (int j = 0; j < times[i].length; j++) { 254 buf.putInt((int) times[i][j]); 255 } 256 } 257 buf.flip(); 258 return buf.order(ByteOrder.nativeOrder()); 259 } 260 } 261