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