blob: c233ea8e78b78d22ba8d149074c90d3dd6cce04c [file] [log] [blame]
Mike Ma2ab01442018-02-13 14:22:47 -08001/*
2 * Copyright (C) 2018 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
17package com.android.internal.os;
18
19import android.os.StrictMode;
20import android.os.SystemClock;
21import android.util.Slog;
22
23import com.android.internal.annotations.VisibleForTesting;
24
Mike Ma363ccf72018-03-01 14:50:02 -080025import java.io.FileNotFoundException;
Mike Ma2ab01442018-02-13 14:22:47 -080026import java.io.IOException;
Mike Ma53712732018-09-18 10:32:34 -070027import java.io.InputStream;
Mike Ma2ab01442018-02-13 14:22:47 -080028import java.nio.ByteBuffer;
29import java.nio.ByteOrder;
Mike Ma53712732018-09-18 10:32:34 -070030import java.nio.file.Files;
Mike Ma363ccf72018-03-01 14:50:02 -080031import java.nio.file.NoSuchFileException;
Mike Ma2ab01442018-02-13 14:22:47 -080032import java.nio.file.Path;
33import java.nio.file.Paths;
Mike Ma53712732018-09-18 10:32:34 -070034import java.util.Arrays;
Mike Ma2ab01442018-02-13 14:22:47 -080035
36/**
37 * Reads cpu time proc files with throttling (adjustable interval).
38 *
39 * KernelCpuProcReader is implemented as singletons for built-in kernel proc files. Get___Instance()
40 * method will return corresponding reader instance. In order to prevent frequent GC,
41 * KernelCpuProcReader reuses a {@link ByteBuffer} to store data read from proc files.
42 *
43 * A KernelCpuProcReader instance keeps an error counter. When the number of read errors within that
44 * instance accumulates to 5, this instance will reject all further read requests.
45 *
46 * Each KernelCpuProcReader instance also has a throttler. Throttle interval can be adjusted via
47 * {@link #setThrottleInterval(long)} method. Default throttle interval is 3000ms. If current
48 * timestamp based on {@link SystemClock#elapsedRealtime()} is less than throttle interval from
49 * the last read timestamp, {@link #readBytes()} will return previous result.
50 *
51 * A KernelCpuProcReader instance is thread-unsafe. Caller needs to hold a lock on this object while
52 * accessing its instance methods or digesting the return values.
53 */
54public class KernelCpuProcReader {
55 private static final String TAG = "KernelCpuProcReader";
56 private static final int ERROR_THRESHOLD = 5;
57 // Throttle interval in milliseconds
58 private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
Mike Ma2ab01442018-02-13 14:22:47 -080059 private static final int MAX_BUFFER_SIZE = 1024 * 1024;
60 private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
61 private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
62 private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_cpupower/concurrent_policy_time";
63
64 private static final KernelCpuProcReader mFreqTimeReader = new KernelCpuProcReader(
65 PROC_UID_FREQ_TIME);
66 private static final KernelCpuProcReader mActiveTimeReader = new KernelCpuProcReader(
67 PROC_UID_ACTIVE_TIME);
68 private static final KernelCpuProcReader mClusterTimeReader = new KernelCpuProcReader(
69 PROC_UID_CLUSTER_TIME);
70
71 public static KernelCpuProcReader getFreqTimeReaderInstance() {
72 return mFreqTimeReader;
73 }
74
75 public static KernelCpuProcReader getActiveTimeReaderInstance() {
76 return mActiveTimeReader;
77 }
78
79 public static KernelCpuProcReader getClusterTimeReaderInstance() {
80 return mClusterTimeReader;
81 }
82
83 private int mErrors;
84 private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
85 private long mLastReadTime = Long.MIN_VALUE;
86 private final Path mProc;
Mike Ma53712732018-09-18 10:32:34 -070087 private byte[] mBuffer = new byte[8 * 1024];
88 private int mContentSize;
Mike Ma2ab01442018-02-13 14:22:47 -080089
90 @VisibleForTesting
91 public KernelCpuProcReader(String procFile) {
92 mProc = Paths.get(procFile);
Mike Ma2ab01442018-02-13 14:22:47 -080093 }
94
95 /**
96 * Reads all bytes from the corresponding proc file.
97 *
98 * If elapsed time since last call to this method is less than the throttle interval, it will
99 * return previous result. When IOException accumulates to 5, it will always return null. This
100 * method is thread-unsafe, so is the return value. Caller needs to hold a lock on this
101 * object while calling this method and digesting its return value.
102 *
103 * @return a {@link ByteBuffer} containing all bytes from the proc file.
104 */
105 public ByteBuffer readBytes() {
106 if (mErrors >= ERROR_THRESHOLD) {
107 return null;
108 }
109 if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
Mike Ma53712732018-09-18 10:32:34 -0700110 if (mContentSize > 0) {
111 return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
112 .order(ByteOrder.nativeOrder());
Mike Ma2ab01442018-02-13 14:22:47 -0800113 }
114 return null;
115 }
116 mLastReadTime = SystemClock.elapsedRealtime();
Mike Ma53712732018-09-18 10:32:34 -0700117 mContentSize = 0;
Mike Ma2ab01442018-02-13 14:22:47 -0800118 final int oldMask = StrictMode.allowThreadDiskReadsMask();
Mike Ma53712732018-09-18 10:32:34 -0700119 try (InputStream in = Files.newInputStream(mProc)) {
120 int numBytes = 0;
121 int curr;
122 while ((curr = in.read(mBuffer, numBytes, mBuffer.length - numBytes)) >= 0) {
123 numBytes += curr;
124 if (numBytes == mBuffer.length) {
125 // Hit the limit. Resize mBuffer.
126 if (mBuffer.length == MAX_BUFFER_SIZE) {
127 mErrors++;
128 Slog.e(TAG, "Proc file is too large: " + mProc);
129 return null;
130 }
131 mBuffer = Arrays.copyOf(mBuffer,
132 Math.min(mBuffer.length << 1, MAX_BUFFER_SIZE));
Mike Ma2ab01442018-02-13 14:22:47 -0800133 }
Mike Ma2ab01442018-02-13 14:22:47 -0800134 }
Mike Ma53712732018-09-18 10:32:34 -0700135 mContentSize = numBytes;
136 return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
137 .order(ByteOrder.nativeOrder());
Mike Ma363ccf72018-03-01 14:50:02 -0800138 } catch (NoSuchFileException | FileNotFoundException e) {
139 // Happens when the kernel does not provide this file. Not a big issue. Just log it.
140 mErrors++;
141 Slog.w(TAG, "File not exist: " + mProc);
Mike Ma2ab01442018-02-13 14:22:47 -0800142 } catch (IOException e) {
143 mErrors++;
144 Slog.e(TAG, "Error reading: " + mProc, e);
Mike Ma2ab01442018-02-13 14:22:47 -0800145 } finally {
146 StrictMode.setThreadPolicyMask(oldMask);
147 }
Mike Ma53712732018-09-18 10:32:34 -0700148 return null;
Mike Ma2ab01442018-02-13 14:22:47 -0800149 }
150
151 /**
152 * Sets the throttle interval. Set to 0 will disable throttling. Thread-unsafe, holding a lock
153 * on this object is recommended.
154 *
155 * @param throttleInterval throttle interval in milliseconds
156 */
157 public void setThrottleInterval(long throttleInterval) {
158 if (throttleInterval >= 0) {
159 mThrottleInterval = throttleInterval;
160 }
161 }
Mike Ma2ab01442018-02-13 14:22:47 -0800162}