• 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 
16 #define HILOG_TAG "Record"
17 
18 #include "subcommand_record.h"
19 
20 #include <csignal>
21 #include <cstdlib>
22 #include <ctime>
23 #include <memory>
24 #include <poll.h>
25 #if defined(CONFIG_HAS_SYSPARA)
26 #include <parameters.h>
27 #endif
28 #include <sys/stat.h>
29 #include <sys/utsname.h>
30 #include <unistd.h>
31 
32 #include "command.h"
33 #include "debug_logger.h"
34 #include "hiperf_client.h"
35 #if defined(is_ohos) && is_ohos
36 #include "hiperf_hilog.h"
37 #endif
38 #include "option.h"
39 #include "perf_event_record.h"
40 #include "perf_file_reader.h"
41 #include "utilities.h"
42 #include "subcommand_report.h"
43 
44 using namespace std::chrono;
45 namespace OHOS {
46 namespace Developtools {
47 namespace HiPerf {
48 const std::string CONTROL_CMD_PREPARE = "prepare";
49 const std::string CONTROL_CMD_START = "start";
50 const std::string CONTROL_CMD_PAUSE = "pause";
51 const std::string CONTROL_CMD_RESUME = "resume";
52 const std::string CONTROL_CMD_STOP = "stop";
53 const std::string CONTROL_FIFO_FILE_C2S = "/data/local/tmp/.hiperf_record_control_c2s";
54 const std::string CONTROL_FIFO_FILE_S2C = "/data/local/tmp/.hiperf_record_control_s2c";
55 
56 const std::string PERF_CPU_TIME_MAX_PERCENT = "/proc/sys/kernel/perf_cpu_time_max_percent";
57 const std::string PERF_EVENT_MAX_SAMPLE_RATE = "/proc/sys/kernel/perf_event_max_sample_rate";
58 const std::string PERF_EVENT_MLOCK_KB = "/proc/sys/kernel/perf_event_mlock_kb";
59 const std::string SCHED_SWITCH = "/sys/kernel/tracing/events/sched/sched_switch/enable";
60 const std::string SCHED_SWITCH_DEBUG = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable";
61 const std::string SCHED_SWITCH_HM = "/sys/kernel/tracing/hongmeng/events/sched/sched_switch/enable";
62 const std::string PROC_VERSION = "/proc/version";
63 const std::string SAVED_CMDLINES_SIZE = "/sys/kernel/tracing/saved_cmdlines_size";
64 
65 // when there are many events, start record will take more time.
66 const std::chrono::milliseconds CONTROL_WAITREPY_TOMEOUT = 2000ms;
67 const std::chrono::milliseconds CONTROL_WAITREPY_TOMEOUT_CHECK = 1000ms;
68 
69 constexpr uint64_t MASK_ALIGNED_8 = 7;
70 constexpr size_t MAX_DWARF_CALL_CHAIN = 2;
71 constexpr uint64_t TYPE_PERF_SAMPLE_BRANCH = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
72                                              PERF_SAMPLE_BRANCH_ANY_RETURN |
73                                              PERF_SAMPLE_BRANCH_IND_CALL;
74 
GetClockId(const std::string & name)75 int GetClockId(const std::string &name)
76 {
77     static std::map<std::string, int> mapClockid = {
78         {"realtime", CLOCK_REALTIME},   {"boottime", CLOCK_BOOTTIME},
79         {"monotonic", CLOCK_MONOTONIC}, {"monotonic_raw", CLOCK_MONOTONIC_RAW},
80         {"clock_tai", CLOCK_TAI},
81     };
82 
83     auto it = mapClockid.find(name);
84     if (it == mapClockid.end()) {
85         return -1;
86     } else {
87         return it->second;
88     }
89 }
90 
GetBranchSampleType(const std::string & name)91 uint64_t GetBranchSampleType(const std::string &name)
92 {
93     static std::map<std::string, uint64_t> mapBranchSampleType = {
94         {"u", PERF_SAMPLE_BRANCH_USER},
95         {"k", PERF_SAMPLE_BRANCH_KERNEL},
96         {"any", PERF_SAMPLE_BRANCH_ANY},
97         {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL},
98         {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN},
99         {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
100     };
101 
102     auto it = mapBranchSampleType.find(name);
103     if (it == mapBranchSampleType.end()) {
104         return 0;
105     } else {
106         return it->second;
107     }
108 }
109 
~SubCommandRecord()110 SubCommandRecord::~SubCommandRecord()
111 {
112     CloseClientThread();
113 }
114 
DumpOptions() const115 void SubCommandRecord::DumpOptions() const
116 {
117     printf("DumpOptions:\n");
118     printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
119     printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
120     printf(" timeStopSec:\t%f sec\n", timeStopSec_);
121     printf(" frequency:\t%d\n", frequency_);
122     printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
123     int i = 0;
124     for (auto &group : selectGroups_) {
125         i++;
126         printf(" selectGroups:\t%2d:%s\n", i, VectorToString(group).c_str());
127     }
128     printf(" no_inherit:\t%s\n", noInherit_ ? "true" : "false");
129     printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
130     printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
131     printf(" excludeTids:\t%s\n", VectorToString(excludeTids_).c_str());
132     printf(" excludeThreads:\t%s\n", VectorToString(excludeThreadNames_).c_str());
133     printf(" kernelCallChain:\t%s\n", kernelCallChain_ ? "true" : "false");
134     printf(" callChainUserOnly_:\t%s\n", callChainUserOnly_ ? "true" : "false");
135     printf(" restart:\t%s\n", restart_ ? "true" : "false");
136     printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
137     printf(" excludePerf:\t%d\n", excludeHiperf_);
138     printf(" cpuPercent:\t%d\n", cpuPercent_);
139     printf(" offCPU_:\t%d\n", offCPU_);
140     printf(" delayUnwind_:\t%d\n", delayUnwind_);
141     printf(" disableUnwind_:\t%d\n", disableUnwind_);
142     printf(" disableCallstackExpend_:\t%d\n", disableCallstackExpend_);
143     printf(" enableDebugInfoSymbolic:\t%d\n", enableDebugInfoSymbolic_);
144     printf(" symbolDir_:\t%s\n", VectorToString(symbolDir_).c_str());
145     printf(" outputFilename_:\t%s\n", outputFilename_.c_str());
146     printf(" appPackage_:\t%s\n", appPackage_.c_str());
147     printf(" checkAppMs_:\t%d\n", checkAppMs_);
148     printf(" clockId_:\t%s\n", clockId_.c_str());
149     printf(" mmapPages_:\t%d\n", mmapPages_);
150     printf(" dataLimit:\t%s\n", strLimit_.c_str());
151     printf(" callStack:\t%s\n", VectorToString(callStackType_).c_str());
152     printf(" branchSampleTypes:\t%s\n", VectorToString(vecBranchFilters_).c_str());
153     printf(" trackedCommand:\t%s\n", VectorToString(trackedCommand_).c_str());
154     printf(" pipe_input:\t%d\n", clientPipeInput_);
155     printf(" pipe_output:\t%d\n", clientPipeOutput_);
156     printf(" cmdlinesSize_:\t%d\n", cmdlinesSize_);
157     printf(" report_:\t%s\n", report_ ? "true" : "false");
158 }
159 
GetOptions(std::vector<std::string> & args)160 bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
161 {
162     if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
163         return false;
164     }
165     if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
166         HLOGD("-a option needs root privilege for system wide profiling.");
167         printf("-a option needs root privilege for system wide profiling.\n");
168         return false;
169     }
170     if (!Option::GetOptionValue(args, "--exclude-hiperf", excludeHiperf_)) {
171         return false;
172     }
173     if (!Option::GetOptionValue(args, "-z", compressData_)) {
174         return false;
175     }
176     if (!Option::GetOptionValue(args, "--no-inherit", noInherit_)) {
177         return false;
178     }
179     if (!Option::GetOptionValue(args, "--offcpu", offCPU_)) {
180         return false;
181     }
182     if (!Option::GetOptionValue(args, "--delay-unwind", delayUnwind_)) {
183         return false;
184     }
185     if (!Option::GetOptionValue(args, "--disable-unwind", disableUnwind_)) {
186         return false;
187     }
188     if (!Option::GetOptionValue(args, "--disable-callstack-expand", disableCallstackExpend_)) {
189         return false;
190     }
191     if (!Option::GetOptionValue(args, "--enable-debuginfo-symbolic", enableDebugInfoSymbolic_)) {
192         return false;
193     }
194     if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
195         return false;
196     }
197     if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
198         return false;
199     }
200     if (!GetOptionFrequencyAndPeriod(args)) {
201         return false;
202     }
203     if (!Option::GetOptionValue(args, "--cpu-limit", cpuPercent_)) {
204         return false;
205     }
206     if (!Option::GetOptionValue(args, "-m", mmapPages_)) {
207         return false;
208     }
209     if (!Option::GetOptionValue(args, "--symbol-dir", symbolDir_)) {
210         return false;
211     }
212     if (!Option::GetOptionValue(args, "-o", outputFilename_)) {
213         return false;
214     }
215     if (!Option::GetOptionValue(args, "--app", appPackage_)) {
216         return false;
217     }
218     if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
219         return false;
220     }
221     if (!Option::GetOptionValue(args, "--clockid", clockId_)) {
222         return false;
223     }
224     if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
225         return false;
226     }
227     if (!Option::GetOptionValue(args, "-p", selectPids_)) {
228         return false;
229     }
230     if (!Option::GetOptionValue(args, "-t", selectTids_)) {
231         return false;
232     }
233     if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
234         return false;
235     }
236     if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
237         return false;
238     }
239     if (!Option::GetOptionValue(args, "-s", callStackType_)) {
240         return false;
241     }
242     if (!Option::GetOptionValue(args, "--kernel-callchain", kernelCallChain_)) {
243         return false;
244     }
245     if (!Option::GetOptionValue(args, "--callchain-useronly", callChainUserOnly_)) {
246         return false;
247     }
248     if (!Option::GetOptionValue(args, "--exclude-tid", excludeTids_)) {
249         return false;
250     }
251     if (!Option::GetOptionValue(args, "--exclude-thread", excludeThreadNames_)) {
252         return false;
253     }
254     if (!Option::GetOptionValue(args, "--restart", restart_)) {
255         return false;
256     }
257     std::vector<std::string> callStackType = {};
258     if (!Option::GetOptionValue(args, "--call-stack", callStackType)) {
259         return false;
260     }
261     if (!callStackType_.empty()) {
262         if (!callStackType.empty()) {
263             printf("'-s %s --call-stack %s' option usage error, please check usage.\n",
264                 VectorToString(callStackType_).c_str(), VectorToString(callStackType).c_str());
265             return false;
266         }
267     } else {
268         callStackType_ = callStackType;
269     }
270     if (!Option::GetOptionValue(args, "--data-limit", strLimit_)) {
271         return false;
272     }
273     if (!Option::GetOptionValue(args, "-j", vecBranchFilters_)) {
274         return false;
275     }
276     if (!Option::GetOptionValue(args, "--pipe_input", clientPipeInput_)) {
277         return false;
278     }
279     if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) {
280         return false;
281     }
282     if (!Option::GetOptionValue(args, "--control", controlCmd_)) {
283         return false;
284     }
285     if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) {
286         return false;
287     }
288     if (!Option::GetOptionValue(args, "--cmdline-size", cmdlinesSize_)) {
289         return false;
290     }
291     if (!Option::GetOptionValue(args, "--report", report_)) {
292         return false;
293     }
294     if (targetSystemWide_ && dedupStack_) {
295         printf("-a option is conflict with --dedup_stack.\n");
296         return false;
297     }
298     if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) {
299         return false;
300     }
301     if (!args.empty()) {
302         printf("'%s' option usage error, please check usage.\n", VectorToString(args).c_str());
303         return false;
304     }
305     return true;
306 }
307 
GetOptionFrequencyAndPeriod(std::vector<std::string> & args)308 bool SubCommandRecord::GetOptionFrequencyAndPeriod(std::vector<std::string> &args)
309 {
310     if (Option::FindOption(args, "-f") != args.end()) {
311         if (!Option::GetOptionValue(args, "-f", frequency_)) {
312             return false;
313         }
314         if (frequency_ < MIN_SAMPLE_FREQUENCY || frequency_ > MAX_SAMPLE_FREQUENCY) {
315             printf("Invalid -f value '%d', frequency should be in %d~%d \n", frequency_,
316                    MIN_SAMPLE_FREQUENCY, MAX_SAMPLE_FREQUENCY);
317             return false;
318         }
319     }
320     if (Option::FindOption(args, "--period") != args.end()) {
321         if (frequency_ != 0) {
322             printf("option -f and --period is conflict, please check usage\n");
323             return false;
324         }
325         if (!Option::GetOptionValue(args, "--period", period_)) {
326             return false;
327         }
328         if (period_ <= 0) {
329             printf("Invalid --period value '%d', period should be greater than 0\n", period_);
330             return false;
331         }
332     }
333     return true;
334 }
335 
CheckDataLimitOption()336 bool SubCommandRecord::CheckDataLimitOption()
337 {
338     if (!strLimit_.empty()) {
339         if (!ParseDataLimitOption(strLimit_)) {
340             printf("Invalid --data-limit value %s\n", strLimit_.c_str());
341             return false;
342         }
343     }
344     return true;
345 }
346 
CheckSelectCpuPidOption()347 bool SubCommandRecord::CheckSelectCpuPidOption()
348 {
349     if (!selectCpus_.empty()) {
350         int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1;
351         for (auto cpu : selectCpus_) {
352             if (cpu < 0 || cpu > maxCpuid) {
353                 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
354                 return false;
355             }
356         }
357     }
358 
359     if (!selectPids_.empty()) {
360         for (auto pid : selectPids_) {
361             if (pid <= 0) {
362                 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
363                 return false;
364             }
365         }
366     }
367     if (!selectTids_.empty()) {
368         for (auto tid : selectTids_) {
369             if (tid <= 0) {
370                 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
371                 return false;
372             }
373         }
374     }
375     return true;
376 }
377 
CheckArgsRange()378 bool SubCommandRecord::CheckArgsRange()
379 {
380     if (timeStopSec_ < MIN_STOP_SECONDS || timeStopSec_ > MAX_STOP_SECONDS) {
381         printf("Invalid -d value '%.3f', the seconds should be in %.3f~%.3f  \n", timeStopSec_,
382                MIN_STOP_SECONDS, MAX_STOP_SECONDS);
383         return false;
384     }
385     if (cpuPercent_ < MIN_CPU_PERCENT || cpuPercent_ > MAX_CPU_PERCENT) {
386         printf("Invalid --cpu-limit value '%d', CPU percent should be in %d~%d \n", cpuPercent_,
387                MIN_CPU_PERCENT, MAX_CPU_PERCENT);
388         return false;
389     }
390     if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
391         printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
392                MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
393         return false;
394     }
395     if (mmapPages_ < MIN_PERF_MMAP_PAGE || mmapPages_ > MAX_PERF_MMAP_PAGE ||
396         !PowerOfTwo(mmapPages_)) {
397         printf("Invalid -m value '%d', value should be in %d~%d and must be a power of two \n",
398                mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE);
399         return false;
400     }
401     if (cmdlinesSize_ < MIN_SAVED_CMDLINES_SIZE || cmdlinesSize_ > MAX_SAVED_CMDLINES_SIZE ||
402         !PowerOfTwo(cmdlinesSize_)) {
403         printf("Invalid --cmdline-size value '%d', value should be in %d~%d and must be a power of two \n",
404                cmdlinesSize_, MIN_SAVED_CMDLINES_SIZE, MAX_SAVED_CMDLINES_SIZE);
405         return false;
406     }
407     if (!clockId_.empty() && GetClockId(clockId_) == -1) {
408         printf("Invalid --clockid value %s\n", clockId_.c_str());
409         return false;
410     }
411     if (!targetSystemWide_ && excludeHiperf_) {
412         printf("--exclude-hiperf must be used with -a\n");
413         return false;
414     }
415     return true;
416 }
417 
CheckOptions()418 bool SubCommandRecord::CheckOptions()
419 {
420     if (!CheckArgsRange()) {
421         return false;
422     }
423     if (!CheckDataLimitOption()) {
424         return false;
425     }
426     if (!ParseCallStackOption(callStackType_)) {
427         return false;
428     }
429     if (!ParseBranchSampleType(vecBranchFilters_)) {
430         return false;
431     }
432     if (!CheckSelectCpuPidOption()) {
433         return false;
434     }
435     if (!ParseControlCmd(controlCmd_)) {
436         return false;
437     }
438     if (!CheckTargetProcessOptions()) {
439         return false;
440     }
441     if (!CheckReportOption()) {
442         return false;
443     }
444     return true;
445 }
446 
ParseOption(std::vector<std::string> & args)447 bool SubCommandRecord::ParseOption(std::vector<std::string> &args)
448 {
449     if (!GetOptions(args)) {
450         return false;
451     }
452     if (!args.empty()) {
453         printf("unknown option %s\n", args.begin()->c_str());
454         return false;
455     }
456     if (controlCmd_.empty()) {
457         if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
458             return false;
459         }
460     }
461     return CheckOptions();
462 }
463 
CheckTargetProcessOptions()464 bool SubCommandRecord::CheckTargetProcessOptions()
465 {
466     bool hasTarget = false;
467     if (targetSystemWide_) {
468         hasTarget = true;
469     }
470     if (!selectPids_.empty() || !selectTids_.empty()) {
471         if (hasTarget) {
472             printf("-p/-t %s options conflict, please check usage\n",
473                    VectorToString(selectPids_).c_str());
474             return false;
475         }
476         hasTarget = true;
477     }
478     if (!trackedCommand_.empty()) {
479         if (hasTarget) {
480             printf("%s options conflict, please check usage\n",
481                    VectorToString(trackedCommand_).c_str());
482             return false;
483         }
484         hasTarget = true;
485     }
486     if (appPackage_ != "") {
487         if (hasTarget) {
488             printf("--app %s options conflict, please check usage\n", appPackage_.c_str());
489             return false;
490         }
491         hasTarget = true;
492     }
493     if (!hasTarget and (controlCmd_.empty() or controlCmd_ == CONTROL_CMD_PREPARE)) {
494         printf("please select a target process\n");
495         return false;
496     }
497     if (controlCmd_ == CONTROL_CMD_PREPARE) {
498         if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
499             return false;
500         }
501     }
502     return CheckTargetPids();
503 }
504 
CheckTargetPids()505 bool SubCommandRecord::CheckTargetPids()
506 {
507     for (auto pid : selectPids_) {
508         if (!IsDir("/proc/" + std::to_string(pid))) {
509             printf("not exist pid %d\n", pid);
510             return false;
511         }
512     }
513     for (auto tid : selectTids_) {
514         if (!IsDir("/proc/" + std::to_string(tid))) {
515             printf("not exist tid %d\n", tid);
516             return false;
517         }
518     }
519     if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) {
520         return false;
521     }
522     if (!selectPids_.empty()) {
523         for (auto pid : selectPids_) {
524             auto tids = GetSubthreadIDs(pid);
525             if (!tids.empty()) {
526                 selectTids_.insert(selectTids_.end(), tids.begin(), tids.end());
527                 mapPids_[pid] = tids;
528             }
529         }
530     }
531     if (!SubCommand::HandleSubCommandExclude(excludeTids_, excludeThreadNames_, selectTids_)) {
532         return false;
533     }
534     selectPids_.insert(selectPids_.end(), selectTids_.begin(), selectTids_.end());
535 
536     return true;
537 }
538 
CheckReportOption()539 bool SubCommandRecord::CheckReportOption()
540 {
541     if (targetSystemWide_ && report_) {
542         printf("--report options conflict, please check usage\n");
543         return false;
544     }
545     return true;
546 }
547 
ParseDataLimitOption(const std::string & str)548 bool SubCommandRecord::ParseDataLimitOption(const std::string &str)
549 {
550     uint unit = 1;
551     char c = str.at(str.size() >= 1 ? str.size() - 1 : 0);
552     if (c == 'K' or c == 'k') {
553         unit = KILO;
554     } else if (c == 'm' or c == 'M') {
555         unit = KILO * KILO;
556     } else if (c == 'g' or c == 'G') {
557         unit = KILO * KILO * KILO;
558     } else {
559         return false;
560     }
561 
562     std::string num = str.substr(0, str.size() >= 1 ? str.size() - 1 : 0);
563     int64_t size = 0;
564     try {
565         size = std::stoul(num);
566     } catch (...) {
567         return false;
568     }
569     if (size <= 0) {
570         return false;
571     }
572 
573     dataSizeLimit_ = size * unit;
574 
575     return true;
576 }
577 
ParseCallStackOption(const std::vector<std::string> & callStackType)578 bool SubCommandRecord::ParseCallStackOption(const std::vector<std::string> &callStackType)
579 {
580     if (callStackType.empty()) {
581         return true;
582     } else if (callStackType[0] == "fp") {
583         if (callStackType.size() != 1) {
584             printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str());
585             return false;
586         }
587         isCallStackFp_ = true;
588     } else if (callStackType[0] == "dwarf") {
589         if (callStackType.size() > MAX_DWARF_CALL_CHAIN) {
590             printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str());
591             return false;
592         } else if (callStackType.size() == MAX_DWARF_CALL_CHAIN) {
593             try {
594                 callStackDwarfSize_ = std::stoul(callStackType.at(1));
595             } catch (...) {
596                 printf("Invalid -s value, dwarf stack size, '%s' is illegal.\n",
597                        callStackType.at(1).c_str());
598                 return false;
599             }
600             if (callStackDwarfSize_ < MIN_SAMPLE_STACK_SIZE) {
601                 printf("Invalid -s value, dwarf stack size, '%s' is too small.\n",
602                        callStackType.at(1).c_str());
603                 return false;
604             }
605             if (callStackDwarfSize_ > MAX_SAMPLE_STACK_SIZE) {
606                 printf("Invalid -s value, dwarf stack size, '%s' is bigger than max value %u.\n",
607                        callStackType.at(1).c_str(), MAX_SAMPLE_STACK_SIZE);
608                 return false;
609             }
610             if ((callStackDwarfSize_ & MASK_ALIGNED_8) != 0) {
611                 printf("Invalid -s value, dwarf stack size, '%s' is not 8 byte aligned.\n",
612                        callStackType.at(1).c_str());
613                 return false;
614             }
615         }
616         isCallStackDwarf_ = true;
617         SymbolsFile::needParseJsFunc_ = true; // only in record and dwarf mode need to parse
618     } else {
619         printf("Invalid -s value '%s'.\n", callStackType.at(0).c_str());
620         return false;
621     }
622     return true;
623 }
624 
ParseBranchSampleType(const std::vector<std::string> & vecBranchSampleTypes)625 bool SubCommandRecord::ParseBranchSampleType(const std::vector<std::string> &vecBranchSampleTypes)
626 {
627     if (!vecBranchSampleTypes.empty()) {
628         for (auto &item : vecBranchSampleTypes) {
629             uint64_t type = GetBranchSampleType(item);
630             if (type != 0) {
631                 branchSampleType_ |= type;
632             } else {
633                 printf("Invalid -j value '%s'\n", item.c_str());
634                 return false;
635             }
636         }
637         if ((branchSampleType_ & TYPE_PERF_SAMPLE_BRANCH) == 0) {
638             printf(
639                 "Invalid -j value, requires at least one of any, any_call, any_ret, ind_call.\n");
640             return false;
641         }
642     }
643     return true;
644 }
645 
ParseControlCmd(const std::string cmd)646 bool SubCommandRecord::ParseControlCmd(const std::string cmd)
647 {
648     if (cmd.empty() or cmd == CONTROL_CMD_PREPARE or cmd == CONTROL_CMD_START or
649         cmd == CONTROL_CMD_PAUSE or cmd == CONTROL_CMD_RESUME or cmd == CONTROL_CMD_STOP) {
650         return true;
651     }
652 
653     printf("Invalid --control %s option, command should be: prepare, start, pause, resume, stop.\n",
654            cmd.c_str());
655     return false;
656 }
657 
SetPerfLimit(const std::string & file,int value,std::function<bool (int,int)> const & cmp,const std::string & param)658 bool SubCommandRecord::SetPerfLimit(const std::string& file, int value, std::function<bool (int, int)> const& cmp,
659     const std::string& param)
660 {
661     int oldValue = 0;
662     if (!ReadIntFromProcFile(file, oldValue)) {
663         printf("read %s fail.\n", file.c_str());
664         return false;
665     }
666 
667     if (cmp(oldValue, value)) {
668         HLOGI("cmp return true.");
669         return true;
670     }
671 
672     if (IsRoot()) {
673         bool ret = WriteIntToProcFile(file, value);
674         if (!ret) {
675             printf("please set %s to %d manually if perf limit is wanted.\n", file.c_str(), value);
676         }
677     }
678 
679     if (!OHOS::system::SetParameter(param, std::to_string(value))) {
680         printf("set parameter %s fail.\n", param.c_str());
681         return false;
682     }
683     isNeedSetPerfHarden_ = true;
684     return true;
685 }
686 
SetPerfCpuMaxPercent()687 bool SubCommandRecord::SetPerfCpuMaxPercent()
688 {
689     auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
690     return SetPerfLimit(PERF_CPU_TIME_MAX_PERCENT, cpuPercent_, cmp, "hiviewdfx.hiperf.perf_cpu_time_max_percent");
691 }
692 
SetPerfMaxSampleRate()693 bool SubCommandRecord::SetPerfMaxSampleRate()
694 {
695     auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
696     int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY;
697     int maxRate = 0;
698     if (!ReadIntFromProcFile(PERF_EVENT_MAX_SAMPLE_RATE, maxRate)) {
699         printf("read %s fail.\n", PERF_EVENT_MAX_SAMPLE_RATE.c_str());
700         return false;
701     }
702     if (maxRate > frequency) {
703         return true;
704     }
705     int newRate = frequency > static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE) ? frequency :
706                   static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE);
707     return SetPerfLimit(PERF_EVENT_MAX_SAMPLE_RATE, newRate, cmp,
708                         "hiviewdfx.hiperf.perf_event_max_sample_rate");
709 }
710 
SetPerfEventMlock()711 bool SubCommandRecord::SetPerfEventMlock()
712 {
713     auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
714     int mlock_kb = sysconf(_SC_NPROCESSORS_CONF) * (mmapPages_ + 1) * 4;
715     return SetPerfLimit(PERF_EVENT_MLOCK_KB, mlock_kb, cmp, "hiviewdfx.hiperf.perf_event_mlock_kb");
716 }
717 
SetPerfHarden()718 bool SubCommandRecord::SetPerfHarden()
719 {
720     if (!isNeedSetPerfHarden_) {
721         return true;
722     }
723 
724     std::string perfHarden = OHOS::system::GetParameter(PERF_DISABLE_PARAM, "1");
725     if (perfHarden == "1") {
726         if (!OHOS::system::SetParameter(PERF_DISABLE_PARAM, "0")) {
727             printf("set parameter security.perf_harden to 0 fail.");
728             return false;
729         }
730     }
731 
732     if (!OHOS::system::SetParameter(PERF_DISABLE_PARAM, "1")) {
733         printf("set parameter security.perf_harden to 1 fail.");
734         return false;
735     }
736     return true;
737 }
738 
TraceOffCpu()739 bool SubCommandRecord::TraceOffCpu()
740 {
741     // whether system support sched_switch event
742     int enable = -1;
743     std::string node = SCHED_SWITCH;
744     if (isHM_) {
745         node = SCHED_SWITCH_HM;
746     }
747     const std::string nodeDebug = SCHED_SWITCH_DEBUG;
748     if (!ReadIntFromProcFile(node.c_str(), enable) and
749         !ReadIntFromProcFile(nodeDebug.c_str(), enable)) {
750         printf("Cannot trace off CPU, event sched:sched_switch is not available (%s or %s)\n",
751             node.c_str(), nodeDebug.c_str());
752         return false;
753     }
754 
755     return true;
756 }
757 
SetSavedCmdlinesSize()758 void SubCommandRecord::SetSavedCmdlinesSize()
759 {
760     if (!ReadIntFromProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) {
761         printf("Failed to read from %s.\n", SAVED_CMDLINES_SIZE.c_str());
762     }
763     if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, cmdlinesSize_)) {
764         printf("Failed to write size:%d to %s.\n", cmdlinesSize_, SAVED_CMDLINES_SIZE.c_str());
765     }
766 }
767 
RecoverSavedCmdlinesSize()768 void SubCommandRecord::RecoverSavedCmdlinesSize()
769 {
770     if (oldCmdlinesSize_ == 0) {
771         return;
772     }
773     if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) {
774         printf("Failed to recover value of %s.\n", SAVED_CMDLINES_SIZE.c_str());
775     }
776 }
777 
PreparePerfEvent()778 bool SubCommandRecord::PreparePerfEvent()
779 {
780     // we need to notify perfEvents_ sampling mode by SetRecordCallBack first
781     auto processRecord = std::bind(&SubCommandRecord::ProcessRecord, this, std::placeholders::_1);
782     perfEvents_.SetRecordCallBack(processRecord);
783 
784     perfEvents_.SetCpu(selectCpus_);
785     perfEvents_.SetPid(selectPids_); // Tids has insert Pids in CheckTargetProcessOptions()
786 
787     perfEvents_.SetSystemTarget(targetSystemWide_);
788     perfEvents_.SetTimeOut(timeStopSec_);
789     perfEvents_.SetVerboseReport(verboseReport_);
790     perfEvents_.SetMmapPages(mmapPages_);
791     if (isCallStackFp_) {
792         perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::FP);
793     } else if (isCallStackDwarf_) {
794         perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::DWARF);
795         perfEvents_.SetDwarfSampleStackSize(callStackDwarfSize_);
796     }
797     if (!perfEvents_.SetBranchSampleType(branchSampleType_)) {
798         printf("branch sample %s is not supported\n", VectorToString(vecBranchFilters_).c_str());
799         HLOGE("Fail to SetBranchSampleType %" PRIx64 "", branchSampleType_);
800         return false;
801     }
802     if (!clockId_.empty()) {
803         perfEvents_.SetClockId(GetClockId(clockId_));
804     }
805 
806     if (frequency_ > 0) {
807         perfEvents_.SetSampleFrequency(frequency_);
808     } else if (period_ > 0) {
809         perfEvents_.SetSamplePeriod(period_);
810     }
811 
812     perfEvents_.SetInherit(!noInherit_);
813     perfEvents_.SetTrackedCommand(trackedCommand_);
814 
815     // set default sample event
816     if (selectEvents_.empty() && selectGroups_.empty()) {
817         selectEvents_.push_back("hw-cpu-cycles");
818     }
819 
820     if (!perfEvents_.AddEvents(selectEvents_)) {
821         HLOGE("Fail to AddEvents events");
822         return false;
823     }
824     for (auto &group : selectGroups_) {
825         if (!perfEvents_.AddEvents(group, true)) {
826             HLOGE("Fail to AddEvents groups");
827             return false;
828         }
829     }
830     // cpu off add after default event (we need both sched_switch and user selected events)
831     if (offCPU_) {
832         if (std::find(selectEvents_.begin(), selectEvents_.end(), "sched_switch") !=
833             selectEvents_.end()) {
834             printf("--offcpu is not supported event sched_switch\n");
835             return false;
836         }
837         // insert a sched_switch event to trace offcpu event
838         if (!perfEvents_.AddOffCpuEvent()) {
839             HLOGE("Fail to AddEOffCpuvent");
840             return false;
841         }
842     }
843 
844     return true;
845 }
846 
PrepareSysKernel()847 bool SubCommandRecord::PrepareSysKernel()
848 {
849     SetHM();
850     SetSavedCmdlinesSize();
851     if (!SetPerfMaxSampleRate()) {
852         HLOGE("Fail to call SetPerfMaxSampleRate(%d)", frequency_);
853         return false;
854     }
855     if (!SetPerfCpuMaxPercent()) {
856         HLOGE("Fail to set perf event cpu limit to %d\n", cpuPercent_);
857         return false;
858     }
859 
860     if (!SetPerfEventMlock()) {
861         HLOGE("Fail to set perf event mlock limit\n");
862         return false;
863     }
864 
865     if (!SetPerfHarden()) {
866         HLOGE("Fail to set perf event harden\n");
867         return false;
868     }
869 
870     if (offCPU_ && !TraceOffCpu()) {
871         HLOGE("Fail to TraceOffCpu");
872         return false;
873     }
874 
875     return true;
876 }
877 
PrepareVirtualRuntime()878 bool SubCommandRecord::PrepareVirtualRuntime()
879 {
880     auto saveRecord = std::bind(&SubCommandRecord::SaveRecord, this, std::placeholders::_1, false);
881     virtualRuntime_.SetRecordMode(saveRecord);
882 
883     // do some config for virtualRuntime_
884     virtualRuntime_.SetCallStackExpend(disableCallstackExpend_ ? 0 : 1);
885     // these is same for virtual runtime
886     virtualRuntime_.SetDisableUnwind(disableUnwind_ or delayUnwind_);
887     virtualRuntime_.EnableDebugInfoSymbolic(enableDebugInfoSymbolic_);
888     if (!symbolDir_.empty()) {
889         if (!virtualRuntime_.SetSymbolsPaths(symbolDir_)) {
890             printf("Failed to set symbol path(%s)\n", VectorToString(symbolDir_).c_str());
891             return false;
892         }
893     }
894 
895     // load vsdo first
896     virtualRuntime_.LoadVdso();
897 
898     if (!callChainUserOnly_) {
899         // prepare from kernel and ko
900         virtualRuntime_.SetNeedKernelCallChain(!callChainUserOnly_);
901         virtualRuntime_.UpdateKernelSpaceMaps();
902         virtualRuntime_.UpdateKernelModulesSpaceMaps();
903         if (isHM_) {
904             virtualRuntime_.UpdateServiceSpaceMaps();
905         }
906     }
907 
908     if (isHM_) {
909         virtualRuntime_.UpdateDevhostSpaceMaps();
910     }
911     if (dedupStack_) {
912         virtualRuntime_.SetDedupStack();
913         auto collectSymbol =
914             std::bind(&SubCommandRecord::CollectSymbol, this, std::placeholders::_1);
915         virtualRuntime_.SetCollectSymbolCallBack(collectSymbol);
916     }
917     return true;
918 }
919 
WriteCommEventBeforeSampling()920 void SubCommandRecord::WriteCommEventBeforeSampling()
921 {
922     for (auto it = mapPids_.begin(); it != mapPids_.end(); ++it) {
923         virtualRuntime_.GetThread(it->first, it->first);
924         for (auto tid : it->second) {
925             virtualRuntime_.GetThread(it->first, tid);
926         }
927     }
928 }
929 
ClientCommandResponse(bool OK)930 bool SubCommandRecord::ClientCommandResponse(bool OK)
931 {
932     using namespace HiperfClient;
933     if (OK) {
934         size_t size = write(clientPipeOutput_, ReplyOK.c_str(), ReplyOK.size());
935         if (size != ReplyOK.size()) {
936             char errInfo[ERRINFOLEN] = { 0 };
937             strerror_r(errno, errInfo, ERRINFOLEN);
938             HLOGD("Server:%s -> %d : %zd %d:%s", ReplyOK.c_str(), clientPipeOutput_, size, errno,
939                   errInfo);
940             return false;
941         }
942         return true;
943     } else {
944         size_t size = write(clientPipeOutput_, ReplyFAIL.c_str(), ReplyFAIL.size());
945         if (size != ReplyFAIL.size()) {
946             char errInfo[ERRINFOLEN] = { 0 };
947             strerror_r(errno, errInfo, ERRINFOLEN);
948             HLOGD("Server:%s -> %d : %zd %d:%s", ReplyFAIL.c_str(), clientPipeOutput_, size, errno,
949                   errInfo);
950             return false;
951         }
952         return true;
953     }
954 }
955 
IsSamplingRunning()956 bool SubCommandRecord::IsSamplingRunning()
957 {
958     constexpr int maxWaitTrackingCount = 3000 / 100; // wait 3 second
959     int waitTrackingCount = maxWaitTrackingCount;
960     while (!perfEvents_.IsTrackRunning()) {
961         waitTrackingCount--;
962         if (waitTrackingCount <= 0) {
963             return false;
964         }
965         constexpr uint64_t waitTrackingSleepMs = 100;
966         std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs));
967     }
968     return true;
969 }
970 
ClientCommandHandle()971 void SubCommandRecord::ClientCommandHandle()
972 {
973     using namespace HiperfClient;
974     if (!IsSamplingRunning()) {
975         return;
976     }
977     // tell the caller if Exist
978     ClientCommandResponse(true);
979 
980     bool hasRead = true;
981     while (!clientExit_) {
982         if (isFifoServer_ && hasRead) {
983             if (clientPipeInput_ != -1) {
984                 // after read(), block is disabled, the poll will be waked neven if no data
985                 close(clientPipeInput_);
986             }
987             clientPipeInput_ = open(CONTROL_FIFO_FILE_C2S.c_str(), O_RDONLY | O_NONBLOCK);
988         }
989         struct pollfd pollFd {
990             clientPipeInput_, POLLIN, 0
991         };
992         int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TOMEOUT.count());
993         if (polled <= 0) {
994             hasRead = false;
995             continue;
996         }
997         hasRead = true;
998         std::string command;
999         while (true) {
1000             char c;
1001             ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1));
1002             if (result <= 0) {
1003                 HLOGD("server :read from pipe file failed");
1004                 break;
1005             }
1006             command.push_back(c);
1007             if (c == '\n') {
1008                 break;
1009             }
1010         }
1011         HLOGD("server:new command %s", command.c_str());
1012         if (command == ReplyStart) {
1013             ClientCommandResponse(perfEvents_.EnableTracking());
1014         } else if (command == ReplyCheck) {
1015             ClientCommandResponse(!clientExit_);
1016         } else if (command == ReplyStop) {
1017             ClientCommandResponse(perfEvents_.StopTracking());
1018         } else if (command == ReplyPause) {
1019             ClientCommandResponse(perfEvents_.PauseTracking());
1020         } else if (command == ReplyResume) {
1021             ClientCommandResponse(perfEvents_.ResumeTracking());
1022         }
1023     }
1024 }
1025 
ProcessControl()1026 bool SubCommandRecord::ProcessControl()
1027 {
1028     if (controlCmd_.empty()) {
1029         return true;
1030     }
1031 
1032     if (controlCmd_ == CONTROL_CMD_PREPARE) {
1033         if (!CreateFifoServer()) {
1034             return false;
1035         }
1036         return true;
1037     }
1038 
1039     isFifoClient_ = true;
1040     bool ret = false;
1041     if (controlCmd_ == CONTROL_CMD_START) {
1042         ret = SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TOMEOUT);
1043     } else if (controlCmd_ == CONTROL_CMD_RESUME) {
1044         ret = SendFifoAndWaitReply(HiperfClient::ReplyResume, CONTROL_WAITREPY_TOMEOUT);
1045     } else if (controlCmd_ == CONTROL_CMD_PAUSE) {
1046         ret = SendFifoAndWaitReply(HiperfClient::ReplyPause, CONTROL_WAITREPY_TOMEOUT);
1047     } else if (controlCmd_ == CONTROL_CMD_STOP) {
1048         ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TOMEOUT);
1049         if (ret) {
1050             // wait sampling process exit really
1051             static constexpr uint64_t waitCheckSleepMs = 200;
1052             std::this_thread::sleep_for(milliseconds(waitCheckSleepMs));
1053             while (SendFifoAndWaitReply(HiperfClient::ReplyCheck, CONTROL_WAITREPY_TOMEOUT_CHECK)) {
1054                 std::this_thread::sleep_for(milliseconds(waitCheckSleepMs));
1055             }
1056             HLOGI("wait reply check end.");
1057         }
1058         remove(CONTROL_FIFO_FILE_C2S.c_str());
1059         remove(CONTROL_FIFO_FILE_S2C.c_str());
1060     }
1061 
1062     if (ret) {
1063         printf("%s sampling success.\n", controlCmd_.c_str());
1064     } else {
1065         printf("%s sampling failed.\n", controlCmd_.c_str());
1066     }
1067     return ret;
1068 }
1069 
CreateFifoServer()1070 bool SubCommandRecord::CreateFifoServer()
1071 {
1072     char errInfo[ERRINFOLEN] = { 0 };
1073     const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1074     if (mkfifo(CONTROL_FIFO_FILE_S2C.c_str(), fifoMode) != 0 or
1075         mkfifo(CONTROL_FIFO_FILE_C2S.c_str(), fifoMode) != 0) {
1076         if (errno == EEXIST) {
1077             printf("another sampling service is running.\n");
1078         } else {
1079             remove(CONTROL_FIFO_FILE_S2C.c_str());
1080             remove(CONTROL_FIFO_FILE_C2S.c_str());
1081         }
1082         strerror_r(errno, errInfo, ERRINFOLEN);
1083         HLOGE("create fifo file failed. %d:%s", errno, errInfo);
1084         return false;
1085     }
1086 
1087     pid_t pid = fork();
1088     if (pid == -1) {
1089         strerror_r(errno, errInfo, ERRINFOLEN);
1090         HLOGE("fork failed. %d:%s", errno, errInfo);
1091         return false;
1092     } else if (pid == 0) { // child process
1093         close(STDIN_FILENO);
1094         close(STDERR_FILENO);
1095         isFifoServer_ = true;
1096         clientPipeOutput_ = open(CONTROL_FIFO_FILE_S2C.c_str(), O_WRONLY);
1097         if (clientPipeOutput_ == -1) {
1098             strerror_r(errno, errInfo, ERRINFOLEN);
1099             HLOGE("open fifo file(%s) failed. %d:%s", CONTROL_FIFO_FILE_S2C.c_str(), errno, errInfo);
1100             return false;
1101         }
1102         nullFd_ = open("/dev/null", O_WRONLY);
1103         (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null
1104     } else {            // parent process
1105         isFifoClient_ = true;
1106         int fd = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK);
1107         if (fd == -1 or !WaitFifoReply(fd, CONTROL_WAITREPY_TOMEOUT)) {
1108             close(fd);
1109             kill(pid, SIGKILL);
1110             remove(CONTROL_FIFO_FILE_C2S.c_str());
1111             remove(CONTROL_FIFO_FILE_S2C.c_str());
1112             strerror_r(errno, errInfo, ERRINFOLEN);
1113             printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo);
1114             return false;
1115         }
1116         close(fd);
1117         printf("%s control hiperf sampling success.\n", restart_ ? "start" : "create");
1118     }
1119     return true;
1120 }
1121 
SendFifoAndWaitReply(const std::string & cmd,const std::chrono::milliseconds & timeOut)1122 bool SubCommandRecord::SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut)
1123 {
1124     // need open for read first, because server maybe send reply before client wait to read
1125     int fdRead = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK);
1126     if (fdRead == -1) {
1127         HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_S2C.c_str());
1128         return false;
1129     }
1130     int fdWrite = open(CONTROL_FIFO_FILE_C2S.c_str(), O_WRONLY | O_NONBLOCK);
1131     if (fdWrite == -1) {
1132         HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_C2S.c_str());
1133         close(fdRead);
1134         return false;
1135     }
1136     size_t size = write(fdWrite, cmd.c_str(), cmd.size());
1137     if (size != cmd.size()) {
1138         HLOGE("failed to write fifo file(%s) command(%s)", CONTROL_FIFO_FILE_C2S.c_str(),
1139               cmd.c_str());
1140         close(fdWrite);
1141         close(fdRead);
1142         return false;
1143     }
1144     close(fdWrite);
1145 
1146     bool ret = WaitFifoReply(fdRead, timeOut);
1147     close(fdRead);
1148     return ret;
1149 }
1150 
WaitFifoReply(int fd,const std::chrono::milliseconds & timeOut)1151 bool SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut)
1152 {
1153     struct pollfd pollFd {
1154         fd, POLLIN, 0
1155     };
1156     int polled = poll(&pollFd, 1, timeOut.count());
1157     std::string reply;
1158     if (polled > 0) {
1159         while (true) {
1160             char c;
1161             ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1));
1162             if (result <= 0) {
1163                 HLOGD("read from fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str());
1164                 break;
1165             }
1166             reply.push_back(c);
1167             if (c == '\n') {
1168                 break;
1169             }
1170         }
1171     } else if (polled == 0) {
1172         HLOGD("wait fifo file(%s) timeout", CONTROL_FIFO_FILE_S2C.c_str());
1173     } else {
1174         HLOGD("wait fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str());
1175     }
1176 
1177     if (reply == HiperfClient::ReplyOK) {
1178         return true;
1179     }
1180     return false;
1181 }
1182 
OnSubCommand(std::vector<std::string> & args)1183 bool SubCommandRecord::OnSubCommand(std::vector<std::string> &args)
1184 {
1185     HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord onSubCommand start");
1186     if (!ProcessControl()) {
1187         return false;
1188     } else if (isFifoClient_) {
1189         return true;
1190     }
1191 
1192     // prepare PerfEvents
1193     if (!PrepareSysKernel() or !PreparePerfEvent()) {
1194         return false;
1195     }
1196 
1197     // prepar some attr before CreateInitRecordFile
1198     if (!perfEvents_.PrepareTracking()) {
1199         HLOGE("Fail to prepare tracking ");
1200         HIPERF_HILOGE(MODULE_DEFAULT, "Fail to prepare tracking ");
1201         return false;
1202     }
1203     HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord perfEvents prepared");
1204 
1205     if (!CreateInitRecordFile(delayUnwind_ ? false : compressData_)) {
1206         HLOGE("Fail to create record file %s", outputFilename_.c_str());
1207         HIPERF_HILOGE(MODULE_DEFAULT, "Fail to create record file %s", outputFilename_.c_str());
1208         return false;
1209     }
1210 
1211     if (!PrepareVirtualRuntime()) {
1212         return false;
1213     }
1214     HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord virtualRuntime prepared");
1215 
1216     // make a thread wait the other command
1217     if (clientPipeOutput_ != -1) {
1218         clientCommandHanle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this);
1219     }
1220 
1221     //write comm event
1222     WriteCommEventBeforeSampling();
1223 
1224     // start tracking
1225     if (isDataSizeLimitStop_) {
1226         // mmap record size has been larger than limit, dont start sampling.
1227     } else if (restart_ && controlCmd_ == CONTROL_CMD_PREPARE) {
1228         if (!perfEvents_.StartTracking(isFifoServer_)) {
1229             return false;
1230         }
1231     } else {
1232         if (!perfEvents_.StartTracking((!isFifoServer_) && (clientPipeInput_ == -1))) {
1233             return false;
1234         }
1235     }
1236     HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord perfEvents tracking finish");
1237 
1238     startSaveFileTimes_ = steady_clock::now();
1239     if (!FinishWriteRecordFile()) {
1240         HLOGE("Fail to finish record file %s", outputFilename_.c_str());
1241         HIPERF_HILOGE(MODULE_DEFAULT, "Fail to finish record file %s", outputFilename_.c_str());
1242         return false;
1243     } else if (!PostProcessRecordFile()) {
1244         HLOGE("Fail to post process record file");
1245         HIPERF_HILOGE(MODULE_DEFAULT, "Fail to post process record file");
1246         return false;
1247     }
1248 
1249     HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord final report");
1250     // finial report
1251     RecordCompleted();
1252     RecoverSavedCmdlinesSize();
1253     OnlineReportData();
1254     CloseClientThread();
1255     return true;
1256 }
1257 
CloseClientThread()1258 void SubCommandRecord::CloseClientThread()
1259 {
1260     if (clientCommandHanle_.joinable()) {
1261         clientExit_ = true;
1262         HLOGI("CloseClientThread");
1263         if (nullFd_ != -1) {
1264             close(nullFd_);
1265         }
1266         clientCommandHanle_.join();
1267         close(clientPipeInput_);
1268         close(clientPipeOutput_);
1269         if (isFifoServer_) {
1270             remove(CONTROL_FIFO_FILE_C2S.c_str());
1271             remove(CONTROL_FIFO_FILE_S2C.c_str());
1272         }
1273     }
1274 }
1275 
ProcessRecord(std::unique_ptr<PerfEventRecord> record)1276 bool SubCommandRecord::ProcessRecord(std::unique_ptr<PerfEventRecord> record)
1277 {
1278     if (record == nullptr) {
1279         HLOGE("record is null");
1280         return false;
1281     }
1282 #if HIDEBUG_RECORD_NOT_PROCESS
1283     // some times we want to check performance
1284     // but we still want to see the record number
1285     if (record->GetType() == PERF_RECORD_SAMPLE) {
1286         recordSamples_++;
1287     } else {
1288         recordNoSamples_++;
1289     }
1290     if (record->GetType() == PERF_RECORD_SAMPLE) {
1291         // when the record is allowed from a cache memory, does not free memory after use
1292         record.release();
1293     }
1294     return true;
1295 #else
1296 #ifdef HIPERF_DEBUG_TIME
1297     const auto startTime = steady_clock::now();
1298 #endif
1299     if (excludeHiperf_) {
1300         static pid_t pid = getpid();
1301         if (record->GetPid() == pid) {
1302             if (record->GetType() == PERF_RECORD_SAMPLE) {
1303                 // when the record is allowed from a cache memory, does not free memory after use
1304                 record.release();
1305             }
1306             // discard record
1307             return true;
1308         }
1309     }
1310 
1311     // May create some simulated events
1312     // it will call ProcessRecord before next line
1313 #if !HIDEBUG_RECORD_NOT_PROCESS_VM
1314     virtualRuntime_.UpdateFromRecord(*record);
1315 #endif
1316 #ifdef HIPERF_DEBUG_TIME
1317     prcessRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
1318 #endif
1319     return SaveRecord(std::move(record), true);
1320 #endif
1321 }
1322 
SaveRecord(std::unique_ptr<PerfEventRecord> record,bool ptrReleaseFlag)1323 bool SubCommandRecord::SaveRecord(std::unique_ptr<PerfEventRecord> record, bool ptrReleaseFlag)
1324 {
1325     ON_SCOPE_EXIT {
1326         if (ptrReleaseFlag && record->GetType() == PERF_RECORD_SAMPLE) {
1327             // when the record is allowed from a cache memory, does not free memory after use
1328             record.release();
1329         }
1330     };
1331 #if HIDEBUG_RECORD_NOT_SAVE
1332     return true;
1333 #endif
1334     if (dataSizeLimit_ > 0u) {
1335         if (dataSizeLimit_ <= fileWriter_->GetDataSize()) {
1336             if (isDataSizeLimitStop_) {
1337                 return false;
1338             }
1339             printf("record size %" PRIu64 " is large than limit %" PRIu64 ". stop sampling.\n",
1340                 fileWriter_->GetDataSize(), dataSizeLimit_);
1341             perfEvents_.StopTracking();
1342             isDataSizeLimitStop_ = true;
1343             return false;
1344         }
1345     }
1346 
1347     if (record) {
1348 #ifdef HIPERF_DEBUG_TIME
1349         const auto saveTime = steady_clock::now();
1350 #endif
1351         if (!fileWriter_->WriteRecord(*record)) {
1352             // write file failed, need stop record
1353             perfEvents_.StopTracking();
1354             HLOGV("fail to write record %s", record->GetName().c_str());
1355             return false;
1356         }
1357         if (record->GetType() == PERF_RECORD_SAMPLE) {
1358             recordSamples_++;
1359         } else {
1360             recordNoSamples_++;
1361         }
1362         HLOGV(" write done. size=%zu name=%s", record->GetSize(), record->GetName().c_str());
1363 #ifdef HIPERF_DEBUG_TIME
1364         saveRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - saveTime);
1365 #endif
1366         return true;
1367     }
1368     return false;
1369 }
1370 
GetCountFromFile(const std::string & fileName)1371 uint32_t SubCommandRecord::GetCountFromFile(const std::string &fileName)
1372 {
1373     uint32_t ret = 0;
1374     std::string str = ReadFileToString(fileName);
1375     std::vector<std::string> subStrs = StringSplit(str);
1376     for (auto subStr : subStrs) {
1377         ret++;
1378         std::vector<std::string> vSubstr = StringSplit(subStr, "-");
1379         static const size_t BEGIN_END = 2;
1380         if (vSubstr.size() == BEGIN_END) {
1381             ret += (std::stoi(vSubstr[1]) - std::stoi(vSubstr[0]));
1382         }
1383     }
1384     return ret;
1385 }
1386 
GetCpuDescFromFile()1387 std::string SubCommandRecord::GetCpuDescFromFile()
1388 {
1389     std::string str = ReadFileToString("/proc/cpuinfo");
1390     std::vector<std::string> subStrs = StringSplit(str, "\n");
1391     for (auto subStr : subStrs) {
1392         if (subStr.find("model name") == std::string::npos) {
1393             continue;
1394         }
1395 
1396         std::vector<std::string> vSubstr = StringSplit(subStr, ": ");
1397         static const size_t NAME_VALUE = 2;
1398         if (vSubstr.size() == NAME_VALUE) {
1399             return vSubstr[1];
1400         } else {
1401             return "";
1402         }
1403     }
1404     return "";
1405 }
1406 
AddCpuFeature()1407 bool SubCommandRecord::AddCpuFeature()
1408 {
1409     utsname unameBuf;
1410     if ((uname(&unameBuf)) != 0) {
1411         perror("uname() failed");
1412         return false;
1413     }
1414 
1415     fileWriter_->AddStringFeature(FEATURE::OSRELEASE, unameBuf.release);
1416     fileWriter_->AddStringFeature(FEATURE::HOSTNAME, unameBuf.nodename);
1417     fileWriter_->AddStringFeature(FEATURE::ARCH, unameBuf.machine);
1418 
1419     try {
1420         uint32_t cpuPresent = GetCountFromFile("/sys/devices/system/cpu/present");
1421         uint32_t cpuOnline = GetCountFromFile("/sys/devices/system/cpu/online");
1422         fileWriter_->AddNrCpusFeature(FEATURE::NRCPUS, cpuPresent - cpuOnline, cpuOnline);
1423     } catch (...) {
1424         HLOGD("get NRCPUS failed");
1425         return false;
1426     }
1427     std::string cpuDesc = GetCpuDescFromFile();
1428     if (!fileWriter_->AddStringFeature(FEATURE::CPUDESC, cpuDesc)) {
1429         return false;
1430     }
1431 
1432     // CPUID(vendor,family,model,stepping in /proc/cpuinfo) isn't supported on Hi3516DV300
1433     // CPU_TOPOLOGY(sockets,dies,threads), isn't supported on Hi3516DV300
1434     // NUMA_TOPOLOGY
1435     // HEADER_PMU_MAPPINGS(/sys/bus/event_source/devices/cpu/type) isn't supported on Hi3516DV300
1436 
1437     return true;
1438 }
1439 
AddMemTotalFeature()1440 void SubCommandRecord::AddMemTotalFeature()
1441 {
1442     std::string str = ReadFileToString("/proc/meminfo");
1443     std::vector<std::string> subStrs = StringSplit(str, " ");
1444     for (auto it = subStrs.begin(); it != subStrs.end(); it++) {
1445         if (it->find("MemTotal:") == std::string::npos) {
1446             continue;
1447         }
1448 
1449         if ((it + 1) != subStrs.end()) {
1450             uint64_t memTotal = std::stoul(*(it + 1));
1451             fileWriter_->AddU64Feature(FEATURE::TOTAL_MEM, memTotal);
1452         }
1453         break;
1454     }
1455 }
1456 
AddEventDescFeature()1457 void SubCommandRecord::AddEventDescFeature()
1458 {
1459     fileWriter_->AddEventDescFeature(FEATURE::EVENT_DESC, perfEvents_.GetAttrWithId());
1460 }
1461 
AddRecordTimeFeature()1462 void SubCommandRecord::AddRecordTimeFeature()
1463 {
1464     // create time
1465     std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
1466     // clang-format off
1467     char buf[256] = { 0 };
1468     ctime_r(&time, buf);
1469     fileWriter_->AddStringFeature(FEATURE::HIPERF_RECORD_TIME,
1470                                   StringReplace(buf, "\n", ""));
1471     // clang-format on
1472     return;
1473 }
1474 
AddWorkloadCmdFeature()1475 void SubCommandRecord::AddWorkloadCmdFeature()
1476 {
1477     if (trackedCommand_.size() > 0) {
1478         fileWriter_->AddStringFeature(FEATURE::HIPERF_WORKLOAD_CMD, trackedCommand_.at(0));
1479     } else {
1480         HLOGD("no trackedCommand");
1481     }
1482 }
1483 
AddCommandLineFeature()1484 void SubCommandRecord::AddCommandLineFeature()
1485 {
1486     // cmdline may end with some no ascii code
1487     // so we cp it with c_str again
1488     std::string fullCommandline =
1489         ReadFileToString("/proc/self/cmdline").c_str() + Command::fullArgument;
1490     fileWriter_->AddStringFeature(FEATURE::CMDLINE, fullCommandline);
1491 }
1492 
AddCpuOffFeature()1493 void SubCommandRecord::AddCpuOffFeature()
1494 {
1495     if (offCPU_) {
1496         fileWriter_->AddBoolFeature(FEATURE::HIPERF_CPU_OFF);
1497     }
1498 }
1499 
AddDevhostFeature()1500 void SubCommandRecord::AddDevhostFeature()
1501 {
1502     if (isHM_) {
1503         fileWriter_->AddStringFeature(FEATURE::HIPERF_HM_DEVHOST,
1504             StringPrintf("%d", virtualRuntime_.devhostPid_));
1505     }
1506 }
1507 
AddFeatureRecordFile()1508 bool SubCommandRecord::AddFeatureRecordFile()
1509 {
1510     // VERSION
1511 
1512     if (!AddCpuFeature()) {
1513         return false;
1514     }
1515 
1516     AddMemTotalFeature();
1517 
1518     AddCommandLineFeature();
1519 
1520     AddEventDescFeature();
1521 
1522     AddRecordTimeFeature();
1523 
1524     AddWorkloadCmdFeature();
1525 
1526     AddCpuOffFeature();
1527 
1528     AddDevhostFeature();
1529 
1530     return true;
1531 }
1532 
CreateInitRecordFile(bool compressData)1533 bool SubCommandRecord::CreateInitRecordFile(bool compressData)
1534 {
1535     if (fileWriter_ == nullptr) {
1536         fileWriter_ = std::make_unique<PerfFileWriter>();
1537     }
1538 
1539     if (!fileWriter_->Open(outputFilename_, compressData)) {
1540         return false;
1541     }
1542 
1543     if (!fileWriter_->WriteAttrAndId(perfEvents_.GetAttrWithId())) {
1544         return false;
1545     }
1546 
1547     if (!AddFeatureRecordFile()) {
1548         return false;
1549     }
1550 
1551     HLOGD("create new record file %s", outputFilename_.c_str());
1552     return true;
1553 }
1554 
PostProcessRecordFile()1555 bool SubCommandRecord::PostProcessRecordFile()
1556 {
1557     if (delayUnwind_) {
1558         // 1. prepare file to rewrite
1559         std::string tempFileName = outputFilename_ + ".tmp";
1560         if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) {
1561             HLOGE("rename failed. unabel to do delay unwind");
1562             perror("Fail to rename data file");
1563             return false;
1564         } else {
1565             HLOGD("use temp file '%s' for delay unwind", tempFileName.c_str());
1566         }
1567 
1568         // renew record file
1569         // release the old one
1570         fileWriter_.reset();
1571         if (!CreateInitRecordFile(compressData_)) {
1572             // create again
1573             HLOGEP("Fail to open data file %s ", outputFilename_.c_str());
1574             return false;
1575         }
1576 
1577         // read temp file
1578         auto fileReader = PerfFileReader::Instance(tempFileName);
1579         if (fileReader == nullptr) {
1580             HLOGEP("Fail to open data file %s ", tempFileName.c_str());
1581             return false;
1582         }
1583 
1584         // 2. read out the file and unwind
1585         auto record_callback = [&](std::unique_ptr<PerfEventRecord> record) {
1586             if (record == nullptr) {
1587                 // return false in callback can stop the read process
1588                 return false;
1589             } else if (record->GetType() == PERF_RECORD_SAMPLE) {
1590                 HLOGM("readback record for unwind");
1591                 virtualRuntime_.UnwindFromRecord(static_cast<PerfRecordSample &>(*record));
1592             }
1593             SaveRecord(std::move(record));
1594             return true;
1595         };
1596         fileReader->ReadDataSection(record_callback);
1597 
1598         // 3. close again
1599 
1600         // lte FinishWriteRecordFile write matched only symbols
1601         delayUnwind_ = false;
1602         if (!FinishWriteRecordFile()) {
1603             HLOGE("Fail to finish record file %s", outputFilename_.c_str());
1604             return false;
1605         }
1606 
1607         remove(tempFileName.c_str());
1608     }
1609     return true;
1610 }
1611 
1612 #if USE_COLLECT_SYMBOLIC
SymbolicHits()1613 void SubCommandRecord::SymbolicHits()
1614 {
1615     if (isHM_) {
1616         for (auto &processPair : kernelThreadSymbolsHits_) {
1617             for (auto &vaddr : processPair.second) {
1618                 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first,
1619                                           PERF_CONTEXT_MAX);
1620             }
1621         }
1622     }
1623 
1624     for (auto &vaddr : kernelSymbolsHits_) {
1625         virtualRuntime_.GetSymbol(vaddr, 0, 0, PERF_CONTEXT_KERNEL);
1626     }
1627 
1628     for (auto &processPair : userSymbolsHits_) {
1629         for (auto &vaddr : processPair.second) {
1630             virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first,
1631                                       PERF_CONTEXT_USER);
1632         }
1633     }
1634 }
1635 #endif
1636 
CollectionSymbol(std::unique_ptr<PerfEventRecord> record)1637 bool SubCommandRecord::CollectionSymbol(std::unique_ptr<PerfEventRecord> record)
1638 {
1639     if (record == nullptr) {
1640         return false;
1641     }
1642     if (record->GetType() == PERF_RECORD_SAMPLE) {
1643         PerfRecordSample *sample = static_cast<PerfRecordSample *>(record.get());
1644 #if USE_COLLECT_SYMBOLIC
1645         CollectSymbol(sample);
1646 #else
1647         virtualRuntime_.SymbolicRecord(*sample);
1648 #endif
1649         // the record is allowed from a cache memory, does not free memory after use
1650         record.release();
1651     }
1652     return true;
1653 }
1654 
CollectSymbol(PerfRecordSample * sample)1655 void SubCommandRecord::CollectSymbol(PerfRecordSample *sample)
1656 {
1657     if (sample == nullptr) {
1658         return;
1659     }
1660     perf_callchain_context context = sample->inKernel() ? PERF_CONTEXT_KERNEL
1661                                                         : PERF_CONTEXT_USER;
1662     pid_t serverPid;
1663     // if no nr use ip ? remove stack nr == 0?
1664     if (sample->data_.nr == 0) {
1665         serverPid = sample->GetServerPidof(0);
1666         if (virtualRuntime_.IsKernelThread(serverPid)) {
1667             kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ip);
1668         } else if (context == PERF_CONTEXT_KERNEL) {
1669             kernelSymbolsHits_.insert(sample->data_.ip);
1670         } else {
1671             userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip);
1672         }
1673     } else {
1674         for (u64 i = 0; i < sample->data_.nr; i++) {
1675             if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) {
1676                 if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) {
1677                     context = PERF_CONTEXT_KERNEL;
1678                 } else {
1679                     context = PERF_CONTEXT_USER;
1680                 }
1681             } else {
1682                 serverPid = sample->GetServerPidof(i);
1683                 if (virtualRuntime_.IsKernelThread(serverPid)) {
1684                     kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ips[i]);
1685                 } else if (context == PERF_CONTEXT_KERNEL) {
1686                     kernelSymbolsHits_.insert(sample->data_.ips[i]);
1687                 } else {
1688                     userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]);
1689                 }
1690             }
1691         }
1692     }
1693 }
1694 
1695 // finish writing data file, then close file
FinishWriteRecordFile()1696 bool SubCommandRecord::FinishWriteRecordFile()
1697 {
1698 #ifdef HIPERF_DEBUG_TIME
1699     const auto startTime = steady_clock::now();
1700 #endif
1701 #if !HIDEBUG_SKIP_PROCESS_SYMBOLS
1702     if (!delayUnwind_) {
1703 #if !HIDEBUG_SKIP_LOAD_KERNEL_SYMBOLS
1704         if (!callChainUserOnly_) {
1705             virtualRuntime_.UpdateKernelSymbols();
1706             virtualRuntime_.UpdateKernelModulesSymbols();
1707             if (isHM_) {
1708                 virtualRuntime_.UpdateServiceSymbols();
1709             }
1710         }
1711         if (isHM_) {
1712             virtualRuntime_.UpdateDevhostSymbols();
1713         }
1714 #endif
1715         HLOGD("Load user symbols");
1716         if (dedupStack_) {
1717             virtualRuntime_.CollectDedupSymbol(kernelSymbolsHits_, userSymbolsHits_);
1718         } else {
1719             fileWriter_->ReadDataSection(
1720                 std::bind(&SubCommandRecord::CollectionSymbol, this, std::placeholders::_1));
1721         }
1722 #if USE_COLLECT_SYMBOLIC
1723         SymbolicHits();
1724 #endif
1725 #if HIDEBUG_SKIP_MATCH_SYMBOLS
1726         disableUnwind_ = true;
1727 #endif
1728 #if !HIDEBUG_SKIP_SAVE_SYMBOLS
1729         if (!fileWriter_->AddSymbolsFeature(virtualRuntime_.GetSymbolsFiles())) {
1730             HLOGE("Fail to AddSymbolsFeature");
1731             return false;
1732         }
1733 #endif
1734     }
1735 #endif
1736 
1737     if (dedupStack_ &&
1738         !fileWriter_->AddUniStackTableFeature(virtualRuntime_.GetUniStackTable())) {
1739         return false;
1740     }
1741     if (!fileWriter_->Close()) {
1742         HLOGE("Fail to close record file %s", outputFilename_.c_str());
1743         return false;
1744     }
1745 #ifdef HIPERF_DEBUG_TIME
1746     saveFeatureTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
1747 #endif
1748     return true;
1749 }
1750 
1751 #ifdef HIPERF_DEBUG_TIME
ReportTime()1752 void SubCommandRecord::ReportTime()
1753 {
1754     printf("updateSymbolsTimes: %0.3f ms\n",
1755            virtualRuntime_.updateSymbolsTimes_.count() / MS_DURATION);
1756     printf("saveFeatureTimes: %0.3f ms\n", saveFeatureTimes_.count() / MS_DURATION);
1757 
1758     printf("prcessRecordTimes: %0.3f ms\n", prcessRecordTimes_.count() / MS_DURATION);
1759     printf("-prcessSampleRecordTimes: %0.3f ms\n",
1760            virtualRuntime_.processSampleRecordTimes_.count() / MS_DURATION);
1761     printf("--unwindFromRecordTimes: %0.3f ms\n",
1762            virtualRuntime_.unwindFromRecordTimes_.count() / MS_DURATION);
1763     printf("-prcessMmapRecordTimes: %0.3f ms\n",
1764            virtualRuntime_.processMmapRecordTimes_.count() / MS_DURATION);
1765     printf("-prcessMmap2RecordTimes: %0.3f ms\n",
1766            virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION);
1767     printf("-prcessCommRecordTimes: %0.3f ms\n",
1768            virtualRuntime_.processCommRecordTimes_.count() / MS_DURATION);
1769     printf("-prcessMmap2RecordTimes: %0.3f ms\n",
1770            virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION);
1771     printf("--updateThreadTimes: %0.3f ms\n",
1772            virtualRuntime_.updateThreadTimes_.count() / MS_DURATION);
1773     printf("---threadParseMapsTimes: %0.3f ms\n",
1774            virtualRuntime_.threadParseMapsTimes_.count() / MS_DURATION);
1775     printf("---threadCreateMmapTimes: %0.3f ms\n",
1776            virtualRuntime_.threadCreateMmapTimes_.count() / MS_DURATION);
1777     printf("--unwindCallStackTimes: %0.3f ms\n",
1778            virtualRuntime_.unwindCallStackTimes_.count() / MS_DURATION);
1779     printf("-symbolicRecordTimes: %0.3f ms\n",
1780            virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION);
1781     printf("saveRecordTimes: %0.3f ms\n", saveRecordTimes_.count() / MS_DURATION);
1782     printf("-writeTimes: %0.3f ms\n", fileWriter_->writeTimes_.count() / MS_DURATION);
1783 
1784     printf("logTimes: %0.3f ms\n", DebugLogger::GetInstance()->logTimes_.count() / MS_DURATION);
1785     printf("-logSprintfTimes: %0.3f ms\n",
1786            DebugLogger::GetInstance()->logSprintfTimes_.count() / MS_DURATION);
1787     printf("-logWriteTimes: %0.3f ms\n",
1788            DebugLogger::GetInstance()->logWriteTimes_.count() / MS_DURATION);
1789     printf("logCount: %zu (%4.2f ms/log)\n", DebugLogger::GetInstance()->logCount_,
1790            DebugLogger::GetInstance()->logTimes_.count() /
1791                static_cast<double>(DebugLogger::GetInstance()->logCount_) / MS_DURATION);
1792 }
1793 #endif
1794 
RecordCompleted()1795 bool SubCommandRecord::RecordCompleted()
1796 {
1797     if (verboseReport_) {
1798         printf("Save Record used %0.3f ms.\n",
1799                duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() /
1800                    MS_DURATION);
1801     }
1802     HLOGV("Save Record used %0.3f ms.\n",
1803           duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() /
1804               MS_DURATION);
1805 
1806     // print brief file info
1807     double mb = static_cast<double>(fileWriter_->GetDataSize()) / (KILO * KILO);
1808     if (compressData_) {
1809         printf("[ hiperf record: Captured and compressed %.3f MB perf data. ]\n", mb);
1810     } else {
1811         printf("[ hiperf record: Captured %.3f MB perf data. ]\n", mb);
1812     }
1813     printf("[ Sample records: %zu, Non sample records: %zu ]\n", recordSamples_, recordNoSamples_);
1814     // Show brief sample lost.
1815     size_t lostSamples = 0;
1816     size_t lostNonSamples = 0;
1817     perfEvents_.GetLostSamples(lostSamples, lostNonSamples);
1818     printf("[ Sample lost: %zu, Non sample lost: %zu ]\n", lostSamples, lostNonSamples);
1819 
1820 #ifdef HIPERF_DEBUG_TIME
1821     ReportTime();
1822 #endif
1823     return true;
1824 }
1825 
RegisterSubCommandRecord(void)1826 bool SubCommandRecord::RegisterSubCommandRecord(void)
1827 {
1828     return SubCommand::RegisterSubCommand("record", std::make_unique<SubCommandRecord>());
1829 }
1830 
SetHM()1831 void SubCommandRecord::SetHM()
1832 {
1833     utsname unameBuf;
1834     if ((uname(&unameBuf)) == 0) {
1835         std::string osrelease = unameBuf.release;
1836         isHM_ = osrelease.find(HMKERNEL) != std::string::npos;
1837     }
1838     virtualRuntime_.SetHM(isHM_);
1839     perfEvents_.SetHM(isHM_);
1840     HLOGD("Set isHM_: %d", isHM_);
1841     if (isHM_) {
1842         // find devhost pid
1843         const std::string basePath {"/proc/"};
1844         std::vector<std::string> subDirs = GetSubDirs(basePath);
1845         for (const auto &subDir : subDirs) {
1846             if (!IsDigits(subDir)) {
1847                 continue;
1848             }
1849             pid_t pid = std::stoll(subDir);
1850             std::string cmdline = GetProcessName(pid);
1851             if (cmdline == "/bin/" + DEVHOST_FILE_NAME) {
1852                 virtualRuntime_.SetDevhostPid(pid);
1853                 break;
1854             }
1855         }
1856     }
1857 }
1858 
OnlineReportData()1859 bool SubCommandRecord::OnlineReportData()
1860 {
1861     if (!report_) {
1862         return true;
1863     }
1864     HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s begin to report file %" HILOG_PUBLIC "s",
1865                   __FUNCTION__, outputFilename_.c_str());
1866     bool ret = false;
1867     std::string tempFileName = outputFilename_ + ".tmp";
1868     if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) {
1869         char errInfo[ERRINFOLEN] = { 0 };
1870         strerror_r(errno, errInfo, ERRINFOLEN);
1871         HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s can't rename file %" HILOG_PUBLIC "s"
1872                       "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n",
1873                       __FUNCTION__, outputFilename_.c_str(), errno, errInfo);
1874         return false;
1875     }
1876 
1877     std::unique_ptr<SubCommandReport> reporter = std::make_unique<SubCommandReport>();
1878     HLOGD("report the file %s to report file %s \n", tempFileName.c_str(), outputFilename_.c_str());
1879     std::vector<std::string> args;
1880     args.emplace_back("-i");
1881     args.emplace_back(tempFileName);
1882     args.emplace_back("-o");
1883     args.emplace_back(outputFilename_);
1884     args.emplace_back("-s");
1885     if (reporter->ParseOption(args)) {
1886         ret =  reporter->OnSubCommand(args);
1887     }
1888 
1889     if (remove(tempFileName.c_str()) != 0) {
1890         char errInfo[ERRINFOLEN] = { 0 };
1891         strerror_r(errno, errInfo, ERRINFOLEN);
1892         HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s remove file failed %" HILOG_PUBLIC "s"
1893                       "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n",
1894                       __FUNCTION__, tempFileName.c_str(), errno, errInfo);
1895     }
1896     HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s report result %" HILOG_PUBLIC "s",
1897                   __FUNCTION__, ret ? "success" : "fail");
1898     return ret;
1899 }
1900 } // namespace HiPerf
1901 } // namespace Developtools
1902 } // namespace OHOS
1903