KernelUidCpuFreqTimeReader.java revision 6b1396e8feef1f923a01daa56aa58ea3fffdcb82
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 android.annotation.Nullable; 20import android.os.SystemClock; 21import android.util.Slog; 22import android.util.SparseArray; 23import android.util.TimeUtils; 24 25import com.android.internal.annotations.VisibleForTesting; 26 27import java.io.BufferedReader; 28import java.io.FileReader; 29import java.io.IOException; 30 31/** 32 * Reads /proc/uid_time_in_state which has the format: 33 * 34 * uid: [freq1] [freq2] [freq3] ... 35 * [uid1]: [time in freq1] [time in freq2] [time in freq3] ... 36 * [uid2]: [time in freq1] [time in freq2] [time in freq3] ... 37 * ... 38 * 39 * This provides the times a UID's processes spent executing at each different cpu frequency. 40 * The file contains a monotonically increasing count of time for a single boot. This class 41 * maintains the previous results of a call to {@link #readDelta} in order to provide a proper 42 * delta. 43 */ 44public class KernelUidCpuFreqTimeReader { 45 private static final boolean DEBUG = false; 46 private static final String TAG = "KernelUidCpuFreqTimeReader"; 47 private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; 48 49 public interface Callback { 50 void onCpuFreqs(long[] cpuFreqs); 51 void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs); 52 } 53 54 private long[] mCpuFreqs; 55 private int mCpuFreqsCount; 56 private long mLastTimeReadMs; 57 private long mNowTimeMs; 58 59 private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>(); 60 61 // We check the existence of proc file a few times (just in case it is not ready yet when we 62 // start reading) and if it is not available, we simply ignore further read requests. 63 private static final int TOTAL_READ_ERROR_COUNT = 5; 64 private int mReadErrorCounter; 65 private boolean mProcFileAvailable; 66 67 public void readDelta(@Nullable Callback callback) { 68 if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { 69 return; 70 } 71 try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { 72 mNowTimeMs = SystemClock.elapsedRealtime(); 73 readDelta(reader, callback); 74 mLastTimeReadMs = mNowTimeMs; 75 mProcFileAvailable = true; 76 } catch (IOException e) { 77 mReadErrorCounter++; 78 Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); 79 } 80 } 81 82 public void removeUid(int uid) { 83 mLastUidCpuFreqTimeMs.delete(uid); 84 } 85 86 public void removeUidsInRange(int startUid, int endUid) { 87 if (endUid < startUid) { 88 return; 89 } 90 mLastUidCpuFreqTimeMs.put(startUid, null); 91 mLastUidCpuFreqTimeMs.put(endUid, null); 92 final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid); 93 final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid); 94 mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); 95 } 96 97 @VisibleForTesting 98 public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException { 99 String line = reader.readLine(); 100 if (line == null) { 101 return; 102 } 103 readCpuFreqs(line, callback); 104 while ((line = reader.readLine()) != null) { 105 final int index = line.indexOf(' '); 106 final int uid = Integer.parseInt(line.substring(0, index - 1), 10); 107 readTimesForUid(uid, line.substring(index + 1, line.length()), callback); 108 } 109 } 110 111 private void readTimesForUid(int uid, String line, Callback callback) { 112 long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid); 113 if (uidTimeMs == null) { 114 uidTimeMs = new long[mCpuFreqsCount]; 115 mLastUidCpuFreqTimeMs.put(uid, uidTimeMs); 116 } 117 final String[] timesStr = line.split(" "); 118 final int size = timesStr.length; 119 if (size != uidTimeMs.length) { 120 Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size 121 + " cpuFreqsCount: " + uidTimeMs.length); 122 return; 123 } 124 final long[] deltaUidTimeMs = new long[size]; 125 final long[] curUidTimeMs = new long[size]; 126 boolean notify = false; 127 for (int i = 0; i < size; ++i) { 128 // Times read will be in units of 10ms 129 final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10; 130 deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i]; 131 // If there is malformed data for any uid, then we just log about it and ignore 132 // the data for that uid. 133 if (deltaUidTimeMs[i] < 0 || totalTimeMs < 0) { 134 if (DEBUG) { 135 final StringBuilder sb = new StringBuilder("Malformed cpu freq data for UID=") 136 .append(uid).append("\n"); 137 sb.append("data=").append("(").append(uidTimeMs[i]).append(",") 138 .append(totalTimeMs).append(")").append("\n"); 139 sb.append("times=").append("("); 140 TimeUtils.formatDuration(mLastTimeReadMs, sb); 141 sb.append(","); 142 TimeUtils.formatDuration(mNowTimeMs, sb); 143 sb.append(")"); 144 Slog.e(TAG, sb.toString()); 145 } 146 return; 147 } 148 curUidTimeMs[i] = totalTimeMs; 149 notify = notify || (deltaUidTimeMs[i] > 0); 150 } 151 if (notify) { 152 System.arraycopy(curUidTimeMs, 0, uidTimeMs, 0, size); 153 if (callback != null) { 154 callback.onUidCpuFreqTime(uid, deltaUidTimeMs); 155 } 156 } 157 } 158 159 private void readCpuFreqs(String line, Callback callback) { 160 if (mCpuFreqs == null) { 161 final String[] freqStr = line.split(" "); 162 // First item would be "uid:" which needs to be ignored 163 mCpuFreqsCount = freqStr.length - 1; 164 mCpuFreqs = new long[mCpuFreqsCount]; 165 for (int i = 0; i < mCpuFreqsCount; ++i) { 166 mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10); 167 } 168 } 169 if (callback != null) { 170 callback.onCpuFreqs(mCpuFreqs); 171 } 172 } 173} 174