storaged: clean up disk stats code

Merged disk_stats_publisher into disk_stats_monitor class.

Moved proc file check to disk_stats class instead of doing so in
storaged, same for uid_io.

Added is_zero function to check zero disk_stats.

Added operators to compute disk_stats.

Change-Id: I277ae51f6fe18464f92e45c03471343784e67093
This commit is contained in:
Jin Qian 2017-08-29 16:48:20 -07:00
parent 8832077aa4
commit 65dea71248
12 changed files with 576 additions and 535 deletions

View File

@ -44,6 +44,7 @@ cc_library_static {
srcs: [
"storaged.cpp",
"storaged_diskstats.cpp",
"storaged_info.cpp",
"storaged_service.cpp",
"storaged_utils.cpp",

View File

@ -29,23 +29,15 @@
#include <batteryservice/IBatteryPropertiesListener.h>
#include <batteryservice/IBatteryPropertiesRegistrar.h>
#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<struct disk_perf> 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;

View File

@ -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 <stdint.h>
// 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<struct disk_perf> 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_ */

View File

@ -85,6 +85,18 @@ struct uid_records {
std::vector<struct uid_record> 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();
};

View File

@ -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<struct uid_info> &uids);
@ -37,8 +36,4 @@ 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_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_ */

View File

@ -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");

View File

@ -31,124 +31,8 @@
#include <storaged.h>
#include <storaged_utils.h>
/* 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<IBatteryPropertiesRegistrar> get_battery_properties_service() {
sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) return NULL;
@ -161,7 +45,7 @@ static sp<IBatteryPropertiesRegistrar> 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();
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <sstream>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <log/log_event_list.h>
#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));
}

View File

@ -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;

View File

@ -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<uint32_t, struct uid_info> 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<int>& uids, const vector<std::string*>& uid_names)
namespace {
void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
{
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) {
@ -151,6 +158,8 @@ static void get_uid_names(const vector<int>& uids, const vector<std::string*>& u
refresh_uid_names = false;
}
} // namespace
std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
{
std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
@ -197,9 +206,11 @@ std::unordered_map<uint32_t, struct uid_info> 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<uint64_t, struct uid_records>& 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);
}

View File

@ -42,124 +42,7 @@
#include <storaged.h>
#include <storaged_utils.h>
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<struct uid_info>& 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;
}

View File

@ -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<uint32_t> nums) {
double mean(std::deque<uint32_t> nums) {
double sum = 0.0;
for (uint32_t i : nums) {
sum += i;
@ -129,7 +123,7 @@ static double mean(std::deque<uint32_t> nums) {
return sum / nums.size();
}
static double standard_deviation(std::deque<uint32_t> nums) {
double standard_deviation(std::deque<uint32_t> 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);
}
}