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 CHECK_TRUE(!input.fail(), systime, "%s:open %s failed, errno = %d", __func__, SYSTIME_PATH, errno);
50 do {
51 if (!input.good()) {
52 return systime;
53 }
54 std::string line;
55 getline(input, line);
56 line += '\n';
57
58 uint64_t nsec, ncent;
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
PutPluginStatsData(StatsData * pluginStats)250 bool IoStats::PutPluginStatsData(StatsData* pluginStats)
251 {
252 PutCpuStatsData(pluginStats);
253 if (type_ == DiskioConfig::IO_REPORT) {
254 PutIoStatsData(pluginStats);
255 } else if (type_ == DiskioConfig::IO_REPORT_EX) {
256 ParseIoStatsEx();
257 }
258 return true;
259 }
260
PutCpuStatsData(StatsData * pluginStats)261 uint32_t IoStats::PutCpuStatsData(StatsData* pluginStats)
262 {
263 std::unique_lock<std::mutex> lock(mutex_);
264 CHECK_TRUE(!cpuDatas_.empty(), 0, "cpuDatas_ is empty");
265
266 uint32_t count = 0;
267 while (cpuDatas_.size() > 0) {
268 auto cpuData = cpuDatas_.front();
269 auto* cpuInfo = pluginStats->add_cpuinfo();
270 CalcCpuStats(cpuData, cpuInfo);
271 cpuDatas_.pop_front();
272 count++;
273 }
274 lock.unlock();
275 return count;
276 }
277
CalcCpuStats(const CpuDatasPtr & cpuData,CpuStats * cpuInfo)278 void IoStats::CalcCpuStats(const CpuDatasPtr& cpuData, CpuStats* cpuInfo)
279 {
280 auto totalTime = cpuData->GetTotalTime();
281 cpuInfo->set_name(cpuData->name_);
282 cpuInfo->set_cpu_user(KeepTowDigits(cpuData->user_, totalTime));
283 cpuInfo->set_cpu_nice(KeepTowDigits(cpuData->nice_, totalTime));
284 cpuInfo->set_cpu_iowait(KeepTowDigits(cpuData->iowait_, totalTime));
285 cpuInfo->set_cpu_steal(KeepTowDigits(cpuData->steal_, totalTime));
286
287 cpuInfo->set_cpu_sys(KeepTowDigits(cpuData->system_ + cpuData->softirq_ + cpuData->hardirq_, totalTime));
288 cpuInfo->set_cpu_idle(KeepTowDigits(cpuData->idle_, totalTime));
289 }
290
KeepTowDigits(const uint64_t & data,uint64_t div)291 double IoStats::KeepTowDigits(const uint64_t& data, uint64_t div)
292 {
293 double result = 0.00;
294 if (data <= 0 || div == 0) {
295 return result;
296 }
297 double ddiv = div;
298 if (ddiv != NUM_TWO) {
299 ddiv = div * NUM_ZERO_POINTZEROONE;
300 }
301 result = static_cast<double>(data) / ddiv;
302 return result;
303 }
304
PutIoStatsData(StatsData * pluginStats)305 uint32_t IoStats::PutIoStatsData(StatsData* pluginStats)
306 {
307 std::unique_lock<std::mutex> lock(mutex_);
308 CHECK_TRUE(!ioDatas_.empty(), 0, "ioDatas_ is empty");
309
310 uint32_t count = 0;
311 while (ioDatas_.size() > 0) {
312 auto ioData = ioDatas_.front();
313 auto* ioInfo = pluginStats->add_statsinfo();
314 CalcIoStats(ioData, ioInfo);
315 ioDatas_.pop_front();
316 count++;
317 }
318 lock.unlock();
319 return count;
320 }
321
CalcIoStats(const DiskDatasPtr & ioData,IoStatData * ioInfo)322 void IoStats::CalcIoStats(const DiskDatasPtr& ioData, IoStatData* ioInfo)
323 {
324 ioInfo->set_name(ioData->deviceName_);
325 // (成功完成读的总次数 + 写 + 丢弃) / sysTime_
326 ioInfo->set_ios_per_sec(KeepTowDigits(ioData->rSucc_ + ioData->wSucc_ + ioData->dSucc_, sysTime_));
327
328 // 读扇区的次数 / sysTime_
329 ioInfo->set_rd_per_sec(KeepTowDigits(KeepTowDigits(ioData->rSectors_, sysTime_), NUM_TWO));
330 ioInfo->set_wr_per_sec(KeepTowDigits(KeepTowDigits(ioData->wSectors_, sysTime_), NUM_TWO));
331 ioInfo->set_dc_per_sec(KeepTowDigits(KeepTowDigits(ioData->dSectors_, sysTime_), NUM_TWO));
332
333 // 读扇区的次数
334 ioInfo->set_rd_kb(KeepTowDigits(ioData->rSectors_, NUM_TWO));
335 ioInfo->set_wr_kb(KeepTowDigits(ioData->wSectors_, NUM_TWO));
336 ioInfo->set_dc_kb(KeepTowDigits(ioData->dSectors_, NUM_TWO));
337 }
338
FindFirstNum(char ** p)339 bool IoStats::FindFirstNum(char** p)
340 {
341 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
342 while (**p > '9' || **p < '0') {
343 if (**p == '\0' || **p == '\n') {
344 return false;
345 }
346 (*p)++;
347 }
348 return true;
349 }
350
RemoveSpaces(char ** p)351 bool IoStats::RemoveSpaces(char** p)
352 {
353 CHECK_NOTNULL(*p, false, "IoStats:%s", __func__);
354 if (**p == '\0' || **p == '\n') {
355 return false;
356 }
357 while (**p == ' ') {
358 (*p)++;
359 if (**p == '\0' || **p == '\n') {
360 return false;
361 }
362 }
363 return true;
364 }
365
ParseLineFields(const std::string & line,std::string & name)366 uint32_t IoStats::ParseLineFields(const std::string& line, std::string& name)
367 {
368 uint64_t num;
369 uint32_t count = 0;
370 char* end = nullptr;
371 char* pTmp = const_cast<char*>(line.c_str());
372
373 fields_.clear();
374 while (pTmp != nullptr && *pTmp != '\n') {
375 CHECK_TRUE(RemoveSpaces(&pTmp), count, "%s: RemoveSpaces failed!", __func__);
376 if (*pTmp >= 'a' && *pTmp <= 'z') {
377 char field[64];
378 int len = 0;
379 int ret = sscanf_s(pTmp, "%63s %n", field, sizeof(field), &len);
380 if (ret == 1 && *field) {
381 name = std::string(field, strlen(field));
382 pTmp += len;
383 }
384 }
385 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
386 num = strtoull(pTmp, &end, DEC_BASE);
387 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
388 fields_.push_back(num);
389 pTmp = end;
390 count++;
391 }
392 return count;
393 }
394
ParseLineFields(const std::string & line)395 uint32_t IoStats::ParseLineFields(const std::string& line)
396 {
397 uint64_t num;
398 uint32_t count = 0;
399 char* end = nullptr;
400 char* pTmp = const_cast<char*>(line.c_str());
401
402 while (pTmp != nullptr && *pTmp != '\n') {
403 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
404 num = static_cast<uint32_t>(strtoull(pTmp, &end, DEC_BASE));
405 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
406 fields_.push_back(num);
407 pTmp = end;
408 count++;
409 }
410 return count;
411 }