• 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 
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