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