Merge "storaged: record IO perf history from proto updates"
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 28de7c9..f5c78f9 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 @@
 #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 @@
     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 @@
     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 @@
         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 @@
     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 7d04c7a..8b07862 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 @@
     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 a8ddf4c..b7fe5b8 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 @@
 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 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 c005c4c..d2c7105 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 @@
     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 @@
     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 06ab955..3b595b7 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -35,5 +35,6 @@
 
 // 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 efab692..adc550a 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 @@
     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 @@
     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 @@
             {"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 @@
         case 't':
             flag_dump_task = true;
             break;
+        case 'p':
+            flag_dump_perf = true;
+            break;
         case 'h':
             help_message();
             return 0;
@@ -136,14 +145,14 @@
         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 @@
 
         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 0cf80a4..1794fb5 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 @@
 
     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::report_storage_info() {
+    storage_info->init(proto.perf_history());
     storage_info->report();
 }
 
@@ -105,15 +127,101 @@
         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 @@
 
     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 5222846..05c1f91 100644
--- a/storaged/storaged.proto
+++ b/storaged/storaged.proto
@@ -36,7 +36,24 @@
   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 fc2351f..c5552f6 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 @@
     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 @@
 
     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 @@
         << 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 4364c6a..e4ba380 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 @@
     }
     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 @@
         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 @@
                 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 @@
     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 @@
     }
 
     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 @@
             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 66a9e35..9295ff2 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 @@
 }
 
 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 @@
     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 @@
 
 } // 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 @@
             }
         }
     }
-
-    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 @@
     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 533bf78..fcd2484 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -91,3 +91,26 @@
     }
     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 bd14d76..5ae1c91 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 @@
         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);
+    }
+}