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 <utils/Mutex.h>
22 #include <utils/RefBase.h>
23 
24 #include <stdint.h>
25 
26 #include <string>
27 #include <unordered_map>
28 
29 namespace android {
30 namespace automotive {
31 namespace watchdog {
32 
33 constexpr const char* kUidIoStatsPath = "/proc/uid_io/stats";
34 
35 enum UidState {
36     FOREGROUND = 0,
37     BACKGROUND,
38     UID_STATES,
39 };
40 
41 enum MetricType {
42     READ_BYTES = 0,  // bytes read (from storage layer)
43     WRITE_BYTES,     // bytes written (to storage layer)
44     FSYNC_COUNT,     // number of fsync syscalls
45     METRIC_TYPES,
46 };
47 
48 // Defines the per-UID I/O stats.
49 class UidIoStats final {
50 public:
UidIoStats()51     UidIoStats() : metrics{{0}} {};
UidIoStats(int64_t fgRdBytes,int64_t bgRdBytes,int64_t fgWrBytes,int64_t bgWrBytes,int64_t fgFsync,int64_t bgFsync)52     UidIoStats(int64_t fgRdBytes, int64_t bgRdBytes, int64_t fgWrBytes, int64_t bgWrBytes,
53                int64_t fgFsync, int64_t bgFsync) {
54         metrics[READ_BYTES][FOREGROUND] = fgRdBytes;
55         metrics[READ_BYTES][BACKGROUND] = bgRdBytes;
56         metrics[WRITE_BYTES][FOREGROUND] = fgWrBytes;
57         metrics[WRITE_BYTES][BACKGROUND] = bgWrBytes;
58         metrics[FSYNC_COUNT][FOREGROUND] = fgFsync;
59         metrics[FSYNC_COUNT][BACKGROUND] = bgFsync;
60     }
61     UidIoStats& operator-=(const UidIoStats& rhs);
62     bool operator==(const UidIoStats& stats) const {
63         return memcmp(&metrics, &stats.metrics, sizeof(metrics)) == 0;
64     }
sumReadBytes()65     int64_t sumReadBytes() const {
66         const auto& [fgBytes, bgBytes] =
67                 std::tuple(metrics[READ_BYTES][FOREGROUND], metrics[READ_BYTES][BACKGROUND]);
68         return (std::numeric_limits<int64_t>::max() - fgBytes) > bgBytes
69                 ? (fgBytes + bgBytes)
70                 : std::numeric_limits<int64_t>::max();
71     }
sumWriteBytes()72     int64_t sumWriteBytes() const {
73         const auto& [fgBytes, bgBytes] =
74                 std::tuple(metrics[WRITE_BYTES][FOREGROUND], metrics[WRITE_BYTES][BACKGROUND]);
75         return (std::numeric_limits<int64_t>::max() - fgBytes) > bgBytes
76                 ? (fgBytes + bgBytes)
77                 : std::numeric_limits<int64_t>::max();
78     }
79     bool isZero() const;
80     std::string toString() const;
81     int64_t metrics[METRIC_TYPES][UID_STATES];
82 };
83 
84 // Collector/Parser for `/proc/uid_io/stats`.
85 class UidIoStatsCollectorInterface : public RefBase {
86 public:
87     // Initializes the collector.
88     virtual void init() = 0;
89     // Collects the per-UID I/O stats.
90     virtual android::base::Result<void> collect() = 0;
91     // Returns the latest per-uid I/O stats.
92     virtual const std::unordered_map<uid_t, UidIoStats> latestStats() const = 0;
93     // Returns the delta of per-uid I/O stats since the last before collection.
94     virtual const std::unordered_map<uid_t, UidIoStats> deltaStats() const = 0;
95     // Returns true only when the per-UID I/O stats file is accessible.
96     virtual bool enabled() const = 0;
97     // Returns the path for the per-UID I/O stats file.
98     virtual const std::string filePath() const = 0;
99 };
100 
101 class UidIoStatsCollector final : public UidIoStatsCollectorInterface {
102 public:
kPath(path)103     explicit UidIoStatsCollector(const std::string& path = kUidIoStatsPath) : kPath(path) {}
104 
~UidIoStatsCollector()105     ~UidIoStatsCollector() {}
106 
init()107     void init() override {
108         Mutex::Autolock lock(mMutex);
109         // Note: Verify proc file access outside the constructor. Otherwise, the unittests of
110         // dependent classes would call the constructor before mocking and get killed due to
111         // sepolicy violation.
112         mEnabled = access(kPath.c_str(), R_OK) == 0;
113     }
114 
115     android::base::Result<void> collect() override;
116 
latestStats()117     const std::unordered_map<uid_t, UidIoStats> latestStats() const override {
118         Mutex::Autolock lock(mMutex);
119         return mLatestStats;
120     }
121 
deltaStats()122     const std::unordered_map<uid_t, UidIoStats> deltaStats() const override {
123         Mutex::Autolock lock(mMutex);
124         return mDeltaStats;
125     }
126 
enabled()127     bool enabled() const override {
128         Mutex::Autolock lock(mMutex);
129         return mEnabled;
130     }
131 
filePath()132     const std::string filePath() const override { return kPath; }
133 
134 private:
135     // Reads the contents of |kPath|.
136     android::base::Result<std::unordered_map<uid_t, UidIoStats>> readUidIoStatsLocked() const;
137 
138     // Path to uid_io stats file. Default path is |kUidIoStatsPath|.
139     const std::string kPath;
140 
141     // Makes sure only one collection is running at any given time.
142     mutable Mutex mMutex;
143 
144     // True if |kPath| is accessible.
145     bool mEnabled GUARDED_BY(mMutex);
146 
147     // Latest dump from the file at |kPath|.
148     std::unordered_map<uid_t, UidIoStats> mLatestStats GUARDED_BY(mMutex);
149 
150     // Delta of per-UID I/O stats since last before collection.
151     std::unordered_map<uid_t, UidIoStats> mDeltaStats GUARDED_BY(mMutex);
152 };
153 
154 }  // namespace watchdog
155 }  // namespace automotive
156 }  // namespace android
157