ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 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 | |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 19 | #include <dirent.h> |
ynwang | af49d97 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 20 | #include <fcntl.h> |
| 21 | #include <linux/time.h> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 22 | #include <stdint.h> |
ynwang | af49d97 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 23 | #include <stdio.h> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
ynwang | af49d97 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 26 | #include <sys/stat.h> |
| 27 | #include <time.h> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 28 | #include <unistd.h> |
| 29 | |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 30 | #include <iomanip> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 31 | #include <sstream> |
| 32 | #include <string> |
| 33 | #include <unordered_map> |
| 34 | |
| 35 | #include <android-base/file.h> |
ynwang | af49d97 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 36 | #include <android-base/logging.h> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 37 | #include <android-base/stringprintf.h> |
| 38 | #include <android-base/strings.h> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 39 | #include <log/log_event_list.h> |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 40 | |
| 41 | #include <storaged.h> |
| 42 | #include <storaged_utils.h> |
| 43 | |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 44 | bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) { |
| 45 | // Get time |
| 46 | struct timespec ts; |
| 47 | // Use monotonic to exclude suspend time so that we measure IO bytes/sec |
| 48 | // when system is running. |
| 49 | int ret = clock_gettime(CLOCK_MONOTONIC, &ts); |
| 50 | if (ret < 0) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 51 | PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed"; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 52 | return false; |
| 53 | } |
| 54 | |
| 55 | std::string buffer; |
| 56 | if (!android::base::ReadFileToString(disk_stats_path, &buffer)) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 57 | PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed."; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 58 | return false; |
| 59 | } |
| 60 | |
| 61 | // Regular diskstats entries |
| 62 | std::stringstream ss(buffer); |
| 63 | for (uint i = 0; i < DISK_STATS_SIZE; ++i) { |
| 64 | ss >> *((uint64_t*)stats + i); |
| 65 | } |
| 66 | // Other entries |
| 67 | stats->start_time = 0; |
| 68 | stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + |
| 69 | ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC); |
| 70 | stats->counter = 1; |
| 71 | stats->io_avg = (double)stats->io_in_flight; |
| 72 | return true; |
| 73 | } |
| 74 | |
| 75 | struct disk_perf get_disk_perf(struct disk_stats* stats) { |
| 76 | struct disk_perf perf; |
| 77 | memset(&perf, 0, sizeof(struct disk_perf)); // initialize |
| 78 | |
| 79 | if (stats->io_ticks) { |
| 80 | if (stats->read_ticks) { |
| 81 | unsigned long long divisor = stats->read_ticks * stats->io_ticks; |
| 82 | perf.read_perf = ((unsigned long long)SECTOR_SIZE * |
| 83 | stats->read_sectors * |
| 84 | stats->io_in_queue + |
| 85 | (divisor >> 1)) / |
| 86 | divisor; |
| 87 | perf.read_ios = ((unsigned long long)SEC_TO_MSEC * |
| 88 | stats->read_ios * |
| 89 | stats->io_in_queue + |
| 90 | (divisor >> 1)) / |
| 91 | divisor; |
| 92 | } |
| 93 | if (stats->write_ticks) { |
| 94 | unsigned long long divisor = stats->write_ticks * stats->io_ticks; |
| 95 | perf.write_perf = ((unsigned long long)SECTOR_SIZE * |
| 96 | stats->write_sectors * |
| 97 | stats->io_in_queue + |
| 98 | (divisor >> 1)) / |
| 99 | divisor; |
| 100 | perf.write_ios = ((unsigned long long)SEC_TO_MSEC * |
| 101 | stats->write_ios * |
| 102 | stats->io_in_queue + |
| 103 | (divisor >> 1)) / |
| 104 | divisor; |
| 105 | } |
| 106 | perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) / |
| 107 | stats->io_ticks; |
| 108 | } |
| 109 | return perf; |
| 110 | } |
| 111 | |
| 112 | struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) { |
| 113 | struct disk_stats inc; |
| 114 | for (uint i = 0; i < DISK_STATS_SIZE; ++i) { |
| 115 | if (i == DISK_STATS_IO_IN_FLIGHT_IDX) { |
| 116 | continue; |
| 117 | } |
| 118 | |
| 119 | *((uint64_t*)&inc + i) = |
| 120 | *((uint64_t*)curr + i) - *((uint64_t*)prev + i); |
| 121 | } |
| 122 | // io_in_flight is exception |
| 123 | inc.io_in_flight = curr->io_in_flight; |
| 124 | |
| 125 | inc.start_time = prev->end_time; |
| 126 | inc.end_time = curr->end_time; |
| 127 | inc.io_avg = curr->io_avg; |
| 128 | inc.counter = 1; |
| 129 | |
| 130 | return inc; |
| 131 | } |
| 132 | |
| 133 | // Add src to dst |
| 134 | void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 135 | if (dst->end_time != 0 && dst->end_time != src->start_time) { |
| 136 | LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats" |
| 137 | << " are added. dst end with " << dst->end_time |
| 138 | << ", src start with " << src->start_time; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | for (uint i = 0; i < DISK_STATS_SIZE; ++i) { |
| 142 | if (i == DISK_STATS_IO_IN_FLIGHT_IDX) { |
| 143 | continue; |
| 144 | } |
| 145 | |
| 146 | *((uint64_t*)dst + i) += *((uint64_t*)src + i); |
| 147 | } |
| 148 | |
| 149 | dst->io_in_flight = src->io_in_flight; |
| 150 | if (dst->counter + src->counter) { |
| 151 | dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) / |
| 152 | (dst->counter + src->counter); |
| 153 | } |
| 154 | dst->counter += src->counter; |
| 155 | dst->end_time = src->end_time; |
| 156 | if (dst->start_time == 0) { |
| 157 | dst->start_time = src->start_time; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) { |
| 162 | CHECK(ext_csd_fd >= 0); |
| 163 | struct hex { |
| 164 | char str[2]; |
| 165 | }; |
| 166 | // List of interesting offsets |
| 167 | static const size_t EXT_CSD_REV_IDX = 192 * sizeof(hex); |
| 168 | static const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(hex); |
| 169 | static const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(hex); |
| 170 | static const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(hex); |
| 171 | |
| 172 | // Read file |
| 173 | CHECK(lseek(ext_csd_fd, 0, SEEK_SET) == 0); |
| 174 | std::string buffer; |
| 175 | if (!android::base::ReadFdToString(ext_csd_fd, &buffer)) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 176 | PLOG_TO(SYSTEM, ERROR) << "ReadFdToString failed."; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 177 | return false; |
| 178 | } |
| 179 | |
| 180 | if (buffer.length() < EXT_CSD_FILE_MIN_SIZE) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 181 | LOG_TO(SYSTEM, ERROR) << "EMMC ext csd file has truncated content. " |
| 182 | << "File length: " << buffer.length(); |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 183 | return false; |
| 184 | } |
| 185 | |
| 186 | std::string sub; |
| 187 | std::stringstream ss; |
| 188 | // Parse EXT_CSD_REV |
| 189 | int ext_csd_rev = -1; |
| 190 | sub = buffer.substr(EXT_CSD_REV_IDX, sizeof(hex)); |
| 191 | ss << sub; |
| 192 | ss >> std::hex >> ext_csd_rev; |
| 193 | if (ext_csd_rev < 0) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 194 | LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_CSD_REV."; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 195 | return false; |
| 196 | } |
| 197 | ss.clear(); |
| 198 | |
| 199 | static const char* ver_str[] = { |
| 200 | "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" |
| 201 | }; |
| 202 | |
Jin Qian | b00d546 | 2017-01-17 15:08:00 -0800 | [diff] [blame] | 203 | strlcpy(info->mmc_ver, |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 204 | (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? |
| 205 | ver_str[ext_csd_rev] : |
| 206 | "Unknown", |
| 207 | MMC_VER_STR_LEN); |
| 208 | |
| 209 | if (ext_csd_rev < 7) { |
| 210 | return 0; |
| 211 | } |
| 212 | |
| 213 | // Parse EXT_PRE_EOL_INFO |
| 214 | info->eol = -1; |
| 215 | sub = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(hex)); |
| 216 | ss << sub; |
| 217 | ss >> std::hex >> info->eol; |
| 218 | if (info->eol < 0) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 219 | LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_PRE_EOL_INFO."; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 220 | return false; |
| 221 | } |
| 222 | ss.clear(); |
| 223 | |
| 224 | // Parse DEVICE_LIFE_TIME_EST |
| 225 | info->lifetime_a = -1; |
| 226 | sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(hex)); |
| 227 | ss << sub; |
| 228 | ss >> std::hex >> info->lifetime_a; |
| 229 | if (info->lifetime_a < 0) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 230 | LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A."; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 231 | return false; |
| 232 | } |
| 233 | ss.clear(); |
| 234 | |
| 235 | info->lifetime_b = -1; |
| 236 | sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(hex)); |
| 237 | ss << sub; |
| 238 | ss >> std::hex >> info->lifetime_b; |
| 239 | if (info->lifetime_b < 0) { |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 240 | LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B."; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 241 | return false; |
| 242 | } |
| 243 | ss.clear(); |
| 244 | |
| 245 | return true; |
| 246 | } |
| 247 | |
Jin Qian | bcd6e3b | 2016-12-28 15:43:51 -0800 | [diff] [blame] | 248 | static bool cmp_uid_info(struct uid_info l, struct uid_info r) { |
| 249 | // Compare background I/O first. |
Jin Qian | 5b962c6 | 2017-01-30 14:48:38 -0800 | [diff] [blame] | 250 | for (int i = UID_STATS - 1; i >= 0; i--) { |
Jin Qian | bcd6e3b | 2016-12-28 15:43:51 -0800 | [diff] [blame] | 251 | uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes; |
| 252 | uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes; |
| 253 | uint64_t l_chars = l.io[i].rchar + l.io[i].wchar; |
| 254 | uint64_t r_chars = r.io[i].rchar + r.io[i].wchar; |
| 255 | |
| 256 | if (l_bytes != r_bytes) { |
| 257 | return l_bytes > r_bytes; |
| 258 | } |
| 259 | if (l_chars != r_chars) { |
| 260 | return l_chars > r_chars; |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | return l.name < r.name; |
| 265 | } |
| 266 | |
| 267 | void sort_running_uids_info(std::vector<struct uid_info> &uids) { |
| 268 | std::sort(uids.begin(), uids.end(), cmp_uid_info); |
| 269 | } |
| 270 | |
| 271 | // Logging functions |
| 272 | void log_console_running_uids_info(std::vector<struct uid_info> uids) { |
Jin Qian | 4bcc8b6 | 2017-03-14 12:40:06 -0700 | [diff] [blame^] | 273 | printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes " |
| 274 | "bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n"); |
Jin Qian | bcd6e3b | 2016-12-28 15:43:51 -0800 | [diff] [blame] | 275 | |
| 276 | for (const auto& uid : uids) { |
Jin Qian | 4bcc8b6 | 2017-03-14 12:40:06 -0700 | [diff] [blame^] | 277 | printf("%s %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\n", uid.name.c_str(), |
Jin Qian | bcd6e3b | 2016-12-28 15:43:51 -0800 | [diff] [blame] | 278 | uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes, |
Jin Qian | e83a610 | 2017-03-02 16:16:54 -0800 | [diff] [blame] | 279 | uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes, |
| 280 | uid.io[0].fsync, uid.io[1].fsync); |
Jin Qian | bcd6e3b | 2016-12-28 15:43:51 -0800 | [diff] [blame] | 281 | } |
| 282 | fflush(stdout); |
| 283 | } |
| 284 | |
Jin Qian | 27506fa | 2017-01-17 11:51:51 -0800 | [diff] [blame] | 285 | #if DEBUG |
| 286 | void log_debug_disk_perf(struct disk_perf* perf, const char* type) { |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 287 | // skip if the input structure are all zeros |
| 288 | if (perf == NULL) return; |
| 289 | struct disk_perf zero_cmp; |
| 290 | memset(&zero_cmp, 0, sizeof(zero_cmp)); |
| 291 | if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return; |
| 292 | |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 293 | LOG_TO(SYSTEM, INFO) << "perf(ios) " << type |
| 294 | << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)" |
| 295 | << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)" |
| 296 | << " q:" << perf->queue; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 297 | } |
Jin Qian | 27506fa | 2017-01-17 11:51:51 -0800 | [diff] [blame] | 298 | #else |
| 299 | void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {} |
| 300 | #endif |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 301 | |
| 302 | void log_event_disk_stats(struct disk_stats* stats, const char* type) { |
| 303 | // skip if the input structure are all zeros |
| 304 | if (stats == NULL) return; |
| 305 | struct disk_stats zero_cmp; |
| 306 | memset(&zero_cmp, 0, sizeof(zero_cmp)); |
| 307 | // skip event logging diskstats when it is zero increment (all first 11 entries are zero) |
| 308 | if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return; |
| 309 | |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 310 | android_log_event_list(EVENTLOGTAG_DISKSTATS) |
| 311 | << type << stats->start_time << stats->end_time |
| 312 | << stats->read_ios << stats->read_merges |
| 313 | << stats->read_sectors << stats->read_ticks |
| 314 | << stats->write_ios << stats->write_merges |
| 315 | << stats->write_sectors << stats->write_ticks |
| 316 | << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue |
| 317 | << LOG_ID_EVENTS; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 318 | } |
| 319 | |
| 320 | void log_event_emmc_info(struct emmc_info* info) { |
| 321 | // skip if the input structure are all zeros |
| 322 | if (info == NULL) return; |
| 323 | struct emmc_info zero_cmp; |
| 324 | memset(&zero_cmp, 0, sizeof(zero_cmp)); |
| 325 | if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return; |
| 326 | |
Jin Qian | 535ddbe | 2017-01-11 17:19:21 -0800 | [diff] [blame] | 327 | android_log_event_list(EVENTLOGTAG_EMMCINFO) |
| 328 | << info->mmc_ver << info->eol << info->lifetime_a << info->lifetime_b |
| 329 | << LOG_ID_EVENTS; |
ynwang | 62cb372 | 2016-06-17 14:30:48 -0700 | [diff] [blame] | 330 | } |