summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJin Qian <jinqian@google.com>2017-09-28 16:02:22 -0700
committerJin Qian <jinqian@google.com>2017-10-10 00:56:03 +0000
commitd691d6e941c930e54313a53f817302fcdaf74460 (patch)
treeb1d70aa475385403a1ea9f9112ccb621b1d8ce8c
parent65dea712483927b59ae2451baa3bb12d6a246169 (diff)
downloadcore-d691d6e941c930e54313a53f817302fcdaf74460.tar.gz
core-d691d6e941c930e54313a53f817302fcdaf74460.tar.bz2
core-d691d6e941c930e54313a53f817302fcdaf74460.zip
storaged: record IO perf history from proto updates
Record the bandwidth when writing storaged.proto to monitor system storage performance. The history is maintained in 3 catergories. 1. samples of last 24 hours. 2. daily average of last 7 days. 3. weekly average of last 52 weeks. Sampling frequency of the first catergory is set to once every hour. Storaged also flushes proto at same frequency. The proto file is padded to reach a minimal size of 128KB. Storaged writes 16KB at a time. Bandwidth calculation ignores time spent in the first write and writes smaller than 16KB. bandwidth = total size of 16KB writes / time spent in 16KB writes Restructured the code so that storaged.cpp handles proto load/flush. It calls individual module to sync proto with internal data structures. Added a cmdline argument to dump perf history. adb shell storaged -p I/O perf history (KB/s) : most_recent <--------- least_recent last 24 hours : 5315 3775 3659 2042 3890 5652 3649 3696 6023 last 7 days : 4648 3532 3828 3567 3841 3359 4676 last 52 weeks : 3817 4275 3836 3766 4280 0 0 0 0 0 ... Test: adb shell storaged -p atp:asit/perf/jank_systemui_test atp:asit/perf/appstartup_hermetic_cyclic_dropcache_test atp:asit/perf/appstartup_non_hermetic_cyclic_dropcache_test Bug: 63629306 Change-Id: Ie7051e7a8df883d4a6818ea9a54a10f4dccb4843
-rw-r--r--storaged/include/storaged.h38
-rw-r--r--storaged/include/storaged_info.h33
-rw-r--r--storaged/include/storaged_service.h13
-rw-r--r--storaged/include/storaged_uid_monitor.h21
-rw-r--r--storaged/include/storaged_utils.h1
-rw-r--r--storaged/main.cpp35
-rw-r--r--storaged/storaged.cpp122
-rw-r--r--storaged/storaged.proto21
-rw-r--r--storaged/storaged_info.cpp129
-rw-r--r--storaged/storaged_service.cpp66
-rw-r--r--storaged/storaged_uid_monitor.cpp71
-rw-r--r--storaged/storaged_utils.cpp23
-rw-r--r--storaged/tests/storaged_test.cpp62
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);
+ }
+}