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 <sys/wait.h>
17 #include <algorithm>
18 #include <cinttypes>
19 #include <csignal>
20 #include <cstring>
21 #include <thread>
22 #include <poll.h>
23 #include <sys/prctl.h>
24 #include <unistd.h>
25 #include "hiperf_hilog.h"
26
27 using namespace std::chrono;
28 namespace OHOS {
29 namespace Developtools {
30 namespace HiPerf {
31 namespace HiperfClient {
32 static const std::string HIPERF_COMMAND_NAME = "hiperf";
33 static const std::string SYSTEM_BIN_PATH = "/system/bin/";
34 static const std::string CURRENT_PATH = "./";
35 static const std::string PERF_DATA_NAME = "perf.data";
36 static const std::string COMMAND_RECORD = "record";
37 static const std::string ARG_OUTPUT_PATH = "-o";
38 static const std::string ARG_DEBUG = "--verbose";
39 static const std::string ARG_DEBUG_MUCH = "--much";
40 static const std::string ARG_HILOG = "--hilog";
41 static const std::string ARG_PIPE_INPUT = "--pipe_input";
42 static const std::string ARG_PIPE_OUTPUT = "--pipe_output";
43 static const std::string ARG_TARGET_SYSTEM_WIDE = "-a";
44 static const std::string ARG_COMPRESS_DATA = "-z";
45 static const std::string ARG_SELECT_CPUS = "-c";
46 static const std::string ARG_TIME_STOP_SEC = "-d";
47 static const std::string ARG_FREQUENCY = "-f";
48 static const std::string ARG_PERIOD = "--period";
49 static const std::string ARG_SELECT_EVENTS = "-e";
50 static const std::string ARG_SELECT_GROUPS = "-g";
51 static const std::string ARG_NO_INHERIT = "--no-inherit";
52 static const std::string ARG_SELECT_PIDS = "-p";
53 static const std::string ARG_SELECT_TIDS = "-t";
54 static const std::string ARG_EXCLUDE_PERF = "--exclude-hiperf";
55 static const std::string ARG_CPU_PERCENT = "--cpu-limit";
56 static const std::string ARG_OFF_CPU = "--offcpu";
57 static const std::string ARG_CALL_GRAPH = "--call-stack";
58 static const std::string ARG_DELAY_UNWIND = "--delay-unwind";
59 static const std::string ARG_DISABLE_UNWIND = "--disable-unwind";
60 static const std::string ARG_DISABLE_CALLSTACK_MERGE = "--disable-callstack-expand";
61 static const std::string ARG_SYMBOL_DIR = "--symbol-dir";
62 static const std::string ARG_DATA_LIMIT = "--data-limit";
63 static const std::string ARG_APP_PACKAGE = "--app";
64 static const std::string ARG_CLOCK_ID = "--clockid";
65 static const std::string ARG_VEC_BRANCH_SAMPLE_TYPES = "-j";
66 static const std::string ARG_MMAP_PAGES = "-m";
67 static const std::string ARG_REPORT = "--report";
68
69 static constexpr int DEFAULT_DURATION_TIME = 10;
70 static constexpr int DEFAULT_FREQUENCY_TIME = 100;
71 static constexpr uint64_t PIPE_READ = 0;
72 static constexpr uint64_t PIPE_WRITE = 1;
73 static constexpr ssize_t ERRINFOLEN = 512;
74 static constexpr size_t SIZE_ARGV_TAIL = 1; // nullptr
75
SetOption(const std::string &name, bool enable)76 void RecordOption::SetOption(const std::string &name, bool enable)
77 {
78 auto it = std::find(args_.begin(), args_.end(), name);
79 if (enable) {
80 if (it == args_.end()) {
81 args_.emplace_back(name);
82 }
83
84 return;
85 }
86 if (it != args_.end()) {
87 args_.erase(it);
88 return;
89 }
90 }
91
SetOption(const std::string &name, int value)92 void RecordOption::SetOption(const std::string &name, int value)
93 {
94 auto it = std::find(args_.begin(), args_.end(), name);
95 if (it != args_.end()) {
96 it++;
97 *it = std::to_string(value);
98 return;
99 }
100
101 args_.emplace_back(name);
102 args_.emplace_back(std::to_string(value));
103 return;
104 }
105
SetOption(const std::string &name, const std::vector<int> &vInt)106 void RecordOption::SetOption(const std::string &name, const std::vector<int> &vInt)
107 {
108 auto it = std::find(args_.begin(), args_.end(), name);
109 if (vInt.empty()) {
110 if (it != args_.end()) {
111 it = args_.erase(it); // remove key
112 if (it != args_.end()) {
113 args_.erase(it); // remove value
114 }
115 }
116 return;
117 }
118
119 std::string str;
120 for (auto n : vInt) {
121 str.append(std::to_string(n));
122 str.append(",");
123 }
124 str.pop_back(); // remove the last ','
125
126 if (it != args_.end()) {
127 it++;
128 *it = str;
129 return;
130 }
131 args_.emplace_back(name);
132 args_.emplace_back(str);
133 }
134
SetOption(const std::string &name, const std::string &str)135 void RecordOption::SetOption(const std::string &name, const std::string &str)
136 {
137 auto it = std::find(args_.begin(), args_.end(), name);
138 if (str.empty()) {
139 if (it != args_.end()) {
140 args_.erase(it);
141 args_.erase(it); // remove value
142 }
143 return;
144 }
145 if (it != args_.end()) {
146 it++;
147 *it = str;
148 return;
149 }
150 args_.emplace_back(name);
151 args_.emplace_back(str);
152 }
153
SetOption(const std::string &name, const std::vector<std::string> &vStr)154 void RecordOption::SetOption(const std::string &name, const std::vector<std::string> &vStr)
155 {
156 auto it = std::find(args_.begin(), args_.end(), name);
157 if (vStr.empty()) {
158 if (it != args_.end()) {
159 args_.erase(it);
160 args_.erase(it); // remove value
161 }
162 return;
163 }
164
165 std::string str;
166 for (auto substr : vStr) {
167 str.append(substr);
168 str.append(",");
169 }
170 str.pop_back(); // remove the last ','
171
172 if (it != args_.end()) {
173 it++;
174 *it = str;
175 return;
176 }
177 args_.emplace_back(name);
178 args_.emplace_back(str);
179 }
180
SetTargetSystemWide(bool enable)181 void RecordOption::SetTargetSystemWide(bool enable)
182 {
183 SetOption(ARG_TARGET_SYSTEM_WIDE, enable);
184 }
185
SetCompressData(bool enable)186 void RecordOption::SetCompressData(bool enable)
187 {
188 SetOption(ARG_COMPRESS_DATA, enable);
189 }
190
SetSelectCpus(const std::vector<int> &cpus)191 void RecordOption::SetSelectCpus(const std::vector<int> &cpus)
192 {
193 SetOption(ARG_SELECT_CPUS, cpus);
194 }
195
SetTimeStopSec(int timeStopSec)196 void RecordOption::SetTimeStopSec(int timeStopSec)
197 {
198 this->timeSpec_ = true;
199 SetOption(ARG_TIME_STOP_SEC, timeStopSec);
200 }
201
SetFrequency(int frequency)202 void RecordOption::SetFrequency(int frequency)
203 {
204 SetOption(ARG_FREQUENCY, frequency);
205 }
206
SetPeriod(int period)207 void RecordOption::SetPeriod(int period)
208 {
209 SetOption(ARG_PERIOD, period);
210 }
211
SetSelectEvents(const std::vector<std::string> &selectEvents)212 void RecordOption::SetSelectEvents(const std::vector<std::string> &selectEvents)
213 {
214 SetOption(ARG_SELECT_EVENTS, selectEvents);
215 }
216
SetSelectGroups(const std::vector<std::string> &selectGroups)217 void RecordOption::SetSelectGroups(const std::vector<std::string> &selectGroups)
218 {
219 SetOption(ARG_SELECT_GROUPS, selectGroups);
220 }
221
SetNoInherit(bool enable)222 void RecordOption::SetNoInherit(bool enable)
223 {
224 SetOption(ARG_NO_INHERIT, enable);
225 }
226
SetSelectPids(const std::vector<pid_t> &selectPids)227 void RecordOption::SetSelectPids(const std::vector<pid_t> &selectPids)
228 {
229 SetOption(ARG_SELECT_PIDS, selectPids);
230 }
231
SetCallStackSamplingConfigs(int duration)232 void RecordOption::SetCallStackSamplingConfigs(int duration)
233 {
234 if (duration <= 0) {
235 duration = DEFAULT_DURATION_TIME;
236 }
237 RecordOption opt;
238 SetSelectEvents(opt.GetSelectEvents());
239 SetTimeStopSec(duration);
240 SetFrequency(DEFAULT_FREQUENCY_TIME);
241 SetCallGraph("fp");
242 }
243
SetSelectTids(const std::vector<pid_t> &selectTids)244 void RecordOption::SetSelectTids(const std::vector<pid_t> &selectTids)
245 {
246 SetOption(ARG_SELECT_TIDS, selectTids);
247 }
248
SetExcludePerf(bool excludePerf)249 void RecordOption::SetExcludePerf(bool excludePerf)
250 {
251 SetOption(ARG_EXCLUDE_PERF, excludePerf);
252 }
253
SetCpuPercent(int cpuPercent)254 void RecordOption::SetCpuPercent(int cpuPercent)
255 {
256 SetOption(ARG_CPU_PERCENT, cpuPercent);
257 }
258
SetOffCPU(bool offCPU)259 void RecordOption::SetOffCPU(bool offCPU)
260 {
261 SetOption(ARG_OFF_CPU, offCPU);
262 }
263
SetCallGraph(const std::string &callGraph)264 void RecordOption::SetCallGraph(const std::string &callGraph)
265 {
266 SetOption(ARG_CALL_GRAPH, callGraph);
267 }
268
SetDelayUnwind(bool delayUnwind)269 void RecordOption::SetDelayUnwind(bool delayUnwind)
270 {
271 SetOption(ARG_DELAY_UNWIND, delayUnwind);
272 }
273
SetDisableUnwind(bool disableUnwind)274 void RecordOption::SetDisableUnwind(bool disableUnwind)
275 {
276 SetOption(ARG_DISABLE_UNWIND, disableUnwind);
277 }
278
SetDisableCallstackMerge(bool disableCallstackMerge)279 void RecordOption::SetDisableCallstackMerge(bool disableCallstackMerge)
280 {
281 SetOption(ARG_DISABLE_CALLSTACK_MERGE, disableCallstackMerge);
282 }
283
SetSymbolDir(const std::string &symbolDir_)284 void RecordOption::SetSymbolDir(const std::string &symbolDir_)
285 {
286 SetOption(ARG_SYMBOL_DIR, symbolDir_);
287 }
288
SetDataLimit(const std::string &limit)289 void RecordOption::SetDataLimit(const std::string &limit)
290 {
291 SetOption(ARG_DATA_LIMIT, limit);
292 }
293
SetAppPackage(const std::string &appPackage)294 void RecordOption::SetAppPackage(const std::string &appPackage)
295 {
296 SetOption(ARG_APP_PACKAGE, appPackage);
297 }
298
SetClockId(const std::string &clockId)299 void RecordOption::SetClockId(const std::string &clockId)
300 {
301 SetOption(ARG_CLOCK_ID, clockId);
302 }
303
SetVecBranchSampleTypes(const std::vector<std::string> &vecBranchSampleTypes)304 void RecordOption::SetVecBranchSampleTypes(const std::vector<std::string> &vecBranchSampleTypes)
305 {
306 SetOption(ARG_VEC_BRANCH_SAMPLE_TYPES, vecBranchSampleTypes);
307 }
308
SetMmapPages(int mmapPages)309 void RecordOption::SetMmapPages(int mmapPages)
310 {
311 SetOption(ARG_MMAP_PAGES, mmapPages);
312 }
313
SetReport(bool report)314 void RecordOption::SetReport(bool report)
315 {
316 SetOption(ARG_REPORT, report);
317 }
318
Client(const std::string &outputDir)319 Client::Client(const std::string &outputDir)
320 {
321 HIPERF_HILOGI(MODULE_CPP_API, "%" HILOG_PUBLIC "s default init with %" HILOG_PUBLIC "s\n",
322 __FUNCTION__, outputDir.c_str());
323 Setup(outputDir);
324 }
325
Setup(std::string outputDir)326 bool Client::Setup(std::string outputDir)
327 {
328 std::string CurrentCommandPath = CURRENT_PATH + HIPERF_COMMAND_NAME;
329 std::string SystemCommandPath = SYSTEM_BIN_PATH + HIPERF_COMMAND_NAME;
330 std::string TempCommandPath = TempBinPath + HIPERF_COMMAND_NAME;
331
332 if (!outputDir.empty() && outputDir.back() != '/') {
333 outputDir.push_back('/');
334 }
335 HIPERF_HILOGI(MODULE_CPP_API, "outputDir setup to %" HILOG_PUBLIC "s\n", outputDir.c_str());
336
337 // found command path
338 if (access(SystemCommandPath.c_str(), X_OK) == 0) {
339 executeCommandPath_ = SystemCommandPath;
340 } else if (access(TempCommandPath.c_str(), X_OK) == 0) {
341 executeCommandPath_ = TempCommandPath;
342 } else if (access(CurrentCommandPath.c_str(), X_OK) == 0) {
343 executeCommandPath_ = CurrentCommandPath;
344 } else {
345 HIPERF_HILOGI(MODULE_CPP_API, "no hiperf command found\n");
346 return ready_;
347 }
348
349 // check output path
350 // found command path
351 if (access(outputDir.c_str(), W_OK) == 0) {
352 outputDir_ = outputDir;
353 } else if (access(CURRENT_PATH.c_str(), W_OK) == 0) {
354 outputDir_ = CURRENT_PATH;
355 } else {
356 HIPERF_HILOGI(MODULE_CPP_API, "no writeable output path found\n");
357 return ready_;
358 }
359 outputFileName_ = PERF_DATA_NAME;
360
361 myPid_ = getpid();
362
363 // every thing check ok
364 ready_ = true;
365
366 return ready_;
367 }
368
~Client()369 Client::~Client()
370 {
371 KillChild();
372 }
373
IsReady()374 bool Client::IsReady()
375 {
376 return ready_;
377 }
378
SetDebugMode()379 void Client::SetDebugMode()
380 {
381 debug_ = true;
382 }
383
SetDebugMuchMode()384 void Client::SetDebugMuchMode()
385 {
386 debugMuch_ = true;
387 }
388
Start()389 bool Client::Start()
390 {
391 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
392
393 std::vector<std::string> args;
394 args.push_back("-p");
395 args.push_back(std::to_string(getpid()));
396 return Start(args);
397 }
398
PrepareExecCmd(std::vector<std::string> &cmd)399 void Client::PrepareExecCmd(std::vector<std::string> &cmd)
400 {
401 cmd.clear();
402 cmd.emplace_back(executeCommandPath_);
403
404 if (debug_) {
405 cmd.emplace_back(ARG_DEBUG);
406 } else if (debugMuch_) {
407 cmd.emplace_back(ARG_DEBUG_MUCH);
408 }
409
410 if (hilog_) {
411 cmd.emplace_back(ARG_HILOG);
412 }
413
414 cmd.emplace_back(COMMAND_RECORD);
415 cmd.emplace_back(ARG_OUTPUT_PATH);
416 cmd.emplace_back(GetOutputPerfDataPath());
417 }
418
GetExecCmd(std::vector<std::string> &cmd, int pipeIn, int pipeOut, const std::vector<std::string> &args)419 void Client::GetExecCmd(std::vector<std::string> &cmd, int pipeIn, int pipeOut,
420 const std::vector<std::string> &args)
421 {
422 PrepareExecCmd(cmd);
423 cmd.emplace_back(ARG_PIPE_INPUT);
424 cmd.emplace_back(std::to_string(pipeIn));
425 cmd.emplace_back(ARG_PIPE_OUTPUT);
426 cmd.emplace_back(std::to_string(pipeOut));
427
428 cmd.insert(cmd.end(), args.begin(), args.end());
429 }
430
GetExecCmd(std::vector<std::string> &cmd, const std::vector<std::string> &args)431 void Client::GetExecCmd(std::vector<std::string> &cmd,
432 const std::vector<std::string> &args)
433 {
434 PrepareExecCmd(cmd);
435
436 cmd.insert(cmd.end(), args.begin(), args.end());
437 }
438
Start(const std::vector<std::string> &args, bool immediately)439 bool Client::Start(const std::vector<std::string> &args, bool immediately)
440 {
441 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
442 if (!ready_) {
443 HIPERF_HILOGI(MODULE_CPP_API, "Client:hiperf not ready.\n");
444 return false;
445 }
446
447 int clientToServerFd[2];
448 int serverToClientFd[2];
449 if (pipe(clientToServerFd) != 0) {
450 char errInfo[ERRINFOLEN] = { 0 };
451 strerror_r(errno, errInfo, ERRINFOLEN);
452 HIPERF_HILOGI(MODULE_CPP_API, "failed to create pipe: %" HILOG_PUBLIC "s", errInfo);
453 return false;
454 } else if (pipe(serverToClientFd) != 0) {
455 char errInfo[ERRINFOLEN] = { 0 };
456 strerror_r(errno, errInfo, ERRINFOLEN);
457 HIPERF_HILOGI(MODULE_CPP_API, "failed to create pipe: %" HILOG_PUBLIC "s", errInfo);
458 close(clientToServerFd[PIPE_READ]);
459 close(clientToServerFd[PIPE_WRITE]);
460 return false;
461 }
462
463 hperfPrePid_ = fork();
464 if (hperfPrePid_ == -1) {
465 char errInfo[ERRINFOLEN] = { 0 };
466 strerror_r(errno, errInfo, ERRINFOLEN);
467 HIPERF_HILOGI(MODULE_CPP_API, "failed to fork: %" HILOG_PUBLIC "s", errInfo);
468 close(clientToServerFd[PIPE_READ]);
469 close(clientToServerFd[PIPE_WRITE]);
470 close(serverToClientFd[PIPE_READ]);
471 close(serverToClientFd[PIPE_WRITE]);
472 return false;
473 } else if (hperfPrePid_ == 0) {
474 // child process
475 close(clientToServerFd[PIPE_WRITE]);
476 close(serverToClientFd[PIPE_READ]);
477
478 std::vector<std::string> cmd;
479 GetExecCmd(cmd, clientToServerFd[PIPE_READ], serverToClientFd[PIPE_WRITE], args);
480 ChildRunExecv(cmd);
481 } else {
482 // parent process
483 close(clientToServerFd[PIPE_READ]);
484 close(serverToClientFd[PIPE_WRITE]);
485
486 clientToServerFd_ = clientToServerFd[PIPE_WRITE];
487 serverToClientFd_ = serverToClientFd[PIPE_READ];
488 }
489 using namespace std::chrono_literals;
490 if (!WaitCommandReply(2000ms)) {
491 HIPERF_HILOGI(MODULE_CPP_API, "start failed . lets kill it");
492 KillChild();
493 return false;
494 }
495 if (immediately) {
496 return StartRun();
497 }
498 return true;
499 }
500
Start(const RecordOption &option)501 bool Client::Start(const RecordOption &option)
502 {
503 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
504 if (!option.GetOutputFileName().empty()) {
505 outputFileName_ = option.GetOutputFileName();
506 }
507 if (option.IsTimeSpecified()) {
508 return RunHiperfCmdSync(option);
509 }
510 return Start(option.GetOptionVecString());
511 }
512
ChildRunExecv(std::vector<std::string> &cmd)513 void Client::ChildRunExecv(std::vector<std::string> &cmd)
514 {
515 // conver vector to array for execvp()
516 char *argv[cmd.size() + SIZE_ARGV_TAIL];
517 size_t i = 0;
518 for (i = 0; i < cmd.size(); ++i) {
519 HIPERF_HILOGI(MODULE_CPP_API, "args %" HILOG_PUBLIC "zu : %" HILOG_PUBLIC "s", i,
520 cmd[i].c_str());
521 argv[i] = cmd[i].data();
522 }
523 argv[i] = nullptr;
524
525 execv(argv[0], argv);
526
527 // never reach the following line, unless calling of execv function failed.
528 char errInfo[ERRINFOLEN] = { 0 };
529 strerror_r(errno, errInfo, ERRINFOLEN);
530 HIPERF_HILOGI(MODULE_CPP_API,
531 "failed to call exec: '%" HILOG_PUBLIC "s' %" HILOG_PUBLIC "s\n",
532 executeCommandPath_.c_str(), errInfo);
533 exit(EXIT_FAILURE); // EXIT_FAILURE 1
534 }
535
ParentWait(pid_t &wpid, pid_t pid, int &childStatus)536 bool Client::ParentWait(pid_t &wpid, pid_t pid, int &childStatus)
537 {
538 bool ret = false;
539 do {
540 // blocking...
541 int option;
542 #ifdef WCONTINUED
543 option = WUNTRACED | WCONTINUED;
544 #else
545 option = WUNTRACED;
546 #endif
547 wpid = waitpid(pid, &childStatus, option);
548 if (wpid == -1) {
549 perror("waitpid");
550 return false;
551 }
552
553 if (WIFEXITED(childStatus)) {
554 // child normally exit
555 // WEXITSTATUS(childStatus) value :
556 // true -> Calling of execv func successed, and recording finished
557 // and child will return the value of recording process's retVal
558 // false -> Calling of execv func failed, child will output errInfo
559 ret = WEXITSTATUS(childStatus) == 0 ? true : false;
560 HIPERF_HILOGI(MODULE_CPP_API,
561 "Hiperf Api Child normally exit Calling of execv : '%" HILOG_PUBLIC "s' \n",
562 ret ? "success" : "failed");
563 return ret;
564 } else if (WIFSIGNALED(childStatus)) {
565 // child was killed by SIGKILL
566 HIPERF_HILOGI(MODULE_CPP_API, "Hiperf recording process was killed by signal SIGKILL\n");
567 ret = false;
568 return ret;
569 } else if (WIFSTOPPED(childStatus)) {
570 // child was stopped by SIGSTOP, and waiting for SIGCONT
571 HIPERF_HILOGI(MODULE_CPP_API, "Hiperf recording process was stopped by signal SIGSTOP\n");
572 #ifdef WIFCONTINUED
573 } else if (WIFCONTINUED(childStatus)) {
574 // child was continued by SIGCONT
575 HIPERF_HILOGI(MODULE_CPP_API, "Hiperf recording process was continued\n by SIGCONT");
576 #endif
577 } else {
578 // non-standard case, may never happen
579 HIPERF_HILOGI(MODULE_CPP_API, "Hiperf recording process Unexpected status\n");
580 }
581 } while (!WIFEXITED(childStatus) && !WIFSIGNALED(childStatus));
582
583 // normal exit.
584 if (WIFEXITED(childStatus)) {
585 ret = WEXITSTATUS(childStatus) == HIPERF_EXIT_CODE;
586 } else {
587 // signal exit, means Hiperf recording process may occur some runtime errors.
588 HIPERF_HILOGI(MODULE_CPP_API,
589 "Hiperf recording occurs some runtime errors, end with signal : %"
590 HILOG_PUBLIC "d, exit status : %" HILOG_PUBLIC "d\n",
591 WIFSIGNALED(childStatus), WEXITSTATUS(childStatus));
592 ret = false;
593 }
594 return ret;
595 }
596
597
RunHiperfCmdSync(const RecordOption &option)598 bool Client::RunHiperfCmdSync(const RecordOption &option)
599 {
600 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
601 if (!ready_) {
602 HIPERF_HILOGI(MODULE_CPP_API, "Client:hiperf not ready.\n");
603 return false;
604 }
605 const std::vector<std::string> &args = option.GetOptionVecString();
606
607 pid_t wpid;
608 int childStatus;
609 bool ret = false;
610 hperfPid_ = fork();
611 if (hperfPid_ == -1) {
612 char errInfo[ERRINFOLEN] = { 0 };
613 strerror_r(errno, errInfo, ERRINFOLEN);
614 HIPERF_HILOGI(MODULE_CPP_API, "failed to fork: %" HILOG_PUBLIC "s", errInfo);
615 return false;
616 } else if (hperfPid_ == 0) {
617 // child execute
618 std::vector<std::string> cmd;
619 GetExecCmd(cmd, args);
620 ChildRunExecv(cmd);
621 } else {
622 ret = ParentWait(wpid, hperfPid_, childStatus);
623 }
624 return ret;
625 }
626
PrePare(const RecordOption &option)627 bool Client::PrePare(const RecordOption &option)
628 {
629 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
630 if (!option.GetOutputFileName().empty()) {
631 outputFileName_ = option.GetOutputFileName();
632 }
633 return Start(option.GetOptionVecString(), false);
634 }
635
WaitCommandReply(std::chrono::milliseconds timeOut)636 bool Client::WaitCommandReply(std::chrono::milliseconds timeOut)
637 {
638 std::string reply;
639 struct pollfd pollFd;
640 pollFd.fd = serverToClientFd_;
641 pollFd.events = POLLIN;
642 pollFd.revents = 0;
643
644 // wait some data
645 int polled = poll(&pollFd, 1, timeOut.count());
646 if (polled > 0) {
647 while (true) {
648 char c;
649 ssize_t result = TEMP_FAILURE_RETRY(read(serverToClientFd_, &c, 1));
650 if (result <= 0) {
651 HIPERF_HILOGI(MODULE_CPP_API, "read failed from pipe");
652 return false; // read fial means not ok
653 }
654
655 reply.push_back(c);
656 if (c == '\n') {
657 break;
658 }
659 }
660 } else if (polled == 0) {
661 HIPERF_HILOGI(MODULE_CPP_API, "Client:command no response %" HILOG_PUBLIC "" PRIu64 ".\n",
662 (uint64_t)timeOut.count());
663 } else {
664 HIPERF_HILOGI(MODULE_CPP_API, "Client:command poll failed.\n");
665 }
666 HIPERF_HILOGI(MODULE_CPP_API, "Client:new reply:%" HILOG_PUBLIC "s\n", reply.c_str());
667 if (reply == ReplyOK) {
668 return true;
669 } else {
670 return false;
671 }
672 }
673
KillChild()674 void Client::KillChild()
675 {
676 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
677 if (clientToServerFd_ != -1) {
678 close(clientToServerFd_);
679 clientToServerFd_ = -1;
680 }
681 if (serverToClientFd_ != -1) {
682 close(serverToClientFd_);
683 serverToClientFd_ = -1;
684 }
685 if (hperfPid_ > 0) {
686 kill(hperfPid_, SIGKILL);
687 hperfPid_ = -1;
688 }
689 if (hperfPrePid_ > 0) {
690 pid_t wpid;
691 int childStatus;
692 ParentWait(wpid, hperfPrePid_, childStatus);
693 hperfPrePid_ = -1;
694 }
695 }
696
SendCommandAndWait(const std::string &cmd)697 bool Client::SendCommandAndWait(const std::string &cmd)
698 {
699 if (clientToServerFd_ == -1) {
700 HIPERF_HILOGI(MODULE_CPP_API, "fd not ready. maybe not called start.");
701 return false;
702 }
703 size_t size = write(clientToServerFd_, cmd.c_str(), cmd.size());
704 HIPERF_HILOGI(MODULE_CPP_API,
705 "Client:%" HILOG_PUBLIC "s -> %" HILOG_PUBLIC "d : %" HILOG_PUBLIC "zd\n",
706 cmd.c_str(), clientToServerFd_, size);
707 if (size == cmd.size()) {
708 return WaitCommandReply();
709 } else {
710 return false;
711 }
712 }
713
StartRun()714 bool Client::StartRun()
715 {
716 if (!ready_) {
717 HIPERF_HILOGI(MODULE_CPP_API, "Client:hiperf not ready.\n");
718 return false;
719 }
720 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
721 if (SendCommandAndWait(ReplyStart)) {
722 return true;
723 }
724 return false;
725 }
726
Pause()727 bool Client::Pause()
728 {
729 if (!ready_) {
730 HIPERF_HILOGI(MODULE_CPP_API, "Client:hiperf not ready.\n");
731 return false;
732 }
733 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
734 if (SendCommandAndWait(ReplyPause)) {
735 return true;
736 }
737 return false;
738 }
739
Resume()740 bool Client::Resume()
741 {
742 if (!ready_) {
743 HIPERF_HILOGI(MODULE_CPP_API, "Client:hiperf not ready.\n");
744 return false;
745 }
746 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
747 if (SendCommandAndWait(ReplyResume)) {
748 return true;
749 }
750 return false;
751 }
752
Stop()753 bool Client::Stop()
754 {
755 if (!ready_) {
756 HIPERF_HILOGI(MODULE_CPP_API, "Client:hiperf not ready.\n");
757 return false;
758 }
759 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
760 if (SendCommandAndWait(ReplyStop)) {
761 // wait sampling process exit really
762 while (SendCommandAndWait(ReplyCheck)) {
763 std::this_thread::sleep_for(1s);
764 }
765 return true;
766 }
767 return false;
768 }
769
EnableHilog()770 void Client::EnableHilog()
771 {
772 HIPERF_HILOGI(MODULE_CPP_API, "Client:%" HILOG_PUBLIC "s\n", __FUNCTION__);
773 hilog_ = true;
774 }
775 } // namespace HiperfClient
776 } // namespace HiPerf
777 } // namespace Developtools
778 } // namespace OHOS
779