diff options
Diffstat (limited to 'storaged')
-rw-r--r-- | storaged/include/storaged.h | 33 | ||||
-rw-r--r-- | storaged/include/storaged_service.h | 6 | ||||
-rw-r--r-- | storaged/include/storaged_utils.h | 4 | ||||
-rw-r--r-- | storaged/main.cpp | 43 | ||||
-rw-r--r-- | storaged/storaged.cpp | 15 | ||||
-rw-r--r-- | storaged/storaged.rc | 1 | ||||
-rw-r--r-- | storaged/storaged_service.cpp | 28 | ||||
-rw-r--r-- | storaged/storaged_utils.cpp | 185 | ||||
-rw-r--r-- | storaged/tests/storaged_test.cpp | 215 |
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 | ||
126 | class tasks_t { | ||
127 | private: | ||
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(); | ||
135 | public: | ||
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 | |||
148 | class stream_stats { | 126 | class stream_stats { |
149 | private: | 127 | private: |
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; |
302 | public: | 278 | public: |
@@ -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; | |||
30 | class IStoraged : public IInterface { | 30 | class IStoraged : public IInterface { |
31 | public: | 31 | public: |
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: | |||
44 | class BpStoraged : public BpInterface<IStoraged> { | 42 | class BpStoraged : public BpInterface<IStoraged> { |
45 | public: | 43 | public: |
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 | ||
56 | class Storaged : public BnStoraged { | 53 | class 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* | |||
31 | void add_disk_stats(struct disk_stats* src, struct disk_stats* dst); | 31 | void add_disk_stats(struct disk_stats* src, struct disk_stats* dst); |
32 | bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info); | 32 | bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info); |
33 | 33 | ||
34 | // Task I/O | ||
35 | bool parse_task_info(uint32_t pid, struct task_info* info); | ||
36 | void sort_running_tasks_info(std::vector<struct task_info> &tasks); | ||
37 | // UID I/O | 34 | // UID I/O |
38 | void sort_running_uids_info(std::vector<struct uid_info> &uids); | 35 | void sort_running_uids_info(std::vector<struct uid_info> &uids); |
39 | 36 | ||
40 | // Logging | 37 | // Logging |
41 | void log_console_running_tasks_info(std::vector<struct task_info> tasks); | ||
42 | void log_console_running_uids_info(std::vector<struct uid_info> uids); | 38 | void log_console_running_uids_info(std::vector<struct uid_info> uids); |
43 | 39 | ||
44 | void log_debug_disk_perf(struct disk_perf* perf, const char* type); | 40 | void 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 | ||
105 | static void help_message(void) { | 100 | static 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 | ||
116 | int main(int argc, char** argv) { | 110 | int 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 @@ | |||
1 | service storaged /system/bin/storaged | 1 | service 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 | ||
30 | extern storaged_t storaged; | 30 | extern storaged_t storaged; |
31 | 31 | ||
32 | std::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 | |||
46 | std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) { | 32 | std::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 | ||
94 | std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) { | ||
95 | return storaged.get_tasks(); | ||
96 | } | ||
97 | |||
98 | std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) { | 70 | std::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 | ||
250 | bool 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 | |||
292 | static 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 | |||
302 | static 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 | |||
316 | std::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 | |||
335 | static 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 | |||
349 | void 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 | |||
382 | std::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 | |||
404 | void 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 */ | ||
409 | void 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 | |||
433 | static bool cmp_uid_info(struct uid_info l, struct uid_info r) { | 248 | static 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 | ||
35 | static void pause(uint32_t sec) { | 34 | static 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 | ||
147 | TEST(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 | |||
187 | static double mean(std::deque<uint32_t> nums) { | 146 | static 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 | ||
247 | static 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 | |||
261 | static 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 | |||
275 | static 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 | |||
287 | static 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 | |||
299 | static bool cmp_app_name(struct task_info i, struct task_info j) { | ||
300 | return strcmp(i.cmd, j.cmd) > 0; | ||
301 | } | ||
302 | |||
303 | static 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 | |||
313 | static 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 | |||
327 | static std::vector<struct task_info> | ||
328 | categorize_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 | ||
349 | TEST(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 | |||
420 | static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) { | 206 | static 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 | ||
572 | TEST(storaged_test, disk_stats_publisher) { | 359 | TEST(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); |