1/*
2 * Copyright (C) 2015 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 */
16package com.android.internal.os;
17
18import android.system.Os;
19import android.text.TextUtils;
20import android.os.StrictMode;
21import android.system.OsConstants;
22import android.util.Slog;
23
24import java.io.BufferedReader;
25import java.io.FileReader;
26import java.io.IOException;
27import java.util.Arrays;
28
29/**
30 * Reads CPU time of a specific core spent at various frequencies and provides a delta from the
31 * last call to {@link #readDelta}. Each line in the proc file has the format:
32 *
33 * freq time
34 *
35 * where time is measured in jiffies.
36 */
37public class KernelCpuSpeedReader {
38    private static final String TAG = "KernelCpuSpeedReader";
39
40    private final String mProcFile;
41    private final int mNumSpeedSteps;
42    private final long[] mLastSpeedTimesMs;
43    private final long[] mDeltaSpeedTimesMs;
44
45    // How long a CPU jiffy is in milliseconds.
46    private final long mJiffyMillis;
47
48    /**
49     * @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read.
50     */
51    public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
52        mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
53                cpuNumber);
54        mNumSpeedSteps = numSpeedSteps;
55        mLastSpeedTimesMs = new long[numSpeedSteps];
56        mDeltaSpeedTimesMs = new long[numSpeedSteps];
57        long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
58        mJiffyMillis = 1000/jiffyHz;
59    }
60
61    /**
62     * The returned array is modified in subsequent calls to {@link #readDelta}.
63     * @return The time (in milliseconds) spent at different cpu speeds since the last call to
64     * {@link #readDelta}.
65     */
66    public long[] readDelta() {
67        StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
68        try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
69            TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
70            String line;
71            int speedIndex = 0;
72            while (speedIndex < mLastSpeedTimesMs.length && (line = reader.readLine()) != null) {
73                splitter.setString(line);
74                splitter.next();
75
76                long time = Long.parseLong(splitter.next()) * mJiffyMillis;
77                if (time < mLastSpeedTimesMs[speedIndex]) {
78                    // The stats reset when the cpu hotplugged. That means that the time
79                    // we read is offset from 0, so the time is the delta.
80                    mDeltaSpeedTimesMs[speedIndex] = time;
81                } else {
82                    mDeltaSpeedTimesMs[speedIndex] = time - mLastSpeedTimesMs[speedIndex];
83                }
84                mLastSpeedTimesMs[speedIndex] = time;
85                speedIndex++;
86            }
87        } catch (IOException e) {
88            Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
89            Arrays.fill(mDeltaSpeedTimesMs, 0);
90        } finally {
91            StrictMode.setThreadPolicy(policy);
92        }
93        return mDeltaSpeedTimesMs;
94    }
95
96    /**
97     * @return The time (in milliseconds) spent at different cpu speeds. The values should be
98     * monotonically increasing, unless the cpu was hotplugged.
99     */
100    public long[] readAbsolute() {
101        StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
102        long[] speedTimeMs = new long[mNumSpeedSteps];
103        try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
104            TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
105            String line;
106            int speedIndex = 0;
107            while (speedIndex < mNumSpeedSteps && (line = reader.readLine()) != null) {
108                splitter.setString(line);
109                splitter.next();
110                long time = Long.parseLong(splitter.next()) * mJiffyMillis;
111                speedTimeMs[speedIndex] = time;
112                speedIndex++;
113            }
114        } catch (IOException e) {
115            Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
116            Arrays.fill(speedTimeMs, 0);
117        } finally {
118            StrictMode.setThreadPolicy(policy);
119        }
120        return speedTimeMs;
121    }
122}
123