summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'storaged')
-rw-r--r--storaged/include/storaged.h33
-rw-r--r--storaged/include/storaged_service.h6
-rw-r--r--storaged/include/storaged_utils.h4
-rw-r--r--storaged/main.cpp43
-rw-r--r--storaged/storaged.cpp15
-rw-r--r--storaged/storaged.rc1
-rw-r--r--storaged/storaged_service.cpp28
-rw-r--r--storaged/storaged_utils.cpp185
-rw-r--r--storaged/tests/storaged_test.cpp215
9 files changed, 4 insertions, 526 deletions
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index d3e6b7140..15d830c39 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -123,28 +123,6 @@ public:
123 } 123 }
124}; 124};
125 125
126class tasks_t {
127private:
128 FRIEND_TEST(storaged_test, tasks_t);
129 sem_t mSem;
130 // hashmap for all running tasks w/ pid as key
131 std::unordered_map<uint32_t, struct task_info> mRunning;
132 // hashmap for all tasks that have been killed (categorized by cmd) w/ cmd as key
133 std::unordered_map<std::string, struct task_info> mOld;
134 std::unordered_map<std::uint32_t, struct task_info> get_running_tasks();
135public:
136 tasks_t() {
137 sem_init(&mSem, 0, 1); // TODO: constructor don't have a return value, what if sem_init fails
138 }
139
140 ~tasks_t() {
141 sem_destroy(&mSem);
142 }
143
144 void update_running_tasks(void);
145 std::vector<struct task_info> get_tasks(void);
146};
147
148class stream_stats { 126class stream_stats {
149private: 127private:
150 double mSum; 128 double mSum;
@@ -282,7 +260,6 @@ struct storaged_config {
282 int periodic_chores_interval_disk_stats_publish; 260 int periodic_chores_interval_disk_stats_publish;
283 int periodic_chores_interval_emmc_info_publish; 261 int periodic_chores_interval_emmc_info_publish;
284 int periodic_chores_interval_uid_io; 262 int periodic_chores_interval_uid_io;
285 bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable
286 bool proc_uid_io_available; // whether uid_io is accessible 263 bool proc_uid_io_available; // whether uid_io is accessible
287 bool emmc_available; // whether eMMC est_csd file is readable 264 bool emmc_available; // whether eMMC est_csd file is readable
288 bool diskstats_available; // whether diskstats is accessible 265 bool diskstats_available; // whether diskstats is accessible
@@ -296,7 +273,6 @@ private:
296 disk_stats_publisher mDiskStats; 273 disk_stats_publisher mDiskStats;
297 disk_stats_monitor mDsm; 274 disk_stats_monitor mDsm;
298 emmc_info_t mEmmcInfo; 275 emmc_info_t mEmmcInfo;
299 tasks_t mTasks;
300 uid_monitor mUidm; 276 uid_monitor mUidm;
301 time_t mStarttime; 277 time_t mStarttime;
302public: 278public:
@@ -307,15 +283,6 @@ public:
307 void pause(void) { 283 void pause(void) {
308 sleep(mConfig.periodic_chores_interval_unit); 284 sleep(mConfig.periodic_chores_interval_unit);
309 } 285 }
310 std::vector<struct task_info> get_tasks(void) {
311 // There could be a race when get_tasks() and the main thread is updating at the same time
312 // While update_running_tasks() is updating the critical sections at the end of the function
313 // all together atomically, the final state of task_t can only be either the main thread's
314 // update or this update. Since the race can only occur when both threads are updating
315 // "simultaneously", either final state is acceptable.
316 mTasks.update_running_tasks();
317 return mTasks.get_tasks();
318 }
319 286
320 void set_privileged_fds(int fd_emmc) { 287 void set_privileged_fds(int fd_emmc) {
321 mEmmcInfo.set_emmc_fd(fd_emmc); 288 mEmmcInfo.set_emmc_fd(fd_emmc);
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 220038c23..0735f292d 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -30,11 +30,9 @@ using namespace android;
30class IStoraged : public IInterface { 30class IStoraged : public IInterface {
31public: 31public:
32 enum { 32 enum {
33 DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION, 33 DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
34 DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION + 1,
35 }; 34 };
36 // Request the service to run the test function 35 // Request the service to run the test function
37 virtual std::vector<struct task_info> dump_tasks(const char* option) = 0;
38 virtual std::vector<struct uid_info> dump_uids(const char* option) = 0; 36 virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
39 37
40 DECLARE_META_INTERFACE(Storaged); 38 DECLARE_META_INTERFACE(Storaged);
@@ -44,7 +42,6 @@ public:
44class BpStoraged : public BpInterface<IStoraged> { 42class BpStoraged : public BpInterface<IStoraged> {
45public: 43public:
46 BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){}; 44 BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
47 virtual std::vector<struct task_info> dump_tasks(const char* option);
48 virtual std::vector<struct uid_info> dump_uids(const char* option); 45 virtual std::vector<struct uid_info> dump_uids(const char* option);
49}; 46};
50 47
@@ -54,7 +51,6 @@ class BnStoraged : public BnInterface<IStoraged> {
54}; 51};
55 52
56class Storaged : public BnStoraged { 53class Storaged : public BnStoraged {
57 virtual std::vector<struct task_info> dump_tasks(const char* option);
58 virtual std::vector<struct uid_info> dump_uids(const char* option); 54 virtual std::vector<struct uid_info> dump_uids(const char* option);
59}; 55};
60 56
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index bb14708c5..2161c4008 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -31,14 +31,10 @@ struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats*
31void add_disk_stats(struct disk_stats* src, struct disk_stats* dst); 31void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
32bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info); 32bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
33 33
34// Task I/O
35bool parse_task_info(uint32_t pid, struct task_info* info);
36void sort_running_tasks_info(std::vector<struct task_info> &tasks);
37// UID I/O 34// UID I/O
38void sort_running_uids_info(std::vector<struct uid_info> &uids); 35void sort_running_uids_info(std::vector<struct uid_info> &uids);
39 36
40// Logging 37// Logging
41void log_console_running_tasks_info(std::vector<struct task_info> tasks);
42void log_console_running_uids_info(std::vector<struct uid_info> uids); 38void log_console_running_uids_info(std::vector<struct uid_info> uids);
43 39
44void log_debug_disk_perf(struct disk_perf* perf, const char* type); 40void log_debug_disk_perf(struct disk_perf* perf, const char* type);
diff --git a/storaged/main.cpp b/storaged/main.cpp
index c7f3dff5c..9ad420e54 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -61,8 +61,7 @@ static int drop_privs() {
61 if (cap_clear(caps.get()) < 0) return -1; 61 if (cap_clear(caps.get()) < 0) return -1;
62 cap_value_t cap_value[] = { 62 cap_value_t cap_value[] = {
63 CAP_SETGID, 63 CAP_SETGID,
64 CAP_SETUID, 64 CAP_SETUID
65 CAP_SYS_PTRACE // allow access to proc/<pid>/io as non-root user
66 }; 65 };
67 if (cap_set_flag(caps.get(), CAP_PERMITTED, 66 if (cap_set_flag(caps.get(), CAP_PERMITTED,
68 arraysize(cap_value), cap_value, 67 arraysize(cap_value), cap_value,
@@ -73,10 +72,6 @@ static int drop_privs() {
73 if (cap_set_proc(caps.get()) < 0) 72 if (cap_set_proc(caps.get()) < 0)
74 return -1; 73 return -1;
75 74
76 gid_t groups[] = { AID_READPROC };
77
78 if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) return -1;
79
80 if (setgid(AID_SYSTEM) != 0) return -1; 75 if (setgid(AID_SYSTEM) != 0) return -1;
81 76
82 if (setuid(AID_SYSTEM) != 0) return -1; 77 if (setuid(AID_SYSTEM) != 0) return -1;
@@ -104,7 +99,6 @@ void* storaged_main(void* s) {
104 99
105static void help_message(void) { 100static void help_message(void) {
106 printf("usage: storaged [OPTION]\n"); 101 printf("usage: storaged [OPTION]\n");
107 printf(" -d --dump Dump task I/O usage to stdout\n");
108 printf(" -u --uid Dump uid I/O usage to stdout\n"); 102 printf(" -u --uid Dump uid I/O usage to stdout\n");
109 printf(" -s --start Start storaged (default)\n"); 103 printf(" -s --start Start storaged (default)\n");
110 fflush(stdout); 104 fflush(stdout);
@@ -115,7 +109,6 @@ static void help_message(void) {
115 109
116int main(int argc, char** argv) { 110int main(int argc, char** argv) {
117 int flag_main_service = 0; 111 int flag_main_service = 0;
118 int flag_dump_task = 0;
119 int flag_dump_uid = 0; 112 int flag_dump_uid = 0;
120 int fd_emmc = -1; 113 int fd_emmc = -1;
121 int opt; 114 int opt;
@@ -125,7 +118,6 @@ int main(int argc, char** argv) {
125 static struct option long_options[] = { 118 static struct option long_options[] = {
126 {"start", no_argument, 0, 's'}, 119 {"start", no_argument, 0, 's'},
127 {"kill", no_argument, 0, 'k'}, 120 {"kill", no_argument, 0, 'k'},
128 {"dump", no_argument, 0, 'd'},
129 {"uid", no_argument, 0, 'u'}, 121 {"uid", no_argument, 0, 'u'},
130 {"help", no_argument, 0, 'h'} 122 {"help", no_argument, 0, 'h'}
131 }; 123 };
@@ -138,9 +130,6 @@ int main(int argc, char** argv) {
138 case 's': 130 case 's':
139 flag_main_service = 1; 131 flag_main_service = 1;
140 break; 132 break;
141 case 'd':
142 flag_dump_task = 1;
143 break;
144 case 'u': 133 case 'u':
145 flag_dump_uid = 1; 134 flag_dump_uid = 1;
146 break; 135 break;
@@ -159,7 +148,7 @@ int main(int argc, char** argv) {
159 flag_main_service = 1; 148 flag_main_service = 1;
160 } 149 }
161 150
162 if (flag_main_service && flag_dump_task) { 151 if (flag_main_service && flag_dump_uid) {
163 fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n"); 152 fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
164 help_message(); 153 help_message();
165 return -1; 154 return -1;
@@ -195,34 +184,6 @@ int main(int argc, char** argv) {
195 return 0; 184 return 0;
196 } 185 }
197 186
198 if (flag_dump_task) {
199 sp<IStoraged> storaged_service = get_storaged_service();
200 if (storaged_service == NULL) {
201 fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
202 return -1;
203 }
204 std::vector<struct task_info> res = storaged_service->dump_tasks(NULL);
205
206 if (res.size() == 0) {
207 fprintf(stderr, "Task I/O is not readable in this version of kernel.\n");
208 return 0;
209 }
210
211 time_t starttime = storaged.get_starttime();
212
213 if (starttime == (time_t)-1) {
214 fprintf(stderr, "Unknown start time\n");
215 } else {
216 char* time_str = ctime(&starttime);
217 printf("Application I/O was collected by storaged since %s", time_str);
218 }
219
220 sort_running_tasks_info(res);
221 log_console_running_tasks_info(res);
222
223 return 0;
224 }
225
226 if (flag_dump_uid) { 187 if (flag_dump_uid) {
227 sp<IStoraged> storaged_service = get_storaged_service(); 188 sp<IStoraged> storaged_service = get_storaged_service();
228 if (storaged_service == NULL) { 189 if (storaged_service == NULL) {
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 1950c217a..d3fa2b97d 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -166,15 +166,6 @@ storaged_t::storaged_t(void) {
166 mConfig.diskstats_available = true; 166 mConfig.diskstats_available = true;
167 } 167 }
168 168
169 mConfig.proc_taskio_readable = true;
170 const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
171 for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
172 if (access(test_paths[i], R_OK) < 0) {
173 mConfig.proc_taskio_readable = false;
174 break;
175 }
176 }
177
178 mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0); 169 mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
179 170
180 mConfig.periodic_chores_interval_unit = 171 mConfig.periodic_chores_interval_unit =
@@ -205,12 +196,6 @@ void storaged_t::event(void) {
205 } 196 }
206 } 197 }
207 198
208#ifdef DEBUG
209 if (mConfig.proc_taskio_readable) {
210 mTasks.update_running_tasks();
211 }
212#endif
213
214 if (mConfig.emmc_available && mTimer && 199 if (mConfig.emmc_available && mTimer &&
215 (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) { 200 (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
216 mEmmcInfo.update(); 201 mEmmcInfo.update();
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index a8fa5a799..53fdb85f5 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -1,5 +1,4 @@
1service storaged /system/bin/storaged 1service storaged /system/bin/storaged
2 class main 2 class main
3 file /d/mmc0/mmc0:0001/ext_csd r 3 file /d/mmc0/mmc0:0001/ext_csd r
4 group root readproc
5 writepid /dev/cpuset/system-background/tasks 4 writepid /dev/cpuset/system-background/tasks
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 2a81aef65..15185ec6f 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -29,20 +29,6 @@
29 29
30extern storaged_t storaged; 30extern storaged_t storaged;
31 31
32std::vector<struct task_info> BpStoraged::dump_tasks(const char* /*option*/) {
33 Parcel data, reply;
34 data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
35
36 remote()->transact(DUMPTASKS, data, &reply);
37
38 uint32_t res_size = reply.readInt32();
39 std::vector<struct task_info> res(res_size);
40 for (auto&& task : res) {
41 reply.read(&task, sizeof(task));
42 }
43 return res;
44}
45
46std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) { 32std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
47 Parcel data, reply; 33 Parcel data, reply;
48 data.writeInterfaceToken(IStoraged::getInterfaceDescriptor()); 34 data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
@@ -65,16 +51,6 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply
65 data.checkInterface(this); 51 data.checkInterface(this);
66 52
67 switch(code) { 53 switch(code) {
68 case DUMPTASKS: {
69 std::vector<struct task_info> res = dump_tasks(NULL);
70
71 reply->writeInt32(res.size());
72 for (auto task : res) {
73 reply->write(&task, sizeof(task));
74 }
75 return NO_ERROR;
76 }
77 break;
78 case DUMPUIDS: { 54 case DUMPUIDS: {
79 std::vector<struct uid_info> res = dump_uids(NULL); 55 std::vector<struct uid_info> res = dump_uids(NULL);
80 reply->writeInt32(res.size()); 56 reply->writeInt32(res.size());
@@ -91,10 +67,6 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply
91 } 67 }
92} 68}
93 69
94std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
95 return storaged.get_tasks();
96}
97
98std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) { 70std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
99 std::vector<struct uid_info> uids_v; 71 std::vector<struct uid_info> uids_v;
100 std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids(); 72 std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids();
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 1dcd0ffcf..51ea64fdd 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -245,191 +245,6 @@ bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) {
245 return true; 245 return true;
246} 246}
247 247
248#define PROC_DIR "/proc/"
249#define PROC_STAT_STARTTIME_IDX ( 22 ) // This index is 1 based according to the linux proc man page
250bool parse_task_info(uint32_t pid, struct task_info* info) {
251 std::string buffer;
252 std::string pid_str = std::to_string(pid);
253 info->pid = pid;
254
255 // Get task I/O
256 std::string task_io_path = android::base::StringPrintf(PROC_DIR "%s/io", pid_str.c_str());
257 if (!android::base::ReadFileToString(task_io_path, &buffer)) return false;
258
259 std::stringstream ss(buffer);
260 std::string title;
261
262 ss >> title >> info->rchar
263 >> title >> info->wchar
264 >> title >> info->syscr
265 >> title >> info->syscw
266 >> title >> info->read_bytes
267 >> title >> info->write_bytes
268 >> title >> info->cancelled_write_bytes;
269 ss.clear();
270
271 // Get cmd string
272 std::string task_cmdline_path = android::base::StringPrintf(PROC_DIR "%u/cmdline", pid);
273 if (!android::base::ReadFileToString(task_cmdline_path, &buffer)) return false;
274 strlcpy(info->cmd, android::base::Trim(buffer).c_str(), sizeof(info->cmd));
275
276 if (info->cmd[0] == '\0') {
277 std::string task_comm_path = android::base::StringPrintf(PROC_DIR "%u/comm", pid);
278 if (!android::base::ReadFileToString(task_comm_path, &buffer)) return false;
279 strlcpy(info->cmd, android::base::Trim(buffer).c_str(), sizeof(info->cmd));
280 }
281
282 // Get task start time
283 std::string task_stat_path = android::base::StringPrintf(PROC_DIR "%u/stat", pid);
284 if (!android::base::ReadFileToString(task_stat_path, &buffer)) return false;
285
286 std::vector<std::string> stat_parts = android::base::Split(buffer, " ");
287 info->starttime = atoll(stat_parts[PROC_STAT_STARTTIME_IDX - 1].c_str());
288
289 return true;
290}
291
292static bool is_pid(char* d_name) {
293 if (!d_name || d_name[0] == '\0') return false;
294 char* c = d_name;
295 while (*c) {
296 if (!isdigit(*c)) return false;
297 ++c;
298 }
299 return true;
300}
301
302static bool cmp_task_info(struct task_info i, struct task_info j) {
303 if (i.write_bytes + i.read_bytes != j.write_bytes + j.read_bytes) {
304 return i.write_bytes + i.read_bytes > j.write_bytes + j.read_bytes;
305 }
306 if (i.wchar + i.rchar != j.wchar + j.rchar) {
307 return i.wchar + i.rchar > j.wchar + j.rchar;
308 }
309 if (i.syscw + i.syscr != j.syscw + j.syscr) {
310 return i.syscw + i.syscr > j.syscw + j.syscr;
311 }
312
313 return strcmp(i.cmd, j.cmd) < 0;
314}
315
316std::unordered_map<uint32_t, struct task_info> tasks_t::get_running_tasks() {
317 std::unordered_map<uint32_t, struct task_info> retval;
318 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(PROC_DIR), closedir);
319 CHECK(dir != NULL);
320 struct dirent* dp;
321
322 for (;;) {
323 if ((dp = readdir(dir.get())) == NULL) break;
324 if (!is_pid(dp->d_name)) continue;
325
326 uint32_t pid = atol(dp->d_name);
327 struct task_info info;
328 if (parse_task_info(pid, &info)) {
329 retval[pid] = info;
330 }
331 }
332 return retval;
333}
334
335static void add_task_info(struct task_info* src, struct task_info* dst) {
336 CHECK(strcmp(src->cmd, dst->cmd) == 0);
337
338 dst->pid = 0;
339 dst->rchar += src->rchar;
340 dst->wchar += src->wchar;
341 dst->syscr += src->syscr;
342 dst->syscw += src->syscw;
343 dst->read_bytes += src->read_bytes;
344 dst->write_bytes += src->write_bytes;
345 dst->cancelled_write_bytes += src->cancelled_write_bytes;
346 dst->starttime = 0;
347}
348
349void tasks_t::update_running_tasks(void) {
350 std::unordered_map<uint32_t, struct task_info> tasks_latest = get_running_tasks();
351 std::unordered_map<std::string, struct task_info> tasks_old = mOld;
352
353 for (auto t : mRunning) {
354 uint32_t pid = t.first;
355 // old task on mRunning still exist on tasks_latest
356 if (tasks_latest.find(pid) != tasks_latest.end() &&
357 tasks_latest[pid].starttime == t.second.starttime) {
358 continue;
359 } else {
360 // This branch will handle 2 cases:
361 // - Task get killed between the 2 samplings
362 // - Task get killed and its pid is reused
363 std::string cmd = t.second.cmd;
364 struct task_info info = t.second;
365
366 if (tasks_old.find(cmd) == tasks_old.end()) {
367 tasks_old[cmd] = info;
368 } else {
369 add_task_info(&info, &tasks_old[cmd]);
370 }
371 }
372 }
373 { // update critical area
374 // this is really fast!
375 std::unique_ptr<lock_t> lock(new lock_t(&mSem));
376 mRunning = tasks_latest;
377 mOld = tasks_old;
378 }
379
380}
381
382std::vector<struct task_info> tasks_t::get_tasks(void) {
383 std::unique_ptr<lock_t> lock(new lock_t(&mSem));
384 std::unordered_map<std::string, struct task_info> tasks_map = mOld;
385
386 for (auto i : mRunning) {
387 std::string cmd = i.second.cmd;
388 if (tasks_map.find(cmd) == tasks_map.end()) {
389 tasks_map[cmd] = i.second;
390 } else {
391 add_task_info(&i.second, &tasks_map[cmd]);
392 }
393 }
394
395 std::vector<struct task_info> retval(tasks_map.size());
396 int idx = 0;
397 for (auto i : tasks_map) {
398 retval[idx++] = i.second;
399 }
400
401 return retval;
402}
403
404void sort_running_tasks_info(std::vector<struct task_info> &tasks) {
405 std::sort(tasks.begin(), tasks.end(), cmp_task_info);
406}
407
408/* Logging functions */
409void log_console_running_tasks_info(std::vector<struct task_info> tasks) {
410// Sample Output:
411// Application Read Write Read Write Read Write Cancelled
412// Name Characters Characters Syscalls Syscalls Bytes Bytes Writebytes
413// ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
414// zygote64 37688308 3388467 7607 4363 314519552 5373952 8192
415// system_server 95874193 2216913 74613 52257 213078016 7237632 16384
416// zygote 506279 1726194 921 263 128114688 1765376 0
417// /vendor/bin/qcks 75415632 75154382 21672 25036 63627264 29974528 10485760
418// /init 86658523 5107871 82113 8633 91015168 1245184 0
419
420 // Title
421 printf(" Application Read Write Read Write Read Write Cancelled\n"
422 " Name Characters Characters Syscalls Syscalls Bytes Bytes Writebytes\n"
423 " ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n");
424
425 for (struct task_info task : tasks) {
426 printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n",
427 task.cmd, task.rchar, task.wchar, task.syscr, task.syscw,
428 task.read_bytes, task.write_bytes, task.cancelled_write_bytes);
429 }
430 fflush(stdout);
431}
432
433static bool cmp_uid_info(struct uid_info l, struct uid_info r) { 248static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
434 // Compare background I/O first. 249 // Compare background I/O first.
435 for (int i = UID_STATS_SIZE - 1; i >= 0; i--) { 250 for (int i = UID_STATS_SIZE - 1; i >= 0; i--) {
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index 99b21ac0f..5395b9a41 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -30,7 +30,6 @@
30#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat" 30#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
31#define SDA_DISK_STATS_PATH "/sys/block/sda/stat" 31#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
32#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd" 32#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
33#define INIT_TASK_IO_PATH "/proc/1/io"
34 33
35static void pause(uint32_t sec) { 34static void pause(uint32_t sec) {
36 const char* path = "/cache/test"; 35 const char* path = "/cache/test";
@@ -144,46 +143,6 @@ TEST(storaged_test, emmc_info) {
144 } 143 }
145} 144}
146 145
147TEST(storaged_test, task_info) {
148 // parse_task_info should read something other than 0 from /proc/1/*
149 struct task_info task_info;
150 memset(&task_info, 0, sizeof(task_info));
151
152 if (!parse_task_info(1, &task_info)) return;
153
154 EXPECT_EQ((uint32_t)1, task_info.pid);
155 EXPECT_LT((uint64_t)0, task_info.rchar);
156 EXPECT_LT((uint64_t)0, task_info.wchar);
157 EXPECT_LT((uint64_t)0, task_info.syscr);
158 EXPECT_LT((uint64_t)0, task_info.syscw);
159 EXPECT_LT((uint64_t)0, task_info.read_bytes);
160 EXPECT_LT((uint64_t)0, task_info.write_bytes);
161 // cancelled_write_bytes of init could be 0, there is no need to test
162 EXPECT_LE((uint64_t)0, task_info.starttime);
163 EXPECT_NE((char*)NULL, strstr(task_info.cmd, "init"));
164
165 // Entries in /proc/1/io should be increasing through time
166 struct task_info task_old, task_new;
167 memset(&task_old, 0, sizeof(task_old));
168 memset(&task_new, 0, sizeof(task_new));
169
170 // parse_task_info should succeed at this point
171 ASSERT_TRUE(parse_task_info(1, &task_old));
172 sleep(1);
173 ASSERT_TRUE(parse_task_info(1, &task_new));
174
175 EXPECT_EQ(task_old.pid, task_new.pid);
176 EXPECT_LE(task_old.rchar, task_new.rchar);
177 EXPECT_LE(task_old.wchar, task_new.wchar);
178 EXPECT_LE(task_old.syscr, task_new.syscr);
179 EXPECT_LE(task_old.syscw, task_new.syscw);
180 EXPECT_LE(task_old.read_bytes, task_new.read_bytes);
181 EXPECT_LE(task_old.write_bytes, task_new.write_bytes);
182 EXPECT_LE(task_old.cancelled_write_bytes, task_new.cancelled_write_bytes);
183 EXPECT_EQ(task_old.starttime, task_new.starttime);
184 EXPECT_EQ(0, strcmp(task_old.cmd, task_new.cmd));
185}
186
187static double mean(std::deque<uint32_t> nums) { 146static double mean(std::deque<uint32_t> nums) {
188 double sum = 0.0; 147 double sum = 0.0;
189 for (uint32_t i : nums) { 148 for (uint32_t i : nums) {
@@ -244,179 +203,6 @@ TEST(storaged_test, stream_stats) {
244 } 203 }
245} 204}
246 205
247static void expect_increasing(struct task_info told, struct task_info tnew) {
248 ASSERT_EQ(told.pid, tnew.pid);
249 ASSERT_EQ(told.starttime, tnew.starttime);
250 ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
251
252 EXPECT_LE(told.rchar, tnew.rchar);
253 EXPECT_LE(told.wchar, tnew.wchar);
254 EXPECT_LE(told.syscr, tnew.syscr);
255 EXPECT_LE(told.syscw, tnew.syscw);
256 EXPECT_LE(told.read_bytes, tnew.read_bytes);
257 EXPECT_LE(told.write_bytes, tnew.write_bytes);
258 EXPECT_LE(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
259}
260
261static void expect_equal(struct task_info told, struct task_info tnew) {
262 ASSERT_EQ(told.pid, tnew.pid);
263 ASSERT_EQ(told.starttime, tnew.starttime);
264 ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
265
266 EXPECT_EQ(told.rchar, tnew.rchar);
267 EXPECT_EQ(told.wchar, tnew.wchar);
268 EXPECT_EQ(told.syscr, tnew.syscr);
269 EXPECT_EQ(told.syscw, tnew.syscw);
270 EXPECT_EQ(told.read_bytes, tnew.read_bytes);
271 EXPECT_EQ(told.write_bytes, tnew.write_bytes);
272 EXPECT_EQ(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
273}
274
275static std::set<uint32_t> find_overlap(std::unordered_map<uint32_t, struct task_info> t1,
276 std::unordered_map<uint32_t, struct task_info> t2) {
277 std::set<uint32_t> retval;
278 for (auto i : t1) {
279 if (t2.find(i.first) != t2.end()) {
280 retval.insert(i.first);
281 }
282 }
283
284 return retval;
285}
286
287static std::set<std::string> find_overlap(std::unordered_map<std::string, struct task_info> t1,
288 std::unordered_map<std::string, struct task_info> t2) {
289 std::set<std::string> retval;
290 for (auto i : t1) {
291 if (t2.find(i.first) != t2.end()) {
292 retval.insert(i.first);
293 }
294 }
295
296 return retval;
297}
298
299static bool cmp_app_name(struct task_info i, struct task_info j) {
300 return strcmp(i.cmd, j.cmd) > 0;
301}
302
303static void expect_match(std::vector<struct task_info> v1, std::vector<struct task_info> v2) {
304 ASSERT_EQ(v1.size(), v2.size());
305 std::sort(v1.begin(), v1.end(), cmp_app_name);
306 std::sort(v2.begin(), v2.end(), cmp_app_name);
307
308 for (uint i = 0; i < v1.size(); ++i) {
309 expect_equal(v1[i], v2[i]);
310 }
311}
312
313static void add_task_info(struct task_info* src, struct task_info* dst) {
314 ASSERT_EQ(0, strcmp(src->cmd, dst->cmd));
315
316 dst->pid = 0;
317 dst->rchar += src->rchar;
318 dst->wchar += src->wchar;
319 dst->syscr += src->syscr;
320 dst->syscw += src->syscw;
321 dst->read_bytes += src->read_bytes;
322 dst->write_bytes += src->write_bytes;
323 dst->cancelled_write_bytes += src->cancelled_write_bytes;
324 dst->starttime = 0;
325}
326
327static std::vector<struct task_info>
328categorize_tasks(std::unordered_map<uint32_t, struct task_info> tasks) {
329 std::unordered_map<std::string, struct task_info> tasks_cmd;
330 for (auto i : tasks) {
331 std::string cmd = i.second.cmd;
332 if (tasks_cmd.find(cmd) == tasks_cmd.end()) {
333 tasks_cmd[cmd] = i.second;
334 } else {
335 add_task_info(&i.second, &tasks_cmd[cmd]);
336 }
337 }
338
339 std::vector<struct task_info> retval(tasks_cmd.size());
340 int cnt = 0;
341 for (auto i : tasks_cmd) {
342 retval[cnt++] = i.second;
343 }
344
345 return retval;
346}
347
348#define TEST_LOOPS 20
349TEST(storaged_test, tasks_t) {
350 // pass this test if /proc/[pid]/io is not readable
351 const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
352 for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
353 if (access(test_paths[i], R_OK) < 0) return;
354 }
355
356 tasks_t tasks;
357 EXPECT_EQ((uint32_t)0, tasks.mRunning.size());
358 EXPECT_EQ((uint32_t)0, tasks.mOld.size());
359
360 tasks.update_running_tasks();
361
362 std::unordered_map<uint32_t, struct task_info> prev_running = tasks.mRunning;
363 std::unordered_map<std::string, struct task_info> prev_old = tasks.mOld;
364
365 // hashmap maintaining
366 std::unordered_map<uint32_t, struct task_info> tasks_pid = tasks.mRunning;
367
368 // get_running_tasks() should return something other than a null map
369 std::unordered_map<uint32_t, struct task_info> test = tasks.get_running_tasks();
370 EXPECT_LE((uint32_t)1, test.size());
371
372 for (int i = 0; i < TEST_LOOPS; ++i) {
373 tasks.update_running_tasks();
374
375 std::set<uint32_t> overlap_running = find_overlap(prev_running, tasks.mRunning);
376 std::set<std::string> overlap_old = find_overlap(prev_old, tasks.mOld);
377
378 // overlap_running should capture init(pid == 1), since init never get killed
379 EXPECT_LE((uint32_t)1, overlap_running.size());
380 EXPECT_NE(overlap_running.find((uint32_t)1), overlap_running.end());
381 // overlap_old should never capture init, since init never get killed
382 EXPECT_EQ(overlap_old.find("init"), overlap_old.end());
383
384 // overlapping entries in previous and current running-tasks map should have increasing contents
385 for (uint32_t i : overlap_running) {
386 expect_increasing(prev_running[i], tasks.mRunning[i]);
387 }
388
389 // overlapping entries in previous and current killed-tasks map should have increasing contents
390 // and the map size should also be increasing
391 for (std::string i : overlap_old) {
392 expect_increasing(prev_old[i], tasks.mOld[i]);
393 }
394 EXPECT_LE(prev_old.size(), tasks.mRunning.size());
395
396 // update app name & tasks_pid
397 for (auto i : tasks.mRunning) {
398 // test will fail if the pid got wrapped
399 if (tasks_pid.find(i.first) != tasks_pid.end()) {
400 expect_increasing(tasks_pid[i.first], i.second);
401 tasks_pid[i.first] = i.second;
402 } else {
403 tasks_pid[i.first] = i.second;
404 }
405 }
406
407 // get maintained tasks
408 std::vector<struct task_info> test_tasks = categorize_tasks(tasks_pid);
409 std::vector<struct task_info> real_tasks = tasks.get_tasks();
410
411 expect_match(test_tasks, real_tasks);
412
413 prev_running = tasks.mRunning;
414 prev_old = tasks.mOld;
415
416 pause(5);
417 }
418}
419
420static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) { 206static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
421 struct disk_perf retval; 207 struct disk_perf retval;
422 retval.read_perf = (double)perf.read_perf * mul; 208 retval.read_perf = (double)perf.read_perf * mul;
@@ -569,6 +355,7 @@ static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2
569 EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue); 355 EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
570} 356}
571 357
358#define TEST_LOOPS 20
572TEST(storaged_test, disk_stats_publisher) { 359TEST(storaged_test, disk_stats_publisher) {
573 // asserting that there is one file for diskstats 360 // asserting that there is one file for diskstats
574 ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0); 361 ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);