1 /*
2  * Copyright (c) 2021, 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_PROCDISKSTATSCOLLECTOR_H_
18 #define CPP_WATCHDOG_SERVER_SRC_PROCDISKSTATSCOLLECTOR_H_
19 
20 #include <android-base/result.h>
21 #include <android-base/stringprintf.h>
22 #include <android-base/strings.h>
23 #include <utils/Mutex.h>
24 #include <utils/RefBase.h>
25 
26 #include <string>
27 #include <unordered_set>
28 #include <vector>
29 
30 namespace android {
31 namespace automotive {
32 namespace watchdog {
33 
34 constexpr const char* kProcDiskStatsPath = "/proc/diskstats";
35 
recordStatsForDevice(const std::string & deviceName)36 inline constexpr bool recordStatsForDevice(const std::string& deviceName) {
37     for (const auto& prefix : {"zram", "ram"}) {
38         if (android::base::StartsWith(deviceName, prefix)) {
39             return false;
40         }
41     }
42     return true;
43 }
44 
45 // Struct that represents the stats from |kUidIoStatsPath|.
46 struct DiskStats {
47     int major = 0;
48     int minor = 0;
49     std::string deviceName;
50     uint64_t numReadsCompleted = 0;
51     uint64_t numReadsMerged = 0;
52     uint64_t numKibRead = 0;
53     uint64_t readTimeInMillis = 0;
54     uint64_t numWritesCompleted = 0;
55     uint64_t numWritesMerged = 0;
56     uint64_t numKibWritten = 0;
57     uint64_t writeTimeInMillis = 0;
58     uint64_t totalIoTimeInMillis = 0;
59     uint64_t weightedTotalIoTimeInMillis = 0;
60     uint64_t numFlushCompleted = 0;
61     uint64_t flushTimeInMillis = 0;
62 
63     DiskStats& operator-=(const DiskStats& rhs);
64     DiskStats& operator+=(const DiskStats& rhs);
65     struct HashByPartition {
66         size_t operator()(const DiskStats& stats) const;
67     };
68     struct EqualByPartition {
69         bool operator()(const DiskStats& lhs, const DiskStats& rhs) const;
70     };
71 };
72 
73 /*
74  * Contains methods that should be implemented by the /proc/diskstats reader or any mock reader
75  * used in tests.
76  */
77 class ProcDiskStatsCollectorInterface : virtual public android::RefBase {
78 public:
79     using PerPartitionDiskStats = ::std::unordered_set<DiskStats, DiskStats::HashByPartition,
80                                                        DiskStats::EqualByPartition>;
81     // Initializes the collector.
82     virtual void init() = 0;
83 
84     // Collects the system-wide block devices statistics.
85     virtual android::base::Result<void> collect() = 0;
86 
87     // Returns the latest per-disk stats.
88     virtual PerPartitionDiskStats latestPerPartitionDiskStats() const = 0;
89 
90     // Returns the aggregated delta stats since the last before collection.
91     virtual DiskStats deltaSystemWideDiskStats() const = 0;
92 
93     // Returns true when the proc diskstats file is accessible. Otherwise, returns false.
94     virtual bool enabled() const = 0;
95 
96     // Path to the disk stats file.
97     virtual std::string filePath() const = 0;
98 };
99 
100 class ProcDiskStatsCollector final : public ProcDiskStatsCollectorInterface {
101 public:
kPath(path)102     explicit ProcDiskStatsCollector(const std::string& path = kProcDiskStatsPath) : kPath(path) {}
103 
~ProcDiskStatsCollector()104     ~ProcDiskStatsCollector() {}
105 
init()106     void init() {
107         Mutex::Autolock lock(mMutex);
108         // Note: Verify proc file access outside the constructor. Otherwise, the unittests of
109         // dependent classes would call the constructor before mocking and get killed due to
110         // sepolicy violation.
111         mEnabled = access(kPath.c_str(), R_OK) == 0;
112     }
113 
114     android::base::Result<void> collect();
115 
latestPerPartitionDiskStats()116     PerPartitionDiskStats latestPerPartitionDiskStats() const {
117         Mutex::Autolock lock(mMutex);
118         return mLatestPerPartitionDiskStats;
119     }
120 
deltaSystemWideDiskStats()121     DiskStats deltaSystemWideDiskStats() const {
122         Mutex::Autolock lock(mMutex);
123         return mDeltaSystemWideDiskStats;
124     }
125 
enabled()126     bool enabled() const {
127         Mutex::Autolock lock(mMutex);
128         return mEnabled;
129     }
130 
filePath()131     std::string filePath() const { return kPath; }
132 
133 private:
134     // Path to disk stats file.
135     const std::string kPath;
136 
137     // Makes sure only one collection is running at any given time.
138     mutable Mutex mMutex;
139 
140     // True if |kPath| is accessible.
141     bool mEnabled GUARDED_BY(mMutex);
142 
143     // Delta of per-UID I/O usage since last before collection.
144     DiskStats mDeltaSystemWideDiskStats GUARDED_BY(mMutex);
145 
146     /*
147      * Latest per-disk stats from the file at |kPath|. Per-disk stats is required for calculating
148      * per-disk delta since last collection. Because the stats reported in |kPath| may overflow,
149      * storing the stats per-disk helps to deal with this issue.
150      */
151     PerPartitionDiskStats mLatestPerPartitionDiskStats GUARDED_BY(mMutex);
152 };
153 
154 }  // namespace watchdog
155 }  // namespace automotive
156 }  // namespace android
157 
158 #endif  //  CPP_WATCHDOG_SERVER_SRC_PROCDISKSTATSCOLLECTOR_H_
159