• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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