Home | History | Annotate | Download | only in os
      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