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 
31 namespace {
32 using namespace OHOS::Developtools::Profiler;
33 const int SLEEP_TIME = 50;
34 const int BUF_MAX_LEN = 64;
35 const int MS_PER_S = 1000;
36 const int US_PER_S = 1000000;
37 const char *FPS_FORMAT = "SP_daemon -profilerfps 31104000 -sections 10";
38 
39 } // namespace
40 
HidumpPlugin()41 HidumpPlugin::HidumpPlugin() : fp_(nullptr, nullptr) {}
42 
~HidumpPlugin()43 HidumpPlugin::~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 
Start(const uint8_t* configData, uint32_t configSize)61 int 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 
Stop()93 int 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 
SetWriter(WriterStruct* writer)109 int HidumpPlugin::SetWriter(WriterStruct* writer)
110 {
111     resultWriter_ = writer;
112     return 0;
113 }
114 
Loop(void)115 void 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 
163 template <typename T>
ParseHidumpInfo(T& hidumpInfoProto, char *buf, size_t len)164 bool 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 
SetConfig(HidumpConfig& config)202 void HidumpPlugin::SetConfig(HidumpConfig& config)
203 {
204     protoConfig_ = config;
205 }
206 
SetTestCmd(const char *test_cmd)207 int 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 
GetTestCmd(void)214 const char *HidumpPlugin::GetTestCmd(void)
215 {
216     return testCmd_;
217 }
218