summaryrefslogtreecommitdiffstats
path: root/storaged
diff options
context:
space:
mode:
Diffstat (limited to 'storaged')
-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);
+ }
+}