1 /**
2  * Copyright (c) 2020, 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 
17 #pragma once
18 
19 #include <android-base/result.h>
20 #include <utils/Mutex.h>
21 #include <utils/RefBase.h>
22 
23 #include <stdint.h>
24 
25 namespace android {
26 namespace automotive {
27 namespace watchdog {
28 
29 constexpr const char* kProcStatPath = "/proc/stat";
30 
31 struct CpuStats {
32     int64_t userTimeMillis = 0;    // Time spent in user mode.
33     int64_t niceTimeMillis = 0;    // Time spent in user mode with low priority (nice).
34     int64_t sysTimeMillis = 0;     // Time spent in system mode.
35     int64_t idleTimeMillis = 0;    // Time spent in the idle task.
36     int64_t ioWaitTimeMillis = 0;  // Time spent on context switching/waiting due to I/O operations.
37     int64_t irqTimeMillis = 0;     // Time servicing interrupts.
38     int64_t softIrqTimeMillis = 0;    // Time servicing soft interrupts.
39     int64_t stealTimeMillis = 0;      // Stolen time (Time spent in other OS in a virtualized env).
40     int64_t guestTimeMillis = 0;      // Time spent running a virtual CPU for guest OS.
41     int64_t guestNiceTimeMillis = 0;  // Time spent running a niced virtual CPU for guest OS.
42 
43     CpuStats& operator-=(const CpuStats& rhs) {
44         userTimeMillis -= rhs.userTimeMillis;
45         niceTimeMillis -= rhs.niceTimeMillis;
46         sysTimeMillis -= rhs.sysTimeMillis;
47         idleTimeMillis -= rhs.idleTimeMillis;
48         ioWaitTimeMillis -= rhs.ioWaitTimeMillis;
49         irqTimeMillis -= rhs.irqTimeMillis;
50         softIrqTimeMillis -= rhs.softIrqTimeMillis;
51         stealTimeMillis -= rhs.stealTimeMillis;
52         guestTimeMillis -= rhs.guestTimeMillis;
53         guestNiceTimeMillis -= rhs.guestNiceTimeMillis;
54         return *this;
55     }
56 };
57 
58 class ProcStatInfo {
59 public:
ProcStatInfo()60     ProcStatInfo() :
61           cpuStats({}),
62           kernelStartTimeEpochSeconds(0),
63           contextSwitchesCount(0),
64           runnableProcessCount(0),
65           ioBlockedProcessCount(0) {}
ProcStatInfo(CpuStats stats,uint64_t ctxtSwitches,uint32_t runnableCnt,uint32_t ioBlockedCnt,time_t kernelStartTimeEpochSeconds)66     ProcStatInfo(CpuStats stats, uint64_t ctxtSwitches, uint32_t runnableCnt,
67                  uint32_t ioBlockedCnt, time_t kernelStartTimeEpochSeconds) :
68           cpuStats(stats),
69           kernelStartTimeEpochSeconds(kernelStartTimeEpochSeconds),
70           contextSwitchesCount(ctxtSwitches),
71           runnableProcessCount(runnableCnt),
72           ioBlockedProcessCount(ioBlockedCnt) {}
73     CpuStats cpuStats;
74     time_t kernelStartTimeEpochSeconds;
75     uint64_t contextSwitchesCount;
76     uint32_t runnableProcessCount;
77     uint32_t ioBlockedProcessCount;
78 
totalCpuTimeMillis()79     int64_t totalCpuTimeMillis() const {
80         return cpuStats.userTimeMillis + cpuStats.niceTimeMillis + cpuStats.sysTimeMillis +
81                 cpuStats.idleTimeMillis + cpuStats.ioWaitTimeMillis + cpuStats.irqTimeMillis +
82                 cpuStats.softIrqTimeMillis + cpuStats.stealTimeMillis + cpuStats.guestTimeMillis +
83                 cpuStats.guestNiceTimeMillis;
84     }
totalProcessCount()85     uint32_t totalProcessCount() const { return runnableProcessCount + ioBlockedProcessCount; }
86     bool operator==(const ProcStatInfo& info) const {
87         return memcmp(&cpuStats, &info.cpuStats, sizeof(cpuStats)) == 0 &&
88                 runnableProcessCount == info.runnableProcessCount &&
89                 ioBlockedProcessCount == info.ioBlockedProcessCount &&
90                 contextSwitchesCount == info.contextSwitchesCount &&
91                 kernelStartTimeEpochSeconds == info.kernelStartTimeEpochSeconds;
92     }
93     ProcStatInfo& operator-=(const ProcStatInfo& rhs) {
94         cpuStats -= rhs.cpuStats;
95         contextSwitchesCount -= rhs.contextSwitchesCount;
96         /* Don't diff kernelStartTimeEpochSeconds, runnableProcessCount, and
97          * ioBlockedProcessCount because they are real-time values unlike other
98          * stats, which are aggregated values since system startup.
99          */
100         return *this;
101     }
102 };
103 
104 class ProcStatCollectorInterface : public RefBase {
105 public:
106     // Initializes the collector.
107     virtual void init() = 0;
108 
109     // Collects proc stat delta since the last collection.
110     virtual android::base::Result<void> collect() = 0;
111 
112     /* Returns true when the proc stat file is accessible. Otherwise, returns false.
113      * Called by WatchdogPerfService and tests.
114      */
115     virtual bool enabled() = 0;
116 
117     virtual std::string filePath() = 0;
118 
119     // Returns the latest stats.
120     virtual const ProcStatInfo latestStats() const = 0;
121 
122     // Returns the delta of stats from the latest collection.
123     virtual const ProcStatInfo deltaStats() const = 0;
124 
125     // Returns the Kernel start time.
126     virtual time_t getKernelStartTimeEpochSeconds() = 0;
127 };
128 
129 // Collector/parser for `/proc/stat` file.
130 class ProcStatCollector final : public ProcStatCollectorInterface {
131 public:
132     explicit ProcStatCollector(const std::string& path = kProcStatPath) :
kPath(path)133           kPath(path), mMillisPerClockTick(1000 / sysconf(_SC_CLK_TCK)), mLatestStats({}) {}
134 
~ProcStatCollector()135     ~ProcStatCollector() {}
136 
init()137     void init() {
138         Mutex::Autolock lock(mMutex);
139         // Note: Verify proc file access outside the constructor. Otherwise, the unittests of
140         // dependent classes would call the constructor before mocking and get killed due to
141         // sepolicy violation.
142         mEnabled = access(kPath.c_str(), R_OK) == 0;
143     }
144 
145     android::base::Result<void> collect();
146 
enabled()147     bool enabled() {
148         Mutex::Autolock lock(mMutex);
149         return mEnabled;
150     }
151 
filePath()152     std::string filePath() { return kProcStatPath; }
153 
latestStats()154     const ProcStatInfo latestStats() const {
155         Mutex::Autolock lock(mMutex);
156         return mLatestStats;
157     }
158 
deltaStats()159     const ProcStatInfo deltaStats() const {
160         Mutex::Autolock lock(mMutex);
161         return mDeltaStats;
162     }
163 
getKernelStartTimeEpochSeconds()164     time_t getKernelStartTimeEpochSeconds() {
165         return mLatestStats.kernelStartTimeEpochSeconds;
166     }
167 
168 private:
169     // Reads the contents of |kPath|.
170     android::base::Result<ProcStatInfo> getProcStatLocked() const;
171 
172     // Path to proc stat file. Default path is |kProcStatPath|.
173     const std::string kPath;
174 
175     // Number of milliseconds per clock cycle.
176     int32_t mMillisPerClockTick;
177 
178     // Makes sure only one collection is running at any given time.
179     mutable Mutex mMutex;
180 
181     // True if |kPath| is accessible.
182     bool mEnabled GUARDED_BY(mMutex);
183 
184     // Latest dump of CPU stats from the file at |kPath|.
185     ProcStatInfo mLatestStats GUARDED_BY(mMutex);
186 
187     // Delta of CPU stats from the latest collection.
188     ProcStatInfo mDeltaStats GUARDED_BY(mMutex);
189 };
190 
191 }  // namespace watchdog
192 }  // namespace automotive
193 }  // namespace android
194