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:
parent
8832077aa4
commit
65dea71248
|
@ -44,6 +44,7 @@ cc_library_static {
|
|||
|
||||
srcs: [
|
||||
"storaged.cpp",
|
||||
"storaged_diskstats.cpp",
|
||||
"storaged_info.cpp",
|
||||
"storaged_service.cpp",
|
||||
"storaged_utils.cpp",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue