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