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, ncent;
58 if (ParseLineFields(line) > 0) {
59 nsec = fields_[0];
60 ncent = fields_[1];
61 systime = nsec * NUM_ONEHUNDRED + ncent;
62 }
63 fields_.clear();
64 } while (0);
65 input.close();
66
67 return systime;
68 }
69
GetIoData()70 bool IoStats::GetIoData()
71 {
72 ParseCpuStats();
73 if (type_ == DiskioConfig::IO_REPORT) {
74 ParseIoStats();
75 } else if (type_ == DiskioConfig::IO_REPORT_EX) {
76 ParseIoStatsEx();
77 }
78 return true;
79 }
80
ParseCpuStats()81 bool IoStats::ParseCpuStats()
82 {
83 std::ifstream input(CPU_PATH, std::ios::in);
84 CHECK_TRUE(!input.fail(), false, "%s: open %s failed, errno = %d", __func__, CPU_PATH, errno);
85 do {
86 if (!input.good()) {
87 return false;
88 }
89 std::string line;
90 getline(input, line);
91
92 auto pos = line.find("cpu");
93 if (pos != std::string::npos) {
94 line += '\n';
95 GetCpuStats(line);
96 }
97 } while (!input.eof());
98 input.close();
99
100 return true;
101 }
102
GetCpuStats(std::string & line)103 bool IoStats::GetCpuStats(std::string& line)
104 {
105 std::string name;
106 auto cpuData = std::make_shared<ProcStats>();
107 CHECK_NOTNULL(cpuData, false, "create ProcStats FAILED!");
108
109 if (ParseLineFields(line, name) > 0) {
110 int index = 0;
111 cpuData->name_ = name;
112 cpuData->user_ = fields_[index];
113 index++;
114 cpuData->nice_ = fields_[index];
115 index++;
116 cpuData->system_ = fields_[index];
117 index++;
118 cpuData->idle_ = fields_[index];
119 index++;
120 cpuData->iowait_ = fields_[index];
121 index++;
122 cpuData->steal_ = fields_[index];
123 index++;
124 cpuData->hardirq_ = fields_[index];
125 index++;
126 cpuData->softirq_ = fields_[index];
127 index++;
128 cpuData->guest_ = fields_[index];
129 index++;
130 cpuData->guestNice_ = fields_[index];
131 cpuDatas_.push_back(cpuData);
132 fields_.clear();
133 return true;
134 }
135
136 return false;
137 }
138
ParseIoStats()139 bool IoStats::ParseIoStats()
140 {
141 std::ifstream input(DISKSTATS_PATH, std::ios::in);
142 CHECK_TRUE(!input.fail(), false, "%s:%d open failed, errno = %d", __func__, __LINE__, errno);
143 do {
144 if (!input.good()) {
145 return false;
146 }
147 std::string line;
148 getline(input, line);
149 line += '\n';
150 GetIoStats(line);
151 } while (!input.eof());
152 input.close();
153
154 return true;
155 }
156
GetIoStats(std::string & line)157 bool IoStats::GetIoStats(std::string& line)
158 {
159 std::string name;
160 auto ioInfo = std::make_shared<DiskStats>();
161 CHECK_NOTNULL(ioInfo, false, "create DiskStats FAILED!");
162
163 if (ParseLineFields(line, name) > 0) {
164 int index = 0;
165 ioInfo->major_ = fields_[index];
166 index++;
167 ioInfo->minor_ = fields_[index];
168 index++;
169 ioInfo->deviceName_ = name;
170
171 ioInfo->rSucc_ = fields_[index];
172 index++;
173 ioInfo->rMerged_ = fields_[index];
174 index++;
175 ioInfo->rSectors_ = fields_[index];
176 index++;
177 ioInfo->timeOfRead_ = fields_[index];
178 index++;
179
180 ioInfo->wSucc_ = fields_[index];
181 index++;
182 ioInfo->wMerged_ = fields_[index];
183 index++;
184 ioInfo->wSectors_ = fields_[index];
185 index++;
186 ioInfo->timeOfWrite_ = fields_[index];
187 index++;
188
189 ioInfo->ios_ = fields_[index];
190 index++;
191 ioInfo->timeOfIo_ = fields_[index];
192 index++;
193 ioInfo->weighted_ = fields_[index];
194 index++;
195
196 ioInfo->dSucc_ = fields_[index];
197 index++;
198 ioInfo->dMerged_ = fields_[index];
199 index++;
200 ioInfo->dSectors_ = fields_[index];
201 index++;
202 ioInfo->timeOfd_ = fields_[index];
203 index++;
204
205 ioInfo->flushSucc_ = fields_[index];
206 index++;
207 ioInfo->timeOfFlush_ = fields_[index];
208
209 ioDatas_.push_back(ioInfo);
210 fields_.clear();
211 return true;
212 }
213 #if __DEBUG__
214 char debugName[128];
215 uint64_t rMergesOrIo = 0;
216 uint64_t rwIos = 0;
217 uint64_t rTicksOrw = 0;
218 auto debugIoInfo = std::make_shared<DiskStats>();
219 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",
220 &debugIoInfo->major_, &debugIoInfo->minor_, debugName, sizeof(debugName),
221 &debugIoInfo->rSucc_, &rMergesOrIo, &rwIos, &rTicksOrw,
222 &debugIoInfo->wSucc_, &debugIoInfo->wMerged_,
223 &debugIoInfo->wSectors_, &debugIoInfo->timeOfWrite_,
224 &debugIoInfo->ios_, &debugIoInfo->timeOfIo_, &debugIoInfo->weighted_,
225 &debugIoInfo->dSucc_, &debugIoInfo->dMerged_,
226 &debugIoInfo->dSectors_, &debugIoInfo->timeOfd_,
227 &debugIoInfo->flushSucc_, &debugIoInfo->timeOfFlush_);
228 if (ret == NUM_SEVEN) {
229 debugIoInfo->rSectors_ = rMergesOrIo;
230 debugIoInfo->wSucc_ = rwIos;
231 debugIoInfo->wSectors_ = rTicksOrw;
232 } else {
233 debugIoInfo->rMerged_ = rMergesOrIo;
234 debugIoInfo->rSectors_ = rwIos;
235 debugIoInfo->timeOfRead_ = rTicksOrw;
236 }
237 debugIoInfo->deviceName_ = std::string(name);
238 ioDatas_.push_back(debugIoInfo);
239 #endif
240
241 return false;
242 }
243
ParseIoStatsEx()244 bool IoStats::ParseIoStatsEx()
245 {
246 return true;
247 }
248
KeepTowDigits(const uint64_t & data,uint64_t div)249 double IoStats::KeepTowDigits(const uint64_t& data, uint64_t div)
250 {
251 double result = 0.00;
252 if (data <= 0 || div == 0) {
253 return result;
254 }
255 double ddiv = div;
256 if (ddiv != NUM_TWO) {
257 ddiv = div * NUM_ZERO_POINTZEROONE;
258 }
259 result = static_cast<double>(data) / ddiv;
260 return result;
261 }
262
FindFirstNum(char ** p)263 bool IoStats::FindFirstNum(char** p)
264 {
265 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
266 while (**p > '9' || **p < '0') {
267 if (**p == '\0' || **p == '\n') {
268 return false;
269 }
270 (*p)++;
271 }
272 return true;
273 }
274
RemoveSpaces(char ** p)275 bool IoStats::RemoveSpaces(char** p)
276 {
277 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
278 if (**p == '\0' || **p == '\n') {
279 return false;
280 }
281 while (**p == ' ') {
282 (*p)++;
283 if (**p == '\0' || **p == '\n') {
284 return false;
285 }
286 }
287 return true;
288 }
289
ParseLineFields(const std::string & line,std::string & name)290 uint32_t IoStats::ParseLineFields(const std::string& line, std::string& name)
291 {
292 uint64_t num;
293 uint32_t count = 0;
294 char* end = nullptr;
295 char* pTmp = const_cast<char*>(line.c_str());
296
297 fields_.clear();
298 while (pTmp != nullptr && *pTmp != '\n') {
299 CHECK_TRUE(RemoveSpaces(&pTmp), count, "%s: RemoveSpaces failed!", __func__);
300 if (*pTmp >= 'a' && *pTmp <= 'z') {
301 char field[64];
302 int len = 0;
303 int ret = sscanf_s(pTmp, "%63s %n", field, sizeof(field), &len);
304 if (ret == 1 && *field) {
305 name = std::string(field, strlen(field));
306 pTmp += len;
307 }
308 }
309 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
310 num = strtoull(pTmp, &end, DEC_BASE);
311 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
312 fields_.push_back(num);
313 pTmp = end;
314 count++;
315 }
316 return count;
317 }
318
ParseLineFields(const std::string & line)319 uint32_t IoStats::ParseLineFields(const std::string& line)
320 {
321 uint64_t num;
322 uint32_t count = 0;
323 char* end = nullptr;
324 char* pTmp = const_cast<char*>(line.c_str());
325
326 while (pTmp != nullptr && *pTmp != '\n') {
327 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
328 num = static_cast<uint32_t>(strtoull(pTmp, &end, DEC_BASE));
329 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
330 fields_.push_back(num);
331 pTmp = end;
332 count++;
333 }
334 return count;
335 }