1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "io_stats.h"
16 #include "securec.h"
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22
23 namespace {
24 const int NUM_ONEHUNDRED = 100;
25 const double NUM_ZERO_POINTZEROONE = 0.01;
26 #if __DEBUG__
27 const char* SYSTIME_PATH = "/data/local/tmp/systimes";
28 const char* CPU_PATH = "/data/local/tmp/cpustats";
29 const char* DISKSTATS_PATH = "/data/local/tmp/diskstats";
30 const int NUM_SEVEN = 7;
31 #else
32 const char* SYSTIME_PATH = "/proc/uptime";
33 const char* CPU_PATH = "/proc/stat";
34 const char* DISKSTATS_PATH = "/proc/diskstats";
35 #endif // #if __DEBUG__
36 const int DEC_BASE = 10;
37 } // namespace
38
IoStats(DiskioConfig::IoReportType type)39 IoStats::IoStats(DiskioConfig::IoReportType type) : type_(type)
40 {
41 sysTime_ = GetSystime();
42 }
43
GetSystime()44 uint64_t IoStats::GetSystime()
45 {
46 uint64_t systime = 1;
47 std::ifstream input(SYSTIME_PATH, std::ios::in);
48 CHECK_TRUE(!input.fail(), systime, "%s:open %s failed, errno = %d", __func__, SYSTIME_PATH, errno);
49 do {
50 if (!input.good()) {
51 return systime;
52 }
53 std::string line;
54 getline(input, line);
55 line += '\n';
56
57 uint64_t nsec = 0;
58 uint64_t ncent = 0;
59 if (ParseLineFields(line) > 0) {
60 nsec = fields_[0];
61 ncent = fields_[1];
62 systime = nsec * NUM_ONEHUNDRED + ncent;
63 }
64 fields_.clear();
65 } while (0);
66 input.close();
67
68 return systime;
69 }
70
GetIoData()71 bool IoStats::GetIoData()
72 {
73 ParseCpuStats();
74 if (type_ == DiskioConfig::IO_REPORT) {
75 ParseIoStats();
76 } else if (type_ == DiskioConfig::IO_REPORT_EX) {
77 ParseIoStatsEx();
78 }
79 return true;
80 }
81
ParseCpuStats()82 bool IoStats::ParseCpuStats()
83 {
84 std::ifstream input(CPU_PATH, std::ios::in);
85 CHECK_TRUE(!input.fail(), false, "%s: open %s failed, errno = %d", __func__, CPU_PATH, errno);
86 do {
87 if (!input.good()) {
88 return false;
89 }
90 std::string line;
91 getline(input, line);
92
93 auto pos = line.find("cpu");
94 if (pos != std::string::npos) {
95 line += '\n';
96 GetCpuStats(line);
97 }
98 } while (!input.eof());
99 input.close();
100
101 return true;
102 }
103
GetCpuStats(std::string & line)104 bool IoStats::GetCpuStats(std::string& line)
105 {
106 std::string name;
107 auto cpuData = std::make_shared<ProcStats>();
108 CHECK_NOTNULL(cpuData, false, "create ProcStats FAILED!");
109
110 if (ParseLineFields(line, name) > 0) {
111 int index = 0;
112 cpuData->name_ = name;
113 cpuData->user_ = fields_[index];
114 index++;
115 cpuData->nice_ = fields_[index];
116 index++;
117 cpuData->system_ = fields_[index];
118 index++;
119 cpuData->idle_ = fields_[index];
120 index++;
121 cpuData->iowait_ = fields_[index];
122 index++;
123 cpuData->steal_ = fields_[index];
124 index++;
125 cpuData->hardirq_ = fields_[index];
126 index++;
127 cpuData->softirq_ = fields_[index];
128 index++;
129 cpuData->guest_ = fields_[index];
130 index++;
131 cpuData->guestNice_ = fields_[index];
132 cpuDatas_.push_back(cpuData);
133 fields_.clear();
134 return true;
135 }
136
137 return false;
138 }
139
ParseIoStats()140 bool IoStats::ParseIoStats()
141 {
142 std::ifstream input(DISKSTATS_PATH, std::ios::in);
143 CHECK_TRUE(!input.fail(), false, "%s:%d open failed, errno = %d", __func__, __LINE__, errno);
144 do {
145 if (!input.good()) {
146 return false;
147 }
148 std::string line;
149 getline(input, line);
150 line += '\n';
151 GetIoStats(line);
152 } while (!input.eof());
153 input.close();
154
155 return true;
156 }
157
GetIoStats(std::string & line)158 bool IoStats::GetIoStats(std::string& line)
159 {
160 std::string name;
161 auto ioInfo = std::make_shared<DiskStats>();
162 CHECK_NOTNULL(ioInfo, false, "create DiskStats FAILED!");
163
164 if (ParseLineFields(line, name) > 0) {
165 int index = 0;
166 ioInfo->major_ = fields_[index];
167 index++;
168 ioInfo->minor_ = fields_[index];
169 index++;
170 ioInfo->deviceName_ = name;
171
172 ioInfo->rSucc_ = fields_[index];
173 index++;
174 ioInfo->rMerged_ = fields_[index];
175 index++;
176 ioInfo->rSectors_ = fields_[index];
177 index++;
178 ioInfo->timeOfRead_ = fields_[index];
179 index++;
180
181 ioInfo->wSucc_ = fields_[index];
182 index++;
183 ioInfo->wMerged_ = fields_[index];
184 index++;
185 ioInfo->wSectors_ = fields_[index];
186 index++;
187 ioInfo->timeOfWrite_ = fields_[index];
188 index++;
189
190 ioInfo->ios_ = fields_[index];
191 index++;
192 ioInfo->timeOfIo_ = fields_[index];
193 index++;
194 ioInfo->weighted_ = fields_[index];
195 index++;
196
197 ioInfo->dSucc_ = fields_[index];
198 index++;
199 ioInfo->dMerged_ = fields_[index];
200 index++;
201 ioInfo->dSectors_ = fields_[index];
202 index++;
203 ioInfo->timeOfd_ = fields_[index];
204 index++;
205
206 ioInfo->flushSucc_ = fields_[index];
207 index++;
208 ioInfo->timeOfFlush_ = fields_[index];
209
210 ioDatas_.push_back(ioInfo);
211 fields_.clear();
212 return true;
213 }
214 #if __DEBUG__
215 char debugName[128];
216 uint64_t rMergesOrIo = 0;
217 uint64_t rwIos = 0;
218 uint64_t rTicksOrw = 0;
219 auto debugIoInfo = std::make_shared<DiskStats>();
220 int ret = sscanf_s(line.c_str(), "%u %u %s %lu %lu %lu %" PRIu64 " %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
221 &debugIoInfo->major_, &debugIoInfo->minor_, debugName, sizeof(debugName),
222 &debugIoInfo->rSucc_, &rMergesOrIo, &rwIos, &rTicksOrw,
223 &debugIoInfo->wSucc_, &debugIoInfo->wMerged_,
224 &debugIoInfo->wSectors_, &debugIoInfo->timeOfWrite_,
225 &debugIoInfo->ios_, &debugIoInfo->timeOfIo_, &debugIoInfo->weighted_,
226 &debugIoInfo->dSucc_, &debugIoInfo->dMerged_,
227 &debugIoInfo->dSectors_, &debugIoInfo->timeOfd_,
228 &debugIoInfo->flushSucc_, &debugIoInfo->timeOfFlush_);
229 if (ret == NUM_SEVEN) {
230 debugIoInfo->rSectors_ = rMergesOrIo;
231 debugIoInfo->wSucc_ = rwIos;
232 debugIoInfo->wSectors_ = rTicksOrw;
233 } else {
234 debugIoInfo->rMerged_ = rMergesOrIo;
235 debugIoInfo->rSectors_ = rwIos;
236 debugIoInfo->timeOfRead_ = rTicksOrw;
237 }
238 debugIoInfo->deviceName_ = std::string(name);
239 ioDatas_.push_back(debugIoInfo);
240 #endif
241
242 return false;
243 }
244
ParseIoStatsEx()245 bool IoStats::ParseIoStatsEx()
246 {
247 return true;
248 }
249
KeepTowDigits(const uint64_t & data,uint64_t div)250 double IoStats::KeepTowDigits(const uint64_t& data, uint64_t div)
251 {
252 double result = 0.00;
253 if (data <= 0 || div == 0) {
254 return result;
255 }
256 double ddiv = div;
257 if (ddiv != NUM_TWO) {
258 ddiv = div * NUM_ZERO_POINTZEROONE;
259 }
260 result = static_cast<double>(data) / ddiv;
261 return result;
262 }
263
FindFirstNum(char ** p)264 bool IoStats::FindFirstNum(char** p)
265 {
266 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
267 while (**p > '9' || **p < '0') {
268 if (**p == '\0' || **p == '\n') {
269 return false;
270 }
271 (*p)++;
272 }
273 return true;
274 }
275
RemoveSpaces(char ** p)276 bool IoStats::RemoveSpaces(char** p)
277 {
278 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
279 if (**p == '\0' || **p == '\n') {
280 return false;
281 }
282 while (**p == ' ') {
283 (*p)++;
284 if (**p == '\0' || **p == '\n') {
285 return false;
286 }
287 }
288 return true;
289 }
290
ParseLineFields(const std::string & line,std::string & name)291 uint32_t IoStats::ParseLineFields(const std::string& line, std::string& name)
292 {
293 uint64_t num;
294 uint32_t count = 0;
295 char* end = nullptr;
296 char* pTmp = const_cast<char*>(line.c_str());
297
298 fields_.clear();
299 while (pTmp != nullptr && *pTmp != '\n') {
300 CHECK_TRUE(RemoveSpaces(&pTmp), count, "%s: RemoveSpaces failed!", __func__);
301 if (*pTmp >= 'a' && *pTmp <= 'z') {
302 char field[64];
303 int len = 0;
304 int ret = sscanf_s(pTmp, "%63s %n", field, sizeof(field), &len);
305 if (ret == 1 && *field) {
306 name = std::string(field, strlen(field));
307 pTmp += len;
308 }
309 }
310 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
311 num = strtoull(pTmp, &end, DEC_BASE);
312 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
313 fields_.push_back(num);
314 pTmp = end;
315 count++;
316 }
317 return count;
318 }
319
ParseLineFields(const std::string & line)320 uint32_t IoStats::ParseLineFields(const std::string& line)
321 {
322 uint64_t num;
323 uint32_t count = 0;
324 char* end = nullptr;
325 char* pTmp = const_cast<char*>(line.c_str());
326
327 while (pTmp != nullptr && *pTmp != '\n') {
328 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
329 num = static_cast<uint32_t>(strtoull(pTmp, &end, DEC_BASE));
330 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
331 fields_.push_back(num);
332 pTmp = end;
333 count++;
334 }
335 return count;
336 }