summaryrefslogtreecommitdiff
path: root/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
blob: 3ed59f1d437085f2a92b639a9ac7bad4277ae0fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.os;

import android.annotation.Nullable;
import android.os.Process;

import com.android.internal.annotations.VisibleForTesting;

import java.io.IOException;

/**
 * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
 * by various threads of the System Server.
 */
public class SystemServerCpuThreadReader {
    private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;

    private long[] mLastThreadCpuTimesUs;
    private long[] mLastBinderThreadCpuTimesUs;

    /**
     * Times (in microseconds) spent by the system server UID.
     */
    public static class SystemServiceCpuThreadTimes {
        // All threads
        public long[] threadCpuTimesUs;
        // Just the threads handling incoming binder calls
        public long[] binderThreadCpuTimesUs;
    }

    private final SystemServiceCpuThreadTimes mDeltaCpuThreadTimes =
            new SystemServiceCpuThreadTimes();

    /**
     * Creates a configured instance of SystemServerCpuThreadReader.
     */
    public static SystemServerCpuThreadReader create() {
        return new SystemServerCpuThreadReader(
                KernelSingleProcessCpuThreadReader.create(Process.myPid()));
    }

    @VisibleForTesting
    public SystemServerCpuThreadReader(int pid,
            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader)
            throws IOException {
        this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader));
    }

    @VisibleForTesting
    public SystemServerCpuThreadReader(KernelSingleProcessCpuThreadReader kernelCpuThreadReader) {
        mKernelCpuThreadReader = kernelCpuThreadReader;
    }

    /**
     * Start tracking CPU time-in-state for the process specified in the constructor.
     */
    public void startTrackingThreadCpuTime() {
        mKernelCpuThreadReader.startTrackingThreadCpuTimes();
    }

    public void setBinderThreadNativeTids(int[] nativeTids) {
        mKernelCpuThreadReader.setSelectedThreadIds(nativeTids);
    }

    /**
     * Returns delta of CPU times, per thread, since the previous call to this method.
     */
    @Nullable
    public SystemServiceCpuThreadTimes readDelta() {
        final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
        if (mLastThreadCpuTimesUs == null) {
            mLastThreadCpuTimesUs = new long[numCpuFrequencies];
            mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];

            mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
            mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
        }

        final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
                mKernelCpuThreadReader.getProcessCpuUsage();
        if (processCpuUsage == null) {
            return null;
        }

        for (int i = numCpuFrequencies - 1; i >= 0; i--) {
            long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000;
            long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000;
            mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
                    Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]);
            mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
                    Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]);
            mLastThreadCpuTimesUs[i] = threadCpuTimesUs;
            mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs;
        }

        return mDeltaCpuThreadTimes;
    }

    /** Returns CPU times, per thread group, since tracking started. */
    @Nullable
    public SystemServiceCpuThreadTimes readAbsolute() {
        final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
        final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
                mKernelCpuThreadReader.getProcessCpuUsage();
        if (processCpuUsage == null) {
            return null;
        }
        final SystemServiceCpuThreadTimes result = new SystemServiceCpuThreadTimes();
        result.threadCpuTimesUs = new long[numCpuFrequencies];
        result.binderThreadCpuTimesUs = new long[numCpuFrequencies];
        for (int i = 0; i < numCpuFrequencies; ++i) {
            result.threadCpuTimesUs[i] = processCpuUsage.threadCpuTimesMillis[i] * 1_000;
            result.binderThreadCpuTimesUs[i] =
                    processCpuUsage.selectedThreadCpuTimesMillis[i] * 1_000;
        }
        return result;
    }
}