blob: 45cc8b2260ef5e7c20469d1b4cd19c1d0c6758d5 [file] [log] [blame]
Adam Lesinski06af1fa2015-05-05 17:35:35 -07001/*
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 */
16package com.android.internal.os;
17
18import android.annotation.Nullable;
Adam Lesinski72478f02015-06-17 15:39:43 -070019import android.os.SystemClock;
Adam Lesinski06af1fa2015-05-05 17:35:35 -070020import android.text.TextUtils;
21import android.util.Slog;
22import android.util.SparseLongArray;
Adam Lesinski72478f02015-06-17 15:39:43 -070023import android.util.TimeUtils;
Adam Lesinski06af1fa2015-05-05 17:35:35 -070024
25import java.io.BufferedReader;
26import java.io.FileReader;
Adam Lesinskib83ffee2015-05-12 14:43:47 -070027import java.io.FileWriter;
Adam Lesinski06af1fa2015-05-05 17:35:35 -070028import java.io.IOException;
29
30/**
31 * Reads /proc/uid_cputime/show_uid_stat which has the line format:
32 *
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -070033 * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
Adam Lesinski06af1fa2015-05-05 17:35:35 -070034 *
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 */
40public class KernelUidCpuTimeReader {
41 private static final String TAG = "KernelUidCpuTimeReader";
42 private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
Adam Lesinskib83ffee2015-05-12 14:43:47 -070043 private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
Adam Lesinski06af1fa2015-05-05 17:35:35 -070044
45 /**
46 * Callback interface for processing each line of the proc file.
47 */
48 public interface Callback {
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -070049 /**
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 */
55 void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs, long powerMaUs);
Adam Lesinski06af1fa2015-05-05 17:35:35 -070056 }
57
58 private SparseLongArray mLastUserTimeUs = new SparseLongArray();
59 private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -070060 private SparseLongArray mLastPowerMaUs = new SparseLongArray();
Adam Lesinski7b3c7522015-06-24 11:37:26 -070061 private long mLastTimeReadUs = 0;
Adam Lesinski06af1fa2015-05-05 17:35:35 -070062
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 */
69 public void readDelta(@Nullable Callback callback) {
Adam Lesinski7b3c7522015-06-24 11:37:26 -070070 long nowUs = SystemClock.elapsedRealtime() * 1000;
Adam Lesinski06af1fa2015-05-05 17:35:35 -070071 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);
Adam Lesinski43ae4412015-07-01 12:09:45 -070080 final long powerMaUs;
81 if (splitter.hasNext()) {
82 powerMaUs = Long.parseLong(splitter.next(), 10) / 1000;
83 } else {
84 powerMaUs = 0;
85 }
Adam Lesinski06af1fa2015-05-05 17:35:35 -070086
87 if (callback != null) {
Adam Lesinskib83ffee2015-05-12 14:43:47 -070088 long userTimeDeltaUs = userTimeUs;
89 long systemTimeDeltaUs = systemTimeUs;
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -070090 long powerDeltaMaUs = powerMaUs;
Adam Lesinski06af1fa2015-05-05 17:35:35 -070091 int index = mLastUserTimeUs.indexOfKey(uid);
Adam Lesinskib83ffee2015-05-12 14:43:47 -070092 if (index >= 0) {
93 userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
94 systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -070095 powerDeltaMaUs -= mLastPowerMaUs.valueAt(index);
Adam Lesinskib83ffee2015-05-12 14:43:47 -070096
Adam Lesinski7b3c7522015-06-24 11:37:26 -070097 final long timeDiffUs = nowUs - mLastTimeReadUs;
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -070098 if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 || powerDeltaMaUs < 0 ||
Adam Lesinski7b3c7522015-06-24 11:37:26 -070099 userTimeDeltaUs > timeDiffUs || systemTimeDeltaUs > timeDiffUs) {
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700100 StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
101 sb.append(uid).append("!\n");
Adam Lesinski72478f02015-06-17 15:39:43 -0700102 sb.append("Time between reads: ");
Adam Lesinski7b3c7522015-06-24 11:37:26 -0700103 TimeUtils.formatDuration(timeDiffUs / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700104 sb.append("\n");
Adam Lesinski72478f02015-06-17 15:39:43 -0700105 sb.append("Previous times: u=");
106 TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700107 sb.append(" s=");
Adam Lesinski72478f02015-06-17 15:39:43 -0700108 TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700109 sb.append(" p=").append(mLastPowerMaUs.valueAt(index) / 1000);
110 sb.append("mAms\n");
111
Adam Lesinski72478f02015-06-17 15:39:43 -0700112 sb.append("Current times: u=");
113 TimeUtils.formatDuration(userTimeUs / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700114 sb.append(" s=");
Adam Lesinski72478f02015-06-17 15:39:43 -0700115 TimeUtils.formatDuration(systemTimeUs / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700116 sb.append(" p=").append(powerMaUs / 1000);
117 sb.append("mAms\n");
118 sb.append("Delta: u=");
Adam Lesinski72478f02015-06-17 15:39:43 -0700119 TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700120 sb.append(" s=");
Adam Lesinski72478f02015-06-17 15:39:43 -0700121 TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700122 sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
Adam Lesinski72478f02015-06-17 15:39:43 -0700123 Slog.wtf(TAG, sb.toString());
124
125 userTimeDeltaUs = 0;
126 systemTimeDeltaUs = 0;
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700127 powerDeltaMaUs = 0;
Adam Lesinskib83ffee2015-05-12 14:43:47 -0700128 }
129 }
130
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700131 if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0 || powerDeltaMaUs != 0) {
132 callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs,
133 powerDeltaMaUs);
Adam Lesinski06af1fa2015-05-05 17:35:35 -0700134 }
135 }
136 mLastUserTimeUs.put(uid, userTimeUs);
137 mLastSystemTimeUs.put(uid, systemTimeUs);
Adam Lesinskia7a4ccc2015-06-26 17:43:04 -0700138 mLastPowerMaUs.put(uid, powerMaUs);
Adam Lesinski06af1fa2015-05-05 17:35:35 -0700139 }
140 } catch (IOException e) {
141 Slog.e(TAG, "Failed to read uid_cputime", e);
142 }
Adam Lesinski7b3c7522015-06-24 11:37:26 -0700143 mLastTimeReadUs = nowUs;
Adam Lesinski06af1fa2015-05-05 17:35:35 -0700144 }
Adam Lesinskib83ffee2015-05-12 14:43:47 -0700145
146 /**
147 * Removes the UID from the kernel module and from internal accounting data.
148 * @param uid The UID to remove.
149 */
150 public void removeUid(int uid) {
151 int index = mLastUserTimeUs.indexOfKey(uid);
152 if (index >= 0) {
153 mLastUserTimeUs.removeAt(index);
154 mLastSystemTimeUs.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 }
Adam Lesinski06af1fa2015-05-05 17:35:35 -0700164}