KernelUidCpuFreqTimeReader.java revision 8cde930d71cc73a5712e48f144506a628bcbd366
1e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar/* 2e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Copyright (C) 2017 The Android Open Source Project 3e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 4e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * you may not use this file except in compliance with the License. 6e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * You may obtain a copy of the License at 7e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 8e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 10e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * See the License for the specific language governing permissions and 14e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * limitations under the License. 15e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 16e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 17e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyarpackage com.android.internal.os; 181e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas 191e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport android.annotation.Nullable; 201e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport android.os.SystemClock; 211e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport android.util.Slog; 221e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport android.util.SparseArray; 231e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport android.util.TimeUtils; 241e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas 251e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport com.android.internal.annotations.VisibleForTesting; 261e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas 271e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikasimport java.io.BufferedReader; 28e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyarimport java.io.FileReader; 29e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyarimport java.io.IOException; 30e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 31e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar/** 32e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Reads /proc/uid_time_in_state which has the format: 33e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 34e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * uid: [freq1] [freq2] [freq3] ... 35c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette * [uid1]: [time in freq1] [time in freq2] [time in freq3] ... 36e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * [uid2]: [time in freq1] [time in freq2] [time in freq3] ... 37e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * ... 38e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 39e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * This provides the times a UID's processes spent executing at each different cpu frequency. 40e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * The file contains a monotonically increasing count of time for a single boot. This class 41e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * maintains the previous results of a call to {@link #readDelta} in order to provide a proper 42e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * delta. 43e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 44e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyarpublic class KernelUidCpuFreqTimeReader { 45e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private static final String TAG = "KernelUidCpuFreqTimeReader"; 46e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; 47e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 48e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar public interface Callback { 49e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar void onCpuFreqs(long[] cpuFreqs); 50e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs); 51e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 52e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 53e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private long[] mCpuFreqs; 54e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private int mCpuFreqsCount; 55e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private long mLastTimeReadMs; 56e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private long mNowTimeMs; 57e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 58e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>(); 59e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 60e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // We check the existence of proc file a few times (just in case it is not ready yet when we 61e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // start reading) and if it is not available, we simply ignore further read requests. 62e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private static final int TOTAL_READ_ERROR_COUNT = 5; 63e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private int mReadErrorCounter; 64e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private boolean mProcFileAvailable; 65e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 66e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar public void readDelta(@Nullable Callback callback) { 67e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { 68e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return; 69e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 70e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { 71e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mNowTimeMs = SystemClock.elapsedRealtime(); 72e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar readDelta(reader, callback); 7313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mLastTimeReadMs = mNowTimeMs; 7413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mProcFileAvailable = true; 7513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } catch (IOException e) { 7613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mReadErrorCounter++; 7713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); 78e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 79e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 8013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar 81e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar public void removeUid(int uid) { 82e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLastUidCpuFreqTimeMs.delete(uid); 83e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 84e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 85e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar public void removeUidsInRange(int startUid, int endUid) { 8613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar if (endUid < startUid) { 8713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar return; 8813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 8913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mLastUidCpuFreqTimeMs.put(startUid, null); 9013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mLastUidCpuFreqTimeMs.put(endUid, null); 9113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid); 9213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid); 9313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); 9413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 9513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar 9613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar @VisibleForTesting 9713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException { 9813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar String line = reader.readLine(); 9913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar if (line == null) { 10013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar return; 101e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 102e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar readCpuFreqs(line, callback); 103e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar while ((line = reader.readLine()) != null) { 104e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final int index = line.indexOf(' '); 105e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final int uid = Integer.parseInt(line.substring(0, index - 1), 10); 10613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar readTimesForUid(uid, line.substring(index + 1, line.length()), callback); 10713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 10813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 10913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar 11013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar private void readTimesForUid(int uid, String line, Callback callback) { 11113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid); 11213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar if (uidTimeMs == null) { 11313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar uidTimeMs = new long[mCpuFreqsCount]; 11413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar mLastUidCpuFreqTimeMs.put(uid, uidTimeMs); 11513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 11613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar final String[] timesStr = line.split(" "); 11713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar final int size = timesStr.length; 118e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (size != uidTimeMs.length) { 119e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size 120e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar + " cpuFreqsCount: " + uidTimeMs.length); 121e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return; 122e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 123e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final long[] deltaUidTimeMs = new long[size]; 124e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final long[] curUidTimeMs = new long[size]; 125e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar boolean notify = false; 126e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar for (int i = 0; i < size; ++i) { 127e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Times read will be in units of 10ms 128e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10; 129e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i]; 130e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // If there is malformed data for any uid, then we just log about it and ignore 131e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // the data for that uid. 132e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (deltaUidTimeMs[i] < 0 || totalTimeMs < 0) { 133e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final StringBuilder sb = new StringBuilder("Malformed cpu freq data for UID=") 134e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar .append(uid).append("\n"); 135e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar sb.append("data=").append("(").append(uidTimeMs[i]).append(",") 136e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar .append(totalTimeMs).append(")").append("\n"); 137e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar sb.append("times=").append("("); 138e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar TimeUtils.formatDuration(mLastTimeReadMs, sb); sb.append(","); 139e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar TimeUtils.formatDuration(mNowTimeMs, sb); sb.append(")"); 140e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar Slog.e(TAG, sb.toString()); 141e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return; 142e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 143e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar curUidTimeMs[i] = totalTimeMs; 144e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar notify = notify || (deltaUidTimeMs[i] > 0); 145e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 146e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (notify) { 147e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar System.arraycopy(curUidTimeMs, 0, uidTimeMs, 0, size); 148e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (callback != null) { 149e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.onUidCpuFreqTime(uid, deltaUidTimeMs); 150e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 151e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 152e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 153e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 154e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private void readCpuFreqs(String line, Callback callback) { 155e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (mCpuFreqs == null) { 156e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final String[] freqStr = line.split(" "); 157e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // First item would be "uid:" which needs to be ignored 158e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mCpuFreqsCount = freqStr.length - 1; 159e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mCpuFreqs = new long[mCpuFreqsCount]; 160e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar for (int i = 0; i < mCpuFreqsCount; ++i) { 161e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10); 162e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 163e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 164e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (callback != null) { 165e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.onCpuFreqs(mCpuFreqs); 166e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 167e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 168e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar} 169e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar