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 
19 #include "IoPerfCollection.h"
20 
21 #include <WatchdogProperties.sysprop.h>
22 #include <android-base/file.h>
23 #include <android-base/stringprintf.h>
24 #include <log/log.h>
25 
26 #include <inttypes.h>
27 
28 #include <iomanip>
29 #include <limits>
30 #include <string>
31 #include <unordered_map>
32 #include <unordered_set>
33 #include <vector>
34 
35 namespace android {
36 namespace automotive {
37 namespace watchdog {
38 
39 using ::android::wp;
40 using ::android::base::Error;
41 using ::android::base::Result;
42 using ::android::base::StringAppendF;
43 using ::android::base::StringPrintf;
44 using ::android::base::WriteStringToFd;
45 
46 namespace {
47 
48 constexpr int32_t kDefaultTopNStatsPerCategory = 10;
49 constexpr int32_t kDefaultTopNStatsPerSubcategory = 5;
50 constexpr const char kBootTimeCollectionTitle[] = "%s\nBoot-time I/O performance report:\n%s\n";
51 constexpr const char kPeriodicCollectionTitle[] =
52         "%s\nLast N minutes I/O performance report:\n%s\n";
53 constexpr const char kCustomCollectionTitle[] = "%s\nCustom I/O performance data report:\n%s\n";
54 constexpr const char kCollectionTitle[] =
55         "Collection duration: %.f seconds\nNumber of collections: %zu\n";
56 constexpr const char kRecordTitle[] = "\nCollection %zu: <%s>\n%s\n%s";
57 constexpr const char kIoReadsTitle[] = "\nTop N Reads:\n%s\n";
58 constexpr const char kIoWritesTitle[] = "\nTop N Writes:\n%s\n";
59 constexpr const char kIoStatsHeader[] =
60         "Android User ID, Package Name, Foreground Bytes, Foreground Bytes %%, Foreground Fsync, "
61         "Foreground Fsync %%, Background Bytes, Background Bytes %%, Background Fsync, "
62         "Background Fsync %%\n";
63 constexpr const char kIoBlockedTitle[] = "\nTop N I/O waiting UIDs:\n%s\n";
64 constexpr const char kIoBlockedHeader[] =
65         "Android User ID, Package Name, Number of owned tasks waiting for I/O, Percentage of owned "
66         "tasks waiting for I/O\n\tCommand, Number of I/O waiting tasks, Percentage of UID's tasks "
67         "waiting for I/O\n";
68 constexpr const char kMajorPageFaultsTitle[] = "\nTop N major page faults:\n%s\n";
69 constexpr const char kMajorFaultsHeader[] =
70         "Android User ID, Package Name, Number of major page faults, Percentage of total major "
71         "page faults\n\tCommand, Number of major page faults, Percentage of UID's major page "
72         "faults\n";
73 constexpr const char kMajorFaultsSummary[] =
74         "Number of major page faults since last collection: %" PRIu64 "\n"
75         "Percentage of change in major page faults since last collection: %.2f%%\n";
76 
percentage(uint64_t numer,uint64_t denom)77 double percentage(uint64_t numer, uint64_t denom) {
78     return denom == 0 ? 0.0 : (static_cast<double>(numer) / static_cast<double>(denom)) * 100.0;
79 }
80 
addUidIoStats(const int64_t entry[][UID_STATES],int64_t total[][UID_STATES])81 void addUidIoStats(const int64_t entry[][UID_STATES], int64_t total[][UID_STATES]) {
82     const auto sum = [](int64_t lhs, int64_t rhs) -> int64_t {
83         return std::numeric_limits<int64_t>::max() - lhs > rhs
84                 ? lhs + rhs
85                 : std::numeric_limits<int64_t>::max();
86     };
87     total[READ_BYTES][FOREGROUND] =
88             sum(total[READ_BYTES][FOREGROUND], entry[READ_BYTES][FOREGROUND]);
89     total[READ_BYTES][BACKGROUND] =
90             sum(total[READ_BYTES][BACKGROUND], entry[READ_BYTES][BACKGROUND]);
91     total[WRITE_BYTES][FOREGROUND] =
92             sum(total[WRITE_BYTES][FOREGROUND], entry[WRITE_BYTES][FOREGROUND]);
93     total[WRITE_BYTES][BACKGROUND] =
94             sum(total[WRITE_BYTES][BACKGROUND], entry[WRITE_BYTES][BACKGROUND]);
95     total[FSYNC_COUNT][FOREGROUND] =
96             sum(total[FSYNC_COUNT][FOREGROUND], entry[FSYNC_COUNT][FOREGROUND]);
97     total[FSYNC_COUNT][BACKGROUND] =
98             sum(total[FSYNC_COUNT][BACKGROUND], entry[FSYNC_COUNT][BACKGROUND]);
99     return;
100 }
101 
toUserPackageStats(MetricType metricType,const UidStats & uidStats)102 UserPackageStats toUserPackageStats(MetricType metricType, const UidStats& uidStats) {
103     const UidIoStats& ioStats = uidStats.ioStats;
104     return UserPackageStats{
105             .uid = uidStats.uid(),
106             .genericPackageName = uidStats.genericPackageName(),
107             .stats = UserPackageStats::
108                     IoStats{.bytes = {ioStats.metrics[metricType][UidState::FOREGROUND],
109                                       ioStats.metrics[metricType][UidState::BACKGROUND]},
110                             .fsync =
111                                     {ioStats.metrics[MetricType::FSYNC_COUNT][UidState::FOREGROUND],
112                                      ioStats.metrics[MetricType::FSYNC_COUNT]
113                                                     [UidState::BACKGROUND]}},
114     };
115 }
116 
cacheTopNIoStats(MetricType metricType,const UidStats & uidStats,std::vector<UserPackageStats> * topNIoStats)117 void cacheTopNIoStats(MetricType metricType, const UidStats& uidStats,
118                       std::vector<UserPackageStats>* topNIoStats) {
119     if (metricType != MetricType::READ_BYTES && metricType != MetricType::WRITE_BYTES) {
120         return;
121     }
122     int64_t totalBytes = metricType == MetricType::READ_BYTES ? uidStats.ioStats.sumReadBytes()
123                                                               : uidStats.ioStats.sumWriteBytes();
124     if (totalBytes == 0) {
125         return;
126     }
127     for (auto it = topNIoStats->begin(); it != topNIoStats->end(); ++it) {
128         if (const auto* ioStats = std::get_if<UserPackageStats::IoStats>(&it->stats);
129             ioStats == nullptr || totalBytes > ioStats->totalBytes()) {
130             topNIoStats->emplace(it, toUserPackageStats(metricType, uidStats));
131             topNIoStats->pop_back();
132             break;
133         }
134     }
135     return;
136 }
137 
138 enum ProcStatType {
139     IO_BLOCKED_TASKS_COUNT = 0,
140     MAJOR_FAULTS,
141     PROC_STAT_TYPES,
142 };
143 
cacheTopNProcessStats(ProcStatType procStatType,const ProcessStats & processStats,std::vector<UserPackageStats::ProcStats::ProcessCount> * topNProcesses)144 bool cacheTopNProcessStats(ProcStatType procStatType, const ProcessStats& processStats,
145                            std::vector<UserPackageStats::ProcStats::ProcessCount>* topNProcesses) {
146     uint64_t count = procStatType == IO_BLOCKED_TASKS_COUNT ? processStats.ioBlockedTasksCount
147                                                             : processStats.totalMajorFaults;
148     if (count == 0) {
149         return false;
150     }
151     for (auto it = topNProcesses->begin(); it != topNProcesses->end(); ++it) {
152         if (count > it->count) {
153             topNProcesses->emplace(it,
154                                    UserPackageStats::ProcStats::ProcessCount{
155                                            .comm = processStats.comm,
156                                            .count = count,
157                                    });
158             topNProcesses->pop_back();
159             return true;
160         }
161     }
162     return false;
163 }
164 
toUserPackageStats(ProcStatType procStatType,const UidStats & uidStats,int topNProcessCount)165 UserPackageStats toUserPackageStats(ProcStatType procStatType, const UidStats& uidStats,
166                                     int topNProcessCount) {
167     uint64_t count = procStatType == IO_BLOCKED_TASKS_COUNT ? uidStats.procStats.ioBlockedTasksCount
168                                                             : uidStats.procStats.totalMajorFaults;
169     UserPackageStats userPackageStats = {
170             .uid = uidStats.uid(),
171             .genericPackageName = uidStats.genericPackageName(),
172             .stats = UserPackageStats::ProcStats{.count = count},
173     };
174     auto& procStats = std::get<UserPackageStats::ProcStats>(userPackageStats.stats);
175     procStats.topNProcesses.resize(topNProcessCount);
176     int cachedProcessCount = 0;
177     for (const auto& [_, processStats] : uidStats.procStats.processStatsByPid) {
178         if (cacheTopNProcessStats(procStatType, processStats, &procStats.topNProcesses)) {
179             ++cachedProcessCount;
180         }
181     }
182     if (cachedProcessCount < topNProcessCount) {
183         procStats.topNProcesses.erase(procStats.topNProcesses.begin() + cachedProcessCount,
184                                       procStats.topNProcesses.end());
185     }
186     return userPackageStats;
187 }
188 
cacheTopNProcStats(ProcStatType procStatType,const UidStats & uidStats,int topNProcessCount,std::vector<UserPackageStats> * topNProcStats)189 bool cacheTopNProcStats(ProcStatType procStatType, const UidStats& uidStats, int topNProcessCount,
190                         std::vector<UserPackageStats>* topNProcStats) {
191     uint64_t count = procStatType == IO_BLOCKED_TASKS_COUNT ? uidStats.procStats.ioBlockedTasksCount
192                                                             : uidStats.procStats.totalMajorFaults;
193     if (count == 0) {
194         return false;
195     }
196     for (auto it = topNProcStats->begin(); it != topNProcStats->end(); ++it) {
197         if (const auto* procStats = std::get_if<UserPackageStats::ProcStats>(&it->stats);
198             procStats == nullptr || count > procStats->count) {
199             topNProcStats->emplace(it,
200                                    toUserPackageStats(procStatType, uidStats, topNProcessCount));
201             topNProcStats->pop_back();
202             return true;
203         }
204     }
205     return false;
206 }
207 
checkDataCollectors(const sp<UidStatsCollectorInterface> & uidStatsCollector,const sp<ProcStat> & procStat)208 Result<void> checkDataCollectors(const sp<UidStatsCollectorInterface>& uidStatsCollector,
209                                  const sp<ProcStat>& procStat) {
210     if (uidStatsCollector != nullptr && procStat != nullptr) {
211         return {};
212     }
213     std::string error;
214     if (uidStatsCollector == nullptr) {
215         error = "Per-UID stats collector must not be null";
216     }
217     if (procStat == nullptr) {
218         StringAppendF(&error, "%s%s", error.empty() ? "" : ", ",
219                       "Proc stats collector must not be null");
220     }
221     return Error() << "Invalid data collectors: " << error;
222 }
223 
224 }  // namespace
225 
toString(MetricType metricsType,const int64_t totalIoStats[][UID_STATES]) const226 std::string UserPackageStats::toString(MetricType metricsType,
227                                        const int64_t totalIoStats[][UID_STATES]) const {
228     std::string buffer;
229     StringAppendF(&buffer, "%" PRIu32 ", %s", multiuser_get_user_id(uid),
230                   genericPackageName.c_str());
231     const auto& ioStats = std::get<UserPackageStats::IoStats>(stats);
232     for (int i = 0; i < UID_STATES; ++i) {
233         StringAppendF(&buffer, ", %" PRIi64 ", %.2f%%, %" PRIi64 ", %.2f%%", ioStats.bytes[i],
234                       percentage(ioStats.bytes[i], totalIoStats[metricsType][i]), ioStats.fsync[i],
235                       percentage(ioStats.fsync[i], totalIoStats[FSYNC_COUNT][i]));
236     }
237     StringAppendF(&buffer, "\n");
238     return buffer;
239 }
240 
toString(int64_t totalCount) const241 std::string UserPackageStats::toString(int64_t totalCount) const {
242     std::string buffer;
243     const auto& procStats = std::get<UserPackageStats::ProcStats>(stats);
244     StringAppendF(&buffer, "%" PRIu32 ", %s, %" PRIu64 ", %.2f%%\n", multiuser_get_user_id(uid),
245                   genericPackageName.c_str(), procStats.count,
246                   percentage(procStats.count, totalCount));
247     for (const auto& processCount : procStats.topNProcesses) {
248         StringAppendF(&buffer, "\t%s, %" PRIu64 ", %.2f%%\n", processCount.comm.c_str(),
249                       processCount.count, percentage(processCount.count, procStats.count));
250     }
251     return buffer;
252 }
253 
toString() const254 std::string UserPackageSummaryStats::toString() const {
255     std::string buffer;
256     if (!topNIoReads.empty()) {
257         StringAppendF(&buffer, kIoReadsTitle, std::string(12, '-').c_str());
258         StringAppendF(&buffer, kIoStatsHeader);
259         for (const auto& stats : topNIoReads) {
260             StringAppendF(&buffer, "%s",
261                           stats.toString(MetricType::READ_BYTES, totalIoStats).c_str());
262         }
263     }
264     if (!topNIoWrites.empty()) {
265         StringAppendF(&buffer, kIoWritesTitle, std::string(13, '-').c_str());
266         StringAppendF(&buffer, kIoStatsHeader);
267         for (const auto& stats : topNIoWrites) {
268             StringAppendF(&buffer, "%s",
269                           stats.toString(MetricType::WRITE_BYTES, totalIoStats).c_str());
270         }
271     }
272     if (!topNIoBlocked.empty()) {
273         StringAppendF(&buffer, kIoBlockedTitle, std::string(23, '-').c_str());
274         StringAppendF(&buffer, kIoBlockedHeader);
275         for (const auto& stats : topNIoBlocked) {
276             const auto it = taskCountByUid.find(stats.uid);
277             if (it == taskCountByUid.end()) {
278                 continue;
279             }
280             StringAppendF(&buffer, "%s", stats.toString(it->second).c_str());
281         }
282     }
283     if (!topNMajorFaults.empty()) {
284         StringAppendF(&buffer, kMajorPageFaultsTitle, std::string(24, '-').c_str());
285         StringAppendF(&buffer, kMajorFaultsHeader);
286         for (const auto& stats : topNMajorFaults) {
287             StringAppendF(&buffer, "%s", stats.toString(totalMajorFaults).c_str());
288         }
289         StringAppendF(&buffer, kMajorFaultsSummary, totalMajorFaults, majorFaultsPercentChange);
290     }
291     return buffer;
292 }
293 
toString() const294 std::string SystemSummaryStats::toString() const {
295     std::string buffer;
296     StringAppendF(&buffer, "CPU I/O wait time/percent: %" PRIu64 " / %.2f%%\n", cpuIoWaitTime,
297                   percentage(cpuIoWaitTime, totalCpuTime));
298     StringAppendF(&buffer, "Number of I/O blocked processes/percent: %" PRIu32 " / %.2f%%\n",
299                   ioBlockedProcessCount, percentage(ioBlockedProcessCount, totalProcessCount));
300     return buffer;
301 }
302 
toString() const303 std::string PerfStatsRecord::toString() const {
304     std::string buffer;
305     StringAppendF(&buffer, "%s%s", systemSummaryStats.toString().c_str(),
306                   userPackageSummaryStats.toString().c_str());
307     return buffer;
308 }
309 
toString() const310 std::string CollectionInfo::toString() const {
311     if (records.empty()) {
312         return kEmptyCollectionMessage;
313     }
314     std::string buffer;
315     double duration = difftime(records.back().time, records.front().time);
316     StringAppendF(&buffer, kCollectionTitle, duration, records.size());
317     for (size_t i = 0; i < records.size(); ++i) {
318         const auto& record = records[i];
319         std::stringstream timestamp;
320         timestamp << std::put_time(std::localtime(&record.time), "%c %Z");
321         StringAppendF(&buffer, kRecordTitle, i, timestamp.str().c_str(),
322                       std::string(45, '=').c_str(), record.toString().c_str());
323     }
324     return buffer;
325 }
326 
init()327 Result<void> IoPerfCollection::init() {
328     Mutex::Autolock lock(mMutex);
329     if (mTopNStatsPerCategory != 0 || mTopNStatsPerSubcategory != 0) {
330         return Error() << "Cannot initialize " << name() << " more than once";
331     }
332     mTopNStatsPerCategory = static_cast<int>(
333             sysprop::topNStatsPerCategory().value_or(kDefaultTopNStatsPerCategory));
334     mTopNStatsPerSubcategory = static_cast<int>(
335             sysprop::topNStatsPerSubcategory().value_or(kDefaultTopNStatsPerSubcategory));
336     size_t periodicCollectionBufferSize = static_cast<size_t>(
337             sysprop::periodicCollectionBufferSize().value_or(kDefaultPeriodicCollectionBufferSize));
338     mBoottimeCollection = {
339             .maxCacheSize = std::numeric_limits<std::size_t>::max(),
340             .records = {},
341     };
342     mPeriodicCollection = {
343             .maxCacheSize = periodicCollectionBufferSize,
344             .records = {},
345     };
346     mCustomCollection = {
347             .maxCacheSize = std::numeric_limits<std::size_t>::max(),
348             .records = {},
349     };
350     return {};
351 }
352 
terminate()353 void IoPerfCollection::terminate() {
354     Mutex::Autolock lock(mMutex);
355 
356     ALOGW("Terminating %s", name().c_str());
357 
358     mBoottimeCollection.records.clear();
359     mBoottimeCollection = {};
360 
361     mPeriodicCollection.records.clear();
362     mPeriodicCollection = {};
363 
364     mCustomCollection.records.clear();
365     mCustomCollection = {};
366 }
367 
onDump(int fd) const368 Result<void> IoPerfCollection::onDump(int fd) const {
369     Mutex::Autolock lock(mMutex);
370     if (!WriteStringToFd(StringPrintf(kBootTimeCollectionTitle, std::string(75, '-').c_str(),
371                                       std::string(33, '=').c_str()),
372                          fd) ||
373         !WriteStringToFd(mBoottimeCollection.toString(), fd) ||
374         !WriteStringToFd(StringPrintf(kPeriodicCollectionTitle, std::string(75, '-').c_str(),
375                                       std::string(38, '=').c_str()),
376                          fd) ||
377         !WriteStringToFd(mPeriodicCollection.toString(), fd)) {
378         return Error(FAILED_TRANSACTION)
379                 << "Failed to dump the boot-time and periodic collection reports.";
380     }
381     return {};
382 }
383 
onCustomCollectionDump(int fd)384 Result<void> IoPerfCollection::onCustomCollectionDump(int fd) {
385     if (fd == -1) {
386         // Custom collection ends so clear the cache.
387         mCustomCollection.records.clear();
388         mCustomCollection = {
389                 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
390                 .records = {},
391         };
392         return {};
393     }
394 
395     if (!WriteStringToFd(StringPrintf(kCustomCollectionTitle, std::string(75, '-').c_str(),
396                                       std::string(75, '-').c_str()),
397                          fd) ||
398         !WriteStringToFd(mCustomCollection.toString(), fd)) {
399         return Error(FAILED_TRANSACTION) << "Failed to write custom I/O collection report.";
400     }
401 
402     return {};
403 }
404 
onBoottimeCollection(time_t time,const wp<UidStatsCollectorInterface> & uidStatsCollector,const wp<ProcStat> & procStat)405 Result<void> IoPerfCollection::onBoottimeCollection(
406         time_t time, const wp<UidStatsCollectorInterface>& uidStatsCollector,
407         const wp<ProcStat>& procStat) {
408     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
409     const sp<ProcStat> procStatSp = procStat.promote();
410     auto result = checkDataCollectors(uidStatsCollectorSp, procStatSp);
411     if (!result.ok()) {
412         return result;
413     }
414     Mutex::Autolock lock(mMutex);
415     return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp, procStatSp,
416                          &mBoottimeCollection);
417 }
418 
419 Result<void> IoPerfCollection::onPeriodicCollection(
420         time_t time, [[maybe_unused]] SystemState systemState,
421         const wp<UidStatsCollectorInterface>& uidStatsCollector, const wp<ProcStat>& procStat) {
422     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
423     const sp<ProcStat> procStatSp = procStat.promote();
424     auto result = checkDataCollectors(uidStatsCollectorSp, procStatSp);
425     if (!result.ok()) {
426         return result;
427     }
428     Mutex::Autolock lock(mMutex);
429     return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp, procStatSp,
430                          &mPeriodicCollection);
431 }
432 
433 Result<void> IoPerfCollection::onCustomCollection(
434         time_t time, [[maybe_unused]] SystemState systemState,
435         const std::unordered_set<std::string>& filterPackages,
436         const wp<UidStatsCollectorInterface>& uidStatsCollector, const wp<ProcStat>& procStat) {
437     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
438     const sp<ProcStat> procStatSp = procStat.promote();
439     auto result = checkDataCollectors(uidStatsCollectorSp, procStatSp);
440     if (!result.ok()) {
441         return result;
442     }
443     Mutex::Autolock lock(mMutex);
444     return processLocked(time, filterPackages, uidStatsCollectorSp, procStatSp, &mCustomCollection);
445 }
446 
processLocked(time_t time,const std::unordered_set<std::string> & filterPackages,const sp<UidStatsCollectorInterface> & uidStatsCollector,const sp<ProcStat> & procStat,CollectionInfo * collectionInfo)447 Result<void> IoPerfCollection::processLocked(
448         time_t time, const std::unordered_set<std::string>& filterPackages,
449         const sp<UidStatsCollectorInterface>& uidStatsCollector, const sp<ProcStat>& procStat,
450         CollectionInfo* collectionInfo) {
451     if (collectionInfo->maxCacheSize == 0) {
452         return Error() << "Maximum cache size cannot be 0";
453     }
454     PerfStatsRecord record{
455             .time = time,
456     };
457     processUidStatsLocked(filterPackages, uidStatsCollector, &record.userPackageSummaryStats);
458     processProcStatLocked(procStat, &record.systemSummaryStats);
459     if (collectionInfo->records.size() > collectionInfo->maxCacheSize) {
460         collectionInfo->records.erase(collectionInfo->records.begin());  // Erase the oldest record.
461     }
462     collectionInfo->records.emplace_back(record);
463     return {};
464 }
465 
processUidStatsLocked(const std::unordered_set<std::string> & filterPackages,const sp<UidStatsCollectorInterface> & uidStatsCollector,UserPackageSummaryStats * userPackageSummaryStats)466 void IoPerfCollection::processUidStatsLocked(
467         const std::unordered_set<std::string>& filterPackages,
468         const sp<UidStatsCollectorInterface>& uidStatsCollector,
469         UserPackageSummaryStats* userPackageSummaryStats) {
470     const std::vector<UidStats>& uidStats = uidStatsCollector->deltaStats();
471     if (uidStats.empty()) {
472         return;
473     }
474     if (filterPackages.empty()) {
475         userPackageSummaryStats->topNIoReads.resize(mTopNStatsPerCategory);
476         userPackageSummaryStats->topNIoWrites.resize(mTopNStatsPerCategory);
477         userPackageSummaryStats->topNIoBlocked.resize(mTopNStatsPerCategory);
478         userPackageSummaryStats->topNMajorFaults.resize(mTopNStatsPerCategory);
479     }
480     for (const auto& curUidStats : uidStats) {
481         uid_t uid = curUidStats.uid();
482         addUidIoStats(curUidStats.ioStats.metrics, userPackageSummaryStats->totalIoStats);
483         userPackageSummaryStats->totalMajorFaults += curUidStats.procStats.totalMajorFaults;
484         if (filterPackages.empty()) {
485             cacheTopNIoStats(MetricType::READ_BYTES, curUidStats,
486                              &userPackageSummaryStats->topNIoReads);
487             cacheTopNIoStats(MetricType::WRITE_BYTES, curUidStats,
488                              &userPackageSummaryStats->topNIoWrites);
489             if (cacheTopNProcStats(IO_BLOCKED_TASKS_COUNT, curUidStats, mTopNStatsPerSubcategory,
490                                    &userPackageSummaryStats->topNIoBlocked)) {
491                 userPackageSummaryStats->taskCountByUid[uid] =
492                         curUidStats.procStats.totalTasksCount;
493             }
494             cacheTopNProcStats(MAJOR_FAULTS, curUidStats, mTopNStatsPerSubcategory,
495                                &userPackageSummaryStats->topNMajorFaults);
496             continue;
497         }
498         if (filterPackages.count(curUidStats.genericPackageName()) != 0) {
499             userPackageSummaryStats->topNIoReads.emplace_back(
500                     toUserPackageStats(MetricType::READ_BYTES, curUidStats));
501             userPackageSummaryStats->topNIoWrites.emplace_back(
502                     toUserPackageStats(MetricType::WRITE_BYTES, curUidStats));
503             userPackageSummaryStats->topNIoBlocked.emplace_back(
504                     toUserPackageStats(IO_BLOCKED_TASKS_COUNT, curUidStats,
505                                        mTopNStatsPerSubcategory));
506             userPackageSummaryStats->topNMajorFaults.emplace_back(
507                     toUserPackageStats(MAJOR_FAULTS, curUidStats, mTopNStatsPerSubcategory));
508             userPackageSummaryStats->taskCountByUid[uid] = curUidStats.procStats.totalTasksCount;
509         }
510     }
511     if (mLastMajorFaults != 0) {
512         int64_t increase = userPackageSummaryStats->totalMajorFaults - mLastMajorFaults;
513         userPackageSummaryStats->majorFaultsPercentChange =
514                 (static_cast<double>(increase) / static_cast<double>(mLastMajorFaults)) * 100.0;
515     }
516     mLastMajorFaults = userPackageSummaryStats->totalMajorFaults;
517 
518     const auto removeEmptyStats = [](std::vector<UserPackageStats>& userPackageStats) {
519         for (auto it = userPackageStats.begin(); it != userPackageStats.end(); ++it) {
520             /* std::monostate is the first alternative in the variant. When the variant is
521              * uninitialized, the index points to this alternative.
522              */
523             if (it->stats.index() == 0) {
524                 userPackageStats.erase(it, userPackageStats.end());
525                 break;
526             }
527         }
528     };
529     removeEmptyStats(userPackageSummaryStats->topNIoReads);
530     removeEmptyStats(userPackageSummaryStats->topNIoWrites);
531     removeEmptyStats(userPackageSummaryStats->topNIoBlocked);
532     removeEmptyStats(userPackageSummaryStats->topNMajorFaults);
533 }
534 
processProcStatLocked(const sp<ProcStat> & procStat,SystemSummaryStats * systemSummaryStats) const535 void IoPerfCollection::processProcStatLocked(const sp<ProcStat>& procStat,
536                                              SystemSummaryStats* systemSummaryStats) const {
537     const ProcStatInfo& procStatInfo = procStat->deltaStats();
538     systemSummaryStats->cpuIoWaitTime = procStatInfo.cpuStats.ioWaitTime;
539     systemSummaryStats->totalCpuTime = procStatInfo.totalCpuTime();
540     systemSummaryStats->ioBlockedProcessCount = procStatInfo.ioBlockedProcessCount;
541     systemSummaryStats->totalProcessCount = procStatInfo.totalProcessCount();
542 }
543 
544 }  // namespace watchdog
545 }  // namespace automotive
546 }  // namespace android
547