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
30 namespace {
31 using namespace OHOS::Developtools::Profiler;
32 const int REDIRECT_STDOUT = 1;
33 const int REDIRECT_STDERR = 2;
34 const int SLEEP_TIME = 50;
35 const int BUF_MAX_LEN = 64;
36 const int MS_PER_S = 1000;
37 const int US_PER_S = 1000000;
38 const char *FPS_FORMAT = "SP_daemon -profilerfps 31104000 -sections 10";
39
40 static pid_t volatile g_child;
41 const int READ = 0;
42 const int WRITE = 1;
43 const int PIPE_LEN = 2;
44 const std::string BIN_COMMAND("/bin/SP_daemon");
45 } // namespace
46
HidumpPlugin()47 HidumpPlugin::HidumpPlugin() : fp_(nullptr, nullptr) {}
48
~HidumpPlugin()49 HidumpPlugin::~HidumpPlugin()
50 {
51 PROFILER_LOG_INFO(LOG_CORE, "%s: ready!", __func__);
52 std::unique_lock<std::mutex> locker(mutex_);
53 if (running_) {
54 running_ = false;
55 if (writeThread_.joinable()) {
56 writeThread_.join();
57 }
58 }
59 locker.unlock();
60
61 if (fp_ != nullptr) {
62 fp_.reset();
63 }
64 PROFILER_LOG_INFO(LOG_CORE, "%s: success!", __func__);
65 }
66
Start(const uint8_t * configData,uint32_t configSize)67 int HidumpPlugin::Start(const uint8_t* configData, uint32_t configSize)
68 {
69 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin:Start ----> !");
70 CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, -1, "HidumpPlugin: ParseFromArray failed");
71
72 fp_ = std::unique_ptr<FILE, int (*)(FILE*)>(CustomPopen(std::to_string(protoConfig_.sections()).c_str(), "r"),
73 CustomPclose);
74 if (fp_.get() == nullptr) {
75 const int bufSize = 256;
76 char buf[bufSize] = {0};
77 strerror_r(errno, buf, bufSize);
78 PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin: CustomPopen(%s) Failed, errno(%d:%s)", FPS_FORMAT, errno, buf);
79 return -1;
80 }
81 CHECK_NOTNULL(resultWriter_, -1, "HidumpPlugin: Writer is no set!");
82 CHECK_NOTNULL(resultWriter_->write, -1, "HidumpPlugin: Writer.write is no set!");
83 CHECK_NOTNULL(resultWriter_->flush, -1, "HidumpPlugin: Writer.flush is no set!");
84 std::unique_lock<std::mutex> locker(mutex_);
85 running_ = true;
86 writeThread_ = std::thread([this] { this->Loop(); });
87 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: ---> Start success!");
88 return 0;
89 }
90
Stop()91 int HidumpPlugin::Stop()
92 {
93 std::unique_lock<std::mutex> locker(mutex_);
94 running_ = false;
95 locker.unlock();
96 if (writeThread_.joinable()) {
97 writeThread_.join();
98 }
99 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin:stop thread success!");
100 if (fp_ != nullptr) {
101 fp_.reset();
102 }
103 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: stop success!");
104 return 0;
105 }
106
SetWriter(WriterStruct * writer)107 int HidumpPlugin::SetWriter(WriterStruct* writer)
108 {
109 resultWriter_ = writer;
110 return 0;
111 }
112
Loop(void)113 void HidumpPlugin::Loop(void)
114 {
115 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: Loop start");
116 CHECK_NOTNULL(resultWriter_, NO_RETVAL, "%s: resultWriter_ nullptr", __func__);
117
118 fcntl(fileno(fp_.get()), F_SETFL, O_NONBLOCK);
119 while (running_) {
120 char buf[BUF_MAX_LEN] = { 0 };
121
122 if (fgets(buf, BUF_MAX_LEN - 1, fp_.get()) == nullptr) {
123 std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
124 continue;
125 }
126 char* pTempBuf = buf;
127 if (strncmp(pTempBuf, "fps:", strlen("fps:")) == 0) {
128 pTempBuf += strlen("fps:");
129 std::string stringBuf(pTempBuf);
130 size_t npos = stringBuf.find("|");
131 uint32_t fps = static_cast<uint32_t>(std::stoi(stringBuf.substr(0, npos)));
132 if (fps > 0) {
133 continue;
134 }
135 }
136 if (resultWriter_->isProtobufSerialize) {
137 HidumpInfo dataProto;
138 if (!ParseHidumpInfo(dataProto, buf)) {
139 continue;
140 }
141 if (dataProto.ByteSizeLong() > 0) {
142 buffer_.resize(dataProto.ByteSizeLong());
143 dataProto.SerializeToArray(buffer_.data(), buffer_.size());
144 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
145 resultWriter_->flush(resultWriter_);
146 }
147 } else {
148 ProtoEncoder::HidumpInfo hidumpInfo(resultWriter_->startReport(resultWriter_));
149 if (!ParseHidumpInfo(hidumpInfo, buf)) {
150 PROFILER_LOG_ERROR(LOG_CORE, "parse hidump info failed!");
151 }
152 int messageLen = hidumpInfo.Finish();
153 resultWriter_->finishReport(resultWriter_, messageLen);
154 resultWriter_->flush(resultWriter_);
155 }
156 }
157
158 PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: Loop exit");
159 }
160
161 template <typename T>
ParseHidumpInfo(T & hidumpInfoProto,char * buf)162 bool HidumpPlugin::ParseHidumpInfo(T& hidumpInfoProto, char *buf)
163 {
164 // format: fps:123|1501960484673
165 if (strncmp(buf, "fps:", strlen("fps:")) != 0 && strncmp(buf, "sectionsFps:", strlen("sectionsFps:")) != 0) {
166 if (strstr(buf, "inaccessible or not found") != nullptr) {
167 PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin: fps command not found!");
168 } else {
169 PROFILER_LOG_ERROR(LOG_CORE, "format error. %s", buf);
170 }
171 return false;
172 }
173
174 if (strncmp(buf, "fps:", strlen("fps:")) == 0) {
175 buf += strlen("fps:");
176 } else if (strncmp(buf, "sectionsFps:", strlen("sectionsFps:")) == 0) {
177 buf += strlen("sectionsFps:");
178 }
179
180 char *tmp = strchr(buf, '|');
181 CHECK_NOTNULL(tmp, false, "format error. %s", buf);
182 *tmp = ' ';
183 std::stringstream strvalue(buf);
184 uint32_t fps = 0;
185 strvalue >> fps;
186 uint64_t time_ms;
187 strvalue >> time_ms;
188
189 auto* eve = hidumpInfoProto.add_fps_event();
190 eve->set_fps(fps);
191 eve->set_id(::FpsData::REALTIME);
192 auto* time = eve->mutable_time();
193 time->set_tv_sec(time_ms / MS_PER_S);
194 time->set_tv_nsec((time_ms % MS_PER_S) * US_PER_S);
195
196 return true;
197 }
198
CustomPopen(const char * command,const char * type)199 FILE* HidumpPlugin::CustomPopen(const char* command, const char* type)
200 {
201 CHECK_TRUE(command != nullptr && type != nullptr, nullptr, "HidumpPlugin:%s param invalid", __func__);
202
203 int fd[PIPE_LEN];
204 pipe(fd);
205
206 pid_t pid = fork();
207 if (pid == -1) {
208 perror("fork");
209 exit(1);
210 }
211
212 // child process
213 if (pid == 0) {
214 if (!strncmp(type, "r", strlen(type))) {
215 close(fd[READ]);
216 dup2(fd[WRITE], REDIRECT_STDOUT); // Redirect stdout to pipe
217 dup2(fd[WRITE], REDIRECT_STDERR); // Redirect stderr to pipe
218 } else {
219 close(fd[WRITE]);
220 dup2(fd[READ], 0); // Redirect stdin to pipe
221 }
222
223 setpgid(pid, pid);
224 execl(BIN_COMMAND.c_str(), "SP_daemon", "-profilerfps", "31104000", "-sections", command, nullptr);
225 exit(0);
226 } else {
227 if (!strncmp(type, "r", strlen(type))) {
228 // Close the WRITE end of the pipe since parent's fd is read-only
229 close(fd[WRITE]);
230 } else {
231 // Close the READ end of the pipe since parent's fd is write-only
232 close(fd[READ]);
233 }
234 }
235
236 g_child = pid;
237
238 if (!strncmp(type, "r", strlen(type))) {
239 return fdopen(fd[READ], "r");
240 }
241
242 return fdopen(fd[WRITE], "w");
243 }
244
CustomPclose(FILE * fp)245 int HidumpPlugin::CustomPclose(FILE* fp)
246 {
247 CHECK_NOTNULL(fp, -1, "HidumpPlugin:%s fp is null", __func__);
248 int stat;
249 if (fclose(fp) != 0) {
250 const int bufSize = 256;
251 char buf[bufSize] = { 0 };
252 strerror_r(errno, buf, bufSize);
253 PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin:%s fclose failed! errno(%d:%s)", __func__, errno, buf);
254 return -1;
255 }
256 kill(g_child, SIGKILL);
257 if (waitpid(g_child, &stat, 0) == -1) {
258 if (errno != EINTR) {
259 stat = errno;
260 }
261 }
262 return stat;
263 }
264
SetConfig(HidumpConfig & config)265 void HidumpPlugin::SetConfig(HidumpConfig& config)
266 {
267 protoConfig_ = config;
268 }
269
SetTestCmd(const char * test_cmd)270 int HidumpPlugin::SetTestCmd(const char *test_cmd)
271 {
272 CHECK_NOTNULL(test_cmd, -1, "HidumpPlugin:%s test_cmd is null", __func__);
273 testCmd_ = const_cast<char *>(test_cmd);
274 return 0;
275 }
276
GetTestCmd(void)277 const char *HidumpPlugin::GetTestCmd(void)
278 {
279 return testCmd_;
280 }
281