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