summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJin Qian2016-12-28 17:43:51 -0600
committerJin Qian2017-01-24 16:45:35 -0600
commitbcd6e3b9d92b2eea3b054372c9adf00a1e6235bc (patch)
tree266fa372c83a1480570cd5e8878e7bdcc50c0a3b /storaged
parent933d02818bbbfa732392c9e78edb643b113717cc (diff)
downloadplatform-system-core-bcd6e3b9d92b2eea3b054372c9adf00a1e6235bc.tar.gz
platform-system-core-bcd6e3b9d92b2eea3b054372c9adf00a1e6235bc.tar.xz
platform-system-core-bcd6e3b9d92b2eea3b054372c9adf00a1e6235bc.zip
storaged: monitor per-uid IO usage
Add uid_monitor class to query /proc/uid_io/stats periodically. Add a log tag to record any UID that exceeds IO threshold. Test: adb shell storaged -u Bug: 34198239 Change-Id: I53568c30dbefe2f4bdb18054d3dedb30b4133d8b
Diffstat (limited to 'storaged')
-rw-r--r--storaged/Android.mk4
-rw-r--r--storaged/EventLogTags.logtags2
-rw-r--r--storaged/include/storaged.h20
-rw-r--r--storaged/include/storaged_service.h4
-rw-r--r--storaged/include/storaged_uid_monitor.h59
-rw-r--r--storaged/include/storaged_utils.h5
-rw-r--r--storaged/main.cpp43
-rw-r--r--storaged/storaged.cpp8
-rw-r--r--storaged/storaged_service.cpp42
-rw-r--r--storaged/storaged_uid_monitor.cpp152
-rw-r--r--storaged/storaged_utils.cpp48
-rw-r--r--storaged/tests/Android.mk2
12 files changed, 381 insertions, 8 deletions
diff --git a/storaged/Android.mk b/storaged/Android.mk
index db97040dc..0e8b574e8 100644
--- a/storaged/Android.mk
+++ b/storaged/Android.mk
@@ -2,14 +2,16 @@
2 2
3LOCAL_PATH := $(call my-dir) 3LOCAL_PATH := $(call my-dir)
4 4
5LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap 5LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap libpackagelistparser
6 6
7include $(CLEAR_VARS) 7include $(CLEAR_VARS)
8 8
9LOCAL_SRC_FILES := storaged.cpp \ 9LOCAL_SRC_FILES := storaged.cpp \
10 storaged_service.cpp \ 10 storaged_service.cpp \
11 storaged_utils.cpp \ 11 storaged_utils.cpp \
12 storaged_uid_monitor.cpp \
12 EventLogTags.logtags 13 EventLogTags.logtags
14
13LOCAL_MODULE := libstoraged 15LOCAL_MODULE := libstoraged
14LOCAL_CFLAGS := -Werror 16LOCAL_CFLAGS := -Werror
15LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include 17LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
index ee92dd1b2..71fda25e5 100644
--- a/storaged/EventLogTags.logtags
+++ b/storaged/EventLogTags.logtags
@@ -37,3 +37,5 @@
372732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1) 372732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
38 38
392733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1) 392733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
40
412734 storaged_uid_io_alert (name|3),(read|2),(write|2),(interval|2) \ No newline at end of file
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 521cc9e29..7fa9958bd 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -17,13 +17,17 @@
17#ifndef _STORAGED_H_ 17#ifndef _STORAGED_H_
18#define _STORAGED_H_ 18#define _STORAGED_H_
19 19
20#include <queue>
21#include <semaphore.h> 20#include <semaphore.h>
22#include <stdint.h> 21#include <stdint.h>
22#include <time.h>
23
24#include <queue>
23#include <string> 25#include <string>
24#include <unordered_map> 26#include <unordered_map>
25#include <vector> 27#include <vector>
26 28
29#include "storaged_uid_monitor.h"
30
27#define FRIEND_TEST(test_case_name, test_name) \ 31#define FRIEND_TEST(test_case_name, test_name) \
28friend class test_case_name##_##test_name##_Test 32friend class test_case_name##_##test_name##_Test
29 33
@@ -165,6 +169,8 @@ public:
165#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat" 169#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
166#define SDA_DISK_STATS_PATH "/sys/block/sda/stat" 170#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
167#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd" 171#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
172#define UID_IO_STATS_PATH "/proc/uid_io/stats"
173
168class disk_stats_monitor { 174class disk_stats_monitor {
169private: 175private:
170 FRIEND_TEST(storaged_test, disk_stats_monitor); 176 FRIEND_TEST(storaged_test, disk_stats_monitor);
@@ -260,12 +266,15 @@ public:
260#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 ) 266#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
261#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 ) 267#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
262#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 86400 ) 268#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 86400 )
269#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT ( 3600 )
263 270
264struct storaged_config { 271struct storaged_config {
265 int periodic_chores_interval_unit; 272 int periodic_chores_interval_unit;
266 int periodic_chores_interval_disk_stats_publish; 273 int periodic_chores_interval_disk_stats_publish;
267 int periodic_chores_interval_emmc_info_publish; 274 int periodic_chores_interval_emmc_info_publish;
275 int periodic_chores_interval_uid_io;
268 bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable 276 bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable
277 bool proc_uid_io_available; // whether uid_io is accessible
269 bool emmc_available; // whether eMMC est_csd file is readable 278 bool emmc_available; // whether eMMC est_csd file is readable
270 bool diskstats_available; // whether diskstats is accessible 279 bool diskstats_available; // whether diskstats is accessible
271}; 280};
@@ -278,6 +287,7 @@ private:
278 disk_stats_monitor mDsm; 287 disk_stats_monitor mDsm;
279 emmc_info_t mEmmcInfo; 288 emmc_info_t mEmmcInfo;
280 tasks_t mTasks; 289 tasks_t mTasks;
290 uid_monitor mUidm;
281 time_t mStarttime; 291 time_t mStarttime;
282public: 292public:
283 storaged_t(void); 293 storaged_t(void);
@@ -295,6 +305,9 @@ public:
295 void set_emmc_interval(int emmc_info) { 305 void set_emmc_interval(int emmc_info) {
296 mConfig.periodic_chores_interval_emmc_info_publish = emmc_info; 306 mConfig.periodic_chores_interval_emmc_info_publish = emmc_info;
297 } 307 }
308 void set_uid_io_interval(int uid_io) {
309 mUidm.set_periodic_chores_interval(uid_io);
310 }
298 std::vector<struct task_info> get_tasks(void) { 311 std::vector<struct task_info> get_tasks(void) {
299 // There could be a race when get_tasks() and the main thread is updating at the same time 312 // There could be a race when get_tasks() and the main thread is updating at the same time
300 // While update_running_tasks() is updating the critical sections at the end of the function 313 // While update_running_tasks() is updating the critical sections at the end of the function
@@ -312,11 +325,16 @@ public:
312 time_t get_starttime(void) { 325 time_t get_starttime(void) {
313 return mStarttime; 326 return mStarttime;
314 } 327 }
328
329 std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
330 return mUidm.get_uids();
331 }
315}; 332};
316 333
317// Eventlog tag 334// Eventlog tag
318// The content must match the definition in EventLogTags.logtags 335// The content must match the definition in EventLogTags.logtags
319#define EVENTLOGTAG_DISKSTATS ( 2732 ) 336#define EVENTLOGTAG_DISKSTATS ( 2732 )
320#define EVENTLOGTAG_EMMCINFO ( 2733 ) 337#define EVENTLOGTAG_EMMCINFO ( 2733 )
338#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
321 339
322#endif /* _STORAGED_H_ */ 340#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 64a9c81c5..220038c23 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -31,9 +31,11 @@ class IStoraged : public IInterface {
31public: 31public:
32 enum { 32 enum {
33 DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION, 33 DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION,
34 DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION + 1,
34 }; 35 };
35 // Request the service to run the test function 36 // Request the service to run the test function
36 virtual std::vector<struct task_info> dump_tasks(const char* option) = 0; 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;
37 39
38 DECLARE_META_INTERFACE(Storaged); 40 DECLARE_META_INTERFACE(Storaged);
39}; 41};
@@ -43,6 +45,7 @@ class BpStoraged : public BpInterface<IStoraged> {
43public: 45public:
44 BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){}; 46 BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
45 virtual std::vector<struct task_info> dump_tasks(const char* option); 47 virtual std::vector<struct task_info> dump_tasks(const char* option);
48 virtual std::vector<struct uid_info> dump_uids(const char* option);
46}; 49};
47 50
48// Server 51// Server
@@ -52,6 +55,7 @@ class BnStoraged : public BnInterface<IStoraged> {
52 55
53class Storaged : public BnStoraged { 56class Storaged : public BnStoraged {
54 virtual std::vector<struct task_info> dump_tasks(const char* option); 57 virtual std::vector<struct task_info> dump_tasks(const char* option);
58 virtual std::vector<struct uid_info> dump_uids(const char* option);
55}; 59};
56 60
57sp<IStoraged> get_storaged_service(); 61sp<IStoraged> get_storaged_service();
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
new file mode 100644
index 000000000..eceb7fd34
--- /dev/null
+++ b/storaged/include/storaged_uid_monitor.h
@@ -0,0 +1,59 @@
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _STORAGED_UID_MONITOR_H_
18#define _STORAGED_UID_MONITOR_H_
19
20#include <stdint.h>
21
22#include <string>
23#include <unordered_map>
24
25enum {
26 UID_FOREGROUND = 0,
27 UID_BACKGROUND = 1,
28 UID_STATS_SIZE = 2
29};
30
31struct uid_io_stats {
32 uint64_t rchar; // characters read
33 uint64_t wchar; // characters written
34 uint64_t read_bytes; // bytes read (from storage layer)
35 uint64_t write_bytes; // bytes written (to storage layer)
36};
37
38struct uid_info {
39 uint32_t uid; // user id
40 std::string name; // package name
41 struct uid_io_stats io[UID_STATS_SIZE]; // [0]:foreground [1]:background
42
43};
44
45class uid_monitor {
46private:
47 std::unordered_map<uint32_t, struct uid_info> last_uids;
48 void set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids, uint64_t ts);
49 int interval; // monitor interval in seconds
50 uint64_t last_report_ts; // timestamp of last report in nsec
51public:
52 uid_monitor();
53 void set_periodic_chores_interval(int t) { interval = t; }
54 int get_periodic_chores_interval() { return interval; }
55 std::unordered_map<uint32_t, struct uid_info> get_uids();
56 void report();
57};
58
59#endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 83538c215..bb14708c5 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -34,12 +34,15 @@ bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
34// Task I/O 34// Task I/O
35bool parse_task_info(uint32_t pid, struct task_info* info); 35bool parse_task_info(uint32_t pid, struct task_info* info);
36void sort_running_tasks_info(std::vector<struct task_info> &tasks); 36void sort_running_tasks_info(std::vector<struct task_info> &tasks);
37// UID I/O
38void sort_running_uids_info(std::vector<struct uid_info> &uids);
37 39
38// Logging 40// Logging
39void log_console_running_tasks_info(std::vector<struct task_info> tasks); 41void log_console_running_tasks_info(std::vector<struct task_info> tasks);
42void log_console_running_uids_info(std::vector<struct uid_info> uids);
40 43
41void log_debug_disk_perf(struct disk_perf* perf, const char* type); 44void log_debug_disk_perf(struct disk_perf* perf, const char* type);
42 45
43void log_event_disk_stats(struct disk_stats* stats, const char* type); 46void log_event_disk_stats(struct disk_stats* stats, const char* type);
44void log_event_emmc_info(struct emmc_info* info_); 47void log_event_emmc_info(struct emmc_info* info_);
45#endif /* _STORAGED_UTILS_H_ */ \ No newline at end of file 48#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 0cb0f5f70..915157493 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -105,9 +105,11 @@ void* storaged_main(void* s) {
105static void help_message(void) { 105static void help_message(void) {
106 printf("usage: storaged [OPTION]\n"); 106 printf("usage: storaged [OPTION]\n");
107 printf(" -d --dump Dump task I/O usage to stdout\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");
108 printf(" -s --start Start storaged (default)\n"); 109 printf(" -s --start Start storaged (default)\n");
109 printf(" --emmc=INTERVAL Set publish interval of emmc lifetime information (in days)\n"); 110 printf(" --emmc=INTERVAL Set publish interval of emmc lifetime information (in days)\n");
110 printf(" --diskstats=INTERVAL Set publish interval of diskstats (in hours)\n"); 111 printf(" --diskstats=INTERVAL Set publish interval of diskstats (in hours)\n");
112 printf(" --uidio=INTERVAL Set publish interval of uid io (in hours)\n");
111 printf(" --unit=INTERVAL Set storaged's refresh interval (in seconds)\n"); 113 printf(" --unit=INTERVAL Set storaged's refresh interval (in seconds)\n");
112 fflush(stdout); 114 fflush(stdout);
113} 115}
@@ -118,10 +120,12 @@ static void help_message(void) {
118int main(int argc, char** argv) { 120int main(int argc, char** argv) {
119 int flag_main_service = 0; 121 int flag_main_service = 0;
120 int flag_dump_task = 0; 122 int flag_dump_task = 0;
123 int flag_dump_uid = 0;
121 int flag_config = 0; 124 int flag_config = 0;
122 int unit_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT; 125 int unit_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
123 int diskstats_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH; 126 int diskstats_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
124 int emmc_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH; 127 int emmc_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
128 int uid_io_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT;
125 int fd_emmc = -1; 129 int fd_emmc = -1;
126 int opt; 130 int opt;
127 131
@@ -131,12 +135,14 @@ int main(int argc, char** argv) {
131 {"start", no_argument, 0, 's'}, 135 {"start", no_argument, 0, 's'},
132 {"kill", no_argument, 0, 'k'}, 136 {"kill", no_argument, 0, 'k'},
133 {"dump", no_argument, 0, 'd'}, 137 {"dump", no_argument, 0, 'd'},
138 {"uid", no_argument, 0, 'u'},
134 {"help", no_argument, 0, 'h'}, 139 {"help", no_argument, 0, 'h'},
135 {"unit", required_argument, 0, 0 }, 140 {"unit", required_argument, 0, 0 },
136 {"diskstats", required_argument, 0, 0 }, 141 {"diskstats", required_argument, 0, 0 },
137 {"emmc", required_argument, 0, 0 } 142 {"emmc", required_argument, 0, 0 },
143 {"uidio", required_argument, 0, 0 }
138 }; 144 };
139 opt = getopt_long(argc, argv, ":skdh0", long_options, &opt_idx); 145 opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
140 if (opt == -1) { 146 if (opt == -1) {
141 break; 147 break;
142 } 148 }
@@ -165,7 +171,15 @@ int main(int argc, char** argv) {
165 171
166 } else if (strcmp(long_options[opt_idx].name, "emmc") == 0) { 172 } else if (strcmp(long_options[opt_idx].name, "emmc") == 0) {
167 emmc_interval = atoi(optarg) * DAY_TO_SEC; 173 emmc_interval = atoi(optarg) * DAY_TO_SEC;
168 if (diskstats_interval == 0) { 174 if (emmc_interval == 0) {
175 fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
176 long_options[opt_idx].name);
177 help_message();
178 return -1;
179 }
180 } else if (strcmp(long_options[opt_idx].name, "uidio") == 0) {
181 uid_io_interval = atoi(optarg) * HOUR_TO_SEC;
182 if (uid_io_interval == 0) {
169 fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n", 183 fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
170 long_options[opt_idx].name); 184 long_options[opt_idx].name);
171 help_message(); 185 help_message();
@@ -187,6 +201,9 @@ int main(int argc, char** argv) {
187 case 'd': 201 case 'd':
188 flag_dump_task = 1; 202 flag_dump_task = 1;
189 break; 203 break;
204 case 'u':
205 flag_dump_uid = 1;
206 break;
190 case 'h': 207 case 'h':
191 help_message(); 208 help_message();
192 return 0; 209 return 0;
@@ -230,6 +247,7 @@ int main(int argc, char** argv) {
230 storaged.set_unit_interval(unit_interval); 247 storaged.set_unit_interval(unit_interval);
231 storaged.set_diskstats_interval(diskstats_interval); 248 storaged.set_diskstats_interval(diskstats_interval);
232 storaged.set_emmc_interval(emmc_interval); 249 storaged.set_emmc_interval(emmc_interval);
250 storaged.set_uid_io_interval(uid_io_interval);
233 } 251 }
234 252
235 // Start the main thread of storaged 253 // Start the main thread of storaged
@@ -278,5 +296,24 @@ int main(int argc, char** argv) {
278 return 0; 296 return 0;
279 } 297 }
280 298
299 if (flag_dump_uid) {
300 sp<IStoraged> storaged_service = get_storaged_service();
301 if (storaged_service == NULL) {
302 fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
303 return -1;
304 }
305 std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
306
307 if (res.size() == 0) {
308 fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
309 return 0;
310 }
311
312 sort_running_uids_info(res);
313 log_console_running_uids_info(res);
314
315 return 0;
316 }
317
281 return 0; 318 return 0;
282} 319}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index e4d3e686e..0c53f4482 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -174,9 +174,12 @@ storaged_t::storaged_t(void) {
174 } 174 }
175 } 175 }
176 176
177 mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
178
177 mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT; 179 mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
178 mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH; 180 mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
179 mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH; 181 mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
182 mUidm.set_periodic_chores_interval(DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT);
180 183
181 mStarttime = time(NULL); 184 mStarttime = time(NULL);
182} 185}
@@ -202,5 +205,10 @@ void storaged_t::event(void) {
202 mEmmcInfo.publish(); 205 mEmmcInfo.publish();
203 } 206 }
204 207
208 if (mConfig.proc_uid_io_available && mTimer &&
209 (mTimer % mUidm.get_periodic_chores_interval()) == 0) {
210 mUidm.report();
211 }
212
205 mTimer += mConfig.periodic_chores_interval_unit; 213 mTimer += mConfig.periodic_chores_interval_unit;
206} 214}
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index aa38ceb64..2a81aef65 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -14,6 +14,8 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17#include <stdint.h>
18
17#include <vector> 19#include <vector>
18 20
19#include <binder/IBinder.h> 21#include <binder/IBinder.h>
@@ -41,6 +43,22 @@ std::vector<struct task_info> BpStoraged::dump_tasks(const char* /*option*/) {
41 return res; 43 return res;
42} 44}
43 45
46std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
47 Parcel data, reply;
48 data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
49
50 remote()->transact(DUMPUIDS, data, &reply);
51
52 uint32_t res_size = reply.readInt32();
53 std::vector<struct uid_info> res(res_size);
54 for (auto&& uid : res) {
55 uid.uid = reply.readInt32();
56 uid.name = reply.readCString();
57 reply.read(&uid.io, sizeof(uid.io));
58 }
59 return res;
60}
61
44IMPLEMENT_META_INTERFACE(Storaged, "Storaged"); 62IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
45 63
46status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { 64status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
@@ -57,14 +75,36 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply
57 return NO_ERROR; 75 return NO_ERROR;
58 } 76 }
59 break; 77 break;
78 case DUMPUIDS: {
79 std::vector<struct uid_info> res = dump_uids(NULL);
80 reply->writeInt32(res.size());
81 for (auto uid : res) {
82 reply->writeInt32(uid.uid);
83 reply->writeCString(uid.name.c_str());
84 reply->write(&uid.io, sizeof(uid.io));
85 }
86 return NO_ERROR;
87 }
88 break;
60 default: 89 default:
61 return BBinder::onTransact(code, data, reply, flags); 90 return BBinder::onTransact(code, data, reply, flags);
62 } 91 }
63} 92}
93
64std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) { 94std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
65 return storaged.get_tasks(); 95 return storaged.get_tasks();
66} 96}
67 97
98std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
99 std::vector<struct uid_info> uids_v;
100 std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids();
101
102 for (const auto& it : uids_m) {
103 uids_v.push_back(it.second);
104 }
105 return uids_v;
106}
107
68sp<IStoraged> get_storaged_service() { 108sp<IStoraged> get_storaged_service() {
69 sp<IServiceManager> sm = defaultServiceManager(); 109 sp<IServiceManager> sm = defaultServiceManager();
70 if (sm == NULL) return NULL; 110 if (sm == NULL) return NULL;
@@ -75,4 +115,4 @@ sp<IStoraged> get_storaged_service() {
75 sp<IStoraged> storaged = interface_cast<IStoraged>(binder); 115 sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
76 116
77 return storaged; 117 return storaged;
78} \ No newline at end of file 118}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
new file mode 100644
index 000000000..4105dae8f
--- /dev/null
+++ b/storaged/storaged_uid_monitor.cpp
@@ -0,0 +1,152 @@
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "storaged"
18
19#include <stdint.h>
20#include <time.h>
21
22#include <string>
23#include <sstream>
24#include <unordered_map>
25
26#include <android-base/file.h>
27#include <android-base/logging.h>
28#include <android-base/macros.h>
29#include <android-base/stringprintf.h>
30#include <log/log_event_list.h>
31#include <packagelistparser/packagelistparser.h>
32
33#include "storaged.h"
34#include "storaged_uid_monitor.h"
35
36static const uint64_t io_alert_threshold = 1024 * 1024 * 1024; // 1GB
37
38using namespace android;
39using namespace android::base;
40
41static bool packagelist_parse_cb(pkg_info* info, void* userdata)
42{
43 std::unordered_map<uint32_t, struct uid_info>* uids =
44 reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata);
45
46 if (uids->find(info->uid) != uids->end()) {
47 (*uids)[info->uid].name = info->name;
48 }
49
50 packagelist_free(info);
51 return true;
52}
53
54void uid_monitor::set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids,
55 uint64_t ts)
56{
57 last_uids = uids;
58 last_report_ts = ts;
59}
60
61std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uids()
62{
63 std::unordered_map<uint32_t, struct uid_info> uids;
64 std::string buffer;
65 if (!android::base::ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
66 PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
67 return uids;
68 }
69
70 std::stringstream ss(buffer);
71 struct uid_info u;
72 bool refresh_uid = false;
73
74 while (ss >> u.uid) {
75 ss >> u.io[UID_FOREGROUND].rchar >> u.io[UID_FOREGROUND].wchar
76 >> u.io[UID_FOREGROUND].read_bytes >> u.io[UID_FOREGROUND].write_bytes
77 >> u.io[UID_BACKGROUND].rchar >> u.io[UID_BACKGROUND].wchar
78 >> u.io[UID_BACKGROUND].read_bytes >> u.io[UID_BACKGROUND].write_bytes;
79
80 if (!ss.good()) {
81 ss.clear(std::ios_base::badbit);
82 break;
83 }
84
85 if (last_uids.find(u.uid) == last_uids.end()) {
86 refresh_uid = true;
87 u.name = std::to_string(u.uid);
88 } else {
89 u.name = last_uids[u.uid].name;
90 }
91 uids[u.uid] = u;
92 }
93
94 if (!ss.eof() || ss.bad()) {
95 uids.clear();
96 LOG_TO(SYSTEM, ERROR) << "read UID IO stats failed";
97 }
98
99 if (refresh_uid) {
100 packagelist_parse(packagelist_parse_cb, &uids);
101 }
102
103 return uids;
104}
105
106void uid_monitor::report()
107{
108 struct timespec ts;
109
110 // Use monotonic to exclude suspend time so that we measure IO bytes/sec
111 // when system is running.
112 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
113 PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
114 return;
115 }
116
117 uint64_t curr_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
118 uint64_t ts_delta = curr_ts - last_report_ts;
119 uint64_t adjusted_threshold = io_alert_threshold * ((double)ts_delta / interval / NS_PER_SEC);
120
121 std::unordered_map<uint32_t, struct uid_info> uids = get_uids();
122 if (uids.empty()) {
123 return;
124 }
125
126 for (const auto& it : uids) {
127 const struct uid_info& uid = it.second;
128 uint64_t bg_read_delta = uid.io[UID_BACKGROUND].read_bytes -
129 last_uids[uid.uid].io[UID_BACKGROUND].read_bytes;
130 uint64_t bg_write_delta = uid.io[UID_BACKGROUND].write_bytes -
131 last_uids[uid.uid].io[UID_BACKGROUND].write_bytes;
132
133 if (bg_read_delta + bg_write_delta >= adjusted_threshold) {
134 android_log_event_list(EVENTLOGTAG_UID_IO_ALERT)
135 << uid.name << bg_read_delta << bg_write_delta
136 << uint64_t(ts_delta / NS_PER_SEC) << LOG_ID_EVENTS;
137 }
138 }
139
140 set_last_uids(std::move(uids), curr_ts);
141}
142
143uid_monitor::uid_monitor()
144{
145 struct timespec ts;
146
147 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
148 PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
149 return;
150 }
151 last_report_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
152}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index c845ac471..6e4ddb66c 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -435,6 +435,54 @@ void log_console_running_tasks_info(std::vector<struct task_info> tasks) {
435 fflush(stdout); 435 fflush(stdout);
436} 436}
437 437
438static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
439 // Compare background I/O first.
440 for (int i = UID_STATS_SIZE - 1; i >= 0; i--) {
441 uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
442 uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;
443 uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;
444 uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;
445
446 if (l_bytes != r_bytes) {
447 return l_bytes > r_bytes;
448 }
449 if (l_chars != r_chars) {
450 return l_chars > r_chars;
451 }
452 }
453
454 return l.name < r.name;
455}
456
457void sort_running_uids_info(std::vector<struct uid_info> &uids) {
458 std::sort(uids.begin(), uids.end(), cmp_uid_info);
459}
460
461// Logging functions
462void log_console_running_uids_info(std::vector<struct uid_info> uids) {
463// Sample Output:
464// Application FG Read FG Write FG Read FG Write BG Read BG Write BG Read BG Write
465// NAME/UID Characters Characters Bytes Bytes Characters Characters Bytes Bytes
466// ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
467// com.google.android.gsf.login 0 0 0 0 57195097 5137089 176386048 6512640
468// com.google.android.googlequicksearchbox 0 0 0 0 4196821 12123468 34295808 13225984
469// 1037 4572 537 0 0 131352 5145643 34263040 5144576
470// com.google.android.youtube 2182 70 0 0 63969383 482939 38731776 466944
471
472 // Title
473 printf("Per-UID I/O stats\n");
474 printf(" Application FG Read FG Write FG Read FG Write BG Read BG Write BG Read BG Write\n"
475 " NAME/UID Characters Characters Bytes Bytes Characters Characters Bytes Bytes\n"
476 " ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n");
477
478 for (const auto& uid : uids) {
479 printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n", uid.name.c_str(),
480 uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
481 uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes);
482 }
483 fflush(stdout);
484}
485
438#if DEBUG 486#if DEBUG
439void log_debug_disk_perf(struct disk_perf* perf, const char* type) { 487void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
440 // skip if the input structure are all zeros 488 // skip if the input structure are all zeros
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
index 4a0e45c78..26d04b162 100644
--- a/storaged/tests/Android.mk
+++ b/storaged/tests/Android.mk
@@ -40,6 +40,6 @@ LOCAL_MODULE := $(test_module_prefix)unit-tests
40LOCAL_MODULE_TAGS := $(test_tags) 40LOCAL_MODULE_TAGS := $(test_tags)
41LOCAL_CFLAGS += $(test_c_flags) 41LOCAL_CFLAGS += $(test_c_flags)
42LOCAL_STATIC_LIBRARIES := libstoraged 42LOCAL_STATIC_LIBRARIES := libstoraged
43LOCAL_SHARED_LIBRARIES := libbase libcutils liblog 43LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
44LOCAL_SRC_FILES := $(test_src_files) 44LOCAL_SRC_FILES := $(test_src_files)
45include $(BUILD_NATIVE_TEST) 45include $(BUILD_NATIVE_TEST)