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