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