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