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