• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.Nullable;
20 import android.util.Slog;
21 import android.util.SparseArray;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 
25 import java.io.BufferedReader;
26 import java.io.FileReader;
27 import 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  */
42 public 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 {
onCpuFreqs(long[] cpuFreqs)47         void onCpuFreqs(long[] cpuFreqs);
onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs)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 
readDelta(@ullable Callback callback)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 
removeUid(int uid)75     public void removeUid(int uid) {
76         mLastUidCpuFreqTimeMs.delete(uid);
77     }
78 
79     @VisibleForTesting
readDelta(BufferedReader reader, @Nullable Callback callback)80     public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
81         String line = reader.readLine();
82         if (line == null) {
83             return;
84         }
85         readCpuFreqs(line, callback);
86         while ((line = reader.readLine()) != null) {
87             final int index = line.indexOf(' ');
88             final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
89             readTimesForUid(uid, line.substring(index + 1, line.length()), callback);
90         }
91     }
92 
readTimesForUid(int uid, String line, Callback callback)93     private void readTimesForUid(int uid, String line, Callback callback) {
94         long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid);
95         if (uidTimeMs == null) {
96             uidTimeMs = new long[mCpuFreqsCount];
97             mLastUidCpuFreqTimeMs.put(uid, uidTimeMs);
98         }
99         final String[] timesStr = line.split(" ");
100         final int size = timesStr.length;
101         if (size != uidTimeMs.length) {
102             Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size
103                     + " cpuFreqsCount: " + uidTimeMs.length);
104             return;
105         }
106         final long[] deltaUidTimeMs = new long[size];
107         for (int i = 0; i < size; ++i) {
108             // Times read will be in units of 10ms
109             final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10;
110             deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i];
111             uidTimeMs[i] = totalTimeMs;
112         }
113         if (callback != null) {
114             callback.onUidCpuFreqTime(uid, deltaUidTimeMs);
115         }
116     }
117 
readCpuFreqs(String line, Callback callback)118     private void readCpuFreqs(String line, Callback callback) {
119         if (mCpuFreqs == null) {
120             final String[] freqStr = line.split(" ");
121             // First item would be "uid:" which needs to be ignored
122             mCpuFreqsCount = freqStr.length - 1;
123             mCpuFreqs = new long[mCpuFreqsCount];
124             for (int i = 0; i < mCpuFreqsCount; ++i) {
125                 mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10);
126             }
127         }
128         if (callback != null) {
129             callback.onCpuFreqs(mCpuFreqs);
130         }
131     }
132 }
133