diff options
Diffstat (limited to 'storaged')
-rw-r--r-- | storaged/include/storaged.h | 38 | ||||
-rw-r--r-- | storaged/include/storaged_info.h | 33 | ||||
-rw-r--r-- | storaged/include/storaged_service.h | 13 | ||||
-rw-r--r-- | storaged/include/storaged_uid_monitor.h | 21 | ||||
-rw-r--r-- | storaged/include/storaged_utils.h | 1 | ||||
-rw-r--r-- | storaged/main.cpp | 35 | ||||
-rw-r--r-- | storaged/storaged.cpp | 122 | ||||
-rw-r--r-- | storaged/storaged.proto | 21 | ||||
-rw-r--r-- | storaged/storaged_info.cpp | 129 | ||||
-rw-r--r-- | storaged/storaged_service.cpp | 66 | ||||
-rw-r--r-- | storaged/storaged_uid_monitor.cpp | 71 | ||||
-rw-r--r-- | storaged/storaged_utils.cpp | 23 | ||||
-rw-r--r-- | storaged/tests/storaged_test.cpp | 62 |
13 files changed, 525 insertions, 110 deletions
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h index 28de7c9ff..f5c78f978 100644 --- a/storaged/include/storaged.h +++ b/storaged/include/storaged.h @@ -32,12 +32,6 @@ #define FRIEND_TEST(test_case_name, test_name) \ friend class test_case_name##_##test_name##_Test -#include "storaged_diskstats.h" -#include "storaged_info.h" -#include "storaged_uid_monitor.h" - -using namespace android; - #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define SECTOR_SIZE ( 512 ) @@ -47,12 +41,23 @@ using namespace android; #define SEC_TO_USEC ( 1000000 ) #define HOUR_TO_SEC ( 3600 ) #define DAY_TO_SEC ( 3600 * 24 ) +#define WEEK_TO_DAYS ( 7 ) +#define YEAR_TO_WEEKS ( 52 ) + +#include "storaged_diskstats.h" +#include "storaged_info.h" +#include "storaged_uid_monitor.h" +#include "storaged.pb.h" + +using namespace std; +using namespace android; // Periodic chores intervals in seconds #define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 ) #define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 ) #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 ) #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300) +#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO (3600) // UID IO threshold in bytes #define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL ) @@ -61,6 +66,7 @@ struct storaged_config { int periodic_chores_interval_unit; int periodic_chores_interval_disk_stats_publish; int periodic_chores_interval_uid_io; + int periodic_chores_interval_flush_proto; int event_time_check_usec; // check how much cputime spent in event loop }; @@ -73,7 +79,10 @@ private: uid_monitor mUidm; time_t mStarttime; sp<IBatteryPropertiesRegistrar> battery_properties; - std::unique_ptr<storage_info_t> storage_info; + unique_ptr<storage_info_t> storage_info; + static const uint32_t crc_init; + static const string proto_file; + storaged_proto::StoragedProto proto; public: storaged_t(void); ~storaged_t() {} @@ -87,12 +96,18 @@ public: return mStarttime; } - std::unordered_map<uint32_t, struct uid_info> get_uids(void) { + unordered_map<uint32_t, struct uid_info> get_uids(void) { return mUidm.get_uid_io_stats(); } - std::map<uint64_t, struct uid_records> get_uid_records( + + vector<vector<uint32_t>> get_perf_history(void) { + return storage_info->get_perf_history(); + } + + map<uint64_t, struct uid_records> get_uid_records( double hours, uint64_t threshold, bool force_report) { - return mUidm.dump(hours, threshold, force_report); + return mUidm.dump(hours, threshold, force_report, + proto.mutable_uid_io_usage()); } void update_uid_io_interval(int interval) { if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) { @@ -105,6 +120,9 @@ public: void binderDied(const wp<IBinder>& who); void report_storage_info(); + + void load_proto(); + void flush_proto(); }; // Eventlog tag diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h index 7d04c7a0d..8b07862b0 100644 --- a/storaged/include/storaged_info.h +++ b/storaged/include/storaged_info.h @@ -19,10 +19,17 @@ #include <string.h> +#include <chrono> + +#include "storaged.h" +#include "storaged.pb.h" + #define FRIEND_TEST(test_case_name, test_name) \ friend class test_case_name##_##test_name##_Test using namespace std; +using namespace chrono; +using namespace storaged_proto; class storage_info_t { protected: @@ -36,16 +43,36 @@ protected: const string userdata_path = "/data"; uint64_t userdata_total_kb; uint64_t userdata_free_kb; + // io perf history + time_point<system_clock> day_start_tp; + vector<uint32_t> recent_perf; + uint32_t nr_samples; + vector<uint32_t> daily_perf; + uint32_t nr_days; + vector<uint32_t> weekly_perf; + uint32_t nr_weeks; + sem_t si_lock; storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0), - userdata_total_kb(0), userdata_free_kb(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<chrono::seconds>( + day_start_tp.time_since_epoch()).count() % DAY_TO_SEC); + } void publish(); storage_info_t* s_info; public: static storage_info_t* get_storage_info(); - virtual ~storage_info_t() {} + virtual ~storage_info_t() { sem_destroy(&si_lock); } virtual void report() {}; - void refresh(); + void init(const IOPerfHistory& perf_history); + void refresh(IOPerfHistory* perf_history); + void update_perf_history(uint32_t bw, + const time_point<system_clock>& tp); + vector<vector<uint32_t>> get_perf_history(void); }; class emmc_info_t : public storage_info_t { diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h index a8ddf4c32..b7fe5b81a 100644 --- a/storaged/include/storaged_service.h +++ b/storaged/include/storaged_service.h @@ -24,16 +24,19 @@ #include "storaged.h" +using namespace std; using namespace android; // Interface class IStoraged : public IInterface { public: enum { - DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION, + DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION, + DUMPPERF, }; // Request the service to run the test function - virtual std::vector<struct uid_info> dump_uids(const char* option) = 0; + virtual vector<struct uid_info> dump_uids(const char* option) = 0; + virtual vector<vector<uint32_t>> dump_perf_history(const char* option) = 0; DECLARE_META_INTERFACE(Storaged); }; @@ -42,7 +45,8 @@ public: class BpStoraged : public BpInterface<IStoraged> { public: BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){}; - virtual std::vector<struct uid_info> dump_uids(const char* option); + virtual vector<struct uid_info> dump_uids(const char* option); + virtual vector<vector<uint32_t>> dump_perf_history(const char* option); }; // Server @@ -51,7 +55,8 @@ class BnStoraged : public BnInterface<IStoraged> { }; class Storaged : public BnStoraged { - virtual std::vector<struct uid_info> dump_uids(const char* option); + virtual vector<struct uid_info> dump_uids(const char* option); + virtual vector<vector<uint32_t>> dump_perf_history(const char* option); virtual status_t dump(int fd, const Vector<String16>& args); }; diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h index c005c4c5f..d2c7105fd 100644 --- a/storaged/include/storaged_uid_monitor.h +++ b/storaged/include/storaged_uid_monitor.h @@ -23,6 +23,10 @@ #include <unordered_map> #include <vector> +#include "storaged.pb.h" + +using namespace storaged_proto; + enum uid_stat_t { FOREGROUND = 0, BACKGROUND = 1, @@ -113,8 +117,6 @@ private: uint64_t start_ts; // true if UID_IO_STATS_PATH is accessible const bool enable; - // protobuf file for io_history - static const std::string io_history_proto_file; // reads from /proc/uid_io/stats std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked(); @@ -122,26 +124,27 @@ private: void add_records_locked(uint64_t curr_ts); // updates curr_io_stats and set last_uid_io_stats void update_curr_io_stats_locked(); - // restores io_history from protobuf file - void load_io_history_from_proto(); - // converts io_history to protobuf and writes to a file - void flush_io_history_to_proto(); + // restores io_history from protobuf + void load_uid_io_proto(const UidIOUsage& proto); + // writes io_history to protobuf + void update_uid_io_proto(UidIOUsage* proto); public: uid_monitor(); ~uid_monitor(); // called by storaged main thread - void init(charger_stat_t stat); + void init(charger_stat_t stat, const UidIOUsage& proto); // called by storaged -u std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats(); // called by dumpsys std::map<uint64_t, struct uid_records> dump( - double hours, uint64_t threshold, bool force_report); + double hours, uint64_t threshold, bool force_report, + UidIOUsage* uid_io_proto); // 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(); + void report(UidIOUsage* proto); }; #endif /* _STORAGED_UID_MONITOR_H_ */ diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h index 06ab9554a..3b595b73f 100644 --- a/storaged/include/storaged_utils.h +++ b/storaged/include/storaged_utils.h @@ -35,5 +35,6 @@ void sort_running_uids_info(std::vector<struct uid_info> &uids); // Logging void log_console_running_uids_info(const std::vector<struct uid_info>& uids, bool flag_dump_task); +void log_console_perf_history(const vector<vector<uint32_t>>& perf_history); #endif /* _STORAGED_UTILS_H_ */ diff --git a/storaged/main.cpp b/storaged/main.cpp index efab69243..adc550a40 100644 --- a/storaged/main.cpp +++ b/storaged/main.cpp @@ -42,12 +42,15 @@ #include <storaged_service.h> #include <storaged_utils.h> +using namespace std; + sp<storaged_t> storaged; // Function of storaged's main thread void* storaged_main(void* /* unused */) { storaged = new storaged_t(); + storaged->load_proto(); storaged->init_battery_service(); storaged->report_storage_info(); @@ -64,6 +67,7 @@ void help_message(void) { printf("usage: storaged [OPTION]\n"); printf(" -u --uid Dump uid I/O usage to stdout\n"); printf(" -t --task Dump task I/O usage to stdout\n"); + printf(" -p --perf Dump I/O perf history to stdout\n"); printf(" -s --start Start storaged (default)\n"); fflush(stdout); } @@ -72,6 +76,7 @@ int main(int argc, char** argv) { bool flag_main_service = false; bool flag_dump_uid = false; bool flag_dump_task = false; + bool flag_dump_perf = false; int opt; for (;;) { @@ -81,9 +86,10 @@ int main(int argc, char** argv) { {"kill", no_argument, 0, 'k'}, {"uid", no_argument, 0, 'u'}, {"task", no_argument, 0, 't'}, + {"perf", no_argument, 0, 'p'}, {"help", no_argument, 0, 'h'} }; - opt = getopt_long(argc, argv, ":skdhu0t", long_options, &opt_idx); + opt = getopt_long(argc, argv, ":skhutp", long_options, &opt_idx); if (opt == -1) { break; } @@ -98,6 +104,9 @@ int main(int argc, char** argv) { case 't': flag_dump_task = true; break; + case 'p': + flag_dump_perf = true; + break; case 'h': help_message(); return 0; @@ -136,14 +145,14 @@ int main(int argc, char** argv) { return 0; } - if (flag_dump_uid || flag_dump_task) { - sp<IStoraged> storaged_service = get_storaged_service(); - if (storaged_service == NULL) { - fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n"); - return -1; - } - std::vector<struct uid_info> res = storaged_service->dump_uids(NULL); + sp<IStoraged> storaged_service = get_storaged_service(); + if (storaged_service == NULL) { + fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n"); + return -1; + } + if (flag_dump_uid || flag_dump_task) { + vector<struct uid_info> res = storaged_service->dump_uids(NULL); if (res.size() == 0) { fprintf(stderr, "UID I/O is not readable in this version of kernel.\n"); return 0; @@ -151,8 +160,16 @@ int main(int argc, char** argv) { sort_running_uids_info(res); log_console_running_uids_info(res, flag_dump_task); + } - return 0; + if (flag_dump_perf) { + vector<vector<uint32_t>> res = storaged_service->dump_perf_history(NULL); + if (res.size() == 0) { + fprintf(stderr, "I/O perf history is empty.\n"); + return 0; + } + + log_console_perf_history(res); } return 0; diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp index 0cf80a4b3..1794fb5f3 100644 --- a/storaged/storaged.cpp +++ b/storaged/storaged.cpp @@ -17,8 +17,15 @@ #define LOG_TAG "storaged" #include <stdlib.h> +#include <stdio.h> #include <time.h> #include <unistd.h> +#include <zlib.h> + +#include <chrono> +#include <fstream> +#include <sstream> +#include <string> #include <android-base/logging.h> #include <batteryservice/BatteryServiceConstants.h> @@ -31,6 +38,20 @@ #include <storaged.h> #include <storaged_utils.h> +using namespace android::base; +using namespace chrono; +using namespace google::protobuf::io; +using namespace storaged_proto; + +namespace { + +const uint32_t benchmark_unit_size = 16 * 1024; // 16KB + +} + +const uint32_t storaged_t::crc_init = 0x5108A4ED; /* STORAGED */ +const std::string storaged_t::proto_file = + "/data/misc/storaged/storaged.proto"; sp<IBatteryPropertiesRegistrar> get_battery_properties_service() { sp<IServiceManager> sm = defaultServiceManager(); @@ -66,7 +87,7 @@ void storaged_t::init_battery_service() { struct BatteryProperty val; battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val); - mUidm.init(is_charger_on(val.valueInt64)); + mUidm.init(is_charger_on(val.valueInt64), proto.uid_io_usage()); // register listener after init uid_monitor battery_properties->registerListener(this); @@ -85,6 +106,7 @@ void storaged_t::binderDied(const wp<IBinder>& who) { } void storaged_t::report_storage_info() { + storage_info->init(proto.perf_history()); storage_info->report(); } @@ -105,15 +127,101 @@ storaged_t::storaged_t(void) { property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO); + mConfig.periodic_chores_interval_flush_proto = + property_get_int32("ro.storaged.flush_proto.interval", + DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO); + storage_info.reset(storage_info_t::get_storage_info()); mStarttime = time(NULL); mTimer = 0; } -void storaged_t::event(void) { - storage_info->refresh(); +void storaged_t::load_proto() { + std::ifstream in(proto_file, + std::ofstream::in | std::ofstream::binary); + + if (!in.good()) { + PLOG_TO(SYSTEM, INFO) << "Open " << proto_file << " failed"; + return; + } + + stringstream ss; + ss << in.rdbuf(); + proto.ParseFromString(ss.str()); + + uint32_t crc = proto.crc(); + proto.set_crc(crc_init); + std::string proto_str = proto.SerializeAsString(); + uint32_t computed_crc = crc32(crc_init, + reinterpret_cast<const Bytef*>(proto_str.c_str()), + proto_str.size()); + + if (crc != computed_crc) { + LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file; + proto.Clear(); + } +} + +void storaged_t::flush_proto() { + proto.set_version(1); + proto.set_crc(crc_init); + while (proto.ByteSize() < 128 * 1024) { + proto.add_padding(0xFEEDBABE); + } + std::string proto_str = proto.SerializeAsString(); + proto.set_crc(crc32(crc_init, + reinterpret_cast<const Bytef*>(proto_str.c_str()), + proto_str.size())); + proto_str = proto.SerializeAsString(); + const char* data = proto_str.data(); + uint32_t size = proto_str.size(); + ssize_t ret; + time_point<steady_clock> start, end; + + std::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))); + if (fd == -1) { + PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file; + return; + } + + uint32_t benchmark_size = 0; + uint64_t benchmark_time_ns = 0; + while (size > 0) { + start = steady_clock::now(); + ret = write(fd, data, MIN(benchmark_unit_size, size)); + if (ret <= 0) { + PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file; + return; + } + end = steady_clock::now(); + /* + * compute bandwidth after the first write and if write returns + * exactly unit size. + */ + if (size != proto_str.size() && ret == benchmark_unit_size) { + benchmark_size += benchmark_unit_size; + benchmark_time_ns += duration_cast<nanoseconds>(end - start).count(); + } + size -= ret; + data += ret; + } + + if (benchmark_size) { + int perf = benchmark_size * 1000000LLU / benchmark_time_ns; + storage_info->update_perf_history(perf, system_clock::now()); + } + + fd.reset(-1); + /* Atomically replace existing proto file to reduce chance of data loss. */ + rename(tmp_file.c_str(), proto_file.c_str()); +} + +void storaged_t::event(void) { if (mDsm.enabled()) { mDsm.update(); if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) { @@ -123,7 +231,13 @@ void storaged_t::event(void) { if (mUidm.enabled() && !(mTimer % mConfig.periodic_chores_interval_uid_io)) { - mUidm.report(); + mUidm.report(proto.mutable_uid_io_usage()); + } + + storage_info->refresh(proto.mutable_perf_history()); + + if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) { + flush_proto(); } mTimer += mConfig.periodic_chores_interval_unit; diff --git a/storaged/storaged.proto b/storaged/storaged.proto index 52228468f..05c1f9127 100644 --- a/storaged/storaged.proto +++ b/storaged/storaged.proto @@ -36,7 +36,24 @@ message UidIOItem { optional UidIORecords records = 2; } -message UidIOHistoryProto { +message UidIOUsage { + repeated UidIOItem uid_io_items = 2; +} + +message IOPerfHistory { + optional uint64 day_start_sec = 1; + repeated uint32 recent_perf = 2; + optional uint32 nr_samples = 3; + repeated uint32 daily_perf = 4; + optional uint32 nr_days = 5; + repeated uint32 weekly_perf = 6; + optional uint32 nr_weeks = 7; +} + +message StoragedProto { optional uint32 crc = 1; - repeated UidIOItem items = 2; + optional uint32 version = 2; + optional UidIOUsage uid_io_usage = 3; + optional IOPerfHistory perf_history = 4; + repeated uint32 padding = 5; } diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp index fc2351f90..c5552f674 100644 --- a/storaged/storaged_info.cpp +++ b/storaged/storaged_info.cpp @@ -20,6 +20,8 @@ #include <string.h> #include <sys/statvfs.h> +#include <numeric> + #include <android-base/file.h> #include <android-base/parseint.h> #include <android-base/logging.h> @@ -27,9 +29,12 @@ #include <log/log_event_list.h> #include "storaged.h" +#include "storaged_info.h" using namespace std; +using namespace chrono; using namespace android::base; +using namespace storaged_proto; const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/"; const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd"; @@ -61,7 +66,37 @@ storage_info_t* storage_info_t::get_storage_info() return new storage_info_t; } -void storage_info_t::refresh() +void storage_info_t::init(const IOPerfHistory& perf_history) +{ + if (!perf_history.has_day_start_sec() || + perf_history.daily_perf_size() > (int)daily_perf.size() || + perf_history.weekly_perf_size() > (int)weekly_perf.size()) { + LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto"; + return; + } + + day_start_tp = {}; + day_start_tp += seconds(perf_history.day_start_sec()); + + nr_samples = perf_history.nr_samples(); + for (auto bw : perf_history.recent_perf()) { + recent_perf.push_back(bw); + } + + nr_days = perf_history.nr_days(); + int i = 0; + for (auto bw : perf_history.daily_perf()) { + daily_perf[i++] = bw; + } + + nr_weeks = perf_history.nr_weeks(); + i = 0; + for (auto bw : perf_history.weekly_perf()) { + weekly_perf[i++] = bw; + } +} + +void storage_info_t::refresh(IOPerfHistory* perf_history) { struct statvfs buf; if (statvfs(userdata_path.c_str(), &buf) != 0) { @@ -71,6 +106,24 @@ void storage_info_t::refresh() userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10; userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10; + + unique_ptr<lock_t> lock(new lock_t(&si_lock)); + + perf_history->Clear(); + perf_history->set_day_start_sec( + duration_cast<seconds>(day_start_tp.time_since_epoch()).count()); + for (const uint32_t& bw : recent_perf) { + perf_history->add_recent_perf(bw); + } + perf_history->set_nr_samples(nr_samples); + for (const uint32_t& bw : daily_perf) { + perf_history->add_daily_perf(bw); + } + perf_history->set_nr_days(nr_days); + for (const uint32_t& bw : weekly_perf) { + perf_history->add_weekly_perf(bw); + } + perf_history->set_nr_weeks(nr_weeks); } void storage_info_t::publish() @@ -80,6 +133,80 @@ void storage_info_t::publish() << LOG_ID_EVENTS; } +void storage_info_t::update_perf_history(uint32_t bw, + const time_point<system_clock>& tp) +{ + unique_ptr<lock_t> lock(new lock_t(&si_lock)); + + if (tp > day_start_tp && + duration_cast<seconds>(tp - day_start_tp).count() < DAY_TO_SEC) { + if (nr_samples >= recent_perf.size()) { + recent_perf.push_back(bw); + } else { + recent_perf[nr_samples] = bw; + } + nr_samples++; + return; + } + + recent_perf.erase(recent_perf.begin() + nr_samples, + recent_perf.end()); + + uint32_t daily_avg_bw = accumulate(recent_perf.begin(), + recent_perf.begin() + nr_samples, 0) / nr_samples; + + day_start_tp = tp - seconds(duration_cast<seconds>( + tp.time_since_epoch()).count() % DAY_TO_SEC); + + nr_samples = 0; + if (recent_perf.empty()) + recent_perf.resize(1); + recent_perf[nr_samples++] = bw; + + if (nr_days < WEEK_TO_DAYS) { + daily_perf[nr_days++] = daily_avg_bw; + return; + } + + uint32_t week_avg_bw = accumulate(daily_perf.begin(), + daily_perf.begin() + nr_days, 0) / nr_days; + + nr_days = 0; + daily_perf[nr_days++] = daily_avg_bw; + + if (nr_weeks >= YEAR_TO_WEEKS) { + nr_weeks = 0; + } + weekly_perf[nr_weeks++] = week_avg_bw; +} + +vector<vector<uint32_t>> storage_info_t::get_perf_history() +{ + unique_ptr<lock_t> lock(new lock_t(&si_lock)); + + vector<vector<uint32_t>> ret(3); + + ret[0].resize(recent_perf.size()); + for (size_t i = 0; i < recent_perf.size(); i++) { + int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size(); + ret[0][i] = recent_perf[idx]; + } + + ret[1].resize(daily_perf.size()); + for (size_t i = 0; i < daily_perf.size(); i++) { + int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size(); + ret[1][i] = daily_perf[idx]; + } + + ret[2].resize(weekly_perf.size()); + for (size_t i = 0; i < weekly_perf.size(); i++) { + int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size(); + ret[2][i] = weekly_perf[idx]; + } + + return ret; +} + void emmc_info_t::report() { if (!report_sysfs() && !report_debugfs()) diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp index 4364c6af3..e4ba380cb 100644 --- a/storaged/storaged_service.cpp +++ b/storaged/storaged_service.cpp @@ -32,18 +32,19 @@ #include <storaged.h> #include <storaged_service.h> +using namespace std; using namespace android::base; extern sp<storaged_t> storaged; -std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) { +vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) { Parcel data, reply; data.writeInterfaceToken(IStoraged::getInterfaceDescriptor()); remote()->transact(DUMPUIDS, data, &reply); uint32_t res_size = reply.readInt32(); - std::vector<struct uid_info> res(res_size); + vector<struct uid_info> res(res_size); for (auto&& uid : res) { uid.uid = reply.readInt32(); uid.name = reply.readCString(); @@ -60,6 +61,32 @@ std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) { } return res; } + +vector<vector<uint32_t>> BpStoraged::dump_perf_history(const char* /*option*/) { + Parcel data, reply; + data.writeInterfaceToken(IStoraged::getInterfaceDescriptor()); + + remote()->transact(DUMPPERF, data, &reply); + + vector<vector<uint32_t>> res(3); + uint32_t size = reply.readUint32(); + res[0].resize(size); + for (uint32_t i = 0; i < size; i++) { + res[0][i] = reply.readUint32(); + } + size = reply.readUint32(); + res[1].resize(size); + for (uint32_t i = 0; i < size; i++) { + res[1][i] = reply.readUint32(); + } + size = reply.readUint32(); + res[2].resize(size); + for (uint32_t i = 0; i < size; i++) { + res[2][i] = reply.readUint32(); + } + return res; +} + IMPLEMENT_META_INTERFACE(Storaged, "Storaged"); status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -67,7 +94,7 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply case DUMPUIDS: { if (!data.checkInterface(this)) return BAD_TYPE; - std::vector<struct uid_info> res = dump_uids(NULL); + vector<struct uid_info> res = dump_uids(NULL); reply->writeInt32(res.size()); for (const auto& uid : res) { reply->writeInt32(uid.uid); @@ -84,14 +111,33 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply return NO_ERROR; } break; + case DUMPPERF: { + if (!data.checkInterface(this)) + return BAD_TYPE; + vector<vector<uint32_t>> res = dump_perf_history(NULL); + reply->writeUint32(res[0].size()); + for (const auto& item : res[0]) { + reply->writeUint32(item); + } + reply->writeUint32(res[1].size()); + for (const auto& item : res[1]) { + reply->writeUint32(item); + } + reply->writeUint32(res[2].size()); + for (const auto& item : res[2]) { + reply->writeUint32(item); + } + return NO_ERROR; + } + break; default: return BBinder::onTransact(code, data, reply, flags); } } -std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) { - std::vector<struct uid_info> uids_v; - std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids(); +vector<struct uid_info> Storaged::dump_uids(const char* /* option */) { + vector<struct uid_info> uids_v; + unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids(); for (const auto& it : uids_m) { uids_v.push_back(it.second); @@ -99,6 +145,10 @@ std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) { return uids_v; } +vector<vector<uint32_t>> Storaged::dump_perf_history(const char* /* option */) { + return storaged->get_perf_history(); +} + status_t Storaged::dump(int fd, const Vector<String16>& args) { IPCThreadState* self = IPCThreadState::self(); const int pid = self->getCallingPid(); @@ -148,7 +198,7 @@ status_t Storaged::dump(int fd, const Vector<String16>& args) { } uint64_t last_ts = 0; - const std::map<uint64_t, struct uid_records>& records = + const map<uint64_t, struct uid_records>& records = storaged->get_uid_records(hours, threshold, force_report); for (const auto& it : records) { if (last_ts != it.second.start_ts) { @@ -173,7 +223,7 @@ status_t Storaged::dump(int fd, const Vector<String16>& args) { if (debug) { for (const auto& task_it : record.ios.task_ios) { const struct io_usage& task_usage = task_it.second; - const std::string& comm = task_it.first; + const string& comm = task_it.first; dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", comm.c_str(), diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp index 66a9e355f..9295ff2a7 100644 --- a/storaged/storaged_uid_monitor.cpp +++ b/storaged/storaged_uid_monitor.cpp @@ -17,11 +17,8 @@ #define LOG_TAG "storaged" #include <stdint.h> -#include <stdio.h> #include <time.h> -#include <zlib.h> -#include <fstream> #include <string> #include <unordered_map> @@ -37,25 +34,19 @@ #include "storaged.h" #include "storaged_uid_monitor.h" -#include "system/core/storaged/storaged.pb.h" using namespace android; using namespace android::base; using namespace android::content::pm; -using namespace google::protobuf::io; using namespace storaged_proto; namespace { bool refresh_uid_names; -const uint32_t crc_init = 0x5108A4ED; /* STORAGED */ const char* UID_IO_STATS_PATH = "/proc/uid_io/stats"; } // namepsace -const std::string uid_monitor::io_history_proto_file = -"/data/misc/storaged/io_history.proto"; - std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats() { std::unique_ptr<lock_t> lock(new lock_t(&um_lock)); @@ -264,10 +255,10 @@ void uid_monitor::add_records_locked(uint64_t curr_ts) } std::map<uint64_t, struct uid_records> uid_monitor::dump( - double hours, uint64_t threshold, bool force_report) + double hours, uint64_t threshold, bool force_report, UidIOUsage* uid_io_proto) { if (force_report) { - report(); + report(uid_io_proto); } std::unique_ptr<lock_t> lock(new lock_t(&um_lock)); @@ -370,14 +361,14 @@ void uid_monitor::update_curr_io_stats_locked() last_uid_io_stats = uid_io_stats; } -void uid_monitor::report() +void uid_monitor::report(UidIOUsage* proto) { std::unique_ptr<lock_t> lock(new lock_t(&um_lock)); update_curr_io_stats_locked(); add_records_locked(time(NULL)); - flush_io_history_to_proto(); + update_uid_io_proto(proto); } namespace { @@ -408,15 +399,15 @@ void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto) } // namespace -void uid_monitor::flush_io_history_to_proto() +void uid_monitor::update_uid_io_proto(UidIOUsage* uid_io_proto) { - UidIOHistoryProto out_proto; + uid_io_proto->Clear(); for (const auto& item : io_history) { const uint64_t& end_ts = item.first; const struct uid_records& recs = item.second; - UidIOItem* item_proto = out_proto.add_items(); + UidIOItem* item_proto = uid_io_proto->add_uid_io_items(); item_proto->set_end_ts(end_ts); UidIORecords* recs_proto = item_proto->mutable_records(); @@ -440,51 +431,11 @@ void uid_monitor::flush_io_history_to_proto() } } } - - out_proto.set_crc(crc_init); - std::string out_proto_str = out_proto.SerializeAsString(); - out_proto.set_crc(crc32(crc_init, - reinterpret_cast<const Bytef*>(out_proto_str.c_str()), - out_proto_str.size())); - - std::string tmp_file = io_history_proto_file + "_tmp"; - std::ofstream out(tmp_file, - std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); - out << out_proto.SerializeAsString(); - out.close(); - - /* Atomically replace existing proto file to reduce chance of data loss. */ - rename(tmp_file.c_str(), io_history_proto_file.c_str()); } -void uid_monitor::load_io_history_from_proto() +void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto) { - std::ifstream in(io_history_proto_file, - std::ofstream::in | std::ofstream::binary); - - if (!in.good()) { - PLOG_TO(SYSTEM, INFO) << "Open " << io_history_proto_file << " failed"; - return; - } - - stringstream ss; - ss << in.rdbuf(); - UidIOHistoryProto in_proto; - in_proto.ParseFromString(ss.str()); - - uint32_t crc = in_proto.crc(); - in_proto.set_crc(crc_init); - std::string io_proto_str = in_proto.SerializeAsString(); - uint32_t computed_crc = crc32(crc_init, - reinterpret_cast<const Bytef*>(io_proto_str.c_str()), - io_proto_str.size()); - - if (crc != computed_crc) { - LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << io_history_proto_file; - return; - } - - for (const auto& item_proto : in_proto.items()) { + for (const auto& item_proto : uid_io_proto.uid_io_items()) { const UidIORecords& records_proto = item_proto.records(); struct uid_records* recs = &io_history[item_proto.end_ts()]; @@ -516,11 +467,11 @@ void uid_monitor::set_charger_state(charger_stat_t stat) charger_stat = stat; } -void uid_monitor::init(charger_stat_t stat) +void uid_monitor::init(charger_stat_t stat, const UidIOUsage& proto) { charger_stat = stat; - load_io_history_from_proto(); + load_uid_io_proto(proto); start_ts = time(NULL); last_uid_io_stats = get_uid_io_stats(); diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp index 533bf7805..fcd2484e0 100644 --- a/storaged/storaged_utils.cpp +++ b/storaged/storaged_utils.cpp @@ -91,3 +91,26 @@ void log_console_running_uids_info(const std::vector<struct uid_info>& uids, boo } fflush(stdout); } + +void log_console_perf_history(const vector<vector<uint32_t>>& perf_history) { + if (perf_history.size() != 3) { + return; + } + + printf("\nI/O perf history (KB/s) : most_recent <--------- least_recent \n"); + + std::stringstream line; + std::copy(perf_history[0].begin(), perf_history[0].end(), + std::ostream_iterator<int>(line, " ")); + printf("last 24 hours : %s\n", line.str().c_str()); + + line.str(""); + std::copy(perf_history[1].begin(), perf_history[1].end(), + std::ostream_iterator<int>(line, " ")); + printf("last 7 days : %s\n", line.str().c_str()); + + line.str(""); + std::copy(perf_history[2].begin(), perf_history[2].end(), + std::ostream_iterator<int>(line, " ")); + printf("last 52 weeks : %s\n", line.str().c_str()); +}
\ No newline at end of file diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp index bd14d766f..5ae1c91c2 100644 --- a/storaged/tests/storaged_test.cpp +++ b/storaged/tests/storaged_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <chrono> #include <deque> #include <fcntl.h> #include <random> @@ -30,6 +31,9 @@ #define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat" #define SDA_DISK_STATS_PATH "/sys/block/sda/stat" +using namespace std; +using namespace chrono; + namespace { void write_and_pause(uint32_t sec) { @@ -343,3 +347,61 @@ TEST(storaged_test, disk_stats_monitor) { write_and_pause(5); } } + +TEST(storaged_test, storage_info_t) { + storage_info_t si; + time_point<steady_clock> tp; + time_point<system_clock> stp; + + // generate perf history [least_recent ------> most recent] + // day 1: 5, 10, 15, 20 | daily average 12 + // day 2: 25, 30, 35, 40, 45 | daily average 35 + // day 3: 50, 55, 60, 65, 70 | daily average 60 + // day 4: 75, 80, 85, 90, 95 | daily average 85 + // day 5: 100, 105, 110, 115, | daily average 107 + // day 6: 120, 125, 130, 135, 140 | daily average 130 + // day 7: 145, 150, 155, 160, 165 | daily average 155 + // end of week 1: | weekly average 83 + // day 1: 170, 175, 180, 185, 190 | daily average 180 + // day 2: 195, 200, 205, 210, 215 | daily average 205 + // day 3: 220, 225, 230, 235 | daily average 227 + // day 4: 240, 245, 250, 255, 260 | daily average 250 + // day 5: 265, 270, 275, 280, 285 | daily average 275 + // day 6: 290, 295, 300, 305, 310 | daily average 300 + // day 7: 315, 320, 325, 330, 335 | daily average 325 + // end of week 2: | weekly average 251 + // day 1: 340, 345, 350, 355 | daily average 347 + // day 2: 360, 365, 370, 375 + si.day_start_tp = {}; + for (int i = 0; i < 75; i++) { + tp += hours(5); + stp = {}; + stp += duration_cast<seconds>(tp.time_since_epoch()); + si.update_perf_history((i + 1) * 5, stp); + } + + vector<vector<uint32_t>> history = si.get_perf_history(); + EXPECT_EQ(history.size(), 3UL); + EXPECT_EQ(history[0].size(), 4UL); + EXPECT_EQ(history[1].size(), 7UL); // 7 days + EXPECT_EQ(history[2].size(), 52UL); // 52 weeks + // last 24 hours + EXPECT_EQ(history[0][0], 375UL); + EXPECT_EQ(history[0][1], 370UL); + EXPECT_EQ(history[0][2], 365UL); + EXPECT_EQ(history[0][3], 360UL); + // daily average of last 7 days + EXPECT_EQ(history[1][0], 347UL); + EXPECT_EQ(history[1][1], 325UL); + EXPECT_EQ(history[1][2], 300UL); + EXPECT_EQ(history[1][3], 275UL); + EXPECT_EQ(history[1][4], 250UL); + EXPECT_EQ(history[1][5], 227UL); + EXPECT_EQ(history[1][6], 205UL); + // weekly average of last 52 weeks + EXPECT_EQ(history[2][0], 251UL); + EXPECT_EQ(history[2][1], 83UL); + for (int i = 2; i < 52; i++) { + EXPECT_EQ(history[2][i], 0UL); + } +} |