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