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