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