storaged_info.cpp revision b90f1ae1e5d9886dc32b2f653ab2899a0416bd71
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 <stdio.h>
20#include <string.h>
21
22#include <android-base/file.h>
23#include <android-base/parseint.h>
24#include <android-base/logging.h>
25#include <android-base/strings.h>
26#include <log/log_event_list.h>
27
28#include "storaged.h"
29
30using namespace std;
31using namespace android::base;
32
33void report_storage_health()
34{
35    emmc_info_t mmc;
36    ufs_info_t ufs;
37
38    mmc.report();
39    ufs.report();
40}
41
42void storage_info_t::publish()
43{
44    android_log_event_list(EVENTLOGTAG_EMMCINFO)
45        << version << eol << lifetime_a << lifetime_b
46        << LOG_ID_EVENTS;
47}
48
49bool emmc_info_t::report()
50{
51    if (!report_sysfs() && !report_debugfs())
52        return false;
53
54    publish();
55    return true;
56}
57
58bool emmc_info_t::report_sysfs()
59{
60    string buffer;
61    uint16_t rev = 0;
62
63    if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
64        return false;
65    }
66
67    if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
68        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
69        return false;
70    }
71
72    version = "emmc ";
73    version += emmc_ver_str[rev];
74
75    if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
76        return false;
77    }
78
79    if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
80        return false;
81    }
82
83    if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
84        return false;
85    }
86
87    if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
88        (lifetime_a == 0 && lifetime_b == 0)) {
89        return false;
90    }
91
92    return true;
93}
94
95const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
96/* 2 characters in string for each byte */
97const size_t EXT_CSD_REV_IDX = 192 * 2;
98const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
99const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
100const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
101
102bool emmc_info_t::report_debugfs()
103{
104    string buffer;
105    uint16_t rev = 0;
106
107    if (!ReadFileToString(emmc_debugfs, &buffer) ||
108        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
109        return false;
110    }
111
112    string str = buffer.substr(EXT_CSD_REV_IDX, 2);
113    if (!ParseUint(str, &rev) ||
114        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
115        return false;
116    }
117
118    version = "emmc ";
119    version += emmc_ver_str[rev];
120
121    str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
122    if (!ParseUint(str, &eol)) {
123        return false;
124    }
125
126    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
127    if (!ParseUint(str, &lifetime_a)) {
128        return false;
129    }
130
131    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
132    if (!ParseUint(str, &lifetime_b)) {
133        return false;
134    }
135
136    return true;
137}
138
139bool ufs_info_t::report()
140{
141    string buffer;
142    if (!ReadFileToString(health_file, &buffer)) {
143        return false;
144    }
145
146    vector<string> lines = Split(buffer, "\n");
147    if (lines.empty()) {
148        return false;
149    }
150
151    char rev[8];
152    if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
153        return false;
154    }
155
156    version = "ufs " + string(rev);
157
158    for (size_t i = 1; i < lines.size(); i++) {
159        char token[32];
160        uint16_t val;
161        int ret;
162        if ((ret = sscanf(lines[i].c_str(),
163                   "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
164                   token, &val)) < 2) {
165            continue;
166        }
167
168        if (string(token) == "bPreEOLInfo") {
169            eol = val;
170        } else if (string(token) == "bDeviceLifeTimeEstA") {
171            lifetime_a = val;
172        } else if (string(token) == "bDeviceLifeTimeEstB") {
173            lifetime_b = val;
174        }
175    }
176
177    if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
178        return false;
179    }
180
181    publish();
182    return true;
183}
184
185