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 23namespace { 24using namespace OHOS::Developtools::Profiler; 25constexpr size_t READ_BUFFER_SIZE = 1024 * 16; 26} // namespace 27 28DiskioDataPlugin::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 40DiskioDataPlugin::~DiskioDataPlugin() 41{ 42 PROFILER_LOG_INFO(LOG_CORE, "%s:~DiskioDataPlugin!", __func__); 43 if (buffer_ != nullptr) { 44 free(buffer_); 45 buffer_ = nullptr; 46 } 47} 48 49int 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 64int 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 79int 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 102int 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 116int32_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 147template <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 163template <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 202template <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}