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 17package com.android.internal.os; 18 19import static org.junit.Assert.assertArrayEquals; 20import static org.junit.Assert.assertEquals; 21import static org.junit.Assert.assertNotNull; 22import static org.mockito.Mockito.when; 23 24import android.support.test.filters.SmallTest; 25import android.support.test.runner.AndroidJUnit4; 26import android.util.SparseArray; 27 28import org.junit.Before; 29import org.junit.Test; 30import org.junit.runner.RunWith; 31import org.mockito.Mock; 32import org.mockito.Mockito; 33import org.mockito.MockitoAnnotations; 34 35import java.nio.ByteBuffer; 36import java.nio.ByteOrder; 37import java.util.Arrays; 38import java.util.Random; 39 40/** 41 * Test class for {@link KernelUidCpuClusterTimeReader}. 42 * 43 * To run it: 44 * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest 45 */ 46@SmallTest 47@RunWith(AndroidJUnit4.class) 48public class KernelUidCpuClusterTimeReaderTest { 49 @Mock 50 private KernelCpuProcReader mProcReader; 51 private KernelUidCpuClusterTimeReader mReader; 52 private VerifiableCallback mCallback; 53 54 @Before 55 public void setUp() { 56 MockitoAnnotations.initMocks(this); 57 mReader = new KernelUidCpuClusterTimeReader(mProcReader); 58 mCallback = new VerifiableCallback(); 59 mReader.setThrottleInterval(0); 60 } 61 62 @Test 63 public void testReadDelta() throws Exception { 64 VerifiableCallback cb = new VerifiableCallback(); 65 final int cores = 6; 66 final int[] clusters = {2, 4}; 67 final int[] uids = {1, 22, 333, 4444, 5555}; 68 69 // Verify initial call 70 final long[][] times = increaseTime(new long[uids.length][cores]); 71 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times)); 72 mReader.readDelta(cb); 73 for (int i = 0; i < uids.length; i++) { 74 cb.verify(uids[i], getTotal(clusters, times[i])); 75 } 76 cb.verifyNoMoreInteractions(); 77 78 // Verify that a second call will only return deltas. 79 cb.clear(); 80 Mockito.reset(mProcReader); 81 final long[][] times1 = increaseTime(times); 82 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1)); 83 mReader.readDelta(cb); 84 for (int i = 0; i < uids.length; i++) { 85 cb.verify(uids[i], getTotal(clusters, subtract(times1[i], times[i]))); 86 } 87 cb.verifyNoMoreInteractions(); 88 89 // Verify that there won't be a callback if the proc file values didn't change. 90 cb.clear(); 91 Mockito.reset(mProcReader); 92 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1)); 93 mReader.readDelta(cb); 94 cb.verifyNoMoreInteractions(); 95 96 // Verify that calling with a null callback doesn't result in any crashes 97 Mockito.reset(mProcReader); 98 final long[][] times2 = increaseTime(times1); 99 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times2)); 100 mReader.readDelta(null); 101 102 // Verify that the readDelta call will only return deltas when 103 // the previous call had null callback. 104 cb.clear(); 105 Mockito.reset(mProcReader); 106 final long[][] times3 = increaseTime(times2); 107 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3)); 108 mReader.readDelta(cb); 109 for (int i = 0; i < uids.length; i++) { 110 cb.verify(uids[i], getTotal(clusters, subtract(times3[i], times2[i]))); 111 } 112 cb.verifyNoMoreInteractions(); 113 114 } 115 116 @Test 117 public void testReadAbsolute() throws Exception { 118 VerifiableCallback cb = new VerifiableCallback(); 119 final int cores = 6; 120 final int[] clusters = {2, 4}; 121 final int[] uids = {1, 22, 333, 4444, 5555}; 122 123 // Verify return absolute value 124 final long[][] times = increaseTime(new long[uids.length][cores]); 125 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times)); 126 mReader.readAbsolute(cb); 127 for (int i = 0; i < uids.length; i++) { 128 cb.verify(uids[i], getTotal(clusters, times[i])); 129 } 130 cb.verifyNoMoreInteractions(); 131 132 // Verify that a second call should return the same absolute value 133 cb.clear(); 134 Mockito.reset(mProcReader); 135 final long[][] times1 = increaseTime(times); 136 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1)); 137 mReader.readAbsolute(cb); 138 for (int i = 0; i < uids.length; i++) { 139 cb.verify(uids[i], getTotal(clusters, times1[i])); 140 } 141 cb.verifyNoMoreInteractions(); 142 } 143 144 @Test 145 public void testReadDelta_malformedData() throws Exception { 146 final int cores = 6; 147 final int[] clusters = {2, 4}; 148 final int[] uids = {1, 22, 333, 4444, 5555}; 149 150 // Verify initial call 151 final long[][] times = increaseTime(new long[uids.length][cores]); 152 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times)); 153 mReader.readDelta(mCallback); 154 for (int i = 0; i < uids.length; i++) { 155 mCallback.verify(uids[i], getTotal(clusters, times[i])); 156 } 157 mCallback.verifyNoMoreInteractions(); 158 159 // Verify that there is no callback if a call has wrong format 160 mCallback.clear(); 161 Mockito.reset(mProcReader); 162 final long[][] temp = increaseTime(times); 163 final long[][] times1 = new long[uids.length][]; 164 for (int i = 0; i < temp.length; i++) { 165 times1[i] = Arrays.copyOfRange(temp[i], 0, 4); 166 } 167 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1)); 168 mReader.readDelta(mCallback); 169 mCallback.verifyNoMoreInteractions(); 170 171 // Verify that the internal state was not modified if the given core count does not match 172 // the following # of entries. 173 mCallback.clear(); 174 Mockito.reset(mProcReader); 175 final long[][] times2 = increaseTime(times); 176 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times2)); 177 mReader.readDelta(mCallback); 178 for (int i = 0; i < uids.length; i++) { 179 mCallback.verify(uids[i], getTotal(clusters, subtract(times2[i], times[i]))); 180 } 181 mCallback.verifyNoMoreInteractions(); 182 183 // Verify that there is no callback if any value in the proc file is -ve. 184 mCallback.clear(); 185 Mockito.reset(mProcReader); 186 final long[][] times3 = increaseTime(times2); 187 times3[uids.length - 1][cores - 1] *= -1; 188 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3)); 189 mReader.readDelta(mCallback); 190 for (int i = 0; i < uids.length - 1; i++) { 191 mCallback.verify(uids[i], getTotal(clusters, subtract(times3[i], times2[i]))); 192 } 193 mCallback.verifyNoMoreInteractions(); 194 195 // Verify that the internal state was not modified when the proc file had -ve value. 196 mCallback.clear(); 197 Mockito.reset(mProcReader); 198 for (int i = 0; i < cores; i++) { 199 times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 2520; 200 } 201 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3)); 202 mReader.readDelta(mCallback); 203 mCallback.verify(uids[uids.length - 1], 204 getTotal(clusters, subtract(times3[uids.length - 1], times2[uids.length - 1]))); 205 206 // Verify that there is no callback if the values in the proc file are decreased. 207 mCallback.clear(); 208 Mockito.reset(mProcReader); 209 final long[][] times4 = increaseTime(times3); 210 System.arraycopy(times3[uids.length - 1], 0, times4[uids.length - 1], 0, cores); 211 times4[uids.length - 1][cores - 1] -= 100; 212 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times4)); 213 mReader.readDelta(mCallback); 214 for (int i = 0; i < uids.length - 1; i++) { 215 mCallback.verify(uids[i], getTotal(clusters, subtract(times4[i], times3[i]))); 216 } 217 mCallback.verifyNoMoreInteractions(); 218 219 // Verify that the internal state was not modified when the proc file had decreased values. 220 mCallback.clear(); 221 Mockito.reset(mProcReader); 222 for (int i = 0; i < cores; i++) { 223 times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 2520; 224 } 225 when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times4)); 226 mReader.readDelta(mCallback); 227 mCallback.verify(uids[uids.length - 1], 228 getTotal(clusters, subtract(times3[uids.length - 1], times2[uids.length - 1]))); 229 mCallback.verifyNoMoreInteractions(); 230 } 231 232 233 private long[] subtract(long[] a1, long[] a2) { 234 long[] val = new long[a1.length]; 235 for (int i = 0; i < val.length; ++i) { 236 val[i] = a1[i] - a2[i]; 237 } 238 return val; 239 } 240 241 /** 242 * Unit is 10ms. What's special about 2520? 2520 is LCM of 1, 2, 3, ..., 10. So that when we 243 * divide shared cpu time by concurrent thread count, we always get a nice integer, avoiding 244 * rounding errors. 245 */ 246 private long[][] increaseTime(long[][] original) { 247 long[][] newTime = new long[original.length][original[0].length]; 248 Random rand = new Random(); 249 for (int i = 0; i < original.length; i++) { 250 for (int j = 0; j < original[0].length; j++) { 251 newTime[i][j] = original[i][j] + rand.nextInt(1000) * 2520 + 2520; 252 } 253 } 254 return newTime; 255 } 256 257 // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader 258 private long[] getTotal(int[] cluster, long[] times) { 259 int core = 0; 260 long[] sumTimes = new long[cluster.length]; 261 for (int i = 0; i < cluster.length; i++) { 262 double sum = 0; 263 for (int j = 0; j < cluster[i]; j++) { 264 sum += (double) times[core++] * 10 / (j + 1); 265 } 266 sumTimes[i] = (long) sum; 267 } 268 return sumTimes; 269 } 270 271 private class VerifiableCallback implements KernelUidCpuClusterTimeReader.Callback { 272 273 SparseArray<long[]> mData = new SparseArray<>(); 274 int count = 0; 275 276 public void verify(int uid, long[] cpuClusterTimeMs) { 277 long[] array = mData.get(uid); 278 assertNotNull(array); 279 assertArrayEquals(cpuClusterTimeMs, array); 280 count++; 281 } 282 283 public void clear() { 284 mData.clear(); 285 count = 0; 286 } 287 288 @Override 289 public void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs) { 290 long[] array = new long[cpuClusterTimeMs.length]; 291 System.arraycopy(cpuClusterTimeMs, 0, array, 0, array.length); 292 mData.put(uid, array); 293 } 294 295 public void verifyNoMoreInteractions() { 296 assertEquals(mData.size(), count); 297 } 298 } 299 300 /** 301 * Format uids and times (in 10ms) into the following format: 302 * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n, 303 * uid1, time1a, time1b, ..., time1n, 304 * uid2, time2a, time2b, ..., time2n, etc.] 305 * where n is the number of policies 306 * xi is the number cpus on a particular policy 307 */ 308 private ByteBuffer getUidTimesBytes(int[] uids, int[] clusters, long[][] times) { 309 int size = (1 + clusters.length + uids.length * (times[0].length + 1)) * 4; 310 ByteBuffer buf = ByteBuffer.allocate(size); 311 buf.order(ByteOrder.nativeOrder()); 312 buf.putInt(clusters.length); 313 for (int i = 0; i < clusters.length; i++) { 314 buf.putInt(clusters[i]); 315 } 316 for (int i = 0; i < uids.length; i++) { 317 buf.putInt(uids[i]); 318 for (int j = 0; j < times[i].length; j++) { 319 buf.putInt((int) (times[i][j])); 320 } 321 } 322 buf.flip(); 323 return buf.order(ByteOrder.nativeOrder()); 324 } 325} 326