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 33static 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 57const char* DISK_STATS_PATH; 58TEST(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 83TEST(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 124static 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 132static 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 141TEST(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 184static 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 195static 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 213TEST(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 321static 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 337TEST(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