storaged: remove task io code
Bug: 34612499 Change-Id: Id0599ee2ae025a186259e95363c1ddd0feae8079
This commit is contained in:
parent
3790f5bfaf
commit
88ad33eff1
9 changed files with 4 additions and 526 deletions
|
@ -123,28 +123,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class tasks_t {
|
||||
private:
|
||||
FRIEND_TEST(storaged_test, tasks_t);
|
||||
sem_t mSem;
|
||||
// hashmap for all running tasks w/ pid as key
|
||||
std::unordered_map<uint32_t, struct task_info> mRunning;
|
||||
// hashmap for all tasks that have been killed (categorized by cmd) w/ cmd as key
|
||||
std::unordered_map<std::string, struct task_info> mOld;
|
||||
std::unordered_map<std::uint32_t, struct task_info> get_running_tasks();
|
||||
public:
|
||||
tasks_t() {
|
||||
sem_init(&mSem, 0, 1); // TODO: constructor don't have a return value, what if sem_init fails
|
||||
}
|
||||
|
||||
~tasks_t() {
|
||||
sem_destroy(&mSem);
|
||||
}
|
||||
|
||||
void update_running_tasks(void);
|
||||
std::vector<struct task_info> get_tasks(void);
|
||||
};
|
||||
|
||||
class stream_stats {
|
||||
private:
|
||||
double mSum;
|
||||
|
@ -282,7 +260,6 @@ struct storaged_config {
|
|||
int periodic_chores_interval_disk_stats_publish;
|
||||
int periodic_chores_interval_emmc_info_publish;
|
||||
int periodic_chores_interval_uid_io;
|
||||
bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable
|
||||
bool proc_uid_io_available; // whether uid_io is accessible
|
||||
bool emmc_available; // whether eMMC est_csd file is readable
|
||||
bool diskstats_available; // whether diskstats is accessible
|
||||
|
@ -296,7 +273,6 @@ private:
|
|||
disk_stats_publisher mDiskStats;
|
||||
disk_stats_monitor mDsm;
|
||||
emmc_info_t mEmmcInfo;
|
||||
tasks_t mTasks;
|
||||
uid_monitor mUidm;
|
||||
time_t mStarttime;
|
||||
public:
|
||||
|
@ -307,15 +283,6 @@ public:
|
|||
void pause(void) {
|
||||
sleep(mConfig.periodic_chores_interval_unit);
|
||||
}
|
||||
std::vector<struct task_info> get_tasks(void) {
|
||||
// There could be a race when get_tasks() and the main thread is updating at the same time
|
||||
// While update_running_tasks() is updating the critical sections at the end of the function
|
||||
// all together atomically, the final state of task_t can only be either the main thread's
|
||||
// update or this update. Since the race can only occur when both threads are updating
|
||||
// "simultaneously", either final state is acceptable.
|
||||
mTasks.update_running_tasks();
|
||||
return mTasks.get_tasks();
|
||||
}
|
||||
|
||||
void set_privileged_fds(int fd_emmc) {
|
||||
mEmmcInfo.set_emmc_fd(fd_emmc);
|
||||
|
|
|
@ -30,11 +30,9 @@ using namespace android;
|
|||
class IStoraged : public IInterface {
|
||||
public:
|
||||
enum {
|
||||
DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION,
|
||||
DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION + 1,
|
||||
DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
|
||||
};
|
||||
// Request the service to run the test function
|
||||
virtual std::vector<struct task_info> dump_tasks(const char* option) = 0;
|
||||
virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
|
||||
|
||||
DECLARE_META_INTERFACE(Storaged);
|
||||
|
@ -44,7 +42,6 @@ public:
|
|||
class BpStoraged : public BpInterface<IStoraged> {
|
||||
public:
|
||||
BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
|
||||
virtual std::vector<struct task_info> dump_tasks(const char* option);
|
||||
virtual std::vector<struct uid_info> dump_uids(const char* option);
|
||||
};
|
||||
|
||||
|
@ -54,7 +51,6 @@ class BnStoraged : public BnInterface<IStoraged> {
|
|||
};
|
||||
|
||||
class Storaged : public BnStoraged {
|
||||
virtual std::vector<struct task_info> dump_tasks(const char* option);
|
||||
virtual std::vector<struct uid_info> dump_uids(const char* option);
|
||||
};
|
||||
|
||||
|
|
|
@ -31,14 +31,10 @@ struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats*
|
|||
void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
|
||||
bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
|
||||
|
||||
// Task I/O
|
||||
bool parse_task_info(uint32_t pid, struct task_info* info);
|
||||
void sort_running_tasks_info(std::vector<struct task_info> &tasks);
|
||||
// UID I/O
|
||||
void sort_running_uids_info(std::vector<struct uid_info> &uids);
|
||||
|
||||
// Logging
|
||||
void log_console_running_tasks_info(std::vector<struct task_info> tasks);
|
||||
void log_console_running_uids_info(std::vector<struct uid_info> uids);
|
||||
|
||||
void log_debug_disk_perf(struct disk_perf* perf, const char* type);
|
||||
|
|
|
@ -61,8 +61,7 @@ static int drop_privs() {
|
|||
if (cap_clear(caps.get()) < 0) return -1;
|
||||
cap_value_t cap_value[] = {
|
||||
CAP_SETGID,
|
||||
CAP_SETUID,
|
||||
CAP_SYS_PTRACE // allow access to proc/<pid>/io as non-root user
|
||||
CAP_SETUID
|
||||
};
|
||||
if (cap_set_flag(caps.get(), CAP_PERMITTED,
|
||||
arraysize(cap_value), cap_value,
|
||||
|
@ -73,10 +72,6 @@ static int drop_privs() {
|
|||
if (cap_set_proc(caps.get()) < 0)
|
||||
return -1;
|
||||
|
||||
gid_t groups[] = { AID_READPROC };
|
||||
|
||||
if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) return -1;
|
||||
|
||||
if (setgid(AID_SYSTEM) != 0) return -1;
|
||||
|
||||
if (setuid(AID_SYSTEM) != 0) return -1;
|
||||
|
@ -104,7 +99,6 @@ void* storaged_main(void* s) {
|
|||
|
||||
static void help_message(void) {
|
||||
printf("usage: storaged [OPTION]\n");
|
||||
printf(" -d --dump Dump task I/O usage to stdout\n");
|
||||
printf(" -u --uid Dump uid I/O usage to stdout\n");
|
||||
printf(" -s --start Start storaged (default)\n");
|
||||
fflush(stdout);
|
||||
|
@ -115,7 +109,6 @@ static void help_message(void) {
|
|||
|
||||
int main(int argc, char** argv) {
|
||||
int flag_main_service = 0;
|
||||
int flag_dump_task = 0;
|
||||
int flag_dump_uid = 0;
|
||||
int fd_emmc = -1;
|
||||
int opt;
|
||||
|
@ -125,7 +118,6 @@ int main(int argc, char** argv) {
|
|||
static struct option long_options[] = {
|
||||
{"start", no_argument, 0, 's'},
|
||||
{"kill", no_argument, 0, 'k'},
|
||||
{"dump", no_argument, 0, 'd'},
|
||||
{"uid", no_argument, 0, 'u'},
|
||||
{"help", no_argument, 0, 'h'}
|
||||
};
|
||||
|
@ -138,9 +130,6 @@ int main(int argc, char** argv) {
|
|||
case 's':
|
||||
flag_main_service = 1;
|
||||
break;
|
||||
case 'd':
|
||||
flag_dump_task = 1;
|
||||
break;
|
||||
case 'u':
|
||||
flag_dump_uid = 1;
|
||||
break;
|
||||
|
@ -159,7 +148,7 @@ int main(int argc, char** argv) {
|
|||
flag_main_service = 1;
|
||||
}
|
||||
|
||||
if (flag_main_service && flag_dump_task) {
|
||||
if (flag_main_service && flag_dump_uid) {
|
||||
fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
|
||||
help_message();
|
||||
return -1;
|
||||
|
@ -195,34 +184,6 @@ int main(int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (flag_dump_task) {
|
||||
sp<IStoraged> storaged_service = get_storaged_service();
|
||||
if (storaged_service == NULL) {
|
||||
fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
|
||||
return -1;
|
||||
}
|
||||
std::vector<struct task_info> res = storaged_service->dump_tasks(NULL);
|
||||
|
||||
if (res.size() == 0) {
|
||||
fprintf(stderr, "Task I/O is not readable in this version of kernel.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t starttime = storaged.get_starttime();
|
||||
|
||||
if (starttime == (time_t)-1) {
|
||||
fprintf(stderr, "Unknown start time\n");
|
||||
} else {
|
||||
char* time_str = ctime(&starttime);
|
||||
printf("Application I/O was collected by storaged since %s", time_str);
|
||||
}
|
||||
|
||||
sort_running_tasks_info(res);
|
||||
log_console_running_tasks_info(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flag_dump_uid) {
|
||||
sp<IStoraged> storaged_service = get_storaged_service();
|
||||
if (storaged_service == NULL) {
|
||||
|
|
|
@ -166,15 +166,6 @@ storaged_t::storaged_t(void) {
|
|||
mConfig.diskstats_available = true;
|
||||
}
|
||||
|
||||
mConfig.proc_taskio_readable = true;
|
||||
const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
|
||||
for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
|
||||
if (access(test_paths[i], R_OK) < 0) {
|
||||
mConfig.proc_taskio_readable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
|
||||
|
||||
mConfig.periodic_chores_interval_unit =
|
||||
|
@ -205,12 +196,6 @@ void storaged_t::event(void) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (mConfig.proc_taskio_readable) {
|
||||
mTasks.update_running_tasks();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mConfig.emmc_available && mTimer &&
|
||||
(mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
|
||||
mEmmcInfo.update();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
service storaged /system/bin/storaged
|
||||
class main
|
||||
file /d/mmc0/mmc0:0001/ext_csd r
|
||||
group root readproc
|
||||
writepid /dev/cpuset/system-background/tasks
|
||||
|
|
|
@ -29,20 +29,6 @@
|
|||
|
||||
extern storaged_t storaged;
|
||||
|
||||
std::vector<struct task_info> BpStoraged::dump_tasks(const char* /*option*/) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
|
||||
|
||||
remote()->transact(DUMPTASKS, data, &reply);
|
||||
|
||||
uint32_t res_size = reply.readInt32();
|
||||
std::vector<struct task_info> res(res_size);
|
||||
for (auto&& task : res) {
|
||||
reply.read(&task, sizeof(task));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
|
||||
|
@ -65,16 +51,6 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply
|
|||
data.checkInterface(this);
|
||||
|
||||
switch(code) {
|
||||
case DUMPTASKS: {
|
||||
std::vector<struct task_info> res = dump_tasks(NULL);
|
||||
|
||||
reply->writeInt32(res.size());
|
||||
for (auto task : res) {
|
||||
reply->write(&task, sizeof(task));
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
break;
|
||||
case DUMPUIDS: {
|
||||
std::vector<struct uid_info> res = dump_uids(NULL);
|
||||
reply->writeInt32(res.size());
|
||||
|
@ -91,10 +67,6 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
|
||||
return storaged.get_tasks();
|
||||
}
|
||||
|
||||
std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
|
||||
std::vector<struct uid_info> uids_v;
|
||||
std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids();
|
||||
|
|
|
@ -245,191 +245,6 @@ bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#define PROC_DIR "/proc/"
|
||||
#define PROC_STAT_STARTTIME_IDX ( 22 ) // This index is 1 based according to the linux proc man page
|
||||
bool parse_task_info(uint32_t pid, struct task_info* info) {
|
||||
std::string buffer;
|
||||
std::string pid_str = std::to_string(pid);
|
||||
info->pid = pid;
|
||||
|
||||
// Get task I/O
|
||||
std::string task_io_path = android::base::StringPrintf(PROC_DIR "%s/io", pid_str.c_str());
|
||||
if (!android::base::ReadFileToString(task_io_path, &buffer)) return false;
|
||||
|
||||
std::stringstream ss(buffer);
|
||||
std::string title;
|
||||
|
||||
ss >> title >> info->rchar
|
||||
>> title >> info->wchar
|
||||
>> title >> info->syscr
|
||||
>> title >> info->syscw
|
||||
>> title >> info->read_bytes
|
||||
>> title >> info->write_bytes
|
||||
>> title >> info->cancelled_write_bytes;
|
||||
ss.clear();
|
||||
|
||||
// Get cmd string
|
||||
std::string task_cmdline_path = android::base::StringPrintf(PROC_DIR "%u/cmdline", pid);
|
||||
if (!android::base::ReadFileToString(task_cmdline_path, &buffer)) return false;
|
||||
strlcpy(info->cmd, android::base::Trim(buffer).c_str(), sizeof(info->cmd));
|
||||
|
||||
if (info->cmd[0] == '\0') {
|
||||
std::string task_comm_path = android::base::StringPrintf(PROC_DIR "%u/comm", pid);
|
||||
if (!android::base::ReadFileToString(task_comm_path, &buffer)) return false;
|
||||
strlcpy(info->cmd, android::base::Trim(buffer).c_str(), sizeof(info->cmd));
|
||||
}
|
||||
|
||||
// Get task start time
|
||||
std::string task_stat_path = android::base::StringPrintf(PROC_DIR "%u/stat", pid);
|
||||
if (!android::base::ReadFileToString(task_stat_path, &buffer)) return false;
|
||||
|
||||
std::vector<std::string> stat_parts = android::base::Split(buffer, " ");
|
||||
info->starttime = atoll(stat_parts[PROC_STAT_STARTTIME_IDX - 1].c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_pid(char* d_name) {
|
||||
if (!d_name || d_name[0] == '\0') return false;
|
||||
char* c = d_name;
|
||||
while (*c) {
|
||||
if (!isdigit(*c)) return false;
|
||||
++c;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cmp_task_info(struct task_info i, struct task_info j) {
|
||||
if (i.write_bytes + i.read_bytes != j.write_bytes + j.read_bytes) {
|
||||
return i.write_bytes + i.read_bytes > j.write_bytes + j.read_bytes;
|
||||
}
|
||||
if (i.wchar + i.rchar != j.wchar + j.rchar) {
|
||||
return i.wchar + i.rchar > j.wchar + j.rchar;
|
||||
}
|
||||
if (i.syscw + i.syscr != j.syscw + j.syscr) {
|
||||
return i.syscw + i.syscr > j.syscw + j.syscr;
|
||||
}
|
||||
|
||||
return strcmp(i.cmd, j.cmd) < 0;
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, struct task_info> tasks_t::get_running_tasks() {
|
||||
std::unordered_map<uint32_t, struct task_info> retval;
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(PROC_DIR), closedir);
|
||||
CHECK(dir != NULL);
|
||||
struct dirent* dp;
|
||||
|
||||
for (;;) {
|
||||
if ((dp = readdir(dir.get())) == NULL) break;
|
||||
if (!is_pid(dp->d_name)) continue;
|
||||
|
||||
uint32_t pid = atol(dp->d_name);
|
||||
struct task_info info;
|
||||
if (parse_task_info(pid, &info)) {
|
||||
retval[pid] = info;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void add_task_info(struct task_info* src, struct task_info* dst) {
|
||||
CHECK(strcmp(src->cmd, dst->cmd) == 0);
|
||||
|
||||
dst->pid = 0;
|
||||
dst->rchar += src->rchar;
|
||||
dst->wchar += src->wchar;
|
||||
dst->syscr += src->syscr;
|
||||
dst->syscw += src->syscw;
|
||||
dst->read_bytes += src->read_bytes;
|
||||
dst->write_bytes += src->write_bytes;
|
||||
dst->cancelled_write_bytes += src->cancelled_write_bytes;
|
||||
dst->starttime = 0;
|
||||
}
|
||||
|
||||
void tasks_t::update_running_tasks(void) {
|
||||
std::unordered_map<uint32_t, struct task_info> tasks_latest = get_running_tasks();
|
||||
std::unordered_map<std::string, struct task_info> tasks_old = mOld;
|
||||
|
||||
for (auto t : mRunning) {
|
||||
uint32_t pid = t.first;
|
||||
// old task on mRunning still exist on tasks_latest
|
||||
if (tasks_latest.find(pid) != tasks_latest.end() &&
|
||||
tasks_latest[pid].starttime == t.second.starttime) {
|
||||
continue;
|
||||
} else {
|
||||
// This branch will handle 2 cases:
|
||||
// - Task get killed between the 2 samplings
|
||||
// - Task get killed and its pid is reused
|
||||
std::string cmd = t.second.cmd;
|
||||
struct task_info info = t.second;
|
||||
|
||||
if (tasks_old.find(cmd) == tasks_old.end()) {
|
||||
tasks_old[cmd] = info;
|
||||
} else {
|
||||
add_task_info(&info, &tasks_old[cmd]);
|
||||
}
|
||||
}
|
||||
}
|
||||
{ // update critical area
|
||||
// this is really fast!
|
||||
std::unique_ptr<lock_t> lock(new lock_t(&mSem));
|
||||
mRunning = tasks_latest;
|
||||
mOld = tasks_old;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<struct task_info> tasks_t::get_tasks(void) {
|
||||
std::unique_ptr<lock_t> lock(new lock_t(&mSem));
|
||||
std::unordered_map<std::string, struct task_info> tasks_map = mOld;
|
||||
|
||||
for (auto i : mRunning) {
|
||||
std::string cmd = i.second.cmd;
|
||||
if (tasks_map.find(cmd) == tasks_map.end()) {
|
||||
tasks_map[cmd] = i.second;
|
||||
} else {
|
||||
add_task_info(&i.second, &tasks_map[cmd]);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<struct task_info> retval(tasks_map.size());
|
||||
int idx = 0;
|
||||
for (auto i : tasks_map) {
|
||||
retval[idx++] = i.second;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void sort_running_tasks_info(std::vector<struct task_info> &tasks) {
|
||||
std::sort(tasks.begin(), tasks.end(), cmp_task_info);
|
||||
}
|
||||
|
||||
/* Logging functions */
|
||||
void log_console_running_tasks_info(std::vector<struct task_info> tasks) {
|
||||
// Sample Output:
|
||||
// Application Read Write Read Write Read Write Cancelled
|
||||
// Name Characters Characters Syscalls Syscalls Bytes Bytes Writebytes
|
||||
// ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
|
||||
// zygote64 37688308 3388467 7607 4363 314519552 5373952 8192
|
||||
// system_server 95874193 2216913 74613 52257 213078016 7237632 16384
|
||||
// zygote 506279 1726194 921 263 128114688 1765376 0
|
||||
// /vendor/bin/qcks 75415632 75154382 21672 25036 63627264 29974528 10485760
|
||||
// /init 86658523 5107871 82113 8633 91015168 1245184 0
|
||||
|
||||
// Title
|
||||
printf(" Application Read Write Read Write Read Write Cancelled\n"
|
||||
" Name Characters Characters Syscalls Syscalls Bytes Bytes Writebytes\n"
|
||||
" ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n");
|
||||
|
||||
for (struct task_info task : tasks) {
|
||||
printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n",
|
||||
task.cmd, task.rchar, task.wchar, task.syscr, task.syscw,
|
||||
task.read_bytes, task.write_bytes, task.cancelled_write_bytes);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
|
||||
// Compare background I/O first.
|
||||
for (int i = UID_STATS_SIZE - 1; i >= 0; i--) {
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
|
||||
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
|
||||
#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
|
||||
#define INIT_TASK_IO_PATH "/proc/1/io"
|
||||
|
||||
static void pause(uint32_t sec) {
|
||||
const char* path = "/cache/test";
|
||||
|
@ -144,46 +143,6 @@ TEST(storaged_test, emmc_info) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(storaged_test, task_info) {
|
||||
// parse_task_info should read something other than 0 from /proc/1/*
|
||||
struct task_info task_info;
|
||||
memset(&task_info, 0, sizeof(task_info));
|
||||
|
||||
if (!parse_task_info(1, &task_info)) return;
|
||||
|
||||
EXPECT_EQ((uint32_t)1, task_info.pid);
|
||||
EXPECT_LT((uint64_t)0, task_info.rchar);
|
||||
EXPECT_LT((uint64_t)0, task_info.wchar);
|
||||
EXPECT_LT((uint64_t)0, task_info.syscr);
|
||||
EXPECT_LT((uint64_t)0, task_info.syscw);
|
||||
EXPECT_LT((uint64_t)0, task_info.read_bytes);
|
||||
EXPECT_LT((uint64_t)0, task_info.write_bytes);
|
||||
// cancelled_write_bytes of init could be 0, there is no need to test
|
||||
EXPECT_LE((uint64_t)0, task_info.starttime);
|
||||
EXPECT_NE((char*)NULL, strstr(task_info.cmd, "init"));
|
||||
|
||||
// Entries in /proc/1/io should be increasing through time
|
||||
struct task_info task_old, task_new;
|
||||
memset(&task_old, 0, sizeof(task_old));
|
||||
memset(&task_new, 0, sizeof(task_new));
|
||||
|
||||
// parse_task_info should succeed at this point
|
||||
ASSERT_TRUE(parse_task_info(1, &task_old));
|
||||
sleep(1);
|
||||
ASSERT_TRUE(parse_task_info(1, &task_new));
|
||||
|
||||
EXPECT_EQ(task_old.pid, task_new.pid);
|
||||
EXPECT_LE(task_old.rchar, task_new.rchar);
|
||||
EXPECT_LE(task_old.wchar, task_new.wchar);
|
||||
EXPECT_LE(task_old.syscr, task_new.syscr);
|
||||
EXPECT_LE(task_old.syscw, task_new.syscw);
|
||||
EXPECT_LE(task_old.read_bytes, task_new.read_bytes);
|
||||
EXPECT_LE(task_old.write_bytes, task_new.write_bytes);
|
||||
EXPECT_LE(task_old.cancelled_write_bytes, task_new.cancelled_write_bytes);
|
||||
EXPECT_EQ(task_old.starttime, task_new.starttime);
|
||||
EXPECT_EQ(0, strcmp(task_old.cmd, task_new.cmd));
|
||||
}
|
||||
|
||||
static double mean(std::deque<uint32_t> nums) {
|
||||
double sum = 0.0;
|
||||
for (uint32_t i : nums) {
|
||||
|
@ -244,179 +203,6 @@ TEST(storaged_test, stream_stats) {
|
|||
}
|
||||
}
|
||||
|
||||
static void expect_increasing(struct task_info told, struct task_info tnew) {
|
||||
ASSERT_EQ(told.pid, tnew.pid);
|
||||
ASSERT_EQ(told.starttime, tnew.starttime);
|
||||
ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
|
||||
|
||||
EXPECT_LE(told.rchar, tnew.rchar);
|
||||
EXPECT_LE(told.wchar, tnew.wchar);
|
||||
EXPECT_LE(told.syscr, tnew.syscr);
|
||||
EXPECT_LE(told.syscw, tnew.syscw);
|
||||
EXPECT_LE(told.read_bytes, tnew.read_bytes);
|
||||
EXPECT_LE(told.write_bytes, tnew.write_bytes);
|
||||
EXPECT_LE(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
|
||||
}
|
||||
|
||||
static void expect_equal(struct task_info told, struct task_info tnew) {
|
||||
ASSERT_EQ(told.pid, tnew.pid);
|
||||
ASSERT_EQ(told.starttime, tnew.starttime);
|
||||
ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
|
||||
|
||||
EXPECT_EQ(told.rchar, tnew.rchar);
|
||||
EXPECT_EQ(told.wchar, tnew.wchar);
|
||||
EXPECT_EQ(told.syscr, tnew.syscr);
|
||||
EXPECT_EQ(told.syscw, tnew.syscw);
|
||||
EXPECT_EQ(told.read_bytes, tnew.read_bytes);
|
||||
EXPECT_EQ(told.write_bytes, tnew.write_bytes);
|
||||
EXPECT_EQ(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
|
||||
}
|
||||
|
||||
static std::set<uint32_t> find_overlap(std::unordered_map<uint32_t, struct task_info> t1,
|
||||
std::unordered_map<uint32_t, struct task_info> t2) {
|
||||
std::set<uint32_t> retval;
|
||||
for (auto i : t1) {
|
||||
if (t2.find(i.first) != t2.end()) {
|
||||
retval.insert(i.first);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static std::set<std::string> find_overlap(std::unordered_map<std::string, struct task_info> t1,
|
||||
std::unordered_map<std::string, struct task_info> t2) {
|
||||
std::set<std::string> retval;
|
||||
for (auto i : t1) {
|
||||
if (t2.find(i.first) != t2.end()) {
|
||||
retval.insert(i.first);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool cmp_app_name(struct task_info i, struct task_info j) {
|
||||
return strcmp(i.cmd, j.cmd) > 0;
|
||||
}
|
||||
|
||||
static void expect_match(std::vector<struct task_info> v1, std::vector<struct task_info> v2) {
|
||||
ASSERT_EQ(v1.size(), v2.size());
|
||||
std::sort(v1.begin(), v1.end(), cmp_app_name);
|
||||
std::sort(v2.begin(), v2.end(), cmp_app_name);
|
||||
|
||||
for (uint i = 0; i < v1.size(); ++i) {
|
||||
expect_equal(v1[i], v2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_task_info(struct task_info* src, struct task_info* dst) {
|
||||
ASSERT_EQ(0, strcmp(src->cmd, dst->cmd));
|
||||
|
||||
dst->pid = 0;
|
||||
dst->rchar += src->rchar;
|
||||
dst->wchar += src->wchar;
|
||||
dst->syscr += src->syscr;
|
||||
dst->syscw += src->syscw;
|
||||
dst->read_bytes += src->read_bytes;
|
||||
dst->write_bytes += src->write_bytes;
|
||||
dst->cancelled_write_bytes += src->cancelled_write_bytes;
|
||||
dst->starttime = 0;
|
||||
}
|
||||
|
||||
static std::vector<struct task_info>
|
||||
categorize_tasks(std::unordered_map<uint32_t, struct task_info> tasks) {
|
||||
std::unordered_map<std::string, struct task_info> tasks_cmd;
|
||||
for (auto i : tasks) {
|
||||
std::string cmd = i.second.cmd;
|
||||
if (tasks_cmd.find(cmd) == tasks_cmd.end()) {
|
||||
tasks_cmd[cmd] = i.second;
|
||||
} else {
|
||||
add_task_info(&i.second, &tasks_cmd[cmd]);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<struct task_info> retval(tasks_cmd.size());
|
||||
int cnt = 0;
|
||||
for (auto i : tasks_cmd) {
|
||||
retval[cnt++] = i.second;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#define TEST_LOOPS 20
|
||||
TEST(storaged_test, tasks_t) {
|
||||
// pass this test if /proc/[pid]/io is not readable
|
||||
const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
|
||||
for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
|
||||
if (access(test_paths[i], R_OK) < 0) return;
|
||||
}
|
||||
|
||||
tasks_t tasks;
|
||||
EXPECT_EQ((uint32_t)0, tasks.mRunning.size());
|
||||
EXPECT_EQ((uint32_t)0, tasks.mOld.size());
|
||||
|
||||
tasks.update_running_tasks();
|
||||
|
||||
std::unordered_map<uint32_t, struct task_info> prev_running = tasks.mRunning;
|
||||
std::unordered_map<std::string, struct task_info> prev_old = tasks.mOld;
|
||||
|
||||
// hashmap maintaining
|
||||
std::unordered_map<uint32_t, struct task_info> tasks_pid = tasks.mRunning;
|
||||
|
||||
// get_running_tasks() should return something other than a null map
|
||||
std::unordered_map<uint32_t, struct task_info> test = tasks.get_running_tasks();
|
||||
EXPECT_LE((uint32_t)1, test.size());
|
||||
|
||||
for (int i = 0; i < TEST_LOOPS; ++i) {
|
||||
tasks.update_running_tasks();
|
||||
|
||||
std::set<uint32_t> overlap_running = find_overlap(prev_running, tasks.mRunning);
|
||||
std::set<std::string> overlap_old = find_overlap(prev_old, tasks.mOld);
|
||||
|
||||
// overlap_running should capture init(pid == 1), since init never get killed
|
||||
EXPECT_LE((uint32_t)1, overlap_running.size());
|
||||
EXPECT_NE(overlap_running.find((uint32_t)1), overlap_running.end());
|
||||
// overlap_old should never capture init, since init never get killed
|
||||
EXPECT_EQ(overlap_old.find("init"), overlap_old.end());
|
||||
|
||||
// overlapping entries in previous and current running-tasks map should have increasing contents
|
||||
for (uint32_t i : overlap_running) {
|
||||
expect_increasing(prev_running[i], tasks.mRunning[i]);
|
||||
}
|
||||
|
||||
// overlapping entries in previous and current killed-tasks map should have increasing contents
|
||||
// and the map size should also be increasing
|
||||
for (std::string i : overlap_old) {
|
||||
expect_increasing(prev_old[i], tasks.mOld[i]);
|
||||
}
|
||||
EXPECT_LE(prev_old.size(), tasks.mRunning.size());
|
||||
|
||||
// update app name & tasks_pid
|
||||
for (auto i : tasks.mRunning) {
|
||||
// test will fail if the pid got wrapped
|
||||
if (tasks_pid.find(i.first) != tasks_pid.end()) {
|
||||
expect_increasing(tasks_pid[i.first], i.second);
|
||||
tasks_pid[i.first] = i.second;
|
||||
} else {
|
||||
tasks_pid[i.first] = i.second;
|
||||
}
|
||||
}
|
||||
|
||||
// get maintained tasks
|
||||
std::vector<struct task_info> test_tasks = categorize_tasks(tasks_pid);
|
||||
std::vector<struct task_info> real_tasks = tasks.get_tasks();
|
||||
|
||||
expect_match(test_tasks, real_tasks);
|
||||
|
||||
prev_running = tasks.mRunning;
|
||||
prev_old = tasks.mOld;
|
||||
|
||||
pause(5);
|
||||
}
|
||||
}
|
||||
|
||||
static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
|
||||
struct disk_perf retval;
|
||||
retval.read_perf = (double)perf.read_perf * mul;
|
||||
|
@ -569,6 +355,7 @@ static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2
|
|||
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);
|
||||
|
|
Loading…
Reference in a new issue