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