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_UIDPROCSTATSCOLLECTOR_H_
18 #define CPP_WATCHDOG_SERVER_SRC_UIDPROCSTATSCOLLECTOR_H_
19 
20 #include <android-base/result.h>
21 #include <android-base/stringprintf.h>
22 #include <gtest/gtest_prod.h>
23 #include <meminfo/procmeminfo.h>
24 #include <utils/Mutex.h>
25 #include <utils/RefBase.h>
26 
27 #include <android_car_feature.h>
28 #include <inttypes.h>
29 #include <stdint.h>
30 #include <unistd.h>
31 
32 #include <string>
33 #include <unordered_map>
34 #include <vector>
35 
36 namespace android {
37 namespace automotive {
38 namespace watchdog {
39 
40 using ::android::base::StringPrintf;
41 
42 #define PID_FOR_INIT 1
43 
44 constexpr const char kProcDirPath[] = "/proc";
45 constexpr const char kStatFileFormat[] = "/%" PRIu32 "/stat";
46 constexpr const char kTaskDirFormat[] = "/%" PRIu32 "/task";
47 constexpr const char kStatusFileFormat[] = "/%" PRIu32 "/status";
48 constexpr const char kSmapsRollupFileFormat[] = "/%" PRIu32 "/smaps_rollup";
49 constexpr const char kStatmFileFormat[] = "/%" PRIu32 "/statm";
50 constexpr const char kTimeInStateFileFormat[] = "/%" PRIu32 "/time_in_state";
51 
52 /**
53  * Per-pid/tid stats.
54  *
55  * The int64_t type is used due to AIDL limitations representing long field values.
56  */
57 struct PidStat {
58     std::string comm = "";
59     std::string state = "";
60     int64_t startTimeMillis = 0;
61     int64_t cpuTimeMillis = 0;
62     uint64_t majorFaults = 0;
63 };
64 
65 // Per-process stats.
66 struct ProcessStats {
67     std::string comm = "";
68     int64_t startTimeMillis = 0;  // Useful when identifying PID reuse
69     int64_t cpuTimeMillis = 0;
70     // Stats in below fields are aggregated across all threads
71     uint64_t totalCpuCycles = 0;
72     uint64_t totalMajorFaults = 0;
73     int totalTasksCount = 0;
74     int ioBlockedTasksCount = 0;
75     std::unordered_map<pid_t, uint64_t> cpuCyclesByTid = {};
76     uint64_t rssKb = 0;
77     /**
78      * PSS/SwapPss will be missing when the smaps_rollup file is not supported or missing for
79      * a process. In such cases, use RSS to rank the processes by memory usage.
80      */
81     uint64_t pssKb = 0;
82     /**
83      * Unique set size is the portion of memory unique (private) to the process. Unshared memory
84      * is reported as USS.
85      *
86      * PSS - USS = Proportional portion of memory shared with one or more process.
87      * RSS - USS = Total portion of memory shared with one or more process.
88      */
89     uint64_t ussKb = 0;
90     uint64_t swapPssKb = 0;
91     std::string toString() const;
92 };
93 
94 // Per-UID stats.
95 struct UidProcStats {
96     int64_t cpuTimeMillis = 0;
97     uint64_t cpuCycles = 0;
98     uint64_t totalMajorFaults = 0;
99     int totalTasksCount = 0;
100     int ioBlockedTasksCount = 0;
101     /**
102      * When smaps_rollup is supported by the Kernel, totalPssKb will be populated. When this
103      * feature is not supported, use totalRssKb to rank the UIDs.
104      *
105      * totalRssKb counts total shared memory from each of the processes. Thus leading to counting
106      * the same portion of memory more than once:
107      *
108      * For example, if N processes share X amount of memory and a subset of the processes (say M)
109      * belong to same UID, then
110      * 1. totalRssKb across all UIDs += Unique memory for N processes + (N * X).
111      * 2. totalRssKb for the UID += Unique memory for M processes + (M * X).
112      */
113     uint64_t totalRssKb = 0;
114     uint64_t totalPssKb = 0;
115     // TODO(b/333212872): Handles totalUssKb, totalSwapPssKb calculation logic here.
116     std::unordered_map<pid_t, ProcessStats> processStatsByPid = {};
117     std::string toString() const;
118 };
119 
120 /**
121  * Collector/parser for `/proc/[pid]/stat`, `/proc/[pid]/task/[tid]/stat` and /proc/[pid]/status`
122  * files.
123  */
124 class UidProcStatsCollectorInterface : public RefBase {
125 public:
126     // Initializes the collector.
127     virtual void init() = 0;
128     // Collects the per-uid stats from /proc directory.
129     virtual android::base::Result<void> collect() = 0;
130     // Returns the latest per-uid process stats.
131     virtual const std::unordered_map<uid_t, UidProcStats> latestStats() const = 0;
132     // Returns the delta of per-uid process stats since the last before collection.
133     virtual const std::unordered_map<uid_t, UidProcStats> deltaStats() const = 0;
134     // Returns true only when the /proc files for the init process are accessible.
135     virtual bool enabled() const = 0;
136     // Returns the /proc files common ancestor directory path.
137     virtual const std::string dirPath() const = 0;
138 };
139 
140 class UidProcStatsCollector final : public UidProcStatsCollectorInterface {
141 public:
142     // TODO(b/333722043): Once carwatchdogd has sys_ptrace capability, set mIsSmapsRollupSupported
143     // field from `android::meminfo::IsSmapsRollupSupported()`.
144     // Disabling smaps_rollup support because this file cannot be read without sys_ptrace
145     // capability.
UidProcStatsCollector()146     UidProcStatsCollector() :
147           UidProcStatsCollector(kProcDirPath, /*isSmapsRollupSupported=*/false) {}
148     // Used by tests.
149     UidProcStatsCollector(const std::string& path, bool isSmapsRollupSupported);
150 
~UidProcStatsCollector()151     ~UidProcStatsCollector() {}
152 
153     void init() override;
154 
155     android::base::Result<void> collect() override;
156 
latestStats()157     const std::unordered_map<uid_t, UidProcStats> latestStats() const {
158         Mutex::Autolock lock(mMutex);
159         return mLatestStats;
160     }
161 
deltaStats()162     const std::unordered_map<uid_t, UidProcStats> deltaStats() const {
163         Mutex::Autolock lock(mMutex);
164         return mDeltaStats;
165     }
166 
enabled()167     bool enabled() const {
168         Mutex::Autolock lock(mMutex);
169         return mIsEnabled;
170     }
171 
dirPath()172     const std::string dirPath() const { return mPath; }
173 
174     static android::base::Result<PidStat> readStatFileForPid(pid_t pid);
175 
176     static android::base::Result<std::tuple<uid_t, pid_t>> readPidStatusFileForPid(pid_t pid);
177 
178 private:
179     android::base::Result<std::unordered_map<uid_t, UidProcStats>> readUidProcStatsLocked() const;
180 
181     /**
182      * Reads the contents of the below files:
183      * 1. Pid stat file at |mPath| + |kStatFileFormat|
184      * 2. Aggregated per-process status at |mPath| + |kStatusFileFormat|
185      * 3. Tid stat file at |mPath| + |kTaskDirFormat| + |kStatFileFormat|
186      */
187     android::base::Result<std::tuple<uid_t, ProcessStats>> readProcessStatsLocked(pid_t pid) const;
188 
189     /**
190      * Reads the smaps rollup file and populates the ProcessStats pointer with info for the given
191      * pid.
192      * Returns true and updates the out pointer only when the read is successful.
193      * Returns false when either the smaps_rollup file is not supported or not available for
194      * the process. When the process terminates while reading, the file won't be available.
195      */
196     bool readSmapsRollup(pid_t pid, ProcessStats* processStatsOut) const;
197 
198     size_t mPageSizeKb;
199 
200     // Tracks memory profiling feature flag.
201     bool mIsMemoryProfilingEnabled;
202 
203     // Tracks smaps rollup support in the Kernel.
204     bool mIsSmapsRollupSupported;
205 
206     // Number of milliseconds per clock cycle.
207     int32_t mMillisPerClockTick;
208 
209     /**
210      * Proc directory path. Default value is |kProcDirPath|.
211      *
212      * Updated by tests to point to a different location when needed.
213      */
214     std::string mPath;
215 
216     // Makes sure only one collection is running at any given time.
217     mutable Mutex mMutex;
218 
219     /**
220      * True if the below files are accessible:
221      * 1. Pid stat file at |mPath| + |kStatFileFormat|
222      * 2. Tid stat file at |mPath| + |kTaskDirFormat| + |kStatFileFormat|
223      * 3. Pid status file at |mPath| + |kStatusFileFormat|
224      * 4. Pid statm file at |mPath| + |kStatmFileFormat|
225      *
226      * Otherwise, set to false.
227      */
228     bool mIsEnabled GUARDED_BY(mMutex);
229 
230     /**
231      * True if the tid time_in_state file at |mPath| + |kTaskDirFormat| + |kTimeInStateFileFormat|
232      * is available.
233      */
234     bool mIsTimeInStateEnabled GUARDED_BY(mMutex);
235 
236     // Latest dump of per-UID stats.
237     std::unordered_map<uid_t, UidProcStats> mLatestStats GUARDED_BY(mMutex);
238 
239     // Latest delta of per-uid stat
240     std::unordered_map<uid_t, UidProcStats> mDeltaStats GUARDED_BY(mMutex);
241 
242     FRIEND_TEST(PerformanceProfilerTest, TestValidProcPidContents);
243     FRIEND_TEST(UidProcStatsCollectorTest, TestValidStatFiles);
244     FRIEND_TEST(UidProcStatsCollectorTest, TestHandlesProcessTerminationBetweenScanningAndParsing);
245     FRIEND_TEST(UidProcStatsCollectorTest, TestHandlesPidTidReuse);
246 };
247 
248 }  // namespace watchdog
249 }  // namespace automotive
250 }  // namespace android
251 
252 #endif  //  CPP_WATCHDOG_SERVER_SRC_UIDPROCSTATSCOLLECTOR_H_
253