1 /*
2 * Copyright (c) 2025 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
16 #include "perf_pipe.h"
17 #include "hiperf_client.h"
18 #include "ipc_utilities.h"
19 #include "utilities.h"
20 #if defined(is_ohos) && is_ohos
21 #include "hiperf_hilog.h"
22 #endif
23
24 using namespace std::chrono;
25 namespace OHOS {
26 namespace Developtools {
27 namespace HiPerf {
28 const std::string RECORD_CONTROL_FIFO_FILE_C2S = "/data/log/hiperflog/.hiperf_record_control_c2s";
29 const std::string RECORD_CONTROL_FIFO_FILE_S2C = "/data/log/hiperflog/.hiperf_record_control_s2c";
30 const std::string STAT_CONTROL_FIFO_FILE_C2S = "/data/log/hiperflog/.hiperf_stat_control_c2s";
31 const std::string STAT_CONTROL_FIFO_FILE_S2C = "/data/log/hiperflog/.hiperf_stat_control_s2c";
32 const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms;
33 const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT_CHECK = 1000ms;
34 static constexpr uint64_t CHECK_WAIT_TIME_MS = 200;
35 static constexpr uint32_t MAX_CLIENT_OUTPUT_WAIT_COUNT = 240;
36
SetFifoFileName(const CommandType & commandType,std::string & controlCmd,std::string & fifoFileC2S,std::string & fifoFileS2C)37 void PerfPipe::SetFifoFileName(const CommandType& commandType, std::string& controlCmd,
38 std::string& fifoFileC2S, std::string& fifoFileS2C)
39 {
40 if (commandType == CommandType::RECORD) {
41 fifoFileC2S_ = RECORD_CONTROL_FIFO_FILE_C2S;
42 fifoFileS2C_ = RECORD_CONTROL_FIFO_FILE_S2C;
43 perfCmd_ = "sampling";
44 } else if (commandType == CommandType::STAT) {
45 fifoFileC2S_ = STAT_CONTROL_FIFO_FILE_C2S;
46 fifoFileS2C_ = STAT_CONTROL_FIFO_FILE_S2C;
47 perfCmd_ = "counting";
48 }
49 fifoFileC2S = fifoFileC2S_;
50 fifoFileS2C = fifoFileS2C_;
51 controlCmd_ = controlCmd;
52 HLOGD("C2S:%s, S2C:%s", fifoFileC2S.c_str(), fifoFileS2C.c_str());
53 HIPERF_HILOGD(MODULE_DEFAULT, "[SetFifoFileName] C2S:%{public}s, S2C:%{public}s",
54 fifoFileC2S_.c_str(), fifoFileS2C.c_str());
55 }
56
RemoveFifoFile()57 void PerfPipe::RemoveFifoFile()
58 {
59 char errInfo[ERRINFOLEN] = { 0 };
60 if (remove(fifoFileC2S_.c_str()) != 0) {
61 strerror_r(errno, errInfo, ERRINFOLEN);
62 HLOGE("remove %s failed, errno:(%d:%s)", fifoFileC2S_.c_str(), errno, errInfo);
63 HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)",
64 fifoFileC2S_.c_str(), errno, errInfo);
65 }
66 if (remove(fifoFileS2C_.c_str()) != 0) {
67 strerror_r(errno, errInfo, ERRINFOLEN);
68 HLOGE("remove %s failed, errno:(%d:%s)", fifoFileS2C_.c_str(), errno, errInfo);
69 HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)",
70 fifoFileS2C_.c_str(), errno, errInfo);
71 }
72 }
73
CreateFifoFile()74 bool PerfPipe::CreateFifoFile()
75 {
76 char errInfo[ERRINFOLEN] = { 0 };
77 const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
78 std::string tempPath("/data/log/hiperflog/");
79 if (!IsDirectoryExists(tempPath)) {
80 HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s not exist.", tempPath.c_str());
81 if (!CreateDirectory(tempPath, HIPERF_FILE_PERM_770)) {
82 HIPERF_HILOGI(MODULE_DEFAULT, "create %{public}s failed.", tempPath.c_str());
83 }
84 }
85 if (mkfifo(fifoFileS2C_.c_str(), fifoMode) != 0 ||
86 mkfifo(fifoFileC2S_.c_str(), fifoMode) != 0) {
87 if (errno == EEXIST) {
88 printf("another %s service is running.\n", perfCmd_.c_str());
89 } else {
90 RemoveFifoFile();
91 }
92 strerror_r(errno, errInfo, ERRINFOLEN);
93 HLOGE("create fifo file failed. %d:%s", errno, errInfo);
94 return false;
95 }
96 return true;
97 }
98
SendFifoAndWaitReply(const std::string & cmd,const std::chrono::milliseconds & timeOut)99 bool PerfPipe::SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut)
100 {
101 // need open for read first, because server maybe send reply before client wait to read
102 char errInfo[ERRINFOLEN] = { 0 };
103 int fdRead = open(fifoFileS2C_.c_str(), O_RDONLY | O_NONBLOCK);
104 if (fdRead == -1) {
105 HLOGE("can not open fifo file(%s)", fifoFileS2C_.c_str());
106 HIPERF_HILOGE(MODULE_DEFAULT,
107 "[SendFifoAndWaitReply] can not open fifo file: %{public}s, errno:(%{public}d:%{public}s)",
108 fifoFileS2C_.c_str(), errno, errInfo);
109 return false;
110 }
111 int fdWrite = open(fifoFileC2S_.c_str(), O_WRONLY | O_NONBLOCK);
112 if (fdWrite == -1) {
113 HLOGE("can not open fifo file(%s)", fifoFileC2S_.c_str());
114 HIPERF_HILOGE(MODULE_DEFAULT,
115 "[SendFifoAndWaitReply] can not open fifo file: %{public}s, errno:(%{public}d:%{public}s)",
116 fifoFileC2S_.c_str(), errno, errInfo);
117 close(fdRead);
118 return false;
119 }
120 ssize_t size = write(fdWrite, cmd.c_str(), cmd.size());
121 if (size != static_cast<ssize_t>(cmd.size())) {
122 HLOGE("failed to write fifo file(%s) command(%s)", fifoFileC2S_.c_str(), cmd.c_str());
123 HIPERF_HILOGE(MODULE_DEFAULT, "failed to write fifo file(%{public}s) command(%{public}s).",
124 fifoFileC2S_.c_str(), cmd.c_str());
125 close(fdWrite);
126 close(fdRead);
127 return false;
128 }
129 close(fdWrite);
130
131 bool ret = WaitFifoReply(fdRead, timeOut);
132 close(fdRead);
133 return ret;
134 }
135
WaitFifoReply(const int fd,const std::chrono::milliseconds & timeOut)136 bool PerfPipe::WaitFifoReply(const int fd, const std::chrono::milliseconds &timeOut)
137 {
138 std::string reply;
139 WaitFifoReply(fd, timeOut, reply);
140 HIPERF_HILOGI(MODULE_DEFAULT, "[WaitFifoReply] WaitFifoReply reply:(%{public}s)", reply.c_str());
141 return reply == HiperfClient::ReplyOK;
142 }
143
WaitFifoReply(const int fd,const std::chrono::milliseconds & timeOut,std::string & reply)144 void PerfPipe::WaitFifoReply(const int fd, const std::chrono::milliseconds &timeOut, std::string& reply)
145 {
146 struct pollfd pollFd {
147 fd, POLLIN, 0
148 };
149 int polled = poll(&pollFd, 1, timeOut.count());
150 reply.clear();
151 if (polled > 0) {
152 bool exitLoop = false;
153 while (!exitLoop) {
154 char c;
155 ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1));
156 if (result <= 0) {
157 HLOGE("[WaitFifoReply] read from fifo file(%s) failed", fifoFileS2C_.c_str());
158 HIPERF_HILOGE(MODULE_DEFAULT, "[WaitFifoReply] read from fifo file(%{public}s) failed",
159 fifoFileS2C_.c_str());
160 exitLoop = true;
161 }
162 reply.push_back(c);
163 if (c == '\n') {
164 exitLoop = true;
165 }
166 }
167 } else if (polled == 0) {
168 HLOGD("[WaitFifoReply] wait fifo file(%s) timeout", fifoFileS2C_.c_str());
169 HIPERF_HILOGD(MODULE_DEFAULT, "[WaitFifoReply] wait fifo file(%{public}s) timeout", fifoFileS2C_.c_str());
170 } else {
171 HLOGD("[WaitFifoReply] wait fifo file(%s) failed", fifoFileS2C_.c_str());
172 HIPERF_HILOGD(MODULE_DEFAULT, "[WaitFifoReply] wait fifo file(%{public}s) failed", fifoFileS2C_.c_str());
173 }
174 }
175
SetOutPutEnd(const bool outputEnd)176 void PerfPipe::SetOutPutEnd(const bool outputEnd)
177 {
178 outputEnd_ = outputEnd;
179 }
180
ProcessStopCommand(const bool ret)181 void PerfPipe::ProcessStopCommand(const bool ret)
182 {
183 if (ret) {
184 // wait sampling process exit really
185 static constexpr uint64_t waitCheckSleepMs = 200;
186 std::this_thread::sleep_for(milliseconds(waitCheckSleepMs));
187 while (SendFifoAndWaitReply(HiperfClient::ReplyCheck, CONTROL_WAITREPY_TIMEOUT_CHECK)) {
188 std::this_thread::sleep_for(milliseconds(waitCheckSleepMs));
189 }
190 HLOGI("wait reply check end.");
191 }
192
193 RemoveFifoFile();
194 }
195
ProcessOutputCommand(bool ret)196 void PerfPipe::ProcessOutputCommand(bool ret)
197 {
198 if (!ret) {
199 HLOGI("send fifo and wait repoy fail");
200 HIPERF_HILOGI(MODULE_DEFAULT, "send fifo and wait repoy fail");
201 return;
202 }
203
204 std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS));
205 uint32_t outputFailCount = 0;
206 while (!outputEnd_) {
207 ret = SendFifoAndWaitReply(HiperfClient::ReplyOutputCheck, CONTROL_WAITREPY_TIMEOUT_CHECK);
208 if (outputFailCount++ > MAX_CLIENT_OUTPUT_WAIT_COUNT || ret) {
209 break;
210 }
211 std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS));
212 }
213 }
214
ProcessControlCmd()215 bool PerfPipe::ProcessControlCmd()
216 {
217 bool ret = false;
218 if (controlCmd_ == CONTROL_CMD_START) {
219 ret = SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TIMEOUT);
220 } else if (controlCmd_ == CONTROL_CMD_RESUME) {
221 ret = SendFifoAndWaitReply(HiperfClient::ReplyResume, CONTROL_WAITREPY_TIMEOUT);
222 } else if (controlCmd_ == CONTROL_CMD_PAUSE) {
223 ret = SendFifoAndWaitReply(HiperfClient::ReplyPause, CONTROL_WAITREPY_TIMEOUT);
224 } else if (controlCmd_ == CONTROL_CMD_STOP) {
225 ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT);
226 if (!ret) {
227 ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT);
228 }
229 ProcessStopCommand(ret);
230 } else if (controlCmd_ == CONTROL_CMD_OUTPUT) {
231 ret = SendFifoAndWaitReply(HiperfClient::ReplyOutput, CONTROL_WAITREPY_TIMEOUT);
232 ProcessOutputCommand(ret);
233 }
234 if (ret) {
235 printf("%s %s success.\n", controlCmd_.c_str(), perfCmd_.c_str());
236 HIPERF_HILOGI(MODULE_DEFAULT, "[ProcessControlCmd] %{public}s %{public}s success.",
237 controlCmd_.c_str(), perfCmd_.c_str());
238 } else {
239 printf("%s %s failed.\n", controlCmd_.c_str(), perfCmd_.c_str());
240 HIPERF_HILOGI(MODULE_DEFAULT, "[ProcessControlCmd] %{public}s %{public}s failed.",
241 controlCmd_.c_str(), perfCmd_.c_str());
242 }
243 return ret;
244 }
245 } // namespace HiPerf
246 } // namespace Developtools
247 } // namespace OHOS
248