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