blob: 2519412f32467cbfe7bc33501a5814375e0b2d93 [file] [log] [blame]
Mike Ma3d422c32017-10-25 11:08:57 -07001/*
2 * Copyright (C) 2017 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.annotation.Nullable;
Mike Ma3d422c32017-10-25 11:08:57 -070020import android.os.SystemClock;
21import android.util.Slog;
22import android.util.SparseArray;
Mike Ma3d422c32017-10-25 11:08:57 -070023
24import com.android.internal.annotations.VisibleForTesting;
25
Mike Ma2ab01442018-02-13 14:22:47 -080026import java.nio.ByteBuffer;
27import java.nio.IntBuffer;
Mike Ma3d422c32017-10-25 11:08:57 -070028
29/**
Mike Ma2ab01442018-02-13 14:22:47 -080030 * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
31 * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}.
32 *
33 * concurrent_active_time is an array of u32's in the following format:
34 * [n, uid0, time0a, time0b, ..., time0n,
35 * uid1, time1a, time1b, ..., time1n,
36 * uid2, time2a, time2b, ..., time2n, etc.]
37 * where n is the total number of cpus (num_possible_cpus)
Mike Ma3d422c32017-10-25 11:08:57 -070038 * ...
Mike Ma2ab01442018-02-13 14:22:47 -080039 * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
Mike Ma3d422c32017-10-25 11:08:57 -070040 * The file contains a monotonically increasing count of time for a single boot. This class
41 * maintains the previous results of a call to {@link #readDelta} in order to provide a
42 * proper delta.
Mike Ma2ab01442018-02-13 14:22:47 -080043 *
44 * This class uses a throttler to reject any {@link #readDelta} call within
45 * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
46 * which has a shorter throttle interval and returns cached result from last read when the request
47 * is throttled.
48 *
49 * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to
50 * the nature of {@link #readDelta(Callback)}).
Mike Ma3d422c32017-10-25 11:08:57 -070051 */
52public class KernelUidCpuActiveTimeReader {
Mike Ma3d422c32017-10-25 11:08:57 -070053 private static final String TAG = "KernelUidCpuActiveTimeReader";
Mike Ma2ab01442018-02-13 14:22:47 -080054 // Throttle interval in milliseconds
55 private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L;
Mike Ma3d422c32017-10-25 11:08:57 -070056
Mike Ma2ab01442018-02-13 14:22:47 -080057 private final KernelCpuProcReader mProcReader;
58 private long mLastTimeReadMs = Long.MIN_VALUE;
59 private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
60 private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
Mike Ma3d422c32017-10-25 11:08:57 -070061
62 public interface Callback {
Mike Ma2ab01442018-02-13 14:22:47 -080063 /**
64 * Notifies when new data is available.
65 *
66 * @param uid uid int
67 * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds
68 */
Mike Ma3d422c32017-10-25 11:08:57 -070069 void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
70 }
71
Mike Ma2ab01442018-02-13 14:22:47 -080072 public KernelUidCpuActiveTimeReader() {
73 mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance();
74 }
75
76 @VisibleForTesting
77 public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) {
78 mProcReader = procReader;
79 }
80
Mike Ma3d422c32017-10-25 11:08:57 -070081 public void readDelta(@Nullable Callback cb) {
Mike Ma2ab01442018-02-13 14:22:47 -080082 if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
83 Slog.w(TAG, "Throttle");
84 return;
85 }
86 synchronized (mProcReader) {
87 final ByteBuffer bytes = mProcReader.readBytes();
88 if (bytes == null || bytes.remaining() <= 4) {
89 // Error already logged in mProcReader.
90 return;
91 }
92 if ((bytes.remaining() & 3) != 0) {
93 Slog.wtf(TAG,
94 "Cannot parse active time proc bytes to int: " + bytes.remaining());
95 return;
96 }
97 final IntBuffer buf = bytes.asIntBuffer();
98 final int cores = buf.get();
99 if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
100 Slog.wtf(TAG,
101 "Cpu active time format error: " + buf.remaining() + " / " + (cores
102 + 1));
103 return;
104 }
105 int numUids = buf.remaining() / (cores + 1);
106 for (int i = 0; i < numUids; i++) {
107 int uid = buf.get();
108 boolean corrupted = false;
109 double curTime = 0;
110 for (int j = 1; j <= cores; j++) {
111 int time = buf.get();
112 if (time < 0) {
113 Slog.e(TAG, "Corrupted data from active time proc: " + time);
114 corrupted = true;
115 } else {
116 curTime += (double) time * 10 / j; // Unit is 10ms.
117 }
118 }
119 double delta = curTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
120 if (delta > 0 && !corrupted) {
121 mLastUidCpuActiveTimeMs.put(uid, curTime);
122 if (cb != null) {
123 cb.onUidCpuActiveTime(uid, (long) delta);
124 }
125 }
126 }
127 // Slog.i(TAG, "Read uids: " + numUids);
128 }
129 mLastTimeReadMs = SystemClock.elapsedRealtime();
130 }
131
132 public void setThrottleInterval(long throttleInterval) {
133 if (throttleInterval >= 0) {
134 mThrottleInterval = throttleInterval;
Mike Ma3d422c32017-10-25 11:08:57 -0700135 }
136 }
137
138 public void removeUid(int uid) {
139 mLastUidCpuActiveTimeMs.delete(uid);
140 }
141
142 public void removeUidsInRange(int startUid, int endUid) {
143 if (endUid < startUid) {
144 Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
145 return;
146 }
147 mLastUidCpuActiveTimeMs.put(startUid, null);
148 mLastUidCpuActiveTimeMs.put(endUid, null);
149 final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
150 final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
151 mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
152 }
Mike Ma3d422c32017-10-25 11:08:57 -0700153}