summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorynwang2016-06-17 16:30:48 -0500
committerJin Qian2016-12-15 17:56:33 -0600
commit62cb372279f0237f759d3fe8a7e03d8272ba327f (patch)
treec91b8bb6cb4e015a9b711983c0cc17c8d3b291ef /storaged
parenteb5763d177aa8c4d85b0c04ac2bb60befa66cde8 (diff)
downloadplatform-system-core-62cb372279f0237f759d3fe8a7e03d8272ba327f.tar.gz
platform-system-core-62cb372279f0237f759d3fe8a7e03d8272ba327f.tar.xz
platform-system-core-62cb372279f0237f759d3fe8a7e03d8272ba327f.zip
storaged: add storaged native daemon
Storaged periodically checks the devices' disk stats and eMMC lifetime. This information is logged to both the kernel and event log. By event logging, the data can be aggregated by the checkin service and then sent to Sawmill. It also periodically traverses /proc/[pid], maintaining the I/O usage of all tasks (all sampled tasks). The task I/O data can be reported using the --dump option. Storaged is booted when the device boots up and requires the permission for reading /proc/diskstats, /proc/[pid]/io and debugfs(eMMC ext_csd). For detailed description, please refer to go/storaged. Bug: 28826771 Change-Id: I774b1a44cc30092bac1bfcbc08bf487295e7cfed
Diffstat (limited to 'storaged')
-rw-r--r--storaged/Android.mk34
-rw-r--r--storaged/EventLogTags.logtags39
-rw-r--r--storaged/include/storaged.h340
-rw-r--r--storaged/include/storaged_service.h59
-rw-r--r--storaged/include/storaged_utils.h46
-rw-r--r--storaged/main.cpp298
-rw-r--r--storaged/storaged.cpp211
-rw-r--r--storaged/storaged.rc5
-rw-r--r--storaged/storaged_service.cpp78
-rw-r--r--storaged/storaged_utils.cpp582
-rw-r--r--storaged/tests/Android.mk45
-rw-r--r--storaged/tests/storaged_test.cpp587
12 files changed, 2324 insertions, 0 deletions
diff --git a/storaged/Android.mk b/storaged/Android.mk
new file mode 100644
index 000000000..db97040dc
--- /dev/null
+++ b/storaged/Android.mk
@@ -0,0 +1,34 @@
1# Copyright 2016 The Android Open Source Project
2
3LOCAL_PATH := $(call my-dir)
4
5LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap
6
7include $(CLEAR_VARS)
8
9LOCAL_SRC_FILES := storaged.cpp \
10 storaged_service.cpp \
11 storaged_utils.cpp \
12 EventLogTags.logtags
13LOCAL_MODULE := libstoraged
14LOCAL_CFLAGS := -Werror
15LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
16LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
17LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
18
19include $(BUILD_STATIC_LIBRARY)
20
21include $(CLEAR_VARS)
22
23LOCAL_MODULE := storaged
24LOCAL_INIT_RC := storaged.rc
25LOCAL_SRC_FILES := main.cpp
26# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
27LOCAL_STATIC_LIBRARIES := libstoraged
28LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
29LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
30LOCAL_C_INCLUDES := external/googletest/googletest/include
31
32include $(BUILD_EXECUTABLE)
33
34include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
new file mode 100644
index 000000000..2e25d4a29
--- /dev/null
+++ b/storaged/EventLogTags.logtags
@@ -0,0 +1,39 @@
1# The entries in this file map a sparse set of log tag numbers to tag names.
2# This is installed on the device, in /system/etc, and parsed by logcat.
3#
4# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
5# negative values alone for now.)
6#
7# Tag names are one or more ASCII letters and numbers or underscores, i.e.
8# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
9# impacts log readability, the latter makes regex searches more annoying).
10#
11# Tag numbers and names are separated by whitespace. Blank lines and lines
12# starting with '#' are ignored.
13#
14# Optionally, after the tag names can be put a description for the value(s)
15# of the tag. Description are in the format
16# (<name>|data type[|data unit])
17# Multiple values are separated by commas.
18#
19# The data type is a number from the following values:
20# 1: int
21# 2: long
22# 3: string
23# 4: list
24# 5: float
25#
26# The data unit is a number taken from the following list:
27# 1: Number of objects
28# 2: Number of bytes
29# 3: Number of milliseconds
30# 4: Number of allocations
31# 5: Id
32# 6: Percent
33# Default value for data of type int/long is 2 (bytes).
34#
35# TODO: generate ".java" and ".h" files with integer constants from this file.
36
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
392733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1) \ No newline at end of file
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
new file mode 100644
index 000000000..eb827cf9d
--- /dev/null
+++ b/storaged/include/storaged.h
@@ -0,0 +1,340 @@
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_H_
18#define _STORAGED_H_
19
20#define DEBUG
21
22#include <queue>
23#include <semaphore.h>
24#include <stdint.h>
25#include <string>
26#include <syslog.h>
27#include <unordered_map>
28#include <vector>
29
30#define FRIEND_TEST(test_case_name, test_name) \
31friend class test_case_name##_##test_name##_Test
32
33/* For debug */
34#ifdef DEBUG
35#define debuginfo(fmt, ...) \
36 do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
37 while(0)
38#else
39#define debuginfo(...)
40#endif
41
42#define KMSG_PRIORITY(PRI) \
43 '<', \
44 '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
45 '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, \
46 '>'
47
48static char kmsg_error_prefix[] = { KMSG_PRIORITY(LOG_ERR),
49 's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', '\0' };
50
51static char kmsg_info_prefix[] = { KMSG_PRIORITY(LOG_INFO),
52 's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', '\0' };
53
54static char kmsg_warning_prefix[] = { KMSG_PRIORITY(LOG_WARNING),
55 's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', '\0' };
56
57// number of attributes diskstats has
58#define DISK_STATS_SIZE ( 11 )
59// maximum size limit of a stats file
60#define DISK_STATS_FILE_MAX_SIZE ( 256 )
61#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
62struct disk_stats {
63 /* It will be extremely unlikely for any of the following entries to overflow.
64 * For read_bytes(which will be greater than any of the following entries), it
65 * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
66 * is the peak memory transfer rate for current memory.
67 * The diskstats entries (first 11) need to be at top in this structure _after_
68 * compiler's optimization.
69 */
70 uint64_t read_ios; // number of read I/Os processed
71 uint64_t read_merges; // number of read I/Os merged with in-queue I/Os
72 uint64_t read_sectors; // number of sectors read
73 uint64_t read_ticks; // total wait time for read requests
74 uint64_t write_ios; // number of write I/Os processed
75 uint64_t write_merges; // number of write I/Os merged with in-queue I/Os
76 uint64_t write_sectors; // number of sectors written
77 uint64_t write_ticks; // total wait time for write requests
78 uint64_t io_in_flight; // number of I/Os currently in flight
79 uint64_t io_ticks; // total time this block device has been active
80 uint64_t io_in_queue; // total wait time for all requests
81
82 uint64_t start_time; // monotonic time accounting starts
83 uint64_t end_time; // monotonic time accounting ends
84 uint32_t counter; // private counter for accumulate calculations
85 double io_avg; // average io_in_flight for accumulate calculations
86};
87
88#define MMC_VER_STR_LEN ( 8 ) // maximum length of the MMC version string
89// minimum size of a ext_csd file
90#define EXT_CSD_FILE_MIN_SIZE ( 1024 )
91struct emmc_info {
92 int eol; // pre-eol (end of life) information
93 int lifetime_a; // device life time estimation (type A)
94 int lifetime_b; // device life time estimation (type B)
95 char mmc_ver[MMC_VER_STR_LEN]; // device version string
96};
97
98struct disk_perf {
99 uint32_t read_perf; // read speed (kbytes/s)
100 uint32_t read_ios; // read I/Os per second
101 uint32_t write_perf; // write speed (kbytes/s)
102 uint32_t write_ios; // write I/Os per second
103 uint32_t queue; // I/Os in queue
104};
105
106#define CMD_MAX_LEN ( 64 )
107struct task_info {
108 uint32_t pid; // task id
109 uint64_t rchar; // characters read
110 uint64_t wchar; // characters written
111 uint64_t syscr; // read syscalls
112 uint64_t syscw; // write syscalls
113 uint64_t read_bytes; // bytes read (from storage layer)
114 uint64_t write_bytes; // bytes written (to storage layer)
115 uint64_t cancelled_write_bytes; // cancelled write byte by truncate
116
117 uint64_t starttime; // start time of task
118
119 char cmd[CMD_MAX_LEN]; // filename of the executable
120};
121
122class lock_t {
123 sem_t* mSem;
124public:
125 lock_t(sem_t* sem) {
126 mSem = sem;
127 sem_wait(mSem);
128 }
129 ~lock_t() {
130 sem_post(mSem);
131 }
132};
133
134class tasks_t {
135private:
136 FRIEND_TEST(storaged_test, tasks_t);
137 sem_t mSem;
138 // hashmap for all running tasks w/ pid as key
139 std::unordered_map<uint32_t, struct task_info> mRunning;
140 // hashmap for all tasks that have been killed (categorized by cmd) w/ cmd as key
141 std::unordered_map<std::string, struct task_info> mOld;
142 std::unordered_map<std::uint32_t, struct task_info> get_running_tasks();
143public:
144 tasks_t() {
145 sem_init(&mSem, 0, 1); // TODO: constructor don't have a return value, what if sem_init fails
146 }
147
148 ~tasks_t() {
149 sem_destroy(&mSem);
150 }
151
152 void update_running_tasks(void);
153 std::vector<struct task_info> get_tasks(void);
154};
155
156class stream_stats {
157private:
158 double mSum;
159 double mSquareSum;
160 uint32_t mCnt;
161public:
162 stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
163 ~stream_stats() {};
164 double get_mean() {
165 return mSum / mCnt;
166 }
167 double get_std() {
168 return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
169 }
170 void add(uint32_t num) {
171 mSum += (double)num;
172 mSquareSum += (double)num * (double)num;
173 mCnt++;
174 }
175 void evict(uint32_t num) {
176 if (mSum < num || mSquareSum < (double)num * (double)num) return;
177 mSum -= (double)num;
178 mSquareSum -= (double)num * (double)num;
179 mCnt--;
180 }
181};
182
183#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
184#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
185#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
186class disk_stats_monitor {
187private:
188 FRIEND_TEST(storaged_test, disk_stats_monitor);
189 const char* DISK_STATS_PATH;
190 struct disk_stats mPrevious;
191 struct disk_stats mAccumulate;
192 bool mStall;
193 std::queue<struct disk_perf> mBuffer;
194 struct {
195 stream_stats read_perf; // read speed (bytes/s)
196 stream_stats read_ios; // read I/Os per second
197 stream_stats write_perf; // write speed (bytes/s)
198 stream_stats write_ios; // write I/O per second
199 stream_stats queue; // I/Os in queue
200 } mStats;
201 bool mValid;
202 const uint32_t mWindow;
203 const double mSigma;
204 struct disk_perf mMean;
205 struct disk_perf mStd;
206
207 void update_mean();
208 void update_std();
209 void add(struct disk_perf* perf);
210 void evict(struct disk_perf* perf);
211 bool detect(struct disk_perf* perf);
212
213 void update(struct disk_stats* stats);
214
215public:
216 disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
217 mStall(false),
218 mValid(false),
219 mWindow(window_size),
220 mSigma(sigma) {
221 memset(&mPrevious, 0, sizeof(mPrevious));
222 memset(&mMean, 0, sizeof(mMean));
223 memset(&mStd, 0, sizeof(mStd));
224
225 if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
226 DISK_STATS_PATH = MMC_DISK_STATS_PATH;
227 } else {
228 DISK_STATS_PATH = SDA_DISK_STATS_PATH;
229 }
230 }
231 void update(void);
232};
233
234class disk_stats_publisher {
235private:
236 FRIEND_TEST(storaged_test, disk_stats_publisher);
237 const char* DISK_STATS_PATH;
238 struct disk_stats mAccumulate;
239 struct disk_stats mPrevious;
240public:
241 disk_stats_publisher(void) {
242 memset(&mAccumulate, 0, sizeof(struct disk_stats));
243 memset(&mPrevious, 0, sizeof(struct disk_stats));
244
245 if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
246 DISK_STATS_PATH = MMC_DISK_STATS_PATH;
247 } else {
248 DISK_STATS_PATH = SDA_DISK_STATS_PATH;
249 }
250 }
251
252 ~disk_stats_publisher(void) {}
253 void publish(void);
254 void update(void);
255};
256
257class emmc_info_t {
258private:
259 struct emmc_info mInfo;
260 bool mValid;
261 int mFdEmmc;
262public:
263 emmc_info_t(void) :
264 mValid(false),
265 mFdEmmc(-1) {
266 memset(&mInfo, 0, sizeof(struct emmc_info));
267 }
268 ~emmc_info_t(void) {}
269
270 void publish(void);
271 void update(void);
272 void set_emmc_fd(int fd) {
273 mFdEmmc = fd;
274 }
275};
276
277// Periodic chores intervals in seconds
278#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 20 )
279#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 60 )
280#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 60 * 2 )
281
282struct storaged_config {
283 int periodic_chores_interval_unit;
284 int periodic_chores_interval_disk_stats_publish;
285 int periodic_chores_interval_emmc_info_publish;
286 bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable
287 bool emmc_available; // whether eMMC est_csd file is readable
288 bool diskstats_available; // whether diskstats is accessible
289};
290
291class storaged_t {
292private:
293 time_t mTimer;
294 storaged_config mConfig;
295 disk_stats_publisher mDiskStats;
296 disk_stats_monitor mDsm;
297 emmc_info_t mEmmcInfo;
298 tasks_t mTasks;
299 time_t mStarttime;
300public:
301 storaged_t(void);
302 ~storaged_t() {}
303 void event(void);
304 void pause(void) {
305 sleep(mConfig.periodic_chores_interval_unit);
306 }
307 void set_unit_interval(int unit) {
308 mConfig.periodic_chores_interval_unit = unit;
309 }
310 void set_diskstats_interval(int disk_stats) {
311 mConfig.periodic_chores_interval_disk_stats_publish = disk_stats;
312 }
313 void set_emmc_interval(int emmc_info) {
314 mConfig.periodic_chores_interval_emmc_info_publish = emmc_info;
315 }
316 std::vector<struct task_info> get_tasks(void) {
317 // There could be a race when get_tasks() and the main thread is updating at the same time
318 // While update_running_tasks() is updating the critical sections at the end of the function
319 // all together atomically, the final state of task_t can only be either the main thread's
320 // update or this update. Since the race can only occur when both threads are updating
321 // "simultaneously", either final state is acceptable.
322 mTasks.update_running_tasks();
323 return mTasks.get_tasks();
324 }
325
326 void set_privileged_fds(int fd_emmc) {
327 mEmmcInfo.set_emmc_fd(fd_emmc);
328 }
329
330 time_t get_starttime(void) {
331 return mStarttime;
332 }
333};
334
335// Eventlog tag
336// The content must match the definition in EventLogTags.logtags
337#define EVENTLOGTAG_DISKSTATS ( 2732 )
338#define EVENTLOGTAG_EMMCINFO ( 2733 )
339
340#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
new file mode 100644
index 000000000..64a9c81c5
--- /dev/null
+++ b/storaged/include/storaged_service.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_SERVICE_H_
18#define _STORAGED_SERVICE_H_
19
20#include <vector>
21
22#include <binder/IInterface.h>
23#include <binder/IBinder.h>
24
25#include "storaged.h"
26
27using namespace android;
28
29// Interface
30class IStoraged : public IInterface {
31public:
32 enum {
33 DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION,
34 };
35 // Request the service to run the test function
36 virtual std::vector<struct task_info> dump_tasks(const char* option) = 0;
37
38 DECLARE_META_INTERFACE(Storaged);
39};
40
41// Client
42class BpStoraged : public BpInterface<IStoraged> {
43public:
44 BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
45 virtual std::vector<struct task_info> dump_tasks(const char* option);
46};
47
48// Server
49class BnStoraged : public BnInterface<IStoraged> {
50 virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
51};
52
53class Storaged : public BnStoraged {
54 virtual std::vector<struct task_info> dump_tasks(const char* option);
55};
56
57sp<IStoraged> get_storaged_service();
58
59#endif /* _STORAGED_SERVICE_H_ */ \ No newline at end of file
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
new file mode 100644
index 000000000..b0e514605
--- /dev/null
+++ b/storaged/include/storaged_utils.h
@@ -0,0 +1,46 @@
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_UTILS_H_
18#define _STORAGED_UTILS_H_
19
20#include <stdint.h>
21#include <string>
22#include <unordered_map>
23#include <vector>
24
25#include "storaged.h"
26
27// Diskstats
28bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
29struct disk_perf get_disk_perf(struct disk_stats* stats);
30struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
31void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
32bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
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
38// Logging
39void log_console_running_tasks_info(std::vector<struct task_info> tasks);
40void log_kernel_disk_stats(struct disk_stats* stats, const char* type);
41void log_kernel_disk_perf(struct disk_perf* perf, const char* type);
42void log_kernel_emmc_info(struct emmc_info* info);
43
44void log_event_disk_stats(struct disk_stats* stats, const char* type);
45void log_event_emmc_info(struct emmc_info* info_);
46#endif /* _STORAGED_UTILS_H_ */ \ No newline at end of file
diff --git a/storaged/main.cpp b/storaged/main.cpp
new file mode 100644
index 000000000..bd1166a6c
--- /dev/null
+++ b/storaged/main.cpp
@@ -0,0 +1,298 @@
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#define KLOG_LEVEL 6
19
20#include <fcntl.h>
21#include <getopt.h>
22#include <pthread.h>
23#include <stdio.h>
24#include <sys/capability.h>
25#include <sys/prctl.h>
26#include <sys/resource.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <vector>
30
31#include <android-base/macros.h>
32#include <android-base/stringprintf.h>
33#include <binder/ProcessState.h>
34#include <binder/IServiceManager.h>
35#include <binder/IPCThreadState.h>
36#include <cutils/android_get_control_file.h>
37#include <cutils/klog.h>
38#include <cutils/sched_policy.h>
39#include <private/android_filesystem_config.h>
40
41#include <storaged.h>
42#include <storaged_service.h>
43#include <storaged_utils.h>
44
45storaged_t storaged;
46
47static int drop_privs() {
48 // privilege setting
49 struct sched_param param;
50 memset(&param, 0, sizeof(param));
51
52 if (set_sched_policy(0, SP_BACKGROUND) < 0) return -1;
53
54 if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) return -1;
55
56 if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) return -1;
57
58 if (prctl(PR_SET_KEEPCAPS, 1) < 0) return -1;
59
60 std::unique_ptr<struct _cap_struct, int(*)(void *)> caps(cap_init(), cap_free);
61 if (cap_clear(caps.get()) < 0) return -1;
62 cap_value_t cap_value[] = {
63 CAP_SETGID,
64 CAP_SETUID,
65 CAP_SYS_PTRACE // allow access to proc/<pid>/io as non-root user
66 };
67 if (cap_set_flag(caps.get(), CAP_PERMITTED,
68 arraysize(cap_value), cap_value,
69 CAP_SET) < 0) return -1;
70 if (cap_set_flag(caps.get(), CAP_EFFECTIVE,
71 arraysize(cap_value), cap_value,
72 CAP_SET) < 0) return -1;
73 if (cap_set_proc(caps.get()) < 0)
74 return -1;
75
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;
81
82 if (setuid(AID_SYSTEM) != 0) return -1;
83
84 if (cap_set_flag(caps.get(), CAP_PERMITTED, 2, cap_value, CAP_CLEAR) < 0) return -1;
85 if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 2, cap_value, CAP_CLEAR) < 0) return -1;
86 if (cap_set_proc(caps.get()) < 0)
87 return -1;
88
89 return 0;
90}
91
92// Function of storaged's main thread
93extern int fd_dmesg;
94void* storaged_main(void* s) {
95 storaged_t* storaged = (storaged_t*)s;
96
97 if (fd_dmesg >= 0) {
98 static const char start_message[] = {KMSG_PRIORITY(LOG_INFO),
99 's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', ' ', 'S', 't', 'a', 'r', 't', '\n'};
100 write(fd_dmesg, start_message, sizeof(start_message));
101 }
102
103 for (;;) {
104 storaged->event();
105 storaged->pause();
106 }
107 return NULL;
108}
109
110static void help_message(void) {
111 printf("usage: storaged [OPTION]\n");
112 printf(" -d --dump Dump task I/O usage to stdout\n");
113 printf(" -s --start Start storaged (default)\n");
114 printf(" --emmc=INTERVAL Set publish interval of emmc lifetime information (in days)\n");
115 printf(" --diskstats=INTERVAL Set publish interval of diskstats (in hours)\n");
116 printf(" --unit=INTERVAL Set storaged's refresh interval (in seconds)\n");
117 fflush(stdout);
118}
119
120#define HOUR_TO_SEC ( 3600 )
121#define DAY_TO_SEC ( 3600 * 24 )
122
123int main(int argc, char** argv) {
124 klog_set_level(KLOG_LEVEL);
125 int flag_main_service = 0;
126 int flag_dump_task = 0;
127 int flag_config = 0;
128 int unit_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
129 int diskstats_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
130 int emmc_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
131 int fd_emmc = -1;
132 int opt;
133
134 for (;;) {
135 int opt_idx = 0;
136 static struct option long_options[] = {
137 {"start", no_argument, 0, 's'},
138 {"kill", no_argument, 0, 'k'},
139 {"dump", no_argument, 0, 'd'},
140 {"help", no_argument, 0, 'h'},
141 {"unit", required_argument, 0, 0 },
142 {"diskstats", required_argument, 0, 0 },
143 {"emmc", required_argument, 0, 0 }
144 };
145 opt = getopt_long(argc, argv, ":skdh0", long_options, &opt_idx);
146 if (opt == -1) {
147 break;
148 }
149
150 switch (opt) {
151 case 0:
152 printf("option %s", long_options[opt_idx].name);
153 if (optarg) {
154 printf(" with arg %s", optarg);
155 if (strcmp(long_options[opt_idx].name, "unit") == 0) {
156 unit_interval = atoi(optarg);
157 if (unit_interval == 0) {
158 fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
159 long_options[opt_idx].name);
160 help_message();
161 return -1;
162 }
163 } else if (strcmp(long_options[opt_idx].name, "diskstats") == 0) {
164 diskstats_interval = atoi(optarg) * HOUR_TO_SEC;
165 if (diskstats_interval == 0) {
166 fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
167 long_options[opt_idx].name);
168 help_message();
169 return -1;
170 }
171
172 } else if (strcmp(long_options[opt_idx].name, "emmc") == 0) {
173 emmc_interval = atoi(optarg) * DAY_TO_SEC;
174 if (diskstats_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 }
181 flag_config = 1;
182 } else {
183 fprintf(stderr, "Invalid argument. Option %s requires an argument.\n",
184 long_options[opt_idx].name);
185 help_message();
186 return -1;
187 }
188 printf("\n");
189 break;
190 case 's':
191 flag_main_service = 1;
192 break;
193 case 'd':
194 flag_dump_task = 1;
195 break;
196 case 'h':
197 help_message();
198 return 0;
199 case '?':
200 default:
201 fprintf(stderr, "no supported option\n");
202 help_message();
203 return -1;
204 }
205 }
206
207 if (argc == 1) {
208 flag_main_service = 1;
209 }
210
211 if (flag_main_service && flag_dump_task) {
212 fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
213 help_message();
214 return -1;
215 }
216
217 if (flag_config && flag_dump_task) {
218 fprintf(stderr, "Invalid arguments. Cannot set configs in \'dump\' option.\n");
219 help_message();
220 return -1;
221 }
222
223 if (flag_main_service) { // start main thread
224 static const char dev_kmsg[] = "/dev/kmsg";
225 fd_dmesg = android_get_control_file(dev_kmsg);
226 if (fd_dmesg < 0)
227 fd_dmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY));
228
229 static const char mmc0_ext_csd[] = "/d/mmc0/mmc0:0001/ext_csd";
230 fd_emmc = android_get_control_file(mmc0_ext_csd);
231 if (fd_emmc < 0)
232 fd_emmc = TEMP_FAILURE_RETRY(open(mmc0_ext_csd, O_RDONLY));
233
234 if (drop_privs() != 0) {
235 return -1;
236 }
237
238 storaged.set_privileged_fds(fd_emmc);
239
240 if (flag_config) {
241 storaged.set_unit_interval(unit_interval);
242 storaged.set_diskstats_interval(diskstats_interval);
243 storaged.set_emmc_interval(emmc_interval);
244 }
245
246 // Start the main thread of storaged
247 pthread_t storaged_main_thread;
248 if (pthread_create(&storaged_main_thread, NULL, storaged_main, &storaged)) {
249 if (fd_dmesg >= 0) {
250 std::string error_message = android::base::StringPrintf(
251 "%s Failed to create main thread\n", kmsg_error_prefix);
252 write(fd_dmesg, error_message.c_str(), error_message.length());
253 }
254 return -1;
255 }
256
257 defaultServiceManager()->addService(String16("storaged"), new Storaged());
258 android::ProcessState::self()->startThreadPool();
259 IPCThreadState::self()->joinThreadPool();
260 pthread_join(storaged_main_thread, NULL);
261
262 close(fd_dmesg);
263 close(fd_emmc);
264
265 return 0;
266 }
267
268 if (flag_dump_task) {
269 sp<IStoraged> storaged_service = get_storaged_service();
270 if (storaged_service == NULL) {
271 fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
272 return -1;
273 }
274 std::vector<struct task_info> res = storaged_service->dump_tasks(NULL);
275
276 if (res.size() == 0) {
277 fprintf(stderr, "Task I/O is not readable in this version of kernel.\n");
278 return 0;
279 }
280
281 time_t starttime = storaged.get_starttime();
282
283 if (starttime == (time_t)-1) {
284 fprintf(stderr, "Unknown start time\n");
285 } else {
286 char* time_str = ctime(&starttime);
287 printf("Application I/O was collected by storaged since %s", time_str);
288 }
289
290 sort_running_tasks_info(res);
291 log_console_running_tasks_info(res);
292
293
294 return 0;
295 }
296
297 return 0;
298} \ No newline at end of file
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
new file mode 100644
index 000000000..140220867
--- /dev/null
+++ b/storaged/storaged.cpp
@@ -0,0 +1,211 @@
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 <stdlib.h>
20#include <time.h>
21#include <unistd.h>
22
23
24#include <android-base/logging.h>
25
26#include <storaged.h>
27#include <storaged_utils.h>
28
29/* disk_stats_publisher */
30void disk_stats_publisher::publish(void) {
31 // Logging
32 log_kernel_disk_stats(&mAccumulate, "regular");
33 struct disk_perf perf = get_disk_perf(&mAccumulate);
34 log_kernel_disk_perf(&perf, "regular");
35 log_event_disk_stats(&mAccumulate, "regular");
36 // Reset global structures
37 memset(&mAccumulate, 0, sizeof(struct disk_stats));
38}
39
40void disk_stats_publisher::update(void) {
41 struct disk_stats curr;
42 if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
43 struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
44 add_disk_stats(&inc, &mAccumulate);
45 #ifdef DEBUG
46// log_kernel_disk_stats(&mPrevious, "prev stats");
47// log_kernel_disk_stats(&curr, "curr stats");
48// log_kernel_disk_stats(&inc, "inc stats");
49// log_kernel_disk_stats(&mAccumulate, "accumulated stats");
50 #endif
51 mPrevious = curr;
52 }
53}
54
55/* disk_stats_monitor */
56void disk_stats_monitor::update_mean() {
57 CHECK(mValid);
58 mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
59 mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
60 mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
61 mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
62 mMean.queue = (uint32_t)mStats.queue.get_mean();
63}
64
65void disk_stats_monitor::update_std() {
66 CHECK(mValid);
67 mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
68 mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
69 mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
70 mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
71 mStd.queue = (uint32_t)mStats.queue.get_std();
72}
73
74void disk_stats_monitor::add(struct disk_perf* perf) {
75 mStats.read_perf.add(perf->read_perf);
76 mStats.read_ios.add(perf->read_ios);
77 mStats.write_perf.add(perf->write_perf);
78 mStats.write_ios.add(perf->write_ios);
79 mStats.queue.add(perf->queue);
80}
81
82void disk_stats_monitor::evict(struct disk_perf* perf) {
83 mStats.read_perf.evict(perf->read_perf);
84 mStats.read_ios.evict(perf->read_ios);
85 mStats.write_perf.evict(perf->write_perf);
86 mStats.write_ios.evict(perf->write_ios);
87 mStats.queue.evict(perf->queue);
88}
89
90bool disk_stats_monitor::detect(struct disk_perf* perf) {
91 return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
92 ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
93 ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
94}
95
96void disk_stats_monitor::update(struct disk_stats* stats) {
97 struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
98 struct disk_perf perf = get_disk_perf(&inc);
99 // Update internal data structures
100 if (LIKELY(mValid)) {
101 CHECK_EQ(mBuffer.size(), mWindow);
102
103 if (UNLIKELY(detect(&perf))) {
104 mStall = true;
105 add_disk_stats(&inc, &mAccumulate);
106 #ifdef DEBUG
107 log_kernel_disk_perf(&mMean, "stalled_mean");
108 log_kernel_disk_perf(&mStd, "stalled_std");
109 #endif
110 } else {
111 if (mStall) {
112 log_kernel_disk_stats(&mAccumulate, "stalled");
113 struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
114 log_kernel_disk_perf(&acc_perf, "stalled");
115
116 log_event_disk_stats(&mAccumulate, "stalled");
117 mStall = false;
118 memset(&mAccumulate, 0, sizeof(mAccumulate));
119 }
120 }
121
122 evict(&mBuffer.front());
123 mBuffer.pop();
124 add(&perf);
125 mBuffer.push(perf);
126
127 update_mean();
128 update_std();
129
130 } else { /* mValid == false */
131 CHECK_LT(mBuffer.size(), mWindow);
132 add(&perf);
133 mBuffer.push(perf);
134 if (mBuffer.size() == mWindow) {
135 mValid = true;
136 update_mean();
137 update_std();
138 }
139 }
140
141 mPrevious = *stats;
142}
143
144void disk_stats_monitor::update(void) {
145 struct disk_stats curr;
146 if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
147 update(&curr);
148 }
149}
150
151/* emmc_info_t */
152void emmc_info_t::publish(void) {
153 if (mValid) {
154 log_kernel_emmc_info(&mInfo);
155 log_event_emmc_info(&mInfo);
156 }
157}
158
159void emmc_info_t::update(void) {
160 if (mFdEmmc >= 0) {
161 mValid = parse_emmc_ecsd(mFdEmmc, &mInfo);
162 }
163}
164
165/* storaged_t */
166storaged_t::storaged_t(void) {
167 mConfig.emmc_available = (access(EMMC_ECSD_PATH, R_OK) >= 0);
168
169 if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
170 mConfig.diskstats_available = false;
171 } else {
172 mConfig.diskstats_available = true;
173 }
174
175 mConfig.proc_taskio_readable = true;
176 const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
177 for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
178 if (access(test_paths[i], R_OK) < 0) {
179 mConfig.proc_taskio_readable = false;
180 break;
181 }
182 }
183
184 mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
185 mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
186 mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
187
188 mStarttime = time(NULL);
189}
190
191void storaged_t::event(void) {
192 if (mConfig.diskstats_available) {
193 mDiskStats.update();
194 mDsm.update();
195 if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
196 mDiskStats.publish();
197 }
198 }
199
200 if (mConfig.proc_taskio_readable) {
201 mTasks.update_running_tasks();
202 }
203
204 if (mConfig.emmc_available && mTimer &&
205 (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
206 mEmmcInfo.update();
207 mEmmcInfo.publish();
208 }
209
210 mTimer += mConfig.periodic_chores_interval_unit;
211} \ No newline at end of file
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
new file mode 100644
index 000000000..f72521c8e
--- /dev/null
+++ b/storaged/storaged.rc
@@ -0,0 +1,5 @@
1service storaged /system/bin/storaged
2 class main
3 file /d/mmc0/mmc0:0001/ext_csd r
4 file /dev/kmsg w
5 group root readproc
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
new file mode 100644
index 000000000..aa38ceb64
--- /dev/null
+++ b/storaged/storaged_service.cpp
@@ -0,0 +1,78 @@
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#include <vector>
18
19#include <binder/IBinder.h>
20#include <binder/IInterface.h>
21
22#include <binder/IPCThreadState.h>
23#include <binder/IServiceManager.h>
24
25#include <storaged.h>
26#include <storaged_service.h>
27
28extern storaged_t storaged;
29
30std::vector<struct task_info> BpStoraged::dump_tasks(const char* /*option*/) {
31 Parcel data, reply;
32 data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
33
34 remote()->transact(DUMPTASKS, data, &reply);
35
36 uint32_t res_size = reply.readInt32();
37 std::vector<struct task_info> res(res_size);
38 for (auto&& task : res) {
39 reply.read(&task, sizeof(task));
40 }
41 return res;
42}
43
44IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
45
46status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
47 data.checkInterface(this);
48
49 switch(code) {
50 case DUMPTASKS: {
51 std::vector<struct task_info> res = dump_tasks(NULL);
52
53 reply->writeInt32(res.size());
54 for (auto task : res) {
55 reply->write(&task, sizeof(task));
56 }
57 return NO_ERROR;
58 }
59 break;
60 default:
61 return BBinder::onTransact(code, data, reply, flags);
62 }
63}
64std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
65 return storaged.get_tasks();
66}
67
68sp<IStoraged> get_storaged_service() {
69 sp<IServiceManager> sm = defaultServiceManager();
70 if (sm == NULL) return NULL;
71
72 sp<IBinder> binder = sm->getService(String16("storaged"));
73 if (binder == NULL) return NULL;
74
75 sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
76
77 return storaged;
78} \ No newline at end of file
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
new file mode 100644
index 000000000..9c0b62595
--- /dev/null
+++ b/storaged/storaged_utils.cpp
@@ -0,0 +1,582 @@
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 <fcntl.h>
20#include <sys/stat.h>
21#include <linux/time.h>
22
23#include <dirent.h>
24#include <stdio.h>
25#include <stdint.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29
30#include <sstream>
31#include <string>
32#include <unordered_map>
33
34#include <android-base/file.h>
35#include <android-base/stringprintf.h>
36#include <android-base/strings.h>
37#include <android-base/logging.h>
38#include <log/log.h>
39#include <log/log_event_list.h>
40#include <cutils/klog.h>
41
42#include <storaged.h>
43#include <storaged_utils.h>
44
45#include <time.h>
46
47#define SECTOR_SIZE ( 512 )
48#define SEC_TO_MSEC ( 1000 )
49#define MSEC_TO_USEC ( 1000 )
50#define USEC_TO_NSEC ( 1000 )
51
52int fd_dmesg = -1;
53
54bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
55 // Get time
56 struct timespec ts;
57 // Use monotonic to exclude suspend time so that we measure IO bytes/sec
58 // when system is running.
59 int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
60 if (ret < 0) {
61 if (fd_dmesg >= 0) {
62 std::string error_message = android::base::StringPrintf(
63 "%s clock_gettime() failed with errno %d\n",
64 kmsg_error_prefix, ret);
65 write(fd_dmesg, error_message.c_str(), error_message.length());
66 }
67 return false;
68 }
69
70 std::string buffer;
71 if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
72 if (fd_dmesg >= 0) {
73 std::string error_message = android::base::StringPrintf(
74 "%s %s: ReadFileToString failed.\n", kmsg_error_prefix, disk_stats_path);
75 write(fd_dmesg, error_message.c_str(), error_message.length());
76 }
77 return false;
78 }
79
80 // Regular diskstats entries
81 std::stringstream ss(buffer);
82 for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
83 ss >> *((uint64_t*)stats + i);
84 }
85 // Other entries
86 stats->start_time = 0;
87 stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
88 ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
89 stats->counter = 1;
90 stats->io_avg = (double)stats->io_in_flight;
91 return true;
92}
93
94struct disk_perf get_disk_perf(struct disk_stats* stats) {
95 struct disk_perf perf;
96 memset(&perf, 0, sizeof(struct disk_perf)); // initialize
97
98 if (stats->io_ticks) {
99 if (stats->read_ticks) {
100 unsigned long long divisor = stats->read_ticks * stats->io_ticks;
101 perf.read_perf = ((unsigned long long)SECTOR_SIZE *
102 stats->read_sectors *
103 stats->io_in_queue +
104 (divisor >> 1)) /
105 divisor;
106 perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
107 stats->read_ios *
108 stats->io_in_queue +
109 (divisor >> 1)) /
110 divisor;
111 }
112 if (stats->write_ticks) {
113 unsigned long long divisor = stats->write_ticks * stats->io_ticks;
114 perf.write_perf = ((unsigned long long)SECTOR_SIZE *
115 stats->write_sectors *
116 stats->io_in_queue +
117 (divisor >> 1)) /
118 divisor;
119 perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
120 stats->write_ios *
121 stats->io_in_queue +
122 (divisor >> 1)) /
123 divisor;
124 }
125 perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
126 stats->io_ticks;
127 }
128 return perf;
129}
130
131struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
132 struct disk_stats inc;
133 for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
134 if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
135 continue;
136 }
137
138 *((uint64_t*)&inc + i) =
139 *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
140 }
141 // io_in_flight is exception
142 inc.io_in_flight = curr->io_in_flight;
143
144 inc.start_time = prev->end_time;
145 inc.end_time = curr->end_time;
146 inc.io_avg = curr->io_avg;
147 inc.counter = 1;
148
149 return inc;
150}
151
152// Add src to dst
153void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
154 if (dst->end_time != 0 && dst->end_time != src->start_time && fd_dmesg >= 0) {
155 std::string warning_message = android::base::StringPrintf(
156 "%s Two dis-continuous periods of diskstats are added. "
157 "dst end with %jd, src start with %jd\n",
158 kmsg_warning_prefix, dst->end_time, src->start_time);
159
160 write(fd_dmesg, warning_message.c_str(), warning_message.length());
161 }
162
163 for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
164 if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
165 continue;
166 }
167
168 *((uint64_t*)dst + i) += *((uint64_t*)src + i);
169 }
170
171 dst->io_in_flight = src->io_in_flight;
172 if (dst->counter + src->counter) {
173 dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
174 (dst->counter + src->counter);
175 }
176 dst->counter += src->counter;
177 dst->end_time = src->end_time;
178 if (dst->start_time == 0) {
179 dst->start_time = src->start_time;
180 }
181}
182
183bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) {
184 CHECK(ext_csd_fd >= 0);
185 struct hex {
186 char str[2];
187 };
188 // List of interesting offsets
189 static const size_t EXT_CSD_REV_IDX = 192 * sizeof(hex);
190 static const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(hex);
191 static const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(hex);
192 static const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(hex);
193
194 // Read file
195 CHECK(lseek(ext_csd_fd, 0, SEEK_SET) == 0);
196 std::string buffer;
197 if (!android::base::ReadFdToString(ext_csd_fd, &buffer)) {
198 if (fd_dmesg >= 0) {
199 std::string error_message = android::base::StringPrintf(
200 "%s ReadFdToString failed.\n", kmsg_error_prefix);
201 write(fd_dmesg, error_message.c_str(), error_message.length());
202 }
203 return false;
204 }
205
206 if (buffer.length() < EXT_CSD_FILE_MIN_SIZE) {
207 if (fd_dmesg >= 0) {
208 std::string error_message = android::base::StringPrintf(
209 "%s EMMC ext csd file has truncated content. File length: %d\n",
210 kmsg_error_prefix, (int)buffer.length());
211 write(fd_dmesg, error_message.c_str(), error_message.length());
212 }
213 return false;
214 }
215
216 std::string sub;
217 std::stringstream ss;
218 // Parse EXT_CSD_REV
219 int ext_csd_rev = -1;
220 sub = buffer.substr(EXT_CSD_REV_IDX, sizeof(hex));
221 ss << sub;
222 ss >> std::hex >> ext_csd_rev;
223 if (ext_csd_rev < 0) {
224 if (fd_dmesg >= 0) {
225 std::string error_message = android::base::StringPrintf(
226 "%s Failure on parsing EXT_CSD_REV.\n", kmsg_error_prefix);
227 write(fd_dmesg, error_message.c_str(), error_message.length());
228 }
229 return false;
230 }
231 ss.clear();
232
233 static const char* ver_str[] = {
234 "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
235 };
236
237 strncpy(info->mmc_ver,
238 (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
239 ver_str[ext_csd_rev] :
240 "Unknown",
241 MMC_VER_STR_LEN);
242
243 if (ext_csd_rev < 7) {
244 return 0;
245 }
246
247 // Parse EXT_PRE_EOL_INFO
248 info->eol = -1;
249 sub = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(hex));
250 ss << sub;
251 ss >> std::hex >> info->eol;
252 if (info->eol < 0) {
253 if (fd_dmesg >= 0) {
254 std::string error_message = android::base::StringPrintf(
255 "%s Failure on parsing EXT_PRE_EOL_INFO.\n", kmsg_error_prefix);
256 write(fd_dmesg, error_message.c_str(), error_message.length());
257 }
258 return false;
259 }
260 ss.clear();
261
262 // Parse DEVICE_LIFE_TIME_EST
263 info->lifetime_a = -1;
264 sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(hex));
265 ss << sub;
266 ss >> std::hex >> info->lifetime_a;
267 if (info->lifetime_a < 0) {
268 if (fd_dmesg >= 0) {
269 std::string error_message = android::base::StringPrintf(
270 "%s Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.\n", kmsg_error_prefix);
271 write(fd_dmesg, error_message.c_str(), error_message.length());
272 }
273 return false;
274 }
275 ss.clear();
276
277 info->lifetime_b = -1;
278 sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(hex));
279 ss << sub;
280 ss >> std::hex >> info->lifetime_b;
281 if (info->lifetime_b < 0) {
282 if (fd_dmesg >= 0) {
283 std::string error_message = android::base::StringPrintf(
284 "%s Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.\n", kmsg_error_prefix);
285 write(fd_dmesg, error_message.c_str(), error_message.length());
286 }
287 return false;
288 }
289 ss.clear();
290
291 return true;
292}
293
294#define PROC_DIR "/proc/"
295#define PROC_STAT_STARTTIME_IDX ( 22 ) // This index is 1 based according to the linux proc man page
296bool parse_task_info(uint32_t pid, struct task_info* info) {
297 std::string buffer;
298 std::string pid_str = std::to_string(pid);
299 info->pid = pid;
300
301 // Get task I/O
302 std::string task_io_path = android::base::StringPrintf(PROC_DIR "%s/io", pid_str.c_str());
303 if (!android::base::ReadFileToString(task_io_path, &buffer)) return false;
304
305 std::stringstream ss(buffer);
306 std::string title;
307
308 ss >> title >> info->rchar
309 >> title >> info->wchar
310 >> title >> info->syscr
311 >> title >> info->syscw
312 >> title >> info->read_bytes
313 >> title >> info->write_bytes
314 >> title >> info->cancelled_write_bytes;
315 ss.clear();
316
317 // Get cmd string
318 std::string task_cmdline_path = android::base::StringPrintf(PROC_DIR "%u/cmdline", pid);
319 if (!android::base::ReadFileToString(task_cmdline_path, &buffer)) return false;
320 strcpy(info->cmd, android::base::Trim(buffer).c_str());
321
322 if (info->cmd[0] == '\0') {
323 std::string task_comm_path = android::base::StringPrintf(PROC_DIR "%u/comm", pid);
324 if (!android::base::ReadFileToString(task_comm_path, &buffer)) return false;
325 strcpy(info->cmd, android::base::Trim(buffer).c_str());
326 }
327
328 // Get task start time
329 std::string task_stat_path = android::base::StringPrintf(PROC_DIR "%u/stat", pid);
330 if (!android::base::ReadFileToString(task_stat_path, &buffer)) return false;
331
332 std::vector<std::string> stat_parts = android::base::Split(buffer, " ");
333 info->starttime = atoll(stat_parts[PROC_STAT_STARTTIME_IDX - 1].c_str());
334
335 return true;
336}
337
338static bool is_pid(char* d_name) {
339 if (!d_name || d_name[0] == '\0') return false;
340 char* c = d_name;
341 while (*c) {
342 if (!isdigit(*c)) return false;
343 ++c;
344 }
345 return true;
346}
347
348static bool cmp_task_info(struct task_info i, struct task_info j) {
349 if (i.write_bytes + i.read_bytes != j.write_bytes + j.read_bytes) {
350 return i.write_bytes + i.read_bytes > j.write_bytes + j.read_bytes;
351 }
352 if (i.wchar + i.rchar != j.wchar + j.rchar) {
353 return i.wchar + i.rchar > j.wchar + j.rchar;
354 }
355 if (i.syscw + i.syscr != j.syscw + j.syscr) {
356 return i.syscw + i.syscr > j.syscw + j.syscr;
357 }
358
359 return strcmp(i.cmd, j.cmd) < 0;
360}
361
362std::unordered_map<uint32_t, struct task_info> tasks_t::get_running_tasks() {
363 std::unordered_map<uint32_t, struct task_info> retval;
364 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(PROC_DIR), closedir);
365 CHECK(dir != NULL);
366 struct dirent* dp;
367
368 for (;;) {
369 if ((dp = readdir(dir.get())) == NULL) break;
370 if (!is_pid(dp->d_name)) continue;
371
372 uint32_t pid = atol(dp->d_name);
373 struct task_info info;
374 if (parse_task_info(pid, &info)) {
375 retval[pid] = info;
376 }
377 }
378 return retval;
379}
380
381static void add_task_info(struct task_info* src, struct task_info* dst) {
382 CHECK(strcmp(src->cmd, dst->cmd) == 0);
383
384 dst->pid = 0;
385 dst->rchar += src->rchar;
386 dst->wchar += src->wchar;
387 dst->syscr += src->syscr;
388 dst->syscw += src->syscw;
389 dst->read_bytes += src->read_bytes;
390 dst->write_bytes += src->write_bytes;
391 dst->cancelled_write_bytes += src->cancelled_write_bytes;
392 dst->starttime = 0;
393}
394
395void tasks_t::update_running_tasks(void) {
396 std::unordered_map<uint32_t, struct task_info> tasks_latest = get_running_tasks();
397 std::unordered_map<std::string, struct task_info> tasks_old = mOld;
398
399 for (auto t : mRunning) {
400 uint32_t pid = t.first;
401 // old task on mRunning still exist on tasks_latest
402 if (tasks_latest.find(pid) != tasks_latest.end() &&
403 tasks_latest[pid].starttime == t.second.starttime) {
404 continue;
405 } else {
406 // This branch will handle 2 cases:
407 // - Task get killed between the 2 samplings
408 // - Task get killed and its pid is reused
409 std::string cmd = t.second.cmd;
410 struct task_info info = t.second;
411
412 if (tasks_old.find(cmd) == tasks_old.end()) {
413 tasks_old[cmd] = info;
414 } else {
415 add_task_info(&info, &tasks_old[cmd]);
416 }
417 }
418 }
419 { // update critical area
420 // this is really fast!
421 std::unique_ptr<lock_t> lock(new lock_t(&mSem));
422 mRunning = tasks_latest;
423 mOld = tasks_old;
424 }
425
426}
427
428std::vector<struct task_info> tasks_t::get_tasks(void) {
429 std::unique_ptr<lock_t> lock(new lock_t(&mSem));
430 std::unordered_map<std::string, struct task_info> tasks_map = mOld;
431
432 for (auto i : mRunning) {
433 std::string cmd = i.second.cmd;
434 if (tasks_map.find(cmd) == tasks_map.end()) {
435 tasks_map[cmd] = i.second;
436 } else {
437 add_task_info(&i.second, &tasks_map[cmd]);
438 }
439 }
440
441 std::vector<struct task_info> retval(tasks_map.size());
442 int idx = 0;
443 for (auto i : tasks_map) {
444 retval[idx++] = i.second;
445 }
446
447 return retval;
448}
449
450void sort_running_tasks_info(std::vector<struct task_info> &tasks) {
451 std::sort(tasks.begin(), tasks.end(), cmp_task_info);
452}
453
454/* Logging functions */
455void log_console_running_tasks_info(std::vector<struct task_info> tasks) {
456// Sample Output:
457// Application Read Write Read Write Read Write Cancelled
458// Name Characters Characters Syscalls Syscalls Bytes Bytes Writebytes
459// ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
460// zygote64 37688308 3388467 7607 4363 314519552 5373952 8192
461// system_server 95874193 2216913 74613 52257 213078016 7237632 16384
462// zygote 506279 1726194 921 263 128114688 1765376 0
463// /vendor/bin/qcks 75415632 75154382 21672 25036 63627264 29974528 10485760
464// /init 86658523 5107871 82113 8633 91015168 1245184 0
465
466 // Title
467 printf(" Application Read Write Read Write Read Write Cancelled\n"
468 " Name Characters Characters Syscalls Syscalls Bytes Bytes Writebytes\n"
469 " ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n");
470
471 for (struct task_info task : tasks) {
472 printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n",
473 task.cmd, task.rchar, task.wchar, task.syscr, task.syscw,
474 task.read_bytes, task.write_bytes, task.cancelled_write_bytes);
475 }
476 fflush(stdout);
477}
478
479void log_kernel_disk_stats(struct disk_stats* stats, const char* type) {
480 // skip if the input structure are all zeros
481 if (stats == NULL) return;
482 struct disk_stats zero_cmp;
483 memset(&zero_cmp, 0, sizeof(zero_cmp));
484 if (memcmp(&zero_cmp, stats, sizeof(struct disk_stats)) == 0) return;
485
486 if (fd_dmesg >= 0) {
487 std::string info_message = android::base::StringPrintf(
488 "%s diskstats %s: %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %.1f %ju %ju\n",
489 kmsg_info_prefix, type, stats->start_time, stats->end_time,
490 stats->read_ios, stats->read_merges,
491 stats->read_sectors, stats->read_ticks,
492 stats->write_ios, stats->write_merges,
493 stats->write_sectors, stats->write_ticks,
494 stats->io_avg, stats->io_ticks,
495 stats->io_in_queue);
496
497 write(fd_dmesg, info_message.c_str(), info_message.length());
498 }
499}
500
501void log_kernel_disk_perf(struct disk_perf* perf, const char* type) {
502 // skip if the input structure are all zeros
503 if (perf == NULL) return;
504 struct disk_perf zero_cmp;
505 memset(&zero_cmp, 0, sizeof(zero_cmp));
506 if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
507
508 if (fd_dmesg >= 0) {
509 std::string info_message = android::base::StringPrintf(
510 "%s perf(ios) %s rd:%luKB/s(%lu/s) wr:%luKB/s(%lu/s) q:%lu\n",
511 kmsg_info_prefix, type,
512 (unsigned long)perf->read_perf, (unsigned long)perf->read_ios,
513 (unsigned long)perf->write_perf, (unsigned long)perf->write_ios,
514 (unsigned long)perf->queue);
515
516 write(fd_dmesg, info_message.c_str(), info_message.length());
517 }
518}
519
520void log_kernel_emmc_info(struct emmc_info* info) {
521 // skip if the input structure are all zeros
522 if (info == NULL) return;
523 struct emmc_info zero_cmp;
524 memset(&zero_cmp, 0, sizeof(zero_cmp));
525 if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return;
526
527 if (fd_dmesg >= 0) {
528 std::string info_message = android::base::StringPrintf(
529 "%s MMC %s eol:%d, lifetime typA:%d, typB:%d\n",
530 kmsg_info_prefix, info->mmc_ver, info->eol, info->lifetime_a, info->lifetime_b);
531
532 write(fd_dmesg, info_message.c_str(), info_message.length());
533 }
534}
535
536void log_event_disk_stats(struct disk_stats* stats, const char* type) {
537 // skip if the input structure are all zeros
538 if (stats == NULL) return;
539 struct disk_stats zero_cmp;
540 memset(&zero_cmp, 0, sizeof(zero_cmp));
541 // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
542 if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
543
544 // Construct eventlog list
545 android_log_context ctx = create_android_logger(EVENTLOGTAG_DISKSTATS);
546
547 android_log_write_string8(ctx, type);
548 android_log_write_int64(ctx, stats->start_time);
549 android_log_write_int64(ctx, stats->end_time);
550 android_log_write_int64(ctx, stats->read_ios);
551 android_log_write_int64(ctx, stats->read_merges);
552 android_log_write_int64(ctx, stats->read_sectors);
553 android_log_write_int64(ctx, stats->read_ticks);
554 android_log_write_int64(ctx, stats->write_ios);
555 android_log_write_int64(ctx, stats->write_merges);
556 android_log_write_int64(ctx, stats->write_sectors);
557 android_log_write_int64(ctx, stats->write_ticks);
558 android_log_write_int64(ctx, (uint64_t)stats->io_avg);
559 android_log_write_int64(ctx, stats->io_ticks);
560 android_log_write_int64(ctx, stats->io_in_queue);
561
562 android_log_write_list(ctx, LOG_ID_EVENTS);
563 android_log_destroy(&ctx);
564}
565
566void log_event_emmc_info(struct emmc_info* info) {
567 // skip if the input structure are all zeros
568 if (info == NULL) return;
569 struct emmc_info zero_cmp;
570 memset(&zero_cmp, 0, sizeof(zero_cmp));
571 if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return;
572
573 android_log_context ctx = create_android_logger(EVENTLOGTAG_EMMCINFO);
574
575 android_log_write_string8(ctx, info->mmc_ver);
576 android_log_write_int32(ctx, info->eol);
577 android_log_write_int32(ctx, info->lifetime_a);
578 android_log_write_int32(ctx, info->lifetime_b);
579
580 android_log_write_list(ctx, LOG_ID_EVENTS);
581 android_log_destroy(&ctx);
582}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
new file mode 100644
index 000000000..4a0e45c78
--- /dev/null
+++ b/storaged/tests/Android.mk
@@ -0,0 +1,45 @@
1#
2# Copyright (C) 2014 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
17LOCAL_PATH := $(call my-dir)
18
19test_module_prefix := storaged-
20test_tags := tests
21
22# -----------------------------------------------------------------------------
23# Unit tests.
24# -----------------------------------------------------------------------------
25
26test_c_flags := \
27 -fstack-protector-all \
28 -g \
29 -Wall -Wextra \
30 -Werror \
31 -fno-builtin \
32
33test_src_files := \
34 storaged_test.cpp \
35
36# Build tests for the logger. Run with:
37# adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
38include $(CLEAR_VARS)
39LOCAL_MODULE := $(test_module_prefix)unit-tests
40LOCAL_MODULE_TAGS := $(test_tags)
41LOCAL_CFLAGS += $(test_c_flags)
42LOCAL_STATIC_LIBRARIES := libstoraged
43LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
44LOCAL_SRC_FILES := $(test_src_files)
45include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
new file mode 100644
index 000000000..4ca9839b6
--- /dev/null
+++ b/storaged/tests/storaged_test.cpp
@@ -0,0 +1,587 @@
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#include <deque>
18#include <fcntl.h>
19#include <random>
20#include <string.h>
21#include <stdio.h>
22#include <sys/stat.h>
23#include <unistd.h>
24
25#include <gtest/gtest.h>
26//#include <private/android_logger.h>
27
28#include <storaged.h> // data structures
29#include <storaged_utils.h> // functions to test
30
31#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
32#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
33#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
34#define INIT_TASK_IO_PATH "/proc/1/io"
35
36static void pause(uint32_t sec) {
37 const char* path = "/cache/test";
38 int fd = open(path, O_WRONLY | O_CREAT);
39 ASSERT_LT(-1, fd);
40 char buffer[2048];
41 memset(buffer, 1, sizeof(buffer));
42 int loop_size = 100;
43 for (int i = 0; i < loop_size; ++i) {
44 ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));
45 }
46 fsync(fd);
47 close(fd);
48
49 fd = open(path, O_RDONLY);
50 ASSERT_LT(-1, fd);
51 for (int i = 0; i < loop_size; ++i) {
52 ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));
53 }
54 close(fd);
55
56 sleep(sec);
57}
58
59// the return values of the tested functions should be the expected ones
60const char* DISK_STATS_PATH;
61TEST(storaged_test, retvals) {
62 struct disk_stats stats;
63 struct emmc_info info;
64 memset(&stats, 0, sizeof(struct disk_stats));
65 memset(&info, 0, sizeof(struct emmc_info));
66
67 int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
68 if (emmc_fd >= 0) {
69 EXPECT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
70 }
71
72 if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
73 DISK_STATS_PATH = MMC_DISK_STATS_PATH;
74 } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
75 DISK_STATS_PATH = SDA_DISK_STATS_PATH;
76 } else {
77 return;
78 }
79
80 EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
81
82 struct disk_stats old_stats;
83 memset(&old_stats, 0, sizeof(struct disk_stats));
84 old_stats = stats;
85
86 const char wrong_path[] = "/this/is/wrong";
87 EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
88
89 // reading a wrong path should not damage the output structure
90 EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
91}
92
93TEST(storaged_test, disk_stats) {
94 struct disk_stats stats;
95 memset(&stats, 0, sizeof(struct disk_stats));
96
97 ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
98
99 // every entry of stats (except io_in_flight) should all be greater than 0
100 for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
101 if (i == 8) continue; // skip io_in_flight which can be 0
102 EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));
103 }
104
105 // accumulation of the increments should be the same with the overall increment
106 struct disk_stats base, tmp, curr, acc, inc[5];
107 memset(&base, 0, sizeof(struct disk_stats));
108 memset(&tmp, 0, sizeof(struct disk_stats));
109 memset(&acc, 0, sizeof(struct disk_stats));
110
111 for (uint i = 0; i < 5; ++i) {
112 ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
113 if (i == 0) {
114 base = curr;
115 tmp = curr;
116 sleep(5);
117 continue;
118 }
119 inc[i] = get_inc_disk_stats(&tmp, &curr);
120 add_disk_stats(&inc[i], &acc);
121 tmp = curr;
122 pause(5);
123 }
124 struct disk_stats overall_inc;
125 memset(&overall_inc, 0, sizeof(disk_stats));
126 overall_inc= get_inc_disk_stats(&base, &curr);
127
128 for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
129 if (i == 8) continue; // skip io_in_flight which can be 0
130 EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
131 }
132}
133
134TEST(storaged_test, emmc_info) {
135 struct emmc_info info, void_info;
136 memset(&info, 0, sizeof(struct emmc_info));
137 memset(&void_info, 0, sizeof(struct emmc_info));
138
139 if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
140 int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
141 ASSERT_GE(emmc_fd, 0);
142 ASSERT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
143 // parse_emmc_ecsd() should put something in info.
144 EXPECT_NE(0, memcmp(&void_info, &info, sizeof(struct emmc_info)));
145 }
146}
147
148TEST(storaged_test, task_info) {
149 // parse_task_info should read something other than 0 from /proc/1/*
150 struct task_info task_info;
151 memset(&task_info, 0, sizeof(task_info));
152
153 if (!parse_task_info(1, &task_info)) return;
154
155 EXPECT_EQ((uint32_t)1, task_info.pid);
156 EXPECT_LT((uint64_t)0, task_info.rchar);
157 EXPECT_LT((uint64_t)0, task_info.wchar);
158 EXPECT_LT((uint64_t)0, task_info.syscr);
159 EXPECT_LT((uint64_t)0, task_info.syscw);
160 EXPECT_LT((uint64_t)0, task_info.read_bytes);
161 EXPECT_LT((uint64_t)0, task_info.write_bytes);
162 // cancelled_write_bytes of init could be 0, there is no need to test
163 EXPECT_LE((uint64_t)0, task_info.starttime);
164 EXPECT_NE((char*)NULL, strstr(task_info.cmd, "init"));
165
166 // Entries in /proc/1/io should be increasing through time
167 struct task_info task_old, task_new;
168 memset(&task_old, 0, sizeof(task_old));
169 memset(&task_new, 0, sizeof(task_new));
170
171 // parse_task_info should succeed at this point
172 ASSERT_TRUE(parse_task_info(1, &task_old));
173 sleep(1);
174 ASSERT_TRUE(parse_task_info(1, &task_new));
175
176 EXPECT_EQ(task_old.pid, task_new.pid);
177 EXPECT_LE(task_old.rchar, task_new.rchar);
178 EXPECT_LE(task_old.wchar, task_new.wchar);
179 EXPECT_LE(task_old.syscr, task_new.syscr);
180 EXPECT_LE(task_old.syscw, task_new.syscw);
181 EXPECT_LE(task_old.read_bytes, task_new.read_bytes);
182 EXPECT_LE(task_old.write_bytes, task_new.write_bytes);
183 EXPECT_LE(task_old.cancelled_write_bytes, task_new.cancelled_write_bytes);
184 EXPECT_EQ(task_old.starttime, task_new.starttime);
185 EXPECT_EQ(0, strcmp(task_old.cmd, task_new.cmd));
186}
187
188static double mean(std::deque<uint32_t> nums) {
189 double sum = 0.0;
190 for (uint32_t i : nums) {
191 sum += i;
192 }
193 return sum / nums.size();
194}
195
196static double standard_deviation(std::deque<uint32_t> nums) {
197 double sum = 0.0;
198 double avg = mean(nums);
199 for (uint32_t i : nums) {
200 sum += ((double)i - avg) * ((double)i - avg);
201 }
202 return sqrt(sum / nums.size());
203}
204
205TEST(storaged_test, stream_stats) {
206 // 100 random numbers
207 std::vector<uint32_t> data = {8147,9058,1270,9134,6324,975,2785,5469,9575,9649,1576,9706,9572,4854,8003,1419,4218,9157,7922,9595,6557,357,8491,9340,6787,7577,7431,3922,6555,1712,7060,318,2769,462,971,8235,6948,3171,9502,344,4387,3816,7655,7952,1869,4898,4456,6463,7094,7547,2760,6797,6551,1626,1190,4984,9597,3404,5853,2238,7513,2551,5060,6991,8909,9593,5472,1386,1493,2575,8407,2543,8143,2435,9293,3500,1966,2511,6160,4733,3517,8308,5853,5497,9172,2858,7572,7537,3804,5678,759,540,5308,7792,9340,1299,5688,4694,119,3371};
208 std::deque<uint32_t> test_data;
209 stream_stats sstats;
210 for (uint32_t i : data) {
211 test_data.push_back(i);
212 sstats.add(i);
213
214 EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
215 EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
216 }
217
218 for (uint32_t i : data) {
219 test_data.pop_front();
220 sstats.evict(i);
221
222 EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
223 EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
224 }
225
226 // some real data
227 std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};
228 test_data.clear();
229 uint32_t window_size = 2;
230 uint32_t idx;
231 stream_stats sstats1;
232 for (idx = 0; idx < window_size; ++idx) {
233 test_data.push_back(another_data[idx]);
234 sstats1.add(another_data[idx]);
235 }
236 EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
237 EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
238 for (;idx < another_data.size(); ++idx) {
239 test_data.pop_front();
240 sstats1.evict(another_data[idx - window_size]);
241 test_data.push_back(another_data[idx]);
242 sstats1.add(another_data[idx]);
243 EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
244 EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
245 }
246}
247
248static void expect_increasing(struct task_info told, struct task_info tnew) {
249 ASSERT_EQ(told.pid, tnew.pid);
250 ASSERT_EQ(told.starttime, tnew.starttime);
251 ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
252
253 EXPECT_LE(told.rchar, tnew.rchar);
254 EXPECT_LE(told.wchar, tnew.wchar);
255 EXPECT_LE(told.syscr, tnew.syscr);
256 EXPECT_LE(told.syscw, tnew.syscw);
257 EXPECT_LE(told.read_bytes, tnew.read_bytes);
258 EXPECT_LE(told.write_bytes, tnew.write_bytes);
259 EXPECT_LE(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
260}
261
262static void expect_equal(struct task_info told, struct task_info tnew) {
263 ASSERT_EQ(told.pid, tnew.pid);
264 ASSERT_EQ(told.starttime, tnew.starttime);
265 ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
266
267 EXPECT_EQ(told.rchar, tnew.rchar);
268 EXPECT_EQ(told.wchar, tnew.wchar);
269 EXPECT_EQ(told.syscr, tnew.syscr);
270 EXPECT_EQ(told.syscw, tnew.syscw);
271 EXPECT_EQ(told.read_bytes, tnew.read_bytes);
272 EXPECT_EQ(told.write_bytes, tnew.write_bytes);
273 EXPECT_EQ(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
274}
275
276static std::set<uint32_t> find_overlap(std::unordered_map<uint32_t, struct task_info> t1,
277 std::unordered_map<uint32_t, struct task_info> t2) {
278 std::set<uint32_t> retval;
279 for (auto i : t1) {
280 if (t2.find(i.first) != t2.end()) {
281 retval.insert(i.first);
282 }
283 }
284
285 return retval;
286}
287
288static std::set<std::string> find_overlap(std::unordered_map<std::string, struct task_info> t1,
289 std::unordered_map<std::string, struct task_info> t2) {
290 std::set<std::string> retval;
291 for (auto i : t1) {
292 if (t2.find(i.first) != t2.end()) {
293 retval.insert(i.first);
294 }
295 }
296
297 return retval;
298}
299
300static bool cmp_app_name(struct task_info i, struct task_info j) {
301 return strcmp(i.cmd, j.cmd) > 0;
302}
303
304static void expect_match(std::vector<struct task_info> v1, std::vector<struct task_info> v2) {
305 ASSERT_EQ(v1.size(), v2.size());
306 std::sort(v1.begin(), v1.end(), cmp_app_name);
307 std::sort(v2.begin(), v2.end(), cmp_app_name);
308
309 for (uint i = 0; i < v1.size(); ++i) {
310 expect_equal(v1[i], v2[i]);
311 }
312}
313
314static void add_task_info(struct task_info* src, struct task_info* dst) {
315 ASSERT_EQ(0, strcmp(src->cmd, dst->cmd));
316
317 dst->pid = 0;
318 dst->rchar += src->rchar;
319 dst->wchar += src->wchar;
320 dst->syscr += src->syscr;
321 dst->syscw += src->syscw;
322 dst->read_bytes += src->read_bytes;
323 dst->write_bytes += src->write_bytes;
324 dst->cancelled_write_bytes += src->cancelled_write_bytes;
325 dst->starttime = 0;
326}
327
328static std::vector<struct task_info>
329categorize_tasks(std::unordered_map<uint32_t, struct task_info> tasks) {
330 std::unordered_map<std::string, struct task_info> tasks_cmd;
331 for (auto i : tasks) {
332 std::string cmd = i.second.cmd;
333 if (tasks_cmd.find(cmd) == tasks_cmd.end()) {
334 tasks_cmd[cmd] = i.second;
335 } else {
336 add_task_info(&i.second, &tasks_cmd[cmd]);
337 }
338 }
339
340 std::vector<struct task_info> retval(tasks_cmd.size());
341 int cnt = 0;
342 for (auto i : tasks_cmd) {
343 retval[cnt++] = i.second;
344 }
345
346 return retval;
347}
348
349#define TEST_LOOPS 20
350TEST(storaged_test, tasks_t) {
351 // pass this test if /proc/[pid]/io is not readable
352 const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
353 for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
354 if (access(test_paths[i], R_OK) < 0) return;
355 }
356
357 tasks_t tasks;
358 EXPECT_EQ((uint32_t)0, tasks.mRunning.size());
359 EXPECT_EQ((uint32_t)0, tasks.mOld.size());
360
361 tasks.update_running_tasks();
362
363 std::unordered_map<uint32_t, struct task_info> prev_running = tasks.mRunning;
364 std::unordered_map<std::string, struct task_info> prev_old = tasks.mOld;
365
366 // hashmap maintaining
367 std::unordered_map<uint32_t, struct task_info> tasks_pid = tasks.mRunning;
368
369 // get_running_tasks() should return something other than a null map
370 std::unordered_map<uint32_t, struct task_info> test = tasks.get_running_tasks();
371 EXPECT_LE((uint32_t)1, test.size());
372
373 for (int i = 0; i < TEST_LOOPS; ++i) {
374 tasks.update_running_tasks();
375
376 std::set<uint32_t> overlap_running = find_overlap(prev_running, tasks.mRunning);
377 std::set<std::string> overlap_old = find_overlap(prev_old, tasks.mOld);
378
379 // overlap_running should capture init(pid == 1), since init never get killed
380 EXPECT_LE((uint32_t)1, overlap_running.size());
381 EXPECT_NE(overlap_running.find((uint32_t)1), overlap_running.end());
382 // overlap_old should never capture init, since init never get killed
383 EXPECT_EQ(overlap_old.find("init"), overlap_old.end());
384
385 // overlapping entries in previous and current running-tasks map should have increasing contents
386 for (uint32_t i : overlap_running) {
387 expect_increasing(prev_running[i], tasks.mRunning[i]);
388 }
389
390 // overlapping entries in previous and current killed-tasks map should have increasing contents
391 // and the map size should also be increasing
392 for (std::string i : overlap_old) {
393 expect_increasing(prev_old[i], tasks.mOld[i]);
394 }
395 EXPECT_LE(prev_old.size(), tasks.mRunning.size());
396
397 // update app name & tasks_pid
398 for (auto i : tasks.mRunning) {
399 // test will fail if the pid got wrapped
400 if (tasks_pid.find(i.first) != tasks_pid.end()) {
401 expect_increasing(tasks_pid[i.first], i.second);
402 tasks_pid[i.first] = i.second;
403 } else {
404 tasks_pid[i.first] = i.second;
405 }
406 }
407
408 // get maintained tasks
409 std::vector<struct task_info> test_tasks = categorize_tasks(tasks_pid);
410 std::vector<struct task_info> real_tasks = tasks.get_tasks();
411
412 expect_match(test_tasks, real_tasks);
413
414 prev_running = tasks.mRunning;
415 prev_old = tasks.mOld;
416
417 pause(5);
418 }
419}
420
421static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
422 struct disk_perf retval;
423 retval.read_perf = (double)perf.read_perf * mul;
424 retval.read_ios = (double)perf.read_ios * mul;
425 retval.write_perf = (double)perf.write_perf * mul;
426 retval.write_ios = (double)perf.write_ios * mul;
427 retval.queue = (double)perf.queue * mul;
428
429 return retval;
430}
431
432static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
433 struct disk_stats retval;
434 retval.read_ios = stats1.read_ios + stats2.read_ios;
435 retval.read_merges = stats1.read_merges + stats2.read_merges;
436 retval.read_sectors = stats1.read_sectors + stats2.read_sectors;
437 retval.read_ticks = stats1.read_ticks + stats2.read_ticks;
438 retval.write_ios = stats1.write_ios + stats2.write_ios;
439 retval.write_merges = stats1.write_merges + stats2.write_merges;
440 retval.write_sectors = stats1.write_sectors + stats2.write_sectors;
441 retval.write_ticks = stats1.write_ticks + stats2.write_ticks;
442 retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;
443 retval.io_ticks = stats1.io_ticks + stats2.io_ticks;
444 retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;
445 retval.end_time = stats1.end_time + stats2.end_time;
446
447 return retval;
448}
449
450TEST(storaged_test, disk_stats_monitor) {
451 // asserting that there is one file for diskstats
452 ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
453 // testing if detect() will return the right value
454 disk_stats_monitor dsm_detect;
455 // feed monitor with constant perf data for io perf baseline
456 // using constant perf is reasonable since the functionality of stream_stats
457 // has already been tested
458 struct disk_perf norm_perf = {
459 .read_perf = 10 * 1024,
460 .read_ios = 50,
461 .write_perf = 5 * 1024,
462 .write_ios = 25,
463 .queue = 5
464 };
465
466 std::random_device rd;
467 std::mt19937 gen(rd());
468 std::uniform_real_distribution<> rand(0.8, 1.2);
469
470 for (uint i = 0; i < dsm_detect.mWindow; ++i) {
471 struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));
472
473 dsm_detect.add(&perf);
474 dsm_detect.mBuffer.push(perf);
475 EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);
476 }
477
478 dsm_detect.mValid = true;
479 dsm_detect.update_mean();
480 dsm_detect.update_std();
481
482 for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
483 struct disk_perf test_perf;
484 struct disk_perf test_mean = dsm_detect.mMean;
485 struct disk_perf test_std = dsm_detect.mStd;
486
487 test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;
488 test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;
489 test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;
490 test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;
491 test_perf.queue = (double)test_mean.queue + i * test_std.queue;
492
493 EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));
494 }
495
496 // testing if stalled disk_stats can be correctly accumulated in the monitor
497 disk_stats_monitor dsm_acc;
498 struct disk_stats norm_inc = {
499 .read_ios = 200,
500 .read_merges = 0,
501 .read_sectors = 200,
502 .read_ticks = 200,
503 .write_ios = 100,
504 .write_merges = 0,
505 .write_sectors = 100,
506 .write_ticks = 100,
507 .io_in_flight = 0,
508 .io_ticks = 600,
509 .io_in_queue = 300,
510 .start_time = 0,
511 .end_time = 100,
512 .counter = 0,
513 .io_avg = 0
514 };
515
516 struct disk_stats stall_inc = {
517 .read_ios = 200,
518 .read_merges = 0,
519 .read_sectors = 20,
520 .read_ticks = 200,
521 .write_ios = 100,
522 .write_merges = 0,
523 .write_sectors = 10,
524 .write_ticks = 100,
525 .io_in_flight = 0,
526 .io_ticks = 600,
527 .io_in_queue = 1200,
528 .start_time = 0,
529 .end_time = 100,
530 .counter = 0,
531 .io_avg = 0
532 };
533
534 struct disk_stats stats_base;
535 memset(&stats_base, 0, sizeof(stats_base));
536
537 int loop_size = 100;
538 for (int i = 0; i < loop_size; ++i) {
539 stats_base = disk_stats_add(stats_base, norm_inc);
540 dsm_acc.update(&stats_base);
541 EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
542 EXPECT_FALSE(dsm_acc.mStall);
543 }
544
545 stats_base = disk_stats_add(stats_base, stall_inc);
546 dsm_acc.update(&stats_base);
547 EXPECT_TRUE(dsm_acc.mValid);
548 EXPECT_TRUE(dsm_acc.mStall);
549
550 for (int i = 0; i < 10; ++i) {
551 stats_base = disk_stats_add(stats_base, norm_inc);
552 dsm_acc.update(&stats_base);
553 EXPECT_TRUE(dsm_acc.mValid);
554 EXPECT_FALSE(dsm_acc.mStall);
555 }
556}
557
558static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
559 EXPECT_LE(stats1.read_ios, stats2.read_ios);
560 EXPECT_LE(stats1.read_merges, stats2.read_merges);
561 EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
562 EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
563
564 EXPECT_LE(stats1.write_ios, stats2.write_ios);
565 EXPECT_LE(stats1.write_merges, stats2.write_merges);
566 EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
567 EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
568
569 EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
570 EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
571}
572
573TEST(storaged_test, disk_stats_publisher) {
574 // asserting that there is one file for diskstats
575 ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
576 disk_stats_publisher dsp;
577 struct disk_stats prev;
578 memset(&prev, 0, sizeof(prev));
579
580 for (int i = 0; i < TEST_LOOPS; ++i) {
581 dsp.update();
582 expect_increasing(prev, dsp.mPrevious);
583 prev = dsp.mPrevious;
584 pause(10);
585 }
586}
587