1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
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_TWO = 2;
25 const int NUM_ONEHUNDRED = 100;
26 const double NUM_ZERO_POINTZEROONE = 0.01;
27 #if __DEBUG__
28 const char* SYSTIME_PATH = "/data/local/tmp/systimes";
29 const char* CPU_PATH = "/data/local/tmp/cpustats";
30 const char* DISKSTATS_PATH = "/data/local/tmp/diskstats";
31 const int NUM_SEVEN = 7;
32 #else
33 const char* SYSTIME_PATH = "/proc/uptime";
34 const char* CPU_PATH = "/proc/stat";
35 const char* DISKSTATS_PATH = "/proc/diskstats";
36 #endif // #if __DEBUG__
37 const int DEC_BASE = 10;
38 } // namespace
39
IoStats(DiskioConfig::IoReportType type)40 IoStats::IoStats(DiskioConfig::IoReportType type) : type_(type)
41 {
42 sysTime_ = GetSystime();
43 }
44
GetSystime()45 uint64_t IoStats::GetSystime()
46 {
47 uint64_t systime = 1;
48 std::ifstream input(SYSTIME_PATH, std::ios::in);
49 if (input.fail()) {
50 HILOG_ERROR(LOG_CORE, "%s:open %s failed, errno = %d", __func__, SYSTIME_PATH, errno);
51 return systime;
52 }
53 do {
54 if (!input.good()) {
55 return systime;
56 }
57 std::string line;
58 getline(input, line);
59 line += '\n';
60
61 uint64_t nsec = 0;
62 uint64_t ncent = 0;
63 if (ParseLineFields(line) > 0) {
64 nsec = fields_[0];
65 ncent = fields_[1];
66 systime = nsec * NUM_ONEHUNDRED + ncent;
67 }
68 fields_.clear();
69 } while (0);
70 input.close();
71
72 return systime;
73 }
74
GetIoData()75 bool IoStats::GetIoData()
76 {
77 ParseCpuStats();
78 if (type_ == DiskioConfig::IO_REPORT) {
79 ParseIoStats();
80 } else if (type_ == DiskioConfig::IO_REPORT_EX) {
81 ParseIoStatsEx();
82 }
83 return true;
84 }
85
ParseCpuStats()86 bool IoStats::ParseCpuStats()
87 {
88 std::ifstream input(CPU_PATH, std::ios::in);
89 if (input.fail()) {
90 HILOG_ERROR(LOG_CORE, "%s: open %s failed, errno = %d", __func__, CPU_PATH, errno);
91 return false;
92 }
93 do {
94 if (!input.good()) {
95 return false;
96 }
97 std::string line;
98 getline(input, line);
99
100 auto pos = line.find("cpu");
101 if (pos != std::string::npos) {
102 line += '\n';
103 GetCpuStats(line);
104 }
105 } while (!input.eof());
106 input.close();
107
108 return true;
109 }
110
GetCpuStats(std::string & line)111 bool IoStats::GetCpuStats(std::string& line)
112 {
113 std::string name;
114 auto cpuData = std::make_shared<ProcStats>();
115 CHECK_NOTNULL(cpuData, false, "create ProcStats FAILED!");
116
117 if (ParseLineFields(line, name) > 0) {
118 int index = 0;
119 cpuData->name_ = name;
120 cpuData->user_ = fields_[index];
121 index++;
122 cpuData->nice_ = fields_[index];
123 index++;
124 cpuData->system_ = fields_[index];
125 index++;
126 cpuData->idle_ = fields_[index];
127 index++;
128 cpuData->iowait_ = fields_[index];
129 index++;
130 cpuData->steal_ = fields_[index];
131 index++;
132 cpuData->hardirq_ = fields_[index];
133 index++;
134 cpuData->softirq_ = fields_[index];
135 index++;
136 cpuData->guest_ = fields_[index];
137 index++;
138 cpuData->guestNice_ = fields_[index];
139 cpuDatas_.push_back(cpuData);
140 fields_.clear();
141 return true;
142 }
143
144 return false;
145 }
146
ParseIoStats()147 bool IoStats::ParseIoStats()
148 {
149 std::ifstream input(DISKSTATS_PATH, std::ios::in);
150 if (input.fail()) {
151 HILOG_ERROR(LOG_CORE, "%s:%d open failed, errno = %d", __func__, __LINE__, errno);
152 return false;
153 }
154 do {
155 if (!input.good()) {
156 return false;
157 }
158 std::string line;
159 getline(input, line);
160 line += '\n';
161 GetIoStats(line);
162 } while (!input.eof());
163 input.close();
164
165 return true;
166 }
167
GetIoStats(std::string & line)168 bool IoStats::GetIoStats(std::string& line)
169 {
170 std::string name;
171 auto ioInfo = std::make_shared<DiskStats>();
172 CHECK_NOTNULL(ioInfo, false, "create DiskStats FAILED!");
173
174 if (ParseLineFields(line, name) > 0) {
175 int index = 0;
176 ioInfo->major_ = fields_[index];
177 index++;
178 ioInfo->minor_ = fields_[index];
179 index++;
180 ioInfo->deviceName_ = name;
181
182 ioInfo->rSucc_ = fields_[index];
183 index++;
184 ioInfo->rMerged_ = fields_[index];
185 index++;
186 ioInfo->rSectors_ = fields_[index];
187 index++;
188 ioInfo->timeOfRead_ = fields_[index];
189 index++;
190
191 ioInfo->wSucc_ = fields_[index];
192 index++;
193 ioInfo->wMerged_ = fields_[index];
194 index++;
195 ioInfo->wSectors_ = fields_[index];
196 index++;
197 ioInfo->timeOfWrite_ = fields_[index];
198 index++;
199
200 ioInfo->ios_ = fields_[index];
201 index++;
202 ioInfo->timeOfIo_ = fields_[index];
203 index++;
204 ioInfo->weighted_ = fields_[index];
205 index++;
206
207 ioInfo->dSucc_ = fields_[index];
208 index++;
209 ioInfo->dMerged_ = fields_[index];
210 index++;
211 ioInfo->dSectors_ = fields_[index];
212 index++;
213 ioInfo->timeOfd_ = fields_[index];
214 index++;
215
216 ioInfo->flushSucc_ = fields_[index];
217 index++;
218 ioInfo->timeOfFlush_ = fields_[index];
219
220 ioDatas_.push_back(ioInfo);
221 fields_.clear();
222 return true;
223 }
224 #if __DEBUG__
225 char debugName[128];
226 uint64_t rMergesOrIo = 0;
227 uint64_t rwIos = 0;
228 uint64_t rTicksOrw = 0;
229 auto debugIoInfo = std::make_shared<DiskStats>();
230 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",
231 &debugIoInfo->major_, &debugIoInfo->minor_, debugName, sizeof(debugName),
232 &debugIoInfo->rSucc_, &rMergesOrIo, &rwIos, &rTicksOrw,
233 &debugIoInfo->wSucc_, &debugIoInfo->wMerged_,
234 &debugIoInfo->wSectors_, &debugIoInfo->timeOfWrite_,
235 &debugIoInfo->ios_, &debugIoInfo->timeOfIo_, &debugIoInfo->weighted_,
236 &debugIoInfo->dSucc_, &debugIoInfo->dMerged_,
237 &debugIoInfo->dSectors_, &debugIoInfo->timeOfd_,
238 &debugIoInfo->flushSucc_, &debugIoInfo->timeOfFlush_);
239 if (ret == NUM_SEVEN) {
240 debugIoInfo->rSectors_ = rMergesOrIo;
241 debugIoInfo->wSucc_ = rwIos;
242 debugIoInfo->wSectors_ = rTicksOrw;
243 } else {
244 debugIoInfo->rMerged_ = rMergesOrIo;
245 debugIoInfo->rSectors_ = rwIos;
246 debugIoInfo->timeOfRead_ = rTicksOrw;
247 }
248 debugIoInfo->deviceName_ = std::string(name);
249 ioDatas_.push_back(debugIoInfo);
250 #endif
251
252 return false;
253 }
254
ParseIoStatsEx()255 bool IoStats::ParseIoStatsEx()
256 {
257 return true;
258 }
259
PutPluginStatsData(StatsData * pluginStats)260 bool IoStats::PutPluginStatsData(StatsData* pluginStats)
261 {
262 PutCpuStatsData(pluginStats);
263 if (type_ == DiskioConfig::IO_REPORT) {
264 PutIoStatsData(pluginStats);
265 } else if (type_ == DiskioConfig::IO_REPORT_EX) {
266 ParseIoStatsEx();
267 }
268 return true;
269 }
270
PutCpuStatsData(StatsData * pluginStats)271 uint32_t IoStats::PutCpuStatsData(StatsData* pluginStats)
272 {
273 std::unique_lock<std::mutex> lock(mutex_);
274 if (cpuDatas_.empty()) {
275 return 0;
276 }
277
278 uint32_t count = 0;
279 while (cpuDatas_.size() > 0) {
280 auto cpuData = cpuDatas_.front();
281 auto* cpuInfo = pluginStats->add_cpuinfo();
282 CalcCpuStats(cpuData, cpuInfo);
283 cpuDatas_.pop_front();
284 count++;
285 }
286 lock.unlock();
287 return count;
288 }
289
CalcCpuStats(const CpuDatasPtr & cpuData,CpuStats * cpuInfo)290 void IoStats::CalcCpuStats(const CpuDatasPtr& cpuData, CpuStats* cpuInfo)
291 {
292 auto totalTime = cpuData->GetTotalTime();
293 cpuInfo->set_name(cpuData->name_);
294 cpuInfo->set_cpu_user(KeepTowDigits(cpuData->user_, totalTime));
295 cpuInfo->set_cpu_nice(KeepTowDigits(cpuData->nice_, totalTime));
296 cpuInfo->set_cpu_iowait(KeepTowDigits(cpuData->iowait_, totalTime));
297 cpuInfo->set_cpu_steal(KeepTowDigits(cpuData->steal_, totalTime));
298
299 cpuInfo->set_cpu_sys(KeepTowDigits(cpuData->system_ + cpuData->softirq_ + cpuData->hardirq_, totalTime));
300 cpuInfo->set_cpu_idle(KeepTowDigits(cpuData->idle_, totalTime));
301 }
302
KeepTowDigits(const uint64_t & data,uint64_t div)303 double IoStats::KeepTowDigits(const uint64_t& data, uint64_t div)
304 {
305 double result = 0.00;
306 if (data <= 0 || div == 0) {
307 return result;
308 }
309 double ddiv = div;
310 if (ddiv != NUM_TWO) {
311 ddiv = div * NUM_ZERO_POINTZEROONE;
312 }
313 result = static_cast<double>(data) / ddiv;
314 return result;
315 }
316
PutIoStatsData(StatsData * pluginStats)317 uint32_t IoStats::PutIoStatsData(StatsData* pluginStats)
318 {
319 std::unique_lock<std::mutex> lock(mutex_);
320 if (ioDatas_.empty()) {
321 return 0;
322 }
323
324 uint32_t count = 0;
325 while (ioDatas_.size() > 0) {
326 auto ioData = ioDatas_.front();
327 auto* ioInfo = pluginStats->add_statsinfo();
328 CalcIoStats(ioData, ioInfo);
329 ioDatas_.pop_front();
330 count++;
331 }
332 lock.unlock();
333 return count;
334 }
335
CalcIoStats(const DiskDatasPtr & ioData,IoStatData * ioInfo)336 void IoStats::CalcIoStats(const DiskDatasPtr& ioData, IoStatData* ioInfo)
337 {
338 ioInfo->set_name(ioData->deviceName_);
339 // (成功完成读的总次数 + 写 + 丢弃) / sysTime_
340 ioInfo->set_ios_per_sec(KeepTowDigits(ioData->rSucc_ + ioData->wSucc_ + ioData->dSucc_, sysTime_));
341
342 // 读扇区的次数 / sysTime_
343 ioInfo->set_rd_per_sec(KeepTowDigits(KeepTowDigits(ioData->rSectors_, sysTime_), NUM_TWO));
344 ioInfo->set_wr_per_sec(KeepTowDigits(KeepTowDigits(ioData->wSectors_, sysTime_), NUM_TWO));
345 ioInfo->set_dc_per_sec(KeepTowDigits(KeepTowDigits(ioData->dSectors_, sysTime_), NUM_TWO));
346
347 // 读扇区的次数
348 ioInfo->set_rd_kb(KeepTowDigits(ioData->rSectors_, NUM_TWO));
349 ioInfo->set_wr_kb(KeepTowDigits(ioData->wSectors_, NUM_TWO));
350 ioInfo->set_dc_kb(KeepTowDigits(ioData->dSectors_, NUM_TWO));
351 }
352
StringToL(const char * word,long & value)353 bool IoStats::StringToL(const char* word, long& value)
354 {
355 char* end = nullptr;
356 errno = 0;
357 value = strtol(word, &end, DEC_BASE);
358 if ((errno == ERANGE && (value == LONG_MAX)) || (errno != 0 && value == 0)) {
359 return false;
360 } else if (end == word && (*word >= '0' && *word <= '9')) {
361 return false;
362 }
363
364 return true;
365 }
366
FindFirstNum(char ** p)367 bool IoStats::FindFirstNum(char** p)
368 {
369 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
370 while (**p > '9' || **p < '0') {
371 if (**p == '\0' || **p == '\n') {
372 return false;
373 }
374 (*p)++;
375 }
376 return true;
377 }
378
RemoveSpaces(char ** p)379 bool IoStats::RemoveSpaces(char** p)
380 {
381 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
382 if (**p == '\0' || **p == '\n') {
383 return false;
384 }
385 while (**p == ' ') {
386 (*p)++;
387 if (**p == '\0' || **p == '\n') {
388 return false;
389 }
390 }
391 return true;
392 }
393
FindFirstSpace(char ** p)394 bool IoStats::FindFirstSpace(char** p)
395 {
396 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
397 while (**p != ' ') {
398 if (**p == '\0' || **p == '\n') {
399 return false;
400 }
401 (*p)++;
402 }
403 return true;
404 }
405
ParseLineFields(const std::string & line,std::string & name)406 uint32_t IoStats::ParseLineFields(const std::string& line, std::string& name)
407 {
408 uint64_t num;
409 uint32_t count = 0;
410 char* end = nullptr;
411 char* pTmp = const_cast<char*>(line.c_str());
412
413 fields_.clear();
414 while (pTmp != nullptr && *pTmp != '\n') {
415 CHECK_TRUE(RemoveSpaces(&pTmp), count, "%s: RemoveSpaces failed!", __func__);
416 if (*pTmp >= 'a' && *pTmp <= 'z') {
417 char field[64];
418 int len = 0;
419 int ret = sscanf_s(pTmp, "%63s %n", field, sizeof(field), &len);
420 if (ret == 1 && *field) {
421 name = std::string(field, strlen(field));
422 pTmp += len;
423 }
424 }
425 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
426 num = strtoull(pTmp, &end, DEC_BASE);
427 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
428 fields_.push_back(num);
429 pTmp = end;
430 count++;
431 }
432 return count;
433 }
434
ParseLineFields(const std::string & line)435 uint32_t IoStats::ParseLineFields(const std::string& line)
436 {
437 uint64_t num;
438 uint32_t count = 0;
439 char* end = nullptr;
440 char* pTmp = const_cast<char*>(line.c_str());
441
442 while (pTmp != nullptr && *pTmp != '\n') {
443 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
444 num = static_cast<uint32_t>(strtoull(pTmp, &end, DEC_BASE));
445 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
446 fields_.push_back(num);
447 pTmp = end;
448 count++;
449 }
450 return count;
451 }