blob: 4cccb05bade70611d90a67f59196fc913817b9a5 [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
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and
* limitations under the License.
#include <android-base/chrono_utils.h>
#include <android-base/result.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <cutils/multiuser.h>
#include <gtest/gtest_prod.h>
#include <time.h>
#include <utils/Errors.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
#include <utils/String16.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "LooperWrapper.h"
#include "ProcPidStat.h"
#include "ProcStat.h"
#include "UidIoStats.h"
namespace android {
namespace automotive {
namespace watchdog {
constexpr const char* kStartCustomCollectionFlag = "--start_io";
constexpr const char* kEndCustomCollectionFlag = "--stop_io";
constexpr const char* kIntervalFlag = "--interval";
constexpr const char* kMaxDurationFlag = "--max_duration";
constexpr const char* kFilterPackagesFlag = "--filter_packages";
// Performance data collected from the `/proc/uid_io/stats` file.
struct UidIoPerfData {
struct Stats {
userid_t userId = 0;
std::string packageName;
uint64_t bytes[UID_STATES];
uint64_t fsync[UID_STATES];
std::vector<Stats> topNReads = {};
std::vector<Stats> topNWrites = {};
uint64_t total[METRIC_TYPES][UID_STATES] = {{0}};
std::string toString(const UidIoPerfData& perfData);
// Performance data collected from the `/proc/stats` file.
struct SystemIoPerfData {
uint64_t cpuIoWaitTime = 0;
uint64_t totalCpuTime = 0;
uint32_t ioBlockedProcessesCnt = 0;
uint32_t totalProcessesCnt = 0;
std::string toString(const SystemIoPerfData& perfData);
// Performance data collected from the `/proc/[pid]/stat` and `/proc/[pid]/task/[tid]/stat` files.
struct ProcessIoPerfData {
struct UidStats {
userid_t userId = 0;
std::string packageName;
uint64_t count = 0;
struct ProcessStats {
std::string comm = "";
uint64_t count = 0;
std::vector<ProcessStats> topNProcesses = {};
std::vector<UidStats> topNIoBlockedUids = {};
// Total # of tasks owned by each UID in |topNIoBlockedUids|.
std::vector<uint64_t> topNIoBlockedUidsTotalTaskCnt = {};
std::vector<UidStats> topNMajorFaultUids = {};
uint64_t totalMajorFaults = 0;
// Percentage of increase/decrease in the major page faults since last collection.
double majorFaultsPercentChange = 0.0;
std::string toString(const ProcessIoPerfData& data);
struct IoPerfRecord {
time_t time; // Collection time.
UidIoPerfData uidIoPerfData;
SystemIoPerfData systemIoPerfData;
ProcessIoPerfData processIoPerfData;
std::string toString(const IoPerfRecord& record);
struct CollectionInfo {
std::chrono::nanoseconds interval = 0ns; // Collection interval between subsequent collections.
size_t maxCacheSize = 0; // Maximum cache size for the collection.
std::unordered_set<std::string> filterPackages; // Filter the output only to the specified
// packages.
nsecs_t lastCollectionUptime = 0; // Used to calculate the uptime for next collection.
std::vector<IoPerfRecord> records; // Cache of collected performance records.
std::string toString(const CollectionInfo& collectionInfo);
enum CollectionEvent {
INIT = 0,
enum SwitchEvent {
// Ends boot-time collection by collecting the last boot-time record and switching the
// collection event to periodic collection.
// Ends custom collection, discards collected data and starts periodic collection.
static inline std::string toString(CollectionEvent event) {
switch (event) {
case CollectionEvent::INIT:
return "INIT";
case CollectionEvent::BOOT_TIME:
return "BOOT_TIME";
case CollectionEvent::PERIODIC:
return "PERIODIC";
case CollectionEvent::CUSTOM:
return "CUSTOM";
case CollectionEvent::TERMINATED:
return "TERMINATED";
return "INVALID";
// IoPerfCollection implements the I/O performance data collection module of the CarWatchDog
// service. It exposes APIs that the CarWatchDog main thread and binder service can call to start
// a collection, update the collection type, and generate collection dumps.
class IoPerfCollection : public MessageHandler {
IoPerfCollection() :
mHandlerLooper(new LooperWrapper()),
mUidIoStats(new UidIoStats()),
mProcStat(new ProcStat()),
mProcPidStat(new ProcPidStat()),
mLastMajorFaults(0) {}
~IoPerfCollection() { terminate(); }
// Starts the boot-time collection in the looper handler on a collection thread and returns
// immediately. Must be called only once. Otherwise, returns an error.
android::base::Result<void> start();
// Terminates the collection thread and returns.
void terminate();
// Ends the boot-time collection, caches boot-time perf records, sends message to the looper to
// begin the periodic collection, and returns immediately.
virtual android::base::Result<void> onBootFinished();
// Depending the arguments, it either:
// 1. Generates a dump from the boot-time and periodic collection events.
// 2. Starts custom collection.
// 3. Ends custom collection and dumps the collected data.
// Returns any error observed during the dump generation.
virtual android::base::Result<void> dump(int fd, const Vector<String16>& args);
// Dumps the help text.
bool dumpHelpText(int fd);
// Generates a dump from the boot-time and periodic collection events.
android::base::Result<void> dumpCollection(int fd);
// Dumps the collectors' status when they are disabled.
android::base::Result<void> dumpCollectorsStatusLocked(int fd);
// Starts a custom collection on the looper handler, temporarily stops the periodic collection
// (won't discard the collected data), and returns immediately. Returns any error observed
// during this process. The custom collection happens once every |interval| seconds. When the
// |maxDuration| is reached, the looper receives a message to end the collection, discards the
// collected data, and starts the periodic collection. This is needed to ensure the custom
// collection doesn't run forever when a subsequent |endCustomCollection| call is not received.
// When |kFilterPackagesFlag| value is provided, the results are filtered only to the specified
// package names.
android::base::Result<void> startCustomCollection(
std::chrono::nanoseconds interval, std::chrono::nanoseconds maxDuration,
const std::unordered_set<std::string>& filterPackages);
// Ends the current custom collection, generates a dump, sends message to looper to start the
// periodic collection, and returns immediately. Returns an error when there is no custom
// collection running or when a dump couldn't be generated from the custom collection.
android::base::Result<void> endCustomCollection(int fd);
// Handles the messages received by the lopper.
void handleMessage(const Message& message) override;
// Processes the events received by |handleMessage|.
android::base::Result<void> processCollectionEvent(CollectionEvent event, CollectionInfo* info);
// Collects/stores the performance data for the current collection event.
android::base::Result<void> collectLocked(CollectionInfo* collectionInfo);
// Collects performance data from the `/proc/uid_io/stats` file.
android::base::Result<void> collectUidIoPerfDataLocked(const CollectionInfo& collectionInfo,
UidIoPerfData* uidIoPerfData);
// Collects performance data from the `/proc/stats` file.
android::base::Result<void> collectSystemIoPerfDataLocked(SystemIoPerfData* systemIoPerfData);
// Collects performance data from the `/proc/[pid]/stat` and
// `/proc/[pid]/task/[tid]/stat` files.
android::base::Result<void> collectProcessIoPerfDataLocked(
const CollectionInfo& collectionInfo, ProcessIoPerfData* processIoPerfData);
// Updates the |mUidToPackageNameMapping| for the given |uids|.
android::base::Result<void> updateUidToPackageNameMapping(
const std::unordered_set<uint32_t>& uids);
// Retrieves package manager from the default service manager.
android::base::Result<void> retrievePackageManager();
// Top N per-UID stats per category.
int mTopNStatsPerCategory;
// Top N per-process stats per subcategory.
int mTopNStatsPerSubcategory;
// Thread on which the actual collection happens.
std::thread mCollectionThread;
// Makes sure only one collection is running at any given time.
Mutex mMutex;
// Handler lopper to execute different collection events on the collection thread.
android::sp<LooperWrapper> mHandlerLooper GUARDED_BY(mMutex);
// Info for the |CollectionEvent::BOOT_TIME| collection event. The cache is persisted until
// system shutdown/reboot.
CollectionInfo mBoottimeCollection GUARDED_BY(mMutex);
// Info for the |CollectionEvent::PERIODIC| collection event. The cache size is limited by
// |ro.carwatchdog.periodic_collection_buffer_size|.
CollectionInfo mPeriodicCollection GUARDED_BY(mMutex);
// Info for the |CollectionEvent::CUSTOM| collection event. The info is cleared at the end of
// every custom collection.
CollectionInfo mCustomCollection GUARDED_BY(mMutex);
// Tracks the current collection event. Updated on |start|, |onBootComplete|,
// |startCustomCollection| and |endCustomCollection|.
CollectionEvent mCurrCollectionEvent GUARDED_BY(mMutex);
// Cache of uid to package name mapping.
std::unordered_map<uint64_t, std::string> mUidToPackageNameMapping GUARDED_BY(mMutex);
// Collector/parser for `/proc/uid_io/stats`.
android::sp<UidIoStats> mUidIoStats GUARDED_BY(mMutex);
// Collector/parser for `/proc/stat`.
android::sp<ProcStat> mProcStat GUARDED_BY(mMutex);
// Collector/parser for `/proc/PID/*` stat files.
android::sp<ProcPidStat> mProcPidStat GUARDED_BY(mMutex);
// Major faults delta from last collection. Useful when calculating the percentage change in
// major faults since last collection.
uint64_t mLastMajorFaults GUARDED_BY(mMutex);
// To get the package names from app uids.
android::sp<android::content::pm::IPackageManagerNative> mPackageManager GUARDED_BY(mMutex);
FRIEND_TEST(IoPerfCollectionTest, TestCollectionStartAndTerminate);
FRIEND_TEST(IoPerfCollectionTest, TestValidCollectionSequence);
FRIEND_TEST(IoPerfCollectionTest, TestCollectionTerminatesOnZeroEnabledCollectors);
FRIEND_TEST(IoPerfCollectionTest, TestCollectionTerminatesOnError);
FRIEND_TEST(IoPerfCollectionTest, TestCustomCollectionTerminatesAfterMaxDuration);
FRIEND_TEST(IoPerfCollectionTest, TestValidUidIoStatFile);
FRIEND_TEST(IoPerfCollectionTest, TestUidIOStatsLessThanTopNStatsLimit);
FRIEND_TEST(IoPerfCollectionTest, TestProcUidIoStatsContentsFromDevice);
FRIEND_TEST(IoPerfCollectionTest, TestValidProcStatFile);
FRIEND_TEST(IoPerfCollectionTest, TestValidProcPidContents);
FRIEND_TEST(IoPerfCollectionTest, TestProcPidContentsLessThanTopNStatsLimit);
FRIEND_TEST(IoPerfCollectionTest, TestCustomCollectionFiltersPackageNames);
} // namespace watchdog
} // namespace automotive
} // namespace android