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 <utils/Mutex.h>
24 #include <utils/RefBase.h>
25 
26 #include <inttypes.h>
27 #include <stdint.h>
28 
29 #include <string>
30 #include <unordered_map>
31 #include <vector>
32 
33 namespace android {
34 namespace automotive {
35 namespace watchdog {
36 
37 using ::android::base::StringPrintf;
38 
39 #define PID_FOR_INIT 1
40 
41 constexpr const char kProcDirPath[] = "/proc";
42 constexpr const char kStatFileFormat[] = "/%" PRIu32 "/stat";
43 constexpr const char kTaskDirFormat[] = "/%" PRIu32 "/task";
44 constexpr const char kStatusFileFormat[] = "/%" PRIu32 "/status";
45 
46 // Per-process stats.
47 struct ProcessStats {
48     std::string comm = "";
49     uint64_t startTime = 0;  // Useful when identifying PID reuse
50     uint64_t totalMajorFaults = 0;
51     int totalTasksCount = 0;
52     int ioBlockedTasksCount = 0;
53     std::string toString() const;
54 };
55 
56 // Per-UID stats.
57 struct UidProcStats {
58     uint64_t totalMajorFaults = 0;
59     int totalTasksCount = 0;
60     int ioBlockedTasksCount = 0;
61     std::unordered_map<pid_t, ProcessStats> processStatsByPid = {};
62     std::string toString() const;
63 };
64 
65 /**
66  * Collector/parser for `/proc/[pid]/stat`, `/proc/[pid]/task/[tid]/stat` and /proc/[pid]/status`
67  * files.
68  */
69 class UidProcStatsCollectorInterface : public RefBase {
70 public:
71     // Collects the per-uid stats from /proc directory.
72     virtual android::base::Result<void> collect() = 0;
73     // Returns the latest per-uid process stats.
74     virtual const std::unordered_map<uid_t, UidProcStats> latestStats() const = 0;
75     // Returns the delta of per-uid process stats since the last before collection.
76     virtual const std::unordered_map<uid_t, UidProcStats> deltaStats() const = 0;
77     // Returns true only when the /proc files for the init process are accessible.
78     virtual bool enabled() const = 0;
79     // Returns the /proc files common ancestor directory path.
80     virtual const std::string dirPath() const = 0;
81 };
82 
83 class UidProcStatsCollector final : public UidProcStatsCollectorInterface {
84 public:
85     explicit UidProcStatsCollector(const std::string& path = kProcDirPath) :
86           mLatestStats({}),
87           mPath(path) {
88         std::string pidStatPath = StringPrintf((mPath + kStatFileFormat).c_str(), PID_FOR_INIT);
89         std::string tidStatPath = StringPrintf((mPath + kTaskDirFormat + kStatFileFormat).c_str(),
90                                                PID_FOR_INIT, PID_FOR_INIT);
91         std::string pidStatusPath = StringPrintf((mPath + kStatusFileFormat).c_str(), PID_FOR_INIT);
92 
93         mEnabled = !access(pidStatPath.c_str(), R_OK) && !access(tidStatPath.c_str(), R_OK) &&
94                 !access(pidStatusPath.c_str(), R_OK);
95     }
96 
~UidProcStatsCollector()97     ~UidProcStatsCollector() {}
98 
99     android::base::Result<void> collect() override;
100 
latestStats()101     const std::unordered_map<uid_t, UidProcStats> latestStats() const {
102         Mutex::Autolock lock(mMutex);
103         return mLatestStats;
104     }
105 
deltaStats()106     const std::unordered_map<uid_t, UidProcStats> deltaStats() const {
107         Mutex::Autolock lock(mMutex);
108         return mDeltaStats;
109     }
110 
enabled()111     bool enabled() const { return mEnabled; }
112 
dirPath()113     const std::string dirPath() const { return mPath; }
114 
115 private:
116     android::base::Result<std::unordered_map<uid_t, UidProcStats>> readUidProcStatsLocked() const;
117 
118     /**
119      * Reads the contents of the below files:
120      * 1. Pid stat file at |mPath| + |kStatFileFormat|
121      * 2. Aggregated per-process status at |mPath| + |kStatusFileFormat|
122      * 3. Tid stat file at |mPath| + |kTaskDirFormat| + |kStatFileFormat|
123      */
124     android::base::Result<std::tuple<uid_t, ProcessStats>> readProcessStatsLocked(pid_t pid) const;
125 
126     // Makes sure only one collection is running at any given time.
127     mutable Mutex mMutex;
128 
129     // Latest dump of per-UID stats.
130     std::unordered_map<uid_t, UidProcStats> mLatestStats GUARDED_BY(mMutex);
131 
132     // Latest delta of per-uid stats.
133     std::unordered_map<uid_t, UidProcStats> mDeltaStats GUARDED_BY(mMutex);
134 
135     /**
136      * True if the below files are accessible:
137      * 1. Pid stat file at |mPath| + |kTaskStatFileFormat|
138      * 2. Tid stat file at |mPath| + |kTaskDirFormat| + |kStatFileFormat|
139      * 3. Pid status file at |mPath| + |kStatusFileFormat|
140      * Otherwise, set to false.
141      */
142     bool mEnabled;
143 
144     /**
145      * Proc directory path. Default value is |kProcDirPath|.
146      * Updated by tests to point to a different location when needed.
147      */
148     std::string mPath;
149 
150     FRIEND_TEST(IoPerfCollectionTest, TestValidProcPidContents);
151     FRIEND_TEST(UidProcStatsCollectorTest, TestValidStatFiles);
152     FRIEND_TEST(UidProcStatsCollectorTest, TestHandlesProcessTerminationBetweenScanningAndParsing);
153     FRIEND_TEST(UidProcStatsCollectorTest, TestHandlesPidTidReuse);
154 };
155 
156 }  // namespace watchdog
157 }  // namespace automotive
158 }  // namespace android
159 
160 #endif  //  CPP_WATCHDOG_SERVER_SRC_UIDPROCSTATSCOLLECTOR_H_
161