diff --git a/storaged/Android.bp b/storaged/Android.bp index 25b433c7e..6aef8c804 100644 --- a/storaged/Android.bp +++ b/storaged/Android.bp @@ -44,6 +44,7 @@ cc_library_static { srcs: [ "storaged.cpp", + "storaged_diskstats.cpp", "storaged_info.cpp", "storaged_service.cpp", "storaged_utils.cpp", diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h index bd99361de..28de7c9ff 100644 --- a/storaged/include/storaged.h +++ b/storaged/include/storaged.h @@ -29,23 +29,15 @@ #include #include +#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 FRIEND_TEST(test_case_name, test_name) \ -friend class test_case_name##_##test_name##_Test - -/* For debug */ -#ifdef DEBUG -#define debuginfo(fmt, ...) \ - do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \ - while(0) -#else -#define debuginfo(...) -#endif - #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define SECTOR_SIZE ( 512 ) @@ -56,162 +48,6 @@ friend class test_case_name##_##test_name##_Test #define HOUR_TO_SEC ( 3600 ) #define DAY_TO_SEC ( 3600 * 24 ) -// number of attributes diskstats has -#define DISK_STATS_SIZE ( 11 ) -// maximum size limit of a stats file -#define DISK_STATS_FILE_MAX_SIZE ( 256 ) -#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 ) -struct disk_stats { - /* It will be extremely unlikely for any of the following entries to overflow. - * For read_bytes(which will be greater than any of the following entries), it - * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which - * is the peak memory transfer rate for current memory. - * The diskstats entries (first 11) need to be at top in this structure _after_ - * compiler's optimization. - */ - uint64_t read_ios; // number of read I/Os processed - uint64_t read_merges; // number of read I/Os merged with in-queue I/Os - uint64_t read_sectors; // number of sectors read - uint64_t read_ticks; // total wait time for read requests - uint64_t write_ios; // number of write I/Os processed - uint64_t write_merges; // number of write I/Os merged with in-queue I/Os - uint64_t write_sectors; // number of sectors written - uint64_t write_ticks; // total wait time for write requests - uint64_t io_in_flight; // number of I/Os currently in flight - uint64_t io_ticks; // total time this block device has been active - uint64_t io_in_queue; // total wait time for all requests - - uint64_t start_time; // monotonic time accounting starts - uint64_t end_time; // monotonic time accounting ends - uint32_t counter; // private counter for accumulate calculations - double io_avg; // average io_in_flight for accumulate calculations -}; - - - -struct disk_perf { - uint32_t read_perf; // read speed (kbytes/s) - uint32_t read_ios; // read I/Os per second - uint32_t write_perf; // write speed (kbytes/s) - uint32_t write_ios; // write I/Os per second - uint32_t queue; // I/Os in queue -}; - -class lock_t { - sem_t* mSem; -public: - lock_t(sem_t* sem) { - mSem = sem; - sem_wait(mSem); - } - ~lock_t() { - sem_post(mSem); - } -}; - -class stream_stats { -private: - double mSum; - double mSquareSum; - uint32_t mCnt; -public: - stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {}; - ~stream_stats() {}; - double get_mean() { - return mSum / mCnt; - } - double get_std() { - return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt)); - } - void add(uint32_t num) { - mSum += (double)num; - mSquareSum += (double)num * (double)num; - mCnt++; - } - void evict(uint32_t num) { - if (mSum < num || mSquareSum < (double)num * (double)num) return; - mSum -= (double)num; - mSquareSum -= (double)num * (double)num; - mCnt--; - } -}; - -#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat" -#define SDA_DISK_STATS_PATH "/sys/block/sda/stat" -#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd" -#define UID_IO_STATS_PATH "/proc/uid_io/stats" - -class disk_stats_monitor { -private: - FRIEND_TEST(storaged_test, disk_stats_monitor); - const char* DISK_STATS_PATH; - struct disk_stats mPrevious; - struct disk_stats mAccumulate; - bool mStall; - std::queue mBuffer; - struct { - stream_stats read_perf; // read speed (bytes/s) - stream_stats read_ios; // read I/Os per second - stream_stats write_perf; // write speed (bytes/s) - stream_stats write_ios; // write I/O per second - stream_stats queue; // I/Os in queue - } mStats; - bool mValid; - const uint32_t mWindow; - const double mSigma; - struct disk_perf mMean; - struct disk_perf mStd; - - void update_mean(); - void update_std(); - void add(struct disk_perf* perf); - void evict(struct disk_perf* perf); - bool detect(struct disk_perf* perf); - - void update(struct disk_stats* stats); - -public: - disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) : - mStall(false), - mValid(false), - mWindow(window_size), - mSigma(sigma) { - memset(&mPrevious, 0, sizeof(mPrevious)); - memset(&mMean, 0, sizeof(mMean)); - memset(&mStd, 0, sizeof(mStd)); - - if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) { - DISK_STATS_PATH = MMC_DISK_STATS_PATH; - } else { - DISK_STATS_PATH = SDA_DISK_STATS_PATH; - } - } - void update(void); -}; - -class disk_stats_publisher { -private: - FRIEND_TEST(storaged_test, disk_stats_publisher); - const char* DISK_STATS_PATH; - struct disk_stats mAccumulate; - struct disk_stats mPrevious; -public: - disk_stats_publisher(void) { - memset(&mAccumulate, 0, sizeof(struct disk_stats)); - memset(&mPrevious, 0, sizeof(struct disk_stats)); - - if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) { - DISK_STATS_PATH = MMC_DISK_STATS_PATH; - } else { - DISK_STATS_PATH = SDA_DISK_STATS_PATH; - } - } - - ~disk_stats_publisher(void) {} - void publish(void); - void update(void); -}; - // Periodic chores intervals in seconds #define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 ) #define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 ) @@ -225,8 +61,6 @@ struct storaged_config { int periodic_chores_interval_unit; int periodic_chores_interval_disk_stats_publish; int periodic_chores_interval_uid_io; - bool proc_uid_io_available; // whether uid_io is accessible - bool diskstats_available; // whether diskstats is accessible int event_time_check_usec; // check how much cputime spent in event loop }; @@ -235,7 +69,6 @@ class storaged_t : public BnBatteryPropertiesListener, private: time_t mTimer; storaged_config mConfig; - disk_stats_publisher mDiskStats; disk_stats_monitor mDsm; uid_monitor mUidm; time_t mStarttime; diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h new file mode 100644 index 000000000..ff030f640 --- /dev/null +++ b/storaged/include/storaged_diskstats.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _STORAGED_DISKSTATS_H_ +#define _STORAGED_DISKSTATS_H_ + +#include + +// number of attributes diskstats has +#define DISK_STATS_SIZE ( 11 ) + +#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat" +#define SDA_DISK_STATS_PATH "/sys/block/sda/stat" + +struct disk_stats { + /* It will be extremely unlikely for any of the following entries to overflow. + * For read_bytes(which will be greater than any of the following entries), it + * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which + * is the peak memory transfer rate for current memory. + * The diskstats entries (first 11) need to be at top in this structure _after_ + * compiler's optimization. + */ + uint64_t read_ios; // number of read I/Os processed + uint64_t read_merges; // number of read I/Os merged with in-queue I/Os + uint64_t read_sectors; // number of sectors read + uint64_t read_ticks; // total wait time for read requests + uint64_t write_ios; // number of write I/Os processed + uint64_t write_merges; // number of write I/Os merged with in-queue I/Os + uint64_t write_sectors; // number of sectors written + uint64_t write_ticks; // total wait time for write requests + uint64_t io_in_flight; // number of I/Os currently in flight + uint64_t io_ticks; // total time this block device has been active + uint64_t io_in_queue; // total wait time for all requests + + uint64_t start_time; // monotonic time accounting starts + uint64_t end_time; // monotonic time accounting ends + uint32_t counter; // private counter for accumulate calculations + double io_avg; // average io_in_flight for accumulate calculations + + bool is_zero() { + return read_ios == 0 && write_ios == 0 && + io_in_flight == 0 && io_ticks == 0 && io_in_queue == 0; + } + + friend disk_stats operator- (disk_stats curr, const disk_stats& prev) { + curr.read_ios -= prev.read_ios; + curr.read_merges -= prev.read_merges; + curr.read_sectors -= prev.read_sectors; + curr.read_ticks -= prev.read_ticks; + curr.write_ios -= prev.write_ios; + curr.write_merges -= prev.write_merges; + curr.write_sectors -= prev.write_sectors; + curr.write_ticks -= prev.write_ticks; + /* skips io_in_flight, use current value */ + curr.io_ticks -= prev.io_ticks; + curr.io_in_queue -= prev.io_in_queue; + return curr; + } + + friend bool operator== (const disk_stats& a, const disk_stats& b) { + return a.read_ios == b.read_ios && + a.read_merges == b.read_merges && + a.read_sectors == b.read_sectors && + a.read_ticks == b.read_ticks && + a.write_ios == b.write_ios && + a.write_merges == b.write_merges && + a.write_sectors == b.write_sectors && + a.write_ticks == b.write_ticks && + /* skips io_in_flight */ + a.io_ticks == b.io_ticks && + a.io_in_queue == b.io_in_queue; + } + + disk_stats& operator+= (const disk_stats& stats) { + read_ios += stats.read_ios; + read_merges += stats.read_merges; + read_sectors += stats.read_sectors; + read_ticks += stats.read_ticks; + write_ios += stats.write_ios; + write_merges += stats.write_merges; + write_sectors += stats.write_sectors; + write_ticks += stats.write_ticks; + /* skips io_in_flight, use current value */ + io_ticks += stats.io_ticks; + io_in_queue += stats.io_in_queue; + return *this; + } +}; + +struct disk_perf { + uint32_t read_perf; // read speed (kbytes/s) + uint32_t read_ios; // read I/Os per second + uint32_t write_perf; // write speed (kbytes/s) + uint32_t write_ios; // write I/Os per second + uint32_t queue; // I/Os in queue + bool is_zero() { + return read_perf == 0 && read_ios == 0 && + write_perf == 0 && write_ios == 0 && queue == 0; + } +}; + +class stream_stats { +private: + double mSum; + double mSquareSum; + uint32_t mCnt; +public: + stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {}; + ~stream_stats() {}; + double get_mean() { + return mSum / mCnt; + } + double get_std() { + return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt)); + } + void add(uint32_t num) { + mSum += (double)num; + mSquareSum += (double)num * (double)num; + mCnt++; + } + void evict(uint32_t num) { + if (mSum < num || mSquareSum < (double)num * (double)num) return; + mSum -= (double)num; + mSquareSum -= (double)num * (double)num; + mCnt--; + } +}; + +class disk_stats_monitor { +private: + FRIEND_TEST(storaged_test, disk_stats_monitor); + const char* const DISK_STATS_PATH; + struct disk_stats mPrevious; + struct disk_stats mAccumulate; /* reset after stall */ + struct disk_stats mAccumulate_pub; /* reset after publish */ + bool mStall; + std::queue mBuffer; + struct { + stream_stats read_perf; // read speed (bytes/s) + stream_stats read_ios; // read I/Os per second + stream_stats write_perf; // write speed (bytes/s) + stream_stats write_ios; // write I/O per second + stream_stats queue; // I/Os in queue + } mStats; + bool mValid; + const uint32_t mWindow; + const double mSigma; + struct disk_perf mMean; + struct disk_perf mStd; + + void update_mean(); + void update_std(); + void add(struct disk_perf* perf); + void evict(struct disk_perf* perf); + bool detect(struct disk_perf* perf); + + void update(struct disk_stats* stats); + +public: + disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) : + DISK_STATS_PATH(access(MMC_DISK_STATS_PATH, R_OK) ? + (access(SDA_DISK_STATS_PATH, R_OK) ? + nullptr : + SDA_DISK_STATS_PATH) : + MMC_DISK_STATS_PATH), + mPrevious(), mAccumulate(), mAccumulate_pub(), + mStall(false), mValid(false), + mWindow(window_size), mSigma(sigma), + mMean(), mStd() {} + bool enabled() { + return DISK_STATS_PATH != nullptr; + } + void update(void); + void publish(void); +}; + +#endif /* _STORAGED_DISKSTATS_H_ */ \ No newline at end of file diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h index 86d3c2875..c005c4c5f 100644 --- a/storaged/include/storaged_uid_monitor.h +++ b/storaged/include/storaged_uid_monitor.h @@ -85,6 +85,18 @@ struct uid_records { std::vector entries; }; +class lock_t { + sem_t* mSem; +public: + lock_t(sem_t* sem) { + mSem = sem; + sem_wait(mSem); + } + ~lock_t() { + sem_post(mSem); + } +}; + class uid_monitor { private: // last dump from /proc/uid_io/stats, uid -> uid_info @@ -99,6 +111,8 @@ private: sem_t um_lock; // start time for IO records 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; @@ -126,6 +140,7 @@ public: // 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(); }; diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h index 1435707fc..06ab9554a 100644 --- a/storaged/include/storaged_utils.h +++ b/storaged/include/storaged_utils.h @@ -27,9 +27,8 @@ // Diskstats bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats); struct disk_perf get_disk_perf(struct disk_stats* stats); -struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr); +void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr, struct disk_stats* inc); void add_disk_stats(struct disk_stats* src, struct disk_stats* dst); -bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info); // UID I/O void sort_running_uids_info(std::vector &uids); @@ -37,8 +36,4 @@ void sort_running_uids_info(std::vector &uids); // Logging void log_console_running_uids_info(const std::vector& uids, bool flag_dump_task); -void log_debug_disk_perf(struct disk_perf* perf, const char* type); - -void log_event_disk_stats(struct disk_stats* stats, const char* type); -void log_event_emmc_info(struct emmc_info* info_); #endif /* _STORAGED_UTILS_H_ */ diff --git a/storaged/main.cpp b/storaged/main.cpp index 49bd91608..efab69243 100644 --- a/storaged/main.cpp +++ b/storaged/main.cpp @@ -60,7 +60,7 @@ void* storaged_main(void* /* unused */) { return NULL; } -static void help_message(void) { +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"); diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp index 49592ebe2..0cf80a4b3 100644 --- a/storaged/storaged.cpp +++ b/storaged/storaged.cpp @@ -31,124 +31,8 @@ #include #include -/* disk_stats_publisher */ -void disk_stats_publisher::publish(void) { - // Logging - struct disk_perf perf = get_disk_perf(&mAccumulate); - log_debug_disk_perf(&perf, "regular"); - log_event_disk_stats(&mAccumulate, "regular"); - // Reset global structures - memset(&mAccumulate, 0, sizeof(struct disk_stats)); -} -void disk_stats_publisher::update(void) { - struct disk_stats curr; - if (parse_disk_stats(DISK_STATS_PATH, &curr)) { - struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr); - add_disk_stats(&inc, &mAccumulate); -#ifdef DEBUG -// log_kernel_disk_stats(&mPrevious, "prev stats"); -// log_kernel_disk_stats(&curr, "curr stats"); -// log_kernel_disk_stats(&inc, "inc stats"); -// log_kernel_disk_stats(&mAccumulate, "accumulated stats"); -#endif - mPrevious = curr; - } -} - -/* disk_stats_monitor */ -void disk_stats_monitor::update_mean() { - CHECK(mValid); - mMean.read_perf = (uint32_t)mStats.read_perf.get_mean(); - mMean.read_ios = (uint32_t)mStats.read_ios.get_mean(); - mMean.write_perf = (uint32_t)mStats.write_perf.get_mean(); - mMean.write_ios = (uint32_t)mStats.write_ios.get_mean(); - mMean.queue = (uint32_t)mStats.queue.get_mean(); -} - -void disk_stats_monitor::update_std() { - CHECK(mValid); - mStd.read_perf = (uint32_t)mStats.read_perf.get_std(); - mStd.read_ios = (uint32_t)mStats.read_ios.get_std(); - mStd.write_perf = (uint32_t)mStats.write_perf.get_std(); - mStd.write_ios = (uint32_t)mStats.write_ios.get_std(); - mStd.queue = (uint32_t)mStats.queue.get_std(); -} - -void disk_stats_monitor::add(struct disk_perf* perf) { - mStats.read_perf.add(perf->read_perf); - mStats.read_ios.add(perf->read_ios); - mStats.write_perf.add(perf->write_perf); - mStats.write_ios.add(perf->write_ios); - mStats.queue.add(perf->queue); -} - -void disk_stats_monitor::evict(struct disk_perf* perf) { - mStats.read_perf.evict(perf->read_perf); - mStats.read_ios.evict(perf->read_ios); - mStats.write_perf.evict(perf->write_perf); - mStats.write_ios.evict(perf->write_ios); - mStats.queue.evict(perf->queue); -} - -bool disk_stats_monitor::detect(struct disk_perf* perf) { - return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) && - ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) && - ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf); -} - -void disk_stats_monitor::update(struct disk_stats* stats) { - struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats); - struct disk_perf perf = get_disk_perf(&inc); - // Update internal data structures - if (LIKELY(mValid)) { - CHECK_EQ(mBuffer.size(), mWindow); - - if (UNLIKELY(detect(&perf))) { - mStall = true; - add_disk_stats(&inc, &mAccumulate); - log_debug_disk_perf(&mMean, "stalled_mean"); - log_debug_disk_perf(&mStd, "stalled_std"); - } else { - if (mStall) { - struct disk_perf acc_perf = get_disk_perf(&mAccumulate); - log_debug_disk_perf(&acc_perf, "stalled"); - log_event_disk_stats(&mAccumulate, "stalled"); - mStall = false; - memset(&mAccumulate, 0, sizeof(mAccumulate)); - } - } - - evict(&mBuffer.front()); - mBuffer.pop(); - add(&perf); - mBuffer.push(perf); - - update_mean(); - update_std(); - - } else { /* mValid == false */ - CHECK_LT(mBuffer.size(), mWindow); - add(&perf); - mBuffer.push(perf); - if (mBuffer.size() == mWindow) { - mValid = true; - update_mean(); - update_std(); - } - } - - mPrevious = *stats; -} - -void disk_stats_monitor::update(void) { - struct disk_stats curr; - if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) { - update(&curr); - } -} - -static sp get_battery_properties_service() { +sp get_battery_properties_service() { sp sm = defaultServiceManager(); if (sm == NULL) return NULL; @@ -161,7 +45,7 @@ static sp get_battery_properties_service() { return battery_properties; } -static inline charger_stat_t is_charger_on(int64_t prop) { +inline charger_stat_t is_charger_on(int64_t prop) { return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ? CHARGER_ON : CHARGER_OFF; } @@ -171,7 +55,7 @@ void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) { } void storaged_t::init_battery_service() { - if (!mConfig.proc_uid_io_available) + if (!mUidm.enabled()) return; battery_properties = get_battery_properties_service(); @@ -206,43 +90,39 @@ void storaged_t::report_storage_info() { /* storaged_t */ storaged_t::storaged_t(void) { - if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) { - mConfig.diskstats_available = false; - } else { - mConfig.diskstats_available = true; - } - - mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0); - mConfig.periodic_chores_interval_unit = - property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT); + property_get_int32("ro.storaged.event.interval", + DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT); mConfig.event_time_check_usec = property_get_int32("ro.storaged.event.perf_check", 0); mConfig.periodic_chores_interval_disk_stats_publish = - property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH); + property_get_int32("ro.storaged.disk_stats_pub", + DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH); mConfig.periodic_chores_interval_uid_io = - property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO); + property_get_int32("ro.storaged.uid_io.interval", + DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO); storage_info.reset(storage_info_t::get_storage_info()); mStarttime = time(NULL); + mTimer = 0; } void storaged_t::event(void) { - if (mConfig.diskstats_available) { - mDiskStats.update(); + storage_info->refresh(); + + if (mDsm.enabled()) { mDsm.update(); - storage_info->refresh(); - if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) { - mDiskStats.publish(); + if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) { + mDsm.publish(); } } - if (mConfig.proc_uid_io_available && mTimer && - (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) { + if (mUidm.enabled() && + !(mTimer % mConfig.periodic_chores_interval_uid_io)) { mUidm.report(); } diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp new file mode 100644 index 000000000..0604e0ad5 --- /dev/null +++ b/storaged/storaged_diskstats.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "storaged" + +#include +#include + +#include + +#include +#include +#include + +#include "storaged.h" +#include "storaged_diskstats.h" + +namespace { + +#ifdef DEBUG +void log_debug_disk_perf(struct disk_perf* perf, const char* type) { + // skip if the input structure are all zeros + if (perf == NULL || perf->is_zero()) return; + + LOG_TO(SYSTEM, INFO) << "disk_perf " << type + << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops" + << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops" + << " q: " << perf->queue; +} +#else +void log_debug_disk_perf(struct disk_perf* perf, const char* type) {} +#endif + +void log_event_disk_stats(struct disk_stats* stats, const char* type) { + // skip if the input structure are all zeros + if (stats == NULL || stats->is_zero()) return; + + android_log_event_list(EVENTLOGTAG_DISKSTATS) + << type << stats->start_time << stats->end_time + << stats->read_ios << stats->read_merges + << stats->read_sectors << stats->read_ticks + << stats->write_ios << stats->write_merges + << stats->write_sectors << stats->write_ticks + << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue + << LOG_ID_EVENTS; +} + +} // namespace + +bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) +{ + // Get time + struct timespec ts; + // Use monotonic to exclude suspend time so that we measure IO bytes/sec + // when system is running. + int ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret < 0) { + PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed"; + return false; + } + + std::string buffer; + if (!android::base::ReadFileToString(disk_stats_path, &buffer)) { + PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed."; + return false; + } + + // Regular diskstats entries + std::stringstream ss(buffer); + for (uint i = 0; i < DISK_STATS_SIZE; ++i) { + ss >> *((uint64_t*)stats + i); + } + // Other entries + stats->start_time = 0; + stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + + ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC); + stats->counter = 1; + stats->io_avg = (double)stats->io_in_flight; + return true; +} + +struct disk_perf get_disk_perf(struct disk_stats* stats) +{ + struct disk_perf perf = {}; + + if (stats->io_ticks) { + if (stats->read_ticks) { + unsigned long long divisor = stats->read_ticks * stats->io_ticks; + perf.read_perf = ((unsigned long long)SECTOR_SIZE * + stats->read_sectors * stats->io_in_queue + + (divisor >> 1)) / divisor; + perf.read_ios = ((unsigned long long)SEC_TO_MSEC * + stats->read_ios * stats->io_in_queue + + (divisor >> 1)) / divisor; + } + if (stats->write_ticks) { + unsigned long long divisor = stats->write_ticks * stats->io_ticks; + perf.write_perf = ((unsigned long long)SECTOR_SIZE * + stats->write_sectors * stats->io_in_queue + + (divisor >> 1)) / divisor; + perf.write_ios = ((unsigned long long)SEC_TO_MSEC * + stats->write_ios * stats->io_in_queue + + (divisor >> 1)) / divisor; + } + perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) / + stats->io_ticks; + } + return perf; +} + +void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr, + struct disk_stats* inc) +{ + *inc = *curr - *prev; + inc->start_time = prev->end_time; + inc->end_time = curr->end_time; + inc->io_avg = curr->io_avg; + inc->counter = 1; +} + +// Add src to dst +void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) +{ + if (dst->end_time != 0 && dst->end_time != src->start_time) { + LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats" + << " are added. dst end with " << dst->end_time + << ", src start with " << src->start_time; + } + + *dst += *src; + + dst->io_in_flight = src->io_in_flight; + if (dst->counter + src->counter) { + dst->io_avg = + ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) / + (dst->counter + src->counter); + } + dst->counter += src->counter; + dst->end_time = src->end_time; + if (dst->start_time == 0) { + dst->start_time = src->start_time; + } +} + +/* disk_stats_monitor */ +void disk_stats_monitor::update_mean() +{ + CHECK(mValid); + mMean.read_perf = (uint32_t)mStats.read_perf.get_mean(); + mMean.read_ios = (uint32_t)mStats.read_ios.get_mean(); + mMean.write_perf = (uint32_t)mStats.write_perf.get_mean(); + mMean.write_ios = (uint32_t)mStats.write_ios.get_mean(); + mMean.queue = (uint32_t)mStats.queue.get_mean(); +} + +void disk_stats_monitor::update_std() +{ + CHECK(mValid); + mStd.read_perf = (uint32_t)mStats.read_perf.get_std(); + mStd.read_ios = (uint32_t)mStats.read_ios.get_std(); + mStd.write_perf = (uint32_t)mStats.write_perf.get_std(); + mStd.write_ios = (uint32_t)mStats.write_ios.get_std(); + mStd.queue = (uint32_t)mStats.queue.get_std(); +} + +void disk_stats_monitor::add(struct disk_perf* perf) +{ + mStats.read_perf.add(perf->read_perf); + mStats.read_ios.add(perf->read_ios); + mStats.write_perf.add(perf->write_perf); + mStats.write_ios.add(perf->write_ios); + mStats.queue.add(perf->queue); +} + +void disk_stats_monitor::evict(struct disk_perf* perf) { + mStats.read_perf.evict(perf->read_perf); + mStats.read_ios.evict(perf->read_ios); + mStats.write_perf.evict(perf->write_perf); + mStats.write_ios.evict(perf->write_ios); + mStats.queue.evict(perf->queue); +} + +bool disk_stats_monitor::detect(struct disk_perf* perf) +{ + return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) && + ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) && + ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf); +} + +void disk_stats_monitor::update(struct disk_stats* curr) +{ + disk_stats inc; + get_inc_disk_stats(&mPrevious, curr, &inc); + add_disk_stats(&inc, &mAccumulate_pub); + + struct disk_perf perf = get_disk_perf(&inc); + log_debug_disk_perf(&perf, "regular"); + + add(&perf); + mBuffer.push(perf); + if (mBuffer.size() > mWindow) { + evict(&mBuffer.front()); + mBuffer.pop(); + mValid = true; + } + + // Update internal data structures + if (LIKELY(mValid)) { + CHECK_EQ(mBuffer.size(), mWindow); + update_mean(); + update_std(); + if (UNLIKELY(detect(&perf))) { + mStall = true; + add_disk_stats(&inc, &mAccumulate); + log_debug_disk_perf(&mMean, "stalled_mean"); + log_debug_disk_perf(&mStd, "stalled_std"); + } else { + if (mStall) { + struct disk_perf acc_perf = get_disk_perf(&mAccumulate); + log_debug_disk_perf(&acc_perf, "stalled"); + log_event_disk_stats(&mAccumulate, "stalled"); + mStall = false; + memset(&mAccumulate, 0, sizeof(mAccumulate)); + } + } + } + + mPrevious = *curr; +} + +void disk_stats_monitor::update() { + disk_stats curr; + if (!parse_disk_stats(DISK_STATS_PATH, &curr)) { + return; + } + + update(&curr); +} + +void disk_stats_monitor::publish(void) +{ + struct disk_perf perf = get_disk_perf(&mAccumulate_pub); + log_debug_disk_perf(&perf, "regular"); + log_event_disk_stats(&mAccumulate, "regular"); + // Reset global structures + memset(&mAccumulate_pub, 0, sizeof(struct disk_stats)); +} diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp index b5fb13e0c..fc2351f90 100644 --- a/storaged/storaged_info.cpp +++ b/storaged/storaged_info.cpp @@ -39,12 +39,16 @@ const char* emmc_info_t::emmc_ver_str[9] = { const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health"; -static bool FileExists(const std::string& filename) +namespace { + +bool FileExists(const std::string& filename) { struct stat buffer; return stat(filename.c_str(), &buffer) == 0; } +} // namespace + storage_info_t* storage_info_t::get_storage_info() { if (FileExists(emmc_info_t::emmc_sysfs) || @@ -121,6 +125,8 @@ bool emmc_info_t::report_sysfs() return true; } +namespace { + const size_t EXT_CSD_FILE_MIN_SIZE = 1024; /* 2 characters in string for each byte */ const size_t EXT_CSD_REV_IDX = 192 * 2; @@ -128,6 +134,8 @@ const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2; const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2; const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2; +} // namespace + bool emmc_info_t::report_debugfs() { string buffer; diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp index a69bc3391..66a9e355f 100644 --- a/storaged/storaged_uid_monitor.cpp +++ b/storaged/storaged_uid_monitor.cpp @@ -45,11 +45,16 @@ using namespace android::content::pm; using namespace google::protobuf::io; using namespace storaged_proto; -static bool refresh_uid_names; -static const uint32_t crc_init = 0x5108A4ED; /* STORAGED */ +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"; +"/data/misc/storaged/io_history.proto"; std::unordered_map uid_monitor::get_uid_io_stats() { @@ -119,7 +124,9 @@ bool io_usage::is_zero() const return true; } -static void get_uid_names(const vector& uids, const vector& uid_names) +namespace { + +void get_uid_names(const vector& uids, const vector& uid_names) { sp sm = defaultServiceManager(); if (sm == NULL) { @@ -151,6 +158,8 @@ static void get_uid_names(const vector& uids, const vector& u refresh_uid_names = false; } +} // namespace + std::unordered_map uid_monitor::get_uid_io_stats_locked() { std::unordered_map uid_io_stats; @@ -197,9 +206,11 @@ std::unordered_map uid_monitor::get_uid_io_stats_lock return uid_io_stats; } -static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours +namespace { -static inline size_t history_size( +const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours + +inline size_t history_size( const std::map& history) { size_t count = 0; @@ -209,6 +220,8 @@ static inline size_t history_size( return count; } +} // namespace + void uid_monitor::add_records_locked(uint64_t curr_ts) { // remove records more than 5 days old @@ -367,7 +380,9 @@ void uid_monitor::report() flush_io_history_to_proto(); } -static void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usage) +namespace { + +void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usage) { usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]); usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]); @@ -379,7 +394,7 @@ static void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usag usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]); } -static void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto) +void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto) { usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on(); usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off(); @@ -391,6 +406,8 @@ static void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto) usage->bytes[WRITE][BACKGROUND][CHARGER_OFF] = io_proto.wr_bg_chg_off(); } +} // namespace + void uid_monitor::flush_io_history_to_proto() { UidIOHistoryProto out_proto; @@ -510,6 +527,7 @@ void uid_monitor::init(charger_stat_t stat) } uid_monitor::uid_monitor() + : enable(!access(UID_IO_STATS_PATH, R_OK)) { sem_init(&um_lock, 0, 1); } diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp index 59cf25118..533bf7805 100644 --- a/storaged/storaged_utils.cpp +++ b/storaged/storaged_utils.cpp @@ -42,124 +42,7 @@ #include #include -bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) { - // Get time - struct timespec ts; - // Use monotonic to exclude suspend time so that we measure IO bytes/sec - // when system is running. - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - if (ret < 0) { - PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed"; - return false; - } - - std::string buffer; - if (!android::base::ReadFileToString(disk_stats_path, &buffer)) { - PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed."; - return false; - } - - // Regular diskstats entries - std::stringstream ss(buffer); - for (uint i = 0; i < DISK_STATS_SIZE; ++i) { - ss >> *((uint64_t*)stats + i); - } - // Other entries - stats->start_time = 0; - stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + - ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC); - stats->counter = 1; - stats->io_avg = (double)stats->io_in_flight; - return true; -} - -struct disk_perf get_disk_perf(struct disk_stats* stats) { - struct disk_perf perf; - memset(&perf, 0, sizeof(struct disk_perf)); // initialize - - if (stats->io_ticks) { - if (stats->read_ticks) { - unsigned long long divisor = stats->read_ticks * stats->io_ticks; - perf.read_perf = ((unsigned long long)SECTOR_SIZE * - stats->read_sectors * - stats->io_in_queue + - (divisor >> 1)) / - divisor; - perf.read_ios = ((unsigned long long)SEC_TO_MSEC * - stats->read_ios * - stats->io_in_queue + - (divisor >> 1)) / - divisor; - } - if (stats->write_ticks) { - unsigned long long divisor = stats->write_ticks * stats->io_ticks; - perf.write_perf = ((unsigned long long)SECTOR_SIZE * - stats->write_sectors * - stats->io_in_queue + - (divisor >> 1)) / - divisor; - perf.write_ios = ((unsigned long long)SEC_TO_MSEC * - stats->write_ios * - stats->io_in_queue + - (divisor >> 1)) / - divisor; - } - perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) / - stats->io_ticks; - } - return perf; -} - -struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) { - struct disk_stats inc; - for (uint i = 0; i < DISK_STATS_SIZE; ++i) { - if (i == DISK_STATS_IO_IN_FLIGHT_IDX) { - continue; - } - - *((uint64_t*)&inc + i) = - *((uint64_t*)curr + i) - *((uint64_t*)prev + i); - } - // io_in_flight is exception - inc.io_in_flight = curr->io_in_flight; - - inc.start_time = prev->end_time; - inc.end_time = curr->end_time; - inc.io_avg = curr->io_avg; - inc.counter = 1; - - return inc; -} - -// Add src to dst -void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) { - if (dst->end_time != 0 && dst->end_time != src->start_time) { - LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats" - << " are added. dst end with " << dst->end_time - << ", src start with " << src->start_time; - } - - for (uint i = 0; i < DISK_STATS_SIZE; ++i) { - if (i == DISK_STATS_IO_IN_FLIGHT_IDX) { - continue; - } - - *((uint64_t*)dst + i) += *((uint64_t*)src + i); - } - - dst->io_in_flight = src->io_in_flight; - if (dst->counter + src->counter) { - dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) / - (dst->counter + src->counter); - } - dst->counter += src->counter; - dst->end_time = src->end_time; - if (dst->start_time == 0) { - dst->start_time = src->start_time; - } -} - -static bool cmp_uid_info(struct uid_info l, struct uid_info r) { +bool cmp_uid_info(struct uid_info l, struct uid_info r) { // Compare background I/O first. for (int i = UID_STATS - 1; i >= 0; i--) { uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes; @@ -208,39 +91,3 @@ void log_console_running_uids_info(const std::vector& uids, boo } fflush(stdout); } - -#if DEBUG -void log_debug_disk_perf(struct disk_perf* perf, const char* type) { - // skip if the input structure are all zeros - if (perf == NULL) return; - struct disk_perf zero_cmp; - memset(&zero_cmp, 0, sizeof(zero_cmp)); - if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return; - - LOG_TO(SYSTEM, INFO) << "perf(ios) " << type - << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)" - << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)" - << " q:" << perf->queue; -} -#else -void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {} -#endif - -void log_event_disk_stats(struct disk_stats* stats, const char* type) { - // skip if the input structure are all zeros - if (stats == NULL) return; - struct disk_stats zero_cmp; - memset(&zero_cmp, 0, sizeof(zero_cmp)); - // skip event logging diskstats when it is zero increment (all first 11 entries are zero) - if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return; - - android_log_event_list(EVENTLOGTAG_DISKSTATS) - << type << stats->start_time << stats->end_time - << stats->read_ios << stats->read_merges - << stats->read_sectors << stats->read_ticks - << stats->write_ios << stats->write_merges - << stats->write_sectors << stats->write_ticks - << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue - << LOG_ID_EVENTS; -} - diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp index b103ac1ec..bd14d766f 100644 --- a/storaged/tests/storaged_test.cpp +++ b/storaged/tests/storaged_test.cpp @@ -30,7 +30,9 @@ #define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat" #define SDA_DISK_STATS_PATH "/sys/block/sda/stat" -static void pause(uint32_t sec) { +namespace { + +void write_and_pause(uint32_t sec) { const char* path = "/cache/test"; int fd = open(path, O_WRONLY | O_CREAT, 0600); ASSERT_LT(-1, fd); @@ -53,6 +55,8 @@ static void pause(uint32_t sec) { sleep(sec); } +} // namespace + // the return values of the tested functions should be the expected ones const char* DISK_STATS_PATH; TEST(storaged_test, retvals) { @@ -77,13 +81,11 @@ TEST(storaged_test, retvals) { EXPECT_FALSE(parse_disk_stats(wrong_path, &stats)); // reading a wrong path should not damage the output structure - EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats))); + EXPECT_EQ(stats, old_stats); } TEST(storaged_test, disk_stats) { - struct disk_stats stats; - memset(&stats, 0, sizeof(struct disk_stats)); - + struct disk_stats stats = {}; ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats)); // every entry of stats (except io_in_flight) should all be greater than 0 @@ -93,11 +95,7 @@ TEST(storaged_test, disk_stats) { } // accumulation of the increments should be the same with the overall increment - struct disk_stats base, tmp, curr, acc, inc[5]; - memset(&base, 0, sizeof(struct disk_stats)); - memset(&tmp, 0, sizeof(struct disk_stats)); - memset(&acc, 0, sizeof(struct disk_stats)); - + struct disk_stats base = {}, tmp = {}, curr, acc = {}, inc[5]; for (uint i = 0; i < 5; ++i) { ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr)); if (i == 0) { @@ -106,22 +104,18 @@ TEST(storaged_test, disk_stats) { sleep(5); continue; } - inc[i] = get_inc_disk_stats(&tmp, &curr); + get_inc_disk_stats(&tmp, &curr, &inc[i]); add_disk_stats(&inc[i], &acc); tmp = curr; - pause(5); + write_and_pause(5); } - struct disk_stats overall_inc; - memset(&overall_inc, 0, sizeof(disk_stats)); - overall_inc= get_inc_disk_stats(&base, &curr); + struct disk_stats overall_inc = {}; + get_inc_disk_stats(&base, &curr, &overall_inc); - for (uint i = 0; i < DISK_STATS_SIZE; ++i) { - if (i == 8) continue; // skip io_in_flight which can be 0 - EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i)); - } + EXPECT_EQ(overall_inc, acc); } -static double mean(std::deque nums) { +double mean(std::deque nums) { double sum = 0.0; for (uint32_t i : nums) { sum += i; @@ -129,7 +123,7 @@ static double mean(std::deque nums) { return sum / nums.size(); } -static double standard_deviation(std::deque nums) { +double standard_deviation(std::deque nums) { double sum = 0.0; double avg = mean(nums); for (uint32_t i : nums) { @@ -181,7 +175,7 @@ TEST(storaged_test, stream_stats) { } } -static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) { +struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) { struct disk_perf retval; retval.read_perf = (double)perf.read_perf * mul; retval.read_ios = (double)perf.read_ios * mul; @@ -192,7 +186,7 @@ static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) { return retval; } -static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) { +struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) { struct disk_stats retval; retval.read_ios = stats1.read_ios + stats2.read_ios; retval.read_merges = stats1.read_merges + stats2.read_merges; @@ -210,6 +204,30 @@ static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_st return retval; } +void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) { + EXPECT_LE(stats1.read_ios, stats2.read_ios); + EXPECT_LE(stats1.read_merges, stats2.read_merges); + EXPECT_LE(stats1.read_sectors, stats2.read_sectors); + EXPECT_LE(stats1.read_ticks, stats2.read_ticks); + EXPECT_LE(stats1.write_ios, stats2.write_ios); + EXPECT_LE(stats1.write_merges, stats2.write_merges); + EXPECT_LE(stats1.write_sectors, stats2.write_sectors); + EXPECT_LE(stats1.write_ticks, stats2.write_ticks); + EXPECT_LE(stats1.io_ticks, stats2.io_ticks); + EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue); + + EXPECT_TRUE(stats1.read_ios < stats2.read_ios || + stats1.read_merges < stats2.read_merges || + stats1.read_sectors < stats2.read_sectors || + stats1.read_ticks < stats2.read_ticks || + stats1.write_ios < stats2.write_ios || + stats1.write_merges < stats2.write_merges || + stats1.write_sectors < stats2.write_sectors || + stats1.write_ticks < stats2.write_ticks || + stats1.io_ticks < stats2.io_ticks || + stats1.io_in_queue < stats2.io_in_queue); +} + TEST(storaged_test, disk_stats_monitor) { // asserting that there is one file for diskstats ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0); @@ -294,14 +312,12 @@ TEST(storaged_test, disk_stats_monitor) { .io_avg = 0 }; - struct disk_stats stats_base; - memset(&stats_base, 0, sizeof(stats_base)); - + struct disk_stats stats_base = {}; int loop_size = 100; for (int i = 0; i < loop_size; ++i) { stats_base = disk_stats_add(stats_base, norm_inc); dsm_acc.update(&stats_base); - EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow); + EXPECT_EQ(dsm_acc.mValid, (uint32_t)i >= dsm_acc.mWindow); EXPECT_FALSE(dsm_acc.mStall); } @@ -316,36 +332,14 @@ TEST(storaged_test, disk_stats_monitor) { EXPECT_TRUE(dsm_acc.mValid); EXPECT_FALSE(dsm_acc.mStall); } -} -static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) { - EXPECT_LE(stats1.read_ios, stats2.read_ios); - EXPECT_LE(stats1.read_merges, stats2.read_merges); - EXPECT_LE(stats1.read_sectors, stats2.read_sectors); - EXPECT_LE(stats1.read_ticks, stats2.read_ticks); - - EXPECT_LE(stats1.write_ios, stats2.write_ios); - EXPECT_LE(stats1.write_merges, stats2.write_merges); - EXPECT_LE(stats1.write_sectors, stats2.write_sectors); - EXPECT_LE(stats1.write_ticks, stats2.write_ticks); - - EXPECT_LE(stats1.io_ticks, stats2.io_ticks); - EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue); -} - -#define TEST_LOOPS 20 -TEST(storaged_test, disk_stats_publisher) { - // asserting that there is one file for diskstats - ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0); - disk_stats_publisher dsp; - struct disk_stats prev; - memset(&prev, 0, sizeof(prev)); - - for (int i = 0; i < TEST_LOOPS; ++i) { - dsp.update(); - expect_increasing(prev, dsp.mPrevious); - prev = dsp.mPrevious; - pause(10); + struct disk_stats stats_prev = {}; + loop_size = 10; + write_and_pause(5); + for (int i = 0; i < loop_size; ++i) { + dsm_detect.update(); + expect_increasing(stats_prev, dsm_detect.mPrevious); + stats_prev = dsm_detect.mPrevious; + write_and_pause(5); } } -