KernelUidCpuTimeReader.java revision 7b3c7529c73581f360cdcd0243e8c68e1cdedb43
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 */
16package com.android.internal.os;
17
18import android.annotation.Nullable;
19import android.os.SystemClock;
20import android.text.TextUtils;
21import android.util.Slog;
22import android.util.SparseLongArray;
23import android.util.TimeUtils;
24
25import java.io.BufferedReader;
26import java.io.FileReader;
27import java.io.FileWriter;
28import 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
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 */
40public 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        void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
50    }
51
52    private SparseLongArray mLastUserTimeUs = new SparseLongArray();
53    private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
54    private long mLastTimeReadUs = 0;
55
56    /**
57     * Reads the proc file, calling into the callback with a delta of time for each UID.
58     * @param callback The callback to invoke for each line of the proc file. If null,
59     *                 the data is consumed and subsequent calls to readDelta will provide
60     *                 a fresh delta.
61     */
62    public void readDelta(@Nullable Callback callback) {
63        long nowUs = SystemClock.elapsedRealtime() * 1000;
64        try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
65            TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
66            String line;
67            while ((line = reader.readLine()) != null) {
68                splitter.setString(line);
69                final String uidStr = splitter.next();
70                final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
71                final long userTimeUs = Long.parseLong(splitter.next(), 10);
72                final long systemTimeUs = Long.parseLong(splitter.next(), 10);
73
74                if (callback != null) {
75                    long userTimeDeltaUs = userTimeUs;
76                    long systemTimeDeltaUs = systemTimeUs;
77                    int index = mLastUserTimeUs.indexOfKey(uid);
78                    if (index >= 0) {
79                        userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
80                        systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
81
82                        final long timeDiffUs = nowUs - mLastTimeReadUs;
83                        if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 ||
84                                userTimeDeltaUs > timeDiffUs || systemTimeDeltaUs > timeDiffUs) {
85                            StringBuilder sb = new StringBuilder("Malformed cpu data!\n");
86                            sb.append("Time between reads: ");
87                            TimeUtils.formatDuration(timeDiffUs / 1000, sb);
88                            sb.append("ms\n");
89                            sb.append("Previous times: u=");
90                            TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
91                            sb.append("ms s=");
92                            TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
93                            sb.append("ms\n");
94                            sb.append("Current times: u=");
95                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
96                            sb.append("ms s=");
97                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
98                            sb.append("ms\n");
99                            sb.append("Delta for UID=").append(uid).append(": u=");
100                            TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
101                            sb.append("ms s=");
102                            TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
103                            sb.append("ms");
104                            Slog.wtf(TAG, sb.toString());
105
106                            userTimeDeltaUs = 0;
107                            systemTimeDeltaUs = 0;
108                        }
109                    }
110
111                    if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
112                        callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
113                    }
114                }
115                mLastUserTimeUs.put(uid, userTimeUs);
116                mLastSystemTimeUs.put(uid, systemTimeUs);
117            }
118        } catch (IOException e) {
119            Slog.e(TAG, "Failed to read uid_cputime", e);
120        }
121        mLastTimeReadUs = nowUs;
122    }
123
124    /**
125     * Removes the UID from the kernel module and from internal accounting data.
126     * @param uid The UID to remove.
127     */
128    public void removeUid(int uid) {
129        int index = mLastUserTimeUs.indexOfKey(uid);
130        if (index >= 0) {
131            mLastUserTimeUs.removeAt(index);
132            mLastSystemTimeUs.removeAt(index);
133        }
134
135        try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
136            writer.write(Integer.toString(uid) + "-" + Integer.toString(uid));
137            writer.flush();
138        } catch (IOException e) {
139            Slog.e(TAG, "failed to remove uid from uid_cputime module", e);
140        }
141    }
142}
143