• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 int32_t kDefaultMaxUserSwitchEvents = 5;
51 constexpr const char kBootTimeCollectionTitle[] = "%s\nBoot-time performance report:\n%s\n";
52 constexpr const char kPeriodicCollectionTitle[] = "%s\nLast N minutes performance report:\n%s\n";
53 constexpr const char kUserSwitchCollectionTitle[] =
54         "%s\nUser-switch events performance report:\n%s\n";
55 constexpr const char kUserSwitchCollectionSubtitle[] = "Number of user switch events: %zu\n";
56 constexpr const char kWakeUpCollectionTitle[] = "%s\nWake-up performance report:\n%s\n";
57 constexpr const char kCustomCollectionTitle[] = "%s\nCustom performance data report:\n%s\n";
58 constexpr const char kUserSwitchEventTitle[] = "\nEvent %zu: From: %d To: %d\n%s\n";
59 constexpr const char kCollectionTitle[] =
60         "Collection duration: %.f seconds\nNumber of collections: %zu\n";
61 constexpr const char kRecordTitle[] = "\nCollection %zu: <%s>\n%s\n%s";
62 constexpr const char kCpuTimeTitle[] = "\nTop N CPU Times:\n%s\n";
63 constexpr const char kCpuTimeHeader[] =
64         "Android User ID, Package Name, CPU Time (ms), Percentage of total CPU time\n\tCommand, "
65         "CPU Time (ms), Percentage of UID's CPU Time\n";
66 constexpr const char kIoReadsTitle[] = "\nTop N Storage I/O Reads:\n%s\n";
67 constexpr const char kIoWritesTitle[] = "\nTop N Storage I/O Writes:\n%s\n";
68 constexpr const char kIoStatsHeader[] =
69         "Android User ID, Package Name, Foreground Bytes, Foreground Bytes %%, Foreground Fsync, "
70         "Foreground Fsync %%, Background Bytes, Background Bytes %%, Background Fsync, "
71         "Background Fsync %%\n";
72 constexpr const char kIoBlockedTitle[] = "\nTop N I/O waiting UIDs:\n%s\n";
73 constexpr const char kIoBlockedHeader[] =
74         "Android User ID, Package Name, Number of owned tasks waiting for I/O, Percentage of owned "
75         "tasks waiting for I/O\n\tCommand, Number of I/O waiting tasks, Percentage of UID's tasks "
76         "waiting for I/O\n";
77 constexpr const char kMajorPageFaultsTitle[] = "\nTop N major page faults:\n%s\n";
78 constexpr const char kMajorFaultsHeader[] =
79         "Android User ID, Package Name, Number of major page faults, Percentage of total major "
80         "page faults\n\tCommand, Number of major page faults, Percentage of UID's major page "
81         "faults\n";
82 constexpr const char kMajorFaultsSummary[] =
83         "Number of major page faults since last collection: %" PRIu64 "\n"
84         "Percentage of change in major page faults since last collection: %.2f%%\n";
85 
percentage(uint64_t numer,uint64_t denom)86 double percentage(uint64_t numer, uint64_t denom) {
87     return denom == 0 ? 0.0 : (static_cast<double>(numer) / static_cast<double>(denom)) * 100.0;
88 }
89 
addUidIoStats(const int64_t entry[][UID_STATES],int64_t total[][UID_STATES])90 void addUidIoStats(const int64_t entry[][UID_STATES], int64_t total[][UID_STATES]) {
91     const auto sum = [](int64_t lhs, int64_t rhs) -> int64_t {
92         return std::numeric_limits<int64_t>::max() - lhs > rhs
93                 ? lhs + rhs
94                 : std::numeric_limits<int64_t>::max();
95     };
96     total[READ_BYTES][FOREGROUND] =
97             sum(total[READ_BYTES][FOREGROUND], entry[READ_BYTES][FOREGROUND]);
98     total[READ_BYTES][BACKGROUND] =
99             sum(total[READ_BYTES][BACKGROUND], entry[READ_BYTES][BACKGROUND]);
100     total[WRITE_BYTES][FOREGROUND] =
101             sum(total[WRITE_BYTES][FOREGROUND], entry[WRITE_BYTES][FOREGROUND]);
102     total[WRITE_BYTES][BACKGROUND] =
103             sum(total[WRITE_BYTES][BACKGROUND], entry[WRITE_BYTES][BACKGROUND]);
104     total[FSYNC_COUNT][FOREGROUND] =
105             sum(total[FSYNC_COUNT][FOREGROUND], entry[FSYNC_COUNT][FOREGROUND]);
106     total[FSYNC_COUNT][BACKGROUND] =
107             sum(total[FSYNC_COUNT][BACKGROUND], entry[FSYNC_COUNT][BACKGROUND]);
108     return;
109 }
110 
toUserPackageStats(MetricType metricType,const UidStats & uidStats)111 UserPackageStats toUserPackageStats(MetricType metricType, const UidStats& uidStats) {
112     const UidIoStats& ioStats = uidStats.ioStats;
113     return UserPackageStats{
114             .uid = uidStats.uid(),
115             .genericPackageName = uidStats.genericPackageName(),
116             .stats = UserPackageStats::
117                     IoStats{.bytes = {ioStats.metrics[metricType][UidState::FOREGROUND],
118                                       ioStats.metrics[metricType][UidState::BACKGROUND]},
119                             .fsync =
120                                     {ioStats.metrics[MetricType::FSYNC_COUNT][UidState::FOREGROUND],
121                                      ioStats.metrics[MetricType::FSYNC_COUNT]
122                                                     [UidState::BACKGROUND]}},
123     };
124 }
125 
cacheTopNIoStats(MetricType metricType,const UidStats & uidStats,std::vector<UserPackageStats> * topNIoStats)126 void cacheTopNIoStats(MetricType metricType, const UidStats& uidStats,
127                       std::vector<UserPackageStats>* topNIoStats) {
128     if (metricType != MetricType::READ_BYTES && metricType != MetricType::WRITE_BYTES) {
129         return;
130     }
131     int64_t totalBytes = metricType == MetricType::READ_BYTES ? uidStats.ioStats.sumReadBytes()
132                                                               : uidStats.ioStats.sumWriteBytes();
133     if (totalBytes == 0) {
134         return;
135     }
136     for (auto it = topNIoStats->begin(); it != topNIoStats->end(); ++it) {
137         if (const auto* ioStats = std::get_if<UserPackageStats::IoStats>(&it->stats);
138             ioStats == nullptr || totalBytes > ioStats->totalBytes()) {
139             topNIoStats->emplace(it, toUserPackageStats(metricType, uidStats));
140             topNIoStats->pop_back();
141             break;
142         }
143     }
144     return;
145 }
146 
147 enum ProcStatType {
148     IO_BLOCKED_TASKS_COUNT = 0,
149     MAJOR_FAULTS,
150     CPU_TIME,
151     PROC_STAT_TYPES,
152 };
153 
cacheTopNProcessStats(ProcStatType procStatType,const ProcessStats & processStats,std::vector<UserPackageStats::ProcStats::ProcessValue> * topNProcesses)154 bool cacheTopNProcessStats(ProcStatType procStatType, const ProcessStats& processStats,
155                            std::vector<UserPackageStats::ProcStats::ProcessValue>* topNProcesses) {
156     uint64_t value = procStatType == CPU_TIME        ? processStats.cpuTimeMillis
157             : procStatType == IO_BLOCKED_TASKS_COUNT ? processStats.ioBlockedTasksCount
158                                                      : processStats.totalMajorFaults;
159     if (value == 0) {
160         return false;
161     }
162     for (auto it = topNProcesses->begin(); it != topNProcesses->end(); ++it) {
163         if (value > it->value) {
164             topNProcesses->emplace(it,
165                                    UserPackageStats::ProcStats::ProcessValue{
166                                            .comm = processStats.comm,
167                                            .value = value,
168                                    });
169             topNProcesses->pop_back();
170             return true;
171         }
172     }
173     return false;
174 }
175 
toUserPackageStats(ProcStatType procStatType,const UidStats & uidStats,int topNProcessCount)176 UserPackageStats toUserPackageStats(ProcStatType procStatType, const UidStats& uidStats,
177                                     int topNProcessCount) {
178     uint64_t value = procStatType == CPU_TIME        ? uidStats.cpuTimeMillis
179             : procStatType == IO_BLOCKED_TASKS_COUNT ? uidStats.procStats.ioBlockedTasksCount
180                                                      : uidStats.procStats.totalMajorFaults;
181     UserPackageStats userPackageStats = {
182             .uid = uidStats.uid(),
183             .genericPackageName = uidStats.genericPackageName(),
184             .stats = UserPackageStats::ProcStats{.value = value},
185     };
186     auto& procStats = std::get<UserPackageStats::ProcStats>(userPackageStats.stats);
187     procStats.topNProcesses.resize(topNProcessCount);
188     int cachedProcessCount = 0;
189     for (const auto& [_, processStats] : uidStats.procStats.processStatsByPid) {
190         if (cacheTopNProcessStats(procStatType, processStats, &procStats.topNProcesses)) {
191             ++cachedProcessCount;
192         }
193     }
194     if (cachedProcessCount < topNProcessCount) {
195         procStats.topNProcesses.erase(procStats.topNProcesses.begin() + cachedProcessCount,
196                                       procStats.topNProcesses.end());
197     }
198     return userPackageStats;
199 }
200 
cacheTopNProcStats(ProcStatType procStatType,const UidStats & uidStats,int topNProcessCount,std::vector<UserPackageStats> * topNProcStats)201 bool cacheTopNProcStats(ProcStatType procStatType, const UidStats& uidStats, int topNProcessCount,
202                         std::vector<UserPackageStats>* topNProcStats) {
203     uint64_t value = procStatType == CPU_TIME        ? uidStats.cpuTimeMillis
204             : procStatType == IO_BLOCKED_TASKS_COUNT ? uidStats.procStats.ioBlockedTasksCount
205                                                      : uidStats.procStats.totalMajorFaults;
206     if (value == 0) {
207         return false;
208     }
209     for (auto it = topNProcStats->begin(); it != topNProcStats->end(); ++it) {
210         if (const auto* procStats = std::get_if<UserPackageStats::ProcStats>(&it->stats);
211             procStats == nullptr || value > procStats->value) {
212             topNProcStats->emplace(it,
213                                    toUserPackageStats(procStatType, uidStats, topNProcessCount));
214             topNProcStats->pop_back();
215             return true;
216         }
217     }
218     return false;
219 }
220 
checkDataCollectors(const sp<UidStatsCollectorInterface> & uidStatsCollector,const sp<ProcStatCollectorInterface> & procStatCollector)221 Result<void> checkDataCollectors(const sp<UidStatsCollectorInterface>& uidStatsCollector,
222                                  const sp<ProcStatCollectorInterface>& procStatCollector) {
223     if (uidStatsCollector != nullptr && procStatCollector != nullptr) {
224         return {};
225     }
226     std::string error;
227     if (uidStatsCollector == nullptr) {
228         error = "Per-UID stats collector must not be null";
229     }
230     if (procStatCollector == nullptr) {
231         StringAppendF(&error, "%s%s", error.empty() ? "" : ", ",
232                       "Proc stats collector must not be null");
233     }
234     return Error() << "Invalid data collectors: " << error;
235 }
236 
237 }  // namespace
238 
toString(MetricType metricsType,const int64_t totalIoStats[][UID_STATES]) const239 std::string UserPackageStats::toString(MetricType metricsType,
240                                        const int64_t totalIoStats[][UID_STATES]) const {
241     std::string buffer;
242     StringAppendF(&buffer, "%" PRIu32 ", %s", multiuser_get_user_id(uid),
243                   genericPackageName.c_str());
244     const auto& ioStats = std::get<UserPackageStats::IoStats>(stats);
245     for (int i = 0; i < UID_STATES; ++i) {
246         StringAppendF(&buffer, ", %" PRIi64 ", %.2f%%, %" PRIi64 ", %.2f%%", ioStats.bytes[i],
247                       percentage(ioStats.bytes[i], totalIoStats[metricsType][i]), ioStats.fsync[i],
248                       percentage(ioStats.fsync[i], totalIoStats[FSYNC_COUNT][i]));
249     }
250     StringAppendF(&buffer, "\n");
251     return buffer;
252 }
253 
toString(int64_t totalValue) const254 std::string UserPackageStats::toString(int64_t totalValue) const {
255     std::string buffer;
256     const auto& procStats = std::get<UserPackageStats::ProcStats>(stats);
257     StringAppendF(&buffer, "%" PRIu32 ", %s, %" PRIu64 ", %.2f%%\n", multiuser_get_user_id(uid),
258                   genericPackageName.c_str(), procStats.value,
259                   percentage(procStats.value, totalValue));
260     for (const auto& processValue : procStats.topNProcesses) {
261         StringAppendF(&buffer, "\t%s, %" PRIu64 ", %.2f%%\n", processValue.comm.c_str(),
262                       processValue.value, percentage(processValue.value, procStats.value));
263     }
264     return buffer;
265 }
266 
toString() const267 std::string UserPackageSummaryStats::toString() const {
268     std::string buffer;
269     if (!topNCpuTimes.empty()) {
270         StringAppendF(&buffer, kCpuTimeTitle, std::string(16, '-').c_str());
271         StringAppendF(&buffer, kCpuTimeHeader);
272         for (const auto& stats : topNCpuTimes) {
273             StringAppendF(&buffer, "%s", stats.toString(totalCpuTimeMillis).c_str());
274         }
275     }
276     if (!topNIoReads.empty()) {
277         StringAppendF(&buffer, kIoReadsTitle, std::string(24, '-').c_str());
278         StringAppendF(&buffer, kIoStatsHeader);
279         for (const auto& stats : topNIoReads) {
280             StringAppendF(&buffer, "%s",
281                           stats.toString(MetricType::READ_BYTES, totalIoStats).c_str());
282         }
283     }
284     if (!topNIoWrites.empty()) {
285         StringAppendF(&buffer, kIoWritesTitle, std::string(25, '-').c_str());
286         StringAppendF(&buffer, kIoStatsHeader);
287         for (const auto& stats : topNIoWrites) {
288             StringAppendF(&buffer, "%s",
289                           stats.toString(MetricType::WRITE_BYTES, totalIoStats).c_str());
290         }
291     }
292     if (!topNIoBlocked.empty()) {
293         StringAppendF(&buffer, kIoBlockedTitle, std::string(23, '-').c_str());
294         StringAppendF(&buffer, kIoBlockedHeader);
295         for (const auto& stats : topNIoBlocked) {
296             const auto it = taskCountByUid.find(stats.uid);
297             if (it == taskCountByUid.end()) {
298                 continue;
299             }
300             StringAppendF(&buffer, "%s", stats.toString(it->second).c_str());
301         }
302     }
303     if (!topNMajorFaults.empty()) {
304         StringAppendF(&buffer, kMajorPageFaultsTitle, std::string(24, '-').c_str());
305         StringAppendF(&buffer, kMajorFaultsHeader);
306         for (const auto& stats : topNMajorFaults) {
307             StringAppendF(&buffer, "%s", stats.toString(totalMajorFaults).c_str());
308         }
309         StringAppendF(&buffer, kMajorFaultsSummary, totalMajorFaults, majorFaultsPercentChange);
310     }
311     return buffer;
312 }
313 
toString() const314 std::string SystemSummaryStats::toString() const {
315     std::string buffer;
316     StringAppendF(&buffer, "Total CPU time (ms): %" PRIu64 "\n", totalCpuTimeMillis);
317     StringAppendF(&buffer, "Total idle CPU time (ms)/percent: %" PRIu64 " / %.2f%%\n",
318                   cpuIdleTimeMillis, percentage(cpuIdleTimeMillis, totalCpuTimeMillis));
319     StringAppendF(&buffer, "CPU I/O wait time (ms)/percent: %" PRIu64 " / %.2f%%\n",
320                   cpuIoWaitTimeMillis, percentage(cpuIoWaitTimeMillis, totalCpuTimeMillis));
321     StringAppendF(&buffer, "Number of context switches: %" PRIu64 "\n", contextSwitchesCount);
322     StringAppendF(&buffer, "Number of I/O blocked processes/percent: %" PRIu32 " / %.2f%%\n",
323                   ioBlockedProcessCount, percentage(ioBlockedProcessCount, totalProcessCount));
324     return buffer;
325 }
326 
toString() const327 std::string PerfStatsRecord::toString() const {
328     std::string buffer;
329     StringAppendF(&buffer, "%s%s", systemSummaryStats.toString().c_str(),
330                   userPackageSummaryStats.toString().c_str());
331     return buffer;
332 }
333 
toString() const334 std::string CollectionInfo::toString() const {
335     if (records.empty()) {
336         return kEmptyCollectionMessage;
337     }
338     std::string buffer;
339     double duration = difftime(records.back().time, records.front().time);
340     StringAppendF(&buffer, kCollectionTitle, duration, records.size());
341     for (size_t i = 0; i < records.size(); ++i) {
342         const auto& record = records[i];
343         std::stringstream timestamp;
344         timestamp << std::put_time(std::localtime(&record.time), "%c %Z");
345         StringAppendF(&buffer, kRecordTitle, i, timestamp.str().c_str(),
346                       std::string(45, '=').c_str(), record.toString().c_str());
347     }
348     return buffer;
349 }
350 
init()351 Result<void> IoPerfCollection::init() {
352     Mutex::Autolock lock(mMutex);
353     if (mTopNStatsPerCategory != 0 || mTopNStatsPerSubcategory != 0) {
354         return Error() << "Cannot initialize " << name() << " more than once";
355     }
356     mTopNStatsPerCategory = static_cast<int>(
357             sysprop::topNStatsPerCategory().value_or(kDefaultTopNStatsPerCategory));
358     mTopNStatsPerSubcategory = static_cast<int>(
359             sysprop::topNStatsPerSubcategory().value_or(kDefaultTopNStatsPerSubcategory));
360     mMaxUserSwitchEvents = static_cast<size_t>(
361             sysprop::maxUserSwitchEvents().value_or(kDefaultMaxUserSwitchEvents));
362     size_t periodicCollectionBufferSize = static_cast<size_t>(
363             sysprop::periodicCollectionBufferSize().value_or(kDefaultPeriodicCollectionBufferSize));
364     mBoottimeCollection = {
365             .maxCacheSize = std::numeric_limits<std::size_t>::max(),
366             .records = {},
367     };
368     mPeriodicCollection = {
369             .maxCacheSize = periodicCollectionBufferSize,
370             .records = {},
371     };
372     mWakeUpCollection = {
373             .maxCacheSize = std::numeric_limits<std::size_t>::max(),
374             .records = {},
375     };
376     mCustomCollection = {
377             .maxCacheSize = std::numeric_limits<std::size_t>::max(),
378             .records = {},
379     };
380     return {};
381 }
382 
terminate()383 void IoPerfCollection::terminate() {
384     Mutex::Autolock lock(mMutex);
385 
386     ALOGW("Terminating %s", name().c_str());
387 
388     mBoottimeCollection.records.clear();
389     mBoottimeCollection = {};
390 
391     mPeriodicCollection.records.clear();
392     mPeriodicCollection = {};
393 
394     mUserSwitchCollections.clear();
395 
396     mCustomCollection.records.clear();
397     mCustomCollection = {};
398 }
399 
onDump(int fd) const400 Result<void> IoPerfCollection::onDump(int fd) const {
401     Mutex::Autolock lock(mMutex);
402     if (!WriteStringToFd(StringPrintf(kBootTimeCollectionTitle, std::string(75, '-').c_str(),
403                                       std::string(33, '=').c_str()),
404                          fd) ||
405         !WriteStringToFd(mBoottimeCollection.toString(), fd)) {
406         return Error(FAILED_TRANSACTION) << "Failed to dump the boot-time collection report.";
407     }
408     if (!WriteStringToFd(StringPrintf(kWakeUpCollectionTitle, std::string(75, '-').c_str(),
409                                       std::string(27, '=').c_str()),
410                          fd) ||
411         !WriteStringToFd(mWakeUpCollection.toString(), fd)) {
412         return Error(FAILED_TRANSACTION) << "Failed to dump the wake-up collection report.";
413     }
414     if (const auto& result = onUserSwitchCollectionDump(fd); !result.ok()) {
415         return result.error();
416     }
417     if (!WriteStringToFd(StringPrintf(kPeriodicCollectionTitle, std::string(75, '-').c_str(),
418                                       std::string(38, '=').c_str()),
419                          fd) ||
420         !WriteStringToFd(mPeriodicCollection.toString(), fd)) {
421         return Error(FAILED_TRANSACTION) << "Failed to dump the periodic collection report.";
422     }
423     return {};
424 }
425 
onCustomCollectionDump(int fd)426 Result<void> IoPerfCollection::onCustomCollectionDump(int fd) {
427     if (fd == -1) {
428         // Custom collection ends so clear the cache.
429         mCustomCollection.records.clear();
430         mCustomCollection = {
431                 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
432                 .records = {},
433         };
434         return {};
435     }
436 
437     if (!WriteStringToFd(StringPrintf(kCustomCollectionTitle, std::string(75, '-').c_str(),
438                                       std::string(75, '-').c_str()),
439                          fd) ||
440         !WriteStringToFd(mCustomCollection.toString(), fd)) {
441         return Error(FAILED_TRANSACTION) << "Failed to write custom I/O collection report.";
442     }
443 
444     return {};
445 }
446 
onSystemStartup()447 Result<void> IoPerfCollection::onSystemStartup() {
448     Mutex::Autolock lock(mMutex);
449     mBoottimeCollection.records.clear();
450     mWakeUpCollection.records.clear();
451     return {};
452 }
453 
onBoottimeCollection(time_t time,const wp<UidStatsCollectorInterface> & uidStatsCollector,const wp<ProcStatCollectorInterface> & procStatCollector)454 Result<void> IoPerfCollection::onBoottimeCollection(
455         time_t time, const wp<UidStatsCollectorInterface>& uidStatsCollector,
456         const wp<ProcStatCollectorInterface>& procStatCollector) {
457     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
458     const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
459     auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
460     if (!result.ok()) {
461         return result;
462     }
463     Mutex::Autolock lock(mMutex);
464     return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
465                          procStatCollectorSp, &mBoottimeCollection);
466 }
467 
onPeriodicCollection(time_t time,SystemState systemState,const wp<UidStatsCollectorInterface> & uidStatsCollector,const wp<ProcStatCollectorInterface> & procStatCollector)468 Result<void> IoPerfCollection::onPeriodicCollection(
469         time_t time, [[maybe_unused]] SystemState systemState,
470         const wp<UidStatsCollectorInterface>& uidStatsCollector,
471         const wp<ProcStatCollectorInterface>& procStatCollector) {
472     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
473     const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
474     auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
475     if (!result.ok()) {
476         return result;
477     }
478     Mutex::Autolock lock(mMutex);
479     return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
480                          procStatCollectorSp, &mPeriodicCollection);
481 }
482 
onUserSwitchCollection(time_t time,userid_t from,userid_t to,const android::wp<UidStatsCollectorInterface> & uidStatsCollector,const android::wp<ProcStatCollectorInterface> & procStatCollector)483 Result<void> IoPerfCollection::onUserSwitchCollection(
484         time_t time, userid_t from, userid_t to,
485         const android::wp<UidStatsCollectorInterface>& uidStatsCollector,
486         const android::wp<ProcStatCollectorInterface>& procStatCollector) {
487     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
488     const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
489     auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
490     if (!result.ok()) {
491         return result;
492     }
493     Mutex::Autolock lock(mMutex);
494     if (mUserSwitchCollections.empty() || mUserSwitchCollections.back().from != from ||
495         mUserSwitchCollections.back().to != to) {
496         UserSwitchCollectionInfo userSwitchCollection = {
497                 {
498                         .maxCacheSize = std::numeric_limits<std::size_t>::max(),
499                         .records = {},
500                 },
501                 .from = from,
502                 .to = to,
503         };
504         mUserSwitchCollections.push_back(userSwitchCollection);
505     }
506     if (mUserSwitchCollections.size() > mMaxUserSwitchEvents) {
507         mUserSwitchCollections.erase(mUserSwitchCollections.begin());
508     }
509     return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
510                          procStatCollectorSp, &mUserSwitchCollections.back());
511 }
512 
onWakeUpCollection(time_t time,const wp<UidStatsCollectorInterface> & uidStatsCollector,const wp<ProcStatCollectorInterface> & procStatCollector)513 Result<void> IoPerfCollection::onWakeUpCollection(
514         time_t time, const wp<UidStatsCollectorInterface>& uidStatsCollector,
515         const wp<ProcStatCollectorInterface>& procStatCollector) {
516     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
517     const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
518     auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
519     if (!result.ok()) {
520         return result;
521     }
522     Mutex::Autolock lock(mMutex);
523     return processLocked(time, std::unordered_set<std::string>(), uidStatsCollectorSp,
524                          procStatCollectorSp, &mWakeUpCollection);
525 }
526 
onCustomCollection(time_t time,SystemState systemState,const std::unordered_set<std::string> & filterPackages,const wp<UidStatsCollectorInterface> & uidStatsCollector,const wp<ProcStatCollectorInterface> & procStatCollector)527 Result<void> IoPerfCollection::onCustomCollection(
528         time_t time, [[maybe_unused]] SystemState systemState,
529         const std::unordered_set<std::string>& filterPackages,
530         const wp<UidStatsCollectorInterface>& uidStatsCollector,
531         const wp<ProcStatCollectorInterface>& procStatCollector) {
532     const sp<UidStatsCollectorInterface> uidStatsCollectorSp = uidStatsCollector.promote();
533     const sp<ProcStatCollectorInterface> procStatCollectorSp = procStatCollector.promote();
534     auto result = checkDataCollectors(uidStatsCollectorSp, procStatCollectorSp);
535     if (!result.ok()) {
536         return result;
537     }
538     Mutex::Autolock lock(mMutex);
539     return processLocked(time, filterPackages, uidStatsCollectorSp, procStatCollectorSp,
540                          &mCustomCollection);
541 }
542 
processLocked(time_t time,const std::unordered_set<std::string> & filterPackages,const sp<UidStatsCollectorInterface> & uidStatsCollector,const sp<ProcStatCollectorInterface> & procStatCollector,CollectionInfo * collectionInfo)543 Result<void> IoPerfCollection::processLocked(
544         time_t time, const std::unordered_set<std::string>& filterPackages,
545         const sp<UidStatsCollectorInterface>& uidStatsCollector,
546         const sp<ProcStatCollectorInterface>& procStatCollector, CollectionInfo* collectionInfo) {
547     if (collectionInfo->maxCacheSize == 0) {
548         return Error() << "Maximum cache size cannot be 0";
549     }
550     PerfStatsRecord record{
551             .time = time,
552     };
553     processUidStatsLocked(filterPackages, uidStatsCollector, &record.userPackageSummaryStats);
554     processProcStatLocked(procStatCollector, &record.systemSummaryStats);
555     record.userPackageSummaryStats.totalCpuTimeMillis =
556             record.systemSummaryStats.totalCpuTimeMillis;
557     if (collectionInfo->records.size() > collectionInfo->maxCacheSize) {
558         collectionInfo->records.erase(collectionInfo->records.begin());  // Erase the oldest record.
559     }
560     collectionInfo->records.emplace_back(record);
561     return {};
562 }
563 
processUidStatsLocked(const std::unordered_set<std::string> & filterPackages,const sp<UidStatsCollectorInterface> & uidStatsCollector,UserPackageSummaryStats * userPackageSummaryStats)564 void IoPerfCollection::processUidStatsLocked(
565         const std::unordered_set<std::string>& filterPackages,
566         const sp<UidStatsCollectorInterface>& uidStatsCollector,
567         UserPackageSummaryStats* userPackageSummaryStats) {
568     const std::vector<UidStats>& uidStats = uidStatsCollector->deltaStats();
569     if (uidStats.empty()) {
570         return;
571     }
572     if (filterPackages.empty()) {
573         userPackageSummaryStats->topNCpuTimes.resize(mTopNStatsPerCategory);
574         userPackageSummaryStats->topNIoReads.resize(mTopNStatsPerCategory);
575         userPackageSummaryStats->topNIoWrites.resize(mTopNStatsPerCategory);
576         userPackageSummaryStats->topNIoBlocked.resize(mTopNStatsPerCategory);
577         userPackageSummaryStats->topNMajorFaults.resize(mTopNStatsPerCategory);
578     }
579     for (const auto& curUidStats : uidStats) {
580         uid_t uid = curUidStats.uid();
581         addUidIoStats(curUidStats.ioStats.metrics, userPackageSummaryStats->totalIoStats);
582         userPackageSummaryStats->totalMajorFaults += curUidStats.procStats.totalMajorFaults;
583         if (filterPackages.empty()) {
584             cacheTopNIoStats(MetricType::READ_BYTES, curUidStats,
585                              &userPackageSummaryStats->topNIoReads);
586             cacheTopNIoStats(MetricType::WRITE_BYTES, curUidStats,
587                              &userPackageSummaryStats->topNIoWrites);
588             cacheTopNProcStats(CPU_TIME, curUidStats, mTopNStatsPerSubcategory,
589                                &userPackageSummaryStats->topNCpuTimes);
590             if (cacheTopNProcStats(IO_BLOCKED_TASKS_COUNT, curUidStats, mTopNStatsPerSubcategory,
591                                    &userPackageSummaryStats->topNIoBlocked)) {
592                 userPackageSummaryStats->taskCountByUid[uid] =
593                         curUidStats.procStats.totalTasksCount;
594             }
595             cacheTopNProcStats(MAJOR_FAULTS, curUidStats, mTopNStatsPerSubcategory,
596                                &userPackageSummaryStats->topNMajorFaults);
597             continue;
598         }
599         if (filterPackages.count(curUidStats.genericPackageName()) != 0) {
600             userPackageSummaryStats->topNIoReads.emplace_back(
601                     toUserPackageStats(MetricType::READ_BYTES, curUidStats));
602             userPackageSummaryStats->topNIoWrites.emplace_back(
603                     toUserPackageStats(MetricType::WRITE_BYTES, curUidStats));
604             userPackageSummaryStats->topNCpuTimes.emplace_back(
605                     toUserPackageStats(CPU_TIME, curUidStats, mTopNStatsPerSubcategory));
606             userPackageSummaryStats->topNIoBlocked.emplace_back(
607                     toUserPackageStats(IO_BLOCKED_TASKS_COUNT, curUidStats,
608                                        mTopNStatsPerSubcategory));
609             userPackageSummaryStats->topNMajorFaults.emplace_back(
610                     toUserPackageStats(MAJOR_FAULTS, curUidStats, mTopNStatsPerSubcategory));
611             userPackageSummaryStats->taskCountByUid[uid] = curUidStats.procStats.totalTasksCount;
612         }
613     }
614     if (mLastMajorFaults != 0) {
615         int64_t increase = userPackageSummaryStats->totalMajorFaults - mLastMajorFaults;
616         userPackageSummaryStats->majorFaultsPercentChange =
617                 (static_cast<double>(increase) / static_cast<double>(mLastMajorFaults)) * 100.0;
618     }
619     mLastMajorFaults = userPackageSummaryStats->totalMajorFaults;
620 
621     const auto removeEmptyStats = [](std::vector<UserPackageStats>& userPackageStats) {
622         for (auto it = userPackageStats.begin(); it != userPackageStats.end(); ++it) {
623             /* std::monostate is the first alternative in the variant. When the variant is
624              * uninitialized, the index points to this alternative.
625              */
626             if (it->stats.index() == 0) {
627                 userPackageStats.erase(it, userPackageStats.end());
628                 break;
629             }
630         }
631     };
632     removeEmptyStats(userPackageSummaryStats->topNCpuTimes);
633     removeEmptyStats(userPackageSummaryStats->topNIoReads);
634     removeEmptyStats(userPackageSummaryStats->topNIoWrites);
635     removeEmptyStats(userPackageSummaryStats->topNIoBlocked);
636     removeEmptyStats(userPackageSummaryStats->topNMajorFaults);
637 }
638 
processProcStatLocked(const sp<ProcStatCollectorInterface> & procStatCollector,SystemSummaryStats * systemSummaryStats) const639 void IoPerfCollection::processProcStatLocked(
640         const sp<ProcStatCollectorInterface>& procStatCollector,
641         SystemSummaryStats* systemSummaryStats) const {
642     const ProcStatInfo& procStatInfo = procStatCollector->deltaStats();
643     systemSummaryStats->cpuIoWaitTimeMillis = procStatInfo.cpuStats.ioWaitTimeMillis;
644     systemSummaryStats->cpuIdleTimeMillis = procStatInfo.cpuStats.idleTimeMillis;
645     systemSummaryStats->totalCpuTimeMillis = procStatInfo.totalCpuTimeMillis();
646     systemSummaryStats->contextSwitchesCount = procStatInfo.contextSwitchesCount;
647     systemSummaryStats->ioBlockedProcessCount = procStatInfo.ioBlockedProcessCount;
648     systemSummaryStats->totalProcessCount = procStatInfo.totalProcessCount();
649 }
650 
onUserSwitchCollectionDump(int fd) const651 Result<void> IoPerfCollection::onUserSwitchCollectionDump(int fd) const {
652     if (!WriteStringToFd(StringPrintf(kUserSwitchCollectionTitle, std::string(75, '-').c_str(),
653                                       std::string(38, '=').c_str()),
654                          fd)) {
655         return Error(FAILED_TRANSACTION) << "Failed to dump the user-switch collection report.";
656     }
657     if (!mUserSwitchCollections.empty() &&
658         !WriteStringToFd(StringPrintf(kUserSwitchCollectionSubtitle, mUserSwitchCollections.size()),
659                          fd)) {
660         return Error(FAILED_TRANSACTION) << "Failed to dump the user-switch collection report.";
661     }
662     if (mUserSwitchCollections.empty() && !WriteStringToFd(kEmptyCollectionMessage, fd)) {
663         return Error(FAILED_TRANSACTION) << "Failed to dump the user-switch collection report.";
664     }
665     for (size_t i = 0; i < mUserSwitchCollections.size(); ++i) {
666         const auto& userSwitchCollection = mUserSwitchCollections[i];
667         if (!WriteStringToFd(StringPrintf(kUserSwitchEventTitle, i, userSwitchCollection.from,
668                                           userSwitchCollection.to, std::string(26, '=').c_str()),
669                              fd) ||
670             !WriteStringToFd(userSwitchCollection.toString(), fd)) {
671             return Error(FAILED_TRANSACTION) << "Failed to dump the user-switch collection report.";
672         }
673     }
674     return {};
675 }
676 
677 }  // namespace watchdog
678 }  // namespace automotive
679 }  // namespace android
680