Loading core/java/com/android/internal/os/KernelCpuProcStringReader.java +75 −45 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.CharBuffer; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; Loading Loading @@ -59,6 +60,7 @@ public class KernelCpuProcStringReader { private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state"; private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time"; private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time"; private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat"; private static final KernelCpuProcStringReader FREQ_TIME_READER = new KernelCpuProcStringReader(PROC_UID_FREQ_TIME); Loading @@ -66,19 +68,25 @@ public class KernelCpuProcStringReader { new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME); private static final KernelCpuProcStringReader CLUSTER_TIME_READER = new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME); private static final KernelCpuProcStringReader USER_SYS_TIME_READER = new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME); public static KernelCpuProcStringReader getFreqTimeReaderInstance() { static KernelCpuProcStringReader getFreqTimeReaderInstance() { return FREQ_TIME_READER; } public static KernelCpuProcStringReader getActiveTimeReaderInstance() { static KernelCpuProcStringReader getActiveTimeReaderInstance() { return ACTIVE_TIME_READER; } public static KernelCpuProcStringReader getClusterTimeReaderInstance() { static KernelCpuProcStringReader getClusterTimeReaderInstance() { return CLUSTER_TIME_READER; } static KernelCpuProcStringReader getUserSysTimeReaderInstance() { return USER_SYS_TIME_READER; } private int mErrors = 0; private final Path mFile; private char[] mBuf; Loading Loading @@ -164,12 +172,12 @@ public class KernelCpuProcStringReader { // ReentrantReadWriteLock allows lock downgrading. mReadLock.lock(); return new ProcFileIterator(total); } catch (FileNotFoundException e) { } catch (FileNotFoundException | NoSuchFileException e) { mErrors++; Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile); } catch (IOException e) { mErrors++; Slog.e(TAG, "Error reading: " + mFile, e); Slog.e(TAG, "Error reading " + mFile, e); } finally { StrictMode.setThreadPolicyMask(oldMask); mWriteLock.unlock(); Loading @@ -193,6 +201,11 @@ public class KernelCpuProcStringReader { mSize = size; } /** @return Whether there are more lines in the iterator. */ public boolean hasNextLine() { return mPos < mSize; } /** * Fetches the next line. Note that all subsequent return values share the same char[] * under the hood. Loading @@ -214,25 +227,48 @@ public class KernelCpuProcStringReader { return CharBuffer.wrap(mBuf, start, i - start); } /** Total size of the proc file in chars. */ public int size() { return mSize; } /** Must call close at the end to release the read lock! Or use try-with-resources. */ public void close() { mReadLock.unlock(); } } /** * Fetches the next line, converts all numbers into long, and puts into the given long[]. * To avoid GC, caller should try to use the same array for all calls. All non-numeric * chars are treated as delimiters. All numbers are non-negative. * Converts all numbers in the CharBuffer into longs, and puts into the given long[]. * * Space and colon are treated as delimiters. All other chars are not allowed. All numbers * are non-negative. To avoid GC, caller should try to use the same array for all calls. * * This method also resets the given buffer to the original position before return so that * it can be read again. * * @param buf The char buffer to be converted. * @param array An array to store the parsed numbers. * @return The number of elements written to the given array. -1 if there is no more line. * @return The number of elements written to the given array. -1 if buf is null, -2 if buf * contains invalid char, -3 if any number overflows. */ public int nextLineAsArray(long[] array) { CharBuffer buf = nextLine(); public static int asLongs(CharBuffer buf, long[] array) { if (buf == null) { return -1; } final int initialPos = buf.position(); int count = 0; long num = -1; char c; while (buf.remaining() > 0 && count < array.length) { c = buf.get(); if (!(isNumber(c) || c == ' ' || c == ':')) { buf.position(initialPos); return -2; } if (num < 0) { if (isNumber(c)) { num = c - '0'; Loading @@ -240,6 +276,10 @@ public class KernelCpuProcStringReader { } else { if (isNumber(c)) { num = num * 10 + c - '0'; if (num < 0) { buf.position(initialPos); return -3; } } else { array[count++] = num; num = -1; Loading @@ -249,21 +289,11 @@ public class KernelCpuProcStringReader { if (num >= 0) { array[count++] = num; } buf.position(initialPos); return count; } /** Total size of the proc file in chars. */ public int size() { return mSize; } /** Must call close at the end to release the read lock! Or use try-with-resources. */ public void close() { mReadLock.unlock(); } private boolean isNumber(char c) { private static boolean isNumber(char c) { return c >= '0' && c <= '9'; } } } core/java/com/android/internal/os/KernelCpuUidTimeReader.java 0 → 100644 +776 −0 File added.Preview size limit exceeded, changes collapsed. Show changes core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +4 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,10 @@ import org.junit.runners.Suite; BatteryStatsUserLifecycleTests.class, KernelCpuProcReaderTest.class, KernelCpuProcStringReaderTest.class, KernelCpuUidActiveTimeReaderTest.class, KernelCpuUidClusterTimeReaderTest.class, KernelCpuUidFreqTimeReaderTest.class, KernelCpuUidUserSysTimeReaderTest.class, KernelMemoryBandwidthStatsTest.class, KernelSingleUidTimeReaderTest.class, KernelUidCpuFreqTimeReaderTest.class, Loading core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java +24 −3 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import java.io.BufferedWriter; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.CharBuffer; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; Loading Loading @@ -149,7 +150,7 @@ public class KernelCpuProcStringReaderTest { + "0 0 1 1 1 0 2 0 221", iter.nextLine().toString()); long[] actual = new long[43]; iter.nextLineAsArray(actual); KernelCpuProcStringReader.asLongs(iter.nextLine(), actual); assertArrayEquals( new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721}, Loading Loading @@ -183,7 +184,7 @@ public class KernelCpuProcStringReaderTest { } } /** Tests nextLineToArray functionality. */ /** Tests reading lines, then converting to long[]. */ @Test public void testReadLineToArray() throws Exception { final long[][] data = getTestArray(800, 50); Loading @@ -193,12 +194,32 @@ public class KernelCpuProcStringReaderTest { long[] actual = new long[50]; try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { for (long[] expected : data) { assertEquals(50, iter.nextLineAsArray(actual)); CharBuffer cb = iter.nextLine(); String before = cb.toString(); assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual)); assertArrayEquals(expected, actual); assertEquals("Buffer not reset to the pos before reading", before, cb.toString()); } } } /** Tests error handling of converting to long[]. */ @Test public void testLineToArrayErrorHandling() { long[] actual = new long[100]; String invalidChar = "123: -1234 456"; String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123"; CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length()); assertEquals("Failed to report err for: " + invalidChar, -2, KernelCpuProcStringReader.asLongs(cb, actual)); assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString()); cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length()); assertEquals("Failed to report err for: " + overflow, -3, KernelCpuProcStringReader.asLongs(cb, actual)); assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString()); } /** * Tests that reading a file over the limit (1MB) will return null. */ Loading core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java 0 → 100644 +262 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.SparseLongArray; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Random; /** * Test class for {@link KernelCpuUidActiveTimeReader}. * * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class KernelCpuUidActiveTimeReaderTest { private File mTestDir; private File mTestFile; private KernelCpuUidActiveTimeReader mReader; private VerifiableCallback mCallback; private Random mRand = new Random(12345); private final int mCpus = 4; private final String mHeadline = "cpus: 4\n"; private final int[] mUids = {0, 1, 22, 333, 4444, 55555}; private Context getContext() { return InstrumentationRegistry.getContext(); } @Before public void setUp() { mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); mTestFile = new File(mTestDir, "test.file"); mReader = new KernelCpuUidActiveTimeReader( new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false); mCallback = new VerifiableCallback(); } @After public void tearDown() throws Exception { FileUtils.deleteContents(mTestDir); FileUtils.deleteContents(getContext().getFilesDir()); } @Test public void testReadDelta() throws Exception { final long[][] times = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times)); mReader.readDelta(mCallback); for (int i = 0; i < mUids.length; ++i) { mCallback.verify(mUids[i], getActiveTime(times[i])); } mCallback.verifyNoMoreInteractions(); // Verify that a second call will only return deltas. mCallback.clear(); final long[][] newTimes1 = increaseTime(times); writeToFile(mHeadline + uidLines(mUids, newTimes1)); mReader.readDelta(mCallback); for (int i = 0; i < mUids.length; ++i) { mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i])); } mCallback.verifyNoMoreInteractions(); // Verify that there won't be a callback if the proc file values didn't change. mCallback.clear(); mReader.readDelta(mCallback); mCallback.verifyNoMoreInteractions(); // Verify that calling with a null callback doesn't result in any crashes mCallback.clear(); final long[][] newTimes2 = increaseTime(newTimes1); writeToFile(mHeadline + uidLines(mUids, newTimes2)); mReader.readDelta(null); mCallback.verifyNoMoreInteractions(); // Verify that the readDelta call will only return deltas when // the previous call had null callback. mCallback.clear(); final long[][] newTimes3 = increaseTime(newTimes2); writeToFile(mHeadline + uidLines(mUids, newTimes3)); mReader.readDelta(mCallback); for (int i = 0; i < mUids.length; ++i) { mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); } @Test public void testReadAbsolute() throws Exception { final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times1)); mReader.readAbsolute(mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times1[i])); } mCallback.verifyNoMoreInteractions(); // Verify that a second call should still return absolute values mCallback.clear(); final long[][] times2 = increaseTime(times1); writeToFile(mHeadline + uidLines(mUids, times2)); mReader.readAbsolute(mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times2[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); } @Test public void testReadDeltaDecreasedTime() throws Exception { final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times1)); mReader.readDelta(mCallback); // Verify that there should not be a callback for a particular UID if its time decreases. mCallback.clear(); final long[][] times2 = increaseTime(times1); System.arraycopy(times1[0], 0, times2[0], 0, mCpus); times2[0][0] = 100; writeToFile(mHeadline + uidLines(mUids, times2)); mReader.readDelta(mCallback); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); // Verify that the internal state was not modified. mCallback.clear(); final long[][] times3 = increaseTime(times2); times3[0] = increaseTime(times1)[0]; writeToFile(mHeadline + uidLines(mUids, times3)); mReader.readDelta(mCallback); mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0])); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i])); } mCallback.verifyNoMoreInteractions(); } @Test public void testReadDeltaNegativeTime() throws Exception { final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times1)); mReader.readDelta(mCallback); // Verify that there should not be a callback for a particular UID if its time is -ve. mCallback.clear(); final long[][] times2 = increaseTime(times1); times2[0][0] *= -1; writeToFile(mHeadline + uidLines(mUids, times2)); mReader.readDelta(mCallback); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); // Verify that the internal state was not modified. mCallback.clear(); final long[][] times3 = increaseTime(times2); times3[0] = increaseTime(times1)[0]; writeToFile(mHeadline + uidLines(mUids, times3)); mReader.readDelta(mCallback); mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0])); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i])); } mCallback.verifyNoMoreInteractions(); } private String uidLines(int[] uids, long[][] times) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < uids.length; i++) { sb.append(uids[i]).append(':'); for (int j = 0; j < times[i].length; j++) { sb.append(' ').append(times[i][j] / 10); } sb.append('\n'); } return sb.toString(); } private void writeToFile(String s) throws IOException { try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { w.write(s); w.flush(); } } private long[][] increaseTime(long[][] original) { long[][] newTime = new long[original.length][original[0].length]; for (int i = 0; i < original.length; i++) { for (int j = 0; j < original[0].length; j++) { newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000; } } return newTime; } private long getActiveTime(long[] times) { return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4; } private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> { SparseLongArray mData = new SparseLongArray(); public void verify(int uid, long time) { assertEquals(time, mData.get(uid)); mData.delete(uid); } public void clear() { mData.clear(); } @Override public void onUidCpuTime(int uid, Long time) { mData.put(uid, time); } public void verifyNoMoreInteractions() { assertEquals(0, mData.size()); } } } Loading
core/java/com/android/internal/os/KernelCpuProcStringReader.java +75 −45 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.CharBuffer; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; Loading Loading @@ -59,6 +60,7 @@ public class KernelCpuProcStringReader { private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state"; private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time"; private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time"; private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat"; private static final KernelCpuProcStringReader FREQ_TIME_READER = new KernelCpuProcStringReader(PROC_UID_FREQ_TIME); Loading @@ -66,19 +68,25 @@ public class KernelCpuProcStringReader { new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME); private static final KernelCpuProcStringReader CLUSTER_TIME_READER = new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME); private static final KernelCpuProcStringReader USER_SYS_TIME_READER = new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME); public static KernelCpuProcStringReader getFreqTimeReaderInstance() { static KernelCpuProcStringReader getFreqTimeReaderInstance() { return FREQ_TIME_READER; } public static KernelCpuProcStringReader getActiveTimeReaderInstance() { static KernelCpuProcStringReader getActiveTimeReaderInstance() { return ACTIVE_TIME_READER; } public static KernelCpuProcStringReader getClusterTimeReaderInstance() { static KernelCpuProcStringReader getClusterTimeReaderInstance() { return CLUSTER_TIME_READER; } static KernelCpuProcStringReader getUserSysTimeReaderInstance() { return USER_SYS_TIME_READER; } private int mErrors = 0; private final Path mFile; private char[] mBuf; Loading Loading @@ -164,12 +172,12 @@ public class KernelCpuProcStringReader { // ReentrantReadWriteLock allows lock downgrading. mReadLock.lock(); return new ProcFileIterator(total); } catch (FileNotFoundException e) { } catch (FileNotFoundException | NoSuchFileException e) { mErrors++; Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile); } catch (IOException e) { mErrors++; Slog.e(TAG, "Error reading: " + mFile, e); Slog.e(TAG, "Error reading " + mFile, e); } finally { StrictMode.setThreadPolicyMask(oldMask); mWriteLock.unlock(); Loading @@ -193,6 +201,11 @@ public class KernelCpuProcStringReader { mSize = size; } /** @return Whether there are more lines in the iterator. */ public boolean hasNextLine() { return mPos < mSize; } /** * Fetches the next line. Note that all subsequent return values share the same char[] * under the hood. Loading @@ -214,25 +227,48 @@ public class KernelCpuProcStringReader { return CharBuffer.wrap(mBuf, start, i - start); } /** Total size of the proc file in chars. */ public int size() { return mSize; } /** Must call close at the end to release the read lock! Or use try-with-resources. */ public void close() { mReadLock.unlock(); } } /** * Fetches the next line, converts all numbers into long, and puts into the given long[]. * To avoid GC, caller should try to use the same array for all calls. All non-numeric * chars are treated as delimiters. All numbers are non-negative. * Converts all numbers in the CharBuffer into longs, and puts into the given long[]. * * Space and colon are treated as delimiters. All other chars are not allowed. All numbers * are non-negative. To avoid GC, caller should try to use the same array for all calls. * * This method also resets the given buffer to the original position before return so that * it can be read again. * * @param buf The char buffer to be converted. * @param array An array to store the parsed numbers. * @return The number of elements written to the given array. -1 if there is no more line. * @return The number of elements written to the given array. -1 if buf is null, -2 if buf * contains invalid char, -3 if any number overflows. */ public int nextLineAsArray(long[] array) { CharBuffer buf = nextLine(); public static int asLongs(CharBuffer buf, long[] array) { if (buf == null) { return -1; } final int initialPos = buf.position(); int count = 0; long num = -1; char c; while (buf.remaining() > 0 && count < array.length) { c = buf.get(); if (!(isNumber(c) || c == ' ' || c == ':')) { buf.position(initialPos); return -2; } if (num < 0) { if (isNumber(c)) { num = c - '0'; Loading @@ -240,6 +276,10 @@ public class KernelCpuProcStringReader { } else { if (isNumber(c)) { num = num * 10 + c - '0'; if (num < 0) { buf.position(initialPos); return -3; } } else { array[count++] = num; num = -1; Loading @@ -249,21 +289,11 @@ public class KernelCpuProcStringReader { if (num >= 0) { array[count++] = num; } buf.position(initialPos); return count; } /** Total size of the proc file in chars. */ public int size() { return mSize; } /** Must call close at the end to release the read lock! Or use try-with-resources. */ public void close() { mReadLock.unlock(); } private boolean isNumber(char c) { private static boolean isNumber(char c) { return c >= '0' && c <= '9'; } } }
core/java/com/android/internal/os/KernelCpuUidTimeReader.java 0 → 100644 +776 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +4 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,10 @@ import org.junit.runners.Suite; BatteryStatsUserLifecycleTests.class, KernelCpuProcReaderTest.class, KernelCpuProcStringReaderTest.class, KernelCpuUidActiveTimeReaderTest.class, KernelCpuUidClusterTimeReaderTest.class, KernelCpuUidFreqTimeReaderTest.class, KernelCpuUidUserSysTimeReaderTest.class, KernelMemoryBandwidthStatsTest.class, KernelSingleUidTimeReaderTest.class, KernelUidCpuFreqTimeReaderTest.class, Loading
core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java +24 −3 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import java.io.BufferedWriter; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.CharBuffer; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; Loading Loading @@ -149,7 +150,7 @@ public class KernelCpuProcStringReaderTest { + "0 0 1 1 1 0 2 0 221", iter.nextLine().toString()); long[] actual = new long[43]; iter.nextLineAsArray(actual); KernelCpuProcStringReader.asLongs(iter.nextLine(), actual); assertArrayEquals( new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721}, Loading Loading @@ -183,7 +184,7 @@ public class KernelCpuProcStringReaderTest { } } /** Tests nextLineToArray functionality. */ /** Tests reading lines, then converting to long[]. */ @Test public void testReadLineToArray() throws Exception { final long[][] data = getTestArray(800, 50); Loading @@ -193,12 +194,32 @@ public class KernelCpuProcStringReaderTest { long[] actual = new long[50]; try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { for (long[] expected : data) { assertEquals(50, iter.nextLineAsArray(actual)); CharBuffer cb = iter.nextLine(); String before = cb.toString(); assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual)); assertArrayEquals(expected, actual); assertEquals("Buffer not reset to the pos before reading", before, cb.toString()); } } } /** Tests error handling of converting to long[]. */ @Test public void testLineToArrayErrorHandling() { long[] actual = new long[100]; String invalidChar = "123: -1234 456"; String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123"; CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length()); assertEquals("Failed to report err for: " + invalidChar, -2, KernelCpuProcStringReader.asLongs(cb, actual)); assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString()); cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length()); assertEquals("Failed to report err for: " + overflow, -3, KernelCpuProcStringReader.asLongs(cb, actual)); assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString()); } /** * Tests that reading a file over the limit (1MB) will return null. */ Loading
core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java 0 → 100644 +262 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.FileUtils; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.SparseLongArray; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Random; /** * Test class for {@link KernelCpuUidActiveTimeReader}. * * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class KernelCpuUidActiveTimeReaderTest { private File mTestDir; private File mTestFile; private KernelCpuUidActiveTimeReader mReader; private VerifiableCallback mCallback; private Random mRand = new Random(12345); private final int mCpus = 4; private final String mHeadline = "cpus: 4\n"; private final int[] mUids = {0, 1, 22, 333, 4444, 55555}; private Context getContext() { return InstrumentationRegistry.getContext(); } @Before public void setUp() { mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); mTestFile = new File(mTestDir, "test.file"); mReader = new KernelCpuUidActiveTimeReader( new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false); mCallback = new VerifiableCallback(); } @After public void tearDown() throws Exception { FileUtils.deleteContents(mTestDir); FileUtils.deleteContents(getContext().getFilesDir()); } @Test public void testReadDelta() throws Exception { final long[][] times = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times)); mReader.readDelta(mCallback); for (int i = 0; i < mUids.length; ++i) { mCallback.verify(mUids[i], getActiveTime(times[i])); } mCallback.verifyNoMoreInteractions(); // Verify that a second call will only return deltas. mCallback.clear(); final long[][] newTimes1 = increaseTime(times); writeToFile(mHeadline + uidLines(mUids, newTimes1)); mReader.readDelta(mCallback); for (int i = 0; i < mUids.length; ++i) { mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i])); } mCallback.verifyNoMoreInteractions(); // Verify that there won't be a callback if the proc file values didn't change. mCallback.clear(); mReader.readDelta(mCallback); mCallback.verifyNoMoreInteractions(); // Verify that calling with a null callback doesn't result in any crashes mCallback.clear(); final long[][] newTimes2 = increaseTime(newTimes1); writeToFile(mHeadline + uidLines(mUids, newTimes2)); mReader.readDelta(null); mCallback.verifyNoMoreInteractions(); // Verify that the readDelta call will only return deltas when // the previous call had null callback. mCallback.clear(); final long[][] newTimes3 = increaseTime(newTimes2); writeToFile(mHeadline + uidLines(mUids, newTimes3)); mReader.readDelta(mCallback); for (int i = 0; i < mUids.length; ++i) { mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); } @Test public void testReadAbsolute() throws Exception { final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times1)); mReader.readAbsolute(mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times1[i])); } mCallback.verifyNoMoreInteractions(); // Verify that a second call should still return absolute values mCallback.clear(); final long[][] times2 = increaseTime(times1); writeToFile(mHeadline + uidLines(mUids, times2)); mReader.readAbsolute(mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times2[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); } @Test public void testReadDeltaDecreasedTime() throws Exception { final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times1)); mReader.readDelta(mCallback); // Verify that there should not be a callback for a particular UID if its time decreases. mCallback.clear(); final long[][] times2 = increaseTime(times1); System.arraycopy(times1[0], 0, times2[0], 0, mCpus); times2[0][0] = 100; writeToFile(mHeadline + uidLines(mUids, times2)); mReader.readDelta(mCallback); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); // Verify that the internal state was not modified. mCallback.clear(); final long[][] times3 = increaseTime(times2); times3[0] = increaseTime(times1)[0]; writeToFile(mHeadline + uidLines(mUids, times3)); mReader.readDelta(mCallback); mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0])); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i])); } mCallback.verifyNoMoreInteractions(); } @Test public void testReadDeltaNegativeTime() throws Exception { final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); writeToFile(mHeadline + uidLines(mUids, times1)); mReader.readDelta(mCallback); // Verify that there should not be a callback for a particular UID if its time is -ve. mCallback.clear(); final long[][] times2 = increaseTime(times1); times2[0][0] *= -1; writeToFile(mHeadline + uidLines(mUids, times2)); mReader.readDelta(mCallback); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i])); } mCallback.verifyNoMoreInteractions(); assertTrue(mTestFile.delete()); // Verify that the internal state was not modified. mCallback.clear(); final long[][] times3 = increaseTime(times2); times3[0] = increaseTime(times1)[0]; writeToFile(mHeadline + uidLines(mUids, times3)); mReader.readDelta(mCallback); mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0])); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i])); } mCallback.verifyNoMoreInteractions(); } private String uidLines(int[] uids, long[][] times) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < uids.length; i++) { sb.append(uids[i]).append(':'); for (int j = 0; j < times[i].length; j++) { sb.append(' ').append(times[i][j] / 10); } sb.append('\n'); } return sb.toString(); } private void writeToFile(String s) throws IOException { try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { w.write(s); w.flush(); } } private long[][] increaseTime(long[][] original) { long[][] newTime = new long[original.length][original[0].length]; for (int i = 0; i < original.length; i++) { for (int j = 0; j < original[0].length; j++) { newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000; } } return newTime; } private long getActiveTime(long[] times) { return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4; } private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> { SparseLongArray mData = new SparseLongArray(); public void verify(int uid, long time) { assertEquals(time, mData.get(uid)); mData.delete(uid); } public void clear() { mData.clear(); } @Override public void onUidCpuTime(int uid, Long time) { mData.put(uid, time); } public void verifyNoMoreInteractions() { assertEquals(0, mData.size()); } } }