19b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka/*
29b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * Copyright (C) 2017 The Android Open Source Project
39b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka *
49b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * Licensed under the Apache License, Version 2.0 (the "License");
59b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * you may not use this file except in compliance with the License.
69b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * You may obtain a copy of the License at
79b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka *
89b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka *      http://www.apache.org/licenses/LICENSE-2.0
99b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka *
109b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * Unless required by applicable law or agreed to in writing, software
119b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * distributed under the License is distributed on an "AS IS" BASIS,
129b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * See the License for the specific language governing permissions and
149b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * limitations under the License.
159b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka */
169b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
179b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankapackage com.android.internal.os;
189b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
199b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport android.annotation.Nullable;
209b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport android.util.Slog;
219b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport android.util.SparseArray;
229b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
239b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport com.android.internal.annotations.VisibleForTesting;
249b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
259b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport java.io.BufferedReader;
269b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport java.io.FileReader;
279b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankaimport java.io.IOException;
289b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
299b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka/**
309b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * Reads /proc/uid_time_in_state which has the format:
319b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka *
329b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * uid: [freq1] [freq2] [freq3] ...
339b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
349b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
359b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * ...
369b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka *
379b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * This provides the times a UID's processes spent executing at each different cpu frequency.
389b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * The file contains a monotonically increasing count of time for a single boot. This class
399b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
409b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka * delta.
419b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka */
429b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shankapublic class KernelUidCpuFreqTimeReader {
439b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private static final String TAG = "KernelUidCpuFreqTimeReader";
449b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
459b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
469b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    public interface Callback {
479b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        void onCpuFreqs(long[] cpuFreqs);
489b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs);
499b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    }
509b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
519b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private long[] mCpuFreqs;
529b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private int mCpuFreqsCount;
539b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
549b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
559b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
56acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka    // We check the existence of proc file a few times (just in case it is not ready yet when we
57acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka    // start reading) and if it is not available, we simply ignore further read requests.
58acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka    private static final int TOTAL_READ_ERROR_COUNT = 5;
59acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka    private int mReadErrorCounter;
60acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka    private boolean mProcFileAvailable;
61acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka
629b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    public void readDelta(@Nullable Callback callback) {
63acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka        if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
64acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka            return;
65acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka        }
669b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
679b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            readDelta(reader, callback);
68acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka            mProcFileAvailable = true;
699b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        } catch (IOException e) {
70acd7f2c78320051ddf85448b067e35a71ca3b400Sudheer Shanka            mReadErrorCounter++;
719b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
729b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
739b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    }
749b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
756d8dcec87200e75ca62715c8feb87794d113e957Sudheer Shanka    public void removeUid(int uid) {
766d8dcec87200e75ca62715c8feb87794d113e957Sudheer Shanka        mLastUidCpuFreqTimeMs.delete(uid);
776d8dcec87200e75ca62715c8feb87794d113e957Sudheer Shanka    }
786d8dcec87200e75ca62715c8feb87794d113e957Sudheer Shanka
799b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    @VisibleForTesting
809b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
819b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        String line = reader.readLine();
829b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        if (line == null) {
839b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            return;
849b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
859b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        readCpuFreqs(line, callback);
869b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        while ((line = reader.readLine()) != null) {
879b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            final int index = line.indexOf(' ');
889b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
899b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            readTimesForUid(uid, line.substring(index + 1, line.length()), callback);
909b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
919b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    }
929b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
939b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private void readTimesForUid(int uid, String line, Callback callback) {
949b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid);
959b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        if (uidTimeMs == null) {
969b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            uidTimeMs = new long[mCpuFreqsCount];
979b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            mLastUidCpuFreqTimeMs.put(uid, uidTimeMs);
989b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
999b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        final String[] timesStr = line.split(" ");
1009b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        final int size = timesStr.length;
1019b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        if (size != uidTimeMs.length) {
1029b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size
1039b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka                    + " cpuFreqsCount: " + uidTimeMs.length);
1049b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            return;
1059b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
1069b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        final long[] deltaUidTimeMs = new long[size];
1079b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        for (int i = 0; i < size; ++i) {
1089b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            // Times read will be in units of 10ms
1099b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10;
1109b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i];
1119b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            uidTimeMs[i] = totalTimeMs;
1129b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
1139b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        if (callback != null) {
1149b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            callback.onUidCpuFreqTime(uid, deltaUidTimeMs);
1159b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
1169b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    }
1179b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka
1189b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    private void readCpuFreqs(String line, Callback callback) {
1199b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        if (mCpuFreqs == null) {
1209b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            final String[] freqStr = line.split(" ");
1219b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            // First item would be "uid:" which needs to be ignored
1229b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            mCpuFreqsCount = freqStr.length - 1;
1239b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            mCpuFreqs = new long[mCpuFreqsCount];
1249b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            for (int i = 0; i < mCpuFreqsCount; ++i) {
1259b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka                mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10);
1269b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            }
1279b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
1289b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        if (callback != null) {
1299b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka            callback.onCpuFreqs(mCpuFreqs);
1309b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka        }
1319b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka    }
1329b735c5c1a4575d9f0ea9f3229ad8bf9401caee0Sudheer Shanka}
133