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.SystemClock;
20 import android.text.TextUtils;
21 import android.util.Slog;
22 import android.util.SparseLongArray;
23 import android.util.TimeUtils;
24 
25 import java.io.BufferedReader;
26 import java.io.FileReader;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 
30 /**
31  * Reads /proc/uid_cputime/show_uid_stat which has the line format:
32  *
33  * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
34  *
35  * This provides the time a UID's processes spent executing in user-space and kernel-space.
36  * The file contains a monotonically increasing count of time for a single boot. This class
37  * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
38  * delta.
39  */
40 public class KernelUidCpuTimeReader {
41     private static final String TAG = "KernelUidCpuTimeReader";
42     private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
43     private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
44 
45     /**
46      * Callback interface for processing each line of the proc file.
47      */
48     public interface Callback {
49         /**
50          * @param uid UID of the app
51          * @param userTimeUs time spent executing in user space in microseconds
52          * @param systemTimeUs time spent executing in kernel space in microseconds
53          * @param powerMaUs power consumed executing, in milli-ampere microseconds
54          */
onUidCpuTime(int uid, long userTimeUs, long systemTimeUs, long powerMaUs)55         void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs, long powerMaUs);
56     }
57 
58     private SparseLongArray mLastUserTimeUs = new SparseLongArray();
59     private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
60     private SparseLongArray mLastPowerMaUs = 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      * @param callback The callback to invoke for each line of the proc file. If null,
66      *                 the data is consumed and subsequent calls to readDelta will provide
67      *                 a fresh delta.
68      */
readDelta(@ullable Callback callback)69     public void readDelta(@Nullable Callback callback) {
70         long nowUs = SystemClock.elapsedRealtime() * 1000;
71         try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
72             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
73             String line;
74             while ((line = reader.readLine()) != null) {
75                 splitter.setString(line);
76                 final String uidStr = splitter.next();
77                 final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
78                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
79                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
80                 final long powerMaUs;
81                 if (splitter.hasNext()) {
82                     powerMaUs = Long.parseLong(splitter.next(), 10) / 1000;
83                 } else {
84                     powerMaUs = 0;
85                 }
86 
87                 if (callback != null) {
88                     long userTimeDeltaUs = userTimeUs;
89                     long systemTimeDeltaUs = systemTimeUs;
90                     long powerDeltaMaUs = powerMaUs;
91                     int index = mLastUserTimeUs.indexOfKey(uid);
92                     if (index >= 0) {
93                         userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
94                         systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
95                         powerDeltaMaUs -= mLastPowerMaUs.valueAt(index);
96 
97                         final long timeDiffUs = nowUs - mLastTimeReadUs;
98                         if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 || powerDeltaMaUs < 0) {
99                             StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
100                             sb.append(uid).append("!\n");
101                             sb.append("Time between reads: ");
102                             TimeUtils.formatDuration(timeDiffUs / 1000, sb);
103                             sb.append("\n");
104                             sb.append("Previous times: u=");
105                             TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
106                             sb.append(" s=");
107                             TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
108                             sb.append(" p=").append(mLastPowerMaUs.valueAt(index) / 1000);
109                             sb.append("mAms\n");
110 
111                             sb.append("Current times: u=");
112                             TimeUtils.formatDuration(userTimeUs / 1000, sb);
113                             sb.append(" s=");
114                             TimeUtils.formatDuration(systemTimeUs / 1000, sb);
115                             sb.append(" p=").append(powerMaUs / 1000);
116                             sb.append("mAms\n");
117                             sb.append("Delta: u=");
118                             TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
119                             sb.append(" s=");
120                             TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
121                             sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
122                             Slog.wtf(TAG, sb.toString());
123 
124                             userTimeDeltaUs = 0;
125                             systemTimeDeltaUs = 0;
126                             powerDeltaMaUs = 0;
127                         }
128                     }
129 
130                     if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0 || powerDeltaMaUs != 0) {
131                         callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs,
132                                 powerDeltaMaUs);
133                     }
134                 }
135                 mLastUserTimeUs.put(uid, userTimeUs);
136                 mLastSystemTimeUs.put(uid, systemTimeUs);
137                 mLastPowerMaUs.put(uid, powerMaUs);
138             }
139         } catch (IOException e) {
140             Slog.e(TAG, "Failed to read uid_cputime", e);
141         }
142         mLastTimeReadUs = nowUs;
143     }
144 
145     /**
146      * Removes the UID from the kernel module and from internal accounting data.
147      * @param uid The UID to remove.
148      */
removeUid(int uid)149     public void removeUid(int uid) {
150         int index = mLastUserTimeUs.indexOfKey(uid);
151         if (index >= 0) {
152             mLastUserTimeUs.removeAt(index);
153             mLastSystemTimeUs.removeAt(index);
154             mLastPowerMaUs.removeAt(index);
155         }
156 
157         try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
158             writer.write(Integer.toString(uid) + "-" + Integer.toString(uid));
159             writer.flush();
160         } catch (IOException e) {
161             Slog.e(TAG, "failed to remove uid from uid_cputime module", e);
162         }
163     }
164 }
165