diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h index 3c0ffe74c..f9ab2d59b 100644 --- a/storaged/include/storaged.h +++ b/storaged/include/storaged.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -83,17 +84,18 @@ class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback, sp health; unique_ptr storage_info; static const uint32_t crc_init; - static const string proto_file; - storaged_proto::StoragedProto proto; - enum stat { - NOT_AVAILABLE, - AVAILABLE, - LOADED, - }; - stat proto_stat; + unordered_map protos; + Mutex proto_mutex; + void load_proto_locked(userid_t user_id); + void prepare_proto(StoragedProto* proto, userid_t user_id); + void flush_proto_locked(userid_t user_id); + void flush_proto_user_system_locked(StoragedProto* proto); + string proto_path(userid_t user_id) { + return string("/data/misc_ce/") + to_string(user_id) + + "/storaged/storaged.proto"; + } public: storaged_t(void); - ~storaged_t() {} void event(void); void event_checked(void); void pause(void) { @@ -114,8 +116,7 @@ public: map get_uid_records( double hours, uint64_t threshold, bool force_report) { - return mUidm.dump(hours, threshold, force_report, - proto.mutable_uid_io_usage()); + return mUidm.dump(hours, threshold, force_report, &protos); } void update_uid_io_interval(int interval) { @@ -124,15 +125,8 @@ public: } } - void set_proto_stat_available(bool available) { - if (available) { - if (proto_stat != LOADED) { - proto_stat = AVAILABLE; - } - } else { - proto_stat = NOT_AVAILABLE; - } - }; + void add_user_ce(userid_t user_id); + void remove_user_ce(userid_t user_id); void init_health_service(); virtual ::android::hardware::Return healthInfoChanged( @@ -141,8 +135,7 @@ public: void report_storage_info(); - void load_proto(); - void flush_proto(); + void flush_protos(); }; // Eventlog tag diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h index 93a1e6a1e..b1efac24f 100644 --- a/storaged/include/storaged_info.h +++ b/storaged/include/storaged_info.h @@ -21,6 +21,8 @@ #include +#include + #include "storaged.h" #include "storaged.pb.h" @@ -28,6 +30,7 @@ friend class test_case_name##_##test_name##_Test using namespace std; +using namespace android; using namespace chrono; using namespace storaged_proto; @@ -51,13 +54,12 @@ protected: uint32_t nr_days; vector weekly_perf; uint32_t nr_weeks; - sem_t si_lock; + Mutex si_mutex; storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0), userdata_total_kb(0), userdata_free_kb(0), nr_samples(0), daily_perf(WEEK_TO_DAYS, 0), nr_days(0), weekly_perf(YEAR_TO_WEEKS, 0), nr_weeks(0) { - sem_init(&si_lock, 0, 1); day_start_tp = system_clock::now(); day_start_tp -= chrono::seconds(duration_cast( day_start_tp.time_since_epoch()).count() % DAY_TO_SEC); @@ -66,7 +68,7 @@ protected: storage_info_t* s_info; public: static storage_info_t* get_storage_info(); - virtual ~storage_info_t() { sem_destroy(&si_lock); } + virtual ~storage_info_t() {}; virtual void report() {}; void load_perf_history_proto(const IOPerfHistory& perf_history); void refresh(IOPerfHistory* perf_history); diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h index 3246caff1..05c3b9469 100644 --- a/storaged/include/storaged_service.h +++ b/storaged/include/storaged_service.h @@ -29,6 +29,9 @@ using namespace android::os; using namespace android::os::storaged; class StoragedService : public BinderService, public BnStoraged { +private: + void dumpUidRecordsDebug(int fd, const vector& entries); + void dumpUidRecords(int fd, const vector& entries); public: static status_t start(); static char const* getServiceName() { return "storaged"; } diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h index 9245ab48f..6310ae4d8 100644 --- a/storaged/include/storaged_uid_monitor.h +++ b/storaged/include/storaged_uid_monitor.h @@ -23,92 +23,101 @@ #include #include +#include +#include + #include "storaged.pb.h" #include "uid_info.h" +#define FRIEND_TEST(test_case_name, test_name) \ +friend class test_case_name##_##test_name##_Test + +using namespace std; using namespace storaged_proto; +using namespace android; using namespace android::os::storaged; class uid_info : public UidInfo { public: - bool parse_uid_io_stats(std::string&& s); + bool parse_uid_io_stats(string&& s); }; -struct io_usage { +class io_usage { +public: + io_usage() : bytes{{{0}}} {}; uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS]; bool is_zero() const; + io_usage& operator+= (const io_usage& stats) { + for (int i = 0; i < IO_TYPES; i++) { + for (int j = 0; j < UID_STATS; j++) { + for (int k = 0; k < CHARGER_STATS; k++) { + bytes[i][j][k] += stats.bytes[i][j][k]; + } + } + } + return *this; + } }; struct uid_io_usage { - struct io_usage uid_ios; + userid_t user_id; + io_usage uid_ios; // mapped from task comm to task io usage - std::map task_ios; + map task_ios; }; struct uid_record { - std::string name; + string name; struct uid_io_usage ios; }; struct uid_records { uint64_t start_ts; - std::vector entries; -}; - -class lock_t { - sem_t* mSem; -public: - lock_t(sem_t* sem) { - mSem = sem; - sem_wait(mSem); - } - ~lock_t() { - sem_post(mSem); - } + vector entries; }; class uid_monitor { private: + FRIEND_TEST(storaged_test, uid_monitor); // last dump from /proc/uid_io/stats, uid -> uid_info - std::unordered_map last_uid_io_stats; + unordered_map last_uid_io_stats; // current io usage for next report, app name -> uid_io_usage - std::unordered_map curr_io_stats; + unordered_map curr_io_stats; // io usage records, end timestamp -> {start timestamp, vector of records} - std::map io_history; + map io_history; // charger ON/OFF charger_stat_t charger_stat; // protects curr_io_stats, last_uid_io_stats, records and charger_stat - sem_t um_lock; + Mutex uidm_mutex; // start time for IO records uint64_t start_ts; // true if UID_IO_STATS_PATH is accessible const bool enable; // reads from /proc/uid_io/stats - std::unordered_map get_uid_io_stats_locked(); + unordered_map get_uid_io_stats_locked(); // flushes curr_io_stats to records void add_records_locked(uint64_t curr_ts); // updates curr_io_stats and set last_uid_io_stats void update_curr_io_stats_locked(); // writes io_history to protobuf - void update_uid_io_proto(UidIOUsage* proto); + void update_uid_io_proto(unordered_map* protos); public: uid_monitor(); - ~uid_monitor(); // called by storaged main thread void init(charger_stat_t stat); // called by storaged -u - std::unordered_map get_uid_io_stats(); + unordered_map get_uid_io_stats(); // called by dumpsys - std::map dump( + map dump( double hours, uint64_t threshold, bool force_report, - UidIOUsage* uid_io_proto); + unordered_map* protos); // called by battery properties listener void set_charger_state(charger_stat_t stat); // called by storaged periodic_chore or dump with force_report bool enabled() { return enable; }; - void report(UidIOUsage* proto); + void report(unordered_map* protos); // restores io_history from protobuf void load_uid_io_proto(const UidIOUsage& proto); }; diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h index b866d20fa..62cb12d1c 100644 --- a/storaged/include/storaged_utils.h +++ b/storaged/include/storaged_utils.h @@ -33,6 +33,7 @@ void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* void add_disk_stats(struct disk_stats* src, struct disk_stats* dst); // UID I/O +map merge_io_usage(const vector& entries); void sort_running_uids_info(std::vector &uids); // Logging diff --git a/storaged/main.cpp b/storaged/main.cpp index 62828f05e..c1b13292e 100644 --- a/storaged/main.cpp +++ b/storaged/main.cpp @@ -51,7 +51,6 @@ sp storaged_sp; void* storaged_main(void* /* unused */) { storaged_sp = new storaged_t(); - storaged_sp->load_proto(); storaged_sp->init_health_service(); storaged_sp->report_storage_info(); diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp index 125473c89..39c347a07 100644 --- a/storaged/storaged.cpp +++ b/storaged/storaged.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "storaged" +#include #include #include #include @@ -28,6 +29,7 @@ #include #include +#include #include #include #include @@ -45,13 +47,18 @@ using namespace storaged_proto; namespace { -const uint32_t benchmark_unit_size = 16 * 1024; // 16KB +/* + * The system user is the initial user that is implicitly created on first boot + * and hosts most of the system services. Keep this in sync with + * frameworks/base/core/java/android/os/UserManager.java + */ +constexpr int USER_SYSTEM = 0; -} +constexpr uint32_t benchmark_unit_size = 16 * 1024; // 16KB + +} // namespace const uint32_t storaged_t::crc_init = 0x5108A4ED; /* STORAGED */ -const std::string storaged_t::proto_file = - "/data/misc_ce/0/storaged/storaged.proto"; using android::hardware::health::V1_0::BatteryStatus; using android::hardware::health::V1_0::toString; @@ -135,7 +142,7 @@ void storaged_t::report_storage_info() { } /* storaged_t */ -storaged_t::storaged_t(void) : proto_stat(NOT_AVAILABLE) { +storaged_t::storaged_t(void) { mConfig.periodic_chores_interval_unit = property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT); @@ -161,68 +168,81 @@ storaged_t::storaged_t(void) : proto_stat(NOT_AVAILABLE) { mTimer = 0; } -void storaged_t::load_proto() { - std::ifstream in(proto_file, - std::ofstream::in | std::ofstream::binary); +void storaged_t::add_user_ce(userid_t user_id) { + Mutex::Autolock _l(proto_mutex); + protos.insert({user_id, {}}); + load_proto_locked(user_id); + protos[user_id].set_loaded(1); +} - if (!in.good()) { - PLOG_TO(SYSTEM, INFO) << "Open " << proto_file << " failed"; - proto_stat = NOT_AVAILABLE; - return; - } +void storaged_t::remove_user_ce(userid_t user_id) { + Mutex::Autolock _l(proto_mutex); + protos.erase(user_id); + RemoveFileIfExists(proto_path(user_id), nullptr); +} - proto_stat = AVAILABLE; +void storaged_t::load_proto_locked(userid_t user_id) { + string proto_file = proto_path(user_id); + ifstream in(proto_file, ofstream::in | ofstream::binary); + + if (!in.good()) return; stringstream ss; ss << in.rdbuf(); - proto.Clear(); - proto.ParseFromString(ss.str()); + StoragedProto* proto = &protos[user_id]; + proto->Clear(); + proto->ParseFromString(ss.str()); - uint32_t crc = proto.crc(); - proto.set_crc(crc_init); - std::string proto_str = proto.SerializeAsString(); + uint32_t crc = proto->crc(); + proto->set_crc(crc_init); + string proto_str = proto->SerializeAsString(); uint32_t computed_crc = crc32(crc_init, reinterpret_cast(proto_str.c_str()), proto_str.size()); if (crc != computed_crc) { LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file; - proto.Clear(); + proto->Clear(); return; } - proto_stat = LOADED; + mUidm.load_uid_io_proto(proto->uid_io_usage()); - storage_info->load_perf_history_proto(proto.perf_history()); - mUidm.load_uid_io_proto(proto.uid_io_usage()); + if (user_id == USER_SYSTEM) { + storage_info->load_perf_history_proto(proto->perf_history()); + } } -void storaged_t::flush_proto() { - if (proto_stat != LOADED) return; +void storaged_t:: prepare_proto(StoragedProto* proto, userid_t user_id) { + proto->set_version(2); + proto->set_crc(crc_init); - proto.set_version(1); - proto.set_crc(crc_init); - while (proto.ByteSize() < 128 * 1024) { - proto.add_padding(0xFEEDBABE); + if (user_id == USER_SYSTEM) { + while (proto->ByteSize() < 128 * 1024) { + proto->add_padding(0xFEEDBABE); + } } - std::string proto_str = proto.SerializeAsString(); - proto.set_crc(crc32(crc_init, + + string proto_str = proto->SerializeAsString(); + proto->set_crc(crc32(crc_init, reinterpret_cast(proto_str.c_str()), proto_str.size())); - proto_str = proto.SerializeAsString(); +} +void storaged_t::flush_proto_user_system_locked(StoragedProto* proto) { + string proto_str = proto->SerializeAsString(); const char* data = proto_str.data(); uint32_t size = proto_str.size(); ssize_t ret; time_point start, end; - std::string tmp_file = proto_file + "_tmp"; + string proto_file = proto_path(USER_SYSTEM); + string tmp_file = proto_file + "_tmp"; unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(), - O_DIRECT | O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, - S_IRUSR | S_IWUSR))); + O_DIRECT | O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, + S_IRUSR | S_IWUSR))); if (fd == -1) { PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file; - proto_stat = NOT_AVAILABLE; return; } @@ -258,11 +278,40 @@ void storaged_t::flush_proto() { rename(tmp_file.c_str(), proto_file.c_str()); } -void storaged_t::event(void) { - if (proto_stat == AVAILABLE) { - load_proto(); +void storaged_t::flush_proto_locked(userid_t user_id) { + StoragedProto* proto = &protos[user_id]; + prepare_proto(proto, user_id); + if (user_id == USER_SYSTEM) { + flush_proto_user_system_locked(proto); + return; } + string proto_file = proto_path(user_id); + string tmp_file = proto_file + "_tmp"; + if (!WriteStringToFile(proto->SerializeAsString(), tmp_file, + S_IRUSR | S_IWUSR)) { + return; + } + + /* Atomically replace existing proto file to reduce chance of data loss. */ + rename(tmp_file.c_str(), proto_file.c_str()); +} + +void storaged_t::flush_protos() { + Mutex::Autolock _l(proto_mutex); + for (const auto& it : protos) { + /* + * Don't flush proto if we haven't loaded it from file and combined + * with data in memory. + */ + if (it.second.loaded() != 1) { + continue; + } + flush_proto_locked(it.first); + } +} + +void storaged_t::event(void) { if (mDsm.enabled()) { mDsm.update(); if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) { @@ -271,12 +320,17 @@ void storaged_t::event(void) { } if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) { - mUidm.report(proto.mutable_uid_io_usage()); + Mutex::Autolock _l(proto_mutex); + mUidm.report(&protos); + } + + if (storage_info) { + Mutex::Autolock _l(proto_mutex); + storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history()); } - storage_info->refresh(proto.mutable_perf_history()); if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) { - flush_proto(); + flush_protos(); } mTimer += mConfig.periodic_chores_interval_unit; diff --git a/storaged/storaged.proto b/storaged/storaged.proto index 05c1f9127..18869fa8c 100644 --- a/storaged/storaged.proto +++ b/storaged/storaged.proto @@ -22,8 +22,9 @@ message TaskIOUsage { message UidRecord { optional string uid_name = 1; - optional IOUsage uid_io = 2; - repeated TaskIOUsage task_io = 3; + optional uint32 user_id = 2; + optional IOUsage uid_io = 3; + repeated TaskIOUsage task_io = 4; } message UidIORecords { @@ -53,7 +54,8 @@ message IOPerfHistory { message StoragedProto { optional uint32 crc = 1; optional uint32 version = 2; - optional UidIOUsage uid_io_usage = 3; - optional IOPerfHistory perf_history = 4; - repeated uint32 padding = 5; + optional uint32 loaded = 3; + optional UidIOUsage uid_io_usage = 4; + optional IOPerfHistory perf_history = 5; + repeated uint32 padding = 6; } diff --git a/storaged/storaged.rc b/storaged/storaged.rc index 6e83e3348..1840d053a 100644 --- a/storaged/storaged.rc +++ b/storaged/storaged.rc @@ -1,7 +1,3 @@ -# remove this after vold can create directory for us. -on property:sys.user.0.ce_available=true - mkdir /data/misc_ce/0/storaged - service storaged /system/bin/storaged class main priority 10 diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp index ae26f2063..3b5edbb56 100644 --- a/storaged/storaged_info.cpp +++ b/storaged/storaged_info.cpp @@ -76,7 +76,7 @@ void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history) } day_start_tp = {}; - day_start_tp += seconds(perf_history.day_start_sec()); + day_start_tp += chrono::seconds(perf_history.day_start_sec()); nr_samples = perf_history.nr_samples(); for (auto bw : perf_history.recent_perf()) { @@ -107,11 +107,11 @@ void storage_info_t::refresh(IOPerfHistory* perf_history) userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10; userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10; - unique_ptr lock(new lock_t(&si_lock)); + Mutex::Autolock _l(si_mutex); perf_history->Clear(); perf_history->set_day_start_sec( - duration_cast(day_start_tp.time_since_epoch()).count()); + duration_cast(day_start_tp.time_since_epoch()).count()); for (const uint32_t& bw : recent_perf) { perf_history->add_recent_perf(bw); } @@ -136,10 +136,10 @@ void storage_info_t::publish() void storage_info_t::update_perf_history(uint32_t bw, const time_point& tp) { - unique_ptr lock(new lock_t(&si_lock)); + Mutex::Autolock _l(si_mutex); if (tp > day_start_tp && - duration_cast(tp - day_start_tp).count() < DAY_TO_SEC) { + duration_cast(tp - day_start_tp).count() < DAY_TO_SEC) { if (nr_samples >= recent_perf.size()) { recent_perf.push_back(bw); } else { @@ -155,7 +155,7 @@ void storage_info_t::update_perf_history(uint32_t bw, uint32_t daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.begin() + nr_samples, 0) / nr_samples; - day_start_tp = tp - seconds(duration_cast( + day_start_tp = tp - chrono::seconds(duration_cast( tp.time_since_epoch()).count() % DAY_TO_SEC); nr_samples = 0; @@ -182,7 +182,7 @@ void storage_info_t::update_perf_history(uint32_t bw, vector storage_info_t::get_perf_history() { - unique_ptr lock(new lock_t(&si_lock)); + Mutex::Autolock _l(si_mutex); vector ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size()); diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp index a5477e6d4..3c790e687 100644 --- a/storaged/storaged_service.cpp +++ b/storaged/storaged_service.cpp @@ -30,24 +30,68 @@ #include #include +#include #include using namespace std; using namespace android::base; -/* - * The system user is the initial user that is implicitly created on first boot - * and hosts most of the system services. Keep this in sync with - * frameworks/base/core/java/android/os/UserManager.java - */ -const int USER_SYSTEM = 0; - extern sp storaged_sp; status_t StoragedService::start() { return BinderService::publish(); } +void StoragedService::dumpUidRecords(int fd, const vector& entries) { + map merged_entries = merge_io_usage(entries); + for (const auto& rec : merged_entries) { + dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 + " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", + rec.first.c_str(), + rec.second.bytes[READ][FOREGROUND][CHARGER_OFF], + rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF], + rec.second.bytes[READ][BACKGROUND][CHARGER_OFF], + rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF], + rec.second.bytes[READ][FOREGROUND][CHARGER_ON], + rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON], + rec.second.bytes[READ][BACKGROUND][CHARGER_ON], + rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]); + } +} + +void StoragedService::dumpUidRecordsDebug(int fd, const vector& entries) { + for (const auto& record : entries) { + const io_usage& uid_usage = record.ios.uid_ios; + dprintf(fd, "%s_%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 + " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", + record.name.c_str(), record.ios.user_id, + uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF], + uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF], + uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF], + uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF], + uid_usage.bytes[READ][FOREGROUND][CHARGER_ON], + uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON], + uid_usage.bytes[READ][BACKGROUND][CHARGER_ON], + uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]); + + for (const auto& task_it : record.ios.task_ios) { + const io_usage& task_usage = task_it.second; + const string& comm = task_it.first; + dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 + " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", + comm.c_str(), + task_usage.bytes[READ][FOREGROUND][CHARGER_OFF], + task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF], + task_usage.bytes[READ][BACKGROUND][CHARGER_OFF], + task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF], + task_usage.bytes[READ][FOREGROUND][CHARGER_ON], + task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON], + task_usage.bytes[READ][BACKGROUND][CHARGER_ON], + task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]); + } + } +} + status_t StoragedService::dump(int fd, const Vector& args) { IPCThreadState* self = IPCThreadState::self(); const int pid = self->getCallingPid(); @@ -97,7 +141,7 @@ status_t StoragedService::dump(int fd, const Vector& args) { } uint64_t last_ts = 0; - const map& records = + map records = storaged_sp->get_uid_records(hours, threshold, force_report); for (const auto& it : records) { if (last_ts != it.second.start_ts) { @@ -106,36 +150,10 @@ status_t StoragedService::dump(int fd, const Vector& args) { dprintf(fd, ",%" PRIu64 "\n", it.first); last_ts = it.first; - for (const auto& record : it.second.entries) { - const struct io_usage& uid_usage = record.ios.uid_ios; - dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 - " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", - record.name.c_str(), - uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF], - uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF], - uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF], - uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF], - uid_usage.bytes[READ][FOREGROUND][CHARGER_ON], - uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON], - uid_usage.bytes[READ][BACKGROUND][CHARGER_ON], - uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]); - if (debug) { - for (const auto& task_it : record.ios.task_ios) { - const struct io_usage& task_usage = task_it.second; - const string& comm = task_it.first; - dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 - " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", - comm.c_str(), - task_usage.bytes[READ][FOREGROUND][CHARGER_OFF], - task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF], - task_usage.bytes[READ][BACKGROUND][CHARGER_OFF], - task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF], - task_usage.bytes[READ][FOREGROUND][CHARGER_ON], - task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON], - task_usage.bytes[READ][BACKGROUND][CHARGER_ON], - task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]); - } - } + if (!debug) { + dumpUidRecords(fd, it.second.entries); + } else { + dumpUidRecordsDebug(fd, it.second.entries); } } @@ -147,16 +165,12 @@ status_t StoragedService::dump(int fd, const Vector& args) { } binder::Status StoragedService::onUserStarted(int32_t userId) { - if (userId == USER_SYSTEM) { - storaged_sp->set_proto_stat_available(true); - } + storaged_sp->add_user_ce(userId); return binder::Status::ok(); } binder::Status StoragedService::onUserStopped(int32_t userId) { - if (userId == USER_SYSTEM) { - storaged_sp->set_proto_stat_available(false); - } + storaged_sp->remove_user_ce(userId); return binder::Status::ok(); } diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp index 640de458d..e0a5d014a 100644 --- a/storaged/storaged_uid_monitor.cpp +++ b/storaged/storaged_uid_monitor.cpp @@ -50,7 +50,7 @@ const char* UID_IO_STATS_PATH = "/proc/uid_io/stats"; std::unordered_map uid_monitor::get_uid_io_stats() { - std::unique_ptr lock(new lock_t(&um_lock)); + Mutex::Autolock _l(uidm_mutex); return get_uid_io_stats_locked(); }; @@ -227,6 +227,7 @@ void uid_monitor::add_records_locked(uint64_t curr_ts) struct uid_record record = {}; record.name = p.first; if (!p.second.uid_ios.is_zero()) { + record.ios.user_id = p.second.user_id; record.ios.uid_ios = p.second.uid_ios; for (const auto& p_task : p.second.task_ios) { if (!p_task.second.is_zero()) @@ -256,13 +257,14 @@ void uid_monitor::add_records_locked(uint64_t curr_ts) } std::map uid_monitor::dump( - double hours, uint64_t threshold, bool force_report, UidIOUsage* uid_io_proto) + double hours, uint64_t threshold, bool force_report, + unordered_map* protos) { if (force_report) { - report(uid_io_proto); + report(protos); } - std::unique_ptr lock(new lock_t(&um_lock)); + Mutex::Autolock _l(uidm_mutex); std::map dump_records; uint64_t first_ts = 0; @@ -310,12 +312,13 @@ void uid_monitor::update_curr_io_stats_locked() for (const auto& it : uid_io_stats) { const uid_info& uid = it.second; - if (curr_io_stats.find(uid.name) == curr_io_stats.end()) { - curr_io_stats[uid.name] = {}; + curr_io_stats[uid.name] = {}; } struct uid_io_usage& usage = curr_io_stats[uid.name]; + usage.user_id = multiuser_get_user_id(uid.uid); + int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes - last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes; int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes - @@ -347,7 +350,7 @@ void uid_monitor::update_curr_io_stats_locked() int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes - last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes; - struct io_usage& task_usage = usage.task_ios[comm]; + io_usage& task_usage = usage.task_ios[comm]; task_usage.bytes[READ][FOREGROUND][charger_stat] += (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta; task_usage.bytes[READ][BACKGROUND][charger_stat] += @@ -362,21 +365,21 @@ void uid_monitor::update_curr_io_stats_locked() last_uid_io_stats = uid_io_stats; } -void uid_monitor::report(UidIOUsage* proto) +void uid_monitor::report(unordered_map* protos) { if (!enabled()) return; - std::unique_ptr lock(new lock_t(&um_lock)); + Mutex::Autolock _l(uidm_mutex); update_curr_io_stats_locked(); add_records_locked(time(NULL)); - update_uid_io_proto(proto); + update_uid_io_proto(protos); } namespace { -void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usage) +void set_io_usage_proto(IOUsage* usage_proto, const io_usage& usage) { usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]); usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]); @@ -388,7 +391,7 @@ void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usage) usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]); } -void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto) +void get_io_usage_proto(io_usage* usage, const IOUsage& io_proto) { usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on(); usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off(); @@ -402,31 +405,41 @@ void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto) } // namespace -void uid_monitor::update_uid_io_proto(UidIOUsage* uid_io_proto) +void uid_monitor::update_uid_io_proto(unordered_map* protos) { - uid_io_proto->Clear(); + for (auto it : *protos) { + it.second.mutable_uid_io_usage()->Clear(); + } for (const auto& item : io_history) { const uint64_t& end_ts = item.first; const struct uid_records& recs = item.second; - - UidIOItem* item_proto = uid_io_proto->add_uid_io_items(); - item_proto->set_end_ts(end_ts); - - UidIORecords* recs_proto = item_proto->mutable_records(); - recs_proto->set_start_ts(recs.start_ts); + unordered_map user_items; for (const auto& entry : recs.entries) { + userid_t user_id = entry.ios.user_id; + UidIOItem* item_proto = user_items[user_id]; + if (item_proto == nullptr) { + item_proto = (*protos)[user_id].mutable_uid_io_usage() + ->add_uid_io_items(); + user_items[user_id] = item_proto; + } + item_proto->set_end_ts(end_ts); + + UidIORecords* recs_proto = item_proto->mutable_records(); + recs_proto->set_start_ts(recs.start_ts); + UidRecord* rec_proto = recs_proto->add_entries(); rec_proto->set_uid_name(entry.name); + rec_proto->set_user_id(user_id); IOUsage* uid_io_proto = rec_proto->mutable_uid_io(); - const struct io_usage& uio_ios = entry.ios.uid_ios; + const io_usage& uio_ios = entry.ios.uid_ios; set_io_usage_proto(uid_io_proto, uio_ios); for (const auto& task_io : entry.ios.task_ios) { const std::string& task_name = task_io.first; - const struct io_usage& task_ios = task_io.second; + const io_usage& task_ios = task_io.second; TaskIOUsage* task_io_proto = rec_proto->add_task_io(); task_io_proto->set_task_name(task_name); @@ -448,6 +461,7 @@ void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto) for (const auto& rec_proto : records_proto.entries()) { struct uid_record record; record.name = rec_proto.uid_name(); + record.ios.user_id = rec_proto.user_id(); get_io_usage_proto(&record.ios.uid_ios, rec_proto.uid_io()); for (const auto& task_io_proto : rec_proto.task_io()) { @@ -462,7 +476,7 @@ void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto) void uid_monitor::set_charger_state(charger_stat_t stat) { - std::unique_ptr lock(new lock_t(&um_lock)); + Mutex::Autolock _l(uidm_mutex); if (charger_stat == stat) { return; @@ -481,12 +495,5 @@ void uid_monitor::init(charger_stat_t stat) } uid_monitor::uid_monitor() - : enable(!access(UID_IO_STATS_PATH, R_OK)) -{ - sem_init(&um_lock, 0, 1); -} - -uid_monitor::~uid_monitor() -{ - sem_destroy(&um_lock); + : enable(!access(UID_IO_STATS_PATH, R_OK)) { } diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp index 9260c3a9c..4fd4bc9dc 100644 --- a/storaged/storaged_utils.cpp +++ b/storaged/storaged_utils.cpp @@ -121,4 +121,12 @@ void log_console_perf_history(const vector& perf_history) { std::copy(perf_history.begin() + start, perf_history.end(), std::ostream_iterator(line, " ")); printf("last 52 weeks : %s\n", line.str().c_str()); -} \ No newline at end of file +} + +map merge_io_usage(const vector& entries) { + map merged_entries; + for (const auto& record : entries) { + merged_entries[record.name] += record.ios.uid_ios; + } + return merged_entries; +} diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp index 20638d821..928119388 100644 --- a/storaged/tests/storaged_test.cpp +++ b/storaged/tests/storaged_test.cpp @@ -33,6 +33,7 @@ using namespace std; using namespace chrono; +using namespace storaged_proto; namespace { @@ -376,7 +377,7 @@ TEST(storaged_test, storage_info_t) { for (int i = 0; i < 75; i++) { tp += hours(5); stp = {}; - stp += duration_cast(tp.time_since_epoch()); + stp += duration_cast(tp.time_since_epoch()); si.update_perf_history((i + 1) * 5, stp); } @@ -406,3 +407,186 @@ TEST(storaged_test, storage_info_t) { EXPECT_EQ(history[i], 0); } } + +TEST(storaged_test, uid_monitor) { + uid_monitor uidm; + + uidm.io_history[200] = { + .start_ts = 100, + .entries = { + { "app1", { + .user_id = 0, + .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000, + } + }, + { "app2", { + .user_id = 0, + .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 1000, + } + }, + { "app1", { + .user_id = 1, + .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000, + .uid_ios.bytes[READ][FOREGROUND][CHARGER_ON] = 1000, + } + }, + }, + }; + + uidm.io_history[300] = { + .start_ts = 200, + .entries = { + { "app1", { + .user_id = 1, + .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] = 1000, + } + }, + { "app3", { + .user_id = 0, + .uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF] = 1000, + } + }, + }, + }; + + StoragedProto proto_0; + UidIOItem* item = proto_0.mutable_uid_io_usage()->add_uid_io_items(); + item->set_end_ts(200); + item->mutable_records()->set_start_ts(100); + UidRecord* rec = item->mutable_records()->add_entries(); + rec->set_uid_name("app1"); + rec->set_user_id(0); + rec->mutable_uid_io()->set_wr_fg_chg_on(1000); + + unordered_map protos; + protos[0] = proto_0; + + uidm.update_uid_io_proto(&protos); + + EXPECT_EQ(protos.size(), 2U); + EXPECT_EQ(protos.count(0), 1UL); + EXPECT_EQ(protos.count(1), 1UL); + + EXPECT_EQ(protos[0].uid_io_usage().uid_io_items_size(), 2); + const UidIOItem& user_0_item_0 = protos[0].uid_io_usage().uid_io_items(0); + EXPECT_EQ(user_0_item_0.end_ts(), 200UL); + EXPECT_EQ(user_0_item_0.records().start_ts(), 100UL); + EXPECT_EQ(user_0_item_0.records().entries_size(), 2); + EXPECT_EQ(user_0_item_0.records().entries(0).uid_name(), "app1"); + EXPECT_EQ(user_0_item_0.records().entries(0).user_id(), 0UL); + EXPECT_EQ(user_0_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL); + EXPECT_EQ(user_0_item_0.records().entries(1).uid_name(), "app2"); + EXPECT_EQ(user_0_item_0.records().entries(1).user_id(), 0UL); + EXPECT_EQ(user_0_item_0.records().entries(1).uid_io().rd_fg_chg_off(), 1000UL); + const UidIOItem& user_0_item_1 = protos[0].uid_io_usage().uid_io_items(1); + EXPECT_EQ(user_0_item_1.end_ts(), 300UL); + EXPECT_EQ(user_0_item_1.records().start_ts(), 200UL); + EXPECT_EQ(user_0_item_1.records().entries_size(), 1); + EXPECT_EQ(user_0_item_1.records().entries(0).uid_name(), "app3"); + EXPECT_EQ(user_0_item_1.records().entries(0).user_id(), 0UL); + EXPECT_EQ(user_0_item_1.records().entries(0).uid_io().rd_bg_chg_off(), 1000UL); + + EXPECT_EQ(protos[1].uid_io_usage().uid_io_items_size(), 2); + const UidIOItem& user_1_item_0 = protos[1].uid_io_usage().uid_io_items(0); + EXPECT_EQ(user_1_item_0.end_ts(), 200UL); + EXPECT_EQ(user_1_item_0.records().start_ts(), 100UL); + EXPECT_EQ(user_1_item_0.records().entries_size(), 1); + EXPECT_EQ(user_1_item_0.records().entries(0).uid_name(), "app1"); + EXPECT_EQ(user_1_item_0.records().entries(0).user_id(), 1UL); + EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().rd_fg_chg_on(), 1000UL); + EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL); + const UidIOItem& user_1_item_1 = protos[1].uid_io_usage().uid_io_items(1); + EXPECT_EQ(user_1_item_1.end_ts(), 300UL); + EXPECT_EQ(user_1_item_1.records().start_ts(), 200UL); + EXPECT_EQ(user_1_item_1.records().entries_size(), 1); + EXPECT_EQ(user_1_item_1.records().entries(0).uid_name(), "app1"); + EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL); + EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL); + + uidm.io_history.clear(); + + uidm.io_history[300] = { + .start_ts = 200, + .entries = { + { "app1", { + .user_id = 0, + .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000, + } + }, + }, + }; + + uidm.io_history[400] = { + .start_ts = 300, + .entries = { + { "app1", { + .user_id = 0, + .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000, + } + }, + }, + }; + + uidm.load_uid_io_proto(protos[0].uid_io_usage()); + uidm.load_uid_io_proto(protos[1].uid_io_usage()); + + EXPECT_EQ(uidm.io_history.size(), 3UL); + EXPECT_EQ(uidm.io_history.count(200), 1UL); + EXPECT_EQ(uidm.io_history.count(300), 1UL); + EXPECT_EQ(uidm.io_history.count(400), 1UL); + + EXPECT_EQ(uidm.io_history[200].start_ts, 100UL); + const vector& entries_0 = uidm.io_history[200].entries; + EXPECT_EQ(entries_0.size(), 3UL); + EXPECT_EQ(entries_0[0].name, "app1"); + EXPECT_EQ(entries_0[0].ios.user_id, 0UL); + EXPECT_EQ(entries_0[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL); + EXPECT_EQ(entries_0[1].name, "app2"); + EXPECT_EQ(entries_0[1].ios.user_id, 0UL); + EXPECT_EQ(entries_0[1].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL); + EXPECT_EQ(entries_0[2].name, "app1"); + EXPECT_EQ(entries_0[2].ios.user_id, 1UL); + EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL); + EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL); + + EXPECT_EQ(uidm.io_history[300].start_ts, 200UL); + const vector& entries_1 = uidm.io_history[300].entries; + EXPECT_EQ(entries_1.size(), 3UL); + EXPECT_EQ(entries_1[0].name, "app1"); + EXPECT_EQ(entries_1[0].ios.user_id, 0UL); + EXPECT_EQ(entries_1[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL); + EXPECT_EQ(entries_1[1].name, "app3"); + EXPECT_EQ(entries_1[1].ios.user_id, 0UL); + EXPECT_EQ(entries_1[1].ios.uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL); + EXPECT_EQ(entries_1[2].name, "app1"); + EXPECT_EQ(entries_1[2].ios.user_id, 1UL); + EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL); + + EXPECT_EQ(uidm.io_history[400].start_ts, 300UL); + const vector& entries_2 = uidm.io_history[400].entries; + EXPECT_EQ(entries_2.size(), 1UL); + EXPECT_EQ(entries_2[0].name, "app1"); + EXPECT_EQ(entries_2[0].ios.user_id, 0UL); + EXPECT_EQ(entries_2[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL); + + map merged_entries_0 = merge_io_usage(entries_0); + EXPECT_EQ(merged_entries_0.size(), 2UL); + EXPECT_EQ(merged_entries_0.count("app1"), 1UL); + EXPECT_EQ(merged_entries_0.count("app2"), 1UL); + EXPECT_EQ(merged_entries_0["app1"].bytes[READ][FOREGROUND][CHARGER_ON], 1000UL); + EXPECT_EQ(merged_entries_0["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 2000UL); + EXPECT_EQ(merged_entries_0["app2"].bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL); + + map merged_entries_1 = merge_io_usage(entries_1); + EXPECT_EQ(merged_entries_1.size(), 2UL); + EXPECT_EQ(merged_entries_1.count("app1"), 1UL); + EXPECT_EQ(merged_entries_1.count("app3"), 1UL); + EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL); + EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL); + EXPECT_EQ(merged_entries_1["app3"].bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL); + + map merged_entries_2 = merge_io_usage(entries_2); + EXPECT_EQ(merged_entries_2.size(), 1UL); + EXPECT_EQ(merged_entries_2.count("app1"), 1UL); + EXPECT_EQ(merged_entries_2["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL); +}