blob: bd8a67a8bd0ef8efc193c86096a6567aeaed78d7 [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.util.Slog;
21import android.util.SparseArray;
Mike Ma3d422c32017-10-25 11:08:57 -070022
23import com.android.internal.annotations.VisibleForTesting;
24
Mike Ma2ab01442018-02-13 14:22:47 -080025import java.nio.ByteBuffer;
26import java.nio.IntBuffer;
Mike Ma234d1822018-03-13 18:53:21 -070027import java.util.function.Consumer;
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 *
Mike Ma69d8b3e2018-02-23 14:51:26 -080049 * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
50 * caller has its own view of delta.
Mike Ma3d422c32017-10-25 11:08:57 -070051 */
Mike Ma69d8b3e2018-02-23 14:51:26 -080052public class KernelUidCpuActiveTimeReader extends
53 KernelUidCpuTimeReaderBase<KernelUidCpuActiveTimeReader.Callback> {
54 private static final String TAG = KernelUidCpuActiveTimeReader.class.getSimpleName();
Mike Ma3d422c32017-10-25 11:08:57 -070055
Mike Ma2ab01442018-02-13 14:22:47 -080056 private final KernelCpuProcReader mProcReader;
Mike Ma2ab01442018-02-13 14:22:47 -080057 private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
Mike Ma234d1822018-03-13 18:53:21 -070058 private int mCores;
Mike Ma3d422c32017-10-25 11:08:57 -070059
Mike Ma69d8b3e2018-02-23 14:51:26 -080060 public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
Mike Ma2ab01442018-02-13 14:22:47 -080061 /**
62 * Notifies when new data is available.
63 *
64 * @param uid uid int
65 * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds
66 */
Mike Ma3d422c32017-10-25 11:08:57 -070067 void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
68 }
69
Mike Ma2ab01442018-02-13 14:22:47 -080070 public KernelUidCpuActiveTimeReader() {
71 mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance();
72 }
73
74 @VisibleForTesting
75 public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) {
76 mProcReader = procReader;
77 }
78
Mike Ma69d8b3e2018-02-23 14:51:26 -080079 @Override
Mike Ma234d1822018-03-13 18:53:21 -070080 protected void readDeltaImpl(@Nullable Callback callback) {
81 readImpl((buf) -> {
82 int uid = buf.get();
83 double activeTime = sumActiveTime(buf);
84 if (activeTime > 0) {
85 double delta = activeTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
86 if (delta > 0) {
87 mLastUidCpuActiveTimeMs.put(uid, activeTime);
88 if (callback != null) {
89 callback.onUidCpuActiveTime(uid, (long) delta);
90 }
91 } else if (delta < 0) {
92 Slog.e(TAG, "Negative delta from active time proc: " + delta);
93 }
94 }
95 });
96 }
97
98 public void readAbsolute(Callback callback) {
99 readImpl((buf) -> {
100 int uid = buf.get();
101 double activeTime = sumActiveTime(buf);
102 if (activeTime > 0) {
103 callback.onUidCpuActiveTime(uid, (long) activeTime);
104 }
105 });
106 }
107
108 private double sumActiveTime(IntBuffer buffer) {
109 double sum = 0;
110 boolean corrupted = false;
111 for (int j = 1; j <= mCores; j++) {
112 int time = buffer.get();
113 if (time < 0) {
114 // Even if error happens, we still need to continue reading.
115 // Buffer cannot be skipped.
116 Slog.e(TAG, "Negative time from active time proc: " + time);
117 corrupted = true;
118 } else {
119 sum += (double) time * 10 / j; // Unit is 10ms.
120 }
121 }
122 return corrupted ? -1 : sum;
123 }
124
125 /**
126 * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
127 * seen results while processing the buffer, while readAbsolute returns the absolute value read
128 * from the buffer without storing. So readImpl contains the common logic of the two, leaving
129 * the difference to a processUid function.
130 *
131 * @param processUid the callback function to process the uid entry in the buffer.
132 */
133 private void readImpl(Consumer<IntBuffer> processUid) {
Mike Ma2ab01442018-02-13 14:22:47 -0800134 synchronized (mProcReader) {
135 final ByteBuffer bytes = mProcReader.readBytes();
136 if (bytes == null || bytes.remaining() <= 4) {
137 // Error already logged in mProcReader.
138 return;
139 }
140 if ((bytes.remaining() & 3) != 0) {
141 Slog.wtf(TAG,
142 "Cannot parse active time proc bytes to int: " + bytes.remaining());
143 return;
144 }
145 final IntBuffer buf = bytes.asIntBuffer();
146 final int cores = buf.get();
Mike Ma234d1822018-03-13 18:53:21 -0700147 if (mCores != 0 && cores != mCores) {
148 Slog.wtf(TAG, "Cpu active time wrong # cores: " + cores);
149 return;
150 }
151 mCores = cores;
Mike Ma2ab01442018-02-13 14:22:47 -0800152 if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
153 Slog.wtf(TAG,
154 "Cpu active time format error: " + buf.remaining() + " / " + (cores
155 + 1));
156 return;
157 }
158 int numUids = buf.remaining() / (cores + 1);
159 for (int i = 0; i < numUids; i++) {
Mike Ma234d1822018-03-13 18:53:21 -0700160 processUid.accept(buf);
Mike Ma2ab01442018-02-13 14:22:47 -0800161 }
Mike Ma69d8b3e2018-02-23 14:51:26 -0800162 if (DEBUG) {
163 Slog.d(TAG, "Read uids: " + numUids);
164 }
Mike Ma3d422c32017-10-25 11:08:57 -0700165 }
166 }
167
168 public void removeUid(int uid) {
169 mLastUidCpuActiveTimeMs.delete(uid);
170 }
171
172 public void removeUidsInRange(int startUid, int endUid) {
Mike Ma3d422c32017-10-25 11:08:57 -0700173 mLastUidCpuActiveTimeMs.put(startUid, null);
174 mLastUidCpuActiveTimeMs.put(endUid, null);
175 final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
176 final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
177 mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
178 }
Mike Ma3d422c32017-10-25 11:08:57 -0700179}