blob: 2761b34b4d09494fed7a2b209c2b4a0738163c78 [file] [log] [blame]
/*
* Copyright (c) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "carwatchdogd"
#define DEBUG false // STOPSHIP if true.
#include "WatchdogPerfService.h"
#include <WatchdogProperties.sysprop.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <log/log.h>
#include <processgroup/sched_policy.h>
#include <pthread.h>
#include <iterator>
#include <vector>
namespace android {
namespace automotive {
namespace watchdog {
using ::android::sp;
using ::android::String16;
using ::android::String8;
using ::android::automotive::watchdog::internal::PowerCycle;
using ::android::base::Error;
using ::android::base::Join;
using ::android::base::ParseUint;
using ::android::base::Result;
using ::android::base::Split;
using ::android::base::StringAppendF;
using ::android::base::StringPrintf;
using ::android::base::WriteStringToFd;
namespace {
// Minimum required collection interval between subsequent collections.
const std::chrono::nanoseconds kMinEventInterval = 1s;
const std::chrono::seconds kDefaultBoottimeCollectionInterval = 1s;
const std::chrono::seconds kDefaultPeriodicCollectionInterval = 20s;
const std::chrono::seconds kDefaultPeriodicMonitorInterval = 5s;
const std::chrono::nanoseconds kCustomCollectionInterval = 10s;
const std::chrono::nanoseconds kCustomCollectionDuration = 30min;
constexpr const char* kServiceName = "WatchdogPerfService";
static const std::string kDumpMajorDelimiter = std::string(100, '-') + "\n"; // NOLINT
constexpr const char* kHelpText =
"\n%s dump options:\n"
"%s: Starts custom performance data collection. Customize the collection behavior with "
"the following optional arguments:\n"
"\t%s <seconds>: Modifies the collection interval. Default behavior is to collect once "
"every %lld seconds.\n"
"\t%s <seconds>: Modifies the maximum collection duration. Default behavior is to collect "
"until %ld minutes before automatically stopping the custom collection and discarding "
"the collected data.\n"
"\t%s <package name>,<package name>,...: Comma-separated value containing package names. "
"When provided, the results are filtered only to the provided package names. Default "
"behavior is to list the results for the top N packages.\n"
"%s: Stops custom performance data collection and generates a dump of "
"the collection report.\n\n"
"When no options are specified, the carwatchdog report contains the performance data "
"collected during boot-time and over the last few minutes before the report generation.\n";
Result<std::chrono::seconds> parseSecondsFlag(const Vector<String16>& args, size_t pos) {
if (args.size() <= pos) {
return Error() << "Value not provided";
}
uint64_t value;
if (std::string strValue = std::string(String8(args[pos]).string());
!ParseUint(strValue, &value)) {
return Error() << "Invalid value " << strValue << ", must be an integer";
}
return std::chrono::seconds(value);
}
constexpr const char* toString(std::variant<EventType, SwitchMessage> what) {
return std::visit(
[&](const auto& v) -> const char* {
switch (static_cast<int>(v)) {
case EventType::INIT:
return "INIT";
case EventType::TERMINATED:
return "TERMINATED";
case EventType::BOOT_TIME_COLLECTION:
return "BOOT_TIME_COLLECTION";
case EventType::PERIODIC_COLLECTION:
return "PERIODIC_COLLECTION";
case EventType::CUSTOM_COLLECTION:
return "CUSTOM_COLLECTION";
case EventType::PERIODIC_MONITOR:
return "PERIODIC_MONITOR";
case EventType::LAST_EVENT:
return "LAST_EVENT";
case SwitchMessage::END_BOOTTIME_COLLECTION:
return "END_BOOTTIME_COLLECTION";
case SwitchMessage::END_CUSTOM_COLLECTION:
return "END_CUSTOM_COLLECTION";
default:
return "INVALID_EVENT_OR_SWITCH_MESSAGE";
}
},
what);
}
constexpr const char* toString(SystemState systemState) {
switch (systemState) {
case SystemState::NORMAL_MODE:
return "NORMAL_MODE";
case SystemState::GARAGE_MODE:
return "GARAGE_MODE";
default:
return "UNKNOWN MODE";
}
}
} // namespace
std::string WatchdogPerfService::EventMetadata::toString() const {
std::string buffer;
const auto intervalInSecs = std::chrono::duration_cast<std::chrono::seconds>(interval).count();
StringAppendF(&buffer, "Event interval: %lld second%s\n", intervalInSecs,
((intervalInSecs > 1) ? "s" : ""));
if (!filterPackages.empty()) {
std::vector<std::string> packages(filterPackages.begin(), filterPackages.end());
StringAppendF(&buffer, "Filtered results to packages: %s\n", Join(packages, ", ").c_str());
}
return buffer;
}
Result<void> WatchdogPerfService::registerDataProcessor(sp<IDataProcessorInterface> processor) {
if (processor == nullptr) {
return Error() << "Must provide a valid data processor";
}
if (const auto result = processor->init(); !result.ok()) {
return Error() << "Failed to initialize " << processor->name().c_str() << ": "
<< result.error().message();
}
Mutex::Autolock lock(mMutex);
mDataProcessors.push_back(processor);
if (DEBUG) {
ALOGD("Successfully registered %s to %s", processor->name().c_str(), kServiceName);
}
return {};
}
Result<void> WatchdogPerfService::start() {
{
Mutex::Autolock lock(mMutex);
if (mCurrCollectionEvent != EventType::INIT || mCollectionThread.joinable()) {
return Error(INVALID_OPERATION) << "Cannot start " << kServiceName << " more than once";
}
std::chrono::nanoseconds boottimeCollectionInterval =
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(sysprop::boottimeCollectionInterval().value_or(
kDefaultBoottimeCollectionInterval.count())));
std::chrono::nanoseconds periodicCollectionInterval =
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(sysprop::periodicCollectionInterval().value_or(
kDefaultPeriodicCollectionInterval.count())));
std::chrono::nanoseconds periodicMonitorInterval =
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(sysprop::periodicMonitorInterval().value_or(
kDefaultPeriodicMonitorInterval.count())));
mBoottimeCollection = {
.eventType = EventType::BOOT_TIME_COLLECTION,
.interval = boottimeCollectionInterval,
.lastUptime = 0,
};
mPeriodicCollection = {
.eventType = EventType::PERIODIC_COLLECTION,
.interval = periodicCollectionInterval,
.lastUptime = 0,
};
mPeriodicMonitor = {
.eventType = EventType::PERIODIC_MONITOR,
.interval = periodicMonitorInterval,
.lastUptime = 0,
};
if (mDataProcessors.empty()) {
ALOGE("Terminating %s: No data processor is registered", kServiceName);
mCurrCollectionEvent = EventType::TERMINATED;
return Error() << "No data processor is registered";
}
}
mCollectionThread = std::thread([&]() {
{
Mutex::Autolock lock(mMutex);
if (EventType expected = EventType::INIT; mCurrCollectionEvent != expected) {
ALOGE("Skipping performance data collection as the current collection event "
"%s != %s",
toString(mCurrCollectionEvent), toString(expected));
return;
}
mCurrCollectionEvent = EventType::BOOT_TIME_COLLECTION;
mBoottimeCollection.lastUptime = mHandlerLooper->now();
mHandlerLooper->setLooper(Looper::prepare(/*opts=*/0));
mHandlerLooper->sendMessage(this, EventType::BOOT_TIME_COLLECTION);
}
if (set_sched_policy(0, SP_BACKGROUND) != 0) {
ALOGW("Failed to set background scheduling priority to %s thread", kServiceName);
}
if (int result = pthread_setname_np(pthread_self(), "WatchdogPerfSvc"); result != 0) {
ALOGE("Failed to set %s thread name: %d", kServiceName, result);
}
ALOGI("Starting %s performance data collection", toString(mCurrCollectionEvent));
bool isCollectionActive = true;
/*
* Loop until the collection is not active -- performance collection runs on this thread in
* a handler.
*/
while (isCollectionActive) {
mHandlerLooper->pollAll(/*timeoutMillis=*/-1);
Mutex::Autolock lock(mMutex);
isCollectionActive = mCurrCollectionEvent != EventType::TERMINATED;
}
});
return {};
}
void WatchdogPerfService::terminate() {
{
Mutex::Autolock lock(mMutex);
if (mCurrCollectionEvent == EventType::TERMINATED) {
ALOGE("%s was terminated already", kServiceName);
return;
}
ALOGE("Terminating %s as carwatchdog is terminating", kServiceName);
if (mCurrCollectionEvent != EventType::INIT) {
/*
* Looper runs only after EventType::TNIT has completed so remove looper messages
* and wake the looper only when the current collection has changed from INIT.
*/
mHandlerLooper->removeMessages(this);
mHandlerLooper->wake();
}
for (const auto& processor : mDataProcessors) {
processor->terminate();
}
mCurrCollectionEvent = EventType::TERMINATED;
}
if (mCollectionThread.joinable()) {
mCollectionThread.join();
if (DEBUG) {
ALOGD("%s collection thread terminated", kServiceName);
}
}
}
void WatchdogPerfService::setSystemState(SystemState systemState) {
Mutex::Autolock lock(mMutex);
if (mSystemState != systemState) {
ALOGI("%s switching from %s to %s", kServiceName, toString(mSystemState),
toString(systemState));
}
mSystemState = systemState;
}
Result<void> WatchdogPerfService::onBootFinished() {
Mutex::Autolock lock(mMutex);
if (EventType expected = EventType::BOOT_TIME_COLLECTION; mCurrCollectionEvent != expected) {
/*
* This case happens when either the WatchdogPerfService has prematurely terminated before
* boot complete notification is received or multiple boot complete notifications are
* received. In either case don't return error as this will lead to runtime exception and
* cause system to boot loop.
*/
ALOGE("Current performance data collection event %s != %s", toString(mCurrCollectionEvent),
toString(expected));
return {};
}
mBoottimeCollection.lastUptime = mHandlerLooper->now();
mHandlerLooper->removeMessages(this);
mHandlerLooper->sendMessage(this, SwitchMessage::END_BOOTTIME_COLLECTION);
if (DEBUG) {
ALOGD("Boot-time event finished");
}
return {};
}
Result<void> WatchdogPerfService::onCustomCollection(int fd, const Vector<String16>& args) {
if (args.empty()) {
return Error(BAD_VALUE) << "No custom collection dump arguments";
}
if (args[0] == String16(kStartCustomCollectionFlag)) {
if (args.size() > 7) {
return Error(BAD_VALUE) << "Number of arguments to start custom performance data "
<< "collection cannot exceed 7";
}
std::chrono::nanoseconds interval = kCustomCollectionInterval;
std::chrono::nanoseconds maxDuration = kCustomCollectionDuration;
std::unordered_set<std::string> filterPackages;
for (size_t i = 1; i < args.size(); ++i) {
if (args[i] == String16(kIntervalFlag)) {
const auto& result = parseSecondsFlag(args, i + 1);
if (!result.ok()) {
return Error(BAD_VALUE)
<< "Failed to parse " << kIntervalFlag << ": " << result.error();
}
interval = std::chrono::duration_cast<std::chrono::nanoseconds>(*result);
++i;
continue;
}
if (args[i] == String16(kMaxDurationFlag)) {
const auto& result = parseSecondsFlag(args, i + 1);
if (!result.ok()) {
return Error(BAD_VALUE)
<< "Failed to parse " << kMaxDurationFlag << ": " << result.error();
}
maxDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(*result);
++i;
continue;
}
if (args[i] == String16(kFilterPackagesFlag)) {
if (args.size() < i + 1) {
return Error(BAD_VALUE)
<< "Must provide value for '" << kFilterPackagesFlag << "' flag";
}
std::vector<std::string> packages =
Split(std::string(String8(args[i + 1]).string()), ",");
std::copy(packages.begin(), packages.end(),
std::inserter(filterPackages, filterPackages.end()));
++i;
continue;
}
ALOGW("Unknown flag %s provided to start custom performance data collection",
String8(args[i]).string());
return Error(BAD_VALUE) << "Unknown flag " << String8(args[i]).string()
<< " provided to start custom performance data collection";
}
if (const auto& result = startCustomCollection(interval, maxDuration, filterPackages);
!result.ok()) {
WriteStringToFd(result.error().message(), fd);
return result;
}
return {};
}
if (args[0] == String16(kEndCustomCollectionFlag)) {
if (args.size() != 1) {
ALOGW("Number of arguments to stop custom performance data collection cannot exceed 1. "
"Stopping the data collection.");
WriteStringToFd("Number of arguments to stop custom performance data collection "
"cannot exceed 1. Stopping the data collection.",
fd);
}
return endCustomCollection(fd);
}
return Error(BAD_VALUE) << "Custom perf collection dump arguments start neither with "
<< kStartCustomCollectionFlag << " nor with "
<< kEndCustomCollectionFlag << " flags";
}
Result<void> WatchdogPerfService::onDump(int fd) const {
Mutex::Autolock lock(mMutex);
if (mCurrCollectionEvent == EventType::TERMINATED) {
ALOGW("%s not active. Dumping cached data", kServiceName);
if (!WriteStringToFd(StringPrintf("%s not active. Dumping cached data.", kServiceName),
fd)) {
return Error(FAILED_TRANSACTION) << "Failed to write " << kServiceName << " status";
}
}
if (const auto& result = dumpCollectorsStatusLocked(fd); !result.ok()) {
return Error(FAILED_TRANSACTION) << result.error();
}
if (!WriteStringToFd(StringPrintf("\n%s%s report:\n%sBoot-time collection information:\n%s\n",
kDumpMajorDelimiter.c_str(), kServiceName,
kDumpMajorDelimiter.c_str(), std::string(33, '=').c_str()),
fd) ||
!WriteStringToFd(mBoottimeCollection.toString(), fd) ||
!WriteStringToFd(StringPrintf("\nPeriodic collection information:\n%s\n",
std::string(32, '=').c_str()),
fd) ||
!WriteStringToFd(mPeriodicCollection.toString(), fd)) {
return Error(FAILED_TRANSACTION)
<< "Failed to dump the boot-time and periodic collection reports.";
}
for (const auto& processor : mDataProcessors) {
if (const auto result = processor->onDump(fd); !result.ok()) {
return result;
}
}
WriteStringToFd(kDumpMajorDelimiter, fd);
return {};
}
bool WatchdogPerfService::dumpHelpText(int fd) const {
return WriteStringToFd(StringPrintf(kHelpText, kServiceName, kStartCustomCollectionFlag,
kIntervalFlag,
std::chrono::duration_cast<std::chrono::seconds>(
kCustomCollectionInterval)
.count(),
kMaxDurationFlag,
std::chrono::duration_cast<std::chrono::minutes>(
kCustomCollectionDuration)
.count(),
kFilterPackagesFlag, kEndCustomCollectionFlag),
fd);
}
Result<void> WatchdogPerfService::dumpCollectorsStatusLocked(int fd) const {
if (!mUidStatsCollector->enabled() &&
!WriteStringToFd(StringPrintf("UidStatsCollector failed to access proc and I/O files"),
fd)) {
return Error() << "Failed to write UidStatsCollector status";
}
if (!mProcStat->enabled() &&
!WriteStringToFd(StringPrintf("ProcStat collector failed to access the file %s",
mProcStat->filePath().c_str()),
fd)) {
return Error() << "Failed to write ProcStat collector status";
}
return {};
}
Result<void> WatchdogPerfService::startCustomCollection(
std::chrono::nanoseconds interval, std::chrono::nanoseconds maxDuration,
const std::unordered_set<std::string>& filterPackages) {
if (interval < kMinEventInterval || maxDuration < kMinEventInterval) {
return Error(INVALID_OPERATION)
<< "Collection interval and maximum duration must be >= "
<< std::chrono::duration_cast<std::chrono::milliseconds>(kMinEventInterval).count()
<< " milliseconds.";
}
Mutex::Autolock lock(mMutex);
if (EventType expected = EventType::PERIODIC_COLLECTION; mCurrCollectionEvent != expected) {
return Error(INVALID_OPERATION)
<< "Cannot start a custom collection when the current collection event "
<< toString(mCurrCollectionEvent) << " != " << toString(expected)
<< " collection event";
}
mCustomCollection = {
.eventType = EventType::CUSTOM_COLLECTION,
.interval = interval,
.lastUptime = mHandlerLooper->now(),
.filterPackages = filterPackages,
};
mHandlerLooper->removeMessages(this);
nsecs_t uptime = mHandlerLooper->now() + maxDuration.count();
mHandlerLooper->sendMessageAtTime(uptime, this, SwitchMessage::END_CUSTOM_COLLECTION);
mCurrCollectionEvent = EventType::CUSTOM_COLLECTION;
mHandlerLooper->sendMessage(this, EventType::CUSTOM_COLLECTION);
ALOGI("Starting %s performance data collection", toString(mCurrCollectionEvent));
return {};
}
Result<void> WatchdogPerfService::endCustomCollection(int fd) {
Mutex::Autolock lock(mMutex);
if (mCurrCollectionEvent != EventType::CUSTOM_COLLECTION) {
return Error(INVALID_OPERATION) << "No custom collection is running";
}
mHandlerLooper->removeMessages(this);
mHandlerLooper->sendMessage(this, SwitchMessage::END_CUSTOM_COLLECTION);
if (const auto result = dumpCollectorsStatusLocked(fd); !result.ok()) {
return Error(FAILED_TRANSACTION) << result.error();
}
if (!WriteStringToFd(StringPrintf("%sPerformance data report for custom collection:\n%s",
kDumpMajorDelimiter.c_str(), kDumpMajorDelimiter.c_str()),
fd) ||
!WriteStringToFd(mCustomCollection.toString(), fd)) {
return Error(FAILED_TRANSACTION) << "Failed to write custom collection report.";
}
for (const auto& processor : mDataProcessors) {
if (const auto result = processor->onCustomCollectionDump(fd); !result.ok()) {
return Error() << processor->name() << " failed on " << toString(mCurrCollectionEvent)
<< " collection: " << result.error();
}
}
if (DEBUG) {
ALOGD("Custom event finished");
}
WriteStringToFd(kDumpMajorDelimiter, fd);
return {};
}
void WatchdogPerfService::handleMessage(const Message& message) {
Result<void> result;
auto switchToPeriodicLocked = [&](bool startNow) {
mHandlerLooper->removeMessages(this);
mCurrCollectionEvent = EventType::PERIODIC_COLLECTION;
mPeriodicCollection.lastUptime = mHandlerLooper->now();
if (startNow) {
mHandlerLooper->sendMessage(this, EventType::PERIODIC_COLLECTION);
} else {
mPeriodicCollection.lastUptime += mPeriodicCollection.interval.count();
mHandlerLooper->sendMessageAtTime(mPeriodicCollection.lastUptime, this,
EventType::PERIODIC_COLLECTION);
}
mPeriodicMonitor.lastUptime = mHandlerLooper->now() + mPeriodicMonitor.interval.count();
mHandlerLooper->sendMessageAtTime(mPeriodicMonitor.lastUptime, this,
EventType::PERIODIC_MONITOR);
ALOGI("Switching to %s and %s", toString(mCurrCollectionEvent),
toString(EventType::PERIODIC_MONITOR));
};
switch (message.what) {
case static_cast<int>(EventType::BOOT_TIME_COLLECTION):
result = processCollectionEvent(&mBoottimeCollection);
break;
case static_cast<int>(SwitchMessage::END_BOOTTIME_COLLECTION):
if (result = processCollectionEvent(&mBoottimeCollection); result.ok()) {
Mutex::Autolock lock(mMutex);
switchToPeriodicLocked(/*startNow=*/false);
}
break;
case static_cast<int>(EventType::PERIODIC_COLLECTION):
result = processCollectionEvent(&mPeriodicCollection);
break;
case static_cast<int>(EventType::CUSTOM_COLLECTION):
result = processCollectionEvent(&mCustomCollection);
break;
case static_cast<int>(EventType::PERIODIC_MONITOR):
result = processMonitorEvent(&mPeriodicMonitor);
break;
case static_cast<int>(SwitchMessage::END_CUSTOM_COLLECTION): {
Mutex::Autolock lock(mMutex);
if (EventType expected = EventType::CUSTOM_COLLECTION;
mCurrCollectionEvent != expected) {
ALOGW("Skipping END_CUSTOM_COLLECTION message as the current collection %s != %s",
toString(mCurrCollectionEvent), toString(expected));
return;
}
mCustomCollection = {};
for (const auto& processor : mDataProcessors) {
/*
* Clear custom collection cache on the data processors when the custom collection
* ends.
*/
processor->onCustomCollectionDump(-1);
}
switchToPeriodicLocked(/*startNow=*/true);
return;
}
default:
result = Error() << "Unknown message: " << message.what;
}
if (!result.ok()) {
Mutex::Autolock lock(mMutex);
ALOGE("Terminating %s: %s", kServiceName, result.error().message().c_str());
/*
* DO NOT CALL terminate() as it tries to join the collection thread but this code is
* executed on the collection thread. Thus it will result in a deadlock.
*/
mCurrCollectionEvent = EventType::TERMINATED;
mHandlerLooper->removeMessages(this);
mHandlerLooper->wake();
}
}
Result<void> WatchdogPerfService::processCollectionEvent(
WatchdogPerfService::EventMetadata* metadata) {
Mutex::Autolock lock(mMutex);
/*
* Messages sent to the looper are intrinsically racy such that a message from the previous
* collection event may land in the looper after the current collection has already begun. Thus
* verify the current collection event before starting the collection.
*/
if (mCurrCollectionEvent != metadata->eventType) {
ALOGW("Skipping %s event on collection event %s", toString(metadata->eventType),
toString(mCurrCollectionEvent));
return {};
}
if (DEBUG) {
ALOGD("Processing %s collection event", toString(metadata->eventType));
}
if (metadata->interval < kMinEventInterval) {
return Error()
<< "Collection interval of "
<< std::chrono::duration_cast<std::chrono::seconds>(metadata->interval).count()
<< " seconds for " << toString(metadata->eventType)
<< " collection cannot be less than "
<< std::chrono::duration_cast<std::chrono::seconds>(kMinEventInterval).count()
<< " seconds";
}
if (const auto result = collectLocked(metadata); !result.ok()) {
return Error() << toString(metadata->eventType) << " collection failed: " << result.error();
}
metadata->lastUptime += metadata->interval.count();
mHandlerLooper->sendMessageAtTime(metadata->lastUptime, this, metadata->eventType);
return {};
}
Result<void> WatchdogPerfService::collectLocked(WatchdogPerfService::EventMetadata* metadata) {
if (!mUidStatsCollector->enabled() && !mProcStat->enabled()) {
return Error() << "No collectors enabled";
}
time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
if (mUidStatsCollector->enabled()) {
if (const auto result = mUidStatsCollector->collect(); !result.ok()) {
return Error() << "Failed to collect per-uid proc and I/O stats: " << result.error();
}
}
if (mProcStat->enabled()) {
if (const auto result = mProcStat->collect(); !result.ok()) {
return Error() << "Failed to collect proc stats: " << result.error();
}
}
for (const auto& processor : mDataProcessors) {
Result<void> result;
switch (mCurrCollectionEvent) {
case EventType::BOOT_TIME_COLLECTION:
result = processor->onBoottimeCollection(now, mUidStatsCollector, mProcStat);
break;
case EventType::PERIODIC_COLLECTION:
result = processor->onPeriodicCollection(now, mSystemState, mUidStatsCollector,
mProcStat);
break;
case EventType::CUSTOM_COLLECTION:
result = processor->onCustomCollection(now, mSystemState, metadata->filterPackages,
mUidStatsCollector, mProcStat);
break;
default:
result = Error() << "Invalid collection event " << toString(mCurrCollectionEvent);
}
if (!result.ok()) {
return Error() << processor->name() << " failed on " << toString(mCurrCollectionEvent)
<< " collection: " << result.error();
}
}
return {};
}
Result<void> WatchdogPerfService::processMonitorEvent(
WatchdogPerfService::EventMetadata* metadata) {
if (metadata->eventType != static_cast<int>(EventType::PERIODIC_MONITOR)) {
return Error() << "Invalid monitor event " << toString(metadata->eventType);
}
if (DEBUG) {
ALOGD("Processing %s monitor event", toString(metadata->eventType));
}
if (metadata->interval < kMinEventInterval) {
return Error()
<< "Monitor interval of "
<< std::chrono::duration_cast<std::chrono::seconds>(metadata->interval).count()
<< " seconds for " << toString(metadata->eventType) << " event cannot be less than "
<< std::chrono::duration_cast<std::chrono::seconds>(kMinEventInterval).count()
<< " seconds";
}
Mutex::Autolock lock(mMutex);
if (!mProcDiskStats->enabled()) {
return Error() << "Cannot access proc disk stats for monitoring";
}
time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
if (const auto result = mProcDiskStats->collect(); !result.ok()) {
return Error() << "Failed to collect disk stats: " << result.error();
}
auto* currCollectionMetadata = currCollectionMetadataLocked();
if (currCollectionMetadata == nullptr) {
return Error() << "No metadata available for current collection event: "
<< toString(mCurrCollectionEvent);
}
bool requestedCollection = false;
const auto requestCollection = [&]() mutable {
if (requestedCollection) {
return;
}
const nsecs_t prevUptime =
currCollectionMetadata->lastUptime - currCollectionMetadata->interval.count();
nsecs_t uptime = mHandlerLooper->now();
if (const auto delta = std::abs(uptime - prevUptime); delta < kMinEventInterval.count()) {
return;
}
currCollectionMetadata->lastUptime = uptime;
mHandlerLooper->removeMessages(this, currCollectionMetadata->eventType);
mHandlerLooper->sendMessage(this, currCollectionMetadata->eventType);
requestedCollection = true;
};
for (const auto& processor : mDataProcessors) {
if (const auto result =
processor->onPeriodicMonitor(now, mProcDiskStats, requestCollection);
!result.ok()) {
return Error() << processor->name() << " failed on " << toString(metadata->eventType)
<< ": " << result.error();
}
}
metadata->lastUptime += metadata->interval.count();
if (metadata->lastUptime == currCollectionMetadata->lastUptime) {
/*
* If the |PERIODIC_MONITOR| and *_COLLECTION events overlap, skip the |PERIODIC_MONITOR|
* event.
*/
metadata->lastUptime += metadata->interval.count();
}
mHandlerLooper->sendMessageAtTime(metadata->lastUptime, this, metadata->eventType);
return {};
}
WatchdogPerfService::EventMetadata* WatchdogPerfService::currCollectionMetadataLocked() {
switch (mCurrCollectionEvent) {
case EventType::BOOT_TIME_COLLECTION:
return &mBoottimeCollection;
case EventType::PERIODIC_COLLECTION:
return &mPeriodicCollection;
case EventType::CUSTOM_COLLECTION:
return &mCustomCollection;
default:
return nullptr;
}
}
} // namespace watchdog
} // namespace automotive
} // namespace android