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