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.annotation.Nullable;
19 import android.os.StrictMode;
20 import android.os.SystemClock;
21 import android.text.TextUtils;
22 import android.util.Slog;
23 import android.util.SparseLongArray;
24 import android.util.TimeUtils;
25 
26 import java.io.BufferedReader;
27 import java.io.FileReader;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 
31 /**
32  * Reads /proc/uid_cputime/show_uid_stat which has the line format:
33  *
34  * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
35  *
36  * This provides the time a UID's processes spent executing in user-space and kernel-space.
37  * The file contains a monotonically increasing count of time for a single boot. This class
38  * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
39  * delta.
40  */
41 public class KernelUidCpuTimeReader extends
42         KernelUidCpuTimeReaderBase<KernelUidCpuTimeReader.Callback> {
43     private static final String TAG = KernelUidCpuTimeReader.class.getSimpleName();
44     private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
45     private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
46 
47     /**
48      * Callback interface for processing each line of the proc file.
49      */
50     public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
51         /**
52          * @param uid          UID of the app
53          * @param userTimeUs   time spent executing in user space in microseconds
54          * @param systemTimeUs time spent executing in kernel space in microseconds
55          */
onUidCpuTime(int uid, long userTimeUs, long systemTimeUs)56         void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
57     }
58 
59     private SparseLongArray mLastUserTimeUs = new SparseLongArray();
60     private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
61     private long mLastTimeReadUs = 0;
62 
63     /**
64      * Reads the proc file, calling into the callback with a delta of time for each UID.
65      *
66      * @param callback The callback to invoke for each line of the proc file. If null,
67      *                 the data is consumed and subsequent calls to readDelta will provide
68      *                 a fresh delta.
69      */
70     @Override
readDeltaImpl(@ullable Callback callback)71     protected void readDeltaImpl(@Nullable Callback callback) {
72         final int oldMask = StrictMode.allowThreadDiskReadsMask();
73         long nowUs = SystemClock.elapsedRealtime() * 1000;
74         try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
75             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
76             String line;
77             while ((line = reader.readLine()) != null) {
78                 splitter.setString(line);
79                 final String uidStr = splitter.next();
80                 final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
81                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
82                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
83 
84                 boolean notifyCallback = false;
85                 long userTimeDeltaUs = userTimeUs;
86                 long systemTimeDeltaUs = systemTimeUs;
87                 // Only report if there is a callback and if this is not the first read.
88                 if (callback != null && mLastTimeReadUs != 0) {
89                     int index = mLastUserTimeUs.indexOfKey(uid);
90                     if (index >= 0) {
91                         userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
92                         systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
93 
94                         final long timeDiffUs = nowUs - mLastTimeReadUs;
95                         if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) {
96                             StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
97                             sb.append(uid).append("!\n");
98                             sb.append("Time between reads: ");
99                             TimeUtils.formatDuration(timeDiffUs / 1000, sb);
100                             sb.append("\n");
101                             sb.append("Previous times: u=");
102                             TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
103                             sb.append(" s=");
104                             TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
105 
106                             sb.append("\nCurrent times: u=");
107                             TimeUtils.formatDuration(userTimeUs / 1000, sb);
108                             sb.append(" s=");
109                             TimeUtils.formatDuration(systemTimeUs / 1000, sb);
110                             sb.append("\nDelta: u=");
111                             TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
112                             sb.append(" s=");
113                             TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
114                             Slog.e(TAG, sb.toString());
115 
116                             userTimeDeltaUs = 0;
117                             systemTimeDeltaUs = 0;
118                         }
119                     }
120 
121                     notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
122                 }
123                 mLastUserTimeUs.put(uid, userTimeUs);
124                 mLastSystemTimeUs.put(uid, systemTimeUs);
125                 if (notifyCallback) {
126                     callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
127                 }
128             }
129         } catch (IOException e) {
130             Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
131         } finally {
132             StrictMode.setThreadPolicyMask(oldMask);
133         }
134         mLastTimeReadUs = nowUs;
135     }
136 
137     /**
138      * Reads the proc file, calling into the callback with raw absolute value of time for each UID.
139      * @param callback The callback to invoke for each line of the proc file.
140      */
readAbsolute(Callback callback)141     public void readAbsolute(Callback callback) {
142         final int oldMask = StrictMode.allowThreadDiskReadsMask();
143         try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
144             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
145             String line;
146             while ((line = reader.readLine()) != null) {
147                 splitter.setString(line);
148                 final String uidStr = splitter.next();
149                 final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
150                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
151                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
152                 callback.onUidCpuTime(uid, userTimeUs, systemTimeUs);
153             }
154         } catch (IOException e) {
155             Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
156         } finally {
157             StrictMode.setThreadPolicyMask(oldMask);
158         }
159     }
160 
161     /**
162      * Removes the UID from the kernel module and from internal accounting data. Only
163      * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
164      * visible system wide.
165      *
166      * @param uid The UID to remove.
167      */
removeUid(int uid)168     public void removeUid(int uid) {
169         final int index = mLastSystemTimeUs.indexOfKey(uid);
170         if (index >= 0) {
171             mLastSystemTimeUs.removeAt(index);
172             mLastUserTimeUs.removeAt(index);
173         }
174         removeUidsFromKernelModule(uid, uid);
175     }
176 
177     /**
178      * Removes UIDs in a given range from the kernel module and internal accounting data. Only
179      * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
180      * visible system wide.
181      *
182      * @param startUid the first uid to remove
183      * @param endUid   the last uid to remove
184      */
removeUidsInRange(int startUid, int endUid)185     public void removeUidsInRange(int startUid, int endUid) {
186         if (endUid < startUid) {
187             return;
188         }
189         mLastSystemTimeUs.put(startUid, 0);
190         mLastUserTimeUs.put(startUid, 0);
191         mLastSystemTimeUs.put(endUid, 0);
192         mLastUserTimeUs.put(endUid, 0);
193         final int startIndex = mLastSystemTimeUs.indexOfKey(startUid);
194         final int endIndex = mLastSystemTimeUs.indexOfKey(endUid);
195         mLastSystemTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
196         mLastUserTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
197         removeUidsFromKernelModule(startUid, endUid);
198     }
199 
removeUidsFromKernelModule(int startUid, int endUid)200     private void removeUidsFromKernelModule(int startUid, int endUid) {
201         Slog.d(TAG, "Removing uids " + startUid + "-" + endUid);
202         final int oldMask = StrictMode.allowThreadDiskWritesMask();
203         try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
204             writer.write(startUid + "-" + endUid);
205             writer.flush();
206         } catch (IOException e) {
207             Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid
208                     + " from uid_cputime module", e);
209         } finally {
210             StrictMode.setThreadPolicyMask(oldMask);
211         }
212     }
213 }
214