• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #include "diskio_data_plugin.h"
17 
18 #include <ctime>
19 
20 #include "buffer_splitter.h"
21 #include "common.h"
22 #include "diskio_plugin_result.pbencoder.h"
23 
24 namespace {
25 using namespace OHOS::Developtools::Profiler;
26 constexpr size_t READ_BUFFER_SIZE = 1024 * 16;
27 } // namespace
28 
DiskioDataPlugin()29 DiskioDataPlugin::DiskioDataPlugin()
30 {
31     ioEntry_ = nullptr;
32     buffer_ = nullptr;
33     path_ = "/proc/vmstat";
34     err_ = -1;
35     prevRdSectorsKb_ = 0;
36     prevWrSectorsKb_ = 0;
37     prevTimestamp_.tv_sec = 0;
38     prevTimestamp_.tv_nsec = 0;
39 }
40 
~DiskioDataPlugin()41 DiskioDataPlugin::~DiskioDataPlugin()
42 {
43     PROFILER_LOG_INFO(LOG_CORE, "%s:~DiskioDataPlugin!", __func__);
44     if (buffer_ != nullptr) {
45         free(buffer_);
46         buffer_ = nullptr;
47     }
48 }
49 
GetCmdArgs(const DiskioConfig & protoConfig)50 std::string DiskioDataPlugin::GetCmdArgs(const DiskioConfig& protoConfig)
51 {
52     std::string args;
53     args += "report_io_stats: " + std::to_string(protoConfig.report_io_stats());
54     return args;
55 }
56 
Start(const uint8_t * configData,uint32_t configSize)57 int DiskioDataPlugin::Start(const uint8_t* configData, uint32_t configSize)
58 {
59     buffer_ = malloc(READ_BUFFER_SIZE);
60     CHECK_NOTNULL(buffer_, RET_FAIL, "%s:malloc buffer_ failed!", __func__);
61 
62     CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, RET_FAIL,
63                "%s:parseFromArray failed!", __func__);
64     if (protoConfig_.report_io_stats()) {
65         ioEntry_ = std::make_shared<IoStats>(protoConfig_.report_io_stats());
66     }
67 
68     int ret = COMMON::PluginWriteToHisysevent("diskio_plugin", "sh", GetCmdArgs(protoConfig_), RET_SUCC, "success");
69     PROFILER_LOG_INFO(LOG_CORE, "%s:start success! hisysevent report diskio_plugin result: %d", __func__, ret);
70     return RET_SUCC;
71 }
72 
ReportOptimize(RandomWriteCtx * randomWrite)73 int DiskioDataPlugin::ReportOptimize(RandomWriteCtx* randomWrite)
74 {
75     ProtoEncoder::DiskioData dataProto(randomWrite);
76     WriteDiskioData(dataProto);
77 
78     if (protoConfig_.report_io_stats() && ioEntry_ != nullptr) {
79         ioEntry_->GetIoData();
80         auto* statsData = dataProto.mutable_statsdata();
81         ioEntry_->PutPluginStatsData(*statsData);
82     }
83 
84     int32_t msgSize = dataProto.Finish();
85     return static_cast<int>(msgSize);
86 }
87 
Report(uint8_t * data,uint32_t dataSize)88 int DiskioDataPlugin::Report(uint8_t* data, uint32_t dataSize)
89 {
90     DiskioData dataProto;
91     uint32_t length;
92 
93     WriteDiskioData(dataProto);
94 
95     if (protoConfig_.report_io_stats() && ioEntry_ != nullptr) {
96         ioEntry_->GetIoData();
97         auto* statsData = dataProto.mutable_statsdata();
98         ioEntry_->PutPluginStatsData(*statsData);
99     }
100 
101     length = dataProto.ByteSizeLong();
102     if (length > dataSize) {
103         return -length;
104     }
105     if (dataProto.SerializeToArray(data, length) > 0) {
106         return length;
107     }
108     return 0;
109 }
110 
Stop()111 int DiskioDataPlugin::Stop()
112 {
113     if (buffer_ != nullptr) {
114         free(buffer_);
115         buffer_ = nullptr;
116     }
117     if (ioEntry_ != nullptr) {
118         ioEntry_.reset();
119         ioEntry_ = nullptr;
120     }
121     PROFILER_LOG_INFO(LOG_CORE, "%s:plugin:stop success!", __func__);
122     return 0;
123 }
124 
ReadFile(std::string & fileName)125 int32_t DiskioDataPlugin::ReadFile(std::string& fileName)
126 {
127     int fd = -1;
128     ssize_t bytesRead = 0;
129     char realPath[PATH_MAX + 1] = {0};
130     CHECK_TRUE((fileName.length() < PATH_MAX) && (realpath(fileName.c_str(), realPath) != nullptr), RET_FAIL,
131                "%s:path is invalid: %s, errno=%d", __func__, fileName.c_str(), errno);
132     fd = open(realPath, O_RDONLY | O_CLOEXEC);
133     if (fd == -1) {
134         PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to open(%s), errno=%d", __func__, fileName.c_str(), errno);
135         err_ = errno;
136         return RET_FAIL;
137     }
138     if (buffer_ == nullptr) {
139         PROFILER_LOG_ERROR(LOG_CORE, "%s:empty address, buffer_ is NULL", __func__);
140         err_ = RET_NULL_ADDR;
141         close(fd);
142         return RET_FAIL;
143     }
144     bytesRead = read(fd, buffer_, READ_BUFFER_SIZE - 1);
145     if (bytesRead <= 0) {
146         close(fd);
147         PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to read(%s), errno=%d", __func__, fileName.c_str(), errno);
148         err_ = errno;
149         return RET_FAIL;
150     }
151     close(fd);
152 
153     return bytesRead;
154 }
155 
SetTimestamp(T & diskioData)156 template <typename T> void DiskioDataPlugin::SetTimestamp(T& diskioData)
157 {
158     auto* prevTimestamp = diskioData.mutable_prev_timestamp();
159     prevTimestamp->set_tv_sec(prevTimestamp_.tv_sec);
160     prevTimestamp->set_tv_nsec(prevTimestamp_.tv_nsec);
161 
162     timespec time;
163     clock_gettime(CLOCK_MONOTONIC, &time);
164     auto* timestamp = diskioData.mutable_timestamp();
165     timestamp->set_tv_sec(time.tv_sec);
166     timestamp->set_tv_nsec(time.tv_nsec);
167 
168     prevTimestamp_.tv_sec = time.tv_sec;
169     prevTimestamp_.tv_nsec = time.tv_nsec;
170 }
171 
SetDiskioData(T & diskioData,const char * pFile,uint32_t fileLen)172 template <typename T> void DiskioDataPlugin::SetDiskioData(T& diskioData, const char* pFile, uint32_t fileLen)
173 {
174     BufferSplitter totalbuffer(const_cast<char*>(pFile), fileLen + 1);
175     int64_t rd_sectors_kb = 0;
176     int64_t wr_sectors_kb = 0;
177 
178     do {
179         totalbuffer.NextWord(' ');
180         std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
181         if (strcmp(curWord.c_str(), "pgpgin") == 0) {
182             if (!totalbuffer.NextWord('\n')) {
183                 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to get pgpgin, CurWord() = %s",
184                                    __func__, totalbuffer.CurWord());
185                 break;
186             }
187             curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
188             if (COMMON::IsNumeric(curWord)) {
189                 rd_sectors_kb = atoi(curWord.c_str());
190             }
191         } else if (strcmp(curWord.c_str(), "pgpgout") == 0) {
192             if (!totalbuffer.NextWord('\n')) {
193                 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to get pgpgout, CurWord() = %s",
194                                    __func__, totalbuffer.CurWord());
195                 break;
196             }
197             curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
198             if (COMMON::IsNumeric(curWord)) {
199                 wr_sectors_kb = atoi(curWord.c_str());
200             }
201         }
202     } while (totalbuffer.NextLine());
203 
204     // 前一次系统从磁盘调入的总KB数rd1,通过时间间隔t内KB增量计算磁盘读取速率(rd2-rd1)/t
205     diskioData.set_prev_rd_sectors_kb(prevRdSectorsKb_);
206     // 前一次系统调出到磁盘的总KB数wr1,通过时间间隔t内KB增量计算磁盘写入速率(wr2-wr1)/t
207     diskioData.set_prev_wr_sectors_kb(prevWrSectorsKb_);
208     diskioData.set_rd_sectors_kb(rd_sectors_kb); // 当前系统从磁盘调入的总KB数rd2
209     diskioData.set_wr_sectors_kb(wr_sectors_kb); // 当前系统调出到磁盘的总KB数wr2
210     prevRdSectorsKb_ = rd_sectors_kb;
211     prevWrSectorsKb_ = wr_sectors_kb;
212     SetTimestamp(diskioData); // 设置前一次时间戳和当前时间戳,以便计算两次获取数据的时间间隔t
213 }
214 
WriteDiskioData(T & diskioData)215 template <typename T> void DiskioDataPlugin::WriteDiskioData(T& diskioData)
216 {
217     int32_t ret = ReadFile(path_);
218     if (ret == RET_FAIL) {
219         return;
220     }
221     if ((buffer_ == nullptr) || (ret == 0)) {
222         return;
223     }
224 
225     SetDiskioData(diskioData, reinterpret_cast<char*>(buffer_), ret);
226 }