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#include "hidump_plugin.h" 16#include <sys/syscall.h> 17#include <sys/types.h> 18#include <unistd.h> 19#include <cstdio> 20#include <cstring> 21#include <fcntl.h> 22#include <cinttypes> 23#include <csignal> 24#include <sstream> 25#include <sys/wait.h> 26 27#include "hidump_plugin_result.pbencoder.h" 28#include "securec.h" 29#include "common.h" 30 31namespace { 32using namespace OHOS::Developtools::Profiler; 33const int SLEEP_TIME = 50; 34const int BUF_MAX_LEN = 64; 35const int MS_PER_S = 1000; 36const int US_PER_S = 1000000; 37const char *FPS_FORMAT = "SP_daemon -profilerfps 31104000 -sections 10"; 38 39} // namespace 40 41HidumpPlugin::HidumpPlugin() : fp_(nullptr, nullptr) {} 42 43HidumpPlugin::~HidumpPlugin() 44{ 45 PROFILER_LOG_INFO(LOG_CORE, "%s: ready!", __func__); 46 std::unique_lock<std::mutex> locker(mutex_); 47 if (running_) { 48 running_ = false; 49 if (writeThread_.joinable()) { 50 writeThread_.join(); 51 } 52 } 53 locker.unlock(); 54 55 if (fp_ != nullptr) { 56 fp_.reset(); 57 } 58 PROFILER_LOG_INFO(LOG_CORE, "%s: success!", __func__); 59} 60 61int HidumpPlugin::Start(const uint8_t* configData, uint32_t configSize) 62{ 63 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin:Start ----> !"); 64 CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, -1, "HidumpPlugin: ParseFromArray failed"); 65 std::vector<std::string> fullCmd; 66 fullCmd.push_back("/system/bin/SP_daemon"); 67 fullCmd.push_back("SP_daemon"); 68 fullCmd.push_back("-profilerfps"); 69 fullCmd.push_back("31104000"); 70 fullCmd.push_back("-sections"); 71 fullCmd.push_back(std::to_string(protoConfig_.sections())); 72 fp_ = std::unique_ptr<FILE, std::function<int (FILE*)>>( 73 COMMON::CustomPopen(fullCmd, "r", pipeFds_, childPid_, true), [this](FILE* fp) -> int { 74 return COMMON::CustomPclose(fp, pipeFds_, childPid_, true); 75 }); 76 if (fp_.get() == nullptr) { 77 const int bufSize = 256; 78 char buf[bufSize] = {0}; 79 strerror_r(errno, buf, bufSize); 80 PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin: CustomPopen(%s) Failed, errno(%d:%s)", FPS_FORMAT, errno, buf); 81 return -1; 82 } 83 CHECK_NOTNULL(resultWriter_, -1, "HidumpPlugin: Writer is no set!"); 84 CHECK_NOTNULL(resultWriter_->write, -1, "HidumpPlugin: Writer.write is no set!"); 85 CHECK_NOTNULL(resultWriter_->flush, -1, "HidumpPlugin: Writer.flush is no set!"); 86 std::unique_lock<std::mutex> locker(mutex_); 87 running_ = true; 88 writeThread_ = std::thread([this] { this->Loop(); }); 89 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: ---> Start success!"); 90 return 0; 91} 92 93int HidumpPlugin::Stop() 94{ 95 std::unique_lock<std::mutex> locker(mutex_); 96 running_ = false; 97 locker.unlock(); 98 if (writeThread_.joinable()) { 99 writeThread_.join(); 100 } 101 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin:stop thread success!"); 102 if (fp_ != nullptr) { 103 fp_.reset(); 104 } 105 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: stop success!"); 106 return 0; 107} 108 109int HidumpPlugin::SetWriter(WriterStruct* writer) 110{ 111 resultWriter_ = writer; 112 return 0; 113} 114 115void HidumpPlugin::Loop(void) 116{ 117 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: Loop start"); 118 CHECK_NOTNULL(resultWriter_, NO_RETVAL, "%s: resultWriter_ nullptr", __func__); 119 120 fcntl(fileno(fp_.get()), F_SETFL, O_NONBLOCK); 121 while (running_) { 122 char buf[BUF_MAX_LEN] = { 0 }; 123 124 if (fgets(buf, BUF_MAX_LEN - 1, fp_.get()) == nullptr) { 125 std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME)); 126 continue; 127 } 128 char* pTempBuf = buf; 129 if (strncmp(pTempBuf, "fps:", strlen("fps:")) == 0) { 130 pTempBuf += strlen("fps:"); 131 std::string stringBuf(pTempBuf); 132 size_t npos = stringBuf.find("|"); 133 uint32_t fps = static_cast<uint32_t>(std::stoi(stringBuf.substr(0, npos))); 134 if (fps > 0) { 135 continue; 136 } 137 } 138 if (resultWriter_->isProtobufSerialize) { 139 HidumpInfo dataProto; 140 if (!ParseHidumpInfo(dataProto, buf, sizeof(buf))) { 141 continue; 142 } 143 if (dataProto.ByteSizeLong() > 0) { 144 buffer_.resize(dataProto.ByteSizeLong()); 145 dataProto.SerializeToArray(buffer_.data(), buffer_.size()); 146 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size()); 147 resultWriter_->flush(resultWriter_); 148 } 149 } else { 150 ProtoEncoder::HidumpInfo hidumpInfo(resultWriter_->startReport(resultWriter_)); 151 if (!ParseHidumpInfo(hidumpInfo, buf, sizeof(buf))) { 152 PROFILER_LOG_ERROR(LOG_CORE, "parse hidump info failed!"); 153 } 154 int messageLen = hidumpInfo.Finish(); 155 resultWriter_->finishReport(resultWriter_, messageLen); 156 resultWriter_->flush(resultWriter_); 157 } 158 } 159 160 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: Loop exit"); 161} 162 163template <typename T> 164bool HidumpPlugin::ParseHidumpInfo(T& hidumpInfoProto, char *buf, size_t len) 165{ 166 UNUSED_PARAMETER(len); 167 // format: fps:123|1501960484673 168 if (strncmp(buf, "fps:", strlen("fps:")) != 0 && strncmp(buf, "sectionsFps:", strlen("sectionsFps:")) != 0) { 169 if (strstr(buf, "inaccessible or not found") != nullptr) { 170 PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin: fps command not found!"); 171 } else { 172 PROFILER_LOG_ERROR(LOG_CORE, "format error. %s", buf); 173 } 174 return false; 175 } 176 177 if (strncmp(buf, "fps:", strlen("fps:")) == 0) { 178 buf += strlen("fps:"); 179 } else if (strncmp(buf, "sectionsFps:", strlen("sectionsFps:")) == 0) { 180 buf += strlen("sectionsFps:"); 181 } 182 183 char *tmp = strchr(buf, '|'); 184 CHECK_NOTNULL(tmp, false, "format error. %s", buf); 185 *tmp = ' '; 186 std::stringstream strvalue(buf); 187 uint32_t fps = 0; 188 strvalue >> fps; 189 uint64_t time_ms; 190 strvalue >> time_ms; 191 192 auto* eve = hidumpInfoProto.add_fps_event(); 193 eve->set_fps(fps); 194 eve->set_id(::FpsData::REALTIME); 195 auto* time = eve->mutable_time(); 196 time->set_tv_sec(time_ms / MS_PER_S); 197 time->set_tv_nsec((time_ms % MS_PER_S) * US_PER_S); 198 199 return true; 200} 201 202void HidumpPlugin::SetConfig(HidumpConfig& config) 203{ 204 protoConfig_ = config; 205} 206 207int HidumpPlugin::SetTestCmd(const char *test_cmd) 208{ 209 CHECK_NOTNULL(test_cmd, -1, "HidumpPlugin:%s test_cmd is null", __func__); 210 testCmd_ = const_cast<char *>(test_cmd); 211 return 0; 212} 213 214const char *HidumpPlugin::GetTestCmd(void) 215{ 216 return testCmd_; 217} 218