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 #define LOG_TAG "carwatchdogd"
18 #define DEBUG false
19 
20 #include "UidIoStats.h"
21 
22 #include <android-base/file.h>
23 #include <android-base/parseint.h>
24 #include <android-base/stringprintf.h>
25 #include <android-base/strings.h>
26 #include <inttypes.h>
27 #include <log/log.h>
28 
29 #include <string>
30 #include <unordered_map>
31 #include <vector>
32 #include <utility>
33 
34 namespace android {
35 namespace automotive {
36 namespace watchdog {
37 
38 using android::base::Error;
39 using android::base::ReadFileToString;
40 using android::base::Result;
41 using android::base::StringPrintf;
42 using base::ParseUint;
43 using base::Split;
44 
45 namespace {
46 
parseUidIoStats(const std::string & data,UidIoStat * uidIoStat)47 bool parseUidIoStats(const std::string& data, UidIoStat* uidIoStat) {
48     std::vector<std::string> fields = Split(data, " ");
49     if (fields.size() < 11 || !ParseUint(fields[0], &uidIoStat->uid) ||
50         !ParseUint(fields[1], &uidIoStat->io[FOREGROUND].rchar) ||
51         !ParseUint(fields[2], &uidIoStat->io[FOREGROUND].wchar) ||
52         !ParseUint(fields[3], &uidIoStat->io[FOREGROUND].readBytes) ||
53         !ParseUint(fields[4], &uidIoStat->io[FOREGROUND].writeBytes) ||
54         !ParseUint(fields[5], &uidIoStat->io[BACKGROUND].rchar) ||
55         !ParseUint(fields[6], &uidIoStat->io[BACKGROUND].wchar) ||
56         !ParseUint(fields[7], &uidIoStat->io[BACKGROUND].readBytes) ||
57         !ParseUint(fields[8], &uidIoStat->io[BACKGROUND].writeBytes) ||
58         !ParseUint(fields[9], &uidIoStat->io[FOREGROUND].fsync) ||
59         !ParseUint(fields[10], &uidIoStat->io[BACKGROUND].fsync)) {
60         ALOGW("Invalid uid I/O stats: \"%s\"", data.c_str());
61         return false;
62     }
63     return true;
64 }
65 
66 }  // namespace
67 
isZero() const68 bool IoUsage::isZero() const {
69     for (int i = 0; i < METRIC_TYPES; i++) {
70         for (int j = 0; j < UID_STATES; j++) {
71             if (metrics[i][j]) {
72                 return false;
73             }
74         }
75     }
76     return true;
77 }
78 
toString() const79 std::string IoUsage::toString() const {
80     return StringPrintf("FgRdBytes:%" PRIu64 " BgRdBytes:%" PRIu64 " FgWrBytes:%" PRIu64
81                         " BgWrBytes:%" PRIu64 " FgFsync:%" PRIu64 " BgFsync:%" PRIu64,
82                         metrics[READ_BYTES][FOREGROUND], metrics[READ_BYTES][BACKGROUND],
83                         metrics[WRITE_BYTES][FOREGROUND], metrics[WRITE_BYTES][BACKGROUND],
84                         metrics[FSYNC_COUNT][FOREGROUND], metrics[FSYNC_COUNT][BACKGROUND]);
85 }
86 
collect()87 Result<std::unordered_map<uint32_t, UidIoUsage>> UidIoStats::collect() {
88     if (!kEnabled) {
89         return Error() << "Can not access " << kPath;
90     }
91 
92     Mutex::Autolock lock(mMutex);
93     const auto& uidIoStats = getUidIoStatsLocked();
94     if (!uidIoStats.ok() || uidIoStats->empty()) {
95         return Error() << "Failed to get UID IO stats: " << uidIoStats.error();
96     }
97 
98     std::unordered_map<uint32_t, UidIoUsage> usage;
99     for (const auto& it : *uidIoStats) {
100         const UidIoStat& uidIoStat = it.second;
101         usage[uidIoStat.uid] = {};
102         struct UidIoUsage& uidUsage = usage[uidIoStat.uid];
103         uidUsage.uid = uidIoStat.uid;
104 
105         int64_t fgRdDelta = uidIoStat.io[FOREGROUND].readBytes -
106                             mLastUidIoStats[uidIoStat.uid].io[FOREGROUND].readBytes;
107         int64_t bgRdDelta = uidIoStat.io[BACKGROUND].readBytes -
108                             mLastUidIoStats[uidIoStat.uid].io[BACKGROUND].readBytes;
109         int64_t fgWrDelta = uidIoStat.io[FOREGROUND].writeBytes -
110                             mLastUidIoStats[uidIoStat.uid].io[FOREGROUND].writeBytes;
111         int64_t bgWrDelta = uidIoStat.io[BACKGROUND].writeBytes -
112                             mLastUidIoStats[uidIoStat.uid].io[BACKGROUND].writeBytes;
113         int64_t fgFsDelta =
114             uidIoStat.io[FOREGROUND].fsync - mLastUidIoStats[uidIoStat.uid].io[FOREGROUND].fsync;
115         int64_t bgFsDelta =
116             uidIoStat.io[BACKGROUND].fsync - mLastUidIoStats[uidIoStat.uid].io[BACKGROUND].fsync;
117 
118         uidUsage.ios.metrics[READ_BYTES][FOREGROUND] += (fgRdDelta < 0) ? 0 : fgRdDelta;
119         uidUsage.ios.metrics[READ_BYTES][BACKGROUND] += (bgRdDelta < 0) ? 0 : bgRdDelta;
120         uidUsage.ios.metrics[WRITE_BYTES][FOREGROUND] += (fgWrDelta < 0) ? 0 : fgWrDelta;
121         uidUsage.ios.metrics[WRITE_BYTES][BACKGROUND] += (bgWrDelta < 0) ? 0 : bgWrDelta;
122         uidUsage.ios.metrics[FSYNC_COUNT][FOREGROUND] += (fgFsDelta < 0) ? 0 : fgFsDelta;
123         uidUsage.ios.metrics[FSYNC_COUNT][BACKGROUND] += (bgFsDelta < 0) ? 0 : bgFsDelta;
124     }
125     mLastUidIoStats = *uidIoStats;
126     return usage;
127 }
128 
getUidIoStatsLocked() const129 Result<std::unordered_map<uint32_t, UidIoStat>> UidIoStats::getUidIoStatsLocked() const {
130     std::string buffer;
131     if (!ReadFileToString(kPath, &buffer)) {
132         return Error() << "ReadFileToString failed for " << kPath;
133     }
134 
135     std::vector<std::string> ioStats = Split(std::move(buffer), "\n");
136     std::unordered_map<uint32_t, UidIoStat> uidIoStats;
137     UidIoStat uidIoStat;
138     for (size_t i = 0; i < ioStats.size(); i++) {
139         if (ioStats[i].empty() || !ioStats[i].compare(0, 4, "task")) {
140             // Skip per-task stats as CONFIG_UID_SYS_STATS_DEBUG is not set in the kernel and
141             // the collected data is aggregated only per-UID.
142             continue;
143         }
144         if (!parseUidIoStats(std::move(ioStats[i]), &uidIoStat)) {
145             return Error() << "Failed to parse the contents of " << kPath;
146         }
147         uidIoStats[uidIoStat.uid] = uidIoStat;
148     }
149     return uidIoStats;
150 }
151 
152 }  // namespace watchdog
153 }  // namespace automotive
154 }  // namespace android
155