• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }