106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski/*
206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * Copyright (C) 2015 The Android Open Source Project
306af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski *
406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * you may not use this file except in compliance with the License.
606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * You may obtain a copy of the License at
706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski *
806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
906af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski *
1006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * Unless required by applicable law or agreed to in writing, software
1106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
1206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1306af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * See the License for the specific language governing permissions and
1406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * limitations under the License.
1506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski */
1606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskipackage com.android.internal.os;
1706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
1806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport android.annotation.Nullable;
1972478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinskiimport android.os.SystemClock;
2006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport android.text.TextUtils;
2106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport android.util.Slog;
2206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport android.util.SparseLongArray;
2372478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinskiimport android.util.TimeUtils;
2406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
2506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport java.io.BufferedReader;
2606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport java.io.FileReader;
27b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinskiimport java.io.FileWriter;
2806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskiimport java.io.IOException;
2906af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
3006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski/**
3106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * Reads /proc/uid_cputime/show_uid_stat which has the line format:
3206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski *
33a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
3406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski *
3506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * This provides the time a UID's processes spent executing in user-space and kernel-space.
3606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * The file contains a monotonically increasing count of time for a single boot. This class
3706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
3806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski * delta.
3906af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski */
4006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinskipublic class KernelUidCpuTimeReader {
4106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    private static final String TAG = "KernelUidCpuTimeReader";
4206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
43b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski    private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
4406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
4506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    /**
4606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     * Callback interface for processing each line of the proc file.
4706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     */
4806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    public interface Callback {
49a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski        /**
50a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski         * @param uid UID of the app
51a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski         * @param userTimeUs time spent executing in user space in microseconds
52a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski         * @param systemTimeUs time spent executing in kernel space in microseconds
53a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski         */
54d4abd1eeb01187fa2ef78b64ae4f493a7bb3563fAdam Lesinski        void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
5506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    }
5606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
5706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    private SparseLongArray mLastUserTimeUs = new SparseLongArray();
5806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
597b3c7529c73581f360cdcd0243e8c68e1cdedb43Adam Lesinski    private long mLastTimeReadUs = 0;
6006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
6106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    /**
6206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     * Reads the proc file, calling into the callback with a delta of time for each UID.
6306af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     * @param callback The callback to invoke for each line of the proc file. If null,
6406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     *                 the data is consumed and subsequent calls to readDelta will provide
6506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     *                 a fresh delta.
6606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski     */
6706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    public void readDelta(@Nullable Callback callback) {
687b3c7529c73581f360cdcd0243e8c68e1cdedb43Adam Lesinski        long nowUs = SystemClock.elapsedRealtime() * 1000;
6906af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski        try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
7006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski            TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
7106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski            String line;
7206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski            while ((line = reader.readLine()) != null) {
7306af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                splitter.setString(line);
7406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                final String uidStr = splitter.next();
7506af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
7606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                final long userTimeUs = Long.parseLong(splitter.next(), 10);
7706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                final long systemTimeUs = Long.parseLong(splitter.next(), 10);
7806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski
79e7f4f0297fe77fd76f944b9a3134534962833262Adam Lesinski                // Only report if there is a callback and if this is not the first read.
80e7f4f0297fe77fd76f944b9a3134534962833262Adam Lesinski                if (callback != null && mLastTimeReadUs != 0) {
81b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                    long userTimeDeltaUs = userTimeUs;
82b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                    long systemTimeDeltaUs = systemTimeUs;
8306af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                    int index = mLastUserTimeUs.indexOfKey(uid);
84b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                    if (index >= 0) {
85b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                        userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
86b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                        systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
87b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski
887b3c7529c73581f360cdcd0243e8c68e1cdedb43Adam Lesinski                        final long timeDiffUs = nowUs - mLastTimeReadUs;
89d4abd1eeb01187fa2ef78b64ae4f493a7bb3563fAdam Lesinski                        if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) {
90a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski                            StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
91a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski                            sb.append(uid).append("!\n");
9272478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            sb.append("Time between reads: ");
937b3c7529c73581f360cdcd0243e8c68e1cdedb43Adam Lesinski                            TimeUtils.formatDuration(timeDiffUs / 1000, sb);
94a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski                            sb.append("\n");
9572478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            sb.append("Previous times: u=");
9672478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
97a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski                            sb.append(" s=");
9872478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
99a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski
100d4abd1eeb01187fa2ef78b64ae4f493a7bb3563fAdam Lesinski                            sb.append("\nCurrent times: u=");
10172478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
102a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski                            sb.append(" s=");
10372478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
104d4abd1eeb01187fa2ef78b64ae4f493a7bb3563fAdam Lesinski                            sb.append("\nDelta: u=");
10572478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
106a7a4cccf7d90324a049fbfd271d25cad51633922Adam Lesinski                            sb.append(" s=");
10772478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
108196473955f9dfeb29b30757ad49df3ed68d6f65eAdam Lesinski                            Slog.e(TAG, sb.toString());
10972478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski
11072478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            userTimeDeltaUs = 0;
11172478f053f403e29223dba6cc7be9e5bf115f670Adam Lesinski                            systemTimeDeltaUs = 0;
112b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                        }
113b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski                    }
114b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski
115d4abd1eeb01187fa2ef78b64ae4f493a7bb3563fAdam Lesinski                    if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
116d4abd1eeb01187fa2ef78b64ae4f493a7bb3563fAdam Lesinski                        callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
11706af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                    }
11806af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                }
11906af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                mLastUserTimeUs.put(uid, userTimeUs);
12006af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski                mLastSystemTimeUs.put(uid, systemTimeUs);
12106af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski            }
12206af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski        } catch (IOException e) {
1238057133d49ab3243de54333012ce5ee8fece5381Adam Lesinski            Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
12406af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski        }
1257b3c7529c73581f360cdcd0243e8c68e1cdedb43Adam Lesinski        mLastTimeReadUs = nowUs;
12606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski    }
127b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski
128b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski    /**
129b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski     * Removes the UID from the kernel module and from internal accounting data.
130b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski     * @param uid The UID to remove.
131b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski     */
132b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski    public void removeUid(int uid) {
133b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski        int index = mLastUserTimeUs.indexOfKey(uid);
134b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski        if (index >= 0) {
135b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski            mLastUserTimeUs.removeAt(index);
136b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski            mLastSystemTimeUs.removeAt(index);
137b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski        }
138b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski
139b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski        try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
140b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski            writer.write(Integer.toString(uid) + "-" + Integer.toString(uid));
141b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski            writer.flush();
142b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski        } catch (IOException e) {
143b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski            Slog.e(TAG, "failed to remove uid from uid_cputime module", e);
144b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski        }
145b83ffee5a40bf00e156152ad85bf8cf6bb96e2f1Adam Lesinski    }
14606af1fac166a83507450b6bbb1f98a8dde68d92eAdam Lesinski}
147