• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 #include <storaged.h>               // data structures
28 #include <storaged_utils.h>         // functions to test
29 
30 #define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
31 #define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
32 
pause(uint32_t sec)33 static void pause(uint32_t sec) {
34     const char* path = "/cache/test";
35     int fd = open(path, O_WRONLY | O_CREAT, 0600);
36     ASSERT_LT(-1, fd);
37     char buffer[2048];
38     memset(buffer, 1, sizeof(buffer));
39     int loop_size = 100;
40     for (int i = 0; i < loop_size; ++i) {
41         ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));
42     }
43     fsync(fd);
44     close(fd);
45 
46     fd = open(path, O_RDONLY);
47     ASSERT_LT(-1, fd);
48     for (int i = 0; i < loop_size; ++i) {
49         ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));
50     }
51     close(fd);
52 
53     sleep(sec);
54 }
55 
56 // the return values of the tested functions should be the expected ones
57 const char* DISK_STATS_PATH;
TEST(storaged_test,retvals)58 TEST(storaged_test, retvals) {
59     struct disk_stats stats;
60     memset(&stats, 0, sizeof(struct disk_stats));
61 
62     if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
63         DISK_STATS_PATH = MMC_DISK_STATS_PATH;
64     } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
65         DISK_STATS_PATH = SDA_DISK_STATS_PATH;
66     } else {
67         return;
68     }
69 
70     EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
71 
72     struct disk_stats old_stats;
73     memset(&old_stats, 0, sizeof(struct disk_stats));
74     old_stats = stats;
75 
76     const char wrong_path[] = "/this/is/wrong";
77     EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
78 
79     // reading a wrong path should not damage the output structure
80     EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
81 }
82 
TEST(storaged_test,disk_stats)83 TEST(storaged_test, disk_stats) {
84     struct disk_stats stats;
85     memset(&stats, 0, sizeof(struct disk_stats));
86 
87     ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
88 
89     // every entry of stats (except io_in_flight) should all be greater than 0
90     for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
91         if (i == 8) continue; // skip io_in_flight which can be 0
92         EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));
93     }
94 
95     // accumulation of the increments should be the same with the overall increment
96     struct disk_stats base, tmp, curr, acc, inc[5];
97     memset(&base, 0, sizeof(struct disk_stats));
98     memset(&tmp, 0, sizeof(struct disk_stats));
99     memset(&acc, 0, sizeof(struct disk_stats));
100 
101     for (uint i = 0; i < 5; ++i) {
102         ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
103         if (i == 0) {
104             base = curr;
105             tmp = curr;
106             sleep(5);
107             continue;
108         }
109         inc[i] = get_inc_disk_stats(&tmp, &curr);
110         add_disk_stats(&inc[i], &acc);
111         tmp = curr;
112         pause(5);
113     }
114     struct disk_stats overall_inc;
115     memset(&overall_inc, 0, sizeof(disk_stats));
116     overall_inc= get_inc_disk_stats(&base, &curr);
117 
118     for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
119         if (i == 8) continue; // skip io_in_flight which can be 0
120         EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
121     }
122 }
123 
mean(std::deque<uint32_t> nums)124 static double mean(std::deque<uint32_t> nums) {
125     double sum = 0.0;
126     for (uint32_t i : nums) {
127     sum += i;
128     }
129     return sum / nums.size();
130 }
131 
standard_deviation(std::deque<uint32_t> nums)132 static double standard_deviation(std::deque<uint32_t> nums) {
133     double sum = 0.0;
134     double avg = mean(nums);
135     for (uint32_t i : nums) {
136     sum += ((double)i - avg) * ((double)i - avg);
137     }
138     return sqrt(sum / nums.size());
139 }
140 
TEST(storaged_test,stream_stats)141 TEST(storaged_test, stream_stats) {
142     // 100 random numbers
143     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};
144     std::deque<uint32_t> test_data;
145     stream_stats sstats;
146     for (uint32_t i : data) {
147         test_data.push_back(i);
148         sstats.add(i);
149 
150         EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
151         EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
152     }
153 
154     for (uint32_t i : data) {
155         test_data.pop_front();
156         sstats.evict(i);
157 
158         EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
159         EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
160     }
161 
162     // some real data
163     std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};
164     test_data.clear();
165     uint32_t window_size = 2;
166     uint32_t idx;
167     stream_stats sstats1;
168     for (idx = 0; idx < window_size; ++idx) {
169         test_data.push_back(another_data[idx]);
170         sstats1.add(another_data[idx]);
171     }
172     EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
173     EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
174     for (;idx < another_data.size(); ++idx) {
175         test_data.pop_front();
176         sstats1.evict(another_data[idx - window_size]);
177         test_data.push_back(another_data[idx]);
178         sstats1.add(another_data[idx]);
179         EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
180         EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
181     }
182 }
183 
disk_perf_multiply(struct disk_perf perf,double mul)184 static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
185     struct disk_perf retval;
186     retval.read_perf = (double)perf.read_perf * mul;
187     retval.read_ios = (double)perf.read_ios * mul;
188     retval.write_perf = (double)perf.write_perf * mul;
189     retval.write_ios = (double)perf.write_ios * mul;
190     retval.queue = (double)perf.queue * mul;
191 
192     return retval;
193 }
194 
disk_stats_add(struct disk_stats stats1,struct disk_stats stats2)195 static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
196     struct disk_stats retval;
197     retval.read_ios = stats1.read_ios + stats2.read_ios;
198     retval.read_merges = stats1.read_merges + stats2.read_merges;
199     retval.read_sectors = stats1.read_sectors + stats2.read_sectors;
200     retval.read_ticks = stats1.read_ticks + stats2.read_ticks;
201     retval.write_ios = stats1.write_ios + stats2.write_ios;
202     retval.write_merges = stats1.write_merges + stats2.write_merges;
203     retval.write_sectors = stats1.write_sectors + stats2.write_sectors;
204     retval.write_ticks = stats1.write_ticks + stats2.write_ticks;
205     retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;
206     retval.io_ticks = stats1.io_ticks + stats2.io_ticks;
207     retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;
208     retval.end_time = stats1.end_time + stats2.end_time;
209 
210     return retval;
211 }
212 
TEST(storaged_test,disk_stats_monitor)213 TEST(storaged_test, disk_stats_monitor) {
214     // asserting that there is one file for diskstats
215     ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
216     // testing if detect() will return the right value
217     disk_stats_monitor dsm_detect;
218     // feed monitor with constant perf data for io perf baseline
219     // using constant perf is reasonable since the functionality of stream_stats
220     // has already been tested
221     struct disk_perf norm_perf = {
222         .read_perf = 10 * 1024,
223         .read_ios = 50,
224         .write_perf = 5 * 1024,
225         .write_ios = 25,
226         .queue = 5
227     };
228 
229     std::random_device rd;
230     std::mt19937 gen(rd());
231     std::uniform_real_distribution<> rand(0.8, 1.2);
232 
233     for (uint i = 0; i < dsm_detect.mWindow; ++i) {
234         struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));
235 
236         dsm_detect.add(&perf);
237         dsm_detect.mBuffer.push(perf);
238         EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);
239     }
240 
241     dsm_detect.mValid = true;
242     dsm_detect.update_mean();
243     dsm_detect.update_std();
244 
245     for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
246         struct disk_perf test_perf;
247         struct disk_perf test_mean = dsm_detect.mMean;
248         struct disk_perf test_std = dsm_detect.mStd;
249 
250         test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;
251         test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;
252         test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;
253         test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;
254         test_perf.queue = (double)test_mean.queue + i * test_std.queue;
255 
256         EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));
257     }
258 
259     // testing if stalled disk_stats can be correctly accumulated in the monitor
260     disk_stats_monitor dsm_acc;
261     struct disk_stats norm_inc = {
262         .read_ios = 200,
263         .read_merges = 0,
264         .read_sectors = 200,
265         .read_ticks = 200,
266         .write_ios = 100,
267         .write_merges = 0,
268         .write_sectors = 100,
269         .write_ticks = 100,
270         .io_in_flight = 0,
271         .io_ticks = 600,
272         .io_in_queue = 300,
273         .start_time = 0,
274         .end_time = 100,
275         .counter = 0,
276         .io_avg = 0
277     };
278 
279     struct disk_stats stall_inc = {
280         .read_ios = 200,
281         .read_merges = 0,
282         .read_sectors = 20,
283         .read_ticks = 200,
284         .write_ios = 100,
285         .write_merges = 0,
286         .write_sectors = 10,
287         .write_ticks = 100,
288         .io_in_flight = 0,
289         .io_ticks = 600,
290         .io_in_queue = 1200,
291         .start_time = 0,
292         .end_time = 100,
293         .counter = 0,
294         .io_avg = 0
295     };
296 
297     struct disk_stats stats_base;
298     memset(&stats_base, 0, sizeof(stats_base));
299 
300     int loop_size = 100;
301     for (int i = 0; i < loop_size; ++i) {
302         stats_base = disk_stats_add(stats_base, norm_inc);
303         dsm_acc.update(&stats_base);
304         EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
305         EXPECT_FALSE(dsm_acc.mStall);
306     }
307 
308     stats_base = disk_stats_add(stats_base, stall_inc);
309     dsm_acc.update(&stats_base);
310     EXPECT_TRUE(dsm_acc.mValid);
311     EXPECT_TRUE(dsm_acc.mStall);
312 
313     for (int i = 0; i < 10; ++i) {
314         stats_base = disk_stats_add(stats_base, norm_inc);
315         dsm_acc.update(&stats_base);
316         EXPECT_TRUE(dsm_acc.mValid);
317         EXPECT_FALSE(dsm_acc.mStall);
318     }
319 }
320 
expect_increasing(struct disk_stats stats1,struct disk_stats stats2)321 static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
322     EXPECT_LE(stats1.read_ios, stats2.read_ios);
323     EXPECT_LE(stats1.read_merges, stats2.read_merges);
324     EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
325     EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
326 
327     EXPECT_LE(stats1.write_ios, stats2.write_ios);
328     EXPECT_LE(stats1.write_merges, stats2.write_merges);
329     EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
330     EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
331 
332     EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
333     EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
334 }
335 
336 #define TEST_LOOPS 20
TEST(storaged_test,disk_stats_publisher)337 TEST(storaged_test, disk_stats_publisher) {
338     // asserting that there is one file for diskstats
339     ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
340     disk_stats_publisher dsp;
341     struct disk_stats prev;
342     memset(&prev, 0, sizeof(prev));
343 
344     for (int i = 0; i < TEST_LOOPS; ++i) {
345         dsp.update();
346         expect_increasing(prev, dsp.mPrevious);
347         prev = dsp.mPrevious;
348         pause(10);
349     }
350 }
351 
352