1 /*
2 * Copyright (c) 2021-2022 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 "hiperf_client.h"
16 #include <algorithm>
17 #include <cinttypes>
18 #include <csignal>
19 #include <cstring>
20 #include <thread>
21 #include <poll.h>
22 #include <sys/prctl.h>
23 #include <unistd.h>
24 #include "hiperf_hilog.h"
25
26 using namespace std::chrono;
27 namespace OHOS {
28 namespace Developtools {
29 namespace HiPerf {
30 namespace HiperfClient {
31 const ssize_t ERRINFOLEN = 512;
SetOption(const std::string & name,bool enable)32 void RecordOption::SetOption(const std::string &name, bool enable)
33 {
34 auto it = std::find(args_.begin(), args_.end(), name);
35 if (enable) {
36 if (it == args_.end()) {
37 args_.emplace_back(name);
38 }
39
40 return;
41 }
42 if (it != args_.end()) {
43 args_.erase(it);
44 return;
45 }
46 }
47
SetOption(const std::string & name,int value)48 void RecordOption::SetOption(const std::string &name, int value)
49 {
50 auto it = std::find(args_.begin(), args_.end(), name);
51 if (it != args_.end()) {
52 it++;
53 *it = std::to_string(value);
54 return;
55 }
56
57 args_.emplace_back(name);
58 args_.emplace_back(std::to_string(value));
59 return;
60 }
61
SetOption(const std::string & name,const std::vector<int> & vInt)62 void RecordOption::SetOption(const std::string &name, const std::vector<int> &vInt)
63 {
64 auto it = std::find(args_.begin(), args_.end(), name);
65 if (vInt.empty()) {
66 if (it != args_.end()) {
67 it = args_.erase(it); // remove key
68 if (it != args_.end()) {
69 args_.erase(it); // remove value
70 }
71 }
72 return;
73 }
74
75 std::string str;
76 for (auto n : vInt) {
77 str.append(std::to_string(n));
78 str.append(",");
79 }
80 str.pop_back(); // remove the last ','
81
82 if (it != args_.end()) {
83 it++;
84 *it = str;
85 return;
86 }
87 args_.emplace_back(name);
88 args_.emplace_back(str);
89 }
90
SetOption(const std::string & name,const std::string & str)91 void RecordOption::SetOption(const std::string &name, const std::string &str)
92 {
93 auto it = std::find(args_.begin(), args_.end(), name);
94 if (str.empty()) {
95 if (it != args_.end()) {
96 args_.erase(it);
97 args_.erase(it); // remove value
98 }
99 return;
100 }
101 if (it != args_.end()) {
102 it++;
103 *it = str;
104 return;
105 }
106 args_.emplace_back(name);
107 args_.emplace_back(str);
108 }
109
SetOption(const std::string & name,const std::vector<std::string> & vStr)110 void RecordOption::SetOption(const std::string &name, const std::vector<std::string> &vStr)
111 {
112 auto it = std::find(args_.begin(), args_.end(), name);
113 if (vStr.empty()) {
114 if (it != args_.end()) {
115 args_.erase(it);
116 args_.erase(it); // remove value
117 }
118 return;
119 }
120
121 std::string str;
122 for (auto substr : vStr) {
123 str.append(substr);
124 str.append(",");
125 }
126 str.pop_back(); // remove the last ','
127
128 if (it != args_.end()) {
129 it++;
130 *it = str;
131 return;
132 }
133 args_.emplace_back(name);
134 args_.emplace_back(str);
135 }
136
SetTargetSystemWide(bool enable)137 void RecordOption::SetTargetSystemWide(bool enable)
138 {
139 SetOption(ArgTargetSystemWide, enable);
140 }
141
SetCompressData(bool enable)142 void RecordOption::SetCompressData(bool enable)
143 {
144 SetOption(ArgCompressData, enable);
145 }
146
SetSelectCpus(const std::vector<int> & cpus)147 void RecordOption::SetSelectCpus(const std::vector<int> &cpus)
148 {
149 SetOption(ArgSelectCpus, cpus);
150 }
151
SetTimeStopSec(int timeStopSec)152 void RecordOption::SetTimeStopSec(int timeStopSec)
153 {
154 SetOption(ArgTimeStopSec, timeStopSec);
155 }
156
SetFrequency(int frequency)157 void RecordOption::SetFrequency(int frequency)
158 {
159 SetOption(ArgFrequency, frequency);
160 }
161
SetPeriod(int period)162 void RecordOption::SetPeriod(int period)
163 {
164 SetOption(ArgPeriod, period);
165 }
166
SetSelectEvents(const std::vector<std::string> & selectEvents)167 void RecordOption::SetSelectEvents(const std::vector<std::string> &selectEvents)
168 {
169 SetOption(ArgSelectEvents, selectEvents);
170 }
171
SetSelectGroups(const std::vector<std::string> & selectGroups)172 void RecordOption::SetSelectGroups(const std::vector<std::string> &selectGroups)
173 {
174 SetOption(ArgSelectGroups, selectGroups);
175 }
176
SetNoInherit(bool enable)177 void RecordOption::SetNoInherit(bool enable)
178 {
179 SetOption(ArgNoInherit, enable);
180 }
181
SetSelectPids(const std::vector<pid_t> & selectPids)182 void RecordOption::SetSelectPids(const std::vector<pid_t> &selectPids)
183 {
184 SetOption(ArgSelectPids, selectPids);
185 }
186
SetSelectTids(const std::vector<pid_t> & selectTids)187 void RecordOption::SetSelectTids(const std::vector<pid_t> &selectTids)
188 {
189 SetOption(ArgSelectTids, selectTids);
190 }
191
SetExcludePerf(bool excludePerf)192 void RecordOption::SetExcludePerf(bool excludePerf)
193 {
194 SetOption(ArgExcludePerf, excludePerf);
195 }
196
SetCpuPercent(int cpuPercent)197 void RecordOption::SetCpuPercent(int cpuPercent)
198 {
199 SetOption(ArgCpuPercent, cpuPercent);
200 }
201
SetOffCPU(bool offCPU)202 void RecordOption::SetOffCPU(bool offCPU)
203 {
204 SetOption(ArgOffCPU, offCPU);
205 }
206
SetCallGraph(const std::string & callGraph)207 void RecordOption::SetCallGraph(const std::string &callGraph)
208 {
209 SetOption(ArgCallGraph, callGraph);
210 }
211
SetDelayUnwind(bool delayUnwind)212 void RecordOption::SetDelayUnwind(bool delayUnwind)
213 {
214 SetOption(ArgDelayUnwind, delayUnwind);
215 }
216
SetDisableUnwind(bool disableUnwind)217 void RecordOption::SetDisableUnwind(bool disableUnwind)
218 {
219 SetOption(ArgDisableUnwind, disableUnwind);
220 }
221
SetDisableCallstackMerge(bool disableCallstackMerge)222 void RecordOption::SetDisableCallstackMerge(bool disableCallstackMerge)
223 {
224 SetOption(ArgDisableCallstackMerge, disableCallstackMerge);
225 }
226
SetSymbolDir(const std::string & symbolDir_)227 void RecordOption::SetSymbolDir(const std::string &symbolDir_)
228 {
229 SetOption(ArgSymbolDir, symbolDir_);
230 }
231
SetDataLimit(const std::string & limit)232 void RecordOption::SetDataLimit(const std::string &limit)
233 {
234 SetOption(ArgDataLimit, limit);
235 }
236
SetAppPackage(const std::string & appPackage)237 void RecordOption::SetAppPackage(const std::string &appPackage)
238 {
239 SetOption(ArgAppPackage, appPackage);
240 }
241
SetClockId(const std::string & clockId)242 void RecordOption::SetClockId(const std::string &clockId)
243 {
244 SetOption(ArgClockId, clockId);
245 }
246
SetVecBranchSampleTypes(const std::vector<std::string> & vecBranchSampleTypes)247 void RecordOption::SetVecBranchSampleTypes(const std::vector<std::string> &vecBranchSampleTypes)
248 {
249 SetOption(ArgVecBranchSampleTypes, vecBranchSampleTypes);
250 }
251
SetMmapPages(int mmapPages)252 void RecordOption::SetMmapPages(int mmapPages)
253 {
254 SetOption(ArgMmapPages, mmapPages);
255 }
256
Client(const std::string & outputDir)257 Client::Client(const std::string &outputDir)
258 {
259 HIPERF_HILOGD(MODULE_CPP_API, "%" HILOG_PUBLIC "s default init with %" HILOG_PUBLIC "s\n",
260 __FUNCTION__, outputDir.c_str());
261 Setup(outputDir);
262 }
263
Setup(std::string outputDir)264 bool Client::Setup(std::string outputDir)
265 {
266 std::string CurrentCommandPath = CurrentPath + HiperfCommandName;
267 std::string SystemCommandPath = SystemBinPath + HiperfCommandName;
268 std::string TempCommandPath = TempBinPath + HiperfCommandName;
269
270 if (!outputDir.empty() && outputDir.back() != '/') {
271 outputDir.push_back('/');
272 }
273 HIPERF_HILOGD(MODULE_CPP_API, "outputDir setup to %" HILOG_PUBLIC "s\n", outputDir.c_str());
274
275 // found command path
276 if (access(CurrentCommandPath.c_str(), X_OK) == 0) {
277 executeCommandPath_ = CurrentCommandPath;
278 } else if (access(TempCommandPath.c_str(), X_OK) == 0) {
279 executeCommandPath_ = TempCommandPath;
280 } else if (access(SystemCommandPath.c_str(), X_OK) == 0) {
281 executeCommandPath_ = SystemCommandPath;
282 } else {
283 HIPERF_HILOGD(MODULE_CPP_API, "no hiperf command found\n");
284 return ready_;
285 }
286
287 // check output path
288 // found command path
289 if (access(outputDir.c_str(), W_OK) == 0) {
290 outputDir_ = outputDir;
291 } else if (access(CurrentPath.c_str(), W_OK) == 0) {
292 outputDir_ = CurrentPath;
293 } else {
294 HIPERF_HILOGD(MODULE_CPP_API, "no writeable output path found\n");
295 return ready_;
296 }
297 outputFileName_ = PerfDataName;
298
299 myPid_ = getpid();
300
301 // every thing check ok
302 ready_ = true;
303
304 return ready_;
305 }
306
~Client()307 Client::~Client()
308 {
309 KillChild();
310 }
311
IsReady()312 bool Client::IsReady()
313 {
314 return ready_;
315 }
316
SetDebugMode()317 void Client::SetDebugMode()
318 {
319 debug_ = true;
320 }
321
SetDebugMuchMode()322 void Client::SetDebugMuchMode()
323 {
324 debugMuch_ = true;
325 }
326
Start()327 bool Client::Start()
328 {
329 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
330
331 std::vector<std::string> args;
332 args.push_back("-p");
333 args.push_back(std::to_string(getpid()));
334 return Start(args);
335 }
336
GetExecCmd(std::vector<std::string> & cmd,int pipeIn,int pipeOut,const std::vector<std::string> & args)337 void Client::GetExecCmd(std::vector<std::string> &cmd, int pipeIn, int pipeOut,
338 const std::vector<std::string> &args)
339 {
340 cmd.clear();
341 cmd.emplace_back(executeCommandPath_);
342
343 if (debug_) {
344 cmd.emplace_back(ArgDebug);
345 } else if (debugMuch_) {
346 cmd.emplace_back(ArgDebugMuch);
347 }
348
349 if (hilog_) {
350 cmd.emplace_back(ArgHilog);
351 }
352
353 cmd.emplace_back(CommandRecord);
354 cmd.emplace_back(ArgPipeInput);
355 cmd.emplace_back(std::to_string(pipeIn));
356 cmd.emplace_back(ArgPipeOutput);
357 cmd.emplace_back(std::to_string(pipeOut));
358 cmd.emplace_back(ArgOutputPath);
359 cmd.emplace_back(GetOutputPerfDataPath());
360
361 cmd.insert(cmd.end(), args.begin(), args.end());
362 }
363
Start(const std::vector<std::string> & args)364 bool Client::Start(const std::vector<std::string> &args)
365 {
366 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
367 if (!ready_) {
368 HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
369 return false;
370 }
371
372 int clientToServerFd[2];
373 int serverToClientFd[2];
374 if (pipe(clientToServerFd) != 0) {
375 char errInfo[ERRINFOLEN] = { 0 };
376 strerror_r(errno, errInfo, ERRINFOLEN);
377 HIPERF_HILOGD(MODULE_CPP_API, "failed to create pipe: %" HILOG_PUBLIC "s", errInfo);
378 return false;
379 } else if (pipe(serverToClientFd) != 0) {
380 char errInfo[ERRINFOLEN] = { 0 };
381 strerror_r(errno, errInfo, ERRINFOLEN);
382 HIPERF_HILOGD(MODULE_CPP_API, "failed to create pipe: %" HILOG_PUBLIC "s", errInfo);
383 close(clientToServerFd[PIPE_READ]);
384 close(clientToServerFd[PIPE_WRITE]);
385 return false;
386 }
387
388 hperfPid_ = fork();
389 if (hperfPid_ == -1) {
390 char errInfo[ERRINFOLEN] = { 0 };
391 strerror_r(errno, errInfo, ERRINFOLEN);
392 HIPERF_HILOGD(MODULE_CPP_API, "failed to fork: %" HILOG_PUBLIC "s", errInfo);
393 close(clientToServerFd[PIPE_READ]);
394 close(clientToServerFd[PIPE_WRITE]);
395 close(serverToClientFd[PIPE_READ]);
396 close(serverToClientFd[PIPE_WRITE]);
397 return false;
398 } else if (hperfPid_ == 0) {
399 // child process
400 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
401 close(clientToServerFd[PIPE_WRITE]);
402 close(serverToClientFd[PIPE_READ]);
403
404 std::vector<std::string> cmd;
405 GetExecCmd(cmd, clientToServerFd[PIPE_READ], serverToClientFd[PIPE_WRITE], args);
406 // conver vector to array for execvp()
407 char *argv[cmd.size() + SIZE_ARGV_TAIL];
408 size_t i = 0;
409 for (i = 0; i < cmd.size(); ++i) {
410 HIPERF_HILOGD(MODULE_CPP_API, "args %" HILOG_PUBLIC "zu : %" HILOG_PUBLIC "s", i,
411 cmd[i].c_str());
412 argv[i] = cmd[i].data();
413 }
414 argv[i] = nullptr;
415
416 execv(argv[0], argv);
417 char errInfo[ERRINFOLEN] = { 0 };
418 strerror_r(errno, errInfo, ERRINFOLEN);
419 HIPERF_HILOGD(MODULE_CPP_API,
420 "failed to call exec: '%" HILOG_PUBLIC "s' %" HILOG_PUBLIC "s\n",
421 executeCommandPath_.c_str(), errInfo);
422 exit(0);
423 } else {
424 // parent process
425 close(clientToServerFd[PIPE_READ]);
426 close(serverToClientFd[PIPE_WRITE]);
427
428 clientToServerFd_ = clientToServerFd[PIPE_WRITE];
429 serverToClientFd_ = serverToClientFd[PIPE_READ];
430 }
431 using namespace std::chrono_literals;
432 if (!WaitCommandReply(1000ms)) {
433 HIPERF_HILOGD(MODULE_CPP_API, "start failed . lets kill it");
434 KillChild();
435 return false;
436 }
437 return true;
438 }
439
Start(const RecordOption & option)440 bool Client::Start(const RecordOption &option)
441 {
442 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
443 if (!option.GetOutputFileName().empty()) {
444 outputFileName_ = option.GetOutputFileName();
445 }
446 return Start(option.GetOptionVecString());
447 }
448
WaitCommandReply(std::chrono::milliseconds timeOut)449 bool Client::WaitCommandReply(std::chrono::milliseconds timeOut)
450 {
451 std::string reply;
452 struct pollfd pollFd;
453 pollFd.fd = serverToClientFd_;
454 pollFd.events = POLLIN;
455 pollFd.revents = 0;
456
457 // wait some data
458 int polled = poll(&pollFd, 1, timeOut.count());
459 if (polled > 0) {
460 while (true) {
461 char c;
462 ssize_t result = TEMP_FAILURE_RETRY(read(serverToClientFd_, &c, 1));
463 if (result <= 0) {
464 HIPERF_HILOGD(MODULE_CPP_API, "read failed from pipe");
465 return false; // read fial means not ok
466 }
467
468 reply.push_back(c);
469 if (c == '\n') {
470 break;
471 }
472 }
473 } else if (polled == 0) {
474 HIPERF_HILOGD(MODULE_CPP_API, "Client:command no response %" HILOG_PUBLIC "" PRIu64 ".\n",
475 (uint64_t)timeOut.count());
476 } else {
477 HIPERF_HILOGD(MODULE_CPP_API, "Client:command poll failed.\n");
478 }
479 HIPERF_HILOGD(MODULE_CPP_API, "Client:new reply:%" HILOG_PUBLIC "s\n", reply.c_str());
480 if (reply == ReplyOK) {
481 return true;
482 } else {
483 return false;
484 }
485 }
486
KillChild()487 void Client::KillChild()
488 {
489 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
490 if (clientToServerFd_ != -1) {
491 close(clientToServerFd_);
492 }
493 if (serverToClientFd_ != -1) {
494 close(serverToClientFd_);
495 }
496 if (hperfPid_ > 0) {
497 kill(hperfPid_, SIGKILL);
498 }
499 }
500
SendCommandAndWait(const std::string & cmd)501 bool Client::SendCommandAndWait(const std::string &cmd)
502 {
503 if (clientToServerFd_ == -1) {
504 HIPERF_HILOGD(MODULE_CPP_API, "fd not ready. maybe not called start.");
505 return false;
506 }
507 size_t size = write(clientToServerFd_, cmd.c_str(), cmd.size());
508 HIPERF_HILOGD(MODULE_CPP_API,
509 "Client:%" HILOG_PUBLIC "s -> %" HILOG_PUBLIC "d : %" HILOG_PUBLIC "zd\n",
510 cmd.c_str(), clientToServerFd_, size);
511 if (size == cmd.size()) {
512 return WaitCommandReply();
513 } else {
514 return false;
515 }
516 }
517
Pause()518 bool Client::Pause()
519 {
520 if (!ready_) {
521 HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
522 return false;
523 }
524 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
525 if (SendCommandAndWait(ReplyPause)) {
526 return true;
527 }
528 return false;
529 }
530
Resume()531 bool Client::Resume()
532 {
533 if (!ready_) {
534 HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
535 return false;
536 }
537 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
538 if (SendCommandAndWait(ReplyResume)) {
539 return true;
540 }
541 return false;
542 }
543
Stop()544 bool Client::Stop()
545 {
546 if (!ready_) {
547 HIPERF_HILOGD(MODULE_CPP_API, "Client:hiperf not ready.\n");
548 return false;
549 }
550 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
551 if (SendCommandAndWait(ReplyStop)) {
552 // wait sampling process exit really
553 while (SendCommandAndWait(ReplyCheck)) {
554 std::this_thread::sleep_for(1s);
555 }
556 return true;
557 }
558 return false;
559 }
560
EnableHilog()561 void Client::EnableHilog()
562 {
563 HIPERF_HILOGD(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
564 hilog_ = true;
565 }
566 } // namespace HiperfClient
567 } // namespace HiPerf
568 } // namespace Developtools
569 } // namespace OHOS
570