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 }