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