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  */
16 package com.android.internal.os;
17 
18 import android.system.Os;
19 import android.text.TextUtils;
20 import android.os.StrictMode;
21 import android.system.OsConstants;
22 import android.util.Slog;
23 
24 import java.io.BufferedReader;
25 import java.io.FileReader;
26 import java.io.IOException;
27 import 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  */
37 public 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      */
KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps)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      */
readDelta()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      */
readAbsolute()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