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